Example #1
0
    def __init__(self, loop, sock, addr, dns_resolver):
        cfg = LocalConfigManager.get_config()

        self._encryptor = Encryptor(cfg['password'], cfg['method'])
        self._loop = loop
        self._client = Client(sock, addr, loop, self._encryptor)
        self._remote = None
        self._dns_resolver = dns_resolver
        self._last_active = time.time()
        self._raddr = cfg['remote_address']
        self._laddr = cfg['local_address']
        self._saddr = None
Example #2
0
class LocalTransfer(object):
    """
    client <==> local <==> remote
    """

    def __init__(self, loop, sock, addr, dns_resolver):
        cfg = LocalConfigManager.get_config()

        self._encryptor = Encryptor(cfg['password'], cfg['method'])
        self._loop = loop
        self._client = Client(sock, addr, loop, self._encryptor)
        self._remote = None
        self._dns_resolver = dns_resolver
        self._last_active = time.time()
        self._raddr = cfg['remote_address']
        self._laddr = cfg['local_address']
        self._saddr = None

    @property
    def closed(self):
        return self._client is None

    @property
    def last_active(self):
        return self._last_active

    @property
    def display_name(self):
        client = '%s:%s' % (self._client.ipaddr, self._client.port)
        if self._saddr is not None:
            server = '%s:%s' % (self._saddr.hostname,
                                self._saddr.port)
        else:
            server = 'None'
        return '%s <==> %s' % (client, server)

    def start(self):
        self._client.start(MainLoop.EVENT_READ, self.handle_event)

    def handle_event(self, sock, event):
        if self.closed:
            return
        self._last_active = time.time()
        if sock == self._client.socket:
            if event & MainLoop.EVENT_ERROR:
                self.stop(warning='client %s:%s error' %
                          (self._client.ipaddr, self._client.port))
                return
            self._handle_client(event)
        elif sock == self._remote.socket:
            if event & MainLoop.EVENT_ERROR:
                self.stop(warning='remote %s:%s error' %
                          (self._remote.ipaddr, self._remote.port))
                return
            self._handle_remote(event)

    @stop_transfer_if_fail
    def _handle_client(self, event):
        """handle the client events"""
        data = None
        if event & MainLoop.EVENT_READ:
            data = self._client.read()
            if data == b'':
                self.stop(info='%s closed by client' % self.display_name)
                return

        if self._client.state == ClientState.UDP_ASSOC:
            return
        elif self._client.state in (ClientState.INIT, ClientState.ADDR)\
                and not data:
            """in state INIT or ADDR, supposed to receive data from client"""
            self.stop(info='client %s:%s closed' %
                      (self._client.ipaddr, self._client.port))
            return

        if self._client.state == ClientState.INIT:
            """
            receive HELLO message from client, shall we verify it ?
            send a HELLO back
            """
            self._client.write(b'\x05\00')  # HELLO
            self._client.state = ClientState.ADDR
        elif self._client.state == ClientState.ADDR:
            """
            receive the server addr,
            give client a feedback and connect to remote
            """
            vsn, cmd, atype, server_addr, server_port = parse_tcp_request(data)
            if cmd == SOCKS5Command.UDP_ASSOCIATE:
                DEBUG('UDP associate')
                self._client.write(build_tcp_reply(5, 0, 0,
                                                   self._client.ipaddr,
                                                   self._client.port))
                self._client.state = ClientState.UDP_ASSOC
                # just wait for the client to disconnect
                return
            elif cmd != SOCKS5Command.CONNECT:
                raise UnknownCommandException(cmd)

            server_addr = tostr(server_addr)
            INFO('connecting %s:%d from %s:%d' %
                 (server_addr, server_port, self._client.ipaddr,
                  self._client.port))
            self._saddr = Address(server_addr, server_port)
            # forward address to remote
            self._client.write(build_tcp_reply(5, 0, 0, self._laddr.ipaddr,
                                               self._laddr.port))
            self._client.state = ClientState.DNS
            self._remote = Remote(None, self._raddr, self._loop,
                                  self._encryptor)
            self._remote.write(data[3:])
            if self._raddr.ipaddr:     # ipaddr
                self._connect_to_remote()
            else:
                self._dns_resolver.resolve(self._raddr.hostname,
                                           self._dns_resolved)
        elif data and self._remote:
            self._remote.write(data)
        if event & MainLoop.EVENT_WRITE:
            """some data unsent"""
            self._client.write()

    @stop_transfer_if_fail
    def _handle_remote(self, event):
        """handle remote events"""
        if event & MainLoop.EVENT_READ:
            data = self._remote.read()
            if data == b'':
                self.stop(info='%s closed by remote' % self.display_name)
                return
            self._client.write(data)

        if event & MainLoop.EVENT_WRITE:
            self._remote.write()
            self._client.state = ClientState.CONNECTED

    @stop_transfer_if_fail
    def _dns_resolved(self, result, error):
        """remote ip address is resolved"""
        if self._client is None:
            # ignore DNS resolve callback if transfer already closed
            return
        elif error:
            self.stop(warning=error)
            return
        self._raddr.ipaddr = result[1]
        self._connect_to_remote()

    def _connect_to_remote(self):
        ipaddr = self._raddr.ipaddr
        port = self._raddr.port
        self._remote.socket = socket.socket(ipaddr.family, socket.SOCK_STREAM,
                                            socket.SOL_TCP)
        self._remote.connect((ipaddr.compressed, port))
        self._remote.start(MainLoop.EVENT_READ | MainLoop.EVENT_WRITE,
                           self.handle_event)

    def stop(self, info=None, warning=None):
        """stop transfer"""
        if self._client is None:
            return
        if info:
            INFO(info)
        elif warning:
            WARN(warning)
        self._client.close()
        self._client = None
        if self._remote:
            self._remote.close()