Example #1
0
 def _setup_transport(self):
     if self._transport is None:
         if self.__proxy is not None:
             self._transport = TCPClient(self.__proxy.host, self.__proxy.port)
         else:
             self._transport = TCPClient(self._host, self._port)
         self._http_parser = HTTPParser(self._transport)
         self._http_parser.connect("received", self._on_response_received)
         self._transport.connect("notify::status", self._on_status_change)
         self._transport.connect("error", self._on_error)
         self._transport.connect("sent", self._on_request_sent)
     
     if self._transport.get_property("status") != IoStatus.OPEN:
         self._transport.open()
Example #2
0
 def _setup_transport(self):
     if self._transport is None:
         if self._http_proxy:
             self._transport = TCPClient(self._http_proxy.host,
                     self._http_proxy.port)
         else:
             self._transport = TCPClient(self._host, self._port)
             if self._proxies:
                 self._transport = ProxyFactory(self._transport,
                         self._proxies, 'http')
         self._setup_parser()
     
     if self._transport.get_property("status") != IoStatus.OPEN:
         self._transport.open()
Example #3
0
    def _setup_transport(self):
        if self._transport is None:
            if self.__proxy is not None:
                self._transport = TCPClient(self.__proxy.host,
                                            self.__proxy.port)
            else:
                self._transport = TCPClient(self._host, self._port)
            self._http_parser = HTTPParser(self._transport)
            self._http_parser.connect("received", self._on_response_received)
            self._transport.connect("notify::status", self._on_status_change)
            self._transport.connect("error", self._on_error)
            self._transport.connect("sent", self._on_request_sent)

        if self._transport.get_property("status") != IoStatus.OPEN:
            self._transport.open()
Example #4
0
    def __init__(self, client, proxy_infos):
        assert (proxy_infos.type in (
            'http',
            'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert (client.domain == AF_INET
                ), "HTTP CONNECT only handles INET address family"
        assert (client.type == SOCK_STREAM
                ), "HTTP CONNECT only handles SOCK_STREAM"
        assert (client.status == IoStatus.CLOSED
                ), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)
Example #5
0
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type == 'socks4'), \
                "SOCKS4Proxy expects a socks4 proxy description"
        # TODO : implement version 4a of the protocol to allow proxy-side name resolution
        assert(client.domain == AF_INET), \
                "SOCKS4 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS4 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS4Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.delimiter = 8
        self._delimiter_parser.connect("received", self._on_proxy_response)
Example #6
0
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type in ('http', 'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert(client.domain == AF_INET), "HTTP CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), "HTTP CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)
Example #7
0
    def __init__(self, client, proxy):
        assert(proxy.type in ('socks', 'socks5')), \
                "SOCKS5Proxy expects a socks5 proxy description"
        assert(client.domain == AF_INET), \
                "SOCKS5 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS5 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS5Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.connect("received", self._on_proxy_response)

        self._state = None
        self._must_auth = False
Example #8
0
class HTTPConnectProxy(AbstractProxy):
    def __init__(self, client, proxy_infos):
        assert (proxy_infos.type in (
            'http',
            'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert (client.domain == AF_INET
                ), "HTTP CONNECT only handles INET address family"
        assert (client.type == SOCK_STREAM
                ), "HTTP CONNECT only handles SOCK_STREAM"
        assert (client.status == IoStatus.CLOSED
                ), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)

    # opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        host = self._client.get_property("host")
        port = self._client.get_property("port")
        proxy_protocol = 'CONNECT %s:%s HTTP/1.1\r\n' % (host, port)
        proxy_protocol += 'Proxy-Connection: Keep-Alive\r\n'
        proxy_protocol += 'Pragma: no-cache\r\n'
        proxy_protocol += 'User-Agent: %s/%s\r\n' % (GNet.NAME, GNet.VERSION)
        if self._proxy.user:
            auth = base64.encodestring(self._proxy.user + ':' +
                                       self._proxy.password).strip()
            proxy_protocol += 'Proxy-authorization: Basic ' + auth + '\r\n'
        proxy_protocol += '\r\n'
        self._transport.send(proxy_protocol)

    # public API
    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._client._proxy_closed()

    def send(self, buffer, callback=None, *args):
        self._client.send(buffer, callback, *args)

    # callbacks and signal handlers
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error_code):
        if error_code == IoError.CONNECTION_FAILED:
            error_code = IoError.PROXY_CONNECTION_FAILED
        self.close()
        self.emit("error", error_code)

    def _on_proxy_response(self, parser, response):
        if self.status == IoStatus.OPENING:
            if response.status == 200:
                del self._http_parser
                self._transport._watch_remove()  # HACK: ok this is ugly !
                self._client._proxy_open()
            elif response.status == 100:
                return True
            elif response.status == 407:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            else:
                raise NotImplementedError("Unknown Proxy response code")
            return False
Example #9
0
class SOCKS4Proxy(AbstractProxy):

    PROTOCOL_VERSION = 4
    CONNECT_COMMAND = 1

    """Proxy class used to communicate with SOCKS4 proxies."""
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type == 'socks4'), \
                "SOCKS4Proxy expects a socks4 proxy description"
        # TODO : implement version 4a of the protocol to allow proxy-side name resolution
        assert(client.domain == AF_INET), \
                "SOCKS4 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS4 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS4Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.delimiter = 8
        self._delimiter_parser.connect("received", self._on_proxy_response)

    # Opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        user = self._proxy.user

        proxy_protocol  = struct.pack('!BBH', SOCKS4Proxy.PROTOCOL_VERSION,
                SOCKS4Proxy.CONNECT_COMMAND, self.port)

        for part in self.host.split('.'):
           proxy_protocol += struct.pack('B', int(part))

        proxy_protocol += user
        proxy_protocol += struct.pack('B', 0)

        self._delimiter_parser.enable()
        self._transport.send(proxy_protocol)
        
    # Public API
    @property
    def protocol(self):
        return "SOCKS4"

    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._delimiter_parser.disable()
        self._client._proxy_closed()
        self._transport.close()

    def send(self, buffer, callback=None, errback=None):
        self._client.send(buffer, callback, errback=None)

    # Callbacks
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error):
        self.close()
        self.emit("error", error)

    def _on_proxy_response(self, parser, response):
        version, response_code = struct.unpack('BB', response[0:2])
        assert(version == 0)
        if self.status == IoStatus.OPENING:
            if response_code == 90:
                self._delimiter_parser.disable()
                self._transport.disable()
                self._client._proxy_open()
            else:
                logger.error("Connection failed (%s)" % response_code)
                self.close()
                self.emit("error", SOCKS4Error(self, response_code))
            return False
Example #10
0
class HTTPConnectProxy(AbstractProxy):
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type in ('http', 'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert(client.domain == AF_INET), "HTTP CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), "HTTP CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)

    # opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        proxy_protocol  = 'CONNECT %s:%s HTTP/1.1\r\n' % (self.host, self.port)
        proxy_protocol += 'Proxy-Connection: Keep-Alive\r\n'
        proxy_protocol += 'Pragma: no-cache\r\n'
        proxy_protocol += 'User-Agent: %s/%s\r\n' % (GNet.NAME, GNet.VERSION)
        if self._proxy.user:
            auth = base64.encodestring(self._proxy.user + ':' + self._proxy.password).strip()
            proxy_protocol += 'Proxy-authorization: Basic ' + auth + '\r\n'
        proxy_protocol += '\r\n'

        self._http_parser.enable()
        self._transport.send(proxy_protocol)

    # public API
    @property
    def protocol(self):
        return "HTTPConnect"

    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass
    
    def close(self):
        """Close the connection."""
        self._http_parser.disable()
        self._client._proxy_closed()
        self._transport.close()

    def send(self, buffer, callback=None, errback=None):
        self._client.send(buffer, callback, errback=None)

    # callbacks and signal handlers
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error):
        self.close()
        self.emit("error", error)

    def _on_proxy_response(self, parser, response):
        if self.status == IoStatus.OPENING:
            if response.status == 200:
                self._http_parser.disable()
                self._transport.disable()
                self._client._proxy_open()
            elif response.status == 100:
                return True
            else:
                logger.error("Connection failed (%s)" % response.status)
                self.close()
                self.emit("error", HTTPConnectError(self, response.status))
            return False
Example #11
0
class HTTP(gobject.GObject):
    """HTTP protocol client class."""

    __gsignals__ = {
        "error":
        (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_ULONG, )),
        "response-received": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                              (object, )),  # HTTPResponse
        "request-sent": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                         (object, )),  # HTTPRequest
    }

    def __init__(self, host, port=80, proxy=None):
        """Connection initialization
        
            @param host: the host to connect to.
            @type host: string

            @param port: the port number to connect to
            @type port: integer

            @param proxy: proxy that we can use to connect
            @type proxy: L{gnet.proxy.ProxyInfos}"""
        gobject.GObject.__init__(self)
        assert (proxy is None or proxy.type == 'http'
                )  # TODO: add support for other proxies (socks4 and 5)
        self._host = host
        self._port = port
        self.__proxy = proxy
        self._transport = None
        self._http_parser = None
        self._outgoing_queue = []
        self._waiting_response = False

    def _setup_transport(self):
        if self._transport is None:
            if self.__proxy is not None:
                self._transport = TCPClient(self.__proxy.host,
                                            self.__proxy.port)
            else:
                self._transport = TCPClient(self._host, self._port)
            self._http_parser = HTTPParser(self._transport)
            self._http_parser.connect("received", self._on_response_received)
            self._transport.connect("notify::status", self._on_status_change)
            self._transport.connect("error", self._on_error)
            self._transport.connect("sent", self._on_request_sent)

        if self._transport.get_property("status") != IoStatus.OPEN:
            self._transport.open()

    def _on_status_change(self, transport, param):
        if transport.get_property("status") == IoStatus.OPEN:
            self._process_queue()
        elif transport.get_property("status") == IoStatus.CLOSED and\
                (self._waiting_response or len(self._outgoing_queue) > 0):
            self._waiting_response = False
            self._setup_transport()

    def _on_request_sent(self, transport, request, length):
        assert (str(self._outgoing_queue[0]) == request)
        self._waiting_response = True
        self.emit("request-sent", self._outgoing_queue[0])

    def _on_response_received(self, parser, response):
        if response.status >= 100 and response.status < 200:
            return
        #if response.status in (301, 302): # UNTESTED: please test
        #    location = response.headers['Location']

        #    location = location.rsplit("://", 1)
        #    if len(location) == 2:
        #        scheme = location[0]
        #        location = location[1]
        #    if scheme == "http":
        #        location = location.rsplit(":", 1)
        #        self._host = location[0]
        #        if len(location) == 2:
        #            self._port = int(location[1])
        #        self._outgoing_queue[0].headers['Host'] = response.headers['Location']
        #        self._setup_transport()
        #        return
        self._outgoing_queue.pop(0)  # pop the request from the queue
        self.emit("response-received", response)
        self._waiting_response = False
        self._process_queue()  # next request ?

    def _on_error(self, transport, error):
        self.emit("error", error)

    def _process_queue(self):
        if len(self._outgoing_queue) == 0 or \
                self._waiting_response: # no pipelining
            return
        if self._transport is None or \
                self._transport.get_property("status") != IoStatus.OPEN:
            self._setup_transport()
            return
        self._transport.send(str(self._outgoing_queue[0]))

    def request(self, resource='/', headers=None, data='', method='GET'):
        if headers is None:
            headers = {}
        headers['Host'] = self._host + ':' + str(self._port)
        headers['Content-Length'] = str(len(data))
        if 'User-Agent' not in headers:
            user_agent = GNet.NAME, GNet.VERSION, platform.system(
            ), platform.machine()
            headers['User-Agent'] = "%s/%s (%s %s)" % user_agent

        if self.__proxy is not None:
            url = 'http://%s:%d%s' % (self._host, self._port, resource)
            if self.__proxy.user:
                auth = self.__proxy.user + ':' + self.__proxy.password
                credentials = base64.encodestring(auth).strip()
                headers['Proxy-Authorization'] = 'Basic ' + credentials
        else:
            url = resource
        request = HTTPRequest(headers, data, method, url)
        self._outgoing_queue.append(request)
        self._process_queue()
Example #12
0
class SOCKS5Proxy(AbstractProxy):

    VERSION = 5
    CMD_CONNECT = 0x01
    AUTH_VERSION = 1
    MAX_LEN = 255
    RESERVED = 0x00

    AUTH_NONE = 0x00
    AUTH_GSSAPI = 0x01
    AUTH_USR_PASS = 0x02
    AUTH_NO_ACCEPT = 0xff

    ATYP_IPV4 = 0x01
    ATYP_DOMAINNAME = 0x03
    ATYP_IPV6 = 0x04

    STATE_NEGO = 0x01
    STATE_AUTH = 0x02
    STATE_CONN = 0x03
    STATE_RECV_IPV4_ADDR = 0x04
    STATE_RECV_ADDR_LEN = 0x05
    STATE_RECV_DOMAINNAME = 0x06

    CODE_SUCCEEDED = 0x00
    CODE_SRV_FAILURE = 0x01
    CODE_NOT_ALLOWED = 0x02
    CODE_NET_UNREACH = 0x03
    CODE_HOST_UNREACH = 0x04
    CODE_REFUSED = 0x05
    CODE_TTL_EXPIRED = 0x06
    CODE_CMD_NOT_SUP = 0x07
    CODE_ATYPE_NOT_SUP = 0x08

    NEGO_REPLY_LEN = 2
    AUTH_REPLY_LEN = 2
    CONN_REPLY_LEN = 4
    IPV4_ADDR_LEN = 4

    """Proxy class used to communicate with SOCKS5 proxies."""
    def __init__(self, client, proxy):
        assert(proxy.type in ('socks', 'socks5')), \
                "SOCKS5Proxy expects a socks5 proxy description"
        assert(client.domain == AF_INET), \
                "SOCKS5 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS5 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS5Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.connect("received", self._on_proxy_response)

        self._state = None
        self._must_auth = False

    # Opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        self._delimiter_parser.enable()
        self._send_nego_msg()
        
    # Public API
    @property
    def protocol(self):
        return "SOCKS5"

    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._delimiter_parser.disable()
        self._client._proxy_closed()
        self._transport.close()

    def send(self, buffer, callback=None, errback=None):
        self._client.send(buffer, callback, errback)

    # Handshake
    def _send_nego_msg(self):
        user = self._proxy.user
        password = self._proxy.password

        methods = [self.AUTH_NONE]
        if user or password:
            methods.append(self.AUTH_USR_PASS)

        msg = struct.pack('!BB', SOCKS5Proxy.VERSION,
                len(methods))
        for method in methods:
            msg += struct.pack('B', method)

        logger.info("Sending negotiation request (%i methods)" % len(methods))
        self._state = SOCKS5Proxy.STATE_NEGO
        self._delimiter_parser.delimiter = SOCKS5Proxy.NEGO_REPLY_LEN
        self._transport.send(msg)
        return True

    def _parse_nego_reply(self, response):
        version, method = struct.unpack('!BB', response[0:2])
        if version != SOCKS5Proxy.VERSION:
            raise Exception("Server is not SOCKS5 compatible")
        if method == SOCKS5Proxy.AUTH_NO_ACCEPT:
            raise Exception("Server doesn't support any of the proposed \
                    authentication methods")

        logger.info("Server chose authentication method %i" % method)
        self._must_auth = (method == SOCKS5Proxy.AUTH_USR_PASS)
        return True

    def _send_auth_msg(self):
        user = self._proxy.user
        password = self._proxy.password

        if len(user) > self.MAX_LEN or len(password) > self.MAX_LEN:
            raise Exception("User and password need to be less than %i \
                    characters long" % self.MAX_LEN)

        msg = struct.pack('B', SOCKS5Proxy.AUTH_VERSION)
        msg += struct.pack('B', len(user))
        if user:
            msg += user
        msg += struct.pack('B', len(password))
        if password:
            msg += password

        logger.info("Sending authentication request")
        self._state = SOCKS5Proxy.STATE_AUTH
        self._delimiter_parser.delimiter = SOCKS5Proxy.AUTH_REPLY_LEN
        self._transport.send(msg)

    def _check_auth_status(self, response):
        version, code = struct.unpack('!BB', response[0:2])
        if (version != SOCKS5Proxy.VERSION or
            code != SOCKS5Proxy.CODE_SUCCEEDED):
            raise Exception("Authentication didn't succeed (%s)" % code)
        logger.info("Authentication succeeded")
        return True

    def _send_connect_msg(self):
        msg = struct.pack('!BBB', SOCKS5Proxy.VERSION,
                SOCKS5Proxy.CMD_CONNECT, SOCKS5Proxy.RESERVED)
        try:
            addr = socket.inet_aton(self.host)
            msg += struct.pack('!BI', SOCKS5Proxy.ATYP_IPV4, addr)
        except:
            if len(self.host) > SOCKS5Proxy.MAX_LEN:
                raise Exception("Hostname is longer than max allowed length")
            msg += struct.pack('!BB', SOCKS5Proxy.ATYP_DOMAINNAME, len(self.host))
            msg += self.host

        msg += struct.pack('!H', self.port)

        logger.info("Connection request to %s:%u" % (self.host, self.port))
        self._state = SOCKS5Proxy.STATE_CONN
        self._delimiter_parser.delimiter = SOCKS5Proxy.CONN_REPLY_LEN
        self._transport.send(msg)

    def _parse_connect_reply(self, response):
        version, code, reserved, atyp = struct.unpack('!BBBB', response[0:4])
        if version != SOCKS5Proxy.VERSION or reserved != SOCKS5Proxy.RESERVED:
            raise Exception("Connection reply isn't SOCKS5 compatible")
        if code == SOCKS5Proxy.CODE_SUCCEEDED:
            logger.info("Connection request has been accepted")
        else:
            raise Exception("Connection request has been declined (%i)" % code)

        if atyp == SOCKS5Proxy.ATYP_IPV4:
            self._state = SOCKS5Proxy.STATE_RECV_IPV4_ADDR
            self._delimiter_parser.delimiter = SOCKS5Proxy.IPV4_ADDR_LEN
        elif atyp == SOCKS5Proxy.ATYP_DOMAINNAME:
            self._state = SOCKS5Proxy.STATE_RECV_ADDR_LEN
            self._delimiter_parser.delimiter = 1
        else:
            raise Exception('Unsupported address type: %i' % atyp)

    def _parse_domain_name_length(self, response):
        length, = struct.unpack('!B', response)
        self._state = SOCKS5Proxy.STATE_RECV_DOMAINNAME
        self._delimiter_parser.delimiter = length

    # Callbacks
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error):
        self.close()
        self.emit("error", error)

    def _on_proxy_response(self, parser, response):
        try:
            if self._state == SOCKS5Proxy.STATE_NEGO:
                self._parse_nego_reply(response)
                if self._must_auth:
                    self._send_auth_msg()
                else:
                    self._send_connect_msg()
            elif self._state == SOCKS5Proxy.STATE_AUTH:
                self._check_auth_status(response)
                self._send_connect_msg()
            elif self._state == SOCKS5Proxy.STATE_CONN:
                self._parse_connect_reply(response)
            elif self._state == SOCKS5Proxy.STATE_RECV_ADDR_LEN:
                self._parse_domain_name_length(response)
            elif (self._state == SOCKS5Proxy.STATE_RECV_IPV4_ADDR or
                  self._state == SOCKS5Proxy.STATE_RECV_DOMAINNAME):
                self._delimiter_parser.disable()
                self._transport.disable()
                self._client._proxy_open()
        except Exception, err:
            logger.error("Handshake failed")
            logger.exception(err)
            self.close()
            self.emit("error", SOCKS5Error(self, str(err)))
        return False
Example #13
0
class SOCKS4Proxy(AbstractProxy):

    PROTOCOL_VERSION = 4
    CONNECT_COMMAND = 1
    """Proxy class used to communicate with SOCKS4 proxies."""
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type == 'socks4'), \
                "SOCKS4Proxy expects a socks4 proxy description"
        # TODO : implement version 4a of the protocol to allow proxy-side name resolution
        assert(client.domain == AF_INET), \
                "SOCKS4 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS4 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS4Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.delimiter = 8
        self._delimiter_parser.connect("received", self._on_proxy_response)

    # Opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        host = self._client.get_property("host")
        port = self._client.get_property("port")
        user = self._proxy.user

        proxy_protocol = struct.pack('!BBH', SOCKS4Proxy.PROTOCOL_VERSION,
                                     SOCKS4Proxy.CONNECT_COMMAND, port)

        for part in host.split('.'):
            proxy_protocol += struct.pack('B', int(part))

        proxy_protocol += user
        proxy_protocol += struct.pack('B', 0)

        self._transport.send(proxy_protocol)

    # Public API
    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._client._proxy_closed()

    def send(self, buffer, callback=None, *args):
        self._client.send(buffer, callback, *args)

    # Callbacks
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error_code):
        if error_code == IoError.CONNECTION_FAILED:
            error_code = IoError.PROXY_CONNECTION_FAILED
        self.close()
        self.emit("error", error_code)

    def _on_proxy_response(self, parser, response):
        version, response_code = struct.unpack('BB', response[0:2])
        assert (version == 0)
        if self.status == IoStatus.OPENING:
            if response_code == 90:
                del self._delimiter_parser
                self._transport._watch_remove()  # HACK: ok this is ugly !
                self._client._proxy_open()
            elif response_code == 91:
                self.close()
                self.emit("error", IoError.PROXY_CONNECTION_FAILED)
            elif response_code == 92:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            elif response_code == 93:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            else:
                raise NotImplementedError("Unknow Proxy response code")
            return False
Example #14
0
class SOCKS4Proxy(AbstractProxy):

    PROTOCOL_VERSION = 4
    CONNECT_COMMAND = 1

    """Proxy class used to communicate with SOCKS4 proxies."""
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type == 'socks4'), \
                "SOCKS4Proxy expects a socks4 proxy description"
        # TODO : implement version 4a of the protocol to allow proxy-side name resolution
        assert(client.domain == AF_INET), \
                "SOCKS4 CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), \
                "SOCKS4 CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), \
                "SOCKS4Proxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)

        self._delimiter_parser = DelimiterParser(self._transport)
        self._delimiter_parser.delimiter = 8
        self._delimiter_parser.connect("received", self._on_proxy_response)

    # Opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        host = self._client.get_property("host")
        port = self._client.get_property("port")
        user = self._proxy.user

        proxy_protocol  = struct.pack('!BBH', SOCKS4Proxy.PROTOCOL_VERSION,
                SOCKS4Proxy.CONNECT_COMMAND, port)

        for part in host.split('.'):
           proxy_protocol += struct.pack('B', int(part))

        proxy_protocol += user
        proxy_protocol += struct.pack('B', 0)

        self._transport.send(proxy_protocol)
        
    # Public API
    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._client._proxy_closed()

    def send(self, buffer, callback=None, *args):
        self._client.send(buffer, callback, *args)

    # Callbacks
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error_code):
        if error_code == IoError.CONNECTION_FAILED:
            error_code = IoError.PROXY_CONNECTION_FAILED
        self.close()
        self.emit("error", error_code)

    def _on_proxy_response(self, parser, response):
        version, response_code = struct.unpack('BB', response[0:2])
        assert(version == 0)
        if self.status == IoStatus.OPENING:
            if response_code == 90:
                del self._delimiter_parser
                self._transport._watch_remove() # HACK: ok this is ugly !
                self._client._proxy_open()
            elif response_code == 91:
                self.close()
                self.emit("error", IoError.PROXY_CONNECTION_FAILED)
            elif response_code == 92:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            elif response_code == 93:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            else:
                raise NotImplementedError("Unknow Proxy response code")
            return False
Example #15
0
class HTTPConnectProxy(AbstractProxy):
    def __init__(self, client, proxy_infos):
        assert (proxy_infos.type in (
            'http',
            'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert (client.domain == AF_INET
                ), "HTTP CONNECT only handles INET address family"
        assert (client.type == SOCK_STREAM
                ), "HTTP CONNECT only handles SOCK_STREAM"
        assert (client.status == IoStatus.CLOSED
                ), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)

    # opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        proxy_protocol = 'CONNECT %s:%s HTTP/1.1\r\n' % (self.host, self.port)
        proxy_protocol += 'Proxy-Connection: Keep-Alive\r\n'
        proxy_protocol += 'Pragma: no-cache\r\n'
        proxy_protocol += 'User-Agent: %s/%s\r\n' % (GNet.NAME, GNet.VERSION)
        if self._proxy.user:
            auth = base64.encodestring(self._proxy.user + ':' +
                                       self._proxy.password).strip()
            proxy_protocol += 'Proxy-authorization: Basic ' + auth + '\r\n'
        proxy_protocol += '\r\n'

        self._http_parser.enable()
        self._transport.send(proxy_protocol)

    # public API
    @property
    def protocol(self):
        return "HTTPConnect"

    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass

    def close(self):
        """Close the connection."""
        self._http_parser.disable()
        self._client._proxy_closed()
        self._transport.close()

    def send(self, buffer, callback=None, errback=None):
        self._client.send(buffer, callback, errback=None)

    # callbacks and signal handlers
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error):
        self.close()
        self.emit("error", error)

    def _on_proxy_response(self, parser, response):
        if self.status == IoStatus.OPENING:
            if response.status == 200:
                self._http_parser.disable()
                self._transport.disable()
                self._client._proxy_open()
            elif response.status == 100:
                return True
            else:
                logger.error("Connection failed (%s)" % response.status)
                self.close()
                self.emit("error", HTTPConnectError(self, response.status))
            return False
Example #16
0
class HTTPConnectProxy(AbstractProxy):
    def __init__(self, client, proxy_infos):
        assert(proxy_infos.type in ('http', 'https')), "HTTPConnectProxy expects an http(s) proxy description"
        assert(client.domain == AF_INET), "HTTP CONNECT only handles INET address family"
        assert(client.type == SOCK_STREAM), "HTTP CONNECT only handles SOCK_STREAM"
        assert(client.status == IoStatus.CLOSED), "HTTPConnectProxy expects a closed client"
        AbstractProxy.__init__(self, client, proxy_infos)

        self._transport = TCPClient(self._proxy.host, self._proxy.port)
        self._transport.connect("notify::status", self._on_transport_status)
        self._transport.connect("error", self._on_transport_error)
        self._http_parser = HTTPParser(self._transport)
        self._http_parser.connect("received", self._on_proxy_response)

    # opening state methods
    def _pre_open(self, io_object=None):
        AbstractProxy._pre_open(self)

    def _post_open(self):
        AbstractProxy._post_open(self)
        host = self._client.get_property("host")
        port = self._client.get_property("port")
        proxy_protocol  = 'CONNECT %s:%s HTTP/1.1\r\n' % (host, port)
        proxy_protocol += 'Proxy-Connection: Keep-Alive\r\n'
        proxy_protocol += 'Pragma: no-cache\r\n'
        proxy_protocol += 'User-Agent: %s/%s\r\n' % (GNet.NAME, GNet.VERSION)
        if self._proxy.user:
            auth = base64.encodestring(self._proxy.user + ':' + self._proxy.password).strip()
            proxy_protocol += 'Proxy-authorization: Basic ' + auth + '\r\n'
        proxy_protocol += '\r\n'
        self._transport.send(proxy_protocol)

    # public API
    def open(self):
        """Open the connection."""
        if not self._configure():
            return
        self._pre_open()
        try:
            self._transport.open()
        except:
            pass
    
    def close(self):
        """Close the connection."""
        self._client._proxy_closed()

    def send(self, buffer, callback=None, *args):
        self._client.send(buffer, callback, *args)

    # callbacks and signal handlers
    def _on_transport_status(self, transport, param):
        if transport.status == IoStatus.OPEN:
            self._post_open()
        elif transport.status == IoStatus.OPENING:
            self._client._proxy_opening(self._transport._transport)
            self._status = transport.status
        else:
            self._status = transport.status

    def _on_transport_error(self, transport, error_code):
        if error_code == IoError.CONNECTION_FAILED:
            error_code = IoError.PROXY_CONNECTION_FAILED
        self.close()
        self.emit("error", error_code)

    def _on_proxy_response(self, parser, response):
        if self.status == IoStatus.OPENING:
            if response.status == 200:
                del self._http_parser
                self._transport._watch_remove() # HACK: ok this is ugly !
                self._client._proxy_open()
            elif response.status == 100:
                return True
            elif response.status == 407:
                self.close()
                self.emit("error", IoError.PROXY_AUTHENTICATION_REQUIRED)
            else:
                raise NotImplementedError("Unknown Proxy response code")
            return False
Example #17
0
class HTTP(gobject.GObject):
    """HTTP protocol client class."""
    
    __gsignals__ = {
            "error" : (gobject.SIGNAL_RUN_FIRST,
                gobject.TYPE_NONE,
                (gobject.TYPE_ULONG,)),

            "response-received": (gobject.SIGNAL_RUN_FIRST,
                gobject.TYPE_NONE,
                (object,)), # HTTPResponse

            "request-sent": (gobject.SIGNAL_RUN_FIRST,
                gobject.TYPE_NONE,
                (object,)), # HTTPRequest
            }

    def __init__(self, host, port=80, proxy=None):
        """Connection initialization
        
            @param host: the host to connect to.
            @type host: string

            @param port: the port number to connect to
            @type port: integer

            @param proxy: proxy that we can use to connect
            @type proxy: L{gnet.proxy.ProxyInfos}"""
        gobject.GObject.__init__(self)
        assert(proxy is None or proxy.type == 'http') # TODO: add support for other proxies (socks4 and 5)
        self._host = host
        self._port = port
        self.__proxy = proxy
        self._transport = None
        self._http_parser = None
        self._outgoing_queue = []
        self._waiting_response = False

    def _setup_transport(self):
        if self._transport is None:
            if self.__proxy is not None:
                self._transport = TCPClient(self.__proxy.host, self.__proxy.port)
            else:
                self._transport = TCPClient(self._host, self._port)
            self._http_parser = HTTPParser(self._transport)
            self._http_parser.connect("received", self._on_response_received)
            self._transport.connect("notify::status", self._on_status_change)
            self._transport.connect("error", self._on_error)
            self._transport.connect("sent", self._on_request_sent)
        
        if self._transport.get_property("status") != IoStatus.OPEN:
            self._transport.open()

    def _on_status_change(self, transport, param):
        if transport.get_property("status") == IoStatus.OPEN:
            self._process_queue()
        elif transport.get_property("status") == IoStatus.CLOSED and\
                (self._waiting_response or len(self._outgoing_queue) > 0):
            self._waiting_response = False
            self._setup_transport()

    def _on_request_sent(self, transport, request, length):
        assert(str(self._outgoing_queue[0]) == request)
        self._waiting_response = True
        self.emit("request-sent", self._outgoing_queue[0])

    def _on_response_received(self, parser, response):
        if response.status >= 100 and response.status < 200:
            return
        #if response.status in (301, 302): # UNTESTED: please test
        #    location = response.headers['Location']

        #    location = location.rsplit("://", 1)
        #    if len(location) == 2:
        #        scheme = location[0]
        #        location = location[1]
        #    if scheme == "http":
        #        location = location.rsplit(":", 1)
        #        self._host = location[0]
        #        if len(location) == 2:
        #            self._port = int(location[1])
        #        self._outgoing_queue[0].headers['Host'] = response.headers['Location']
        #        self._setup_transport()
        #        return
        self._outgoing_queue.pop(0) # pop the request from the queue
        self.emit("response-received", response)
        self._waiting_response = False
        self._process_queue() # next request ?

    def _on_error(self, transport, error):
        self.emit("error", error)

    def _process_queue(self):
        if len(self._outgoing_queue) == 0 or \
                self._waiting_response: # no pipelining
            return
        if self._transport is None or \
                self._transport.get_property("status") != IoStatus.OPEN:
            self._setup_transport()
            return
        self._transport.send(str(self._outgoing_queue[0]))

    def request(self, resource='/', headers=None, data='', method='GET'):
        if headers is None:
            headers = {}
        headers['Host'] = self._host + ':' + str(self._port)
        headers['Content-Length'] = str(len(data))
        if 'User-Agent' not in headers:
            user_agent = GNet.NAME, GNet.VERSION, platform.system(), platform.machine()
            headers['User-Agent'] = "%s/%s (%s %s)" % user_agent

        if self.__proxy is not None:
            url = 'http://%s:%d%s' % (self._host, self._port, resource)
            if self.__proxy.user:
                auth = self.__proxy.user + ':' + self.__proxy.password
                credentials = base64.encodestring(auth).strip()
                headers['Proxy-Authorization'] = 'Basic ' + credentials
        else:
            url = resource
        request  = HTTPRequest(headers, data, method, url)
        self._outgoing_queue.append(request)
        self._process_queue()