@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/README.md
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
# 🏓
|
|
1
|
+
# 🏓 TPing
|
|
2
2
|
|
|
3
|
-
**Ping for Telegram Proxies
|
|
3
|
+
**Ping for Telegram Proxies.** It checks Telegram proxies the way `ping` checks IP addresses.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
TPing connects through the proxy exactly like the official Telegram apps (Android and Desktop) and only reports success when a Telegram datacenter actually answers. If TPing says a proxy works, Telegram can use it. If it fails, the error says where it broke.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## ✨ Features
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
11
|
+
- 🔐 **Every official proxy type**: MTProto with simple, `dd`, and Fake TLS (`ee`) secrets, SOCKS5 with or without login, HTTP (Telegram Desktop), and the new WEB proxy (all four carrier modes)
|
|
12
|
+
- 🤝 **Real handshake**: a `req_pq_multi` request goes through the proxy to the Telegram datacenter and must come back with our nonce
|
|
13
|
+
- 🌐 **Datacenter selection**: test DC 2 (default), any set of DCs, or all five
|
|
14
|
+
- 🏓 **Continuous mode** like `ping`, with count and interval
|
|
15
|
+
- 🔄 **Many proxies at once**: from arguments, a file, or stdin
|
|
16
|
+
- 📊 **Statistics**: loss, min/avg/max/mdev, last error
|
|
17
|
+
- 🔍 **Clear errors**, for example: wrong secret, timeout during the Fake TLS handshake, SOCKS5 authentication failed
|
|
16
18
|
|
|
17
19
|
---
|
|
18
20
|
|
|
@@ -22,7 +24,7 @@ A simple and clean CLI tool to continuously ping Telegram proxies (MTProto and S
|
|
|
22
24
|
npm install -g @erfan_bahramali/tping
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
Or use directly with npx:
|
|
27
|
+
Or use it directly with npx:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
28
30
|
npx @erfan_bahramali/tping <proxy>
|
|
@@ -32,27 +34,23 @@ npx @erfan_bahramali/tping <proxy>
|
|
|
32
34
|
|
|
33
35
|
## 🚀 Usage
|
|
34
36
|
|
|
35
|
-
You can pass proxy as text or file path. For multiple proxies, separate by newlines (in text or file).
|
|
36
|
-
|
|
37
37
|
```bash
|
|
38
38
|
# Single proxy
|
|
39
|
-
tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=
|
|
39
|
+
tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=ee0123456789abcdef0123456789abcdef7777772e676f6f676c652e636f6d"
|
|
40
40
|
|
|
41
|
-
#
|
|
42
|
-
tping "
|
|
43
|
-
https://t.me/socks?server=
|
|
41
|
+
# Several proxies (arguments, newlines, or spaces)
|
|
42
|
+
tping "tg://proxy?server=a.example.com&port=443&secret=dd0123456789abcdef0123456789abcdef" \
|
|
43
|
+
"https://t.me/socks?server=b.example.com&port=1080&user=me&pass=secret"
|
|
44
44
|
|
|
45
|
-
# From file
|
|
45
|
+
# From a file, or from stdin
|
|
46
46
|
tping proxies.txt
|
|
47
|
+
cat proxies.txt | tping -
|
|
47
48
|
|
|
48
|
-
#
|
|
49
|
-
tping -c 5
|
|
50
|
-
|
|
51
|
-
# Ping unlimited times with custom interval (500ms)
|
|
52
|
-
tping -i 500 "https://t.me/socks?server=proxy.example.com&port=1080"
|
|
49
|
+
# 5 pings, 500ms apart, through all five Telegram datacenters
|
|
50
|
+
tping -c 5 -i 500 --dc all proxies.txt
|
|
53
51
|
|
|
54
|
-
#
|
|
55
|
-
tping -
|
|
52
|
+
# Show how long each phase took
|
|
53
|
+
tping -V -c 3 proxies.txt
|
|
56
54
|
```
|
|
57
55
|
|
|
58
56
|
---
|
|
@@ -63,56 +61,79 @@ tping -c 10 -i 500 proxies.txt
|
|
|
63
61
|
|--------|-------------|
|
|
64
62
|
| `-c, --count <n>` | Number of pings to send (default: unlimited) |
|
|
65
63
|
| `-i, --interval <ms>` | Interval between pings in milliseconds (default: 1000) |
|
|
66
|
-
| `-t, --timeout <ms>` | Timeout for
|
|
64
|
+
| `-t, --timeout <ms>` | Timeout for one ping (connect + handshake + reply) in milliseconds (default: 10000) |
|
|
65
|
+
| `-d, --dc <ids>` | Telegram DC(s) to test: `1`–`5`, comma-separated, or `all` (default: 2) |
|
|
66
|
+
| `-6, --ipv6` | Use IPv6 datacenter addresses for SOCKS5/HTTP proxies |
|
|
67
|
+
| `-V, --verbose` | Show the time of each phase: TCP connect, proxy handshake, Telegram round trip |
|
|
67
68
|
| `-v, --version` | Show version number |
|
|
68
69
|
| `-h, --help` | Show help |
|
|
69
70
|
|
|
71
|
+
**Exit status:** `0` if every proxy answered at least once, `1` if any proxy never answered, `2` on invalid input.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 🔗 Supported proxy formats
|
|
76
|
+
|
|
77
|
+
| Type | Formats |
|
|
78
|
+
|------|---------|
|
|
79
|
+
| MTProto | `https://t.me/proxy?server=…&port=…&secret=…`, also `telegram.me`, `telegram.dog`, `www.`, `http://`, no scheme, `tg://proxy?…`, `tg:proxy?…` |
|
|
80
|
+
| SOCKS5 | `https://t.me/socks?server=…&port=…&user=…&pass=…`, `tg://socks?…`, `socks5://user:pass@host:port` |
|
|
81
|
+
| HTTP | `http://user:pass@host:port` (HTTP proxies are only supported by Telegram Desktop) |
|
|
82
|
+
| WEB | `https://t.me/webproxy?server=host[/path]&secret=…`, `tg://webproxy?…` (always HTTPS on port 443) |
|
|
83
|
+
|
|
84
|
+
MTProto secrets may be hex or base64/base64url, like in the official apps:
|
|
85
|
+
|
|
86
|
+
| Secret | Mode | What TPing does |
|
|
87
|
+
|--------|------|-----------------|
|
|
88
|
+
| 16 bytes | simple | obfuscated intermediate transport |
|
|
89
|
+
| `dd` + 16 bytes | secure | obfuscated padded intermediate transport |
|
|
90
|
+
| `ee` + 16 bytes + domain | Fake TLS | TLS-looking handshake with the domain as SNI, then padded intermediate inside TLS records |
|
|
91
|
+
|
|
92
|
+
WEB proxy secrets are 16 bytes or `dd` + 16 bytes (no Fake TLS). A link whose server has a base path (`host%2Fpath`) carries the secret in its marked form (`0x70` + secret, base64url), as the official installer generates it.
|
|
93
|
+
|
|
94
|
+
Shell-escaped links (`t.me/proxy\?server\=…`) are accepted. Invalid lines are reported and skipped.
|
|
95
|
+
|
|
70
96
|
---
|
|
71
97
|
|
|
72
98
|
## 📊 Example Output
|
|
73
99
|
|
|
74
100
|
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
✓ proxy.example.com:443 │ MTProto │ seq=1 │ 45ms
|
|
79
|
-
✓ proxy.example.com:443 │ MTProto │ seq=2 │ 48ms
|
|
80
|
-
✓ proxy.example.com:443 │ MTProto │ seq=3 │ 43ms
|
|
81
|
-
^C
|
|
82
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
83
|
-
📊 Statistics for proxy.example.com:443
|
|
84
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
85
|
-
Packets: 3 sent │ 3 received │ 0% loss
|
|
86
|
-
Latency: 43ms min │ 45ms avg │ 48ms max
|
|
87
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
101
|
+
✅ #1 │ [1] proxy.example.com:443 │ MTProto · Fake TLS │ 182ms
|
|
102
|
+
❌ #1 │ [2] 203.0.113.7:8443 │ MTProto · dd │ Proxy closed the connection before Telegram answered (wrong secret, or the proxy cannot reach Telegram)
|
|
103
|
+
✅ #1 │ [3] socks.example.com:1080 │ SOCKS5 │ 95ms
|
|
88
104
|
```
|
|
89
105
|
|
|
106
|
+
Press Ctrl+C to stop and print the statistics table. Press it twice to quit immediately.
|
|
107
|
+
|
|
90
108
|
---
|
|
91
109
|
|
|
92
|
-
## 🔍 How it
|
|
110
|
+
## 🔍 How it works
|
|
111
|
+
|
|
112
|
+
TPing builds the same connection as the official apps:
|
|
113
|
+
|
|
114
|
+
1. **Fake TLS (`ee`)**: sends a Chrome-like TLS ClientHello (shuffled extensions, GREASE, a valid x25519 key share, padded to 517 bytes). The client random is an HMAC-SHA256 of the hello with the secret, with the current time mixed in. The proxy's reply must be exactly ServerHello + ChangeCipherSpec + one Application Data record carrying a valid HMAC. A wrong secret, or the real website answering instead of the proxy, is detected here.
|
|
115
|
+
2. **Transport obfuscation**: a random 64-byte header carries the protocol tag and the DC id, encrypted with AES-256-CTR keys derived from the secret ([MTProto transport obfuscation](https://core.telegram.org/mtproto/mtproto-transports#transport-obfuscation)).
|
|
116
|
+
3. **MTProto ping**: a `req_pq_multi` request is sent and a `resPQ` with the same nonce must come back from the Telegram datacenter.
|
|
117
|
+
|
|
118
|
+
For **WEB proxies** ([tproxy-server](https://github.com/telegramdesktop/tproxy-server)), TPing does what the Telegram app and its bridge page do together. It derives the bridge capability (HMAC-SHA256 of the hostname, and base path if any, keyed with the secret) and loads `https://host/?bridge=…` with full certificate checks. It then creates a relay session (HELLO → WELCOME) and opens one stream per tested DC, carrying the normal obfuscated MTProxy bytes until the DC answers. All four carrier modes the relay can choose are supported: `https`, `https-lanes`, `websocket` and `websocket-lanes`. The session is deleted afterwards so it does not take up a slot on the relay. The official apps skip WEB proxies in their availability checks; TPing tests them for real.
|
|
119
|
+
|
|
120
|
+
For SOCKS5 and HTTP proxies, TPing opens a tunnel to the Telegram datacenter's IP and runs the same obfuscated MTProto handshake through it. For HTTP proxies that refuse `CONNECT`, it falls back to the HTTP transport, as Telegram Desktop does.
|
|
93
121
|
|
|
94
|
-
|
|
95
|
-
step every official Telegram client does:
|
|
122
|
+
The reported latency covers the whole sequence, TCP connect + proxy handshake + Telegram round trip, which is the same thing the official apps show as a proxy's ping. With several DCs, the slowest DC is reported and each DC is listed.
|
|
96
123
|
|
|
97
|
-
|
|
98
|
-
keys derived from the proxy secret (per the official
|
|
99
|
-
[MTProto transport obfuscation](https://core.telegram.org/mtproto/mtproto-transports#transport-obfuscation)).
|
|
100
|
-
2. **Fake TLS** (secrets with `ee` prefix) — a TLS 1.3 ClientHello with a valid
|
|
101
|
-
HMAC digest is sent; the proxy's signed ServerHello is verified before any
|
|
102
|
-
data is trusted. Wrong secrets are detected immediately.
|
|
103
|
-
3. **MTProto ping** — a `req_pq_multi` request is sent through the proxy to the
|
|
104
|
-
Telegram datacenter. The measured latency is the round trip of receiving a
|
|
105
|
-
valid `resPQ` reply with our echoed nonce. A proxy only passes if the
|
|
106
|
-
datacenter actually answers — dead or fake proxies are reported as failures,
|
|
107
|
-
never as successes.
|
|
124
|
+
---
|
|
108
125
|
|
|
109
|
-
|
|
110
|
-
|
|
126
|
+
## 🧪 Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
bun install
|
|
130
|
+
bun test # unit tests + end-to-end tests against local mock servers (MTProxy, Fake TLS, SOCKS5, HTTP, WEB relay)
|
|
131
|
+
bun run build
|
|
132
|
+
TPING_LIVE_PROXY="https://t.me/proxy?server=…&port=…&secret=…" bun test # also test a real proxy
|
|
133
|
+
```
|
|
111
134
|
|
|
112
135
|
---
|
|
113
136
|
|
|
114
137
|
## 📋 License
|
|
115
138
|
|
|
116
139
|
MIT © 2026 [ErfanBahramali](https://github.com/ErfanBahramali)
|
|
117
|
-
|
|
118
|
-
---
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const isQuadraticResidue: (value: bigint) => boolean;
|
|
2
|
+
export declare const bufferToBigInt: (buffer: Buffer) => bigint;
|
|
3
|
+
/**
|
|
4
|
+
* Generate a public key that is a valid x25519 point, as Telegram clients do
|
|
5
|
+
* for the fake TLS key_share extension (a random 32-byte string would be an
|
|
6
|
+
* easy DPI fingerprint, since half of them are not on the curve).
|
|
7
|
+
*/
|
|
8
|
+
export declare const generateX25519PublicKey: () => Buffer;
|
|
9
|
+
export declare const isOnCurve: (key: Buffer) => boolean;
|
|
10
|
+
//# sourceMappingURL=x25519.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x25519.d.ts","sourceRoot":"","sources":["../../src/crypto/x25519.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,KAAG,OACE,CAAC;AAStD,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,MAAsD,CAAC;AAEvG;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QAAO,MAa1C,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,KAAG,OAAgE,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
const P = (1n << 255n) - 19n;
|
|
3
|
+
const A = 486662n;
|
|
4
|
+
const mod = (value) => {
|
|
5
|
+
const result = value % P;
|
|
6
|
+
return result < 0n ? result + P : result;
|
|
7
|
+
};
|
|
8
|
+
const modPow = (base, exponent) => {
|
|
9
|
+
let result = 1n;
|
|
10
|
+
let b = mod(base);
|
|
11
|
+
let e = exponent;
|
|
12
|
+
while (e > 0n) {
|
|
13
|
+
if (e & 1n) {
|
|
14
|
+
result = (result * b) % P;
|
|
15
|
+
}
|
|
16
|
+
b = (b * b) % P;
|
|
17
|
+
e >>= 1n;
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
/** y^2 = x^3 + A*x^2 + x on Curve25519. */
|
|
22
|
+
const curveY2 = (x) => mod(((x + A) * x + 1n) * x);
|
|
23
|
+
export const isQuadraticResidue = (value) => value === 0n || modPow(value, (P - 1n) / 2n) === 1n;
|
|
24
|
+
/** x-coordinate of 2*point (Montgomery doubling). */
|
|
25
|
+
const doubleX = (x) => {
|
|
26
|
+
const numerator = mod(x * x - 1n);
|
|
27
|
+
const denominator = mod(4n * curveY2(x));
|
|
28
|
+
return mod(numerator * numerator * modPow(denominator, P - 2n));
|
|
29
|
+
};
|
|
30
|
+
export const bufferToBigInt = (buffer) => BigInt(`0x${buffer.toString('hex') || '0'}`);
|
|
31
|
+
/**
|
|
32
|
+
* Generate a public key that is a valid x25519 point, as Telegram clients do
|
|
33
|
+
* for the fake TLS key_share extension (a random 32-byte string would be an
|
|
34
|
+
* easy DPI fingerprint, since half of them are not on the curve).
|
|
35
|
+
*/
|
|
36
|
+
export const generateX25519PublicKey = () => {
|
|
37
|
+
for (;;) {
|
|
38
|
+
const candidate = randomBytes(32);
|
|
39
|
+
candidate[31] &= 0x7f;
|
|
40
|
+
let x = mod(bufferToBigInt(candidate));
|
|
41
|
+
if (!isQuadraticResidue(curveY2(x))) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
for (let i = 0; i < 3; i++) {
|
|
45
|
+
x = doubleX(x);
|
|
46
|
+
}
|
|
47
|
+
return Buffer.from(x.toString(16).padStart(64, '0'), 'hex');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export const isOnCurve = (key) => isQuadraticResidue(curveY2(mod(bufferToBigInt(key))));
|
|
51
|
+
//# sourceMappingURL=x25519.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x25519.js","sourceRoot":"","sources":["../../src/crypto/x25519.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,66 +1,109 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'module';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
|
-
import { program } from 'commander';
|
|
4
|
+
import { InvalidArgumentError, Option, program } from 'commander';
|
|
4
5
|
import { parseProxies } from './lib/parser.js';
|
|
5
|
-
import {
|
|
6
|
+
import { pingProxies } from './lib/ping.js';
|
|
7
|
+
import { DC_IDS, DEFAULT_DC } from './lib/datacenters.js';
|
|
8
|
+
import { waitForWebProxyCleanup } from './webproxy/check.js';
|
|
9
|
+
const { version } = createRequire(import.meta.url)('../package.json');
|
|
10
|
+
const integer = (name, min) => (value) => {
|
|
11
|
+
const number = Number(value);
|
|
12
|
+
if (!/^\d+$/.test(value.trim()) || !Number.isSafeInteger(number) || number < min) {
|
|
13
|
+
throw new InvalidArgumentError(`${name} must be an integer >= ${min}.`);
|
|
14
|
+
}
|
|
15
|
+
return number;
|
|
16
|
+
};
|
|
17
|
+
const parseDcs = (value) => {
|
|
18
|
+
if (value.trim().toLowerCase() === 'all') {
|
|
19
|
+
return [...DC_IDS];
|
|
20
|
+
}
|
|
21
|
+
const ids = value.split(',').map((part) => Number(part.trim()));
|
|
22
|
+
if (ids.length === 0 || ids.some((id) => !DC_IDS.includes(id))) {
|
|
23
|
+
throw new InvalidArgumentError(`DC must be "all" or a comma-separated list of ${DC_IDS.join(', ')}.`);
|
|
24
|
+
}
|
|
25
|
+
return [...new Set(ids)];
|
|
26
|
+
};
|
|
6
27
|
program
|
|
7
28
|
.name('tping')
|
|
8
29
|
.description('Ping for Telegram Proxies')
|
|
9
|
-
.version(
|
|
10
|
-
.argument('[
|
|
11
|
-
.option('-c, --count <n>', 'Number of pings to send (default: unlimited)',
|
|
12
|
-
.option('-i, --interval <ms>', 'Interval between pings in milliseconds (
|
|
13
|
-
.option('-t, --timeout <ms>', 'Timeout for each ping in milliseconds (
|
|
30
|
+
.version(version, '-v, --version')
|
|
31
|
+
.argument('[proxies...]', 'Proxy links, file paths, or "-" for stdin')
|
|
32
|
+
.option('-c, --count <n>', 'Number of pings to send (default: unlimited)', integer('count', 1))
|
|
33
|
+
.option('-i, --interval <ms>', 'Interval between pings in milliseconds', integer('interval', 0), 1000)
|
|
34
|
+
.option('-t, --timeout <ms>', 'Timeout for each ping (connect + handshake + reply) in milliseconds', integer('timeout', 100), 10000)
|
|
35
|
+
.addOption(new Option('-d, --dc <ids>', 'Telegram DC(s) to test through the proxy: 1-5, comma-separated, or "all"')
|
|
36
|
+
.argParser(parseDcs)
|
|
37
|
+
.default([DEFAULT_DC], String(DEFAULT_DC)))
|
|
38
|
+
.option('-6, --ipv6', 'Use IPv6 DC addresses for SOCKS5/HTTP proxies', false)
|
|
39
|
+
.option('-V, --verbose', 'Show the time spent in each phase (tcp, handshake, rtt)', false)
|
|
14
40
|
.showHelpAfterError('(add --help for additional information)')
|
|
15
41
|
.addHelpText('after', `
|
|
16
42
|
Supported proxy formats:
|
|
17
|
-
MTProto:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
SOCKS5:
|
|
43
|
+
MTProto: https://t.me/proxy?server=...&port=...&secret=... (also tg://proxy?...)
|
|
44
|
+
secret: 32 hex (simple), dd + 32 hex (secure), ee + 32 hex + domain hex (Fake TLS),
|
|
45
|
+
or the same bytes in base64 / base64url
|
|
46
|
+
SOCKS5: https://t.me/socks?server=...&port=...&user=...&pass=... (also tg://socks?...)
|
|
47
|
+
socks5://user:pass@host:port
|
|
48
|
+
HTTP: http://user:pass@host:port (Telegram Desktop only)
|
|
49
|
+
WEB: https://t.me/webproxy?server=host[/path]&secret=... (also tg://webproxy?...)
|
|
50
|
+
HTTPS on port 443 through a tproxy-server relay; secret: 32 hex or dd + 32 hex
|
|
51
|
+
|
|
52
|
+
A ping succeeds only when a Telegram datacenter answers through the proxy.
|
|
53
|
+
Exit status: 0 if every proxy answered at least once, 1 otherwise, 2 on bad input.
|
|
21
54
|
|
|
22
55
|
Examples:
|
|
23
|
-
$ tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=
|
|
56
|
+
$ tping "https://t.me/proxy?server=proxy.example.com&port=443&secret=0123456789abcdef0123456789abcdef"
|
|
24
57
|
$ tping -c 5 "https://t.me/socks?server=proxy.example.com&port=1080"
|
|
25
|
-
$ tping -c 10 -i 500
|
|
26
|
-
$
|
|
58
|
+
$ tping -c 10 -i 500 --dc all proxies.txt
|
|
59
|
+
$ cat proxies.txt | tping -c 1 -
|
|
27
60
|
`)
|
|
28
|
-
.action(async (
|
|
29
|
-
if (
|
|
61
|
+
.action(async (inputs, options) => {
|
|
62
|
+
if (inputs.length === 0) {
|
|
30
63
|
program.help();
|
|
31
64
|
}
|
|
32
|
-
|
|
33
|
-
const proxies =
|
|
65
|
+
const parsed = inputs.map(parseProxies);
|
|
66
|
+
const proxies = parsed.flatMap((p) => p.proxies);
|
|
67
|
+
const issues = parsed.flatMap((p) => p.issues);
|
|
68
|
+
for (const issue of issues) {
|
|
69
|
+
const shown = issue.input.length > 60 ? `${issue.input.slice(0, 57)}...` : issue.input;
|
|
70
|
+
console.error(chalk.yellow(`⚠️ Skipped "${shown}": ${issue.reason}`));
|
|
71
|
+
}
|
|
34
72
|
if (proxies.length === 0) {
|
|
35
|
-
console.
|
|
36
|
-
process.exit(
|
|
73
|
+
console.error(chalk.red('Error: no valid proxy found (pass a proxy link or a file path)'));
|
|
74
|
+
process.exit(2);
|
|
37
75
|
}
|
|
38
|
-
options.interval = options.interval || 1000;
|
|
39
|
-
options.timeout = options.timeout || 10000;
|
|
40
|
-
// Print header
|
|
41
76
|
console.log();
|
|
42
77
|
console.log(chalk.bold.cyan('🏓 TPing'));
|
|
43
78
|
console.log(chalk.gray('━'.repeat(50)));
|
|
44
|
-
console.log(chalk.cyan(`📡 Proxies:
|
|
45
|
-
console.log(chalk.cyan(`🎯 Count:
|
|
46
|
-
console.log(chalk.cyan(`⏰ Interval: ${chalk.white(options.interval
|
|
47
|
-
console.log(chalk.cyan(`⏳ Timeout:
|
|
79
|
+
console.log(chalk.cyan(`📡 Proxies: ${chalk.white(proxies.length)}`));
|
|
80
|
+
console.log(chalk.cyan(`🎯 Count: ${chalk.white(options.count ?? 'unlimited')}`));
|
|
81
|
+
console.log(chalk.cyan(`⏰ Interval: ${chalk.white(`${options.interval}ms`)}`));
|
|
82
|
+
console.log(chalk.cyan(`⏳ Timeout: ${chalk.white(`${options.timeout}ms`)}`));
|
|
83
|
+
console.log(chalk.cyan(`🌐 DC: ${chalk.white(options.dc.join(', '))}`));
|
|
48
84
|
console.log(chalk.gray('━'.repeat(50)));
|
|
49
85
|
console.log();
|
|
50
|
-
// Setup abort controller for Ctrl+C
|
|
51
86
|
const abortController = new AbortController();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return;
|
|
87
|
+
const handleSignal = () => {
|
|
88
|
+
if (abortController.signal.aborted) {
|
|
89
|
+
process.exit(130);
|
|
56
90
|
}
|
|
57
|
-
isAborting = true;
|
|
58
91
|
abortController.abort();
|
|
59
|
-
|
|
92
|
+
process.stderr.write(chalk.yellow('\n⏸️ Stopping... (press Ctrl+C again to force quit)\n'));
|
|
60
93
|
};
|
|
61
|
-
process.on('SIGINT',
|
|
62
|
-
process.on('SIGTERM',
|
|
63
|
-
await
|
|
64
|
-
|
|
65
|
-
|
|
94
|
+
process.on('SIGINT', handleSignal);
|
|
95
|
+
process.on('SIGTERM', handleSignal);
|
|
96
|
+
const results = await pingProxies(proxies, {
|
|
97
|
+
count: options.count,
|
|
98
|
+
interval: options.interval,
|
|
99
|
+
timeout: options.timeout,
|
|
100
|
+
dcs: options.dc,
|
|
101
|
+
ipv6: options.ipv6,
|
|
102
|
+
verbose: options.verbose,
|
|
103
|
+
}, abortController.signal);
|
|
104
|
+
const allAnswered = results.every((list) => list.some((r) => r.success));
|
|
105
|
+
await waitForWebProxyCleanup();
|
|
106
|
+
process.exit(allAnswered ? 0 : 1);
|
|
107
|
+
});
|
|
108
|
+
await program.parseAsync();
|
|
66
109
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Socket } from 'net';
|
|
2
|
+
import { SocketStream } from '../net/stream.js';
|
|
3
|
+
export declare class TimeoutError extends Error {
|
|
4
|
+
constructor();
|
|
5
|
+
}
|
|
6
|
+
export declare class AbortedError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
export declare const STAGE_CONNECTING = "connecting to proxy";
|
|
10
|
+
export declare const STAGE_WAITING = "waiting for Telegram";
|
|
11
|
+
/**
|
|
12
|
+
* One check: a deadline and an abort signal shared by every connection it opens,
|
|
13
|
+
* plus the current phase for error messages.
|
|
14
|
+
*/
|
|
15
|
+
export declare class Attempt {
|
|
16
|
+
private readonly signal?;
|
|
17
|
+
stage: string;
|
|
18
|
+
private readonly streams;
|
|
19
|
+
private primary?;
|
|
20
|
+
private failure?;
|
|
21
|
+
private readonly timer;
|
|
22
|
+
private readonly onAbort;
|
|
23
|
+
constructor(deadline: number, signal?: AbortSignal | undefined);
|
|
24
|
+
/** The default TCP stream, created on first use. */
|
|
25
|
+
get stream(): SocketStream;
|
|
26
|
+
newStream(socket?: Socket, readyEvent?: string): SocketStream;
|
|
27
|
+
close(): void;
|
|
28
|
+
private failAll;
|
|
29
|
+
}
|
|
30
|
+
export declare const describeError: (err: unknown, stage: string) => string;
|
|
31
|
+
//# sourceMappingURL=attempt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.d.ts","sourceRoot":"","sources":["../../src/lib/attempt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,YAAY,EAAqB,MAAM,kBAAkB,CAAC;AAEnE,qBAAa,YAAa,SAAQ,KAAK;;CAKtC;AAED,qBAAa,YAAa,SAAQ,KAAK;;CAKtC;AAED,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AACtD,eAAO,MAAM,aAAa,yBAAyB,CAAC;AAEpD;;;GAGG;AACH,qBAAa,OAAO;IAQY,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAPtD,KAAK,SAAoB;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgD;gBAE5D,QAAQ,EAAE,MAAM,EAAmB,MAAM,CAAC,EAAE,WAAW,YAAA;IAQnE,oDAAoD;IACpD,IAAI,MAAM,IAAI,YAAY,CAGzB;IAED,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY;IAS7D,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,OAAO;CAIhB;AAeD,eAAO,MAAM,aAAa,GAAI,KAAK,OAAO,EAAE,OAAO,MAAM,KAAG,MAuB3D,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { SocketStream, StreamClosedError } from '../net/stream.js';
|
|
2
|
+
export class TimeoutError extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super('Timeout');
|
|
5
|
+
this.name = 'TimeoutError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class AbortedError extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('Aborted');
|
|
11
|
+
this.name = 'AbortedError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export const STAGE_CONNECTING = 'connecting to proxy';
|
|
15
|
+
export const STAGE_WAITING = 'waiting for Telegram';
|
|
16
|
+
/**
|
|
17
|
+
* One check: a deadline and an abort signal shared by every connection it opens,
|
|
18
|
+
* plus the current phase for error messages.
|
|
19
|
+
*/
|
|
20
|
+
export class Attempt {
|
|
21
|
+
signal;
|
|
22
|
+
stage = STAGE_CONNECTING;
|
|
23
|
+
streams = new Set();
|
|
24
|
+
primary;
|
|
25
|
+
failure;
|
|
26
|
+
timer;
|
|
27
|
+
onAbort = () => this.failAll(new AbortedError());
|
|
28
|
+
constructor(deadline, signal) {
|
|
29
|
+
this.signal = signal;
|
|
30
|
+
this.timer = setTimeout(() => this.failAll(new TimeoutError()), Math.max(0, deadline - Date.now()));
|
|
31
|
+
if (signal?.aborted) {
|
|
32
|
+
this.onAbort();
|
|
33
|
+
}
|
|
34
|
+
signal?.addEventListener('abort', this.onAbort, { once: true });
|
|
35
|
+
}
|
|
36
|
+
/** The default TCP stream, created on first use. */
|
|
37
|
+
get stream() {
|
|
38
|
+
this.primary ??= this.newStream();
|
|
39
|
+
return this.primary;
|
|
40
|
+
}
|
|
41
|
+
newStream(socket, readyEvent) {
|
|
42
|
+
const stream = new SocketStream(socket, readyEvent);
|
|
43
|
+
if (this.failure) {
|
|
44
|
+
stream.fail(this.failure);
|
|
45
|
+
}
|
|
46
|
+
this.streams.add(stream);
|
|
47
|
+
return stream;
|
|
48
|
+
}
|
|
49
|
+
close() {
|
|
50
|
+
clearTimeout(this.timer);
|
|
51
|
+
this.signal?.removeEventListener('abort', this.onAbort);
|
|
52
|
+
this.streams.forEach((stream) => stream.destroy());
|
|
53
|
+
}
|
|
54
|
+
failAll(err) {
|
|
55
|
+
this.failure ??= err;
|
|
56
|
+
this.streams.forEach((stream) => stream.fail(err));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const NETWORK_ERRORS = {
|
|
60
|
+
ECONNREFUSED: 'Connection refused',
|
|
61
|
+
ENOTFOUND: 'DNS lookup failed (unknown host)',
|
|
62
|
+
EAI_AGAIN: 'DNS lookup failed (temporary)',
|
|
63
|
+
EHOSTUNREACH: 'Host unreachable',
|
|
64
|
+
ENETUNREACH: 'Network unreachable',
|
|
65
|
+
ETIMEDOUT: 'Connection timed out',
|
|
66
|
+
EPIPE: 'Connection broken',
|
|
67
|
+
};
|
|
68
|
+
const CERTIFICATE_ERROR = /CERT|SELF_SIGNED|ALTNAME|UNABLE_TO_(GET|VERIFY)/;
|
|
69
|
+
const TLS_HANDSHAKE_ERROR = /^ERR_(SSL|TLS)_/;
|
|
70
|
+
export const describeError = (err, stage) => {
|
|
71
|
+
if (err instanceof TimeoutError) {
|
|
72
|
+
return `Timeout while ${stage}`;
|
|
73
|
+
}
|
|
74
|
+
if (err instanceof StreamClosedError) {
|
|
75
|
+
return stage === STAGE_WAITING
|
|
76
|
+
? 'Proxy closed the connection before Telegram answered (wrong secret, or the proxy cannot reach Telegram)'
|
|
77
|
+
: `Connection closed while ${stage}`;
|
|
78
|
+
}
|
|
79
|
+
const code = err?.code;
|
|
80
|
+
if (code === 'ECONNRESET') {
|
|
81
|
+
return `Connection reset while ${stage}`;
|
|
82
|
+
}
|
|
83
|
+
if (code && NETWORK_ERRORS[code]) {
|
|
84
|
+
return NETWORK_ERRORS[code];
|
|
85
|
+
}
|
|
86
|
+
if (code && CERTIFICATE_ERROR.test(code)) {
|
|
87
|
+
return `TLS certificate error (${code})`;
|
|
88
|
+
}
|
|
89
|
+
if (code && TLS_HANDSHAKE_ERROR.test(code)) {
|
|
90
|
+
return `TLS handshake failed, not a normal HTTPS server (${code})`;
|
|
91
|
+
}
|
|
92
|
+
return err instanceof Error ? err.message : String(err);
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=attempt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt.js","sourceRoot":"","sources":["../../src/lib/attempt.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/checker.d.ts
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CheckResult, TelegramProxy } from '../types/index.js';
|
|
2
|
+
import { type DcAddress } from './datacenters.js';
|
|
3
|
+
export interface CheckOptions {
|
|
4
|
+
/** Deadline for one whole check (connect + handshake + round trip), per DC. */
|
|
5
|
+
readonly timeout: number;
|
|
6
|
+
readonly dcs?: readonly number[];
|
|
7
|
+
/** Use IPv6 DC addresses for SOCKS5/HTTP proxies. */
|
|
8
|
+
readonly ipv6?: boolean;
|
|
9
|
+
readonly signal?: AbortSignal;
|
|
10
|
+
/** Override DC addresses (tests). */
|
|
11
|
+
readonly resolveDc?: (dc: number, transport: 'tcp' | 'http') => DcAddress;
|
|
12
|
+
/** Override where a WEB proxy hostname connects, and trusted CA (tests). */
|
|
13
|
+
readonly webProxyEndpoint?: {
|
|
14
|
+
readonly host: string;
|
|
15
|
+
readonly port: number;
|
|
16
|
+
readonly ca?: Buffer;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
2
19
|
/**
|
|
3
|
-
*
|
|
20
|
+
* Check one proxy the way the official clients use it: open a real connection,
|
|
21
|
+
* do the proxy handshake, and get a resPQ back from each requested Telegram DC.
|
|
4
22
|
*/
|
|
5
|
-
export declare const checkProxy: (proxy: TelegramProxy,
|
|
23
|
+
export declare const checkProxy: (proxy: TelegramProxy, options: CheckOptions) => Promise<CheckResult>;
|
|
6
24
|
//# sourceMappingURL=checker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../../src/lib/checker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../../src/lib/checker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAKX,aAAa,EAEd,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,qDAAqD;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,MAAM,KAAK,SAAS,CAAC;IAC1E,4EAA4E;IAC5E,QAAQ,CAAC,gBAAgB,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpG;AAgID;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAU,OAAO,aAAa,EAAE,SAAS,YAAY,KAAG,OAAO,CAAC,WAAW,CA+BjG,CAAC"}
|