From 9ce4e7c4d0f914d0e3e237190739302f17efe244 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Mon, 26 Mar 2018 21:05:12 +0100 Subject: [PATCH] =?UTF-8?q?Update=20PySocks=201.6.5=20(b4323df)=20?= =?UTF-8?q?=E2=86=92=201.6.8=20(524ceb4).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 1 + lib/socks/socks.py | 372 ++++++++++++++++++++++--------------- lib/win_inet_pton/__init__.py | 1 + lib/win_inet_pton/win_inet_pton.py | 84 +++++++++ 4 files changed, 306 insertions(+), 152 deletions(-) create mode 100644 lib/win_inet_pton/__init__.py create mode 100644 lib/win_inet_pton/win_inet_pton.py diff --git a/CHANGES.md b/CHANGES.md index fa63098..fe2ba62 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ * Update moment.js 2.17.1 to 2.21.0 * Update profilehooks 1.9.0 (de7d59b) to 1.10.0 (0ce1e29) * Update Certifi 2017.07.27 (f808089) to 2018.01.18 (e225253) +* Update PySocks 1.6.5 (b4323df) to 1.6.8 (524ceb4) [develop changelog] diff --git a/lib/socks/socks.py b/lib/socks/socks.py index 9184eae..7bcf590 100644 --- a/lib/socks/socks.py +++ b/lib/socks/socks.py @@ -1,11 +1,9 @@ -""" -SocksiPy - Python SOCKS module. -Version 1.6.5 +"""SocksiPy - Python SOCKS module. Copyright 2006 Dan-Haim. All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, @@ -23,7 +21,7 @@ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE. +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. This module provides a standard socket-like interface for Python @@ -39,37 +37,46 @@ mainly to merge bug fixes found in Sourceforge Modifications made by Anorov (https://github.com/Anorov) -Forked and renamed to PySocks --Fixed issue with HTTP proxy failure checking (same bug that was in the old ___recvall() method) +-Fixed issue with HTTP proxy failure checking (same bug that was in the + old ___recvall() method) -Included SocksiPyHandler (sockshandler.py), to be used as a urllib2 handler, - courtesy of e000 (https://github.com/e000): https://gist.github.com/869791#file_socksipyhandler.py + courtesy of e000 (https://github.com/e000): + https://gist.github.com/869791#file_socksipyhandler.py -Re-styled code to make it readable -Aliased PROXY_TYPE_SOCKS5 -> SOCKS5 etc. -Improved exception handling and output - -Removed irritating use of sequence indexes, replaced with tuple unpacked variables + -Removed irritating use of sequence indexes, replaced with tuple unpacked + variables -Fixed up Python 3 bytestring handling - chr(0x03).encode() -> b"\x03" -Other general fixes --Added clarification that the HTTP proxy connection method only supports CONNECT-style tunneling HTTP proxies +-Added clarification that the HTTP proxy connection method only supports + CONNECT-style tunneling HTTP proxies -Various small bug fixes """ -__version__ = "1.6.5" - -import socket -import struct +from base64 import b64encode +from collections import Callable from errno import EOPNOTSUPP, EINVAL, EAGAIN +import functools from io import BytesIO -from os import SEEK_CUR +import logging import os +from os import SEEK_CUR +import socket +import struct import sys -from collections import Callable -from base64 import b64encode + +__version__ = "1.6.7" if os.name == "nt" and sys.version_info < (3, 0): try: import win_inet_pton except ImportError: - raise ImportError("To run PySocks on Windows you must install win_inet_pton") + raise ImportError( + "To run PySocks on Windows you must install win_inet_pton") + +log = logging.getLogger(__name__) PROXY_TYPE_SOCKS4 = SOCKS4 = 1 PROXY_TYPE_SOCKS5 = SOCKS5 = 2 @@ -80,10 +87,28 @@ PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys())) _orgsocket = _orig_socket = socket.socket + +def set_self_blocking(function): + + @functools.wraps(function) + def wrapper(*args, **kwargs): + self = args[0] + try: + _is_blocking = self.gettimeout() + if _is_blocking == 0: + self.setblocking(True) + return function(*args, **kwargs) + except Exception as e: + raise + finally: + # set orgin blocking + if _is_blocking == 0: + self.setblocking(False) + return wrapper + + class ProxyError(IOError): - """ - socket_err contains original socket.error exception. - """ + """Socket_err contains original socket.error exception.""" def __init__(self, msg, socket_err=None): self.msg = msg self.socket_err = socket_err @@ -94,61 +119,82 @@ class ProxyError(IOError): def __str__(self): return self.msg -class GeneralProxyError(ProxyError): pass -class ProxyConnectionError(ProxyError): pass -class SOCKS5AuthError(ProxyError): pass -class SOCKS5Error(ProxyError): pass -class SOCKS4Error(ProxyError): pass -class HTTPError(ProxyError): pass - -SOCKS4_ERRORS = { 0x5B: "Request rejected or failed", - 0x5C: "Request rejected because SOCKS server cannot connect to identd on the client", - 0x5D: "Request rejected because the client program and identd report different user-ids" - } - -SOCKS5_ERRORS = { 0x01: "General SOCKS server failure", - 0x02: "Connection not allowed by ruleset", - 0x03: "Network unreachable", - 0x04: "Host unreachable", - 0x05: "Connection refused", - 0x06: "TTL expired", - 0x07: "Command not supported, or protocol error", - 0x08: "Address type not supported" - } - -DEFAULT_PORTS = { SOCKS4: 1080, - SOCKS5: 1080, - HTTP: 8080 - } - -def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None): - """ - set_default_proxy(proxy_type, addr[, port[, rdns[, username, password]]]) - Sets a default proxy which all further socksocket objects will use, - unless explicitly changed. All parameters are as for socket.set_proxy(). - """ +class GeneralProxyError(ProxyError): + pass + + +class ProxyConnectionError(ProxyError): + pass + + +class SOCKS5AuthError(ProxyError): + pass + + +class SOCKS5Error(ProxyError): + pass + + +class SOCKS4Error(ProxyError): + pass + + +class HTTPError(ProxyError): + pass + +SOCKS4_ERRORS = { + 0x5B: "Request rejected or failed", + 0x5C: ("Request rejected because SOCKS server cannot connect to identd on" + " the client"), + 0x5D: ("Request rejected because the client program and identd report" + " different user-ids") +} + +SOCKS5_ERRORS = { + 0x01: "General SOCKS server failure", + 0x02: "Connection not allowed by ruleset", + 0x03: "Network unreachable", + 0x04: "Host unreachable", + 0x05: "Connection refused", + 0x06: "TTL expired", + 0x07: "Command not supported, or protocol error", + 0x08: "Address type not supported" +} + +DEFAULT_PORTS = {SOCKS4: 1080, SOCKS5: 1080, HTTP: 8080} + + +def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, + username=None, password=None): + """Sets a default proxy. + + All further socksocket objects will use the default unless explicitly + changed. All parameters are as for socket.set_proxy().""" socksocket.default_proxy = (proxy_type, addr, port, rdns, username.encode() if username else None, password.encode() if password else None) -setdefaultproxy = set_default_proxy + +def setdefaultproxy(*args, **kwargs): + if "proxytype" in kwargs: + kwargs["proxy_type"] = kwargs.pop("proxytype") + return set_default_proxy(*args, **kwargs) + def get_default_proxy(): - """ - Returns the default proxy, set by set_default_proxy. - """ + """Returns the default proxy, set by set_default_proxy.""" return socksocket.default_proxy getdefaultproxy = get_default_proxy + def wrap_module(module): - """ - Attempts to replace a module's socket library with a SOCKS socket. Must set - a default proxy using set_default_proxy(...) first. - This will only work on modules that import socket directly into the namespace; - most of the Python Standard Library falls into this category. - """ + """Attempts to replace a module's socket library with a SOCKS socket. + + Must set a default proxy using set_default_proxy(...) first. This will + only work on modules that import socket directly into the namespace; + most of the Python Standard Library falls into this category.""" if socksocket.default_proxy: module.socket.socket = socksocket else: @@ -156,10 +202,12 @@ def wrap_module(module): wrapmodule = wrap_module -def create_connection(dest_pair, proxy_type=None, proxy_addr=None, + +def create_connection(dest_pair, + timeout=None, source_address=None, + proxy_type=None, proxy_addr=None, proxy_port=None, proxy_rdns=True, proxy_username=None, proxy_password=None, - timeout=None, source_address=None, socket_options=None): """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object @@ -174,10 +222,10 @@ def create_connection(dest_pair, proxy_type=None, proxy_addr=None, """ # Remove IPv6 brackets on the remote address and proxy address. remote_host, remote_port = dest_pair - if remote_host.startswith('['): - remote_host = remote_host.strip('[]') - if proxy_addr and proxy_addr.startswith('['): - proxy_addr = proxy_addr.strip('[]') + if remote_host.startswith("["): + remote_host = remote_host.strip("[]") + if proxy_addr and proxy_addr.startswith("["): + proxy_addr = proxy_addr.strip("[]") err = None @@ -215,9 +263,9 @@ def create_connection(dest_pair, proxy_type=None, proxy_addr=None, raise socket.error("gai returned empty list.") + class _BaseSocket(socket.socket): - """Allows Python 2's "delegated" methods such as send() to be overridden - """ + """Allows Python 2 delegated methods such as send() to be overridden.""" def __init__(self, *pos, **kw): _orig_socket.__init__(self, *pos, **kw) @@ -228,6 +276,7 @@ class _BaseSocket(socket.socket): _savenames = list() + def _makemethod(name): return lambda self, *pos, **kw: self._savedmethods[name](*pos, **kw) for name in ("sendto", "send", "recvfrom", "recv"): @@ -241,6 +290,7 @@ for name in ("sendto", "send", "recvfrom", "recv"): _BaseSocket._savenames.append(name) setattr(_BaseSocket, name, _makemethod(name)) + class socksocket(_BaseSocket): """socksocket([family[, type[, proto]]]) -> socket object @@ -252,12 +302,13 @@ class socksocket(_BaseSocket): default_proxy = None - def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, *args, **kwargs): + def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, + proto=0, *args, **kwargs): if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM): msg = "Socket type must be stream or datagram, not {!r}" raise ValueError(msg.format(type)) - _BaseSocket.__init__(self, family, type, proto, *args, **kwargs) + super(socksocket, self).__init__(family, type, proto, *args, **kwargs) self._proxyconn = None # TCP connection to keep UDP relay alive if self.default_proxy: @@ -270,10 +321,9 @@ class socksocket(_BaseSocket): self._timeout = None def _readall(self, file, count): - """ - Receive EXACTLY the number of bytes requested from the file object. - Blocks until the required number of bytes have been received. - """ + """Receive EXACTLY the number of bytes requested from the file object. + + Blocks until the required number of bytes have been received.""" data = b"" while len(data) < count: d = file.read(count - len(data)) @@ -287,7 +337,7 @@ class socksocket(_BaseSocket): try: # test if we're connected, if so apply timeout peer = self.get_proxy_peername() - _BaseSocket.settimeout(self, self._timeout) + super(socksocket, self).settimeout(self._timeout) except socket.error: pass @@ -300,36 +350,38 @@ class socksocket(_BaseSocket): else: self.settimeout(0.0) - def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None): - """set_proxy(proxy_type, addr[, port[, rdns[, username[, password]]]]) - Sets the proxy to be used. + def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True, + username=None, password=None): + """ Sets the proxy to be used. - proxy_type - The type of the proxy to be used. Three types + proxy_type - The type of the proxy to be used. Three types are supported: PROXY_TYPE_SOCKS4 (including socks4a), PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP addr - The address of the server (IP or DNS). port - The port of the server. Defaults to 1080 for SOCKS - servers and 8080 for HTTP proxy servers. + servers and 8080 for HTTP proxy servers. rdns - Should DNS queries be performed on the remote side (rather than the local side). The default is True. Note: This has no effect with SOCKS4 servers. username - Username to authenticate with to the server. The default is no authentication. password - Password to authenticate with to the server. - Only relevant when username is also provided. - """ + Only relevant when username is also provided.""" self.proxy = (proxy_type, addr, port, rdns, username.encode() if username else None, password.encode() if password else None) - setproxy = set_proxy + def setproxy(self, *args, **kwargs): + if "proxytype" in kwargs: + kwargs["proxy_type"] = kwargs.pop("proxytype") + return self.set_proxy(*args, **kwargs) def bind(self, *pos, **kw): - """ - Implements proxy connection for UDP sockets, - which happens during the bind() phase. - """ - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy + """Implements proxy connection for UDP sockets. + + Happens during the bind() phase.""" + (proxy_type, proxy_addr, proxy_port, rdns, username, + password) = self.proxy if not proxy_type or self.type != socket.SOCK_DGRAM: return _orig_socket.bind(self, *pos, **kw) @@ -338,7 +390,7 @@ class socksocket(_BaseSocket): if proxy_type != SOCKS5: msg = "UDP only supported by SOCKS5 proxy type" raise socket.error(EOPNOTSUPP, msg) - _BaseSocket.bind(self, *pos, **kw) + super(socksocket, self).bind(*pos, **kw) # Need to specify actual local port because # some relays drop packets if a port of zero is specified. @@ -357,13 +409,13 @@ class socksocket(_BaseSocket): # but some proxies return a private IP address (10.x.y.z) host, _ = proxy _, port = relay - _BaseSocket.connect(self, (host, port)) - _BaseSocket.settimeout(self, self._timeout) + super(socksocket, self).connect((host, port)) + super(socksocket, self).settimeout(self._timeout) self.proxy_sockname = ("0.0.0.0", 0) # Unknown def sendto(self, bytes, *args, **kwargs): if self.type != socket.SOCK_DGRAM: - return _BaseSocket.sendto(self, bytes, *args, **kwargs) + return super(socksocket, self).sendto(bytes, *args, **kwargs) if not self._proxyconn: self.bind(("", 0)) @@ -377,22 +429,23 @@ class socksocket(_BaseSocket): header.write(STANDALONE) self._write_SOCKS5_address(address, header) - sent = _BaseSocket.send(self, header.getvalue() + bytes, *flags, **kwargs) + sent = super(socksocket, self).send(header.getvalue() + bytes, *flags, + **kwargs) return sent - header.tell() def send(self, bytes, flags=0, **kwargs): if self.type == socket.SOCK_DGRAM: return self.sendto(bytes, flags, self.proxy_peername, **kwargs) else: - return _BaseSocket.send(self, bytes, flags, **kwargs) + return super(socksocket, self).send(bytes, flags, **kwargs) def recvfrom(self, bufsize, flags=0): if self.type != socket.SOCK_DGRAM: - return _BaseSocket.recvfrom(self, bufsize, flags) + return super(socksocket, self).recvfrom(bufsize, flags) if not self._proxyconn: self.bind(("", 0)) - buf = BytesIO(_BaseSocket.recv(self, bufsize + 1024, flags)) + buf = BytesIO(super(socksocket, self).recv(bufsize + 1024, flags)) buf.seek(2, SEEK_CUR) frag = buf.read(1) if ord(frag): @@ -413,12 +466,10 @@ class socksocket(_BaseSocket): def close(self): if self._proxyconn: self._proxyconn.close() - return _BaseSocket.close(self) + return super(socksocket, self).close() def get_proxy_sockname(self): - """ - Returns the bound IP address and port number at the proxy. - """ + """Returns the bound IP address and port number at the proxy.""" return self.proxy_sockname getproxysockname = get_proxy_sockname @@ -427,26 +478,23 @@ class socksocket(_BaseSocket): """ Returns the IP and port number of the proxy. """ - return _BaseSocket.getpeername(self) + return self.getpeername() getproxypeername = get_proxy_peername def get_peername(self): - """ - Returns the IP address and port number of the destination - machine (note: get_proxy_peername returns the proxy) - """ + """Returns the IP address and port number of the destination machine. + + Note: get_proxy_peername returns the proxy.""" return self.proxy_peername getpeername = get_peername def _negotiate_SOCKS5(self, *dest_addr): - """ - Negotiates a stream connection through a SOCKS5 server. - """ + """Negotiates a stream connection through a SOCKS5 server.""" CONNECT = b"\x01" - self.proxy_peername, self.proxy_sockname = self._SOCKS5_request(self, - CONNECT, dest_addr) + self.proxy_peername, self.proxy_sockname = self._SOCKS5_request( + self, CONNECT, dest_addr) def _SOCKS5_request(self, conn, cmd, dst): """ @@ -477,7 +525,8 @@ class socksocket(_BaseSocket): if chosen_auth[0:1] != b"\x05": # Note: string[i:i+1] is used because indexing of a bytestring # via bytestring[i] yields an integer in Python 3 - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") + raise GeneralProxyError( + "SOCKS5 proxy server sent invalid data") # Check the chosen authentication method @@ -492,7 +541,8 @@ class socksocket(_BaseSocket): auth_status = self._readall(reader, 2) if auth_status[0:1] != b"\x01": # Bad response - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") + raise GeneralProxyError( + "SOCKS5 proxy server sent invalid data") if auth_status[1:2] != b"\x00": # Authentication failed raise SOCKS5AuthError("SOCKS5 authentication failed") @@ -503,9 +553,12 @@ class socksocket(_BaseSocket): elif chosen_auth[1:2] != b"\x00": # Reaching here is always bad if chosen_auth[1:2] == b"\xFF": - raise SOCKS5AuthError("All offered SOCKS5 authentication methods were rejected") + raise SOCKS5AuthError( + "All offered SOCKS5 authentication methods were" + " rejected") else: - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") + raise GeneralProxyError( + "SOCKS5 proxy server sent invalid data") # Now we can request the actual connection writer.write(b"\x05" + cmd + b"\x00") @@ -515,7 +568,8 @@ class socksocket(_BaseSocket): # Get the response resp = self._readall(reader, 3) if resp[0:1] != b"\x05": - raise GeneralProxyError("SOCKS5 proxy server sent invalid data") + raise GeneralProxyError( + "SOCKS5 proxy server sent invalid data") status = ord(resp[1:2]) if status != 0x00: @@ -526,7 +580,7 @@ class socksocket(_BaseSocket): # Get the bound address/port bnd = self._read_SOCKS5_address(reader) - _BaseSocket.settimeout(self, self._timeout) + super(socksocket, self).settimeout(self._timeout) return (resolved, bnd) finally: reader.close() @@ -557,11 +611,14 @@ class socksocket(_BaseSocket): # Well it's not an IP number, so it's probably a DNS name. if rdns: # Resolve remotely - host_bytes = host.encode('idna') + host_bytes = host.encode("idna") file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes) else: # Resolve locally - addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_ADDRCONFIG) + addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + socket.AI_ADDRCONFIG) # We can't really work out what IP is reachable, so just pick the # first. target_addr = addresses[0] @@ -590,9 +647,7 @@ class socksocket(_BaseSocket): return addr, port def _negotiate_SOCKS4(self, dest_addr, dest_port): - """ - Negotiates a connection through a SOCKS4 server. - """ + """Negotiates a connection through a SOCKS4 server.""" proxy_type, addr, port, rdns, username, password = self.proxy writer = self.makefile("wb") @@ -608,7 +663,8 @@ class socksocket(_BaseSocket): addr_bytes = b"\x00\x00\x00\x01" remote_resolve = True else: - addr_bytes = socket.inet_aton(socket.gethostbyname(dest_addr)) + addr_bytes = socket.inet_aton( + socket.gethostbyname(dest_addr)) # Construct the request packet writer.write(struct.pack(">BBH", 0x04, 0x01, dest_port)) @@ -623,14 +679,15 @@ class socksocket(_BaseSocket): # NOTE: This is actually an extension to the SOCKS4 protocol # called SOCKS4A and may not be supported in all cases. if remote_resolve: - writer.write(dest_addr.encode('idna') + b"\x00") + writer.write(dest_addr.encode("idna") + b"\x00") writer.flush() # Get the response from the server resp = self._readall(reader, 8) if resp[0:1] != b"\x00": # Bad data - raise GeneralProxyError("SOCKS4 proxy server sent invalid data") + raise GeneralProxyError( + "SOCKS4 proxy server sent invalid data") status = ord(resp[1:2]) if status != 0x5A: @@ -639,7 +696,8 @@ class socksocket(_BaseSocket): raise SOCKS4Error("{0:#04x}: {1}".format(status, error)) # Get the bound address/port - self.proxy_sockname = (socket.inet_ntoa(resp[4:]), struct.unpack(">H", resp[2:4])[0]) + self.proxy_sockname = (socket.inet_ntoa(resp[4:]), + struct.unpack(">H", resp[2:4])[0]) if remote_resolve: self.proxy_peername = socket.inet_ntoa(addr_bytes), dest_port else: @@ -649,22 +707,23 @@ class socksocket(_BaseSocket): writer.close() def _negotiate_HTTP(self, dest_addr, dest_port): - """ - Negotiates a connection through an HTTP server. - NOTE: This currently only supports HTTP CONNECT-style proxies. - """ + """Negotiates a connection through an HTTP server. + + NOTE: This currently only supports HTTP CONNECT-style proxies.""" proxy_type, addr, port, rdns, username, password = self.proxy # If we need to resolve locally, we do this now addr = dest_addr if rdns else socket.gethostbyname(dest_addr) http_headers = [ - b"CONNECT " + addr.encode('idna') + b":" + str(dest_port).encode() + b" HTTP/1.1", - b"Host: " + dest_addr.encode('idna') + (b"CONNECT " + addr.encode("idna") + b":" + + str(dest_port).encode() + b" HTTP/1.1"), + b"Host: " + dest_addr.encode("idna") ] if username and password: - http_headers.append(b"Proxy-Authorization: basic " + b64encode(username + b":" + password)) + http_headers.append(b"Proxy-Authorization: basic " + + b64encode(username + b":" + password)) http_headers.append(b"\r\n") @@ -684,19 +743,23 @@ class socksocket(_BaseSocket): raise GeneralProxyError("HTTP proxy server sent invalid response") if not proto.startswith("HTTP/"): - raise GeneralProxyError("Proxy server does not appear to be an HTTP proxy") + raise GeneralProxyError( + "Proxy server does not appear to be an HTTP proxy") try: status_code = int(status_code) except ValueError: - raise HTTPError("HTTP proxy server did not return a valid HTTP status") + raise HTTPError( + "HTTP proxy server did not return a valid HTTP status") if status_code != 200: error = "{0}: {1}".format(status_code, status_msg) if status_code in (400, 403, 405): - # It's likely that the HTTP proxy server does not support the CONNECT tunneling method - error += ("\n[*] Note: The HTTP proxy server may not be supported by PySocks" - " (must be a CONNECT tunnel proxy)") + # It's likely that the HTTP proxy server does not support the + # CONNECT tunneling method + error += ("\n[*] Note: The HTTP proxy server may not be" + " supported by PySocks (must be a CONNECT tunnel" + " proxy)") raise HTTPError(error) self.proxy_sockname = (b"0.0.0.0", 0) @@ -708,7 +771,7 @@ class socksocket(_BaseSocket): HTTP: _negotiate_HTTP } - + @set_self_blocking def connect(self, dest_pair): """ Connects to the specified destination through a proxy. @@ -721,7 +784,8 @@ class socksocket(_BaseSocket): # Probably IPv6, not supported -- raise an error, and hope # Happy Eyeballs (RFC6555) makes sure at least the IPv4 # connection works... - raise socket.error("PySocks doesn't support IPv6") + raise socket.error("PySocks doesn't support IPv6: %s" + % str(dest_pair)) dest_addr, dest_port = dest_pair @@ -738,32 +802,34 @@ class socksocket(_BaseSocket): self.proxy_peername = (dest_addr, dest_port) return - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy + (proxy_type, proxy_addr, proxy_port, rdns, username, + password) = self.proxy # Do a minimal input check first if (not isinstance(dest_pair, (list, tuple)) or len(dest_pair) != 2 or not dest_addr or not isinstance(dest_port, int)): - raise GeneralProxyError("Invalid destination-connection (host, port) pair") - + # Inputs failed, raise an error + raise GeneralProxyError( + "Invalid destination-connection (host, port) pair") # We set the timeout here so that we don't hang in connection or during # negotiation. - _BaseSocket.settimeout(self, self._timeout) + super(socksocket, self).settimeout(self._timeout) if proxy_type is None: # Treat like regular socket object self.proxy_peername = dest_pair - _BaseSocket.settimeout(self, self._timeout) - _BaseSocket.connect(self, (dest_addr, dest_port)) + super(socksocket, self).settimeout(self._timeout) + super(socksocket, self).connect((dest_addr, dest_port)) return proxy_addr = self._proxy_addr() try: # Initial connection to proxy server. - _BaseSocket.connect(self, proxy_addr) + super(socksocket, self).connect(proxy_addr) except socket.error as error: # Error while connecting to proxy @@ -773,7 +839,8 @@ class socksocket(_BaseSocket): printable_type = PRINTABLE_PROXY_TYPES[proxy_type] msg = "Error connecting to {0} proxy {1}".format(printable_type, - proxy_server) + proxy_server) + log.debug("%s due to: %s", msg, error) raise ProxyConnectionError(msg, error) else: @@ -795,7 +862,8 @@ class socksocket(_BaseSocket): """ Return proxy address to connect to as tuple object """ - proxy_type, proxy_addr, proxy_port, rdns, username, password = self.proxy + (proxy_type, proxy_addr, proxy_port, rdns, username, + password) = self.proxy proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type) if not proxy_port: raise GeneralProxyError("Invalid proxy type") diff --git a/lib/win_inet_pton/__init__.py b/lib/win_inet_pton/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/win_inet_pton/__init__.py @@ -0,0 +1 @@ + diff --git a/lib/win_inet_pton/win_inet_pton.py b/lib/win_inet_pton/win_inet_pton.py new file mode 100644 index 0000000..12aaf46 --- /dev/null +++ b/lib/win_inet_pton/win_inet_pton.py @@ -0,0 +1,84 @@ +# This software released into the public domain. Anyone is free to copy, +# modify, publish, use, compile, sell, or distribute this software, +# either in source code form or as a compiled binary, for any purpose, +# commercial or non-commercial, and by any means. + +import socket +import ctypes +import os + + +class sockaddr(ctypes.Structure): + _fields_ = [("sa_family", ctypes.c_short), + ("__pad1", ctypes.c_ushort), + ("ipv4_addr", ctypes.c_byte * 4), + ("ipv6_addr", ctypes.c_byte * 16), + ("__pad2", ctypes.c_ulong)] + +if hasattr(ctypes, 'windll'): + WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA + WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringA +else: + def not_windows(): + raise SystemError( + "Invalid platform. ctypes.windll must be available." + ) + WSAStringToAddressA = not_windows + WSAAddressToStringA = not_windows + + +def inet_pton(address_family, ip_string): + addr = sockaddr() + addr.sa_family = address_family + addr_size = ctypes.c_int(ctypes.sizeof(addr)) + + if WSAStringToAddressA( + ip_string, + address_family, + None, + ctypes.byref(addr), + ctypes.byref(addr_size) + ) != 0: + raise socket.error(ctypes.FormatError()) + + if address_family == socket.AF_INET: + return ctypes.string_at(addr.ipv4_addr, 4) + if address_family == socket.AF_INET6: + return ctypes.string_at(addr.ipv6_addr, 16) + + raise socket.error('unknown address family') + + +def inet_ntop(address_family, packed_ip): + addr = sockaddr() + addr.sa_family = address_family + addr_size = ctypes.c_int(ctypes.sizeof(addr)) + ip_string = ctypes.create_string_buffer(128) + ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string)) + + if address_family == socket.AF_INET: + if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr): + raise socket.error('packed IP wrong length for inet_ntoa') + ctypes.memmove(addr.ipv4_addr, packed_ip, 4) + elif address_family == socket.AF_INET6: + if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr): + raise socket.error('packed IP wrong length for inet_ntoa') + ctypes.memmove(addr.ipv6_addr, packed_ip, 16) + else: + raise socket.error('unknown address family') + + if WSAAddressToStringA( + ctypes.byref(addr), + addr_size, + None, + ip_string, + ctypes.byref(ip_string_size) + ) != 0: + raise socket.error(ctypes.FormatError()) + + return ip_string[:ip_string_size.value - 1] + +# Adding our two functions to the socket library +if os.name == 'nt': + socket.inet_pton = inet_pton + socket.inet_ntop = inet_ntop