Plex plugin to to play various online streams (mostly Latvian).

socks.py 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. # -*- coding: utf-8 -*-
  2. """
  3. SOCKS support for urllib3
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~
  5. This contrib module contains provisional support for SOCKS proxies from within
  6. urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and
  7. SOCKS5. To enable its functionality, either install PySocks or install this
  8. module with the ``socks`` extra.
  9. Known Limitations:
  10. - Currently PySocks does not support contacting remote websites via literal
  11. IPv6 addresses. Any such connection attempt will fail.
  12. - Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any
  13. such connection attempt will fail.
  14. """
  15. from __future__ import absolute_import
  16. try:
  17. import socks
  18. except ImportError:
  19. import warnings
  20. from ..exceptions import DependencyWarning
  21. warnings.warn((
  22. 'SOCKS support in urllib3 requires the installation of optional '
  23. 'dependencies: specifically, PySocks. For more information, see '
  24. 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies'
  25. ),
  26. DependencyWarning
  27. )
  28. raise
  29. from socket import error as SocketError, timeout as SocketTimeout
  30. from ..connection import (
  31. HTTPConnection, HTTPSConnection
  32. )
  33. from ..connectionpool import (
  34. HTTPConnectionPool, HTTPSConnectionPool
  35. )
  36. from ..exceptions import ConnectTimeoutError, NewConnectionError
  37. from ..poolmanager import PoolManager
  38. from ..util.url import parse_url
  39. try:
  40. import ssl
  41. except ImportError:
  42. ssl = None
  43. class SOCKSConnection(HTTPConnection):
  44. """
  45. A plain-text HTTP connection that connects via a SOCKS proxy.
  46. """
  47. def __init__(self, *args, **kwargs):
  48. self._socks_options = kwargs.pop('_socks_options')
  49. super(SOCKSConnection, self).__init__(*args, **kwargs)
  50. def _new_conn(self):
  51. """
  52. Establish a new connection via the SOCKS proxy.
  53. """
  54. extra_kw = {}
  55. if self.source_address:
  56. extra_kw['source_address'] = self.source_address
  57. if self.socket_options:
  58. extra_kw['socket_options'] = self.socket_options
  59. try:
  60. conn = socks.create_connection(
  61. (self.host, self.port),
  62. proxy_type=self._socks_options['socks_version'],
  63. proxy_addr=self._socks_options['proxy_host'],
  64. proxy_port=self._socks_options['proxy_port'],
  65. proxy_username=self._socks_options['username'],
  66. proxy_password=self._socks_options['password'],
  67. timeout=self.timeout,
  68. **extra_kw
  69. )
  70. except SocketTimeout as e:
  71. raise ConnectTimeoutError(
  72. self, "Connection to %s timed out. (connect timeout=%s)" %
  73. (self.host, self.timeout))
  74. except socks.ProxyError as e:
  75. # This is fragile as hell, but it seems to be the only way to raise
  76. # useful errors here.
  77. if e.socket_err:
  78. error = e.socket_err
  79. if isinstance(error, SocketTimeout):
  80. raise ConnectTimeoutError(
  81. self,
  82. "Connection to %s timed out. (connect timeout=%s)" %
  83. (self.host, self.timeout)
  84. )
  85. else:
  86. raise NewConnectionError(
  87. self,
  88. "Failed to establish a new connection: %s" % error
  89. )
  90. else:
  91. raise NewConnectionError(
  92. self,
  93. "Failed to establish a new connection: %s" % e
  94. )
  95. except SocketError as e: # Defensive: PySocks should catch all these.
  96. raise NewConnectionError(
  97. self, "Failed to establish a new connection: %s" % e)
  98. return conn
  99. # We don't need to duplicate the Verified/Unverified distinction from
  100. # urllib3/connection.py here because the HTTPSConnection will already have been
  101. # correctly set to either the Verified or Unverified form by that module. This
  102. # means the SOCKSHTTPSConnection will automatically be the correct type.
  103. class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection):
  104. pass
  105. class SOCKSHTTPConnectionPool(HTTPConnectionPool):
  106. ConnectionCls = SOCKSConnection
  107. class SOCKSHTTPSConnectionPool(HTTPSConnectionPool):
  108. ConnectionCls = SOCKSHTTPSConnection
  109. class SOCKSProxyManager(PoolManager):
  110. """
  111. A version of the urllib3 ProxyManager that routes connections via the
  112. defined SOCKS proxy.
  113. """
  114. pool_classes_by_scheme = {
  115. 'http': SOCKSHTTPConnectionPool,
  116. 'https': SOCKSHTTPSConnectionPool,
  117. }
  118. def __init__(self, proxy_url, username=None, password=None,
  119. num_pools=10, headers=None, **connection_pool_kw):
  120. parsed = parse_url(proxy_url)
  121. if parsed.scheme == 'socks5':
  122. socks_version = socks.PROXY_TYPE_SOCKS5
  123. elif parsed.scheme == 'socks4':
  124. socks_version = socks.PROXY_TYPE_SOCKS4
  125. else:
  126. raise ValueError(
  127. "Unable to determine SOCKS version from %s" % proxy_url
  128. )
  129. self.proxy_url = proxy_url
  130. socks_options = {
  131. 'socks_version': socks_version,
  132. 'proxy_host': parsed.host,
  133. 'proxy_port': parsed.port,
  134. 'username': username,
  135. 'password': password,
  136. }
  137. connection_pool_kw['_socks_options'] = socks_options
  138. super(SOCKSProxyManager, self).__init__(
  139. num_pools, headers, **connection_pool_kw
  140. )
  141. self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme