ssl2 2.0.1__tar.gz → 2.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ssl2
3
- Version: 2.0.1
3
+ Version: 2.2.0
4
4
  Summary: PEP 748 TLS interface: typed ClientHello model and parser, JA3, JA4 and Akamai fingerprints, proxies, sockets and asyncio streams
5
5
  Requires-Python: >=3.14
6
6
  Project-URL: Repository, https://github.com/baobien-dev/ssl2
@@ -175,7 +175,7 @@ async def main() -> None:
175
175
  asyncio.run(main())
176
176
  ```
177
177
 
178
- `context` is an engine's `ClientContext`. A peer that drops TCP without close_notify reads as `b""`, the same default the `ssl` module has. On a direct connection, and behind an HTTP or SOCKS proxy, both hand the TCP socket to the engine through `TLSBuffer.attach`, so records move between the kernel and the TLS state without a stop in Python; behind an HTTPS proxy, or over any other `AsyncTransport`, the records go through the buffer's memory BIOs instead. A socket timeout applies to every wait.
178
+ `context` is an engine's `ClientContext`. `server_hostname=` names the server for SNI and certificate verification when the address is not that name, such as an IP address picked in advance. A peer that drops TCP without close_notify reads as `b""`, the same default the `ssl` module has. On a direct connection, and behind an HTTP or SOCKS proxy, both hand the TCP socket to the engine through `TLSBuffer.attach`, so records move between the kernel and the TLS state without a stop in Python; behind an HTTPS proxy, or over any other `AsyncTransport`, the records go through the buffer's memory BIOs instead. A socket timeout applies to every wait. An `AsyncTransport` answers `getsockname()` and `getpeername()` like a socket, and a `TLSStream` passes the question to the transport under it, so a stream behind an HTTPS proxy names the TCP socket to the proxy.
179
179
 
180
180
  ## Writing an engine
181
181
 
@@ -167,7 +167,7 @@ async def main() -> None:
167
167
  asyncio.run(main())
168
168
  ```
169
169
 
170
- `context` is an engine's `ClientContext`. A peer that drops TCP without close_notify reads as `b""`, the same default the `ssl` module has. On a direct connection, and behind an HTTP or SOCKS proxy, both hand the TCP socket to the engine through `TLSBuffer.attach`, so records move between the kernel and the TLS state without a stop in Python; behind an HTTPS proxy, or over any other `AsyncTransport`, the records go through the buffer's memory BIOs instead. A socket timeout applies to every wait.
170
+ `context` is an engine's `ClientContext`. `server_hostname=` names the server for SNI and certificate verification when the address is not that name, such as an IP address picked in advance. A peer that drops TCP without close_notify reads as `b""`, the same default the `ssl` module has. On a direct connection, and behind an HTTP or SOCKS proxy, both hand the TCP socket to the engine through `TLSBuffer.attach`, so records move between the kernel and the TLS state without a stop in Python; behind an HTTPS proxy, or over any other `AsyncTransport`, the records go through the buffer's memory BIOs instead. A socket timeout applies to every wait. An `AsyncTransport` answers `getsockname()` and `getpeername()` like a socket, and a `TLSStream` passes the question to the transport under it, so a stream behind an HTTPS proxy names the TCP socket to the proxy.
171
171
 
172
172
  ## Writing an engine
173
173
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssl2"
3
- version = "2.0.1"
3
+ version = "2.2.0"
4
4
  description = "PEP 748 TLS interface: typed ClientHello model and parser, JA3, JA4 and Akamai fingerprints, proxies, sockets and asyncio streams"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssl2"
3
- version = "2.0.1"
3
+ version = "2.2.0"
4
4
  description = "PEP 748 TLS interface: typed ClientHello model and parser, JA3, JA4 and Akamai fingerprints, proxies, sockets and asyncio streams"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -20,6 +20,10 @@ class AsyncTransport(Protocol):
20
20
 
21
21
  async def close(self) -> None: ...
22
22
 
23
+ def getsockname(self) -> tuple[str | None, int]: ...
24
+
25
+ def getpeername(self) -> tuple[str | None, int]: ...
26
+
23
27
 
24
28
  class StreamTransport:
25
29
  def __init__(
@@ -38,6 +42,12 @@ class StreamTransport:
38
42
  async def close(self) -> None:
39
43
  self._writer.close()
40
44
 
45
+ def getsockname(self) -> tuple[str | None, int]:
46
+ return self._writer.get_extra_info("sockname")
47
+
48
+ def getpeername(self) -> tuple[str | None, int]:
49
+ return self._writer.get_extra_info("peername")
50
+
41
51
 
42
52
  class SocketTransport:
43
53
  def __init__(self, sock: socket.socket) -> None:
@@ -77,6 +87,12 @@ class SocketTransport:
77
87
  async def close(self) -> None:
78
88
  self._socket.close()
79
89
 
90
+ def getsockname(self) -> tuple[str | None, int]:
91
+ return self._socket.getsockname()
92
+
93
+ def getpeername(self) -> tuple[str | None, int]:
94
+ return self._socket.getpeername()
95
+
80
96
 
81
97
  @dataclass(frozen=True, slots=True)
82
98
  class Tunnel:
@@ -110,13 +126,16 @@ class TLSStream:
110
126
  proxy: Proxy | None = None,
111
127
  timeout: float | None = None,
112
128
  proxy_context: ClientContext | None = None,
129
+ server_hostname: str | None = None,
113
130
  ) -> Self:
114
131
  async with asyncio.timeout(timeout):
115
132
  tunnel = await cls.tunnel(
116
133
  context if proxy_context is None else proxy_context, address, proxy
117
134
  )
118
135
  try:
119
- buffer = context.create_buffer(address[0])
136
+ buffer = context.create_buffer(
137
+ address[0] if server_hostname is None else server_hostname
138
+ )
120
139
  if tunnel.socket is not None and not tunnel.leftover:
121
140
  return await cls.open_socket(context, buffer, tunnel.socket)
122
141
  return await cls.open(
@@ -214,6 +233,12 @@ class TLSStream:
214
233
  def getpeercert(self) -> bytes | None:
215
234
  return self._buffer.getpeercert()
216
235
 
236
+ def getsockname(self) -> tuple[str | None, int]:
237
+ return self._transport.getsockname()
238
+
239
+ def getpeername(self) -> tuple[str | None, int]:
240
+ return self._transport.getpeername()
241
+
217
242
  @classmethod
218
243
  async def associate(
219
244
  cls, proxy: Proxy, transport: AsyncTransport
@@ -197,8 +197,11 @@ class TLSSocket:
197
197
  proxy: Proxy | None = None,
198
198
  timeout: float | None = None,
199
199
  proxy_context: ClientContext | None = None,
200
+ server_hostname: str | None = None,
200
201
  ) -> Self:
201
- buffer = context.create_buffer(address[0])
202
+ buffer = context.create_buffer(
203
+ address[0] if server_hostname is None else server_hostname
204
+ )
202
205
  if proxy is None:
203
206
  return cls.open_socket(
204
207
  context, buffer, socket.create_connection(address, timeout=timeout)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes