python-socks 2.1.1__py3-none-any.whl → 2.3.0__py3-none-any.whl
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/_version.py +1 -1
- python_socks/async_/anyio/v2/__init__.py +8 -0
- python_socks/async_/anyio/v2/_chain.py +26 -0
- python_socks/async_/anyio/v2/_connect.py +16 -0
- python_socks/async_/anyio/v2/_factory.py +11 -0
- python_socks/async_/anyio/v2/_proxy.py +215 -0
- python_socks/async_/anyio/v2/_stream.py +59 -0
- python_socks/async_/asyncio/v2/_chain.py +14 -19
- python_socks/async_/asyncio/v2/_connect.py +9 -4
- python_socks/async_/asyncio/v2/_proxy.py +103 -85
- python_socks/async_/asyncio/v2/_stream.py +1 -1
- python_socks/async_/trio/v2/_chain.py +14 -19
- python_socks/async_/trio/v2/_connect.py +4 -2
- python_socks/async_/trio/v2/_proxy.py +97 -80
- python_socks/sync/v2/_chain.py +5 -15
- python_socks/sync/v2/_connect.py +19 -0
- python_socks/sync/v2/_proxy.py +71 -64
- {python_socks-2.1.1.dist-info → python_socks-2.3.0.dist-info}/METADATA +1 -1
- {python_socks-2.1.1.dist-info → python_socks-2.3.0.dist-info}/RECORD +22 -15
- {python_socks-2.1.1.dist-info → python_socks-2.3.0.dist-info}/LICENSE.txt +0 -0
- {python_socks-2.1.1.dist-info → python_socks-2.3.0.dist-info}/WHEEL +0 -0
- {python_socks-2.1.1.dist-info → python_socks-2.3.0.dist-info}/top_level.txt +0 -0
python_socks/_version.py
CHANGED
|
@@ -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
|
|
@@ -1,31 +1,26 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Sequence
|
|
2
2
|
from ._proxy import AsyncioProxy
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ProxyChain:
|
|
6
|
-
def __init__(self, proxies:
|
|
6
|
+
def __init__(self, proxies: Sequence[AsyncioProxy]):
|
|
7
7
|
self._proxies = proxies
|
|
8
8
|
|
|
9
|
-
async def connect(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
for i in range(length):
|
|
16
|
-
_stream = await proxies[i].connect(
|
|
17
|
-
dest_host=proxies[i + 1].proxy_host,
|
|
18
|
-
dest_port=proxies[i + 1].proxy_port,
|
|
19
|
-
timeout=timeout,
|
|
20
|
-
_stream=_stream
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
_stream = await proxies[length].connect(
|
|
21
|
+
return await forward.connect(
|
|
24
22
|
dest_host=dest_host,
|
|
25
23
|
dest_port=dest_port,
|
|
26
24
|
dest_ssl=dest_ssl,
|
|
27
25
|
timeout=timeout,
|
|
28
|
-
_stream=_stream
|
|
29
26
|
)
|
|
30
|
-
|
|
31
|
-
return _stream
|
|
@@ -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
|
+
)
|