Advertisement
Guest User

Untitled

a guest
Sep 4th, 2018
273
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 30.66 KB | None | 0 0
  1. __version__ = "1.6.7"
  2.  
  3. log = logging.getLogger(__name__)
  4.  
  5. PROXY_TYPE_SOCKS4 = SOCKS4 = 1
  6. PROXY_TYPE_SOCKS5 = SOCKS5 = 2
  7. PROXY_TYPE_HTTP = HTTP = 3
  8.  
  9. PROXY_TYPES = {"SOCKS4": SOCKS4, "SOCKS5": SOCKS5, "HTTP": HTTP}
  10. PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys()))
  11.  
  12. _orgsocket = _orig_socket = socket.socket
  13.  
  14.  
  15. def set_self_blocking(function):
  16.  
  17.     @functools.wraps(function)
  18.     def wrapper(*args, **kwargs):
  19.         self = args[0]
  20.         try:
  21.             _is_blocking = self.gettimeout()
  22.             if _is_blocking == 0:
  23.                 self.setblocking(True)
  24.             return function(*args, **kwargs)
  25.         except Exception as e:
  26.             raise
  27.         finally:
  28.             # set orgin blocking
  29.             if _is_blocking == 0:
  30.                 self.setblocking(False)
  31.     return wrapper
  32.  
  33.  
  34. class ProxyError(IOError):
  35.     """Socket_err contains original socket.error exception."""
  36.     def __init__(self, msg, socket_err=None):
  37.         self.msg = msg
  38.         self.socket_err = socket_err
  39.  
  40.         if socket_err:
  41.             self.msg += ": {}".format(socket_err)
  42.  
  43.     def __str__(self):
  44.         return self.msg
  45.  
  46.  
  47. class GeneralProxyError(ProxyError):
  48.     pass
  49.  
  50.  
  51. class ProxyConnectionError(ProxyError):
  52.     pass
  53.  
  54.  
  55. class SOCKS5AuthError(ProxyError):
  56.     pass
  57.  
  58.  
  59. class SOCKS5Error(ProxyError):
  60.     pass
  61.  
  62.  
  63. class SOCKS4Error(ProxyError):
  64.     pass
  65.  
  66.  
  67. class HTTPError(ProxyError):
  68.     pass
  69.  
  70. SOCKS4_ERRORS = {
  71.     0x5B: "Request rejected or failed",
  72.     0x5C: ("Request rejected because SOCKS server cannot connect to identd on"
  73.            " the client"),
  74.     0x5D: ("Request rejected because the client program and identd report"
  75.            " different user-ids")
  76. }
  77.  
  78. SOCKS5_ERRORS = {
  79.     0x01: "General SOCKS server failure",
  80.     0x02: "Connection not allowed by ruleset",
  81.     0x03: "Network unreachable",
  82.     0x04: "Host unreachable",
  83.     0x05: "Connection refused",
  84.     0x06: "TTL expired",
  85.     0x07: "Command not supported, or protocol error",
  86.     0x08: "Address type not supported"
  87. }
  88.  
  89. DEFAULT_PORTS = {SOCKS4: 1080, SOCKS5: 1080, HTTP: 8080}
  90.  
  91.  
  92. def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True,
  93.                       username=None, password=None):
  94.     """Sets a default proxy.
  95.  
  96.    All further socksocket objects will use the default unless explicitly
  97.    changed. All parameters are as for socket.set_proxy()."""
  98.     socksocket.default_proxy = (proxy_type, addr, port, rdns,
  99.                                 username.encode() if username else None,
  100.                                 password.encode() if password else None)
  101.  
  102.  
  103. def setdefaultproxy(*args, **kwargs):
  104.     if "proxytype" in kwargs:
  105.         kwargs["proxy_type"] = kwargs.pop("proxytype")
  106.     return set_default_proxy(*args, **kwargs)
  107.  
  108.  
  109. def get_default_proxy():
  110.     """Returns the default proxy, set by set_default_proxy."""
  111.     return socksocket.default_proxy
  112.  
  113. getdefaultproxy = get_default_proxy
  114.  
  115.  
  116. def wrap_module(module):
  117.     """Attempts to replace a module's socket library with a SOCKS socket.
  118.  
  119.    Must set a default proxy using set_default_proxy(...) first. This will
  120.    only work on modules that import socket directly into the namespace;
  121.    most of the Python Standard Library falls into this category."""
  122.     if socksocket.default_proxy:
  123.         module.socket.socket = socksocket
  124.     else:
  125.         raise GeneralProxyError("No default proxy specified")
  126.  
  127. wrapmodule = wrap_module
  128.  
  129.  
  130. def create_connection(dest_pair,
  131.                       timeout=None, source_address=None,
  132.                       proxy_type=None, proxy_addr=None,
  133.                       proxy_port=None, proxy_rdns=True,
  134.                       proxy_username=None, proxy_password=None,
  135.                       socket_options=None):
  136.     """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object
  137.  
  138.    Like socket.create_connection(), but connects to proxy
  139.    before returning the socket object.
  140.  
  141.    dest_pair - 2-tuple of (IP/hostname, port).
  142.    **proxy_args - Same args passed to socksocket.set_proxy() if present.
  143.    timeout - Optional socket timeout value, in seconds.
  144.    source_address - tuple (host, port) for the socket to bind to as its source
  145.    address before connecting (only for compatibility)
  146.    """
  147.     # Remove IPv6 brackets on the remote address and proxy address.
  148.     remote_host, remote_port = dest_pair
  149.     if remote_host.startswith("["):
  150.         remote_host = remote_host.strip("[]")
  151.     if proxy_addr and proxy_addr.startswith("["):
  152.         proxy_addr = proxy_addr.strip("[]")
  153.  
  154.     err = None
  155.  
  156.     # Allow the SOCKS proxy to be on IPv4 or IPv6 addresses.
  157.     for r in socket.getaddrinfo(proxy_addr, proxy_port, 0, socket.SOCK_STREAM):
  158.         family, socket_type, proto, canonname, sa = r
  159.         sock = None
  160.         try:
  161.             sock = socksocket(family, socket_type, proto)
  162.  
  163.             if socket_options:
  164.                 for opt in socket_options:
  165.                     sock.setsockopt(*opt)
  166.  
  167.             if isinstance(timeout, (int, float)):
  168.                 sock.settimeout(timeout)
  169.  
  170.             if proxy_type:
  171.                 sock.set_proxy(proxy_type, proxy_addr, proxy_port, proxy_rdns,
  172.                                proxy_username, proxy_password)
  173.             if source_address:
  174.                 sock.bind(source_address)
  175.  
  176.             sock.connect((remote_host, remote_port))
  177.             return sock
  178.  
  179.         except (socket.error, ProxyConnectionError) as e:
  180.             err = e
  181.             if sock:
  182.                 sock.close()
  183.                 sock = None
  184.  
  185.     if err:
  186.         raise err
  187.  
  188.     raise socket.error("gai returned empty list.")
  189.  
  190.  
  191. class _BaseSocket(socket.socket):
  192.     """Allows Python 2 delegated methods such as send() to be overridden."""
  193.     def __init__(self, *pos, **kw):
  194.         _orig_socket.__init__(self, *pos, **kw)
  195.  
  196.         self._savedmethods = dict()
  197.         for name in self._savenames:
  198.             self._savedmethods[name] = getattr(self, name)
  199.             delattr(self, name)  # Allows normal overriding mechanism to work
  200.  
  201.     _savenames = list()
  202.  
  203.  
  204. def _makemethod(name):
  205.     return lambda self, *pos, **kw: self._savedmethods[name](*pos, **kw)
  206. for name in ("sendto", "send", "recvfrom", "recv"):
  207.     method = getattr(_BaseSocket, name, None)
  208.  
  209.     # Determine if the method is not defined the usual way
  210.     # as a function in the class.
  211.     # Python 2 uses __slots__, so there are descriptors for each method,
  212.     # but they are not functions.
  213.     if not isinstance(method, Callable):
  214.         _BaseSocket._savenames.append(name)
  215.         setattr(_BaseSocket, name, _makemethod(name))
  216.  
  217.  
  218. class socksocket(_BaseSocket):
  219.     """socksocket([family[, type[, proto]]]) -> socket object
  220.  
  221.    Open a SOCKS enabled socket. The parameters are the same as
  222.    those of the standard socket init. In order for SOCKS to work,
  223.    you must specify family=AF_INET and proto=0.
  224.    The "type" argument must be either SOCK_STREAM or SOCK_DGRAM.
  225.    """
  226.  
  227.     default_proxy = None
  228.  
  229.     def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
  230.                  proto=0, *args, **kwargs):
  231.         if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM):
  232.             msg = "Socket type must be stream or datagram, not {!r}"
  233.             raise ValueError(msg.format(type))
  234.  
  235.         super(socksocket, self).__init__(family, type, proto, *args, **kwargs)
  236.         self._proxyconn = None  # TCP connection to keep UDP relay alive
  237.  
  238.         if self.default_proxy:
  239.             self.proxy = self.default_proxy
  240.         else:
  241.             self.proxy = (None, None, None, None, None, None)
  242.         self.proxy_sockname = None
  243.         self.proxy_peername = None
  244.  
  245.         self._timeout = None
  246.  
  247.     def _readall(self, file, count):
  248.         """Receive EXACTLY the number of bytes requested from the file object.
  249.  
  250.        Blocks until the required number of bytes have been received."""
  251.         data = b""
  252.         while len(data) < count:
  253.             d = file.read(count - len(data))
  254.             if not d:
  255.                 raise GeneralProxyError("Connection closed unexpectedly")
  256.             data += d
  257.         return data
  258.  
  259.     def settimeout(self, timeout):
  260.         self._timeout = timeout
  261.         try:
  262.             # test if we're connected, if so apply timeout
  263.             peer = self.get_proxy_peername()
  264.             super(socksocket, self).settimeout(self._timeout)
  265.         except socket.error:
  266.             pass
  267.  
  268.     def gettimeout(self):
  269.         return self._timeout
  270.  
  271.     def setblocking(self, v):
  272.         if v:
  273.             self.settimeout(None)
  274.         else:
  275.             self.settimeout(0.0)
  276.  
  277.     def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True,
  278.                   username=None, password=None):
  279.         """ Sets the proxy to be used.
  280.  
  281.        proxy_type -  The type of the proxy to be used. Three types
  282.                        are supported: PROXY_TYPE_SOCKS4 (including socks4a),
  283.                        PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
  284.        addr -        The address of the server (IP or DNS).
  285.        port -        The port of the server. Defaults to 1080 for SOCKS
  286.                        servers and 8080 for HTTP proxy servers.
  287.        rdns -        Should DNS queries be performed on the remote side
  288.                       (rather than the local side). The default is True.
  289.                       Note: This has no effect with SOCKS4 servers.
  290.        username -    Username to authenticate with to the server.
  291.                       The default is no authentication.
  292.        password -    Password to authenticate with to the server.
  293.                       Only relevant when username is also provided."""
  294.         self.proxy = (proxy_type, addr, port, rdns,
  295.                       username.encode() if username else None,
  296.                       password.encode() if password else None)
  297.  
  298.     def setproxy(self, *args, **kwargs):
  299.         if "proxytype" in kwargs:
  300.             kwargs["proxy_type"] = kwargs.pop("proxytype")
  301.         return self.set_proxy(*args, **kwargs)
  302.  
  303.     def bind(self, *pos, **kw):
  304.         """Implements proxy connection for UDP sockets.
  305.  
  306.        Happens during the bind() phase."""
  307.         (proxy_type, proxy_addr, proxy_port, rdns, username,
  308.          password) = self.proxy
  309.         if not proxy_type or self.type != socket.SOCK_DGRAM:
  310.             return _orig_socket.bind(self, *pos, **kw)
  311.  
  312.         if self._proxyconn:
  313.             raise socket.error(EINVAL, "Socket already bound to an address")
  314.         if proxy_type != SOCKS5:
  315.             msg = "UDP only supported by SOCKS5 proxy type"
  316.             raise socket.error(EOPNOTSUPP, msg)
  317.         super(socksocket, self).bind(*pos, **kw)
  318.  
  319.         # Need to specify actual local port because
  320.         # some relays drop packets if a port of zero is specified.
  321.         # Avoid specifying host address in case of NAT though.
  322.         _, port = self.getsockname()
  323.         dst = ("0", port)
  324.  
  325.         self._proxyconn = _orig_socket()
  326.         proxy = self._proxy_addr()
  327.         self._proxyconn.connect(proxy)
  328.  
  329.         UDP_ASSOCIATE = b"\x03"
  330.         _, relay = self._SOCKS5_request(self._proxyconn, UDP_ASSOCIATE, dst)
  331.  
  332.         # The relay is most likely on the same host as the SOCKS proxy,
  333.         # but some proxies return a private IP address (10.x.y.z)
  334.         host, _ = proxy
  335.         _, port = relay
  336.         super(socksocket, self).connect((host, port))
  337.         super(socksocket, self).settimeout(self._timeout)
  338.         self.proxy_sockname = ("0.0.0.0", 0)  # Unknown
  339.  
  340.     def sendto(self, bytes, *args, **kwargs):
  341.         if self.type != socket.SOCK_DGRAM:
  342.             return super(socksocket, self).sendto(bytes, *args, **kwargs)
  343.         if not self._proxyconn:
  344.             self.bind(("", 0))
  345.  
  346.         address = args[-1]
  347.         flags = args[:-1]
  348.  
  349.         header = BytesIO()
  350.         RSV = b"\x00\x00"
  351.         header.write(RSV)
  352.         STANDALONE = b"\x00"
  353.         header.write(STANDALONE)
  354.         self._write_SOCKS5_address(address, header)
  355.  
  356.         sent = super(socksocket, self).send(header.getvalue() + bytes, *flags,
  357.                                             **kwargs)
  358.         return sent - header.tell()
  359.  
  360.     def send(self, bytes, flags=0, **kwargs):
  361.         if self.type == socket.SOCK_DGRAM:
  362.             return self.sendto(bytes, flags, self.proxy_peername, **kwargs)
  363.         else:
  364.             return super(socksocket, self).send(bytes, flags, **kwargs)
  365.  
  366.     def recvfrom(self, bufsize, flags=0):
  367.         if self.type != socket.SOCK_DGRAM:
  368.             return super(socksocket, self).recvfrom(bufsize, flags)
  369.         if not self._proxyconn:
  370.             self.bind(("", 0))
  371.  
  372.         buf = BytesIO(super(socksocket, self).recv(bufsize + 1024, flags))
  373.         buf.seek(2, SEEK_CUR)
  374.         frag = buf.read(1)
  375.         if ord(frag):
  376.             raise NotImplementedError("Received UDP packet fragment")
  377.         fromhost, fromport = self._read_SOCKS5_address(buf)
  378.  
  379.         if self.proxy_peername:
  380.             peerhost, peerport = self.proxy_peername
  381.             if fromhost != peerhost or peerport not in (0, fromport):
  382.                 raise socket.error(EAGAIN, "Packet filtered")
  383.  
  384.         return (buf.read(bufsize), (fromhost, fromport))
  385.  
  386.     def recv(self, *pos, **kw):
  387.         bytes, _ = self.recvfrom(*pos, **kw)
  388.         return bytes
  389.  
  390.     def close(self):
  391.         if self._proxyconn:
  392.             self._proxyconn.close()
  393.         return super(socksocket, self).close()
  394.  
  395.     def get_proxy_sockname(self):
  396.         """Returns the bound IP address and port number at the proxy."""
  397.         return self.proxy_sockname
  398.  
  399.     getproxysockname = get_proxy_sockname
  400.  
  401.     def get_proxy_peername(self):
  402.         """
  403.        Returns the IP and port number of the proxy.
  404.        """
  405.         return self.getpeername()
  406.  
  407.     getproxypeername = get_proxy_peername
  408.  
  409.     def get_peername(self):
  410.         """Returns the IP address and port number of the destination machine.
  411.  
  412.        Note: get_proxy_peername returns the proxy."""
  413.         return self.proxy_peername
  414.  
  415.     getpeername = get_peername
  416.  
  417.     def _negotiate_SOCKS5(self, *dest_addr):
  418.         """Negotiates a stream connection through a SOCKS5 server."""
  419.         CONNECT = b"\x01"
  420.         self.proxy_peername, self.proxy_sockname = self._SOCKS5_request(
  421.             self, CONNECT, dest_addr)
  422.  
  423.     def _SOCKS5_request(self, conn, cmd, dst):
  424.         """
  425.        Send SOCKS5 request with given command (CMD field) and
  426.        address (DST field). Returns resolved DST address that was used.
  427.        """
  428.         proxy_type, addr, port, rdns, username, password = self.proxy
  429.  
  430.         writer = conn.makefile("wb")
  431.         reader = conn.makefile("rb", 0)  # buffering=0 renamed in Python 3
  432.         try:
  433.             # First we'll send the authentication packages we support.
  434.             if username and password:
  435.                 # The username/password details were supplied to the
  436.                 # set_proxy method so we support the USERNAME/PASSWORD
  437.                 # authentication (in addition to the standard none).
  438.                 writer.write(b"\x05\x02\x00\x02")
  439.             else:
  440.                 # No username/password were entered, therefore we
  441.                 # only support connections with no authentication.
  442.                 writer.write(b"\x05\x01\x00")
  443.  
  444.             # We'll receive the server's response to determine which
  445.             # method was selected
  446.             writer.flush()
  447.             chosen_auth = self._readall(reader, 2)
  448.  
  449.             if chosen_auth[0:1] != b"\x05":
  450.                 # Note: string[i:i+1] is used because indexing of a bytestring
  451.                 # via bytestring[i] yields an integer in Python 3
  452.                 raise GeneralProxyError(
  453.                     "SOCKS5 proxy server sent invalid data")
  454.  
  455.             # Check the chosen authentication method
  456.  
  457.             if chosen_auth[1:2] == b"\x02":
  458.                 # Okay, we need to perform a basic username/password
  459.                 # authentication.
  460.                 if not (username and password):
  461.                     # Although we said we don't support authentication, the
  462.                     # server may still request basic username/password
  463.                     # authentication
  464.                     raise SOCKS5AuthError("No username/password supplied. "
  465.                                           "Server requested username/password"
  466.                                           " authentication")
  467.  
  468.                 writer.write(b"\x01" + chr(len(username)).encode()
  469.                              + username
  470.                              + chr(len(password)).encode()
  471.                              + password)
  472.                 writer.flush()
  473.                 auth_status = self._readall(reader, 2)
  474.                 if auth_status[0:1] != b"\x01":
  475.                     # Bad response
  476.                     raise GeneralProxyError(
  477.                         "SOCKS5 proxy server sent invalid data")
  478.                 if auth_status[1:2] != b"\x00":
  479.                     # Authentication failed
  480.                     raise SOCKS5AuthError("SOCKS5 authentication failed")
  481.  
  482.                 # Otherwise, authentication succeeded
  483.  
  484.             # No authentication is required if 0x00
  485.             elif chosen_auth[1:2] != b"\x00":
  486.                 # Reaching here is always bad
  487.                 if chosen_auth[1:2] == b"\xFF":
  488.                     raise SOCKS5AuthError(
  489.                         "All offered SOCKS5 authentication methods were"
  490.                         " rejected")
  491.                 else:
  492.                     raise GeneralProxyError(
  493.                         "SOCKS5 proxy server sent invalid data")
  494.  
  495.             # Now we can request the actual connection
  496.             writer.write(b"\x05" + cmd + b"\x00")
  497.             resolved = self._write_SOCKS5_address(dst, writer)
  498.             writer.flush()
  499.  
  500.             # Get the response
  501.             resp = self._readall(reader, 3)
  502.             if resp[0:1] != b"\x05":
  503.                 raise GeneralProxyError(
  504.                     "SOCKS5 proxy server sent invalid data")
  505.  
  506.             status = ord(resp[1:2])
  507.             if status != 0x00:
  508.                 # Connection failed: server returned an error
  509.                 error = SOCKS5_ERRORS.get(status, "Unknown error")
  510.                 raise SOCKS5Error("{:#04x}: {}".format(status, error))
  511.  
  512.             # Get the bound address/port
  513.             bnd = self._read_SOCKS5_address(reader)
  514.  
  515.             super(socksocket, self).settimeout(self._timeout)
  516.             return (resolved, bnd)
  517.         finally:
  518.             reader.close()
  519.             writer.close()
  520.  
  521.     def _write_SOCKS5_address(self, addr, file):
  522.         """
  523.        Return the host and port packed for the SOCKS5 protocol,
  524.        and the resolved address as a tuple object.
  525.        """
  526.         host, port = addr
  527.         proxy_type, _, _, rdns, username, password = self.proxy
  528.         family_to_byte = {socket.AF_INET: b"\x01", socket.AF_INET6: b"\x04"}
  529.  
  530.         # If the given destination address is an IP address, we'll
  531.         # use the IP address request even if remote resolving was specified.
  532.         # Detect whether the address is IPv4/6 directly.
  533.         for family in (socket.AF_INET, socket.AF_INET6):
  534.             try:
  535.                 addr_bytes = socket.inet_pton(family, host)
  536.                 file.write(family_to_byte[family] + addr_bytes)
  537.                 host = socket.inet_ntop(family, addr_bytes)
  538.                 file.write(struct.pack(">H", port))
  539.                 return host, port
  540.             except socket.error:
  541.                 continue
  542.  
  543.         # Well it's not an IP number, so it's probably a DNS name.
  544.         if rdns:
  545.             # Resolve remotely
  546.             host_bytes = host.encode("idna")
  547.             file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes)
  548.         else:
  549.             # Resolve locally
  550.             addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
  551.                                            socket.SOCK_STREAM,
  552.                                            socket.IPPROTO_TCP,
  553.                                            socket.AI_ADDRCONFIG)
  554.             # We can't really work out what IP is reachable, so just pick the
  555.             # first.
  556.             target_addr = addresses[0]
  557.             family = target_addr[0]
  558.             host = target_addr[4][0]
  559.  
  560.             addr_bytes = socket.inet_pton(family, host)
  561.             file.write(family_to_byte[family] + addr_bytes)
  562.             host = socket.inet_ntop(family, addr_bytes)
  563.         file.write(struct.pack(">H", port))
  564.         return host, port
  565.  
  566.     def _read_SOCKS5_address(self, file):
  567.         atyp = self._readall(file, 1)
  568.         if atyp == b"\x01":
  569.             addr = socket.inet_ntoa(self._readall(file, 4))
  570.         elif atyp == b"\x03":
  571.             length = self._readall(file, 1)
  572.             addr = self._readall(file, ord(length))
  573.         elif atyp == b"\x04":
  574.             addr = socket.inet_ntop(socket.AF_INET6, self._readall(file, 16))
  575.         else:
  576.             raise GeneralProxyError("SOCKS5 proxy server sent invalid data")
  577.  
  578.         port = struct.unpack(">H", self._readall(file, 2))[0]
  579.         return addr, port
  580.  
  581.     def _negotiate_SOCKS4(self, dest_addr, dest_port):
  582.         """Negotiates a connection through a SOCKS4 server."""
  583.         proxy_type, addr, port, rdns, username, password = self.proxy
  584.  
  585.         writer = self.makefile("wb")
  586.         reader = self.makefile("rb", 0)  # buffering=0 renamed in Python 3
  587.         try:
  588.             # Check if the destination address provided is an IP address
  589.             remote_resolve = False
  590.             try:
  591.                 addr_bytes = socket.inet_aton(dest_addr)
  592.             except socket.error:
  593.                 # It's a DNS name. Check where it should be resolved.
  594.                 if rdns:
  595.                     addr_bytes = b"\x00\x00\x00\x01"
  596.                     remote_resolve = True
  597.                 else:
  598.                     addr_bytes = socket.inet_aton(
  599.                         socket.gethostbyname(dest_addr))
  600.  
  601.             # Construct the request packet
  602.             writer.write(struct.pack(">BBH", 0x04, 0x01, dest_port))
  603.             writer.write(addr_bytes)
  604.  
  605.             # The username parameter is considered userid for SOCKS4
  606.             if username:
  607.                 writer.write(username)
  608.             writer.write(b"\x00")
  609.  
  610.             # DNS name if remote resolving is required
  611.             # NOTE: This is actually an extension to the SOCKS4 protocol
  612.             # called SOCKS4A and may not be supported in all cases.
  613.             if remote_resolve:
  614.                 writer.write(dest_addr.encode("idna") + b"\x00")
  615.             writer.flush()
  616.  
  617.             # Get the response from the server
  618.             resp = self._readall(reader, 8)
  619.             if resp[0:1] != b"\x00":
  620.                 # Bad data
  621.                 raise GeneralProxyError(
  622.                     "SOCKS4 proxy server sent invalid data")
  623.  
  624.             status = ord(resp[1:2])
  625.             if status != 0x5A:
  626.                 # Connection failed: server returned an error
  627.                 error = SOCKS4_ERRORS.get(status, "Unknown error")
  628.                 raise SOCKS4Error("{:#04x}: {}".format(status, error))
  629.  
  630.             # Get the bound address/port
  631.             self.proxy_sockname = (socket.inet_ntoa(resp[4:]),
  632.                                    struct.unpack(">H", resp[2:4])[0])
  633.             if remote_resolve:
  634.                 self.proxy_peername = socket.inet_ntoa(addr_bytes), dest_port
  635.             else:
  636.                 self.proxy_peername = dest_addr, dest_port
  637.         finally:
  638.             reader.close()
  639.             writer.close()
  640.  
  641.     def _negotiate_HTTP(self, dest_addr, dest_port):
  642.         """Negotiates a connection through an HTTP server.
  643.  
  644.        NOTE: This currently only supports HTTP CONNECT-style proxies."""
  645.         proxy_type, addr, port, rdns, username, password = self.proxy
  646.  
  647.         # If we need to resolve locally, we do this now
  648.         addr = dest_addr if rdns else socket.gethostbyname(dest_addr)
  649.  
  650.         http_headers = [
  651.             (b"CONNECT " + addr.encode("idna") + b":"
  652.              + str(dest_port).encode() + b" HTTP/1.1"),
  653.             b"Host: " + dest_addr.encode("idna")
  654.         ]
  655.  
  656.         if username and password:
  657.             http_headers.append(b"Proxy-Authorization: basic "
  658.                                 + b64encode(username + b":" + password))
  659.  
  660.         http_headers.append(b"\r\n")
  661.  
  662.         self.sendall(b"\r\n".join(http_headers))
  663.  
  664.         # We just need the first line to check if the connection was successful
  665.         fobj = self.makefile()
  666.         status_line = fobj.readline()
  667.         fobj.close()
  668.  
  669.         if not status_line:
  670.             raise GeneralProxyError("Connection closed unexpectedly")
  671.  
  672.         try:
  673.             proto, status_code, status_msg = status_line.split(" ", 2)
  674.         except ValueError:
  675.             raise GeneralProxyError("HTTP proxy server sent invalid response")
  676.  
  677.         if not proto.startswith("HTTP/"):
  678.             raise GeneralProxyError(
  679.                 "Proxy server does not appear to be an HTTP proxy")
  680.  
  681.         try:
  682.             status_code = int(status_code)
  683.         except ValueError:
  684.             raise HTTPError(
  685.                 "HTTP proxy server did not return a valid HTTP status")
  686.  
  687.         if status_code != 200:
  688.             error = "{}: {}".format(status_code, status_msg)
  689.             if status_code in (400, 403, 405):
  690.                 # It's likely that the HTTP proxy server does not support the
  691.                 # CONNECT tunneling method
  692.                 error += ("\n[*] Note: The HTTP proxy server may not be"
  693.                           " supported by PySocks (must be a CONNECT tunnel"
  694.                           " proxy)")
  695.             raise HTTPError(error)
  696.  
  697.         self.proxy_sockname = (b"0.0.0.0", 0)
  698.         self.proxy_peername = addr, dest_port
  699.  
  700.     _proxy_negotiators = {
  701.                            SOCKS4: _negotiate_SOCKS4,
  702.                            SOCKS5: _negotiate_SOCKS5,
  703.                            HTTP: _negotiate_HTTP
  704.                          }
  705.  
  706.     @set_self_blocking
  707.     def connect(self, dest_pair, catch_errors=None):
  708.         """
  709.        Connects to the specified destination through a proxy.
  710.        Uses the same API as socket's connect().
  711.        To select the proxy server, use set_proxy().
  712.  
  713.        dest_pair - 2-tuple of (IP/hostname, port).
  714.        """
  715.         if len(dest_pair) != 2 or dest_pair[0].startswith("["):
  716.             # Probably IPv6, not supported -- raise an error, and hope
  717.             # Happy Eyeballs (RFC6555) makes sure at least the IPv4
  718.             # connection works...
  719.             raise socket.error("PySocks doesn't support IPv6: %s"
  720.                                % str(dest_pair))
  721.  
  722.         dest_addr, dest_port = dest_pair
  723.  
  724.         if self.type == socket.SOCK_DGRAM:
  725.             if not self._proxyconn:
  726.                 self.bind(("", 0))
  727.             dest_addr = socket.gethostbyname(dest_addr)
  728.  
  729.             # If the host address is INADDR_ANY or similar, reset the peer
  730.             # address so that packets are received from any peer
  731.             if dest_addr == "0.0.0.0" and not dest_port:
  732.                 self.proxy_peername = None
  733.             else:
  734.                 self.proxy_peername = (dest_addr, dest_port)
  735.             return
  736.  
  737.         (proxy_type, proxy_addr, proxy_port, rdns, username,
  738.          password) = self.proxy
  739.  
  740.         # Do a minimal input check first
  741.         if (not isinstance(dest_pair, (list, tuple))
  742.                 or len(dest_pair) != 2
  743.                 or not dest_addr
  744.                 or not isinstance(dest_port, int)):
  745.             # Inputs failed, raise an error
  746.             raise GeneralProxyError(
  747.                 "Invalid destination-connection (host, port) pair")
  748.  
  749.         # We set the timeout here so that we don't hang in connection or during
  750.         # negotiation.
  751.         super(socksocket, self).settimeout(self._timeout)
  752.  
  753.         if proxy_type is None:
  754.             # Treat like regular socket object
  755.             self.proxy_peername = dest_pair
  756.             super(socksocket, self).settimeout(self._timeout)
  757.             super(socksocket, self).connect((dest_addr, dest_port))
  758.             return
  759.  
  760.         proxy_addr = self._proxy_addr()
  761.  
  762.         try:
  763.             # Initial connection to proxy server.
  764.             super(socksocket, self).connect(proxy_addr)
  765.  
  766.         except socket.error as error:
  767.             # Error while connecting to proxy
  768.             self.close()
  769.             if not catch_errors:
  770.                 proxy_addr, proxy_port = proxy_addr
  771.                 proxy_server = "{}:{}".format(proxy_addr, proxy_port)
  772.                 printable_type = PRINTABLE_PROXY_TYPES[proxy_type]
  773.  
  774.                 msg = "Error connecting to {} proxy {}".format(printable_type,
  775.                                                                     proxy_server)
  776.                 log.debug("%s due to: %s", msg, error)
  777.                 raise ProxyConnectionError(msg, error)
  778.             else:
  779.                 raise error
  780.  
  781.         else:
  782.             # Connected to proxy server, now negotiate
  783.             try:
  784.                 # Calls negotiate_{SOCKS4, SOCKS5, HTTP}
  785.                 negotiate = self._proxy_negotiators[proxy_type]
  786.                 negotiate(self, dest_addr, dest_port)
  787.             except socket.error as error:
  788.                 if not catch_errors:
  789.                     # Wrap socket errors
  790.                     self.close()
  791.                     raise GeneralProxyError("Socket error", error)
  792.                 else:
  793.                     raise error
  794.             except ProxyError:
  795.                 # Protocol error while negotiating with proxy
  796.                 self.close()
  797.                 raise
  798.                
  799.     @set_self_blocking
  800.     def connect_ex(self, dest_pair):
  801.         """ https://docs.python.org/3/library/socket.html#socket.socket.connect_ex
  802.        Like connect(address), but return an error indicator instead of raising an exception for errors returned by the C-level connect() call (other problems, such as "host not found" can still raise exceptions).
  803.        """
  804.         try:
  805.             self.connect(dest_pair, catch_errors=True)
  806.             return 0
  807.         except OSError as e:
  808.             # If the error is numeric (socket errors are numeric), then return number as
  809.             # connect_ex expects. Otherwise raise the error again (socket timeout for example)
  810.             if e.errno:
  811.                 return e.errno
  812.             else:
  813.                 raise
  814.  
  815.     def _proxy_addr(self):
  816.         """
  817.        Return proxy address to connect to as tuple object
  818.        """
  819.         (proxy_type, proxy_addr, proxy_port, rdns, username,
  820.          password) = self.proxy
  821.         proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type)
  822.         if not proxy_port:
  823.             raise GeneralProxyError("Invalid proxy type")
  824.         return proxy_addr, proxy_port
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement