|
|
@@ -0,0 +1,101 @@
|
|
|
+import logging
|
|
|
+
|
|
|
+from urllib.parse import urlparse
|
|
|
+from urllib3 import PoolManager, HTTPConnectionPool, HTTPSConnectionPool
|
|
|
+from urllib3.connection import HTTPConnection, HTTPSConnection
|
|
|
+from .constants import HTTP_PROXY_SOCK_RDNS_SCHEMAS
|
|
|
+
|
|
|
+
|
|
|
+logger = logging.getLogger('netbox.utilities')
|
|
|
+
|
|
|
+
|
|
|
+class ProxyHTTPConnection(HTTPConnection):
|
|
|
+ """
|
|
|
+ A Proxy connection class that uses a SOCK proxy - used to create
|
|
|
+ a urllib3 PoolManager that routes connections via the proxy.
|
|
|
+ This is for an HTTP (not HTTPS) connection
|
|
|
+ """
|
|
|
+ use_rdns = False
|
|
|
+
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ socks_options = kwargs.pop('_socks_options')
|
|
|
+ self._proxy_url = socks_options['proxy_url']
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+
|
|
|
+ def _new_conn(self):
|
|
|
+ try:
|
|
|
+ from python_socks.sync import Proxy
|
|
|
+ except ModuleNotFoundError as e:
|
|
|
+ logger.info("Configuring an HTTP proxy using SOCKS requires the python_socks library. Check that it has been installed.")
|
|
|
+ raise e
|
|
|
+
|
|
|
+ proxy = Proxy.from_url(self._proxy_url, rdns=self.use_rdns)
|
|
|
+ return proxy.connect(
|
|
|
+ dest_host=self.host,
|
|
|
+ dest_port=self.port,
|
|
|
+ timeout=self.timeout
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+class ProxyHTTPSConnection(ProxyHTTPConnection, HTTPSConnection):
|
|
|
+ """
|
|
|
+ A Proxy connection class for an HTTPS (not HTTP) connection.
|
|
|
+ """
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class RdnsProxyHTTPConnection(ProxyHTTPConnection):
|
|
|
+ """
|
|
|
+ A Proxy connection class for an HTTP remote-dns connection.
|
|
|
+ I.E. socks4a, socks4h, socks5a, socks5h
|
|
|
+ """
|
|
|
+ use_rdns = True
|
|
|
+
|
|
|
+
|
|
|
+class RdnsProxyHTTPSConnection(ProxyHTTPSConnection):
|
|
|
+ """
|
|
|
+ A Proxy connection class for an HTTPS remote-dns connection.
|
|
|
+ I.E. socks4a, socks4h, socks5a, socks5h
|
|
|
+ """
|
|
|
+ use_rdns = True
|
|
|
+
|
|
|
+
|
|
|
+class ProxyHTTPConnectionPool(HTTPConnectionPool):
|
|
|
+ ConnectionCls = ProxyHTTPConnection
|
|
|
+
|
|
|
+
|
|
|
+class ProxyHTTPSConnectionPool(HTTPSConnectionPool):
|
|
|
+ ConnectionCls = ProxyHTTPSConnection
|
|
|
+
|
|
|
+
|
|
|
+class RdnsProxyHTTPConnectionPool(HTTPConnectionPool):
|
|
|
+ ConnectionCls = RdnsProxyHTTPConnection
|
|
|
+
|
|
|
+
|
|
|
+class RdnsProxyHTTPSConnectionPool(HTTPSConnectionPool):
|
|
|
+ ConnectionCls = RdnsProxyHTTPSConnection
|
|
|
+
|
|
|
+
|
|
|
+class ProxyPoolManager(PoolManager):
|
|
|
+ def __init__(self, proxy_url, timeout=5, num_pools=10, headers=None, **connection_pool_kw):
|
|
|
+ # python_socks uses rdns param to denote remote DNS parsing and
|
|
|
+ # doesn't accept the 'h' or 'a' in the proxy URL
|
|
|
+ if use_rdns := urlparse(proxy_url).scheme in HTTP_PROXY_SOCK_RDNS_SCHEMAS:
|
|
|
+ proxy_url = proxy_url.replace('socks5h:', 'socks5:').replace('socks5a:', 'socks5:')
|
|
|
+ proxy_url = proxy_url.replace('socks4h:', 'socks4:').replace('socks4a:', 'socks4:')
|
|
|
+
|
|
|
+ connection_pool_kw['_socks_options'] = {'proxy_url': proxy_url}
|
|
|
+ connection_pool_kw['timeout'] = timeout
|
|
|
+
|
|
|
+ super().__init__(num_pools, headers, **connection_pool_kw)
|
|
|
+
|
|
|
+ if use_rdns:
|
|
|
+ self.pool_classes_by_scheme = {
|
|
|
+ 'http': RdnsProxyHTTPConnectionPool,
|
|
|
+ 'https': RdnsProxyHTTPSConnectionPool,
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ self.pool_classes_by_scheme = {
|
|
|
+ 'http': ProxyHTTPConnectionPool,
|
|
|
+ 'https': ProxyHTTPSConnectionPool,
|
|
|
+ }
|