python-socks 2.8.1__tar.gz → 2.8.2__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.8.1/python_socks.egg-info → python_socks-2.8.2}/PKG-INFO +1 -1
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/http.py +12 -2
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/socks5.py +33 -26
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_version.py +1 -1
- {python_socks-2.8.1 → python_socks-2.8.2/python_socks.egg-info}/PKG-INFO +1 -1
- python_socks-2.8.2/tests/test_misc.py +50 -0
- python_socks-2.8.1/tests/test_misc.py +0 -21
- {python_socks-2.8.1 → python_socks-2.8.2}/LICENSE.txt +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/MANIFEST.in +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/README.md +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/pyproject.toml +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_abc.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/abc.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/factory_async.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/factory_sync.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/http_async.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/http_sync.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks4_async.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks4_sync.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks5_async.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks5_sync.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_errors.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_helpers.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/errors.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/socks4.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_types.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/_proxy_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_resolver.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_resolver.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_resolver.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_resolver.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/py.typed +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_resolver.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_chain.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_connect.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_proxy.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_ssl_transport.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_stream.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/SOURCES.txt +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/dependency_links.txt +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/requires.txt +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/top_level.txt +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/setup.cfg +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/__init__.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/config.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/conftest.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/http_app.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/http_server.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/mocks.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/proxy_server.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_aio.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_aio_v2.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_anyio.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_anyio_v2.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_curio.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_trio.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_trio_v2.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_sync.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_sync_v2.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_resolvers.py +0 -0
- {python_socks-2.8.1 → python_socks-2.8.2}/tests/utils.py +0 -0
|
@@ -6,6 +6,7 @@ from collections import namedtuple
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
8
|
from .._version import __title__, __version__
|
|
9
|
+
from .._helpers import is_ipv6_address
|
|
9
10
|
|
|
10
11
|
from .errors import ReplyError
|
|
11
12
|
|
|
@@ -94,8 +95,17 @@ class ConnectRequest:
|
|
|
94
95
|
|
|
95
96
|
def dumps(self) -> bytes:
|
|
96
97
|
buff = _Buffer()
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
# RFC 3986 § 3.2.2 / RFC 7230 § 5.4: a literal IPv6 address in an
|
|
99
|
+
# authority component must be enclosed in square brackets so the
|
|
100
|
+
# `:` between literal and port is unambiguous. Without brackets,
|
|
101
|
+
# `2001:db8::1:443` could be parsed as either the address
|
|
102
|
+
# `2001:db8::1` with port `443` or the address `2001:db8::1:443`
|
|
103
|
+
# with no port — proxies disagree, and several misroute the
|
|
104
|
+
# unbracketed form (typically returning `200 Connection
|
|
105
|
+
# established` against a connection that was never actually made).
|
|
106
|
+
host = f'[{self.host}]' if is_ipv6_address(self.host) else self.host
|
|
107
|
+
buff.append_line(f'CONNECT {host}:{self.port} HTTP/1.1')
|
|
108
|
+
buff.append_line(f'Host: {host}:{self.port}')
|
|
99
109
|
buff.append_line(f'User-Agent: {DEFAULT_USER_AGENT}')
|
|
100
110
|
|
|
101
111
|
if self.username and self.password:
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import enum
|
|
2
|
+
from functools import singledispatchmethod
|
|
2
3
|
import ipaddress
|
|
3
4
|
import socket
|
|
4
|
-
from typing import Optional, Union
|
|
5
|
+
from typing import Optional, Type, Union
|
|
5
6
|
from dataclasses import dataclass, field
|
|
6
7
|
|
|
7
8
|
from .errors import ReplyError
|
|
8
9
|
from .._helpers import is_ip_address
|
|
9
10
|
|
|
10
|
-
|
|
11
11
|
RSV = NULL = AUTH_GRANTED = 0x00
|
|
12
12
|
SOCKS_VER = 0x05
|
|
13
13
|
|
|
@@ -302,34 +302,38 @@ ConnectionState = Union[
|
|
|
302
302
|
|
|
303
303
|
|
|
304
304
|
class Connection:
|
|
305
|
-
_state: ConnectionState
|
|
306
|
-
|
|
307
305
|
def __init__(self):
|
|
308
306
|
self._state = StateServerWaitingForAuthMethods()
|
|
309
307
|
|
|
308
|
+
@singledispatchmethod
|
|
310
309
|
def send(self, request: Request) -> bytes:
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
310
|
+
raise RuntimeError(f'Invalid request type: {request.__class__}')
|
|
311
|
+
|
|
312
|
+
@send.register
|
|
313
|
+
def _send_auth_methods(self, request: AuthMethodsRequest) -> bytes:
|
|
314
|
+
if not self._state_is(StateServerWaitingForAuthMethods):
|
|
315
|
+
raise RuntimeError('Server is not currently waiting for auth methods')
|
|
316
|
+
self._state = StateClientSentAuthMethods(request)
|
|
317
|
+
return request.dumps()
|
|
318
|
+
|
|
319
|
+
@send.register
|
|
320
|
+
def _send_auth(self, request: AuthRequest) -> bytes:
|
|
321
|
+
if not self._state_is(StateServerWaitingForAuth):
|
|
322
|
+
raise RuntimeError('Server is not currently waiting for authentication')
|
|
323
|
+
self._state = StateClientSentAuthRequest(request)
|
|
324
|
+
return request.dumps()
|
|
325
|
+
|
|
326
|
+
@send.register
|
|
327
|
+
def _send_connect(self, request: ConnectRequest) -> bytes:
|
|
328
|
+
if not self._state_is(StateClientAuthenticated):
|
|
329
|
+
raise RuntimeError('Client is not authenticated')
|
|
330
|
+
self._state = StateClientSentConnectRequest(request)
|
|
331
|
+
return request.dumps()
|
|
330
332
|
|
|
331
333
|
def receive(self, data: bytes) -> Reply:
|
|
332
|
-
|
|
334
|
+
reply: Reply
|
|
335
|
+
|
|
336
|
+
if self._state_is(StateClientSentAuthMethods):
|
|
333
337
|
reply = AuthMethodReply.loads(data)
|
|
334
338
|
reply.validate(self._state.data)
|
|
335
339
|
if reply.method == AuthMethod.USERNAME_PASSWORD:
|
|
@@ -338,18 +342,21 @@ class Connection:
|
|
|
338
342
|
self._state = StateClientAuthenticated()
|
|
339
343
|
return reply
|
|
340
344
|
|
|
341
|
-
if
|
|
345
|
+
if self._state_is(StateClientSentAuthRequest):
|
|
342
346
|
reply = AuthReply.loads(data)
|
|
343
347
|
self._state = StateClientAuthenticated(data=reply)
|
|
344
348
|
return reply
|
|
345
349
|
|
|
346
|
-
if
|
|
350
|
+
if self._state_is(StateClientSentConnectRequest):
|
|
347
351
|
reply = ConnectReply.loads(data)
|
|
348
352
|
self._state = StateServerConnected(data=reply)
|
|
349
353
|
return reply
|
|
350
354
|
|
|
351
355
|
raise RuntimeError(f'Invalid connection state: {self._state}')
|
|
352
356
|
|
|
357
|
+
def _state_is(self, state_cls: Type[ConnectionState]):
|
|
358
|
+
return self.state.__class__ is state_cls
|
|
359
|
+
|
|
353
360
|
@property
|
|
354
361
|
def state(self):
|
|
355
362
|
return self._state
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__title__ = 'python-socks'
|
|
2
|
-
__version__ = '2.8.
|
|
2
|
+
__version__ = '2.8.2'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# noinspection PyPackageRequirements
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from python_socks._helpers import is_ip_address # noqa
|
|
5
|
+
from python_socks._protocols.http import BasicAuth, ConnectRequest # noqa
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize('address', ('::1', b'::1', '127.0.0.1', b'127.0.0.1'))
|
|
9
|
+
def test_is_ip_address(address):
|
|
10
|
+
assert is_ip_address(address)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_basic_auth():
|
|
14
|
+
login = 'login'
|
|
15
|
+
password = 'password'
|
|
16
|
+
|
|
17
|
+
auth1 = BasicAuth(login=login, password=password)
|
|
18
|
+
auth2 = BasicAuth.decode(auth1.encode())
|
|
19
|
+
|
|
20
|
+
assert auth2.login == login
|
|
21
|
+
assert auth2.password == password
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_connect_request_brackets_ipv6_host():
|
|
25
|
+
"""RFC 7230 § 5.4: literal IPv6 in host:port MUST be bracketed."""
|
|
26
|
+
req = ConnectRequest(
|
|
27
|
+
host='2001:db8::1', port=443, username=None, password=None,
|
|
28
|
+
)
|
|
29
|
+
raw = req.dumps()
|
|
30
|
+
first_line = raw.split(b'\r\n', 1)[0]
|
|
31
|
+
assert first_line == b'CONNECT [2001:db8::1]:443 HTTP/1.1'
|
|
32
|
+
assert b'Host: [2001:db8::1]:443\r\n' in raw
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_connect_request_does_not_bracket_ipv4_host():
|
|
36
|
+
req = ConnectRequest(
|
|
37
|
+
host='192.0.2.1', port=443, username=None, password=None,
|
|
38
|
+
)
|
|
39
|
+
raw = req.dumps()
|
|
40
|
+
assert raw.split(b'\r\n', 1)[0] == b'CONNECT 192.0.2.1:443 HTTP/1.1'
|
|
41
|
+
assert b'Host: 192.0.2.1:443\r\n' in raw
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_connect_request_does_not_bracket_hostname():
|
|
45
|
+
req = ConnectRequest(
|
|
46
|
+
host='example.com', port=443, username=None, password=None,
|
|
47
|
+
)
|
|
48
|
+
raw = req.dumps()
|
|
49
|
+
assert raw.split(b'\r\n', 1)[0] == b'CONNECT example.com:443 HTTP/1.1'
|
|
50
|
+
assert b'Host: example.com:443\r\n' in raw
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# noinspection PyPackageRequirements
|
|
2
|
-
import pytest
|
|
3
|
-
|
|
4
|
-
from python_socks._helpers import is_ip_address # noqa
|
|
5
|
-
from python_socks._protocols.http import BasicAuth # noqa
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@pytest.mark.parametrize('address', ('::1', b'::1', '127.0.0.1', b'127.0.0.1'))
|
|
9
|
-
def test_is_ip_address(address):
|
|
10
|
-
assert is_ip_address(address)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_basic_auth():
|
|
14
|
-
login = 'login'
|
|
15
|
-
password = 'password'
|
|
16
|
-
|
|
17
|
-
auth1 = BasicAuth(login=login, password=password)
|
|
18
|
-
auth2 = BasicAuth.decode(auth1.encode())
|
|
19
|
-
|
|
20
|
-
assert auth2.login == login
|
|
21
|
-
assert auth2.password == password
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|