From f989cbad6ed2fe9077763dd41480e40b31239ad4 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sun, 9 Sep 2018 12:40:03 +0100 Subject: [PATCH] =?UTF-8?q?Update=20PySocks=201.6.8=20(524ceb4)=20?= =?UTF-8?q?=E2=86=92=201.6.8=20(b687a34).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.md | 3 ++- lib/socks/socks.py | 67 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7bc81084..96552add 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,8 @@ * Update Certifi 2018.01.18 (e225253) to 2018.08.24 (8be9f89) * Update dateutil module 2.7.2 (ff03c0f) to 2.7.2 (49690ee) * Update feedparser 5.2.1 (5646f4c) to 5.2.1 (2b11c80) -* Update profilehooks module 1.10.0 (0ce1e29) to 1.10.1 (fdbf19d) +* Update profilehooks module 1.10.0 (0ce1e29) to 1.10.1 (fdbf19d) +* Update PySocks 1.6.8 (524ceb4) to 1.6.8 (b687a34) [develop changelog] diff --git a/lib/socks/socks.py b/lib/socks/socks.py index 7bcf590d..7aed0377 100644 --- a/lib/socks/socks.py +++ b/lib/socks/socks.py @@ -55,7 +55,10 @@ Modifications made by Anorov (https://github.com/Anorov) """ from base64 import b64encode -from collections import Callable +try: + from collections.abc import Callable +except ImportError: + from collections import Callable from errno import EOPNOTSUPP, EINVAL, EAGAIN import functools from io import BytesIO @@ -114,7 +117,7 @@ class ProxyError(IOError): self.socket_err = socket_err if socket_err: - self.msg += ": {0}".format(socket_err) + self.msg += ": {}".format(socket_err) def __str__(self): return self.msg @@ -533,6 +536,14 @@ class socksocket(_BaseSocket): if chosen_auth[1:2] == b"\x02": # Okay, we need to perform a basic username/password # authentication. + if not (username and password): + # Although we said we don't support authentication, the + # server may still request basic username/password + # authentication + raise SOCKS5AuthError("No username/password supplied. " + "Server requested username/password" + " authentication") + writer.write(b"\x01" + chr(len(username)).encode() + username + chr(len(password)).encode() @@ -575,7 +586,7 @@ class socksocket(_BaseSocket): if status != 0x00: # Connection failed: server returned an error error = SOCKS5_ERRORS.get(status, "Unknown error") - raise SOCKS5Error("{0:#04x}: {1}".format(status, error)) + raise SOCKS5Error("{:#04x}: {}".format(status, error)) # Get the bound address/port bnd = self._read_SOCKS5_address(reader) @@ -693,7 +704,7 @@ class socksocket(_BaseSocket): if status != 0x5A: # Connection failed: server returned an error error = SOCKS4_ERRORS.get(status, "Unknown error") - raise SOCKS4Error("{0:#04x}: {1}".format(status, error)) + raise SOCKS4Error("{:#04x}: {}".format(status, error)) # Get the bound address/port self.proxy_sockname = (socket.inet_ntoa(resp[4:]), @@ -753,7 +764,7 @@ class socksocket(_BaseSocket): "HTTP proxy server did not return a valid HTTP status") if status_code != 200: - error = "{0}: {1}".format(status_code, status_msg) + error = "{}: {}".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 @@ -772,7 +783,7 @@ class socksocket(_BaseSocket): } @set_self_blocking - def connect(self, dest_pair): + def connect(self, dest_pair, catch_errors=None): """ Connects to the specified destination through a proxy. Uses the same API as socket's connect(). @@ -834,14 +845,17 @@ class socksocket(_BaseSocket): except socket.error as error: # Error while connecting to proxy self.close() - proxy_addr, proxy_port = proxy_addr - proxy_server = "{0}:{1}".format(proxy_addr, proxy_port) - printable_type = PRINTABLE_PROXY_TYPES[proxy_type] + if not catch_errors: + proxy_addr, proxy_port = proxy_addr + proxy_server = "{}:{}".format(proxy_addr, proxy_port) + printable_type = PRINTABLE_PROXY_TYPES[proxy_type] - msg = "Error connecting to {0} proxy {1}".format(printable_type, - proxy_server) - log.debug("%s due to: %s", msg, error) - raise ProxyConnectionError(msg, error) + msg = "Error connecting to {} proxy {}".format(printable_type, + proxy_server) + log.debug("%s due to: %s", msg, error) + raise ProxyConnectionError(msg, error) + else: + raise error else: # Connected to proxy server, now negotiate @@ -850,13 +864,32 @@ class socksocket(_BaseSocket): negotiate = self._proxy_negotiators[proxy_type] negotiate(self, dest_addr, dest_port) except socket.error as error: - # Wrap socket errors - self.close() - raise GeneralProxyError("Socket error", error) + if not catch_errors: + # Wrap socket errors + self.close() + raise GeneralProxyError("Socket error", error) + else: + raise error except ProxyError: # Protocol error while negotiating with proxy self.close() raise + + @set_self_blocking + def connect_ex(self, dest_pair): + """ https://docs.python.org/3/library/socket.html#socket.socket.connect_ex + 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). + """ + try: + self.connect(dest_pair, catch_errors=True) + return 0 + except OSError as e: + # If the error is numeric (socket errors are numeric), then return number as + # connect_ex expects. Otherwise raise the error again (socket timeout for example) + if e.errno: + return e.errno + else: + raise def _proxy_addr(self): """ @@ -867,4 +900,4 @@ class socksocket(_BaseSocket): proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type) if not proxy_port: raise GeneralProxyError("Invalid proxy type") - return proxy_addr, proxy_port + return proxy_addr, proxy_port \ No newline at end of file