mirror of
https://github.com/SantaSpeen/winConnect.git
synced 2026-02-16 02:20:58 +00:00
[!] Change semantic
[!] Chane base class for support other connectors
This commit is contained in:
@@ -7,14 +7,13 @@ import zlib
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import ormsgpack
|
import ormsgpack
|
||||||
import pywintypes
|
|
||||||
import win32file
|
|
||||||
|
|
||||||
from winConnect.crypto.WinConnectCrypto import WinConnectCrypto
|
from .. import exceptions
|
||||||
from winConnect.crypto.crypto_classes import WinConnectCryptoNone
|
from ..crypto.WinConnectCrypto import WinConnectCrypto
|
||||||
from winConnect.errors import WinConnectErrors, WinConnectError
|
from ..crypto.crypto_classes import WinConnectCryptoNone
|
||||||
from winConnect import exceptions
|
from ..errors import WinConnectErrors, WinConnectError
|
||||||
from winConnect.utils import SimpleConvertor
|
from ..utils import SimpleConvertor
|
||||||
|
|
||||||
|
|
||||||
# header: len(data) in struct.pack via header_format
|
# header: len(data) in struct.pack via header_format
|
||||||
# data: action:data
|
# data: action:data
|
||||||
@@ -30,23 +29,25 @@ class WinConnectBase:
|
|||||||
|
|
||||||
ormsgpack_options = ormsgpack.OPT_NON_STR_KEYS | ormsgpack.OPT_NAIVE_UTC | ormsgpack.OPT_PASSTHROUGH_TUPLE # ormsgpack options
|
ormsgpack_options = ormsgpack.OPT_NON_STR_KEYS | ormsgpack.OPT_NAIVE_UTC | ormsgpack.OPT_PASSTHROUGH_TUPLE # ormsgpack options
|
||||||
|
|
||||||
def __init__(self, pipe_name: str):
|
def __init__(self):
|
||||||
self._log = logging.getLogger(f"WinConnect:{pipe_name}")
|
self._log = logging.getLogger(f"WinConnectBase")
|
||||||
|
self._log_prefix = "WinConnectBase"
|
||||||
# versions:
|
# versions:
|
||||||
# 1 - 0.9.1
|
# 1 - 0.9.1
|
||||||
# 2 - 0.9.2 (with crypto)
|
# 2 - 0.9.2 (with crypto)
|
||||||
# 3 - 0.9.3+ (with crypto+salt)
|
# 3 - 0.9.3+ (with crypto+salt)
|
||||||
self._version = 3
|
self._version = 3
|
||||||
self._pipe_name = r'\\.\pipe\{}'.format(pipe_name)
|
|
||||||
self._pipe = None
|
self._sock = None
|
||||||
|
|
||||||
self._opened = False
|
self._opened = False
|
||||||
|
self._connected = False
|
||||||
|
self._inited = False
|
||||||
|
|
||||||
self._header_format = self.init_header_format
|
self._header_format = self.init_header_format
|
||||||
self._header_size = struct.calcsize(self._header_format) # bytes
|
self._header_size = struct.calcsize(self._header_format) # bytes
|
||||||
self._calc_body_max_size()
|
self._calc_body_max_size()
|
||||||
|
|
||||||
self._connected = False
|
|
||||||
self._inited = False
|
|
||||||
self._session_encoding = self.init_encoding
|
self._session_encoding = self.init_encoding
|
||||||
|
|
||||||
self._crypto = WinConnectCrypto()
|
self._crypto = WinConnectCrypto()
|
||||||
@@ -64,7 +65,7 @@ class WinConnectBase:
|
|||||||
raise exceptions.WinConnectCryptoException("Crypto failed test")
|
raise exceptions.WinConnectCryptoException("Crypto failed test")
|
||||||
|
|
||||||
def set_logger(self, logger):
|
def set_logger(self, logger):
|
||||||
logger.debug(f"[{self._pipe_name}] Update logger")
|
logger.debug(f"[{self._log_prefix}] Update logger")
|
||||||
self._log = logger
|
self._log = logger
|
||||||
self._crypto.set_logger(logger)
|
self._crypto.set_logger(logger)
|
||||||
|
|
||||||
@@ -87,10 +88,6 @@ class WinConnectBase:
|
|||||||
except struct.error as e:
|
except struct.error as e:
|
||||||
raise exceptions.WinConnectStructFormatException(f"Error in struct format. ({e})")
|
raise exceptions.WinConnectStructFormatException(f"Error in struct format. ({e})")
|
||||||
|
|
||||||
@property
|
|
||||||
def pipe_name(self):
|
|
||||||
return self._pipe_name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def encoding(self):
|
def encoding(self):
|
||||||
if not self._inited:
|
if not self._inited:
|
||||||
@@ -111,7 +108,7 @@ class WinConnectBase:
|
|||||||
def __parse_message(message: bytes):
|
def __parse_message(message: bytes):
|
||||||
return message.split(b":", 1)
|
return message.split(b":", 1)
|
||||||
|
|
||||||
def _open_pipe(self): ...
|
def _open_sock(self): ...
|
||||||
|
|
||||||
def _wait_connect(self): ...
|
def _wait_connect(self): ...
|
||||||
|
|
||||||
@@ -139,17 +136,7 @@ class WinConnectBase:
|
|||||||
raise exceptions.WinConnectBadDataTypeException('Is client using correct lib? Unknown data type')
|
raise exceptions.WinConnectBadDataTypeException('Is client using correct lib? Unknown data type')
|
||||||
return action, ready_data
|
return action, ready_data
|
||||||
|
|
||||||
def __raw_read(self, size):
|
def __raw_read(self, size) -> bytes: ...
|
||||||
with self._pipe_lock:
|
|
||||||
try:
|
|
||||||
_, data = win32file.ReadFile(self._pipe, size)
|
|
||||||
return data
|
|
||||||
except pywintypes.error as e:
|
|
||||||
if e.winerror == 109:
|
|
||||||
exc = exceptions.WinConnectConnectionClosedException("Connection closed")
|
|
||||||
exc.real_exc = e
|
|
||||||
raise exc
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def __read_and_decrypt(self, size):
|
def __read_and_decrypt(self, size):
|
||||||
data = self.__raw_read(size)
|
data = self.__raw_read(size)
|
||||||
@@ -177,14 +164,10 @@ class WinConnectBase:
|
|||||||
# Read body
|
# Read body
|
||||||
data = self.__read_and_decrypt(message_size)
|
data = self.__read_and_decrypt(message_size)
|
||||||
action, data = self.__handle_receive_data_type(data)
|
action, data = self.__handle_receive_data_type(data)
|
||||||
self._log.debug(f"[{self._pipe_name}] Received message: {action=} {data=}")
|
self._log.debug(f"[{self._log_prefix}] Received message: {action=} {data=}")
|
||||||
return action, data
|
return action, data
|
||||||
|
|
||||||
def __raw_write(self, packet):
|
def __raw_write(self, packet): ...
|
||||||
with self._pipe_lock:
|
|
||||||
if self.closed:
|
|
||||||
raise exceptions.WinConnectSessionClosedException("Session is closed")
|
|
||||||
win32file.WriteFile(self._pipe, packet)
|
|
||||||
|
|
||||||
def _send_message(self, action: str, data: Any):
|
def _send_message(self, action: str, data: Any):
|
||||||
with self._write_lock:
|
with self._write_lock:
|
||||||
@@ -197,7 +180,7 @@ class WinConnectBase:
|
|||||||
if message_size > self._body_max_size:
|
if message_size > self._body_max_size:
|
||||||
raise exceptions.WinConnectBaseException('Message is too big')
|
raise exceptions.WinConnectBaseException('Message is too big')
|
||||||
|
|
||||||
self._log.debug(f"[{self._pipe_name}] Sending message: {action=} {data=}; {message_size} {packed_data=}")
|
self._log.debug(f"[{self._log_prefix}] Sending message: {action=} {data=}; {message_size} {packed_data=}")
|
||||||
# Send header
|
# Send header
|
||||||
self.__raw_write(struct.pack(self.__header_settings[0], message_size))
|
self.__raw_write(struct.pack(self.__header_settings[0], message_size))
|
||||||
# Send body
|
# Send body
|
||||||
@@ -208,7 +191,7 @@ class WinConnectBase:
|
|||||||
self._send_message("err", e)
|
self._send_message("err", e)
|
||||||
|
|
||||||
def __read_chunked_message(self, data_info: bytes):
|
def __read_chunked_message(self, data_info: bytes):
|
||||||
self._log.debug(f"[{self._pipe_name}] Receive long message. Reading in chunks...")
|
self._log.debug(f"[{self._log_prefix}] Receive long message. Reading in chunks...")
|
||||||
chunk_size = self._body_max_size - 32
|
chunk_size = self._body_max_size - 32
|
||||||
cdata_sha256, cdata_len = data_info[:32], int(data_info[32:])
|
cdata_sha256, cdata_len = data_info[:32], int(data_info[32:])
|
||||||
if cdata_len > self.read_max_buffer:
|
if cdata_len > self.read_max_buffer:
|
||||||
@@ -226,7 +209,7 @@ class WinConnectBase:
|
|||||||
return zlib.decompress(_buffer)
|
return zlib.decompress(_buffer)
|
||||||
|
|
||||||
def __send_chunked_message(self, data: bytes):
|
def __send_chunked_message(self, data: bytes):
|
||||||
self._log.debug(f"[{self._pipe_name}] Long message. Sending in chunks...")
|
self._log.debug(f"[{self._log_prefix}] Long message. Sending in chunks...")
|
||||||
chunk_size = self._body_max_size - 32
|
chunk_size = self._body_max_size - 32
|
||||||
cdata = zlib.compress(data)
|
cdata = zlib.compress(data)
|
||||||
|
|
||||||
@@ -269,7 +252,7 @@ class WinConnectBase:
|
|||||||
command, data = self.__parse_message(data)
|
command, data = self.__parse_message(data)
|
||||||
match command:
|
match command:
|
||||||
case b'get_session_settings':
|
case b'get_session_settings':
|
||||||
self._log.debug(f"[{self._pipe_name}] Received get_session_settings from {data}")
|
self._log.debug(f"[{self._log_prefix}] Received get_session_settings from {data}")
|
||||||
_blank_settings['version'] = self._version
|
_blank_settings['version'] = self._version
|
||||||
_blank_settings['encoding'] = self._session_encoding
|
_blank_settings['encoding'] = self._session_encoding
|
||||||
_blank_settings['header_size'] = self._header_size
|
_blank_settings['header_size'] = self._header_size
|
||||||
@@ -280,7 +263,7 @@ class WinConnectBase:
|
|||||||
self._send_message("cmd", session_settings)
|
self._send_message("cmd", session_settings)
|
||||||
return True
|
return True
|
||||||
case b'set_session_settings':
|
case b'set_session_settings':
|
||||||
self._log.debug(f"[{self._pipe_name}] Received session settings.")
|
self._log.debug(f"[{self._log_prefix}] Received session settings.")
|
||||||
len_salt, data_salt = self.__parse_message(data)
|
len_salt, data_salt = self.__parse_message(data)
|
||||||
len_salt = int(len_salt)
|
len_salt = int(len_salt)
|
||||||
if len_salt > 0:
|
if len_salt > 0:
|
||||||
@@ -289,7 +272,7 @@ class WinConnectBase:
|
|||||||
data, salt = data_salt, b''
|
data, salt = data_salt, b''
|
||||||
|
|
||||||
if salt != self._crypto.crypt_salt:
|
if salt != self._crypto.crypt_salt:
|
||||||
self._log.debug(f"[{self._pipe_name}] Updating salt")
|
self._log.debug(f"[{self._log_prefix}] Updating salt")
|
||||||
self._crypto.set_salt(salt)
|
self._crypto.set_salt(salt)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -353,8 +336,8 @@ class WinConnectBase:
|
|||||||
self._opened = False
|
self._opened = False
|
||||||
self._connected = False
|
self._connected = False
|
||||||
self._inited = False
|
self._inited = False
|
||||||
self._pipe = None
|
self._sock = None
|
||||||
self._log.debug(f"[{self._pipe_name}] Session closed")
|
self._log.debug(f"[{self._log_prefix}] Session closed")
|
||||||
|
|
||||||
def _read(self) -> Any:
|
def _read(self) -> Any:
|
||||||
if self.closed:
|
if self.closed:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from winConnect.connectors.WinConnectBase import WinConnectBase
|
from ._base_base import WinConnectBase
|
||||||
|
|
||||||
|
|
||||||
class WinConnectClient(WinConnectBase):
|
class WinConnectClient(WinConnectBase):
|
||||||
@@ -15,14 +15,14 @@ class WinConnectClient(WinConnectBase):
|
|||||||
if not self.closed:
|
if not self.closed:
|
||||||
self._send_message("cmd", b"close:")
|
self._send_message("cmd", b"close:")
|
||||||
|
|
||||||
def __check_pipe(self):
|
def __check_sock(self):
|
||||||
if not self._opened:
|
if not self._opened:
|
||||||
self._open_pipe()
|
self._open_sock()
|
||||||
if not self._inited:
|
if not self._inited:
|
||||||
self._init()
|
self._init()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.__check_pipe()
|
self.__check_sock()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
@@ -30,10 +30,10 @@ class WinConnectClient(WinConnectBase):
|
|||||||
|
|
||||||
def connect(self, program_name: str="NoName"):
|
def connect(self, program_name: str="NoName"):
|
||||||
"""Connect to server and initialize session"""
|
"""Connect to server and initialize session"""
|
||||||
self._open_pipe()
|
self._open_sock()
|
||||||
self._init(program_name)
|
self._init(program_name)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def read_pipe(self):
|
def read_pipe(self):
|
||||||
self.__check_pipe()
|
self.__check_sock()
|
||||||
return self._read()
|
return self._read()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import win32pipe
|
import win32pipe
|
||||||
|
|
||||||
from winConnect.connectors.WinConnectBase import WinConnectBase
|
from ._base_base import WinConnectBase
|
||||||
|
|
||||||
|
|
||||||
class WinConnectServer(WinConnectBase):
|
class WinConnectServer(WinConnectBase):
|
||||||
@@ -9,19 +9,15 @@ class WinConnectServer(WinConnectBase):
|
|||||||
super().__init__(pipe_name)
|
super().__init__(pipe_name)
|
||||||
self.run = True
|
self.run = True
|
||||||
|
|
||||||
def _open_pipe(self): ...
|
|
||||||
|
|
||||||
def _wait_connect(self): ...
|
|
||||||
|
|
||||||
def _close_session(self):
|
def _close_session(self):
|
||||||
self.run = False
|
self.run = False
|
||||||
|
|
||||||
def wait_client(self):
|
def wait_client(self):
|
||||||
if not self._opened:
|
if not self._opened:
|
||||||
self._open_pipe()
|
self._open_sock()
|
||||||
self._wait_connect()
|
self._wait_connect()
|
||||||
self._connected = True
|
self._connected = True
|
||||||
self._log.debug(f"[{self._pipe_name}] Client connected")
|
self._log.debug(f"[{self._log_prefix}] Client connected")
|
||||||
|
|
||||||
def read_pipe(self):
|
def read_pipe(self):
|
||||||
if not self._connected:
|
if not self._connected:
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
from .WinConnectClient import WinConnectPipeClient
|
from .client import WinConnectPipeClient
|
||||||
from .WinConnectServer import WinConnectPipeServer
|
from .server import WinConnectPipeServer
|
||||||
|
|||||||
@@ -4,20 +4,24 @@ import pywintypes
|
|||||||
import win32file
|
import win32file
|
||||||
|
|
||||||
from winConnect import exceptions
|
from winConnect import exceptions
|
||||||
from winConnect.connectors.WinConnectBase import WinConnectBase
|
from .._base_base import WinConnectBase
|
||||||
|
|
||||||
|
|
||||||
class WinConnectNamedPipe(WinConnectBase):
|
class WinConnectNamedPipe(WinConnectBase):
|
||||||
def __init__(self, pipe_name: str):
|
def __init__(self, pipe_name: str):
|
||||||
super().__init__(pipe_name)
|
super().__init__()
|
||||||
self._log = logging.getLogger(f"WinConnectNamedPipe:{pipe_name}")
|
self._log = logging.getLogger(f"WinConnectNamedPipe:{pipe_name}")
|
||||||
self._pipe_name = r'\\.\pipe\{}'.format(pipe_name)
|
self._pipe_name = r'\\.\pipe\{}'.format(pipe_name)
|
||||||
self._pipe = None
|
self._sock = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pipe_path(self):
|
||||||
|
return self._pipe_name
|
||||||
|
|
||||||
def __raw_read(self, size):
|
def __raw_read(self, size):
|
||||||
with self._pipe_lock:
|
with self._pipe_lock:
|
||||||
try:
|
try:
|
||||||
_, data = win32file.ReadFile(self._pipe, size)
|
_, data = win32file.ReadFile(self._sock, size)
|
||||||
return data
|
return data
|
||||||
except pywintypes.error as e:
|
except pywintypes.error as e:
|
||||||
if e.winerror == 109:
|
if e.winerror == 109:
|
||||||
@@ -30,7 +34,7 @@ class WinConnectNamedPipe(WinConnectBase):
|
|||||||
with self._pipe_lock:
|
with self._pipe_lock:
|
||||||
if self.closed:
|
if self.closed:
|
||||||
raise exceptions.WinConnectSessionClosedException("Session is closed")
|
raise exceptions.WinConnectSessionClosedException("Session is closed")
|
||||||
win32file.WriteFile(self._pipe, packet)
|
win32file.WriteFile(self._sock, packet)
|
||||||
|
|
||||||
def _close_pipe(self):
|
def _close_pipe(self):
|
||||||
win32file.CloseHandle(self._pipe)
|
win32file.CloseHandle(self._sock)
|
||||||
@@ -2,8 +2,8 @@ import pywintypes
|
|||||||
import win32file
|
import win32file
|
||||||
|
|
||||||
from winConnect.exceptions import WinConnectConnectionNoPipeException
|
from winConnect.exceptions import WinConnectConnectionNoPipeException
|
||||||
from ._WinConnectNamedPipe import WinConnectNamedPipe
|
from ._base import WinConnectNamedPipe
|
||||||
from ..WinConnectClient import WinConnectClient
|
from .._base_client import WinConnectClient
|
||||||
|
|
||||||
|
|
||||||
class WinConnectPipeClient(WinConnectNamedPipe, WinConnectClient):
|
class WinConnectPipeClient(WinConnectNamedPipe, WinConnectClient):
|
||||||
@@ -18,7 +18,7 @@ class WinConnectPipeClient(WinConnectNamedPipe, WinConnectClient):
|
|||||||
def __init__(self, pipe_name: str):
|
def __init__(self, pipe_name: str):
|
||||||
super().__init__(pipe_name)
|
super().__init__(pipe_name)
|
||||||
|
|
||||||
def _open_pipe(self):
|
def _open_sock(self):
|
||||||
try:
|
try:
|
||||||
self._pipe = win32file.CreateFile(
|
self._pipe = win32file.CreateFile(
|
||||||
self._pipe_name,
|
self._pipe_name,
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import win32pipe
|
import win32pipe
|
||||||
|
|
||||||
from ._WinConnectNamedPipe import WinConnectNamedPipe
|
from ._base import WinConnectNamedPipe
|
||||||
from ..WinConnectServer import WinConnectServer
|
from .._base_server import WinConnectServer
|
||||||
|
|
||||||
|
|
||||||
class WinConnectPipeServer(WinConnectNamedPipe, WinConnectServer):
|
class WinConnectPipeServer(WinConnectNamedPipe, WinConnectServer):
|
||||||
@@ -16,12 +16,12 @@ class WinConnectPipeServer(WinConnectNamedPipe, WinConnectServer):
|
|||||||
super().__init__(pipe_name)
|
super().__init__(pipe_name)
|
||||||
self.run = True
|
self.run = True
|
||||||
|
|
||||||
def _open_pipe(self):
|
def _open_sock(self):
|
||||||
pipe_nOutBufferSize, pipe_nInBufferSize = self._body_max_size+20, self._body_max_size+20
|
pipe_nOutBufferSize, pipe_nInBufferSize = self._body_max_size+20, self._body_max_size+20
|
||||||
self._log.debug(f"[{self._pipe_name}] Creating pipe. "
|
self._log.debug(f"[{self._pipe_name}] Creating pipe. "
|
||||||
f"Settings: {self.pipe_openMode=}, {self.pipe_pipeMode=}, {self.pipe_nMaxInstances=}, "
|
f"Settings: {self.pipe_openMode=}, {self.pipe_pipeMode=}, {self.pipe_nMaxInstances=}, "
|
||||||
f"{pipe_nOutBufferSize=}, {pipe_nInBufferSize=}, {self.pipe_nDefaultTimeOut=}, {self.pipe_sa=}")
|
f"{pipe_nOutBufferSize=}, {pipe_nInBufferSize=}, {self.pipe_nDefaultTimeOut=}, {self.pipe_sa=}")
|
||||||
self._pipe = win32pipe.CreateNamedPipe(
|
self._sock = win32pipe.CreateNamedPipe(
|
||||||
self._pipe_name,
|
self._pipe_name,
|
||||||
self.pipe_openMode,
|
self.pipe_openMode,
|
||||||
self.pipe_pipeMode,
|
self.pipe_pipeMode,
|
||||||
@@ -35,4 +35,4 @@ class WinConnectPipeServer(WinConnectNamedPipe, WinConnectServer):
|
|||||||
self._log.debug(f"[{self._pipe_name}] Pipe opened")
|
self._log.debug(f"[{self._pipe_name}] Pipe opened")
|
||||||
|
|
||||||
def _wait_connect(self):
|
def _wait_connect(self):
|
||||||
win32pipe.ConnectNamedPipe(self._pipe, None)
|
win32pipe.ConnectNamedPipe(self._sock, None)
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from ._WinConnectTCPSocket import WinConnectTPC
|
|
||||||
from ..WinConnectClient import WinConnectClient
|
|
||||||
|
|
||||||
|
|
||||||
class WinConnectTPCClient(WinConnectTPC, WinConnectClient):
|
|
||||||
pass
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from ._WinConnectTCPSocket import WinConnectTPC
|
|
||||||
from ..WinConnectServer import WinConnectServer
|
|
||||||
|
|
||||||
|
|
||||||
class WinConnectPipeServer(WinConnectTPC, WinConnectServer):
|
|
||||||
pass
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
from winConnect.connectors.WinConnectBase import WinConnectBase
|
|
||||||
|
|
||||||
|
|
||||||
class WinConnectTPC(WinConnectBase):
|
|
||||||
pass
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
from .server import WinConnectTCPServer
|
||||||
|
from .client import WinConnectTPCClient
|
||||||
|
|||||||
5
winConnect/connectors/socket/_base.py
Normal file
5
winConnect/connectors/socket/_base.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .._base_base import WinConnectBase
|
||||||
|
|
||||||
|
|
||||||
|
class WinConnectTPC(WinConnectBase):
|
||||||
|
pass
|
||||||
6
winConnect/connectors/socket/client.py
Normal file
6
winConnect/connectors/socket/client.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from ._base import WinConnectTPC
|
||||||
|
from .._base_client import WinConnectClient
|
||||||
|
|
||||||
|
|
||||||
|
class WinConnectTPCClient(WinConnectTPC, WinConnectClient):
|
||||||
|
pass
|
||||||
6
winConnect/connectors/socket/server.py
Normal file
6
winConnect/connectors/socket/server.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from ._base import WinConnectTPC
|
||||||
|
from .._base_server import WinConnectServer
|
||||||
|
|
||||||
|
|
||||||
|
class WinConnectTCPServer(WinConnectTPC, WinConnectServer):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user