python-socks 2.8.1__py3-none-any.whl → 2.8.2__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.
@@ -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
- buff.append_line(f'CONNECT {self.host}:{self.port} HTTP/1.1')
98
- buff.append_line(f'Host: {self.host}:{self.port}')
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
- if type(request) is AuthMethodsRequest:
312
- if type(self._state) is not StateServerWaitingForAuthMethods:
313
- raise RuntimeError('Server is not currently waiting for auth methods')
314
- self._state = StateClientSentAuthMethods(request)
315
- return request.dumps()
316
-
317
- if type(request) is AuthRequest:
318
- if type(self._state) is not StateServerWaitingForAuth:
319
- raise RuntimeError('Server is not currently waiting for authentication')
320
- self._state = StateClientSentAuthRequest(request)
321
- return request.dumps()
322
-
323
- if type(request) is ConnectRequest:
324
- if type(self._state) is not StateClientAuthenticated:
325
- raise RuntimeError('Client is not authenticated')
326
- self._state = StateClientSentConnectRequest(request)
327
- return request.dumps()
328
-
329
- raise RuntimeError(f'Invalid request type: {type(request)}')
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
- if type(self._state) is StateClientSentAuthMethods:
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 type(self._state) is StateClientSentAuthRequest:
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 type(self._state) is StateClientSentConnectRequest:
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
python_socks/_version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  __title__ = 'python-socks'
2
- __version__ = '2.8.1'
2
+ __version__ = '2.8.2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-socks
3
- Version: 2.8.1
3
+ Version: 2.8.2
4
4
  Summary: Proxy (SOCKS4, SOCKS5, HTTP CONNECT) client for Python
5
5
  Author-email: Roman Snegirev <snegiryev@gmail.com>
6
6
  License: Apache-2.0
@@ -3,7 +3,7 @@ python_socks/_abc.py,sha256=_juTZE0iqoTChE53IHjBKEYFO-fxYZfPhdUc6EgFlwE,909
3
3
  python_socks/_errors.py,sha256=M2KkgCrFy_ebEqw7JoxCWMjIIQ5Zu_B7YlXYhsLo-gk,329
4
4
  python_socks/_helpers.py,sha256=UacO07M9i8G8CfqzBYWnehgCTFbZTlwJWAsFoXR3mVk,2708
5
5
  python_socks/_types.py,sha256=dgQifNf2mIwdKoX7YmI91H_lUsW9-Y0ZkZGlIV6_NNw,90
6
- python_socks/_version.py,sha256=e7j6da3GYTfq-hRQ9-hzCSija3gACzgMjjpG6A56uGs,49
6
+ python_socks/_version.py,sha256=60qSCLD6nICI7tW_gU87eg-E18vmyw7k12UWh4OVy7g,49
7
7
  python_socks/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  python_socks/_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  python_socks/_connectors/abc.py,sha256=1fgjPiv64UAMiJDsJXYr-GofPGhuHTwTzaodQfrzf7Q,397
@@ -17,9 +17,9 @@ python_socks/_connectors/socks5_async.py,sha256=qniozb0P8gtXF_vK3b7kjRmoGNMHmYAb
17
17
  python_socks/_connectors/socks5_sync.py,sha256=G_FW1XsiaMDbzR7vcBMtRpD3R9LpO5V5zu9pltpo7xI,2602
18
18
  python_socks/_protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  python_socks/_protocols/errors.py,sha256=4XqU4AqgrWaedmUVGMjd6TvQKXv3mV0H76s5lf8aazY,150
20
- python_socks/_protocols/http.py,sha256=QhLHqBgc6DfsvC8AIhylN1tjfk7QzxgwemrK1LuL3vk,4499
20
+ python_socks/_protocols/http.py,sha256=T8hxXJnxApF2dxMC89jajeE66v7rtjmHcnrHICKTY0s,5175
21
21
  python_socks/_protocols/socks4.py,sha256=j1Psqi3PDiKAF19g_Mh3sf_RwXu7o-8X7BqWOaoTbKE,3061
22
- python_socks/_protocols/socks5.py,sha256=1PEr1s4JxmpST9OYEeVgbidnV_d4Vb85JZHmWqK-r9c,9713
22
+ python_socks/_protocols/socks5.py,sha256=7AhzpKYM6ZBdGEoTD5ynKdApTKz4Q1l7LcHxhHfgRVw,9931
23
23
  python_socks/async_/__init__.py,sha256=TeBn5oirLy6aPoWcTc3OW9OxXh5Uv5LADwF_Y7f7HjY,64
24
24
  python_socks/async_/_proxy_chain.py,sha256=TiG1Uu45anONWi2ZFj11w4-LrNbja8SbG27bWCX9Et4,987
25
25
  python_socks/async_/anyio/__init__.py,sha256=pZnrND4Qd-pIGg7KI1ML41kZNkWHmkd3vrvwZZIJyk4,106
@@ -70,8 +70,8 @@ python_socks/sync/v2/_connect.py,sha256=QrZ1CZimYCUMUuaD8JdUblwlqlzIbqFrbsPwMzox
70
70
  python_socks/sync/v2/_proxy.py,sha256=7FuO6wML0DkEonoGAprhTlVd8fazOgNeNrJyxABmP1Q,3621
71
71
  python_socks/sync/v2/_ssl_transport.py,sha256=MURwzvKu-S2-MI7o8KaZTvvBFOiiKIu5R0ERVIMvERE,6088
72
72
  python_socks/sync/v2/_stream.py,sha256=Jq89x1xmIX3L_v-WyS1nRTItjTnYTcDcfDblsANxfJI,1544
73
- python_socks-2.8.1.dist-info/licenses/LICENSE.txt,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
74
- python_socks-2.8.1.dist-info/METADATA,sha256=jDS83DnufjrnQhuWQBgKuXA7h0KMUNre3G83vwrOXC4,8151
75
- python_socks-2.8.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
76
- python_socks-2.8.1.dist-info/top_level.txt,sha256=7qyDAVwjTZ0sIYG1iatFbE-SLpWhONjaoMQ0Ib2DX0Q,13
77
- python_socks-2.8.1.dist-info/RECORD,,
73
+ python_socks-2.8.2.dist-info/licenses/LICENSE.txt,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
74
+ python_socks-2.8.2.dist-info/METADATA,sha256=QZw-g2v-tBl8fXCKS9nO_wsICjCR5TJG53-hzdFODpU,8151
75
+ python_socks-2.8.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
76
+ python_socks-2.8.2.dist-info/top_level.txt,sha256=7qyDAVwjTZ0sIYG1iatFbE-SLpWhONjaoMQ0Ib2DX0Q,13
77
+ python_socks-2.8.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.0)
2
+ Generator: setuptools (82.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5