Projects STRLCPY aardwolf Commits 00aa31d7
🤬
  • ■ ■ ■ ■ ■
    aardwolf/commons/target.py
    skipped 12 lines
    13 13   
    14 14  class RDPConnectionDialect(enum.Enum):
    15 15   RDP = 'RDP'
     16 + VNC = 'VNC'
    16 17   
    17 18  class RDPConnectionProtocol(enum.Enum):
    18 19   TCP = 'TCP'
    skipped 2 lines
    21 22   """
    22 23   """
    23 24   def __init__(self, ip = None, port = 3389, hostname = None, timeout = 1, dc_ip=None,
    24  - domain = None, proxy = None, protocol = RDPConnectionProtocol.TCP, serverip = None):
     25 + domain = None, proxy = None, protocol = RDPConnectionProtocol.TCP, serverip = None, dialect = RDPConnectionDialect.RDP):
    25 26   self.ip = ip
    26 27   self.port = port
    27 28   self.hostname = hostname
    skipped 3 lines
    31 32   self.proxy = proxy
    32 33   self.protocol = protocol
    33 34   self.serverip = serverip
     35 + self.dialect = dialect
     36 + if self.dialect == RDPConnectionDialect.VNC:
     37 + self.port = 5900
    34 38   
    35 39   def to_target_string(self):
    36 40   return 'termsrv/%s@%s' % (self.hostname, self.domain)
    skipped 9 lines
    46 50   proxy = copy.deepcopy(self.proxy),
    47 51   protocol = self.protocol,
    48 52   serverip = self.serverip,
     53 + dialect = self.dialect
    49 54   )
    50 55   return t
    51 56  
    52 57   @staticmethod
    53  - def from_connection_string(s):
     58 + def from_connection_string(s, is_rdp = True):
    54 59   port = 3389
     60 + if is_rdp is False:
     61 + port = 5900
     62 +
    55 63   dc = None
    56 64  
    57 65   _, target = s.rsplit('@', 1)
    skipped 54 lines
  • ■ ■ ■ ■ ■
    aardwolf/commons/url.py
    skipped 4 lines
    5 5  from aardwolf.commons.target import RDPTarget, RDPConnectionDialect, RDPConnectionProtocol
    6 6  from aardwolf.commons.authbuilder import AuthenticatorBuilder
    7 7  from aardwolf.connection import RDPConnection
     8 +from aardwolf.vncconnection import VNCConnection
    8 9  from getpass import getpass
    9 10  import base64
    10 11  import ipaddress
    skipped 39 lines
    50 51   tio = copy.deepcopy(iosettings)
    51 52   credential = self.get_credential()
    52 53   target = self.get_target()
    53  - return RDPConnection(target, credential, tio)
     54 + if target.dialect is not None:
     55 + if target.dialect == RDPConnectionDialect.RDP:
     56 + return RDPConnection(target, credential, tio)
     57 + elif target.dialect == RDPConnectionDialect.VNC:
     58 + return VNCConnection(target, credential, tio)
     59 + else:
     60 + raise Exception('Unknown dialect %s' % target.dialect)
     61 + raise Exception('Either target or dialect must be defined first!')
    54 62   
    55 63   def create_connection_newtarget(self, ip_or_hostname, iosettings):
    56 64   tio = copy.deepcopy(iosettings)
    skipped 42 lines
    99 107   domain = self.domain,
    100 108   proxy = self.get_proxy(),
    101 109   protocol=self.protocol,
     110 + dialect=self.dialect
    102 111   )
    103 112   return t
    104 113   
    skipped 12 lines
    117 126  
    118 127   
    119 128   def scheme_decoder(self, scheme):
    120  - #print('SCHEME: %s' % scheme)
     129 + print('SCHEME: %s' % scheme)
    121 130   schemes = scheme.upper().split('+')
    122 131  
    123 132   connection_tags = schemes[0].split('-')
    skipped 227 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/examples/aardpclient.py
    skipped 4 lines
    5 5  import traceback
    6 6  import queue
    7 7  import threading
     8 +import concurrent.futures
    8 9   
    9 10  from aardwolf import logger
    10 11  from aardwolf.commons.url import RDPConnectionURL
    skipped 43 lines
    54 55   self.settings = settings
    55 56   self.in_q = in_q
    56 57   
    57  - async def inputhandler(self):
    58  - # This is super-ugly but I could not find a better solution
    59  - # Problem is that pyqt5 is not async, and QT internally is using its own event loop
    60  - # which makes everythign a mess.
    61  - # If you know better (that doesn't require a noname unmaintained lib install) lemme know!
    62  - try:
    63  - while not self.conn.disconnected_evt.is_set():
    64  - try:
    65  - data = self.in_q.get(False)
    66  - except queue.Empty:
    67  - await asyncio.sleep(0.01)
    68  - continue
    69  - await self.conn.ext_in_queue.put(data)
    70  - except:
    71  - traceback.print_exc()
     58 + def inputhandler(self, loop:asyncio.AbstractEventLoop):
     59 + while not self.conn.disconnected_evt.is_set():
     60 + data = self.in_q.get()
     61 + loop.call_soon_threadsafe(self.conn.ext_in_queue.put_nowait, data)
     62 + 
     63 + #async def inputhandler(self):
     64 + # # This is super-ugly but I could not find a better solution
     65 + # # Problem is that pyqt5 is not async, and QT internally is using its own event loop
     66 + # # which makes everythign a mess.
     67 + # # If you know better (that doesn't require a noname unmaintained lib install) lemme know!
     68 + # try:
     69 + # while not self.conn.disconnected_evt.is_set():
     70 + # try:
     71 + # data = self.in_q.get(False)
     72 + # except queue.Empty:
     73 + # await asyncio.sleep(0.01)
     74 + # continue
     75 + # await self.conn.ext_in_queue.put(data)
     76 + # except:
     77 + # traceback.print_exc()
    72 78  
    73 79   async def rdpconnection(self):
    74 80   try:
    skipped 3 lines
    78 84   if err is not None:
    79 85   raise err
    80 86   
    81  - asyncio.create_task(self.inputhandler())
     87 + #asyncio.create_task(self.inputhandler())
     88 + asyncio.get_event_loop().run_in_executor(None, self.inputhandler, asyncio.get_event_loop())
    82 89   self.loop_started_evt.set()
    83 90   while True:
    84 91   data = await self.conn.ext_out_queue.get()
    skipped 221 lines
  • ■ ■ ■ ■ ■
    aardwolf/network/selector.py
    skipped 19 lines
    20 20   elif target.proxy.type in [RDPProxyType.WSNET,RDPProxyType.WSNETWS, RDPProxyType.WSNETWSS, RDPProxyType.SOCKS5, RDPProxyType.SOCKS5_SSL, RDPProxyType.SOCKS4, RDPProxyType.SOCKS4_SSL]:
    21 21   return SocksProxyConnection(target = target), None
    22 22   
    23  - #elif target.proxy.type in [RDPProxyType.MULTIPLEXOR, RDPProxyType.MULTIPLEXOR_SSL]:
    24  - # mpc = MultiplexorProxyConnection(target)
    25  - # socks_proxy, err = await mpc.connect()
    26  - # return socks_proxy, err
    27  - 
    28 23   else:
    29 24   return None, Exception('Cant select correct connection type!')
    30 25   except Exception as e:
    skipped 1 lines
  • ■ ■ ■ ■ ■
    aardwolf/transport/tcp.py
    skipped 52 lines
    53 53   lasterror = None
    54 54   while not self.disconnected.is_set():
    55 55   try:
    56  - data = await self.reader.read(16384)
     56 + await asyncio.sleep(0)
     57 + data = await self.reader.read(1073741824)
    57 58   #print('TCP <- %s' % data.hex())
    58 59   await self.in_queue.put( (data, None) )
    59 60   if data == b'':
    skipped 51 lines
    111 112  
    112 113   try:
    113 114   self.reader, self.writer = await asyncio.wait_for(con, int(self.settings.timeout))
    114  - #sock = self.writer.transport.get_extra_info('socket')
    115  - #if sock is not None:
    116  - # sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    117  - # sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
    118  - # sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
    119  - # sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
    120  - 
    121 115   except asyncio.TimeoutError:
    122 116   logging.debug('[TCPSocket] Connection timeout')
    123 117   raise Exception('[TCPSocket] Connection timeout')
    skipped 27 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/transport/tcpstream.py
     1 + 
     2 +import asyncio
     3 + 
     4 +class TCPStream:
     5 + @staticmethod
     6 + async def from_tcpsocket(tcpsocket):
     7 + # tcpsocket must be running at this point
     8 + in_queue = tcpsocket.in_queue
     9 + out_queue = tcpsocket.out_queue
     10 + closed_event = tcpsocket.disconnected
     11 +
     12 + writer = TCPStreamWriter(out_queue, closed_event)
     13 + reader = TCPStreamReader(in_queue, closed_event)
     14 + await writer.run()
     15 + await reader.run()
     16 +
     17 + return reader, writer
     18 + 
     19 + 
     20 +class TCPStreamWriter:
     21 + def __init__(self, out_queue, closed_event):
     22 + self.out_queue = out_queue
     23 + self.closed_event = closed_event
     24 + 
     25 + def write(self, data):
     26 + self.out_queue.put_nowait(data)
     27 + 
     28 + def close(self):
     29 + self.out_queue.put_nowait(None)
     30 + self.closed_event.set()
     31 + 
     32 + async def drain(self):
     33 + await asyncio.sleep(0)
     34 + return
     35 + 
     36 + async def run(self):
     37 + return
     38 + 
     39 +class TCPStreamReader:
     40 + def __init__(self, in_queue, closed_event):
     41 + self.wsnet_reader_type = None
     42 + self.in_queue = in_queue
     43 + self.closed_event = closed_event
     44 + self.buffer = b''
     45 + self.data_in_evt = []
     46 + self.err = None
     47 + 
     48 + self.__lock = asyncio.Lock()
     49 + 
     50 + async def run(self):
     51 + return
     52 + 
     53 + async def read(self, n = -1):
     54 + try:
     55 + if self.closed_event.is_set():
     56 + return b''
     57 + 
     58 + if self.__lock.locked() is True:
     59 + raise Exception("Another operation is already in progress")
     60 +
     61 + if len(self.buffer) > 0 and len(self.buffer) >= n:
     62 + if n == -1:
     63 + temp = self.buffer
     64 + self.buffer = b''
     65 + else:
     66 + temp = self.buffer[:n]
     67 + self.buffer = self.buffer[n:]
     68 +
     69 + else:
     70 + async with self.__lock:
     71 + if n == -1:
     72 + res, self.err = await self.in_queue.get()
     73 + self.buffer = b''
     74 + 
     75 + while len(self.buffer) > 0 and len(self.buffer) >= n:
     76 + res, self.err = await self.in_queue.get()
     77 + if self.err is not None:
     78 + if res is not None:
     79 + self.buffer += res
     80 + raise self.err
     81 + self.buffer += res
     82 +
     83 + if n == -1:
     84 + temp = self.buffer
     85 + self.buffer = b''
     86 + return temp
     87 + else:
     88 + temp = self.buffer[:n]
     89 + self.buffer = self.buffer[n:]
     90 + return temp
     91 + return temp
     92 + 
     93 + except Exception as e:
     94 + #print(e)
     95 + self.closed_event.set()
     96 + raise
     97 + 
     98 + async def readexactly(self, n):
     99 + try:
     100 + if n < 1:
     101 + raise Exception('Readexactly must be a positive integer!')
     102 + 
     103 + if self.__lock.locked() is True:
     104 + raise Exception("Another operation is already in progress")
     105 + async with self.__lock:
     106 + while len(self.buffer) < n:
     107 + res, self.err = await self.in_queue.get()
     108 + if self.err is not None:
     109 + if res is not None:
     110 + self.buffer += res
     111 + raise self.err
     112 +
     113 + self.buffer += res
     114 +
     115 + temp = self.buffer[:n]
     116 + self.buffer = self.buffer[n:]
     117 + return temp
     118 + except Exception as e:
     119 + #print(e)
     120 + self.closed_event.set()
     121 + raise
     122 + 
     123 + async def readuntil(self, pattern):
     124 + try:
     125 + if self.closed_event.is_set():
     126 + raise Exception('Pipe broken!')
     127 +
     128 + if self.__lock.locked() is True:
     129 + raise Exception("Another operation is already in progress")
     130 +
     131 + async with self.__lock:
     132 + ploc = self.buffer.find(pattern)
     133 + while ploc == -1:
     134 + res, self.err = await self.in_queue.get()
     135 + if self.err is not None:
     136 + if res is not None:
     137 + self.buffer += res
     138 + raise self.err
     139 +
     140 + self.buffer += res
     141 + ploc = self.buffer.find(pattern)
     142 +
     143 + end = self.buffer.find(pattern)+len(pattern)
     144 + temp = self.buffer[:end]
     145 + self.buffer = self.buffer[end:]
     146 + #print('readuntil ret %s' % temp)
     147 + return temp
     148 + 
     149 + except Exception as e:
     150 + #print(e)
     151 + self.closed_event.set()
     152 + raise
     153 + 
     154 + async def readline(self):
     155 + return await self.readuntil(b'\n')
  • ■ ■ ■ ■ ■ ■
    aardwolf/utils/rle/rle.c
    skipped 962 lines
    963 963   }
    964 964  }
    965 965   
     966 +static void
     967 +convert_rgbx_rgba(uint8 *decomp_buff, int decomp_buff_size, uint8 *dst, int dst_size){
     968 + for(int i =0; i< decomp_buff_size; i=i+4){
     969 + dst[i] = decomp_buff[i];
     970 + dst[i+1] = decomp_buff[i+1];
     971 + dst[i+2] = decomp_buff[i+2];
     972 + dst[i+3] = 0xff;
     973 + }
     974 +}
     975 + 
    966 976  /* *INDENT-ON* */
    967 977   
    968 978  static PyObject*
    skipped 50 lines
    1019 1029   Py_RETURN_NONE;
    1020 1030  }
    1021 1031   
     1032 +static PyObject*
     1033 +mask_rgbx_wrapper(PyObject* self, PyObject* args)
     1034 +{
     1035 + Py_buffer output, input;
     1036 + 
     1037 + if (!PyArg_ParseTuple(args, "s*s*", &output, &input)){
     1038 + PyErr_SetString(PyExc_TypeError, "Input parameter error");
     1039 + return (PyObject *) NULL;
     1040 + }
     1041 + 
     1042 + convert_rgbx_rgba((uint8*)input.buf, input.len, (uint8*)output.buf, output.len);
     1043 +
     1044 + Py_RETURN_NONE;
     1045 +}
     1046 + 
    1022 1047  static PyMethodDef rle_methods[] =
    1023 1048  {
    1024 1049   {"bitmap_decompress", bitmap_decompress_wrapper, METH_VARARGS, "decompress bitmap from microsoft rle algorithm."},
     1050 + {"mask_rgbx", mask_rgbx_wrapper, METH_VARARGS, "Converts RGBX to RGBA"},
    1025 1051   {NULL, NULL, 0, NULL}
    1026 1052  };
    1027 1053   
    skipped 24 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/vncconnection.py
     1 + 
     2 +import asyncio
     3 +import os
     4 +import io
     5 +from sys import byteorder
     6 +import traceback
     7 +from struct import pack, unpack
     8 +from typing import cast
     9 + 
     10 +from aardwolf import logger
     11 +from aardwolf.network.selector import NetworkSelector
     12 +from aardwolf.transport.tcpstream import TCPStream
     13 +from aardwolf.crypto.symmetric import DES
     14 +from aardwolf.crypto.BASE import cipherMODE
     15 + 
     16 +from aardwolf.commons.queuedata import *
     17 + 
     18 +from PIL import Image, ImageDraw
     19 +from PIL.ImageQt import ImageQt
     20 +import rle
     21 + 
     22 +# https://datatracker.ietf.org/doc/html/rfc6143
     23 + 
     24 +RAW_ENCODING = 0
     25 +COPY_RECTANGLE_ENCODING = 1
     26 +RRE_ENCODING = 2
     27 +CORRE_ENCODING = 4
     28 +HEXTILE_ENCODING = 5
     29 +ZLIB_ENCODING = 6
     30 +TIGHT_ENCODING = 7
     31 +ZLIBHEX_ENCODING = 8
     32 +TRLE_ENCODING = 15
     33 +ZRLE_ENCODING = 16
     34 + 
     35 +class VNCConnection:
     36 + def __init__(self, target, credentials, iosettings):
     37 + self.target = target
     38 + self.credentials = credentials
     39 + self.authapi = None
     40 + self.iosettings = iosettings
     41 + self.shared_flag = False
     42 + self.client_version = '003.008'
     43 + self.server_version = None
     44 + self.server_name = None
     45 + self.disconnected_evt = asyncio.Event() #this will be set if we disconnect for whatever reason
     46 + self.server_supp_security_types = []
     47 + self.__selected_security_type = 2
     48 + 
     49 + # these are the main queues with which you can communicate with the server
     50 + # ext_out_queue: yields video data
     51 + # ext_in_queue: expects keyboard/mouse data
     52 + self.ext_out_queue = asyncio.Queue()
     53 + self.ext_in_queue = asyncio.Queue()
     54 + 
     55 + self.__reader = None
     56 + self.__writer = None
     57 + 
     58 + 
     59 + self.bpp = None
     60 + self.bypp = None
     61 + self.depth = None
     62 + self.bigendian = None
     63 + self.truecolor = None
     64 + self.redmax = None
     65 + self.greenmax = None
     66 + self.bluemax = None
     67 + self.redshift = None
     68 + self.greenshift = None
     69 + self.blueshift = None
     70 + self.width = None
     71 + self.height = None
     72 + self.__desktop_buffer = None
     73 + self.__frame_update_req_evt = asyncio.Event()
     74 + 
     75 +
     76 + async def terminate(self):
     77 + try:
     78 + return True, None
     79 + except Exception as e:
     80 + traceback.print_exc()
     81 + return None, e
     82 + finally:
     83 + self.disconnected_evt.set()
     84 +
     85 + async def __aenter__(self):
     86 + return self
     87 +
     88 + async def __aexit__(self, exc_type, exc, traceback):
     89 + await asyncio.wait_for(self.terminate(), timeout = 5)
     90 +
     91 + async def connect(self):
     92 + """
     93 + Performs the entire connection sequence
     94 + """
     95 + try:
     96 + # starting lower-layer transports
     97 + logger.debug('Selecting network')
     98 + remote_socket, err = await NetworkSelector.select(self.target)
     99 + if err is not None:
     100 + raise err
     101 + 
     102 + logger.debug('Connecting sockets')
     103 + _, err = await remote_socket.connect()
     104 + if err is not None:
     105 + raise err
     106 +
     107 + logger.debug('Wrapping sockets to streamer')
     108 + self.__reader, self.__writer = await TCPStream.from_tcpsocket(remote_socket)
     109 + 
     110 + logger.debug('Performing banner exchange')
     111 + _, err = await self.__banner_exchange()
     112 + if err is not None:
     113 + raise err
     114 + logger.debug('Banner exchange OK')
     115 + 
     116 + logger.debug('Performing security handshake')
     117 + _, err = await self.__security_handshake()
     118 + if err is not None:
     119 + raise err
     120 + logger.debug('Security handshake OK')
     121 +
     122 + logger.debug('Client init')
     123 + _, err = await self.__client_init()
     124 + if err is not None:
     125 + raise err
     126 + logger.debug('Client init OK')
     127 + 
     128 + self.__reader_loop_task = asyncio.create_task(self.__reader_loop())
     129 + self.__external_reader_task = asyncio.create_task(self.__external_reader())
     130 +
     131 + return True, None
     132 + except Exception as e:
     133 + self.disconnected_evt.set()
     134 + return None, e
     135 +
     136 + async def __banner_exchange(self):
     137 + try:
     138 + banner = await self.__reader.readuntil(b'\n')
     139 + self.server_version = banner[4:-1].decode()
     140 + logger.debug('Server version: %s' % self.server_version)
     141 + version_reply = b'RFB %s\n' % self.client_version.encode()
     142 + logger.debug('Version reply: %s' % version_reply)
     143 + self.__writer.write(version_reply)
     144 + 
     145 + print(self.server_version)
     146 + return True, None
     147 + except Exception as e:
     148 + return None, e
     149 + 
     150 + async def __security_handshake(self):
     151 + try:
     152 + no_sec_types = await self.__reader.readexactly(1)
     153 + no_sec_types = ord(no_sec_types)
     154 + if no_sec_types == 0:
     155 + logger.debug('Server sent empty support security types, connection will terminate soon')
     156 + err_string_size = await self.__reader.readexactly(1)
     157 + err_string_size = ord(err_string_size)
     158 + err_string = await self.__reader.readexactly(err_string_size)
     159 + err_string = err_string.decode()
     160 + raise Exception(err_string)
     161 + sec_types = await self.__reader.readexactly(no_sec_types)
     162 + for sectype in sec_types:
     163 + self.server_supp_security_types.append(sectype)
     164 +
     165 + if self.__selected_security_type == 2:
     166 + logger.debug('Selecting default VNC auth type')
     167 + self.__writer.write(bytes([self.__selected_security_type]))
     168 + challenge = await self.__reader.readexactly(16)
     169 + 
     170 + password = self.credentials.secret.ljust(8, '\x00').encode('ascii')
     171 + print(password)
     172 + # converting password to key
     173 + newkey = b''
     174 + for ki in range(len(password)):
     175 + bsrc = password[ki]
     176 + btgt = 0
     177 + for i in range(8):
     178 + if bsrc & (1 << i):
     179 + btgt = btgt | (1 << 7 - i)
     180 + newkey+= bytes([btgt])
     181 + ctx = DES(newkey, mode = cipherMODE.ECB, IV = None)
     182 + response = ctx.encrypt(challenge)
     183 + print(response)
     184 + self.__writer.write(response)
     185 + 
     186 + else:
     187 + raise Exception('Unsupported security type selected!')
     188 +
     189 + auth_result = await self.__reader.readexactly(4)
     190 + auth_result = int.from_bytes(auth_result, byteorder = 'big', signed=False)
     191 + if auth_result != 0:
     192 + logger.debug('Auth Failed!')
     193 + err_string_size = await self.__reader.readexactly(4)
     194 + err_string_size = int.from_bytes(err_string_size, byteorder = 'big', signed=False)
     195 + err_string = await self.__reader.readexactly(err_string_size)
     196 + err_string = err_string.decode()
     197 + raise Exception(err_string)
     198 + logger.debug('Auth OK!')
     199 + 
     200 + print(self.server_supp_security_types)
     201 + return True, None
     202 + except Exception as e:
     203 + return None, e
     204 +
     205 + async def __client_init(self):
     206 + try:
     207 + # sending client init
     208 + self.__writer.write(bytes([int(self.shared_flag)]))
     209 + # reading server_init
     210 + framebuffer_width = await self.__reader.readexactly(2)
     211 + self.width = int.from_bytes(framebuffer_width, byteorder = 'big', signed = False)
     212 + framebuffer_height = await self.__reader.readexactly(2)
     213 + self.height = int.from_bytes(framebuffer_height, byteorder = 'big', signed = False)
     214 + pixel_format_raw = await self.__reader.readexactly(16)
     215 + self.bpp, self.depth, self.bigendian, self.truecolor, \
     216 + self.redmax, self.greenmax, self.bluemax, self.redshift, \
     217 + self.greenshift, self.blueshift = unpack("!BBBBHHHBBBxxx", pixel_format_raw)
     218 + self.bypp = self.bpp // 8 # calc bytes per pixel
     219 + 
     220 + name_string_size = await self.__reader.readexactly(4)
     221 + name_string_size = int.from_bytes(name_string_size, byteorder = 'big', signed=False)
     222 + name_string = await self.__reader.readexactly(name_string_size)
     223 + self.server_name = name_string.decode()
     224 + 
     225 + 
     226 + self.__desktop_buffer = Image.new(mode="RGBA", size=(self.width, self.height))
     227 + _, err = await self.set_encodings()
     228 + if err is not None:
     229 + raise err
     230 + 
     231 + await self.set_pixel_format()
     232 + await self.framebuffer_update_request()
     233 + await self.framebuffer_update_request(incremental = 1)
     234 +
     235 + asyncio.create_task(self.testloop())
     236 + return True, None
     237 + except Exception as e:
     238 + return None, e
     239 + 
     240 + async def __external_reader(self):
     241 + # This coroutine handles keyboard/mouse etc input from the user
     242 + # It wraps the data in it's appropriate format then dispatches it to the server
     243 + try:
     244 + while True:
     245 + indata = await self.ext_in_queue.get()
     246 + if indata is None:
     247 + #signaling exit
     248 + await self.terminate()
     249 + return
     250 + if indata.type == RDPDATATYPE.KEYSCAN:
     251 + indata = cast(RDP_KEYBOARD_SCANCODE, indata)
     252 + #await self.handle_out_data(cli_input, sec_hdr, data_hdr, None, self.__joined_channels['MCS'].channel_id, False)
     253 +
     254 + if indata.type == RDPDATATYPE.KEYUNICODE:
     255 + indata = cast(RDP_KEYBOARD_UNICODE, indata)
     256 + #await self.handle_out_data(cli_input, sec_hdr, data_hdr, None, self.__joined_channels['MCS'].channel_id, False)
     257 +
     258 + elif indata.type == RDPDATATYPE.MOUSE:
     259 + #PointerEvent
     260 + indata = cast(RDP_MOUSE, indata)
     261 + if indata.xPos < 0 or indata.yPos < 0:
     262 + continue
     263 +
     264 + #print('sending mouse!')
     265 + msg = pack("!BBHH", 5, int(indata.pressed), indata.xPos, indata.yPos)
     266 + self.__writer.write(msg)
     267 + 
     268 + elif indata.type == RDPDATATYPE.CLIPBOARD_DATA_TXT:
     269 + indata = cast(RDP_CLIPBOARD_DATA_TXT, indata)
     270 + txtdata = indata.data.encode('ascii')
     271 + msg = pack("!BxxxIs", 6, len(txtdata), txtdata)
     272 + self.__writer.write(msg)
     273 + 
     274 + except asyncio.CancelledError:
     275 + return None, None
     276 + 
     277 + except Exception as e:
     278 + traceback.print_exc()
     279 + return None, e
     280 + 
     281 + async def testloop(self):
     282 + ctr = 0
     283 + while True:
     284 + await self.__frame_update_req_evt.wait()
     285 + if ctr == 100:
     286 + ctr = 0
     287 + await self.framebuffer_update_request(1)
     288 + else:
     289 + await self.framebuffer_update_request(incremental=1)
     290 + #await self.framebuffer_update_request(incremental=1)
     291 + #await self.framebuffer_update_request(incremental=1)
     292 + self.__frame_update_req_evt.clear()
     293 + ctr += 1
     294 + #x = RDP_MOUSE()
     295 + #x.yPos = 1
     296 + #x.xPos = 1
     297 + #x.pressed = False
     298 + #await self.ext_in_queue.put(x)
     299 + 
     300 + async def __send_rect(self, x, y, width, height, image:Image):
     301 + try:
     302 + #image.save('./test/test_%s.png' % os.urandom(4).hex(), 'PNG')
     303 + #updating desktop buffer to have a way to copy rectangles later
     304 + if self.width == width and self.height == height:
     305 + self.__desktop_buffer = image
     306 + self.__desktop_buffer.save('test.png', 'PNG')
     307 + else:
     308 + self.__desktop_buffer.paste(image, [x, y, x+width, y+height])
     309 +
     310 + #if self.iosettings.video_out_format == 'pil' or self.iosettings.video_out_format == 'pillow':
     311 + # return image
     312 +
     313 + if self.iosettings.video_out_format == 'raw':
     314 + image = image.tobytes()
     315 + 
     316 + elif self.iosettings.video_out_format == 'qt':
     317 + image = ImageQt(image)
     318 +
     319 + elif self.iosettings.video_out_format == 'png':
     320 + img_byte_arr = io.BytesIO()
     321 + image.save(img_byte_arr, format=self.iosettings.video_out_format.upper())
     322 + image = img_byte_arr.getvalue()
     323 + else:
     324 + raise ValueError('Output format of "%s" is not supported!' % self.iosettings.video_out_format)
     325 +
     326 + rect = RDP_VIDEO()
     327 + rect.x = x
     328 + rect.y = y
     329 + rect.width = width
     330 + rect.height = height
     331 + rect.bitsPerPixel = self.bpp
     332 + rect.is_compressed = False
     333 + rect.data = image
     334 + await self.ext_out_queue.put(rect)
     335 + 
     336 + 
     337 + except Exception as e:
     338 + traceback.print_exc()
     339 + return None, e
     340 + 
     341 +
     342 +
     343 + async def __reader_loop(self):
     344 + try:
     345 + while True:
     346 + msgtype = await self.__reader.readexactly(1)
     347 + msgtype = ord(msgtype)
     348 + print(msgtype)
     349 + if msgtype == 0:
     350 + # framebufferupdate
     351 + num_rect = await self.__reader.readexactly(3)
     352 + num_rect = int.from_bytes(num_rect[1:], byteorder = 'big', signed = False)
     353 + print(num_rect)
     354 + for _ in range(num_rect):
     355 + rect_hdr = await self.__reader.readexactly(12)
     356 + (x, y, width, height, encoding) = unpack("!HHHHI", rect_hdr)
     357 +
     358 + 
     359 + if encoding == RAW_ENCODING:
     360 + #print(width*height*self.bypp)
     361 + try:
     362 + #print(self.bypp)
     363 + #print('x %s' % x)
     364 + #print('y %s' % y)
     365 + #print('width %s' % width)
     366 + #print('height %s' % height)
     367 + data = await self.__reader.readexactly(width*height*self.bypp)
     368 + image = bytes(width * height * self.bypp)
     369 + rle.mask_rgbx(image, data)
     370 + #print(image[:4])
     371 + #print(len(image))
     372 + #print(width*height*self.bypp)
     373 + image = Image.frombytes('RGBA', [width, height], image)
     374 + await self.__send_rect(x,y,width, height, image)
     375 + except Exception as e:
     376 + traceback.print_exc()
     377 + continue
     378 + 
     379 + elif encoding == COPY_RECTANGLE_ENCODING:
     380 + try:
     381 + data = await self.__reader.readexactly(4)
     382 + (srcx, srcy) = unpack("!HH", data)
     383 + 
     384 + #print('copy')
     385 + #print('width %s' % width)
     386 + #print('height %s' % height)
     387 + #print('x %s' % x)
     388 + #print('y %s' % y)
     389 + #print('srcx %s' % srcx)
     390 + #print('srcy %s' % srcy)
     391 +
     392 + newrect = self.__desktop_buffer.crop([srcx, srcy, srcx+width, srcy+height])
     393 + await self.__send_rect(x,y,width,height, newrect)
     394 + continue
     395 + except Exception as e:
     396 + traceback.print_exc()
     397 + return
     398 + 
     399 + 
     400 + elif encoding == RRE_ENCODING:
     401 + try:
     402 + print('RRE!')
     403 + sub_rect_num = await self.__reader.readexactly(4)
     404 + sub_rect_num = int.from_bytes(sub_rect_num, byteorder = 'big', signed = False)
     405 + #print(sub_rect_num)
     406 + backgroud_pixel = await self.__reader.readexactly(self.bypp)
     407 + r,g,b,a = unpack("BBBB", backgroud_pixel)
     408 + a = 255
     409 + rect = Image.new('RGBA', [width, height])
     410 + rect.paste((r,g,b,a), [0,0, width, height])
     411 +
     412 + format = "!%dsHHHH" % self.bypp
     413 + for _ in range(sub_rect_num):
     414 + data = await self.__reader.readexactly(self.bypp + 8)
     415 + (color, subx, suby, subwidth, subheight) = unpack(format, data)
     416 + r,g,b,a = unpack("BBBB", color)
     417 + a = 255
     418 + #rect = Image.new('RGBA', subwidth, subheight)
     419 + rect.paste((r,g,b,a), [subx,suby, subx+subwidth, suby+subheight])
     420 + #await self.__send_rect(subx + x, suby + y, width, height)
     421 + await self.__send_rect(x,y, width, height, rect)
     422 + except Exception as e:
     423 + traceback.print_exc()
     424 + return
     425 +
     426 +
     427 + elif encoding == TRLE_ENCODING:
     428 + try:
     429 + sub_enc = await self.__reader.readexactly(1)
     430 + sub_enc = ord(sub_enc)
     431 + run_length_encoded = bool(sub_enc >> 7)
     432 + palette_size = sub_enc
     433 + if run_length_encoded is True:
     434 + palette_size -= 128
     435 + if palette_size == 0:
     436 + data = await self.__reader.readexactly(width*height*self.bypp)
     437 + elif palette_size == 1:
     438 + data = await self.__reader.readexactly(self.bypp)
     439 +
     440 +
     441 + 
     442 + 
     443 + 
     444 + 
     445 + except Exception as e:
     446 + traceback.print_exc()
     447 + return
     448 + 
     449 + else:
     450 + print('Unknown encoding %s' % encoding)
     451 + elif msgtype == 1:
     452 + print('colormap')
     453 + hdr = await self.__reader.readexactly(5)
     454 + (_, firstcolor, numcolor) = unpack("!BHH", hdr)
     455 + for _ in range(numcolor):
     456 + color_raw = await self.__reader.readexactly(6)
     457 + (red, green, blue) = unpack("!HHH", color_raw)
     458 +
     459 + elif msgtype == 2:
     460 + print('bell')
     461 +
     462 + elif msgtype == 3:
     463 + hdr = await self.__reader.readexactly(7)
     464 + (_,_,_, cliplen ) = unpack("!BBBI", hdr)
     465 + cliptext = await self.__reader.readexactly(cliplen)
     466 + cliptext = cliptext.decode()
     467 + print(cliptext)
     468 + else:
     469 + print('Unexpected message tpye %s' % msgtype)
     470 + 
     471 + if msgtype == 0:
     472 + self.__frame_update_req_evt.set()
     473 + 
     474 +
     475 + return True, None
     476 + except Exception as e:
     477 + return None, e
     478 + 
     479 + async def set_encodings(self, list_of_encodings = [2, 1, 0]):
     480 + try:
     481 + enc_encodings = b''
     482 + for encoding in list_of_encodings:
     483 + enc_encodings += encoding.to_bytes(4, byteorder = 'big', signed = True)
     484 + sendbuff = pack("!BxH", 2, len(list_of_encodings)) + enc_encodings
     485 + print(sendbuff)
     486 + self.__writer.write(sendbuff)
     487 + return True, None
     488 + except Exception as e:
     489 + return None, e
     490 +
     491 + 
     492 + async def set_pixel_format(self, bpp=32, depth=24, bigendian=0, truecolor=1, redmax=255, greenmax=255, bluemax=255,
     493 + redshift=0, greenshift=8, blueshift=16):
     494 + pixformat = pack("!BBBBHHHBBBxxx", bpp, depth, bigendian, truecolor, redmax, greenmax, bluemax, redshift,
     495 + greenshift, blueshift)
     496 + print(pixformat)
     497 + self.__writer.write(b'\x00\x00\x00\x00' + pixformat)
     498 + # rember these settings
     499 + self.bpp, self.depth, self.bigendian, self.truecolor = bpp, depth, bigendian, truecolor
     500 + self.redmax, self.greenmax, self.bluemax = redmax, greenmax, bluemax
     501 + self.redshift, self.greenshift, self.blueshift = redshift, greenshift, blueshift
     502 + self.bypp = self.bpp // 8 # calc bytes per pixel
     503 + # ~ print self.bypp
     504 + 
     505 + async def framebuffer_update_request(self, x=0, y=0, width=None, height=None, incremental=0):
     506 + if width is None:
     507 + width = self.width - x
     508 + 
     509 + if height is None:
     510 + height = self.height - y
     511 + 
     512 + self.__writer.write(pack("!BBHHHH", 3, incremental, x, y, width, height))
     513 +
     514 + def _handle_rectangle(self, block):
     515 + (x, y, width, height, encoding) = unpack("!HHHHI", block)
     516 + if self.rectangles:
     517 + self.rectangles -= 1
     518 + self.rectanglePos.append((x, y, width, height))
     519 + if encoding == COPY_RECTANGLE_ENCODING:
     520 + self._handleDecodeCopyrect( 4, x, y, width, height)
     521 + elif encoding == RAW_ENCODING:
     522 + self._handle_decode_raw( width * height * self.bypp, x, y, width, height)
     523 + elif encoding == HEXTILE_ENCODING:
     524 + self._do_next_hextile_subrect(None, None, x, y, width, height, None, None)
     525 + elif encoding == CORRE_ENCODING:
     526 + self._handle_decode_corre( 4 + self.bypp, x, y, width, height)
     527 + elif encoding == RRE_ENCODING:
     528 + self._handleDecodeRRE(4 + self.bypp, x, y, width, height)
     529 + else:
     530 + logger.msg("unknown encoding received (encoding %d)\n" % encoding)
     531 + self._do_connection()
     532 + else:
     533 + self._do_connection()
     534 +
     535 + def _handle_decode_raw(self, block, x, y, width, height):
     536 + # TODO convert pixel format?
     537 + self.update_rectangle(x, y, width, height, block)
     538 + 
     539 + def _handleDecodeCopyrect(self, block, x, y, width, height):
     540 + (srcx, srcy) = unpack("!HH", block)
     541 + self.copy_rectangle(srcx, srcy, x, y, width, height)
     542 +
     543 + def _handle_decode_corre_rectangles(self, block, topx, topy):
     544 + # ~ print "_handleDecodeCORRERectangle"
     545 + pos = 0
     546 + end = len(block)
     547 + sz = self.bypp + 4
     548 + format = "!%dsBBBB" % self.bypp
     549 + while pos < sz:
     550 + (color, x, y, width, height) = unpack(format, block[pos:pos + sz])
     551 + self.fill_rectangle(topx + x, topy + y, width, height, color)
     552 + pos += sz
     553 + 
     554 + def _do_next_hextile_subrect(self, bg, color, x, y, width, height, tx, ty):
     555 + """
     556 + # Hextile Encoding
     557 + :param bg:
     558 + :param color:
     559 + :param x:
     560 + :param y:
     561 + :param width:
     562 + :param height:
     563 + :param tx:
     564 + :param ty:
     565 + :return:
     566 + """
     567 + # ~ print "_doNextHextileSubrect %r" % ((color, x, y, width, height, tx, ty), )
     568 + # coords of next tile
     569 + # its line after line of tiles
     570 + # finished when the last line is completly received
     571 + 
     572 + # dont inc the first time
     573 + if tx is not None:
     574 + # calc next subrect pos
     575 + tx += 16
     576 + if tx >= x + width:
     577 + tx = x
     578 + ty += 16
     579 + else:
     580 + tx = x
     581 + ty = y
     582 + # more tiles?
     583 + if ty >= y + height:
     584 + self._do_connection() # read more!
     585 + else:
     586 + self._handle_decode_hextile(1, bg, color, x, y, width, height, tx, ty)
     587 +
     588 + def _handle_decode_hextile(self, block, bg, color, x, y, width, height, tx, ty):
     589 + """
     590 + # Hextile Decoding
     591 + :param block:
     592 + :param bg:
     593 + :param color:
     594 + :param x:
     595 + :param y:
     596 + :param width:
     597 + :param height:
     598 + :param tx:
     599 + :param ty:
     600 + :return:
     601 + """
     602 + (sub_encoding,) = unpack("!B", block)
     603 + # calc tile size
     604 + tw = th = 16
     605 + if x + width - tx < 16:
     606 + tw = x + width - tx
     607 + 
     608 + if y + height - ty < 16:
     609 + th = y + height - ty
     610 + 
     611 + # decode tile
     612 + if sub_encoding & 1: # RAW
     613 + self._handle_decode_hextile_raw( tw * th * self.bypp, bg, color, x, y, width, height, tx, ty, tw, th)
     614 + else:
     615 + num_bytes = 0
     616 + if sub_encoding & 2: # BackgroundSpecified
     617 + num_bytes += self.bypp
     618 + if sub_encoding & 4: # ForegroundSpecified
     619 + num_bytes += self.bypp
     620 + if sub_encoding & 8: # AnySubrects
     621 + num_bytes += 1
     622 + if num_bytes:
     623 + self._handle_decode_hextile_subrect(num_bytes, sub_encoding, bg, color, x, y, width, height, tx, ty, tw, th)
     624 + else:
     625 + self.fill_rectangle(tx, ty, tw, th, bg)
     626 + self._do_next_hextile_subrect(bg, color, x, y, width, height, tx, ty)
     627 +
     628 + def _handle_decode_hextile_raw(self, block, bg, color, x, y, width, height, tx, ty, tw, th):
     629 + """the tile is in raw encoding"""
     630 + self.update_rectangle(tx, ty, tw, th, block)
     631 + self._do_next_hextile_subrect(bg, color, x, y, width, height, tx, ty)
     632 +
     633 + def _handle_decode_corre_rectangles(self, block, topx, topy):
     634 + # ~ print "_handleDecodeCORRERectangle"
     635 + pos = 0
     636 + end = len(block)
     637 + sz = self.bypp + 4
     638 + format = "!%dsBBBB" % self.bypp
     639 + while pos < sz:
     640 + (color, x, y, width, height) = unpack(format, block[pos:pos + sz])
     641 + self.fill_rectangle(topx + x, topy + y, width, height, color)
     642 + pos += sz
     643 +
     644 + def _handleDecodeRRE(self, block, x, y, width, height):
     645 + (subrects,) = unpack("!I", block[:4])
     646 + color = block[4:]
     647 + self.fill_rectangle(x, y, width, height, color)
     648 + self._handle_rre_sub_rectangles((8 + self.bypp) * subrects, x, y)
     649 +
     650 + def _handle_rre_sub_rectangles(self, block, topx, topy):
     651 + """
     652 + # RRE Sub Rectangles
     653 + :param block:
     654 + :param topx:
     655 + :param topy:
     656 + :return:
     657 + """
     658 + 
     659 + pos = 0
     660 + end = len(block)
     661 + sz = self.bypp + 8
     662 + format = "!%dsHHHH" % self.bypp
     663 + while pos < end:
     664 + (color, x, y, width, height) = unpack(format, block[pos:pos + sz])
     665 + self.fill_rectangle(topx + x, topy + y, width, height, color)
     666 + pos += sz
     667 + 
     668 + def fill_rectangle(self, x, y, width, height, color):
     669 + """fill the area with the color. the color is a string in
     670 + the pixel format set up earlier"""
     671 + # fallback variant, use update recatngle
     672 + # override with specialized function for better performance
     673 + self.update_rectangle(x, y, width, height, color * width * height)
     674 + 
     675 + def update_rectangle(self, x, y, width, height, size):
     676 + print(x, y, width, height, size)
     677 + 
     678 + def copy_rectangle(self, srcx, srcy, x, y, width, height):
     679 + print(srcx, srcy, x, y, width, height)
     680 + 
     681 +async def amain():
     682 + try:
     683 + logger.setLevel(1)
     684 + from aardwolf.commons.url import RDPConnectionURL
     685 + from aardwolf.commons.iosettings import RDPIOSettings
     686 + 
     687 + iosettings = RDPIOSettings()
     688 + iosettings.video_out_format = 'raw'
     689 +
     690 + url = 'vnc+plain://alma:[email protected]:5900'
     691 + url = RDPConnectionURL(url)
     692 + connection = url.get_connection(iosettings)
     693 + print(connection)
     694 + print(connection.target.port)
     695 + 
     696 + _, err = await connection.connect()
     697 + if err is not None:
     698 + raise err
     699 + 
     700 + while True:
     701 + await asyncio.sleep(10)
     702 + 
     703 + return True, None
     704 + except Exception as e:
     705 + traceback.print_exc()
     706 + return None, e
     707 + 
     708 +def main():
     709 + asyncio.run(amain())
     710 + 
     711 +if __name__ == '__main__':
     712 + main()
Please wait...
Page is in error, reload to recover