#!/usr/bin/env python3
"""
MikroTik API Authentication Tester
Tests credentials using the MikroTik API protocol on port 8728/8729
"""

import ssl
import socket
import sys

class MikrotikAPITester:
    def __init__(self, host, port=8729):
        self.host = host
        self.port = port
        self.sock = None
        self.ssl_sock = None
        
    def connect(self):
        """Establish SSL connection to MikroTik router"""
        try:
            # Create socket and SSL context
            context = ssl.create_default_context()
            context.check_hostname = False
            context.verify_mode = ssl.CERT_NONE
            # Allow older SSL/TLS versions for compatibility
            context.set_ciphers('DEFAULT:@SECLEVEL=0')
            
            # Connect to router
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.settimeout(10)
            self.sock.connect((self.host, self.port))
            self.ssl_sock = context.wrap_socket(self.sock)
            return True
        except Exception as e:
            print(f"Connection failed: {e}")
            return False
    
    def disconnect(self):
        """Close the connection"""
        if self.ssl_sock:
            self.ssl_sock.close()
        if self.sock:
            self.sock.close()
    
    def encode_length(self, length):
        """Encode length for MikroTik API protocol"""
        if length <= 0x7F:
            return bytes([length])
        elif length <= 0x3FFF:
            return bytes([((length >> 8) & 0xFF) | 0x80, length & 0xFF])
        elif length <= 0x1FFFFF:
            return bytes([((length >> 16) & 0xFF) | 0xC0, 
                         (length >> 8) & 0xFF, 
                         length & 0xFF])
        elif length <= 0xFFFFFFF:
            return bytes([((length >> 24) & 0xFF) | 0xE0,
                         (length >> 16) & 0xFF,
                         (length >> 8) & 0xFF,
                         length & 0xFF])
        else:
            return bytes([0xF0,
                         (length >> 24) & 0xFF,
                         (length >> 16) & 0xFF,
                         (length >> 8) & 0xFF,
                         length & 0xFF])
    
    def decode_length(self):
        """Decode length from MikroTik API protocol"""
        c = self.ssl_sock.recv(1)[0]
        
        if (c & 0x80) == 0x00:
            return c
        elif (c & 0xC0) == 0x80:
            return ((c & ~0xC0) << 8) + self.ssl_sock.recv(1)[0]
        elif (c & 0xE0) == 0xC0:
            data = self.ssl_sock.recv(2)
            return ((c & ~0xE0) << 16) + (data[0] << 8) + data[1]
        elif (c & 0xF0) == 0xE0:
            data = self.ssl_sock.recv(3)
            return ((c & ~0xF0) << 24) + (data[0] << 16) + (data[1] << 8) + data[2]
        elif (c & 0xF8) == 0xF0:
            data = self.ssl_sock.recv(4)
            return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]
    
    def write_word(self, word):
        """Send a word to the router"""
        word_bytes = word.encode('utf-8')
        self.ssl_sock.send(self.encode_length(len(word_bytes)))
        self.ssl_sock.send(word_bytes)
    
    def read_word(self):
        """Read a word from the router"""
        length = self.decode_length()
        if length == 0:
            return ""
        return self.ssl_sock.recv(length).decode('utf-8', 'ignore')
    
    def write_sentence(self, words):
        """Send a sentence (list of words) to the router"""
        for word in words:
            self.write_word(word)
        self.write_word("")
    
    def read_sentence(self):
        """Read a sentence from the router"""
        sentence = []
        while True:
            word = self.read_word()
            if word == "":
                break
            sentence.append(word)
        return sentence
    
    def test_login(self, username, password):
        """Test login credentials"""
        if not self.connect():
            return False
        
        try:
            # Send login command
            self.write_sentence(["/login", f"=name={username}", f"=password={password}"])
            
            # Read response
            response = self.read_sentence()
            
            if response and response[0] == "!done":
                return True
            else:
                return False
        except Exception as e:
            print(f"Login test error: {e}")
            return False
        finally:
            self.disconnect()

def main():
    host = "10.250.2.2"
    
    # Test both SSL and non-SSL ports
    ports = [8729, 8728]  # SSL and non-SSL
    
    # Common MikroTik default passwords
    credentials = [
        ("admin", ""),           # Empty password
        ("admin", "admin"),      # admin/admin
        ("admin", "password"),   # admin/password
        ("admin", "123456"),     # admin/123456
        ("admin", "mikrotik"),   # admin/mikrotik
        ("admin", "router"),     # admin/router
        ("admin", "default"),    # admin/default
    ]
    
    print(f"Testing MikroTik API authentication on {host}")
    print("=" * 60)
    
    for port in ports:
        port_type = "SSL" if port == 8729 else "Non-SSL"
        print(f"\nTesting port {port} ({port_type}):")
        
        tester = MikrotikAPITester(host, port)
        
        for username, password in credentials:
            pwd_display = f'"{password}"' if password else "(empty)"
            print(f"  Testing {username}:{pwd_display}...", end=" ")
            
            if tester.test_login(username, password):
                print(f"✓ SUCCESS!")
                print(f"\n*** WORKING CREDENTIALS FOUND ***")
                print(f"Host: {host}")
                print(f"Port: {port} ({port_type})")
                print(f"Username: {username}")
                print(f"Password: {pwd_display}")
                return
            else:
                print("✗ Failed")
    
    print("\nNo working credentials found with common defaults.")
    print("\nSuggestions:")
    print("1. Device may have custom password")
    print("2. Try hardware reset if you own the device")
    print("3. Check if WinBox shows any additional information")

if __name__ == "__main__":
    main()