@erfan_bahramali/tping 1.0.0 → 1.1.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 +76 -55
- package/dist/crypto/x25519.d.ts +10 -0
- package/dist/crypto/x25519.d.ts.map +1 -0
- package/dist/crypto/x25519.js +51 -0
- package/dist/crypto/x25519.js.map +1 -0
- package/dist/index.js +82 -39
- package/dist/index.js.map +1 -1
- package/dist/lib/attempt.d.ts +31 -0
- package/dist/lib/attempt.d.ts.map +1 -0
- package/dist/lib/attempt.js +94 -0
- package/dist/lib/attempt.js.map +1 -0
- package/dist/lib/checker.d.ts +21 -3
- package/dist/lib/checker.d.ts.map +1 -1
- package/dist/lib/checker.js +133 -49
- package/dist/lib/checker.js.map +1 -1
- package/dist/lib/datacenters.d.ts +15 -0
- package/dist/lib/datacenters.d.ts.map +1 -0
- package/dist/lib/datacenters.js +20 -0
- package/dist/lib/datacenters.js.map +1 -0
- package/dist/lib/parser.d.ts +18 -3
- package/dist/lib/parser.d.ts.map +1 -1
- package/dist/lib/parser.js +174 -78
- package/dist/lib/parser.js.map +1 -1
- package/dist/lib/ping.d.ts +13 -6
- package/dist/lib/ping.d.ts.map +1 -1
- package/dist/lib/ping.js +44 -44
- package/dist/lib/ping.js.map +1 -1
- package/dist/mtproto/exchange.d.ts +10 -0
- package/dist/mtproto/exchange.d.ts.map +1 -0
- package/dist/mtproto/exchange.js +20 -0
- package/dist/mtproto/exchange.js.map +1 -0
- package/dist/mtproto/fake-tls.d.ts +36 -44
- package/dist/mtproto/fake-tls.d.ts.map +1 -1
- package/dist/mtproto/fake-tls.js +174 -148
- package/dist/mtproto/fake-tls.js.map +1 -1
- package/dist/mtproto/messages.d.ts +23 -36
- package/dist/mtproto/messages.d.ts.map +1 -1
- package/dist/mtproto/messages.js +64 -90
- package/dist/mtproto/messages.js.map +1 -1
- package/dist/mtproto/obfuscation.d.ts +23 -20
- package/dist/mtproto/obfuscation.d.ts.map +1 -1
- package/dist/mtproto/obfuscation.js +64 -64
- package/dist/mtproto/obfuscation.js.map +1 -1
- package/dist/mtproto/secret.d.ts +28 -0
- package/dist/mtproto/secret.d.ts.map +1 -0
- package/dist/mtproto/secret.js +68 -0
- package/dist/mtproto/secret.js.map +1 -0
- package/dist/mtproto/transport.d.ts +28 -0
- package/dist/mtproto/transport.d.ts.map +1 -0
- package/dist/mtproto/transport.js +78 -0
- package/dist/mtproto/transport.js.map +1 -0
- package/dist/net/http-client.d.ts +37 -0
- package/dist/net/http-client.d.ts.map +1 -0
- package/dist/net/http-client.js +93 -0
- package/dist/net/http-client.js.map +1 -0
- package/dist/net/http-proxy.d.ts +26 -0
- package/dist/net/http-proxy.d.ts.map +1 -0
- package/dist/net/http-proxy.js +71 -0
- package/dist/net/http-proxy.js.map +1 -0
- package/dist/net/socks5.d.ts +15 -0
- package/dist/net/socks5.d.ts.map +1 -0
- package/dist/net/socks5.js +112 -0
- package/dist/net/socks5.js.map +1 -0
- package/dist/net/stream.d.ts +54 -0
- package/dist/net/stream.d.ts.map +1 -0
- package/dist/net/stream.js +142 -0
- package/dist/net/stream.js.map +1 -0
- package/dist/net/websocket.d.ts +19 -0
- package/dist/net/websocket.d.ts.map +1 -0
- package/dist/net/websocket.js +123 -0
- package/dist/net/websocket.js.map +1 -0
- package/dist/types/index.d.ts +40 -17
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/format.d.ts +7 -14
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +101 -87
- package/dist/utils/format.js.map +1 -1
- package/dist/webproxy/check.d.ts +15 -0
- package/dist/webproxy/check.d.ts.map +1 -0
- package/dist/webproxy/check.js +423 -0
- package/dist/webproxy/check.js.map +1 -0
- package/dist/webproxy/frames.d.ts +30 -0
- package/dist/webproxy/frames.d.ts.map +1 -0
- package/dist/webproxy/frames.js +59 -0
- package/dist/webproxy/frames.js.map +1 -0
- package/dist/webproxy/link.d.ts +27 -0
- package/dist/webproxy/link.d.ts.map +1 -0
- package/dist/webproxy/link.js +107 -0
- package/dist/webproxy/link.js.map +1 -0
- package/package.json +16 -11
- package/dist/mtproto/connection.d.ts +0 -59
- package/dist/mtproto/connection.d.ts.map +0 -1
- package/dist/mtproto/connection.js +0 -198
- package/dist/mtproto/connection.js.map +0 -1
- package/dist/mtproto/socks5.d.ts +0 -36
- package/dist/mtproto/socks5.d.ts.map +0 -1
- package/dist/mtproto/socks5.js +0 -148
- package/dist/mtproto/socks5.js.map +0 -1
package/dist/mtproto/messages.js
CHANGED
|
@@ -1,104 +1,78 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
const REQ_PQ_MULTI = 0xbe7e8ef1;
|
|
3
|
+
const RES_PQ = 0x05162463;
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Format: time.time() * 2^32, must be divisible by 4
|
|
4
|
-
* Client message IDs must be divisible by 4
|
|
5
|
+
* Client message_id: unixtime * 2^32, divisible by 4.
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Must be divisible by 4 (client messages)
|
|
12
|
-
msgId = (msgId / 4n) * 4n;
|
|
13
|
-
return msgId;
|
|
14
|
-
}
|
|
7
|
+
export const generateMessageId = (nowMs = Date.now()) => {
|
|
8
|
+
const seconds = BigInt(Math.floor(nowMs / 1000));
|
|
9
|
+
const fraction = BigInt(Math.floor(((nowMs % 1000) / 1000) * 2 ** 32));
|
|
10
|
+
return ((seconds << 32n) | fraction) & ~3n;
|
|
11
|
+
};
|
|
15
12
|
/**
|
|
16
|
-
*
|
|
17
|
-
* TL Schema: req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
|
|
18
|
-
*
|
|
19
|
-
* @param nonce - 16-byte random nonce
|
|
20
|
-
* @returns TL-serialized message
|
|
13
|
+
* Unencrypted message: auth_key_id (0) + message_id + length + body.
|
|
21
14
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
export const buildUnencryptedMessage = (body, messageId = generateMessageId()) => {
|
|
16
|
+
const header = Buffer.alloc(20);
|
|
17
|
+
header.writeBigInt64LE(messageId, 8);
|
|
18
|
+
header.writeUInt32LE(body.length, 16);
|
|
19
|
+
return Buffer.concat([header, body]);
|
|
20
|
+
};
|
|
27
21
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* Format:
|
|
31
|
-
* - auth_key_id: 8 bytes (all zeros for unencrypted)
|
|
32
|
-
* - message_id: 8 bytes
|
|
33
|
-
* - message_length: 4 bytes
|
|
34
|
-
* - message: N bytes
|
|
35
|
-
*
|
|
36
|
-
* @param tlMessage - TL-serialized message
|
|
37
|
-
* @param messageId - Message ID
|
|
38
|
-
* @returns Unencrypted message
|
|
22
|
+
* req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
|
|
39
23
|
*/
|
|
40
|
-
export
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return Buffer.concat([authKeyId, msgIdBuf, msgLen, tlMessage]);
|
|
47
|
-
}
|
|
24
|
+
export const buildReqPqMulti = (nonce = randomBytes(16)) => {
|
|
25
|
+
const body = Buffer.alloc(20);
|
|
26
|
+
body.writeUInt32LE(REQ_PQ_MULTI, 0);
|
|
27
|
+
nonce.copy(body, 4);
|
|
28
|
+
return { message: buildUnencryptedMessage(body), nonce };
|
|
29
|
+
};
|
|
48
30
|
/**
|
|
49
|
-
*
|
|
31
|
+
* Parse an unencrypted resPQ message (transport framing already removed;
|
|
32
|
+
* trailing transport padding is allowed).
|
|
50
33
|
*
|
|
51
|
-
*
|
|
52
|
-
* @returns Payload with length prefix
|
|
34
|
+
* resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
|
53
35
|
*/
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
36
|
+
export const parseResPQ = (data) => {
|
|
37
|
+
if (data.length < 20 + 4 + 32) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (data.readBigUInt64LE(0) !== 0n) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const bodyLength = data.readUInt32LE(16);
|
|
44
|
+
if (bodyLength < 36 || 20 + bodyLength > data.length) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (data.readUInt32LE(20) !== RES_PQ) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
nonce: Buffer.from(data.subarray(24, 40)),
|
|
52
|
+
serverNonce: Buffer.from(data.subarray(40, 56)),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
59
55
|
/**
|
|
60
|
-
* Parse
|
|
61
|
-
* TL Schema: resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
|
62
|
-
*
|
|
63
|
-
* @param data - Decrypted response data
|
|
64
|
-
* @returns Parsed response or null if invalid
|
|
56
|
+
* Parse a req_pq_multi message (used by tests and mock servers).
|
|
65
57
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
let offset = 0;
|
|
69
|
-
// Intermediate frame: 4-byte LE length prefix, then the unencrypted
|
|
70
|
-
// MTProto envelope: auth_key_id(8) + message_id(8) + body_length(4) + body.
|
|
71
|
-
if (data.length < 4)
|
|
72
|
-
return null;
|
|
73
|
-
const length = data.readUInt32LE(offset);
|
|
74
|
-
offset += 4;
|
|
75
|
-
if (data.length < 4 + length)
|
|
76
|
-
return null;
|
|
77
|
-
// Parse auth_key_id (8 bytes) - should be all zeros
|
|
78
|
-
const authKeyId = data.slice(offset, offset + 8);
|
|
79
|
-
offset += 8;
|
|
80
|
-
if (!authKeyId.equals(Buffer.alloc(8, 0)))
|
|
81
|
-
return null;
|
|
82
|
-
// Parse message_id (8 bytes) - skip
|
|
83
|
-
offset += 8;
|
|
84
|
-
// Parse message_length (4 bytes) - skip
|
|
85
|
-
offset += 4;
|
|
86
|
-
// Parse constructor (4 bytes)
|
|
87
|
-
const constructor = data.readUInt32LE(offset);
|
|
88
|
-
offset += 4;
|
|
89
|
-
// Valid constructors: 0x05162463 or 0x83c95aec
|
|
90
|
-
if (constructor !== 0x05162463 && constructor !== 0x83c95aec) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
// Parse nonce (16 bytes)
|
|
94
|
-
const nonce = data.slice(offset, offset + 16);
|
|
95
|
-
offset += 16;
|
|
96
|
-
// Parse server_nonce (16 bytes)
|
|
97
|
-
const serverNonce = data.slice(offset, offset + 16);
|
|
98
|
-
return { nonce, serverNonce, constructor };
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
58
|
+
export const parseReqPqMulti = (data) => {
|
|
59
|
+
if (data.length < 40 || data.readBigUInt64LE(0) !== 0n || data.readUInt32LE(20) !== REQ_PQ_MULTI) {
|
|
101
60
|
return null;
|
|
102
61
|
}
|
|
103
|
-
|
|
62
|
+
return Buffer.from(data.subarray(24, 40));
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Build a resPQ reply for the given nonce (used by tests and mock servers).
|
|
66
|
+
*/
|
|
67
|
+
export const buildResPQ = (nonce) => {
|
|
68
|
+
const pq = Buffer.from([0x08, 0x17, 0xed, 0x48, 0x94, 0x1a, 0x08, 0xf9, 0x81, 0x00, 0x00, 0x00]);
|
|
69
|
+
const fingerprints = Buffer.alloc(16);
|
|
70
|
+
fingerprints.writeUInt32LE(0x1cb5c415, 0); // vector constructor
|
|
71
|
+
fingerprints.writeUInt32LE(1, 4);
|
|
72
|
+
fingerprints.writeBigUInt64LE(0xc3b42b026ce86b21n, 8);
|
|
73
|
+
const body = Buffer.concat([Buffer.alloc(4), nonce, randomBytes(16), pq, fingerprints]);
|
|
74
|
+
body.writeUInt32LE(RES_PQ, 0);
|
|
75
|
+
const message = buildUnencryptedMessage(body, (generateMessageId() | 1n));
|
|
76
|
+
return message;
|
|
77
|
+
};
|
|
104
78
|
//# sourceMappingURL=messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/mtproto/messages.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/mtproto/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { AESCTR } from '../crypto/aes-ctr.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
SECURE: Buffer<ArrayBuffer>;
|
|
9
|
-
};
|
|
10
|
-
interface ObfuscationKeys {
|
|
11
|
-
header: Buffer;
|
|
12
|
-
encryptor: AESCTR;
|
|
13
|
-
decryptor: AESCTR;
|
|
2
|
+
import type { Pipe } from '../net/stream.js';
|
|
3
|
+
import { type TransportMode } from './transport.js';
|
|
4
|
+
export interface Obfuscation {
|
|
5
|
+
readonly header: Buffer;
|
|
6
|
+
readonly encryptor: AESCTR;
|
|
7
|
+
readonly decryptor: AESCTR;
|
|
14
8
|
}
|
|
15
9
|
/**
|
|
16
|
-
*
|
|
17
|
-
* Generates random 64-byte header and derives encryption keys.
|
|
10
|
+
* Build the obfuscated2 init header (MTProto transport obfuscation).
|
|
18
11
|
*
|
|
19
|
-
* @param
|
|
20
|
-
* @param
|
|
21
|
-
* @param
|
|
22
|
-
* @returns Header buffer and encryptor/decryptor ciphers
|
|
12
|
+
* @param dcId - DC id written at offset 60 (signed 16-bit LE); negative for media DCs
|
|
13
|
+
* @param mode - transport framing, selects the protocol tag at offset 56
|
|
14
|
+
* @param secret - 16-byte proxy key; omitted for direct connections to a DC
|
|
23
15
|
*/
|
|
24
|
-
export declare
|
|
25
|
-
|
|
16
|
+
export declare const createObfuscation: (dcId: number, mode: TransportMode, secret?: Buffer) => Obfuscation;
|
|
17
|
+
/**
|
|
18
|
+
* Pipe that prepends the init header to the first write and AES-CTR
|
|
19
|
+
* encrypts/decrypts everything else.
|
|
20
|
+
*/
|
|
21
|
+
export declare class ObfuscatedPipe implements Pipe {
|
|
22
|
+
private readonly inner;
|
|
23
|
+
private readonly obfuscation;
|
|
24
|
+
private headerSent;
|
|
25
|
+
constructor(inner: Pipe, obfuscation: Obfuscation);
|
|
26
|
+
write(data: Buffer): void;
|
|
27
|
+
read(length: number): Promise<Buffer>;
|
|
28
|
+
}
|
|
26
29
|
//# sourceMappingURL=obfuscation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obfuscation.d.ts","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"obfuscation.d.ts","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAgBnE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAeD;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,MAAM,aAAa,EAAE,SAAS,MAAM,KAAG,WAoBtF,CAAC;AAEF;;;GAGG;AACH,qBAAa,cAAe,YAAW,IAAI;IAG7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAAQ,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFtE,OAAO,CAAC,UAAU,CAAS;gBAEE,KAAK,EAAE,IAAI,EAAmB,WAAW,EAAE,WAAW;IAEnF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAUnB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5C"}
|
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
import { randomBytes, createHash } from 'crypto';
|
|
2
2
|
import { AESCTR } from '../crypto/aes-ctr.js';
|
|
3
|
+
import { PROTOCOL_TAGS } from './transport.js';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* First-int values the 64-byte init header must not start with: they would make
|
|
6
|
+
* the connection look like HTTP (HEAD/POST/GET /OPTIONS), TLS, or a plain transport tag.
|
|
5
7
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const RESERVED_KEYWORDS = [
|
|
15
|
-
Buffer.from([0x50, 0x56, 0x72, 0x47]), // PVrG
|
|
16
|
-
Buffer.from([0x47, 0x45, 0x54, 0x20]), // GET
|
|
17
|
-
Buffer.from([0x50, 0x4f, 0x53, 0x54]), // POST
|
|
18
|
-
Buffer.from([0xee, 0xee, 0xee, 0xee]), // eeee
|
|
8
|
+
const FORBIDDEN_FIRST_INTS = [
|
|
9
|
+
0x44414548, // HEAD
|
|
10
|
+
0x54534f50, // POST
|
|
11
|
+
0x20544547, // GET
|
|
12
|
+
0x4954504f, // OPTI
|
|
13
|
+
0x02010316, // TLS handshake
|
|
14
|
+
0xdddddddd,
|
|
15
|
+
0xeeeeeeee,
|
|
19
16
|
];
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
* @param secret - 16-byte secret from proxy
|
|
25
|
-
* @param dcId - Telegram datacenter ID
|
|
26
|
-
* @param protocolTag - Transport protocol tag (default: INTERMEDIATE)
|
|
27
|
-
* @returns Header buffer and encryptor/decryptor ciphers
|
|
28
|
-
*/
|
|
29
|
-
export function initObfuscation(secret, dcId, protocolTag = PROTO_TAG.INTERMEDIATE) {
|
|
30
|
-
if (secret.length !== 16) {
|
|
31
|
-
throw new Error('Secret must be 16 bytes');
|
|
32
|
-
}
|
|
33
|
-
// Generate 64 random bytes, avoiding reserved patterns
|
|
34
|
-
let random;
|
|
35
|
-
while (true) {
|
|
36
|
-
random = randomBytes(64);
|
|
37
|
-
// Check if first byte is not 0xef
|
|
17
|
+
const generateRandomHeader = () => {
|
|
18
|
+
for (;;) {
|
|
19
|
+
const random = randomBytes(64);
|
|
38
20
|
if (random[0] === 0xef)
|
|
39
21
|
continue;
|
|
40
|
-
|
|
41
|
-
if (random.slice(4, 8).equals(Buffer.alloc(4)))
|
|
22
|
+
if (FORBIDDEN_FIRST_INTS.includes(random.readUInt32LE(0)))
|
|
42
23
|
continue;
|
|
43
|
-
|
|
44
|
-
const isReserved = RESERVED_KEYWORDS.some((keyword) => keyword.equals(random.slice(0, 4)));
|
|
45
|
-
if (isReserved)
|
|
24
|
+
if (random.readUInt32LE(4) === 0)
|
|
46
25
|
continue;
|
|
47
|
-
|
|
26
|
+
return random;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const deriveKey = (material, secret) => secret ? createHash('sha256').update(material).update(secret).digest() : Buffer.from(material);
|
|
30
|
+
/**
|
|
31
|
+
* Build the obfuscated2 init header (MTProto transport obfuscation).
|
|
32
|
+
*
|
|
33
|
+
* @param dcId - DC id written at offset 60 (signed 16-bit LE); negative for media DCs
|
|
34
|
+
* @param mode - transport framing, selects the protocol tag at offset 56
|
|
35
|
+
* @param secret - 16-byte proxy key; omitted for direct connections to a DC
|
|
36
|
+
*/
|
|
37
|
+
export const createObfuscation = (dcId, mode, secret) => {
|
|
38
|
+
if (secret && secret.length !== 16) {
|
|
39
|
+
throw new Error('Proxy secret key must be 16 bytes');
|
|
48
40
|
}
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const encryptKey = createHash('sha256')
|
|
55
|
-
.update(Buffer.concat([header.slice(8, 40), secret]))
|
|
56
|
-
.digest(); // 32 bytes
|
|
57
|
-
const encryptIv = header.slice(40, 56); // 16 bytes
|
|
58
|
-
// Derive decryption key and IV
|
|
59
|
-
const decryptKey = createHash('sha256')
|
|
60
|
-
.update(Buffer.concat([reversed.slice(0, 32), secret]))
|
|
61
|
-
.digest();
|
|
62
|
-
const decryptIv = reversed.slice(32, 48);
|
|
63
|
-
// Create AES-256-CTR ciphers
|
|
64
|
-
const encryptor = new AESCTR(encryptKey, encryptIv);
|
|
65
|
-
const decryptor = new AESCTR(decryptKey, decryptIv);
|
|
66
|
-
// Encode protocol tag and DC ID at positions 56-62
|
|
67
|
-
protocolTag.copy(header, 56);
|
|
68
|
-
// DC ID as signed 16-bit little-endian
|
|
41
|
+
const header = generateRandomHeader();
|
|
42
|
+
const reversed = Buffer.from(header.subarray(8, 56)).reverse();
|
|
43
|
+
const encryptor = new AESCTR(deriveKey(header.subarray(8, 40), secret), Buffer.from(header.subarray(40, 56)));
|
|
44
|
+
const decryptor = new AESCTR(deriveKey(reversed.subarray(0, 32), secret), Buffer.from(reversed.subarray(32, 48)));
|
|
45
|
+
header.writeUInt32LE(PROTOCOL_TAGS[mode], 56);
|
|
69
46
|
header.writeInt16LE(dcId, 60);
|
|
70
|
-
// Encrypt the
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const encryptedTail = encryptor.encrypt(header).subarray(56);
|
|
75
|
-
encryptedTail.copy(header, 56);
|
|
47
|
+
// Encrypt the full header to advance the keystream by 64 bytes, but only
|
|
48
|
+
// the tail (protocol tag, DC id, 2 random bytes) goes out encrypted.
|
|
49
|
+
const encrypted = encryptor.encrypt(header);
|
|
50
|
+
encrypted.copy(header, 56, 56);
|
|
76
51
|
return { header, encryptor, decryptor };
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Pipe that prepends the init header to the first write and AES-CTR
|
|
55
|
+
* encrypts/decrypts everything else.
|
|
56
|
+
*/
|
|
57
|
+
export class ObfuscatedPipe {
|
|
58
|
+
inner;
|
|
59
|
+
obfuscation;
|
|
60
|
+
headerSent = false;
|
|
61
|
+
constructor(inner, obfuscation) {
|
|
62
|
+
this.inner = inner;
|
|
63
|
+
this.obfuscation = obfuscation;
|
|
64
|
+
}
|
|
65
|
+
write(data) {
|
|
66
|
+
const encrypted = this.obfuscation.encryptor.encrypt(data);
|
|
67
|
+
if (this.headerSent) {
|
|
68
|
+
this.inner.write(encrypted);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.headerSent = true;
|
|
72
|
+
this.inner.write(Buffer.concat([this.obfuscation.header, encrypted]));
|
|
73
|
+
}
|
|
74
|
+
async read(length) {
|
|
75
|
+
return this.obfuscation.decryptor.encrypt(await this.inner.read(length));
|
|
76
|
+
}
|
|
77
77
|
}
|
|
78
78
|
//# sourceMappingURL=obfuscation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obfuscation.js","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"obfuscation.js","sourceRoot":"","sources":["../../src/mtproto/obfuscation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAsB,MAAM,gBAAgB,CAAC;AAEnE;;;GAGG;AACH,MAAM,oBAAoB,GAAsB;IAC9C,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,OAAO;IACnB,UAAU,EAAE,gBAAgB;IAC5B,UAAU;IACV,UAAU;CACX,CAAC;AAQF,MAAM,oBAAoB,GAAG,GAAW,EAAE;IACxC,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,SAAS;QACjC,IAAI,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QACpE,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QAC3C,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,QAAgB,EAAE,MAAe,EAAU,EAAE,CAC9D,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAEjG;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAY,EAAE,IAAmB,EAAE,MAAe,EAAe,EAAE;IACnG,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,oBAAoB,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IAE/D,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9G,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAElH,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAE9B,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAE/B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAC1C,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,cAAc;IAGI;IAA8B;IAFnD,UAAU,GAAG,KAAK,CAAC;IAE3B,YAA6B,KAAW,EAAmB,WAAwB;QAAtD,UAAK,GAAL,KAAK,CAAM;QAAmB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAEvF,KAAK,CAAC,IAAY;QAChB,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,OAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MTProto proxy secret handling, matching Telegram Android (tgnet) and Desktop.
|
|
3
|
+
*
|
|
4
|
+
* Accepted encodings: hex, or base64 / base64url (padding optional).
|
|
5
|
+
* Accepted layouts (raw bytes):
|
|
6
|
+
* 16 bytes -> simple obfuscation, intermediate transport (0xeeeeeeee)
|
|
7
|
+
* 0xdd + 16 bytes -> secure, padded intermediate transport (0xdddddddd)
|
|
8
|
+
* 0xee + 16 bytes + domain -> fake TLS, padded intermediate inside TLS records
|
|
9
|
+
*/
|
|
10
|
+
export type SecretKind = 'simple' | 'secure' | 'faketls';
|
|
11
|
+
export interface ParsedSecret {
|
|
12
|
+
readonly kind: SecretKind;
|
|
13
|
+
/** 16-byte key used for obfuscation key derivation and fake TLS HMAC. */
|
|
14
|
+
readonly key: Buffer;
|
|
15
|
+
/** SNI domain (fake TLS only). */
|
|
16
|
+
readonly domain?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare class SecretError extends Error {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Decode a secret string the same way the official clients do:
|
|
23
|
+
* all-hex with even length is hex, anything else is base64url.
|
|
24
|
+
*/
|
|
25
|
+
export declare const decodeSecret: (value: string) => Buffer | null;
|
|
26
|
+
export declare const parseSecret: (value: string) => ParsedSecret;
|
|
27
|
+
export declare const describeSecretKind: (kind: SecretKind) => string;
|
|
28
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/mtproto/secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,IAcrD,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,YA0B3C,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,UAAU,KAAG,MASrD,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MTProto proxy secret handling, matching Telegram Android (tgnet) and Desktop.
|
|
3
|
+
*
|
|
4
|
+
* Accepted encodings: hex, or base64 / base64url (padding optional).
|
|
5
|
+
* Accepted layouts (raw bytes):
|
|
6
|
+
* 16 bytes -> simple obfuscation, intermediate transport (0xeeeeeeee)
|
|
7
|
+
* 0xdd + 16 bytes -> secure, padded intermediate transport (0xdddddddd)
|
|
8
|
+
* 0xee + 16 bytes + domain -> fake TLS, padded intermediate inside TLS records
|
|
9
|
+
*/
|
|
10
|
+
export class SecretError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'SecretError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decode a secret string the same way the official clients do:
|
|
18
|
+
* all-hex with even length is hex, anything else is base64url.
|
|
19
|
+
*/
|
|
20
|
+
export const decodeSecret = (value) => {
|
|
21
|
+
const text = value.trim();
|
|
22
|
+
if (text.length === 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (text.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(text)) {
|
|
26
|
+
return Buffer.from(text, 'hex');
|
|
27
|
+
}
|
|
28
|
+
if (/^[A-Za-z0-9+/_-]+={0,2}$/.test(text)) {
|
|
29
|
+
const standard = text.replace(/-/g, '+').replace(/_/g, '/').replace(/=+$/, '');
|
|
30
|
+
const decoded = Buffer.from(standard, 'base64');
|
|
31
|
+
return decoded.length > 0 ? decoded : null;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
35
|
+
export const parseSecret = (value) => {
|
|
36
|
+
const bytes = decodeSecret(value);
|
|
37
|
+
if (!bytes) {
|
|
38
|
+
throw new SecretError('Secret is neither hex nor base64');
|
|
39
|
+
}
|
|
40
|
+
if (bytes.length === 16) {
|
|
41
|
+
return { kind: 'simple', key: bytes };
|
|
42
|
+
}
|
|
43
|
+
if (bytes[0] === 0xdd) {
|
|
44
|
+
if (bytes.length !== 17) {
|
|
45
|
+
throw new SecretError(`dd secret must be 17 bytes, got ${bytes.length}`);
|
|
46
|
+
}
|
|
47
|
+
return { kind: 'secure', key: bytes.subarray(1, 17) };
|
|
48
|
+
}
|
|
49
|
+
if (bytes[0] === 0xee) {
|
|
50
|
+
if (bytes.length <= 17) {
|
|
51
|
+
throw new SecretError(`Fake-TLS (ee) secret has no domain; Telegram rejects it (as dd it would be dd${bytes.subarray(1).toString('hex')})`);
|
|
52
|
+
}
|
|
53
|
+
const domain = bytes.subarray(17).toString('utf8');
|
|
54
|
+
return { kind: 'faketls', key: bytes.subarray(1, 17), domain };
|
|
55
|
+
}
|
|
56
|
+
throw new SecretError(`Unsupported secret format (${bytes.length} bytes, prefix 0x${bytes[0].toString(16).padStart(2, '0')})`);
|
|
57
|
+
};
|
|
58
|
+
export const describeSecretKind = (kind) => {
|
|
59
|
+
switch (kind) {
|
|
60
|
+
case 'faketls':
|
|
61
|
+
return 'Fake TLS';
|
|
62
|
+
case 'secure':
|
|
63
|
+
return 'dd';
|
|
64
|
+
default:
|
|
65
|
+
return 'simple';
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/mtproto/secret.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Pipe } from '../net/stream.js';
|
|
2
|
+
/**
|
|
3
|
+
* MTProto TCP transports (https://core.telegram.org/mtproto/mtproto-transports).
|
|
4
|
+
*
|
|
5
|
+
* The official clients pick the transport from the secret:
|
|
6
|
+
* no secret (direct DC, SOCKS5, HTTP) -> abridged
|
|
7
|
+
* 16-byte secret -> intermediate
|
|
8
|
+
* dd / ee secret -> padded intermediate
|
|
9
|
+
*/
|
|
10
|
+
export type TransportMode = 'abridged' | 'intermediate' | 'padded';
|
|
11
|
+
export declare const PROTOCOL_TAGS: Readonly<Record<TransportMode, number>>;
|
|
12
|
+
/**
|
|
13
|
+
* A 4-byte frame carrying a negative error code instead of a message.
|
|
14
|
+
*/
|
|
15
|
+
export declare class TransportError extends Error {
|
|
16
|
+
readonly code: number;
|
|
17
|
+
constructor(code: number);
|
|
18
|
+
}
|
|
19
|
+
export declare class FramingError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
export declare const encodeFrame: (mode: TransportMode, payload: Buffer) => Buffer;
|
|
23
|
+
/**
|
|
24
|
+
* Read one server frame. Quick-ack frames are skipped; 4-byte error frames throw TransportError.
|
|
25
|
+
* For the padded transport the returned payload may carry trailing padding.
|
|
26
|
+
*/
|
|
27
|
+
export declare const readFrame: (mode: TransportMode, pipe: Pipe) => Promise<Buffer>;
|
|
28
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/mtproto/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEnE,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAIjE,CAAC;AAUF;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAIlC;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,WAAW,GAAI,MAAM,aAAa,EAAE,SAAS,MAAM,KAAG,MAiBlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAU,MAAM,aAAa,EAAE,MAAM,IAAI,KAAG,OAAO,CAAC,MAAM,CA6B/E,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { randomBytes, randomInt } from 'crypto';
|
|
2
|
+
export const PROTOCOL_TAGS = {
|
|
3
|
+
abridged: 0xefefefef,
|
|
4
|
+
intermediate: 0xeeeeeeee,
|
|
5
|
+
padded: 0xdddddddd,
|
|
6
|
+
};
|
|
7
|
+
const MAX_FRAME = 1 << 24;
|
|
8
|
+
const TRANSPORT_ERRORS = {
|
|
9
|
+
[-404]: 'not found',
|
|
10
|
+
[-429]: 'too many connections from this IP (flood)',
|
|
11
|
+
[-444]: 'invalid DC',
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* A 4-byte frame carrying a negative error code instead of a message.
|
|
15
|
+
*/
|
|
16
|
+
export class TransportError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
constructor(code) {
|
|
19
|
+
super(`Telegram returned transport error ${code}${TRANSPORT_ERRORS[code] ? ` (${TRANSPORT_ERRORS[code]})` : ''}`);
|
|
20
|
+
this.code = code;
|
|
21
|
+
this.name = 'TransportError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class FramingError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'FramingError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const encodeFrame = (mode, payload) => {
|
|
31
|
+
if (payload.length % 4 !== 0) {
|
|
32
|
+
throw new Error('MTProto payload length must be a multiple of 4');
|
|
33
|
+
}
|
|
34
|
+
if (mode === 'abridged') {
|
|
35
|
+
const words = payload.length / 4;
|
|
36
|
+
const header = words < 0x7f
|
|
37
|
+
? Buffer.from([words])
|
|
38
|
+
: Buffer.from([0x7f, words & 0xff, (words >> 8) & 0xff, (words >> 16) & 0xff]);
|
|
39
|
+
return Buffer.concat([header, payload]);
|
|
40
|
+
}
|
|
41
|
+
const padding = mode === 'padded' ? randomBytes(randomInt(16)) : Buffer.alloc(0);
|
|
42
|
+
const header = Buffer.alloc(4);
|
|
43
|
+
header.writeUInt32LE(payload.length + padding.length, 0);
|
|
44
|
+
return Buffer.concat([header, payload, padding]);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Read one server frame. Quick-ack frames are skipped; 4-byte error frames throw TransportError.
|
|
48
|
+
* For the padded transport the returned payload may carry trailing padding.
|
|
49
|
+
*/
|
|
50
|
+
export const readFrame = async (mode, pipe) => {
|
|
51
|
+
for (;;) {
|
|
52
|
+
let length;
|
|
53
|
+
if (mode === 'abridged') {
|
|
54
|
+
const first = (await pipe.read(1))[0];
|
|
55
|
+
if (first & 0x80) {
|
|
56
|
+
await pipe.read(3); // quick ack
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
length = first === 0x7f ? (await pipe.read(3)).readUIntLE(0, 3) * 4 : first * 4;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const value = (await pipe.read(4)).readUInt32LE(0);
|
|
63
|
+
if (value & 0x80000000) {
|
|
64
|
+
continue; // quick ack
|
|
65
|
+
}
|
|
66
|
+
length = value;
|
|
67
|
+
}
|
|
68
|
+
if (length < 4 || length > MAX_FRAME) {
|
|
69
|
+
throw new FramingError(`Invalid frame length ${length} from server (wrong secret or not an MTProto proxy)`);
|
|
70
|
+
}
|
|
71
|
+
const payload = await pipe.read(length);
|
|
72
|
+
if (length === 4) {
|
|
73
|
+
throw new TransportError(payload.readInt32LE(0));
|
|
74
|
+
}
|
|
75
|
+
return payload;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=transport.js.map
|