tunnelfetch 1.9.0 → 1.12.0
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.
- package/README.md +432 -71
- package/README.zh-CN.md +82 -31
- package/package.json +1 -1
- package/src/proxy/http-connect.js +11 -13
- package/src/proxy/index.js +11 -1
- package/src/proxy/socks5.js +2 -10
- package/src/proxy/tunnel.js +110 -0
- package/src/tls/connect.js +28 -4
- package/src/tls/handshake-messages.js +13 -4
- package/src/tls/handshake.js +1 -0
- package/src/transport.js +14 -0
- package/src/util/bytes.js +35 -18
- package/types/proxy/index.d.ts +7 -0
- package/types/proxy/tunnel.d.ts +11 -0
- package/types/tls/connect.d.ts +40 -6
- package/types/tls/handshake-messages.d.ts +7 -1
- package/types/util/bytes.d.ts +14 -10
package/types/tls/connect.d.ts
CHANGED
|
@@ -13,9 +13,23 @@
|
|
|
13
13
|
* @property {number[]} [groups] supported_groups, in preference order.
|
|
14
14
|
* @property {number[]} [offerGroups] groups to send an actual key_share for. Default the first
|
|
15
15
|
* supported group; a HelloRetryRequest recovers any other choice at the cost of a round trip.
|
|
16
|
-
* @property {number[]} [ciphers] cipher suites to offer, in preference order.
|
|
17
|
-
* one this package can perform
|
|
18
|
-
* server selects it, so an unknown suite is refused here rather than on the wire.
|
|
16
|
+
* @property {number[]} [ciphers] cipher suites to offer, in preference order. By default every
|
|
17
|
+
* suite must be one this package can perform: an offer it cannot honour is a dead connection the
|
|
18
|
+
* moment a server selects it, so an unknown suite is refused here rather than on the wire.
|
|
19
|
+
* @property {number[]} [omitExtensions] extension types to leave out of the ClientHello, the
|
|
20
|
+
* subtractive counterpart to `extraExtensions`. `status_request` (5) is the one extension this
|
|
21
|
+
* package sends that curl does not, so an identity matching a sample without it needs this.
|
|
22
|
+
* Dropping it gives up OCSP stapling, which is the only revocation signal this package can
|
|
23
|
+
* consume — pairing it with `trust.revocation: 'require-staple'` is refused rather than left to
|
|
24
|
+
* fail every connection.
|
|
25
|
+
* @property {boolean} [allowUnperformableCiphers] offer suites this package cannot complete.
|
|
26
|
+
* For fingerprint fidelity only. Real clients offer far more than this package implements — curl
|
|
27
|
+
* 8.21.0 offers thirty against seven performable here, Chromium fifteen against seven — so a
|
|
28
|
+
* hello restricted to what it can honour carries a cipher list shorter than any real client's,
|
|
29
|
+
* which is exactly what a JA3 hash reads. With this set, a server that selects an unperformable
|
|
30
|
+
* suite fails the handshake; the first such suite sits behind the TLS 1.3 ones in both real
|
|
31
|
+
* lists, so a 1.3-capable server does not reach it. Knowingly trading a rare failure for an
|
|
32
|
+
* accurate fingerprint is a legitimate choice; making it silently is not.
|
|
19
33
|
* @property {Uint8Array[]} [extraExtensions] pre-encoded ClientHello extensions, appended before
|
|
20
34
|
* ordering. `extensionOrder` can only arrange extensions that were BUILT — it filters to what
|
|
21
35
|
* exists and sorts that — so ordering alone cannot produce an extension this package does not
|
|
@@ -169,11 +183,31 @@ export type TlsOptions = {
|
|
|
169
183
|
*/
|
|
170
184
|
offerGroups?: number[] | undefined;
|
|
171
185
|
/**
|
|
172
|
-
* cipher suites to offer, in preference order.
|
|
173
|
-
* one this package can perform
|
|
174
|
-
* server selects it, so an unknown suite is refused here rather than on the wire.
|
|
186
|
+
* cipher suites to offer, in preference order. By default every
|
|
187
|
+
* suite must be one this package can perform: an offer it cannot honour is a dead connection the
|
|
188
|
+
* moment a server selects it, so an unknown suite is refused here rather than on the wire.
|
|
175
189
|
*/
|
|
176
190
|
ciphers?: number[] | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* extension types to leave out of the ClientHello, the
|
|
193
|
+
* subtractive counterpart to `extraExtensions`. `status_request` (5) is the one extension this
|
|
194
|
+
* package sends that curl does not, so an identity matching a sample without it needs this.
|
|
195
|
+
* Dropping it gives up OCSP stapling, which is the only revocation signal this package can
|
|
196
|
+
* consume — pairing it with `trust.revocation: 'require-staple'` is refused rather than left to
|
|
197
|
+
* fail every connection.
|
|
198
|
+
*/
|
|
199
|
+
omitExtensions?: number[] | undefined;
|
|
200
|
+
/**
|
|
201
|
+
* offer suites this package cannot complete.
|
|
202
|
+
* For fingerprint fidelity only. Real clients offer far more than this package implements — curl
|
|
203
|
+
* 8.21.0 offers thirty against seven performable here, Chromium fifteen against seven — so a
|
|
204
|
+
* hello restricted to what it can honour carries a cipher list shorter than any real client's,
|
|
205
|
+
* which is exactly what a JA3 hash reads. With this set, a server that selects an unperformable
|
|
206
|
+
* suite fails the handshake; the first such suite sits behind the TLS 1.3 ones in both real
|
|
207
|
+
* lists, so a 1.3-capable server does not reach it. Knowingly trading a rare failure for an
|
|
208
|
+
* accurate fingerprint is a legitimate choice; making it silently is not.
|
|
209
|
+
*/
|
|
210
|
+
allowUnperformableCiphers?: boolean | undefined;
|
|
177
211
|
/**
|
|
178
212
|
* pre-encoded ClientHello extensions, appended before
|
|
179
213
|
* ordering. `extensionOrder` can only arrange extensions that were BUILT — it filters to what
|
|
@@ -32,7 +32,7 @@ export function generateKeyShare(group: number, deps?: import("./connect.js").Tl
|
|
|
32
32
|
* @returns {Promise<Uint8Array>} throws on any degenerate or malformed peer key
|
|
33
33
|
*/
|
|
34
34
|
export function deriveSharedSecret(group: number, privateKey: CryptoKey | import("./hybrid.js").HybridPrivate, peerKey: Uint8Array, deps?: import("./connect.js").TlsDeps): Promise<Uint8Array>;
|
|
35
|
-
export function buildClientHello({ hostname, keyShares, random, legacySessionId, ciphers, groups, sigSchemes, alpn, versions, extensionOrder, extraExtensions, psk, grease, randomBytes, }: {
|
|
35
|
+
export function buildClientHello({ hostname, keyShares, random, legacySessionId, ciphers, groups, sigSchemes, alpn, versions, extensionOrder, extraExtensions, omitExtensions, psk, grease, randomBytes, }: {
|
|
36
36
|
hostname: any;
|
|
37
37
|
keyShares: any;
|
|
38
38
|
random: any;
|
|
@@ -44,6 +44,7 @@ export function buildClientHello({ hostname, keyShares, random, legacySessionId,
|
|
|
44
44
|
versions?: number[] | undefined;
|
|
45
45
|
extensionOrder?: readonly number[] | undefined;
|
|
46
46
|
extraExtensions?: never[] | undefined;
|
|
47
|
+
omitExtensions?: never[] | undefined;
|
|
47
48
|
psk?: null | undefined;
|
|
48
49
|
grease?: boolean | undefined;
|
|
49
50
|
randomBytes?: ((n: any) => Uint8Array<any>) | undefined;
|
|
@@ -305,6 +306,7 @@ export function checkAlpn(extensions: Map<number, Uint8Array>, offeredAlpn: stri
|
|
|
305
306
|
* @property {string[]} [alpn] default ['http/1.1']; empty array omits the extension
|
|
306
307
|
* @property {number[]} [versions] default [TLS13, TLS12]
|
|
307
308
|
* @property {Uint8Array[]} [extraExtensions] pre-encoded, sent verbatim (the HRR cookie)
|
|
309
|
+
* @property {number[]} [omitExtensions] extension types to leave out of the hello
|
|
308
310
|
* @property {{ identity: Uint8Array, obfuscatedTicketAge: number, binderLen: number }} [psk]
|
|
309
311
|
* offer this resumption PSK. Encoded with a zeroed binder placeholder; the caller MUST derive
|
|
310
312
|
* the real binder over `message.subarray(0, truncatedLength)` and patch it in at
|
|
@@ -432,6 +434,10 @@ export type ClientHelloOptions = {
|
|
|
432
434
|
* pre-encoded, sent verbatim (the HRR cookie)
|
|
433
435
|
*/
|
|
434
436
|
extraExtensions?: Uint8Array<ArrayBufferLike>[] | undefined;
|
|
437
|
+
/**
|
|
438
|
+
* extension types to leave out of the hello
|
|
439
|
+
*/
|
|
440
|
+
omitExtensions?: number[] | undefined;
|
|
435
441
|
/**
|
|
436
442
|
* offer this resumption PSK. Encoded with a zeroed binder placeholder; the caller MUST derive
|
|
437
443
|
* the real binder over `message.subarray(0, truncatedLength)` and patch it in at
|
package/types/util/bytes.d.ts
CHANGED
|
@@ -74,21 +74,25 @@ export class UnexpectedEofError extends TunnelFetchError {
|
|
|
74
74
|
* be retained beyond the caller's immediate use if memory matters.
|
|
75
75
|
*
|
|
76
76
|
* When the source is a byte stream — on the target runtime, a socket's readable is one — the
|
|
77
|
-
* reader pulls with BYOB reads
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
77
|
+
* reader pulls with BYOB reads instead of taking the source's own chunking. This is measured, not
|
|
78
|
+
* stylistic: the runtime delivers socket data in chunks of at most 4096 bytes, ~1200 of them for a
|
|
79
|
+
* 4 MB body, and every chunk is a runtime/JS boundary crossing; a BYOB read collects several of
|
|
80
|
+
* them into one.
|
|
81
|
+
*
|
|
82
|
+
* How MANY it collects is the transport's decision, not the view's. A BYOB read resolves the
|
|
83
|
+
* instant any byte is available and never waits to fill, so the view is a ceiling that is normally
|
|
84
|
+
* not reached: measured over a 4 MB body, 37 KB average fill on a direct socket and 8 KB through a
|
|
85
|
+
* proxy, whatever the view size. Sizing the view far above that buys nothing and costs the
|
|
86
|
+
* allocation — see BYOB_PULL_BYTES. Sources that are not byte streams (every in-process
|
|
83
87
|
* ReadableStream in this package and its tests) take the default-reader path unchanged.
|
|
84
88
|
*/
|
|
85
89
|
export class ByteReader {
|
|
86
90
|
/**
|
|
87
91
|
* @param {ReadableStream<Uint8Array>} readable
|
|
88
|
-
* @param {number} [pullBytes] size of each BYOB view pulled from the source. Tunable because
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
+
* @param {number} [pullBytes] size of each BYOB view pulled from the source. Tunable because the
|
|
93
|
+
* right value depends on how much the transport hands over per read, and that differs by a
|
|
94
|
+
* factor of four between a direct socket and a proxied one — see BYOB_PULL_BYTES for the sweep.
|
|
95
|
+
* Ignored on sources that are not byte streams, which take the default-reader path.
|
|
92
96
|
*/
|
|
93
97
|
constructor(readable: ReadableStream<Uint8Array>, pullBytes?: number);
|
|
94
98
|
_pullBytes: number;
|