Projects STRLCPY aardwolf Commits 34cf53a1
🤬
  • adding virtual channel support, socks proxy

  • Loading...
  • SkelSec committed 1 year ago
    34cf53a1
    1 parent 86c4b511
  • ■ ■ ■ ■
    aardwolf/_version.py
    1 1   
    2  -__version__ = "0.2.1"
     2 +__version__ = "0.2.2"
    3 3  __banner__ = \
    4 4  """
    5 5  # aardwolf %s
    skipped 2 lines
  • ■ ■ ■ ■ ■
    aardwolf/commons/iosettings.py
    skipped 1 lines
    2 2  from aardwolf.protocol.x224.constants import SUPP_PROTOCOLS, NEG_FLAGS
    3 3  from aardwolf.commons.queuedata.constants import VIDEO_FORMAT
    4 4  from aardwolf.protocol.T125.extendedinfopacket import PERF
     5 +from aardwolf.extensions.RDPEDYC.channel import RDPEDYCChannel
     6 + 
     7 +from aardwolf.extensions.RDPEDYC.vchannels.echo import VchannelECHO
     8 +from aardwolf.extensions.RDPEDYC.vchannels.test import VchannelTEST
    5 9   
    6 10  class RDPIOSettings:
    7 11   def __init__(self):
    8 12   # RDP only settings
    9 13   # Which channels should be enabled
    10  - self.channels = [RDPECLIPChannel]
     14 + self.channels = [RDPECLIPChannel, RDPEDYCChannel]
     15 + self.vchannels = {
     16 + 'ECHO' : VchannelECHO(),
     17 + 'DATATEST1' : VchannelTEST(),
     18 + }
    11 19   # Authentication protocols supported
    12 20   self.supported_protocols = None # supported_protocols if None: it will be determined automatically. otherwise select one or more from these SUPP_PROTOCOLS.RDP | SUPP_PROTOCOLS.SSL |SUPP_PROTOCOLS.HYBRID_EX
    13 21   
    skipped 44 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/connection.py
    skipped 1259 lines
    1260 1260   try:
    1261 1261   from aardwolf.commons.factory import RDPConnectionFactory
    1262 1262   from aardwolf.commons.iosettings import RDPIOSettings
     1263 + from aardwolf.extensions.RDPEDYC.channel import RDPEDYCChannel
    1263 1264   
    1264 1265   iosettings = RDPIOSettings()
    1265  - url = 'rdp+ntlm-password://TEST\\Administrator:[email protected]'
    1266  - rdpurl = RDPConnectionFactory.from_url(url)
     1266 + iosettings.channels.append(RDPEDYCChannel)
     1267 + url = 'rdp+ntlm-password://TEST\\Administrator:[email protected]'
     1268 + rdpurl = RDPConnectionFactory.from_url(url, iosettings)
    1267 1269   conn = rdpurl.get_connection(iosettings)
    1268 1270   _, err = await conn.connect()
    1269 1271   if err is not None:
    skipped 1 lines
    1271 1273  
    1272 1274   while True:
    1273 1275   data = await conn.ext_out_queue.get()
    1274  - print(data)
     1276 + #print(data)
    1275 1277   except Exception as e:
    1276 1278   traceback.print_exc()
    1277 1279   
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPECLIP/protocol/formatlist.py
    skipped 110 lines
    111 111   self.formatId:CLIPBRD_FORMAT = None
    112 112   self.formatName:str = '' #always 32 bytes total, truncated if need be
    113 113   self.encoding = encoding
     114 + self.clpfmt = None #stored for unknown data formats
    114 115   
    115 116   def to_bytes(self):
    116 117   t = self.formatId.value.to_bytes(4, byteorder='little', signed=False)
    skipped 11 lines
    128 129   @staticmethod
    129 130   def from_buffer(buff: io.BytesIO, encoding:str='utf-16-le'):
    130 131   msg = CLIPRDR_SHORT_FORMAT_NAME(encoding)
    131  - msg.formatId = CLIPBRD_FORMAT(int.from_bytes(buff.read(4), byteorder='little', signed=False))
     132 + msg.clpfmt = int.from_bytes(buff.read(4), byteorder='little', signed=False)
     133 + try:
     134 + msg.formatId = CLIPBRD_FORMAT(msg.clpfmt)
     135 + except:
     136 + msg.formatId = CLIPBRD_FORMAT.UNKNOWN
    132 137   msg.formatName = buff.read(32).decode(encoding).replace('\x00', '')
    133 138   return msg
    134 139   
    skipped 14 lines
    149 154   def __init__(self, encoding = 'utf-16-le'):
    150 155   self.formatId:CLIPBRD_FORMAT = None
    151 156   self.wszFormatName:str = None #variable, if none or '' then one single b'\x00'
     157 + self.clpfmt = None
    152 158   
    153 159   def to_bytes(self):
    154 160   if self.wszFormatName is None or self.wszFormatName == '':
    skipped 14 lines
    169 175   @staticmethod
    170 176   def from_buffer(buff: io.BytesIO):
    171 177   msg = CLIPRDR_LONG_FORMAT_NAME()
    172  - t = buff.read(4)
    173  - # not all formats are documented!!!!
     178 + msg.clpfmt = int.from_bytes(buff.read(4), byteorder='little', signed=False)
    174 179   try:
    175  - msg.formatId = CLIPBRD_FORMAT(int.from_bytes(t, byteorder='little', signed=False))
    176  - except Exception as e:
    177  - msg.formatId = int.from_bytes(t, byteorder='little', signed=False)
     180 + msg.formatId = CLIPBRD_FORMAT(msg.clpfmt)
     181 + except:
     182 + msg.formatId = CLIPBRD_FORMAT.UNKNOWN
    178 183  
    179 184   t = buff.read(1)
    180 185   if t == b'\x00':
    skipped 21 lines
  • ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/__init__.py
     1 + 
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/channel.py
     1 +import asyncio
     2 +import traceback
     3 +from typing import cast, Dict, List
     4 + 
     5 +from aardwolf import logger
     6 +from aardwolf.channels import Channel
     7 +from aardwolf.protocol.T124.userdata.constants import ChannelOption
     8 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_MESSAGE, DYNVC_CMD
     9 +from aardwolf.extensions.RDPEDYC.protocol.caps import DYNVC_CAPS_REQ
     10 +from aardwolf.extensions.RDPEDYC.protocol.create import DYNVC_CREATE_REQ, DYNVC_CREATE_RSP
     11 +from aardwolf.extensions.RDPEDYC.protocol.close import DYNVC_CLOSE
     12 + 
     13 +from aardwolf.protocol.channelpdu import CHANNEL_PDU_HEADER, CHANNEL_FLAG
     14 +from aardwolf.protocol.T128.security import TS_SECURITY_HEADER,SEC_HDR_FLAG
     15 +from aardwolf.extensions.RDPEDYC.vchannels import VirtualChannelBase
     16 + 
     17 +class RDPEDYCChannel(Channel):
     18 + name = 'drdynvc'
     19 + def __init__(self, iosettings):
     20 + Channel.__init__(self, self.name, ChannelOption.INITIALIZED|ChannelOption.ENCRYPT_RDP)
     21 + self.defined_channels = iosettings.vchannels
     22 + self.compression_needed = False
     23 + self.version = 1
     24 + self.version_data_sent = False
     25 + self.channels:Dict[int, VirtualChannelBase] = {}
     26 +
     27 + async def start(self):
     28 + try:
     29 + #print('START called!')
     30 + return True, None
     31 + except Exception as e:
     32 + return None, e
     33 + 
     34 + async def stop(self):
     35 + try:
     36 + return True, None
     37 + except Exception as e:
     38 + return None, e
     39 + 
     40 + async def close_channel(self, channelid):
     41 + resp = DYNVC_CLOSE()
     42 + resp.ChannelId = channelid
     43 + await self.fragment_and_send(resp.to_bytes())
     44 + if channelid not in self.channels:
     45 + return
     46 + await self.channels[channelid].channel_closed()
     47 + 
     48 + async def send_channel_create_response(self, channelid, result:int = 0):
     49 + resp = DYNVC_CREATE_RSP()
     50 + resp.ChannelId = channelid
     51 + resp.CreationStatus = result
     52 + await self.fragment_and_send(resp.to_bytes())
     53 +
     54 + async def process_channel_data(self, data):
     55 + #print('CHANNELDATA: %s' % data)
     56 + channeldata = CHANNEL_PDU_HEADER.from_bytes(data)
     57 + #print(channeldata)
     58 + msg = DYNVC_MESSAGE.from_bytes(channeldata.data)
     59 + #print(msg)
     60 + if msg.cmd == DYNVC_CMD.CAPS_RSP and self.version_data_sent is False:
     61 + await self.fragment_and_send(DYNVC_CAPS_REQ().to_bytes())
     62 + self.version_data_sent = True
     63 + return
     64 +
     65 + if msg.cmd == DYNVC_CMD.CREATE_RSP:
     66 + msg = cast(DYNVC_CREATE_REQ, msg)
     67 + if msg.ChannelName not in self.defined_channels:
     68 + logger.debug('Server supports channel "%s" but client doesn\'t have definition!' % msg.ChannelName)
     69 + await self.send_channel_create_response(msg.ChannelId, result = 0xC0000001) #STATUS_UNSUCCESSFUL
     70 + return
     71 +
     72 + _, err = await self.defined_channels[msg.ChannelName].channel_init_internal(msg.ChannelId, self)
     73 + if err is not None:
     74 + logger.debug('Channel initialization failed! Error: %s' % err)
     75 + await self.send_channel_create_response(msg.ChannelId, result = 0xC0000001) #STATUS_UNSUCCESSFUL
     76 + return
     77 +
     78 + self.channels[msg.ChannelId] = self.defined_channels[msg.ChannelName]
     79 + await self.send_channel_create_response(msg.ChannelId, result = 0) #STATUS_SUCCESS
     80 + return
     81 + 
     82 + elif msg.cmd == DYNVC_CMD.DATA_FIRST:
     83 + if msg.ChannelId not in self.channels:
     84 + logger.debug('DATA arrived for unknown channel %s' % msg.ChannelId)
     85 + return
     86 + await self.channels[msg.ChannelId].channel_rawdata_in(msg)
     87 + elif msg.cmd == DYNVC_CMD.DATA:
     88 + if msg.ChannelId not in self.channels:
     89 + logger.debug('DATA arrived for unknown channel %s' % msg.ChannelId)
     90 + return
     91 + await self.channels[msg.ChannelId].channel_rawdata_in(msg)
     92 + 
     93 + elif msg.cmd == DYNVC_CMD.CLOSE:
     94 + if msg.ChannelId not in self.channels:
     95 + logger.debug('CLOSE arrived for unknown channel %s' % msg.ChannelId)
     96 + return
     97 + await self.channels[msg.ChannelId].channel_closed()
     98 +
     99 + elif msg.cmd in [DYNVC_CMD.DATA_FIRST_COMPRESSED, DYNVC_CMD.DATA_COMPRESSED]:
     100 + logger.debug('Compressed data recieved! Currently compression is not supported!')
     101 +
     102 + elif msg.cmd in [DYNVC_CMD.SOFT_SYNC_REQUEST, DYNVC_CMD.SOFT_SYNC_REQUEST]:
     103 + logger.debug('Softsync packets are currently not supported')
     104 +
     105 + else:
     106 + logger.debug('Unknown message type of %s recieved!' % msg.cmd)
     107 + 
     108 + async def fragment_and_send(self, data):
     109 + try:
     110 + if len(data) < 16000:
     111 + if self.compression_needed is False:
     112 + flags = CHANNEL_FLAG.CHANNEL_FLAG_FIRST|CHANNEL_FLAG.CHANNEL_FLAG_LAST #|CHANNEL_FLAG.CHANNEL_FLAG_SHOW_PROTOCOL
     113 + packet = CHANNEL_PDU_HEADER.serialize_packet(flags, data)
     114 + else:
     115 + raise NotImplementedError('Compression not implemented!')
     116 + else:
     117 + raise NotImplementedError('Chunked send not implemented!')
     118 +
     119 + sec_hdr = None
     120 + if self.connection.cryptolayer is not None:
     121 + sec_hdr = TS_SECURITY_HEADER()
     122 + sec_hdr.flags = SEC_HDR_FLAG.ENCRYPT
     123 + sec_hdr.flagsHi = 0
     124 + 
     125 + await self.send_channel_data(packet, sec_hdr, None, None, False)
     126 + 
     127 + return True, False
     128 + except Exception as e:
     129 + traceback.print_exc()
     130 + return None,e
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/__init__.py
     1 +import io
     2 +import enum
     3 + 
     4 +# again, this is so messed up. The same commandid can be request or response as well...
     5 +class DYNVC_CMD(enum.Enum):
     6 + CREATE_RSP = 0x01 #The message contained in the optionalFields field is a Create Request PDU (section 2.2.2.1) or a Create Response PDU (section 2.2.2.2).
     7 + DATA_FIRST = 0x02 #The message contained in the optionalFields field is a Data First PDU (section 2.2.3.1).
     8 + DATA = 0x03 #The message contained in the optionalFields field is a Data PDU (section 2.2.3.2).
     9 + CLOSE = 0x04 #The message contained in the optionalFields field is a Close Request PDU (section 2.2.4) or a Close Response PDU (section 2.2.4).
     10 + CAPS_RSP = 0x05 #The message contained in the optionalFields field is a Capability Request PDU (section 2.2.1.1) or a Capabilities Response PDU (section 2.2.1.2).
     11 + DATA_FIRST_COMPRESSED = 0x06 #The message contained in the optionalFields field is a Data First Compressed PDU (section 2.2.3.3).
     12 + DATA_COMPRESSED = 0x07 #The message contained in the optionalFields field is a Data Compressed PDU (section 2.2.3.4).
     13 + SOFT_SYNC_REQUEST = 0x08 #The message contained in the optionalFields field is a Soft-Sync Request PDU (section 2.2.5.1).
     14 + SOFT_SYNC_RESPONSE = 0x09 #The message contained in the optionalFields field is a Soft-Sync Response PDU (section 2.2.5.2).
     15 + 
     16 + 
     17 +def dynvc_header_from_bytes(data:bytes, cbid_mod = False, sp_mod = False):
     18 + hdr = data[0]
     19 + cmd = DYNVC_CMD(hdr >> 4)
     20 + sp = (hdr >> 2) & 0b11
     21 + cbid = hdr & 0b11
     22 + if cbid_mod is True:
     23 + if cbid == 0: cbid += 1
     24 + else: cbid = cbid * 2
     25 + if sp_mod is True:
     26 + if sp == 0: sp += 1
     27 + else: sp = sp * 2
     28 + return cbid, sp, cmd
     29 + 
     30 +def dynvc_header_from_buff(buffer:io.BytesIO, cbid_mod = False, sp_mod = False):
     31 + return dynvc_header_from_bytes(buffer.read(1), cbid_mod=cbid_mod, sp_mod=sp_mod)
     32 + 
     33 +def dynvc_header_to_bytes(cbid:int, sp:int, cmd:DYNVC_CMD, cbid_mod = False, sp_mod = False):
     34 + if cbid_mod is True:
     35 + if cbid == 1: cbid = 0
     36 + elif cbid == 2: cbid = 1
     37 + elif cbid == 4: cbid = 2
     38 + if sp_mod is True:
     39 + if sp == 1: sp = 0
     40 + elif sp == 2: sp = 1
     41 + elif sp == 4: sp = 2
     42 + return bytes([(cmd.value << 4) ^ (sp << 2) ^ cbid])
     43 + 
     44 +from aardwolf.extensions.RDPEDYC.protocol.softsync import DYNVC_SOFT_SYNC_REQUEST, DYNVC_SOFT_SYNC_RESPONSE
     45 +from aardwolf.extensions.RDPEDYC.protocol.close import DYNVC_CLOSE
     46 +from aardwolf.extensions.RDPEDYC.protocol.data import DYNVC_DATA_FIRST, DYNVC_DATA
     47 +from aardwolf.extensions.RDPEDYC.protocol.create import DYNVC_CREATE_REQ, DYNVC_CREATE_RSP
     48 +from aardwolf.extensions.RDPEDYC.protocol.caps import DYNVC_CAPS_REQ
     49 + 
     50 +class DYNVC_MESSAGE:
     51 + def __init__(self):
     52 + self.cmd:DYNVC_CMD2MSG = None
     53 + 
     54 + @staticmethod
     55 + def from_bytes(data:bytes):
     56 + return DYNVC_MESSAGE.from_buffer(io.BytesIO(data))
     57 +
     58 + @staticmethod
     59 + def from_buffer(buff:io.BytesIO):
     60 + _, _, cmd = dynvc_header_from_bytes(buff.read(1))
     61 + buff.seek(-1, io.SEEK_CUR)
     62 + if DYNVC_CMD2MSG[cmd] is None:
     63 + raise NotImplementedError()
     64 + return DYNVC_CMD2MSG[cmd].from_buffer(buff)
     65 +
     66 + 
     67 +DYNVC_CMD2MSG = {
     68 + DYNVC_CMD.CREATE_RSP : DYNVC_CREATE_REQ,
     69 + DYNVC_CMD.DATA_FIRST : DYNVC_DATA_FIRST,
     70 + DYNVC_CMD.DATA : DYNVC_DATA,
     71 + DYNVC_CMD.CLOSE : DYNVC_CLOSE,
     72 + DYNVC_CMD.CAPS_RSP : DYNVC_CAPS_REQ,
     73 + DYNVC_CMD.DATA_FIRST_COMPRESSED : None, #not supported!
     74 + DYNVC_CMD.DATA_COMPRESSED : None, # not supported!
     75 + DYNVC_CMD.SOFT_SYNC_REQUEST : DYNVC_SOFT_SYNC_REQUEST,
     76 + DYNVC_CMD.SOFT_SYNC_RESPONSE : DYNVC_SOFT_SYNC_RESPONSE,
     77 +}
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/caps.py
     1 +import io
     2 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD, dynvc_header_to_bytes, dynvc_header_from_buff
     3 + 
     4 +class DYNVC_CAPS_REQ:
     5 + def __init__(self):
     6 + self.cbid:int = 0
     7 + self.sp:int = 0
     8 + self.cmd:DYNVC_CMD = DYNVC_CMD.CAPS_RSP
     9 + self.pad:int = 0
     10 + self.version:int = 1
     11 + self.PriorityCharge0:int = None
     12 + self.PriorityCharge1:int = None
     13 + self.PriorityCharge2:int = None
     14 + self.PriorityCharge3:int = None
     15 + 
     16 + @staticmethod
     17 + def from_bytes(data: bytes):
     18 + return DYNVC_CAPS_REQ.from_buffer(io.BytesIO(data))
     19 + 
     20 + @staticmethod
     21 + def from_buffer(buff: io.BytesIO):
     22 + msg = DYNVC_CAPS_REQ()
     23 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff)
     24 + msg.pad = buff.read(1)[0]
     25 + msg.version = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     26 + if msg.version > 1:
     27 + msg.PriorityCharge0 = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     28 + msg.PriorityCharge1 = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     29 + msg.PriorityCharge2 = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     30 + msg.PriorityCharge3 = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     31 + return msg
     32 + 
     33 + def to_bytes(self):
     34 + t = b''
     35 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd)
     36 + t += bytes([self.pad])
     37 + t += self.version.to_bytes(2, byteorder='little', signed=False)
     38 + if self.version > 1:
     39 + t += self.PriorityCharge0.to_bytes(2, byteorder='little', signed=False)
     40 + t += self.PriorityCharge1.to_bytes(2, byteorder='little', signed=False)
     41 + t += self.PriorityCharge2.to_bytes(2, byteorder='little', signed=False)
     42 + t += self.PriorityCharge3.to_bytes(2, byteorder='little', signed=False)
     43 + return t
     44 +
     45 + def __str__(self):
     46 + t = ''
     47 + for k in self.__dict__:
     48 + t += '%s: %s\r\n' % (k, self.__dict__[k])
     49 + return t
     50 + 
     51 + 
     52 +class DYNVC_CAPS_RSP:
     53 + def __init__(self):
     54 + self.cbid:int = 0
     55 + self.sp:int = 0
     56 + self.cmd:DYNVC_CMD = DYNVC_CMD.CAPS_RSP
     57 + self.pad:int = 0
     58 + self.version:int = 1
     59 + 
     60 + @staticmethod
     61 + def from_bytes(data: bytes):
     62 + return DYNVC_CAPS_RSP.from_buffer(io.BytesIO(data))
     63 + 
     64 + @staticmethod
     65 + def from_buffer(buff: io.BytesIO):
     66 + msg = DYNVC_CAPS_RSP()
     67 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff)
     68 + msg.pad = buff.read(1)[0]
     69 + msg.version = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     70 + return msg
     71 + 
     72 + def to_bytes(self):
     73 + t = b''
     74 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd)
     75 + t += bytes([self.pad])
     76 + t += self.version.to_bytes(2, byteorder='little', signed=False)
     77 + return t
     78 +
     79 + def __str__(self):
     80 + t = ''
     81 + for k in self.__dict__:
     82 + t += '%s: %s\r\n' % (k, self.__dict__[k])
     83 + return t
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/close.py
     1 +import io
     2 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD, dynvc_header_to_bytes, dynvc_header_from_buff
     3 + 
     4 + 
     5 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/c02dfd21-ccbc-4254-985b-3ef6dd115dec
     6 +class DYNVC_CLOSE:
     7 + def __init__(self):
     8 + self.cbid:int = 3
     9 + self.sp:int = 0
     10 + self.cmd:DYNVC_CMD = DYNVC_CMD.CLOSE
     11 + self.ChannelId:int = None
     12 + 
     13 + @staticmethod
     14 + def from_bytes(data: bytes):
     15 + return DYNVC_CLOSE.from_buffer(io.BytesIO(data))
     16 + 
     17 + @staticmethod
     18 + def from_buffer(buff: io.BytesIO):
     19 + msg = DYNVC_CLOSE()
     20 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff, cbid_mod=True)
     21 + msg.ChannelId = int.from_bytes(buff.read(msg.cbid), byteorder='little', signed=False)
     22 + return msg
     23 + 
     24 + def to_bytes(self):
     25 + if self.cbid is None:
     26 + self.cbid = 4
     27 + t = b''
     28 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd, cbid_mod=True)
     29 + t += self.ChannelId.to_bytes(self.cbid, byteorder='little', signed=False)
     30 + return t
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/create.py
     1 +import io
     2 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD, dynvc_header_to_bytes, dynvc_header_from_buff
     3 + 
     4 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/4448ba4d-9a72-429f-8b65-6f4ec44f2985
     5 +class DYNVC_CREATE_REQ:
     6 + def __init__(self):
     7 + self.cbid:int = None
     8 + self.pri:int = 0
     9 + self.cmd:DYNVC_CMD = DYNVC_CMD.CREATE_RSP
     10 + self.ChannelId:int = 0
     11 + self.ChannelName:str = None
     12 + 
     13 + @staticmethod
     14 + def from_bytes(data: bytes):
     15 + return DYNVC_CREATE_REQ.from_buffer(io.BytesIO(data))
     16 + 
     17 + @staticmethod
     18 + def from_buffer(buff: io.BytesIO):
     19 + msg = DYNVC_CREATE_REQ()
     20 + msg.cbid, msg.pri, msg.cmd = dynvc_header_from_buff(buff, cbid_mod=True)
     21 + msg.ChannelId = int.from_bytes(buff.read(msg.cbid), byteorder='little', signed=False)
     22 + msg.ChannelName = ''
     23 + for _ in range(255):
     24 + t = buff.read(1)
     25 + if t == b'\x00':
     26 + break
     27 + msg.ChannelName += chr(ord(t))
     28 + return msg
     29 + 
     30 + def to_bytes(self):
     31 + if self.cbid is None:
     32 + self.cbid = 2
     33 + if self.ChannelName[:-1] != '\x00':
     34 + self.ChannelName += '\x00'
     35 +
     36 + t = b''
     37 + t += dynvc_header_to_bytes(self.cbid, self.pri, self.cmd, cbid_mod=True)
     38 + t += self.ChannelId.to_bytes(self.cbid, byteorder='little', signed=False)
     39 + t += self.ChannelName.encode()
     40 + return t
     41 +
     42 + def __str__(self):
     43 + t = ''
     44 + for k in self.__dict__:
     45 + t += '%s: %s\r\n' % (k, self.__dict__[k])
     46 + return t
     47 + 
     48 + 
     49 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/8f284ea3-54f3-4c24-8168-8a001c63b581
     50 +class DYNVC_CREATE_RSP:
     51 + def __init__(self):
     52 + self.cbid:int = None
     53 + self.sp:int = 0
     54 + self.cmd:DYNVC_CMD = DYNVC_CMD.CREATE_RSP
     55 + self.ChannelId:int = 0
     56 + self.CreationStatus:int = None # actually a HRESULT
     57 + 
     58 + @staticmethod
     59 + def from_bytes(data: bytes):
     60 + return DYNVC_CREATE_RSP.from_buffer(io.BytesIO(data))
     61 + 
     62 + @staticmethod
     63 + def from_buffer(buff: io.BytesIO):
     64 + msg = DYNVC_CREATE_RSP()
     65 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff, cbid_mod=True)
     66 + msg.ChannelId = int.from_bytes(buff.read(msg.cbid), byteorder='little', signed=False)
     67 + msg.CreationStatus = int.from_bytes(buff.read(4), byteorder='little', signed=False)
     68 + return msg
     69 + 
     70 + def to_bytes(self):
     71 + if self.cbid is None:
     72 + self.cbid = 1
     73 +
     74 + t = b''
     75 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd, cbid_mod=True)
     76 + t += self.ChannelId.to_bytes(self.cbid, byteorder='little', signed=False)
     77 + t += self.CreationStatus.to_bytes(4, byteorder='little', signed=False)
     78 + return t
     79 + 
     80 + def __str__(self):
     81 + t = ''
     82 + for k in self.__dict__:
     83 + t += '%s: %s\r\n' % (k, self.__dict__[k])
     84 + return t
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/data.py
     1 + 
     2 +import io
     3 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD, dynvc_header_to_bytes, dynvc_header_from_buff
     4 + 
     5 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/69377767-56a6-4ab8-996b-7758676e9261
     6 +class DYNVC_DATA_FIRST:
     7 + def __init__(self):
     8 + self.cbid:int = 4
     9 + self.len:int = 4
     10 + self.cmd:DYNVC_CMD = DYNVC_CMD.DATA_FIRST
     11 + self.ChannelId:int = 0
     12 + self.Length:int = None
     13 + self.Data:bytes = None
     14 + 
     15 + @staticmethod
     16 + def from_bytes(data: bytes):
     17 + return DYNVC_DATA_FIRST.from_buffer(io.BytesIO(data))
     18 + 
     19 + @staticmethod
     20 + def from_buffer(buff: io.BytesIO):
     21 + hdr_start = buff.tell()
     22 + msg = DYNVC_DATA_FIRST()
     23 + msg.cbid, msg.len, msg.cmd = dynvc_header_from_buff(buff, cbid_mod=True, sp_mod = True)
     24 + msg.ChannelId = int.from_bytes(buff.read(msg.cbid), byteorder='little', signed=False)
     25 + msg.Length = int.from_bytes(buff.read(msg.len), byteorder='little', signed=False)
     26 + hdr_end = buff.tell()
     27 + hdrsize = hdr_end - hdr_start
     28 + if hdrsize+msg.Length < 1600:
     29 + msg.Data = buff.read(msg.Length)
     30 + else:
     31 + msg.Data = buff.read(1600 - hdrsize)
     32 + 
     33 + return msg
     34 + 
     35 + def to_bytes(self):
     36 + t = b''
     37 + t += dynvc_header_to_bytes(self.cbid, self.len, self.cmd, cbid_mod=True, sp_mod = True)
     38 + t += self.ChannelId.to_bytes(self.cbid, byteorder='little', signed=False)
     39 + t += self.Length.to_bytes(self.len, byteorder='little', signed=False)
     40 + t += self.Data
     41 + return t
     42 + 
     43 + @staticmethod
     44 + def chunk_data(data, channel_id):
     45 + chunksize = 1600-9
     46 + if len(data) < chunksize:
     47 + msg = DYNVC_DATA()
     48 + msg.cbid = 4
     49 + msg.ChannelId = channel_id
     50 + msg.Data = data
     51 + yield msg.to_bytes()
     52 + else:
     53 + msg = DYNVC_DATA_FIRST()
     54 + msg.cbid = 4
     55 + msg.len = 4
     56 + msg.cmd = DYNVC_CMD.DATA_FIRST
     57 + msg.ChannelId = channel_id
     58 + msg.Data = data[:chunksize]
     59 + msg.Length = len(data)
     60 + yield msg.to_bytes()
     61 + 
     62 + i = chunksize
     63 + while i < len(data):
     64 + chunk = data[i:i+chunksize]
     65 + msg = DYNVC_DATA()
     66 + msg.cbid = 4
     67 + msg.ChannelId = channel_id
     68 + msg.Data = chunk
     69 + yield msg.to_bytes()
     70 + i += chunksize
     71 +
     72 + def __str__(self):
     73 + t = ''
     74 + for k in self.__dict__:
     75 + if k == 'Data' and len(self.Data) > 100:
     76 + res = 'Data: %s...(%s)\r\n' % (self.Data[50], len(self.Data))
     77 + else:
     78 + res = '%s: %s\r\n' % (k, self.__dict__[k])
     79 + t += res
     80 + return t
     81 + 
     82 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/15b59886-db44-47f1-8da3-47c8fcd82803
     83 +class DYNVC_DATA:
     84 + def __init__(self):
     85 + self.cbid:int = None
     86 + self.sp:int = 0
     87 + self.cmd:DYNVC_CMD = DYNVC_CMD.DATA
     88 + self.ChannelId:int = 0
     89 + self.Data:bytes = None
     90 + 
     91 + @staticmethod
     92 + def from_bytes(data: bytes):
     93 + return DYNVC_DATA.from_buffer(io.BytesIO(data))
     94 + 
     95 + @staticmethod
     96 + def from_buffer(buff: io.BytesIO):
     97 + hdr_start = buff.tell()
     98 + msg = DYNVC_DATA()
     99 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff, cbid_mod=True, sp_mod = True)
     100 + msg.ChannelId = int.from_bytes(buff.read(msg.cbid), byteorder='little', signed=False)
     101 + hdr_end = buff.tell()
     102 + hdrsize = hdr_end - hdr_start
     103 + msg.Data = buff.read(1600 - hdrsize)
     104 + return msg
     105 + 
     106 + def to_bytes(self):
     107 + t = b''
     108 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd, cbid_mod=True, sp_mod = True)
     109 + t += self.ChannelId.to_bytes(self.cbid, byteorder='little', signed=False)
     110 + t += self.Data
     111 + return t
     112 + 
     113 + 
     114 + def __str__(self):
     115 + t = ''
     116 + for k in self.__dict__:
     117 + if k == 'Data' and len(self.Data) > 100:
     118 + res = 'Data: %s...(%s)\r\n' % (self.Data[50], len(self.Data))
     119 + else:
     120 + res = '%s: %s\r\n' % (k, self.__dict__[k])
     121 + t += res
     122 + return t
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/protocol/softsync.py
     1 +import enum
     2 +import io
     3 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD, dynvc_header_to_bytes, dynvc_header_from_buff
     4 + 
     5 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/f82105dd-0abd-4126-a61b-41a7909e974f
     6 + 
     7 +class DYNVC_SOFT_SYNC_FLAG(enum.IntFlag):
     8 + TCP_FLUSHED = 0x01
     9 + CHANNEL_LIST_PRESENT = 0x02
     10 + 
     11 +class DYNVC_SOFT_SYNC_REQUEST:
     12 + def __init__(self):
     13 + self.cbid:int = None
     14 + self.sp:int = 0
     15 + self.cmd:DYNVC_CMD = DYNVC_CMD.SOFT_SYNC_REQUEST
     16 + self.Pad:int = 0
     17 + self.Length:int = None
     18 + self.Flags:DYNVC_SOFT_SYNC_FLAG = None
     19 + self.NumberOfTunnels:int = None
     20 + self.SoftSyncChannelLists:List[DYNVC_SOFT_SYNC_CHANNEL_LIST] = []
     21 + 
     22 + @staticmethod
     23 + def from_bytes(data: bytes):
     24 + return DYNVC_SOFT_SYNC_REQUEST.from_buffer(io.BytesIO(data))
     25 + 
     26 + @staticmethod
     27 + def from_buffer(buff: io.BytesIO):
     28 + msg = DYNVC_SOFT_SYNC_REQUEST()
     29 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff)
     30 + msg.Pad = buff.read(1)[0]
     31 + msg.Length = int.from_bytes(buff.read(4), byteorder='little', signed=False)
     32 + msg.Flags = DYNVC_SOFT_SYNC_FLAG(int.from_bytes(buff.read(2), byteorder='little', signed=False))
     33 + msg.NumberOfTunnels = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     34 + for _ in range(msg.NumberOfTunnels):
     35 + msg.SoftSyncChannelLists.append(DYNVC_SOFT_SYNC_CHANNEL_LIST.from_buffer(buff))
     36 + return msg
     37 + 
     38 + def to_bytes(self):
     39 + t = b''
     40 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd)
     41 + t += self.Pad.to_bytes(1, byteorder='little', signed=False)
     42 + t += self.Length.to_bytes(4, byteorder='little', signed=False)
     43 + t += self.Flags.to_bytes(2, byteorder='little', signed=False)
     44 + t += len(self.SoftSyncChannelLists).to_bytes(2, byteorder='little', signed=False)
     45 + for ch in self.SoftSyncChannelLists:
     46 + t += ch.to_bytes()
     47 + return t
     48 + 
     49 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/f82105dd-0abd-4126-a61b-41a7909e974f
     50 + 
     51 +from typing import List
     52 + 
     53 +class DYNVC_TUNNELTYPE(enum.Enum):
     54 + UDPFECR = 0x00000001 #RDP-UDP Forward Error Correction (FEC) multitransport tunnel ([MS-RDPEMT] section 1.3).
     55 + UDPFECL = 0x00000003 #RDP-UDP FEC lossy multitransport tunnel ([MS-RDPEMT] section 1.3).
     56 + 
     57 + 
     58 +class DYNVC_SOFT_SYNC_CHANNEL_LIST:
     59 + def __init__(self):
     60 + self.TunnelType:DYNVC_TUNNELTYPE = None
     61 + self.NumberOfDVCs:int = None
     62 + self.ListOfDVCIds:List[int] = []
     63 + 
     64 + @staticmethod
     65 + def from_bytes(data: bytes):
     66 + return DYNVC_SOFT_SYNC_CHANNEL_LIST.from_buffer(io.BytesIO(data))
     67 + 
     68 + @staticmethod
     69 + def from_buffer(buff: io.BytesIO):
     70 + msg = DYNVC_SOFT_SYNC_CHANNEL_LIST()
     71 + msg.TunnelType = DYNVC_TUNNELTYPE(int.from_bytes(buff.read(4), byteorder='little', signed=False))
     72 + msg.NumberOfDVCs = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     73 + for _ in range(msg.NumberOfDVCs):
     74 + msg.ListOfDVCIds.append(int.from_bytes(buff.read(4), byteorder='little', signed=False))
     75 + return msg
     76 + 
     77 + def to_bytes(self):
     78 + t = b''
     79 + t += self.TunnelType.value.to_bytes(4, byteorder='little', signed=False)
     80 + t += len(self.ListOfDVCIds).to_bytes(2, byteorder='little', signed=False)
     81 + for channelid in self.ListOfDVCIds:
     82 + t += channelid.to_bytes(4, byteorder='little', signed=False)
     83 + return t
     84 + 
     85 +# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedyc/7d120558-b35d-4b78-81e8-bac2cf081bd7
     86 +class DYNVC_SOFT_SYNC_RESPONSE:
     87 + def __init__(self):
     88 + self.cbid:int = None
     89 + self.sp:int = 0
     90 + self.cmd:DYNVC_CMD = DYNVC_CMD.SOFT_SYNC_RESPONSE
     91 + self.Pad:int = 0
     92 + self.NumberOfTunnels:int = None
     93 + self.TunnelsToSwitch:List[DYNVC_TUNNELTYPE] = []
     94 + 
     95 + @staticmethod
     96 + def from_bytes(data: bytes):
     97 + return DYNVC_SOFT_SYNC_RESPONSE.from_buffer(io.BytesIO(data))
     98 + 
     99 + @staticmethod
     100 + def from_buffer(buff: io.BytesIO):
     101 + msg = DYNVC_SOFT_SYNC_RESPONSE()
     102 + msg.cbid, msg.sp, msg.cmd = dynvc_header_from_buff(buff)
     103 + msg.Pad = buff.read(1)[0]
     104 + msg.NumberOfTunnels = int.from_bytes(buff.read(2), byteorder='little', signed=False)
     105 + for _ in range(msg.NumberOfTunnels):
     106 + msg.TunnelsToSwitch.append(DYNVC_TUNNELTYPE(int.from_bytes(buff.read(4), byteorder='little', signed=False)))
     107 + return msg
     108 + 
     109 + def to_bytes(self):
     110 + t = b''
     111 + t += dynvc_header_to_bytes(self.cbid, self.sp, self.cmd)
     112 + t += self.Pad.to_bytes(1, byteorder='little', signed=False)
     113 + t += len(self.NumberOfTunnels).to_bytes(2, byteorder='little', signed=False)
     114 + for ch in self.TunnelsToSwitch:
     115 + t += ch.value.to_bytes(4, byteorder='little', signed=False)
     116 + return t
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/vchannels/__init__.py
     1 +import asyncio
     2 + 
     3 +from aardwolf.extensions.RDPEDYC.protocol import DYNVC_CMD
     4 +from aardwolf.extensions.RDPEDYC.protocol.data import DYNVC_DATA_FIRST, DYNVC_DATA
     5 + 
     6 +class VirtualChannelBase:
     7 + def __init__(self, name:str, buffered = True):
     8 + self.channel_name:str = name
     9 + self.channel_id:int = None
     10 + self.channel_closed_evt:asyncio.Event = asyncio.Event()
     11 + self.__channel_data_buffered = buffered
     12 + self.__virtual_channel_manager = None
     13 + self.__fragment_buffer:bytes = b''
     14 + self.__current_data_length = -1
     15 + self.__send_lock = asyncio.Lock()
     16 + 
     17 + async def channel_init_internal(self, channelid:int, manager):
     18 + self.channel_id = channelid
     19 + self.__virtual_channel_manager = manager
     20 + return await self.channel_init()
     21 + 
     22 + async def channel_rawdata_in(self, msg:DYNVC_DATA_FIRST|DYNVC_DATA):
     23 + """
     24 + This method is called by the Channel Manager whenever (uncompressed) data is coming in.
     25 + The data might be fragmented, so this function serves to reconstruct the entire data that is
     26 + send by the server.
     27 + 
     28 + """
     29 + if self.__channel_data_buffered is False:
     30 + await self.channel_data_in(msg.Data)
     31 + return
     32 +
     33 + self.__fragment_buffer += msg.Data
     34 + if msg.cmd == DYNVC_CMD.DATA_FIRST:
     35 + self.__current_data_length = msg.Length
     36 + 
     37 + if len(self.__fragment_buffer) == self.__current_data_length or self.__current_data_length == -1:
     38 + await self.channel_data_in(self.__fragment_buffer)
     39 + self.__fragment_buffer = b''
     40 + self.__current_data_length = -1
     41 + #print('__current_data_length: %s' % self.__current_data_length)
     42 + #print('len(self.__fragment_buffer): %s' % len(self.__fragment_buffer))
     43 + 
     44 + async def channel_data_out(self, data:bytes):
     45 + async with self.__send_lock:
     46 + for datamsg in DYNVC_DATA_FIRST.chunk_data(data, self.channel_id):
     47 + #print('Sending reply data: %s' % datamsg)
     48 + await self.__virtual_channel_manager.fragment_and_send(datamsg)
     49 +
     50 + async def channel_closed(self):
     51 + """
     52 + The remote end closed the channel
     53 + """
     54 + self.channel_closed_evt.set()
     55 + 
     56 + async def close_channel(self):
     57 + """
     58 + Triggers the channel to be closed
     59 + """
     60 + await self.__virtual_channel_manager.close_channel(self.channel_id)
     61 + 
     62 + #### OVERRIDE THESE
     63 + async def channel_init(self):
     64 + """
     65 + This function is called when the channel is about to start.
     66 + YOU MUST return a Tuple[Bool, Exception] after finishing init.
     67 + If you return exception in the tuple, the channel will be discarded.
     68 + """
     69 + return True, None
     70 + 
     71 + async def channel_data_in(self, data:bytes):
     72 + """
     73 + This function will be called when data arrives on this channel.
     74 + If you used unbuffered channel, the data here will arrive in 1600 byte chunks
     75 + """
     76 + pass
     77 + 
     78 +
     79 + 
     80 +
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/vchannels/echo/__init__.py
     1 + 
     2 +from aardwolf.extensions.RDPEDYC.vchannels import VirtualChannelBase
     3 + 
     4 + 
     5 +class VchannelECHO(VirtualChannelBase):
     6 + def __init__(self):
     7 + VirtualChannelBase.__init__(self, 'ECHO')
     8 +
     9 + async def channel_init(self):
     10 + #print('ECHO channel started!')
     11 + return True, None
     12 + 
     13 + async def channel_data_in(self, data):
     14 + #print('ECHO channel data in: %s' % data)
     15 + await self.channel_data_out(data)
     16 +
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/vchannels/socksoverrdp/__init__.py
     1 +import io
     2 +import asyncio
     3 +from aardwolf.extensions.RDPEDYC.vchannels import VirtualChannelBase
     4 + 
     5 +# SOCKS over RDP vchannel
     6 +#
     7 +# This vchannel is compatible with the "SocksOverRDP" project written by Balazs Bucsay (@xoreipeip)
     8 +#
     9 +# To make use of this, you'll need to log in to the server and execute the "SocksOverRDP-Server" component
     10 +# from the following repo https://github.com/nccgroup/SocksOverRDP
     11 +# The server component doesn't require administrative privileges, however
     12 +# it will only run until your session is alive. You'll have to re-launch it every time you log in.
     13 +#
     14 +# Thank you for the cool project Balazs!
     15 + 
     16 +class SOCKSDataMessage:
     17 + def __init__(self, connid:int, iserror:bool, data:bytes):
     18 + self.connid = connid
     19 + self.datalen = len(data)
     20 + self.iserror = iserror
     21 + self.data = data
     22 + 
     23 + def to_bytes(self):
     24 + t = self.connid.to_bytes(4, byteorder='little', signed=False)
     25 + t += self.datalen.to_bytes(4, byteorder='little', signed=False)
     26 + t += bytes([int(self.iserror)])
     27 + t += self.data
     28 + return t
     29 + 
     30 + @staticmethod
     31 + def from_bytes(data:bytes):
     32 + return SOCKSDataMessage.from_buffer(io.BytesIO(data))
     33 +
     34 + @staticmethod
     35 + def from_buffer(buff:io.BytesIO):
     36 + connid = int.from_bytes(buff.read(4), byteorder='little', signed=False)
     37 + datalen = int.from_bytes(buff.read(4), byteorder='little', signed=False)
     38 + iserror = bool(buff.read(1)[0])
     39 + data = buff.read(datalen)
     40 + return SOCKSDataMessage(connid, iserror, data)
     41 +
     42 + def __str__(self):
     43 + t = ''
     44 + for k in self.__dict__:
     45 + t += '%s: %s\r\n' % (k, self.__dict__[k])
     46 + return k
     47 + 
     48 +class SocksOverRDPChannel(VirtualChannelBase):
     49 + def __init__(self, channelname, listen_ip:str, listen_port:int):
     50 + VirtualChannelBase.__init__(self, channelname)
     51 + self.listen_ip = listen_ip
     52 + self.listen_port = listen_port
     53 + self.__server = None
     54 + self.__connection_id = 1
     55 + self.__connections = {}
     56 + 
     57 + def get_connection_id(self):
     58 + t = self.__connection_id
     59 + self.__connection_id += 1
     60 + return t
     61 + 
     62 + async def __handle_tcp_client(self, reader:asyncio.StreamReader, writer:asyncio.StreamWriter):
     63 + try:
     64 + #print('Client connected!')
     65 + connid = self.get_connection_id()
     66 + self.__connections[connid] = writer
     67 + while True:
     68 + data = await reader.read(1590)
     69 + if data == b'':
     70 + #print('Client disconnected!')
     71 + return
     72 + msg = SOCKSDataMessage(connid, False, data)
     73 + await self.channel_data_out(msg.to_bytes())
     74 + 
     75 + except Exception as e:
     76 + print('Error! %s' % e)
     77 + return
     78 + finally:
     79 + msg = SOCKSDataMessage(connid, True, b'AAAA')
     80 + await self.channel_data_out(msg.to_bytes())
     81 + 
     82 + async def channel_init(self):
     83 + #print('Channel init called!')
     84 + self.__server = await asyncio.start_server(self.__handle_tcp_client, self.listen_ip, self.listen_port)
     85 + print('SOCKS server listening on %s:%s' % (self.listen_ip, self.listen_port))
     86 + return True, None
     87 + 
     88 + async def channel_data_in(self, data:bytes):
     89 + try:
     90 + msg = SOCKSDataMessage.from_bytes(data)
     91 + if msg.connid not in self.__connections:
     92 + print('MSG recieved for unknown connid "%s"' % msg.connid)
     93 + return
     94 + if msg.iserror is False:
     95 + self.__connections[msg.connid].write(msg.data)
     96 + await self.__connections[msg.connid].drain()
     97 + 
     98 + else:
     99 + print('Remote end terminated the connection!')
     100 + self.__connections[msg.connid].close()
     101 + del self.__connections[msg.connid]
     102 + except Exception as e:
     103 + print('Error! %s' % e)
     104 + return
     105 + 
     106 + async def channel_closed(self):
     107 + for connid in self.__connections:
     108 + self.__connections[connid].close()
     109 + self.__server.close()
     110 + 
     111 + 
  • ■ ■ ■ ■ ■ ■
    aardwolf/extensions/RDPEDYC/vchannels/test/__init__.py
     1 + 
     2 +from aardwolf.extensions.RDPEDYC.vchannels import VirtualChannelBase
     3 +import asyncio
     4 + 
     5 + 
     6 +class VchannelTEST(VirtualChannelBase):
     7 + def __init__(self):
     8 + VirtualChannelBase.__init__(self, 'DATATEST1')
     9 +
     10 + async def channel_init(self):
     11 + print('DATATEST1 channel started!')
     12 + return True, None
     13 + 
     14 + async def channel_data_in(self, data):
     15 + print('DATATEST1 channel data in: %s' % data)
     16 + #await self.channel_data_out(data)
     17 + 
     18 + await asyncio.sleep(5)
     19 + await self.channel_data_out(b'AB'*5000)
Please wait...
Page is in error, reload to recover