socks.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import logging
  2. from urllib.parse import urlparse
  3. from urllib3 import PoolManager, HTTPConnectionPool, HTTPSConnectionPool
  4. from urllib3.connection import HTTPConnection, HTTPSConnection
  5. from .constants import HTTP_PROXY_SOCK_RDNS_SCHEMAS
  6. logger = logging.getLogger('netbox.utilities')
  7. class ProxyHTTPConnection(HTTPConnection):
  8. """
  9. A Proxy connection class that uses a SOCK proxy - used to create
  10. a urllib3 PoolManager that routes connections via the proxy.
  11. This is for an HTTP (not HTTPS) connection
  12. """
  13. use_rdns = False
  14. def __init__(self, *args, **kwargs):
  15. socks_options = kwargs.pop('_socks_options')
  16. self._proxy_url = socks_options['proxy_url']
  17. super().__init__(*args, **kwargs)
  18. def _new_conn(self):
  19. try:
  20. from python_socks.sync import Proxy
  21. except ModuleNotFoundError as e:
  22. logger.info("Configuring an HTTP proxy using SOCKS requires the python_socks library. Check that it has been installed.")
  23. raise e
  24. proxy = Proxy.from_url(self._proxy_url, rdns=self.use_rdns)
  25. return proxy.connect(
  26. dest_host=self.host,
  27. dest_port=self.port,
  28. timeout=self.timeout
  29. )
  30. class ProxyHTTPSConnection(ProxyHTTPConnection, HTTPSConnection):
  31. """
  32. A Proxy connection class for an HTTPS (not HTTP) connection.
  33. """
  34. pass
  35. class RdnsProxyHTTPConnection(ProxyHTTPConnection):
  36. """
  37. A Proxy connection class for an HTTP remote-dns connection.
  38. I.E. socks4a, socks4h, socks5a, socks5h
  39. """
  40. use_rdns = True
  41. class RdnsProxyHTTPSConnection(ProxyHTTPSConnection):
  42. """
  43. A Proxy connection class for an HTTPS remote-dns connection.
  44. I.E. socks4a, socks4h, socks5a, socks5h
  45. """
  46. use_rdns = True
  47. class ProxyHTTPConnectionPool(HTTPConnectionPool):
  48. ConnectionCls = ProxyHTTPConnection
  49. class ProxyHTTPSConnectionPool(HTTPSConnectionPool):
  50. ConnectionCls = ProxyHTTPSConnection
  51. class RdnsProxyHTTPConnectionPool(HTTPConnectionPool):
  52. ConnectionCls = RdnsProxyHTTPConnection
  53. class RdnsProxyHTTPSConnectionPool(HTTPSConnectionPool):
  54. ConnectionCls = RdnsProxyHTTPSConnection
  55. class ProxyPoolManager(PoolManager):
  56. def __init__(self, proxy_url, timeout=5, num_pools=10, headers=None, **connection_pool_kw):
  57. # python_socks uses rdns param to denote remote DNS parsing and
  58. # doesn't accept the 'h' or 'a' in the proxy URL
  59. if use_rdns := urlparse(proxy_url).scheme in HTTP_PROXY_SOCK_RDNS_SCHEMAS:
  60. proxy_url = proxy_url.replace('socks5h:', 'socks5:').replace('socks5a:', 'socks5:')
  61. proxy_url = proxy_url.replace('socks4h:', 'socks4:').replace('socks4a:', 'socks4:')
  62. connection_pool_kw['_socks_options'] = {'proxy_url': proxy_url}
  63. connection_pool_kw['timeout'] = timeout
  64. super().__init__(num_pools, headers, **connection_pool_kw)
  65. if use_rdns:
  66. self.pool_classes_by_scheme = {
  67. 'http': RdnsProxyHTTPConnectionPool,
  68. 'https': RdnsProxyHTTPSConnectionPool,
  69. }
  70. else:
  71. self.pool_classes_by_scheme = {
  72. 'http': ProxyHTTPConnectionPool,
  73. 'https': ProxyHTTPSConnectionPool,
  74. }