Example #1
0
 async def __add_client(self, client_id, client_name, sock_stream):
     msg = f'ADD_CLIENT: {client_name}(CID:{client_id}) ... '
     if not validate_id_string(client_id, Protocol.CID_LENGTH):
         raise ProxyError(msg + f'[ERROR] (invalid client id provided.)')
     if client_id in self.__clients:
         if self.__clients[client_id].status == RunningStatus.STOPPED:
             del self.__clients[client_id]
         else:
             raise ProxyError(msg + f'[ERROR] (client id already registered.)')
     self.__clients[client_id] = _PeerClient(self.server_host, sock_stream, client_id, client_name)
     log.success(msg + '[OK]')
Example #2
0
    def execute(self, loop, api_name, *args, timeout=None):
        """
        客户端及其代理端口的操作接口。以一般调用的方式执行协程函数功能。
        :param loop:
        :param api_name:
        :param args:
        :param timeout:
        :return:
        """
        tcp_api_list = (self.AsyncApi.PAUSE_TCP_MAP, self.AsyncApi.RESUME_TCP_MAP, self.AsyncApi.REMOVE_TCP_MAP)
        udp_api_list = (self.AsyncApi.PAUSE_UDP_MAP, self.AsyncApi.RESUME_UDP_MAP, self.AsyncApi.REMOVE_UDP_MAP)
        client_api_list = (self.AsyncApi.PAUSE_CLIENT, self.AsyncApi.RESUME_CLIENT)
        client_api_list2 = (self.AsyncApi.REMOVE_CLIENT,)
        server_api_list = (self.AsyncApi.STARTUP_SERVER, self.AsyncApi.SHUTDOWN_SERVER)

        try:
            if api_name in tcp_api_list + udp_api_list:
                client_object = None
                server_port = args[0]
                for c in self.__clients:
                    if api_name in tcp_api_list and server_port in self.__clients[c].tcp_maps or \
                            api_name in udp_api_list and server_port in self.__clients[c].udp_maps:
                        client_object = self.__clients[c]
                        break
                if client_object is None:
                    raise ProxyError(f'proxy port{server_port} not found.')
                asyncio.run_coroutine_threadsafe(
                    methodcaller(api_name, server_port)(client_object), loop=loop
                ).result(timeout=timeout)
            elif api_name in client_api_list:
                client_id = args[0]
                if client_id not in self.__clients:
                    raise ProxyError(f'client with cid({client_id}) not found.')
                asyncio.run_coroutine_threadsafe(
                    methodcaller(api_name)(self.__clients[client_id]), loop=loop
                ).result(timeout=timeout)
            elif api_name in client_api_list2:
                client_id = args[0]
                if client_id not in self.__clients:
                    raise ProxyError(f'client with cid({client_id}) not found.')
                asyncio.run_coroutine_threadsafe(
                    methodcaller(api_name, client_id)(self), loop=loop
                ).result(timeout=timeout)
            elif api_name in server_api_list:
                asyncio.run_coroutine_threadsafe(
                    methodcaller(api_name)(self), loop=loop
                ).result(timeout=timeout)
            else:
                raise ProxyError(f'{api_name} not supported.')
        except ProxyError as e:
            raise ProxyError(f'Call of API({api_name}) failed as {e}')
Example #3
0
 async def add_udp_map(self, server_port, client_port):
     if server_port in self.udp_maps:
         raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'already registered.'))
     if not check_udp_port_available(server_port):
         raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'target port is in use.'))
     self.udp_maps[server_port] = {
         UdpMapInfo.CLIENT_PORT: client_port,
         UdpMapInfo.UDP_SOCKET: None,
         UdpMapInfo.SWITCH: True,
         UdpMapInfo.STATISTIC: [0, 0],
         UdpMapInfo.CREATE_TIME: datetime.utcnow(),
         UdpMapInfo.TASK_TRANSFER: asyncio.create_task(self.__udp_service_task(server_port)),
     }
     log.success(self._map_msg(server_port, client_port, 'OK'))
Example #4
0
 async def add_tcp_map(self, server_port, client_port):
     if server_port in self.tcp_maps:
         raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'already registered.'))
     if check_listening(self.server_host, server_port):
         raise ProxyError(self._map_msg(server_port, client_port, 'ERROR', f'target port is in use.'))
     self.tcp_maps[server_port] = {
         TcpMapInfo.CLIENT_PORT: client_port,
         TcpMapInfo.SWITCH: True,
         TcpMapInfo.STATISTIC: [0, 0],
         TcpMapInfo.CREATE_TIME: datetime.utcnow(),
         TcpMapInfo.CONN_POOL: {},
         TcpMapInfo.TASK_LISTEN: asyncio.create_task(self.__tcp_service_task(server_port)),
     }
     log.success(self._map_msg(server_port, client_port, 'OK'))
Example #5
0
 async def remove_udp_map(self, client_port):
     if client_port not in self.udp_maps:
         raise ProxyError(
             self._map_msg(client_port, None, 'ERROR', f'not registered.'))
     else:
         server_port = self.udp_maps[client_port][UdpMapInfo.SERVER_PORT]
     status, detail = (await self.__protocol.request(
         Protocol.ClientCommand.REMOVE_UDP_MAP,
         f'{self.udp_maps[client_port][UdpMapInfo.SERVER_PORT]}',
         Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1)
     if status != Protocol.Result.SUCCESS:
         raise ProxyError(self._map_msg(client_port, server_port, 'ERROR'),
                          detail)
     del self.udp_maps[client_port]
     log.success(self._map_msg(client_port, server_port, 'OK'))
Example #6
0
 async def remove_client(self, client_id):
     if client_id not in self.__clients:
         raise ProxyError(f'REMOVE_CLIENT: (None)(CID:{client_id}) ... [ERROR] (not registered.)')
     if self.__clients[client_id].status != RunningStatus.STOPPED:
         await self.__clients[client_id].close_client(wait=True)
     log.success(f'REMOVE_CLIENT: {self.__clients[client_id].client_name}(CID:{client_id}) ... [OK]')
     del self.__clients[client_id]
Example #7
0
 async def remove_udp_map(self, server_port):
     if server_port not in self.udp_maps:
         raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.'))
     await self.pause_udp_map(server_port)
     self.udp_maps[server_port][UdpMapInfo.UDP_SOCKET].close()
     self.udp_maps[server_port][UdpMapInfo.TASK_TRANSFER].cancel()
     log.success(self._map_msg(server_port, self.udp_maps[server_port][UdpMapInfo.CLIENT_PORT], 'OK'))
     del self.udp_maps[server_port]
Example #8
0
 async def remove_tcp_map(self, server_port):
     if server_port not in self.tcp_maps:
         raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.'))
     await self.pause_tcp_map(server_port)
     for stream in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]:
         stream[1].close()
     self.tcp_maps[server_port][TcpMapInfo.TASK_LISTEN].cancel()
     log.success(self._map_msg(server_port, self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT], 'OK'))
     del self.tcp_maps[server_port]
Example #9
0
 async def add_tcp_map(self, client_port, server_port):
     if client_port in self.tcp_maps:
         raise ProxyError(
             self._map_msg(client_port, server_port, 'ERROR',
                           f'already registered.'))
     status, detail = (await self.__protocol.request(
         Protocol.ClientCommand.ADD_TCP_MAP,
         f'{client_port}{Protocol.PARAM_SEPARATOR}{server_port}',
         Protocol.CONNECTION_TIMEOUT)).split(Protocol.PARAM_SEPARATOR, 1)
     if status != Protocol.Result.SUCCESS:
         raise ProxyError(
             self._map_msg(client_port, server_port, 'ERROR', detail))
     self.tcp_maps[client_port] = {
         TcpMapInfo.SERVER_PORT: server_port,
         TcpMapInfo.SWITCH: True,
         TcpMapInfo.STATISTIC: [0, 0],
         TcpMapInfo.CREATE_TIME: datetime.utcnow(),
     }
     log.success(self._map_msg(client_port, server_port, 'OK'))
Example #10
0
    async def __serve_request_task(self):
        while True:
            uuid, cmd, data = await self.__protocol.get_request(timeout=None)
            result = Protocol.PARAM_SEPARATOR.join(
                (Protocol.Result.SUCCESS, ''))
            try:
                if cmd == Protocol.Command.PING:
                    result += datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')

                elif cmd == Protocol.ServerCommand.ADD_TCP_CONNECTION:
                    client_port = int(data)
                    server_port = self.tcp_maps[client_port][
                        TcpMapInfo.SERVER_PORT]
                    reader1, writer1 = await asyncio.open_connection(
                        '127.0.0.1', client_port)
                    reader2, writer2 = await asyncio.open_connection(
                        self.server_host, self.server_port)
                    conn_uuid = uuid4().hex
                    identify = Protocol.PARAM_SEPARATOR.join(
                        (Protocol.ConnectionType.PROXY_TCP_DATA,
                         self.client_id, str(server_port), conn_uuid))
                    writer2.write((identify + '\n').encode())
                    await writer2.drain()
                    resp = (await reader2.readline()).decode().strip()
                    if not resp or resp[:len(Protocol.Result.SUCCESS
                                             )] != Protocol.Result.SUCCESS:
                        raise ProxyError(
                            'Add tcp connection failed while connecting to server.'
                        )
                    asyncio.create_task(
                        self.__tcp_data_relay_task(client_port,
                                                   (reader1, writer1),
                                                   (reader2, writer2)))
                    result += conn_uuid

                else:
                    log.warning(
                        f'Unrecognised request from Server: {Protocol.make_req(uuid, cmd, data)}'
                    )
                    result = Protocol.PARAM_SEPARATOR.join(
                        (Protocol.Result.INVALID, 'unrecognised request'))

            except Exception as e:
                log.error(
                    f'Error while processing request({Protocol.make_req(uuid, cmd, data)}): {e}'
                )
                result = Protocol.PARAM_SEPARATOR.join(
                    (Protocol.Result.ERROR, str(e)))
            finally:
                try:
                    await self.__protocol.send_response(uuid, cmd, result)
                except Exception as e:
                    log.error(e)
                    break
Example #11
0
    async def __process_conn_task(self):
        while True:
            try:
                server_port, reader, writer = await self.__conn_queue.get()
            except Exception as e:
                log.error(e)
                break
            else:
                self.__conn_queue.task_done()

            try:
                client_port = self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT]

                result = await self.__protocol.request(
                    Protocol.ServerCommand.ADD_TCP_CONNECTION, client_port, timeout=Protocol.CONNECTION_TIMEOUT)

                status, detail = result.split(Protocol.PARAM_SEPARATOR, 1)
                if status == Protocol.Result.SUCCESS:
                    conn_uuid = detail
                else:
                    raise ProxyError(detail)

                wait_time = 0
                while wait_time < Protocol.CONNECTION_TIMEOUT:
                    if conn_uuid in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]:
                        break
                    await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD)
                    wait_time += Protocol.TASK_SCHEDULE_PERIOD
                if conn_uuid not in self.tcp_maps[server_port][TcpMapInfo.CONN_POOL]:
                    raise ProxyError(f'{self.client_id}({client_port}) ----> Server({server_port}) ... [TIMEOUT].')

                reader2, writer2 = self.tcp_maps[server_port][TcpMapInfo.CONN_POOL][conn_uuid]
                del self.tcp_maps[server_port][TcpMapInfo.CONN_POOL][conn_uuid]
                asyncio.create_task(self.__tcp_data_relay_task(server_port, (reader, writer), (reader2, writer2)))

            except Exception as e:
                log.error(e)
                writer.close()
Example #12
0
    async def __dispatch_datagram_task(self):
        while True:
            try:
                data, address = self.__udp_socket.recvfrom(Protocol.SOCKET_BUFFER_SIZE)
            except BlockingIOError:
                await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD)
                continue
            except Exception as e:
                log.error(e)
                break

            try:
                try:
                    packet_info = Protocol.unpack_udp_packet(data, unpack_data=False)
                except Exception as e:
                    raise ProxyError(f'Protocol.unpack_udp_packet(): {e}')

                if packet_info[Protocol.UdpPacketInfo.TYPE] == Protocol.UdpPacketType.SYNC:
                    cid = packet_info['client_id']
                    timestamp = packet_info[Protocol.UdpPacketInfo.TIMESTAMP]
                    if cid not in self.__clients:
                        raise ProxyError(f'Received udp sync packet from client(CID:{cid}) which is not found.')
                    client = self.__clients[cid]
                    log.debug(f'Received udp sync packet from {client.client_name}(CID:{cid}) at {timestamp}')
                    client.udp_statistic(0, len(data))
                    client.udp_e_address = address
                    if client.status == RunningStatus.RUNNING:
                        sync_packet = Protocol.pack_udp_sync_packet(server_id=self.server_id)
                        try:
                            self.__udp_socket.sendto(sync_packet, address)
                        except IOError as e:
                            log.error(e)
                            break
                        client.udp_statistic(len(sync_packet), 0)
                elif packet_info[Protocol.UdpPacketInfo.TYPE] == Protocol.UdpPacketType.DATA:
                    server_port = packet_info[Protocol.UdpPacketInfo.SERVER_PORT]
                    user_address = packet_info[Protocol.UdpPacketInfo.USER_ADDRESS]
                    client = None
                    for cid in self.__clients:
                        if server_port in self.__clients[cid].udp_maps:
                            client = self.__clients[cid]
                            client.udp_statistic(0, Protocol.UDP_DATA_HEADER_LEN)
                            client.udp_maps[server_port][UdpMapInfo.STATISTIC][1] += \
                                len(data) - Protocol.UDP_DATA_HEADER_LEN
                            break
                    if not client:
                        raise ProxyError(f'Received udp data packet which is not owned by any client.')
                    if client.status == RunningStatus.RUNNING and client.udp_maps[server_port][UdpMapInfo.SWITCH]:
                        udp_socket = client.udp_maps[server_port][UdpMapInfo.UDP_SOCKET]
                        try:
                            udp_socket.sendto(data[Protocol.UDP_DATA_HEADER_LEN:], user_address)
                        except IOError as e:
                            log.error(e)
                            continue
                        log.debug(f'Forwards udp data packet from port({server_port}) to {user_address}')
                    else:
                        log.warning(f'Drops udp data packet on port({server_port}) as switch is turned off.')
                else:
                    raise ProxyError(f'Received udp packet from {address} with unknown type.')
            except Exception as e:
                log.debug(f'Received udp packet from: {address}, data:\n' + data.hex())
                log.warning(e)
Example #13
0
    async def __process_conn_task(self):

        async def stream_readline(stream_reader):
            return await stream_reader.readline()

        while True:
            try:
                reader, writer = await self.__conn_queue.get()
            except Exception as e:
                log.error(e)
                break
            else:
                self.__conn_queue.task_done()

            try:
                task = asyncio.create_task(stream_readline(reader))
                await asyncio.wait_for(task, timeout=Protocol.CONNECTION_TIMEOUT)
                identify = task.result().decode().strip()
                if not identify:
                    peer = writer.get_extra_info('peername')
                    raise ProxyError(f'Identify string is empty of tcp connection from {peer}')
                log.debug(identify)
            except Exception as e:
                log.error(e)
                writer.close()
                continue

            reject_reason = 'unknown error'
            try:
                conn_type, data = identify.split(Protocol.PARAM_SEPARATOR, 1)
                val_list = data.split(Protocol.PARAM_SEPARATOR)

                if conn_type == Protocol.ConnectionType.MANAGER:
                    reject_reason = 'role of MANAGER is not supported yet.'
                    raise NotImplementedError('Reject manager connection as ' + reject_reason)
                elif conn_type == Protocol.ConnectionType.PROXY_CLIENT:
                    client_id = val_list[0]
                    client_name = val_list[1]
                    if not client_id:  # generate new client id if it is empty
                        while True:
                            client_id = uuid4().hex[-Protocol.CID_LENGTH:].upper()
                            if client_id not in self.__clients:
                                break
                    try:
                        await self.__add_client(client_id, client_name, (reader, writer))
                    except Exception as e:
                        reject_reason = str(e)
                        raise
                    writer.write(f'{Protocol.Result.SUCCESS}{Protocol.PARAM_SEPARATOR}{client_id}\n'.encode())
                    await writer.drain()
                elif conn_type == Protocol.ConnectionType.PROXY_TCP_DATA:
                    client_id = val_list[0]
                    server_port = int(val_list[1])
                    conn_id = val_list[2]
                    if client_id not in self.__clients:
                        reject_reason = f'provided client_id({client_id}) not found.'
                        raise ProxyError('Reject reverse-data-connection (tcp) as ' + reject_reason)
                    else:
                        client = self.__clients[client_id]
                    if client.status != RunningStatus.RUNNING:
                        reject_reason = f'{client.client_name}(CID:{client.client_id}) is paused.'
                        raise ProxyError('Reject reverse-data-connection (tcp) as ' + reject_reason)
                    if not client.tcp_maps[server_port][TcpMapInfo.SWITCH]:
                        reject_reason = f'server_port({server_port}) is paused.'
                        raise ProxyError('Reject reverse-data-connection (tcp) as ' + reject_reason)
                    client.tcp_maps[server_port][TcpMapInfo.CONN_POOL][conn_id] = (reader, writer)
                    writer.write(f'{Protocol.Result.SUCCESS}{Protocol.PARAM_SEPARATOR}\n'.encode())
                    await writer.drain()
                else:
                    reject_reason = 'unrecognised connection type'
                    raise ProxyError(f'Reject connection on port({self.server_port}) with identify: {identify}')
            except Exception as e:
                log.error(e)
                writer.write(f'{Protocol.Result.ERROR}{Protocol.PARAM_SEPARATOR}{reject_reason}\n'.encode())
                await writer.drain()
                writer.close()
Example #14
0
    async def __udp_receive_task(self):
        def udp_send_data(__b_data, __udp_port, __user_address):
            key = hash((__udp_port, __user_address))
            if key in self.__udp_req_map:
                sock = self.__udp_req_map[key][2]
                self.__udp_req_map[key][3] = time.time()
            else:
                sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                sock.setblocking(False)
                self.__udp_req_map[key] = [
                    __udp_port, user_address, sock,
                    time.time()
                ]
            sock.sendto(__b_data, ('127.0.0.1', __udp_port))

        while True:
            try:
                data, address = self.__udp_socket.recvfrom(
                    Protocol.SOCKET_BUFFER_SIZE)
            except BlockingIOError:
                await asyncio.sleep(Protocol.TASK_SCHEDULE_PERIOD)
                continue
            except IOError as e:
                log.error(e)
                break

            try:
                # if address[0] != self.server_host:
                #    raise ProxyError(f'Received udp packet from {address} which is not the server.')
                try:
                    packet_info = Protocol.unpack_udp_packet(data,
                                                             unpack_data=False)
                except Exception as e:
                    raise ProxyError(f'Protocol.unpack_udp_packet(): {e}')
                if packet_info[Protocol.UdpPacketInfo.
                               TYPE] == Protocol.UdpPacketType.SYNC:
                    self.udp_statistic(0, len(data))
                    self.__udp_ping = time.time()
                    log.debug(
                        f'UDP_PING: {packet_info[Protocol.UdpPacketInfo.TIMESTAMP]}'
                    )
                elif packet_info[Protocol.UdpPacketInfo.
                                 TYPE] == Protocol.UdpPacketType.DATA:
                    self.udp_statistic(0, Protocol.UDP_DATA_HEADER_LEN)
                    self.udp_maps[UdpMapInfo.STATISTIC][1] += len(
                        data) - Protocol.UDP_DATA_HEADER_LEN
                    user_address = packet_info[
                        Protocol.UdpPacketInfo.USER_ADDRESS]
                    server_port = packet_info[
                        Protocol.UdpPacketInfo.SERVER_PORT]
                    client_port = None
                    for port in self.udp_maps:
                        if self.udp_maps[port][
                                UdpMapInfo.SERVER_PORT] == server_port:
                            client_port = port
                            break
                    if not client_port:
                        log.warning(
                            f'Received udp data packet on server port({server_port}) that not registered.'
                        )
                        continue
                    try:
                        udp_send_data(data[Protocol.UDP_DATA_HEADER_LEN:],
                                      client_port, user_address)
                    except IOError as e:
                        log.error(e)
                        continue
                    log.debug(
                        f'Received udp data packet from {user_address} on port({client_port})'
                    )
                else:
                    self.udp_statistic(0, len(data))
                    raise ProxyError(
                        f'Received udp packet from {address} with unknown type.'
                    )
            except Exception as e:
                log.debug(f'Received udp packet from: {address}, data:\n' +
                          data.hex())
                log.warning(e)
        self.__protocol.close()
Example #15
0
 async def resume_client(self):
     msg = f'RESUME_CLIENT: {self.client_name}(CID:{self.client_id}) ... '
     if self.status == RunningStatus.STOPPED:
         raise ProxyError(msg + '[ERROR] (already stopped.)')
     self.status = RunningStatus.RUNNING
     log.success(msg + '[OK]')
Example #16
0
 async def resume_udp_map(self, server_port):
     if server_port not in self.udp_maps:
         raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.'))
     self.udp_maps[server_port][UdpMapInfo.SWITCH] = True
     log.success(self._map_msg(server_port, self.udp_maps[server_port][UdpMapInfo.CLIENT_PORT], 'OK'))
Example #17
0
 async def pause_tcp_map(self, server_port):
     if server_port not in self.tcp_maps:
         raise ProxyError(self._map_msg(server_port, None, 'ERROR', f'not registered.'))
     self.tcp_maps[server_port][TcpMapInfo.SWITCH] = False
     log.success(self._map_msg(server_port, self.tcp_maps[server_port][TcpMapInfo.CLIENT_PORT], 'OK'))