python-socks 2.1.1__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python-socks-2.1.1/python_socks.egg-info → python-socks-2.3.0}/PKG-INFO +1 -1
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_version.py +1 -1
- python-socks-2.3.0/python_socks/async_/anyio/v2/_chain.py +26 -0
- python-socks-2.3.0/python_socks/async_/anyio/v2/_connect.py +16 -0
- python-socks-2.3.0/python_socks/async_/anyio/v2/_factory.py +11 -0
- python-socks-2.3.0/python_socks/async_/anyio/v2/_proxy.py +215 -0
- python-socks-2.3.0/python_socks/async_/anyio/v2/_stream.py +59 -0
- python-socks-2.3.0/python_socks/async_/asyncio/v2/_chain.py +26 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/v2/_connect.py +9 -4
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/v2/_proxy.py +103 -85
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/v2/_stream.py +1 -1
- python-socks-2.3.0/python_socks/async_/trio/v2/_chain.py +26 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/v2/_connect.py +4 -2
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/v2/_proxy.py +97 -80
- python-socks-2.3.0/python_socks/sync/v2/__init__.py +8 -0
- python-socks-2.3.0/python_socks/sync/v2/_chain.py +26 -0
- python-socks-2.3.0/python_socks/sync/v2/_connect.py +19 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/v2/_proxy.py +71 -64
- {python-socks-2.1.1 → python-socks-2.3.0/python_socks.egg-info}/PKG-INFO +1 -1
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks.egg-info/SOURCES.txt +7 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/setup.py +1 -0
- python-socks-2.1.1/python_socks/async_/asyncio/v2/_chain.py +0 -31
- python-socks-2.1.1/python_socks/async_/trio/v2/_chain.py +0 -31
- python-socks-2.1.1/python_socks/sync/v2/_chain.py +0 -36
- {python-socks-2.1.1 → python-socks-2.3.0}/LICENSE.txt +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/MANIFEST.in +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/README.md +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/pyproject.toml +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_abc.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_basic_auth.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_errors.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_helpers.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/http.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/http_async.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/http_sync.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks4.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks4_async.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks4_sync.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks5.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks5_async.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proto/socks5_sync.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_proxy_factory.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/_types.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/_proxy_chain.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_chain.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_connect.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_factory.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_proxy.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_resolver.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/anyio/_stream.py +0 -0
- {python-socks-2.1.1/python_socks/async_/trio → python-socks-2.3.0/python_socks/async_/anyio}/v2/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/_connect.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/_proxy.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/_resolver.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/_stream.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/v2/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/asyncio/v2/_factory.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/curio/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/curio/_connect.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/curio/_proxy.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/curio/_resolver.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/curio/_stream.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/_connect.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/_proxy.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/_resolver.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/_stream.py +0 -0
- {python-socks-2.1.1/python_socks/sync → python-socks-2.3.0/python_socks/async_/trio}/v2/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/v2/_factory.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/async_/trio/v2/_stream.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/__init__.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/_chain.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/_connect.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/_proxy.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/_resolver.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/_stream.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/v2/_factory.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/v2/_ssl_transport.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks/sync/v2/_stream.py +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks.egg-info/dependency_links.txt +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks.egg-info/requires.txt +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/python_socks.egg-info/top_level.txt +0 -0
- {python-socks-2.1.1 → python-socks-2.3.0}/setup.cfg +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__title__ = 'python-socks'
|
|
2
|
-
__version__ = '2.
|
|
2
|
+
__version__ = '2.3.0'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Sequence
|
|
2
|
+
from ._proxy import AnyioProxy
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ProxyChain:
|
|
6
|
+
def __init__(self, proxies: Sequence[AnyioProxy]):
|
|
7
|
+
self._proxies = proxies
|
|
8
|
+
|
|
9
|
+
async def connect(
|
|
10
|
+
self,
|
|
11
|
+
dest_host,
|
|
12
|
+
dest_port,
|
|
13
|
+
dest_ssl=None,
|
|
14
|
+
timeout=None,
|
|
15
|
+
):
|
|
16
|
+
forward = None
|
|
17
|
+
for proxy in self._proxies:
|
|
18
|
+
proxy._forward = forward
|
|
19
|
+
forward = proxy
|
|
20
|
+
|
|
21
|
+
return await forward.connect(
|
|
22
|
+
dest_host=dest_host,
|
|
23
|
+
dest_port=dest_port,
|
|
24
|
+
dest_ssl=dest_ssl,
|
|
25
|
+
timeout=timeout,
|
|
26
|
+
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import anyio
|
|
2
|
+
import anyio.abc
|
|
3
|
+
from ._stream import AnyioSocketStream
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def connect_tcp(
|
|
7
|
+
host: str,
|
|
8
|
+
port: int,
|
|
9
|
+
local_host: str = None,
|
|
10
|
+
) -> AnyioSocketStream:
|
|
11
|
+
s = await anyio.connect_tcp(
|
|
12
|
+
remote_host=host,
|
|
13
|
+
remote_port=port,
|
|
14
|
+
local_host=local_host,
|
|
15
|
+
)
|
|
16
|
+
return AnyioSocketStream(s)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from ...._proxy_factory import ProxyFactory
|
|
2
|
+
from ...._types import ProxyType
|
|
3
|
+
from ._proxy import Socks5Proxy, Socks4Proxy, HttpProxy, AnyioProxy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Proxy(ProxyFactory[AnyioProxy]):
|
|
7
|
+
types = {
|
|
8
|
+
ProxyType.SOCKS4: Socks4Proxy,
|
|
9
|
+
ProxyType.SOCKS5: Socks5Proxy,
|
|
10
|
+
ProxyType.HTTP: HttpProxy,
|
|
11
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import ssl
|
|
2
|
+
|
|
3
|
+
import anyio
|
|
4
|
+
|
|
5
|
+
from ._connect import connect_tcp
|
|
6
|
+
from ._stream import AnyioSocketStream
|
|
7
|
+
from .._resolver import Resolver
|
|
8
|
+
from ...._errors import ProxyConnectionError, ProxyTimeoutError
|
|
9
|
+
from ...._proto.http_async import HttpProto
|
|
10
|
+
from ...._proto.socks4_async import Socks4Proto
|
|
11
|
+
from ...._proto.socks5_async import Socks5Proto
|
|
12
|
+
|
|
13
|
+
DEFAULT_TIMEOUT = 60
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AnyioProxy:
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
proxy_host: str,
|
|
20
|
+
proxy_port: int,
|
|
21
|
+
proxy_ssl: ssl.SSLContext = None,
|
|
22
|
+
forward: 'AnyioProxy' = None,
|
|
23
|
+
):
|
|
24
|
+
self._proxy_host = proxy_host
|
|
25
|
+
self._proxy_port = proxy_port
|
|
26
|
+
self._proxy_ssl = proxy_ssl
|
|
27
|
+
self._forward = forward
|
|
28
|
+
|
|
29
|
+
self._resolver = Resolver()
|
|
30
|
+
|
|
31
|
+
async def connect(
|
|
32
|
+
self,
|
|
33
|
+
dest_host: str,
|
|
34
|
+
dest_port: int,
|
|
35
|
+
dest_ssl: ssl.SSLContext = None,
|
|
36
|
+
timeout: float = None,
|
|
37
|
+
) -> AnyioSocketStream:
|
|
38
|
+
if timeout is None:
|
|
39
|
+
timeout = DEFAULT_TIMEOUT
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
with anyio.fail_after(timeout):
|
|
43
|
+
return await self._connect(
|
|
44
|
+
dest_host=dest_host,
|
|
45
|
+
dest_port=dest_port,
|
|
46
|
+
dest_ssl=dest_ssl,
|
|
47
|
+
)
|
|
48
|
+
except TimeoutError as e:
|
|
49
|
+
raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
|
|
50
|
+
|
|
51
|
+
async def _connect(
|
|
52
|
+
self,
|
|
53
|
+
dest_host: str,
|
|
54
|
+
dest_port: int,
|
|
55
|
+
dest_ssl: ssl.SSLContext = None,
|
|
56
|
+
) -> AnyioSocketStream:
|
|
57
|
+
try:
|
|
58
|
+
if self._forward is None:
|
|
59
|
+
stream = await connect_tcp(
|
|
60
|
+
host=self._proxy_host,
|
|
61
|
+
port=self._proxy_port,
|
|
62
|
+
)
|
|
63
|
+
else:
|
|
64
|
+
stream = await self._forward.connect(
|
|
65
|
+
dest_host=self._proxy_host,
|
|
66
|
+
dest_port=self._proxy_port,
|
|
67
|
+
)
|
|
68
|
+
except OSError as e:
|
|
69
|
+
raise ProxyConnectionError(
|
|
70
|
+
e.errno,
|
|
71
|
+
f"Couldn't connect to proxy {self._proxy_host}:{self._proxy_port} [{e.strerror}]",
|
|
72
|
+
) from e
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
if self._proxy_ssl is not None:
|
|
76
|
+
stream = await stream.start_tls(
|
|
77
|
+
hostname=self._proxy_host,
|
|
78
|
+
ssl_context=self._proxy_ssl,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
await self._negotiate(
|
|
82
|
+
stream=stream,
|
|
83
|
+
dest_host=dest_host,
|
|
84
|
+
dest_port=dest_port,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if dest_ssl is not None:
|
|
88
|
+
stream = await stream.start_tls(
|
|
89
|
+
hostname=dest_host,
|
|
90
|
+
ssl_context=dest_ssl,
|
|
91
|
+
)
|
|
92
|
+
except BaseException:
|
|
93
|
+
with anyio.CancelScope(shield=True):
|
|
94
|
+
await stream.close()
|
|
95
|
+
raise
|
|
96
|
+
|
|
97
|
+
return stream
|
|
98
|
+
|
|
99
|
+
async def _negotiate(
|
|
100
|
+
self,
|
|
101
|
+
stream: AnyioSocketStream,
|
|
102
|
+
dest_host: str,
|
|
103
|
+
dest_port: int,
|
|
104
|
+
):
|
|
105
|
+
raise NotImplementedError()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class Socks5Proxy(AnyioProxy):
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
proxy_host,
|
|
112
|
+
proxy_port,
|
|
113
|
+
username=None,
|
|
114
|
+
password=None,
|
|
115
|
+
rdns=None,
|
|
116
|
+
proxy_ssl=None,
|
|
117
|
+
forward: 'AnyioProxy' = None,
|
|
118
|
+
):
|
|
119
|
+
super().__init__(
|
|
120
|
+
proxy_host=proxy_host,
|
|
121
|
+
proxy_port=proxy_port,
|
|
122
|
+
proxy_ssl=proxy_ssl,
|
|
123
|
+
forward=forward,
|
|
124
|
+
)
|
|
125
|
+
self._username = username
|
|
126
|
+
self._password = password
|
|
127
|
+
self._rdns = rdns
|
|
128
|
+
|
|
129
|
+
async def _negotiate(
|
|
130
|
+
self,
|
|
131
|
+
stream: AnyioSocketStream,
|
|
132
|
+
dest_host: str,
|
|
133
|
+
dest_port: int,
|
|
134
|
+
):
|
|
135
|
+
proto = Socks5Proto(
|
|
136
|
+
stream=stream,
|
|
137
|
+
resolver=self._resolver,
|
|
138
|
+
dest_host=dest_host,
|
|
139
|
+
dest_port=dest_port,
|
|
140
|
+
username=self._username,
|
|
141
|
+
password=self._password,
|
|
142
|
+
rdns=self._rdns,
|
|
143
|
+
)
|
|
144
|
+
await proto.negotiate()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class Socks4Proxy(AnyioProxy):
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
proxy_host,
|
|
151
|
+
proxy_port,
|
|
152
|
+
user_id=None,
|
|
153
|
+
rdns=None,
|
|
154
|
+
proxy_ssl=None,
|
|
155
|
+
forward: 'AnyioProxy' = None,
|
|
156
|
+
):
|
|
157
|
+
super().__init__(
|
|
158
|
+
proxy_host=proxy_host,
|
|
159
|
+
proxy_port=proxy_port,
|
|
160
|
+
proxy_ssl=proxy_ssl,
|
|
161
|
+
forward=forward,
|
|
162
|
+
)
|
|
163
|
+
self._user_id = user_id
|
|
164
|
+
self._rdns = rdns
|
|
165
|
+
|
|
166
|
+
async def _negotiate(
|
|
167
|
+
self,
|
|
168
|
+
stream: AnyioSocketStream,
|
|
169
|
+
dest_host: str,
|
|
170
|
+
dest_port: int,
|
|
171
|
+
):
|
|
172
|
+
proto = Socks4Proto(
|
|
173
|
+
stream=stream,
|
|
174
|
+
resolver=self._resolver,
|
|
175
|
+
dest_host=dest_host,
|
|
176
|
+
dest_port=dest_port,
|
|
177
|
+
user_id=self._user_id,
|
|
178
|
+
rdns=self._rdns,
|
|
179
|
+
)
|
|
180
|
+
await proto.negotiate()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class HttpProxy(AnyioProxy):
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
proxy_host,
|
|
187
|
+
proxy_port,
|
|
188
|
+
username=None,
|
|
189
|
+
password=None,
|
|
190
|
+
proxy_ssl=None,
|
|
191
|
+
forward: 'AnyioProxy' = None,
|
|
192
|
+
):
|
|
193
|
+
super().__init__(
|
|
194
|
+
proxy_host=proxy_host,
|
|
195
|
+
proxy_port=proxy_port,
|
|
196
|
+
proxy_ssl=proxy_ssl,
|
|
197
|
+
forward=forward,
|
|
198
|
+
)
|
|
199
|
+
self._username = username
|
|
200
|
+
self._password = password
|
|
201
|
+
|
|
202
|
+
async def _negotiate(
|
|
203
|
+
self,
|
|
204
|
+
stream: AnyioSocketStream,
|
|
205
|
+
dest_host: str,
|
|
206
|
+
dest_port: int,
|
|
207
|
+
):
|
|
208
|
+
proto = HttpProto(
|
|
209
|
+
stream=stream,
|
|
210
|
+
dest_host=dest_host,
|
|
211
|
+
dest_port=dest_port,
|
|
212
|
+
username=self._username,
|
|
213
|
+
password=self._password,
|
|
214
|
+
)
|
|
215
|
+
await proto.negotiate()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import ssl
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import anyio
|
|
5
|
+
import anyio.abc
|
|
6
|
+
from anyio.streams.tls import TLSStream
|
|
7
|
+
|
|
8
|
+
from ...._errors import ProxyError
|
|
9
|
+
from .... import _abc as abc
|
|
10
|
+
|
|
11
|
+
DEFAULT_RECEIVE_SIZE = 65536
|
|
12
|
+
|
|
13
|
+
AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AnyioSocketStream(abc.AsyncSocketStream):
|
|
17
|
+
_stream: AnyioStreamType
|
|
18
|
+
|
|
19
|
+
def __init__(self, stream: AnyioStreamType) -> None:
|
|
20
|
+
self._stream = stream
|
|
21
|
+
|
|
22
|
+
async def write_all(self, data: bytes):
|
|
23
|
+
await self._stream.send(item=data)
|
|
24
|
+
|
|
25
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE):
|
|
26
|
+
try:
|
|
27
|
+
return await self._stream.receive(max_bytes=max_bytes)
|
|
28
|
+
except anyio.EndOfStream: # pragma: no cover
|
|
29
|
+
return b""
|
|
30
|
+
|
|
31
|
+
async def read_exact(self, n: int):
|
|
32
|
+
data = bytearray()
|
|
33
|
+
while len(data) < n:
|
|
34
|
+
packet = await self.read(n - len(data))
|
|
35
|
+
if not packet: # pragma: no cover
|
|
36
|
+
raise ProxyError('Connection closed unexpectedly')
|
|
37
|
+
data += packet
|
|
38
|
+
return data
|
|
39
|
+
|
|
40
|
+
async def start_tls(
|
|
41
|
+
self,
|
|
42
|
+
hostname: str,
|
|
43
|
+
ssl_context: ssl.SSLContext,
|
|
44
|
+
) -> 'AnyioSocketStream':
|
|
45
|
+
ssl_stream = await TLSStream.wrap(
|
|
46
|
+
self._stream,
|
|
47
|
+
ssl_context=ssl_context,
|
|
48
|
+
hostname=hostname,
|
|
49
|
+
standard_compatible=False,
|
|
50
|
+
server_side=False,
|
|
51
|
+
)
|
|
52
|
+
return AnyioSocketStream(ssl_stream)
|
|
53
|
+
|
|
54
|
+
async def close(self):
|
|
55
|
+
await self._stream.aclose()
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def anyio_stream(self) -> AnyioStreamType: # pragma: no cover
|
|
59
|
+
return self._stream
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Sequence
|
|
2
|
+
from ._proxy import AsyncioProxy
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ProxyChain:
|
|
6
|
+
def __init__(self, proxies: Sequence[AsyncioProxy]):
|
|
7
|
+
self._proxies = proxies
|
|
8
|
+
|
|
9
|
+
async def connect(
|
|
10
|
+
self,
|
|
11
|
+
dest_host: str,
|
|
12
|
+
dest_port: int,
|
|
13
|
+
dest_ssl=None,
|
|
14
|
+
timeout=None,
|
|
15
|
+
):
|
|
16
|
+
forward = None
|
|
17
|
+
for proxy in self._proxies:
|
|
18
|
+
proxy._forward = forward
|
|
19
|
+
forward = proxy
|
|
20
|
+
|
|
21
|
+
return await forward.connect(
|
|
22
|
+
dest_host=dest_host,
|
|
23
|
+
dest_port=dest_port,
|
|
24
|
+
dest_ssl=dest_ssl,
|
|
25
|
+
timeout=timeout,
|
|
26
|
+
)
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from typing import Optional, Tuple
|
|
3
|
+
from ._stream import AsyncioSocketStream
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
async def connect_tcp(
|
|
6
7
|
host: str,
|
|
7
8
|
port: int,
|
|
9
|
+
loop: asyncio.AbstractEventLoop,
|
|
8
10
|
local_addr: Optional[Tuple[str, int]] = None,
|
|
9
|
-
) ->
|
|
10
|
-
|
|
11
|
+
) -> AsyncioSocketStream:
|
|
11
12
|
kwargs = {}
|
|
12
13
|
if local_addr is not None:
|
|
13
|
-
kwargs['local_addr'] = local_addr
|
|
14
|
+
kwargs['local_addr'] = local_addr # pragma: no cover
|
|
14
15
|
|
|
15
16
|
reader, writer = await asyncio.open_connection(
|
|
16
17
|
host=host,
|
|
@@ -18,4 +19,8 @@ async def connect_tcp(
|
|
|
18
19
|
**kwargs,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
|
-
return
|
|
22
|
+
return AsyncioSocketStream(
|
|
23
|
+
loop=loop,
|
|
24
|
+
reader=reader,
|
|
25
|
+
writer=writer,
|
|
26
|
+
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import ssl
|
|
3
|
+
import warnings
|
|
3
4
|
|
|
4
5
|
import async_timeout
|
|
5
6
|
|
|
@@ -21,8 +22,15 @@ class AsyncioProxy:
|
|
|
21
22
|
proxy_host: str,
|
|
22
23
|
proxy_port: int,
|
|
23
24
|
proxy_ssl: ssl.SSLContext = None,
|
|
25
|
+
forward: 'AsyncioProxy' = None,
|
|
24
26
|
loop: asyncio.AbstractEventLoop = None,
|
|
25
27
|
):
|
|
28
|
+
if loop is not None: # pragma: no cover
|
|
29
|
+
warnings.warn(
|
|
30
|
+
'The loop argument is deprecated and scheduled for removal in the future.',
|
|
31
|
+
DeprecationWarning,
|
|
32
|
+
stacklevel=2,
|
|
33
|
+
)
|
|
26
34
|
|
|
27
35
|
if loop is None:
|
|
28
36
|
loop = asyncio.get_event_loop()
|
|
@@ -32,13 +40,8 @@ class AsyncioProxy:
|
|
|
32
40
|
self._proxy_host = proxy_host
|
|
33
41
|
self._proxy_port = proxy_port
|
|
34
42
|
self._proxy_ssl = proxy_ssl
|
|
43
|
+
self._forward = forward
|
|
35
44
|
|
|
36
|
-
self._dest_host = None
|
|
37
|
-
self._dest_port = None
|
|
38
|
-
self._dest_ssl = None
|
|
39
|
-
self._timeout = None
|
|
40
|
-
|
|
41
|
-
self._stream = None
|
|
42
45
|
self._resolver = Resolver(loop=loop)
|
|
43
46
|
|
|
44
47
|
async def connect(
|
|
@@ -47,83 +50,77 @@ class AsyncioProxy:
|
|
|
47
50
|
dest_port: int,
|
|
48
51
|
dest_ssl: ssl.SSLContext = None,
|
|
49
52
|
timeout: float = None,
|
|
50
|
-
_stream: AsyncioSocketStream = None,
|
|
51
53
|
) -> AsyncioSocketStream:
|
|
52
|
-
|
|
53
54
|
if timeout is None:
|
|
54
55
|
timeout = DEFAULT_TIMEOUT
|
|
55
56
|
|
|
56
|
-
self._dest_host = dest_host
|
|
57
|
-
self._dest_port = dest_port
|
|
58
|
-
self._dest_ssl = dest_ssl
|
|
59
|
-
self._timeout = timeout
|
|
60
|
-
|
|
61
57
|
try:
|
|
62
|
-
|
|
58
|
+
async with async_timeout.timeout(timeout):
|
|
59
|
+
return await self._connect(
|
|
60
|
+
dest_host=dest_host,
|
|
61
|
+
dest_port=dest_port,
|
|
62
|
+
dest_ssl=dest_ssl,
|
|
63
|
+
)
|
|
63
64
|
except asyncio.TimeoutError as e:
|
|
64
|
-
raise ProxyTimeoutError('Proxy connection timed out: {}'.format(
|
|
65
|
-
|
|
66
|
-
async def _connect(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
79
|
-
else:
|
|
80
|
-
self._stream = _stream
|
|
81
|
-
|
|
82
|
-
if self._proxy_ssl is not None: # pragma: no cover
|
|
83
|
-
self._stream = await self._stream.start_tls(
|
|
84
|
-
hostname=self._proxy_host,
|
|
85
|
-
ssl_context=self._proxy_ssl,
|
|
86
|
-
)
|
|
87
|
-
except OSError as e:
|
|
88
|
-
await self._close()
|
|
89
|
-
msg = 'Could not connect to proxy {}:{} [{}]'.format(
|
|
90
|
-
self._proxy_host,
|
|
91
|
-
self._proxy_port,
|
|
92
|
-
e.strerror,
|
|
65
|
+
raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
|
|
66
|
+
|
|
67
|
+
async def _connect(
|
|
68
|
+
self,
|
|
69
|
+
dest_host: str,
|
|
70
|
+
dest_port: int,
|
|
71
|
+
dest_ssl: ssl.SSLContext = None,
|
|
72
|
+
) -> AsyncioSocketStream:
|
|
73
|
+
try:
|
|
74
|
+
if self._forward is None:
|
|
75
|
+
stream = await connect_tcp(
|
|
76
|
+
host=self._proxy_host,
|
|
77
|
+
port=self._proxy_port,
|
|
78
|
+
loop=self._loop,
|
|
93
79
|
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
)
|
|
107
|
-
except (asyncio.CancelledError, Exception):
|
|
108
|
-
await self._close()
|
|
109
|
-
raise
|
|
110
|
-
|
|
111
|
-
return self._stream
|
|
112
|
-
|
|
113
|
-
async def _negotiate(self):
|
|
114
|
-
raise NotImplementedError()
|
|
80
|
+
else:
|
|
81
|
+
stream = await self._forward.connect(
|
|
82
|
+
dest_host=self._proxy_host,
|
|
83
|
+
dest_port=self._proxy_port,
|
|
84
|
+
)
|
|
85
|
+
except OSError as e:
|
|
86
|
+
raise ProxyConnectionError(
|
|
87
|
+
e.errno,
|
|
88
|
+
f"Couldn't connect to proxy {self._proxy_host}:{self._proxy_port} [{e.strerror}]",
|
|
89
|
+
) from e
|
|
90
|
+
except (asyncio.CancelledError, Exception):
|
|
91
|
+
raise
|
|
115
92
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
93
|
+
try:
|
|
94
|
+
if self._proxy_ssl is not None: # pragma: no cover
|
|
95
|
+
stream = await stream.start_tls(
|
|
96
|
+
hostname=self._proxy_host,
|
|
97
|
+
ssl_context=self._proxy_ssl,
|
|
98
|
+
)
|
|
119
99
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
100
|
+
await self._negotiate(
|
|
101
|
+
stream=stream,
|
|
102
|
+
dest_host=dest_host,
|
|
103
|
+
dest_port=dest_port,
|
|
104
|
+
)
|
|
123
105
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
106
|
+
if dest_ssl is not None:
|
|
107
|
+
stream = await stream.start_tls(
|
|
108
|
+
hostname=dest_host,
|
|
109
|
+
ssl_context=dest_ssl,
|
|
110
|
+
)
|
|
111
|
+
except (asyncio.CancelledError, Exception):
|
|
112
|
+
await stream.close()
|
|
113
|
+
raise
|
|
114
|
+
|
|
115
|
+
return stream
|
|
116
|
+
|
|
117
|
+
async def _negotiate(
|
|
118
|
+
self,
|
|
119
|
+
stream: AsyncioSocketStream,
|
|
120
|
+
dest_host: str,
|
|
121
|
+
dest_port: int,
|
|
122
|
+
):
|
|
123
|
+
raise NotImplementedError()
|
|
127
124
|
|
|
128
125
|
|
|
129
126
|
class Socks5Proxy(AsyncioProxy):
|
|
@@ -135,24 +132,31 @@ class Socks5Proxy(AsyncioProxy):
|
|
|
135
132
|
password=None,
|
|
136
133
|
rdns=None,
|
|
137
134
|
proxy_ssl=None,
|
|
135
|
+
forward: 'AsyncioProxy' = None,
|
|
138
136
|
loop: asyncio.AbstractEventLoop = None,
|
|
139
137
|
):
|
|
140
138
|
super().__init__(
|
|
141
139
|
proxy_host=proxy_host,
|
|
142
140
|
proxy_port=proxy_port,
|
|
143
141
|
proxy_ssl=proxy_ssl,
|
|
142
|
+
forward=forward,
|
|
144
143
|
loop=loop,
|
|
145
144
|
)
|
|
146
145
|
self._username = username
|
|
147
146
|
self._password = password
|
|
148
147
|
self._rdns = rdns
|
|
149
148
|
|
|
150
|
-
async def _negotiate(
|
|
149
|
+
async def _negotiate(
|
|
150
|
+
self,
|
|
151
|
+
stream: AsyncioSocketStream,
|
|
152
|
+
dest_host: str,
|
|
153
|
+
dest_port: int,
|
|
154
|
+
):
|
|
151
155
|
proto = Socks5Proto(
|
|
152
|
-
stream=
|
|
156
|
+
stream=stream,
|
|
153
157
|
resolver=self._resolver,
|
|
154
|
-
dest_host=
|
|
155
|
-
dest_port=
|
|
158
|
+
dest_host=dest_host,
|
|
159
|
+
dest_port=dest_port,
|
|
156
160
|
username=self._username,
|
|
157
161
|
password=self._password,
|
|
158
162
|
rdns=self._rdns,
|
|
@@ -168,23 +172,30 @@ class Socks4Proxy(AsyncioProxy):
|
|
|
168
172
|
user_id=None,
|
|
169
173
|
rdns=None,
|
|
170
174
|
proxy_ssl=None,
|
|
175
|
+
forward: 'AsyncioProxy' = None,
|
|
171
176
|
loop: asyncio.AbstractEventLoop = None,
|
|
172
177
|
):
|
|
173
178
|
super().__init__(
|
|
174
179
|
proxy_host=proxy_host,
|
|
175
180
|
proxy_port=proxy_port,
|
|
176
181
|
proxy_ssl=proxy_ssl,
|
|
182
|
+
forward=forward,
|
|
177
183
|
loop=loop,
|
|
178
184
|
)
|
|
179
185
|
self._user_id = user_id
|
|
180
186
|
self._rdns = rdns
|
|
181
187
|
|
|
182
|
-
async def _negotiate(
|
|
188
|
+
async def _negotiate(
|
|
189
|
+
self,
|
|
190
|
+
stream: AsyncioSocketStream,
|
|
191
|
+
dest_host: str,
|
|
192
|
+
dest_port: int,
|
|
193
|
+
):
|
|
183
194
|
proto = Socks4Proto(
|
|
184
|
-
stream=
|
|
195
|
+
stream=stream,
|
|
185
196
|
resolver=self._resolver,
|
|
186
|
-
dest_host=
|
|
187
|
-
dest_port=
|
|
197
|
+
dest_host=dest_host,
|
|
198
|
+
dest_port=dest_port,
|
|
188
199
|
user_id=self._user_id,
|
|
189
200
|
rdns=self._rdns,
|
|
190
201
|
)
|
|
@@ -199,22 +210,29 @@ class HttpProxy(AsyncioProxy):
|
|
|
199
210
|
username=None,
|
|
200
211
|
password=None,
|
|
201
212
|
proxy_ssl=None,
|
|
213
|
+
forward: 'AsyncioProxy' = None,
|
|
202
214
|
loop: asyncio.AbstractEventLoop = None,
|
|
203
215
|
):
|
|
204
216
|
super().__init__(
|
|
205
217
|
proxy_host=proxy_host,
|
|
206
218
|
proxy_port=proxy_port,
|
|
207
219
|
proxy_ssl=proxy_ssl,
|
|
220
|
+
forward=forward,
|
|
208
221
|
loop=loop,
|
|
209
222
|
)
|
|
210
223
|
self._username = username
|
|
211
224
|
self._password = password
|
|
212
225
|
|
|
213
|
-
async def _negotiate(
|
|
226
|
+
async def _negotiate(
|
|
227
|
+
self,
|
|
228
|
+
stream: AsyncioSocketStream,
|
|
229
|
+
dest_host: str,
|
|
230
|
+
dest_port: int,
|
|
231
|
+
):
|
|
214
232
|
proto = HttpProto(
|
|
215
|
-
stream=
|
|
216
|
-
dest_host=
|
|
217
|
-
dest_port=
|
|
233
|
+
stream=stream,
|
|
234
|
+
dest_host=dest_host,
|
|
235
|
+
dest_port=dest_port,
|
|
218
236
|
username=self._username,
|
|
219
237
|
password=self._password,
|
|
220
238
|
)
|
|
@@ -17,7 +17,7 @@ async def backport_start_tls(
|
|
|
17
17
|
ssl_handshake_timeout=None,
|
|
18
18
|
): # pragma: no cover
|
|
19
19
|
"""
|
|
20
|
-
Python 3.6 asyncio doesn't have a start_tls() method on the loop
|
|
20
|
+
Python 3.6 asyncio doesn't have a start_tls() method on the loop,
|
|
21
21
|
so we use this function in place of the loop's start_tls() method.
|
|
22
22
|
Adapted from this comment:
|
|
23
23
|
https://github.com/urllib3/urllib3/issues/1323#issuecomment-362494839
|