浏览代码

Add validation of telegram connectivity

tags/v2.2.4^2
9seconds 1 个月前
父节点
当前提交
8154f65e0e
共有 5 个文件被更改,包括 255 次插入52 次删除
  1. 30
    0
      essentials/addresses.go
  2. 1
    1
      go.mod
  3. 188
    8
      internal/cli/doctor.go
  4. 二进制
      mtg-linux
  5. 36
    43
      mtglib/internal/dc/init.go

+ 30
- 0
essentials/addresses.go 查看文件

@@ -0,0 +1,30 @@
1
+package essentials
2
+
3
+// TelegramCoreAddresses are publicly known addresses of Telegram core network.
4
+var TelegramCoreAddresses = map[int][]string{
5
+	1: {
6
+		"149.154.175.50:443",
7
+		"[2001:b28:f23d:f001::a]:443",
8
+	},
9
+	2: {
10
+		"149.154.167.51:443",
11
+		"95.161.76.100:443",
12
+		"[2001:67c:04e8:f002::a]:443",
13
+	},
14
+	3: {
15
+		"149.154.175.100:443",
16
+		"[2001:b28:f23d:f003::a]:443",
17
+	},
18
+	4: {
19
+		"149.154.167.91:443",
20
+		"[2001:67c:04e8:f004::a]:443",
21
+	},
22
+	5: {
23
+		"149.154.171.5:443",
24
+		"[2001:b28:f23f:f005::a]:443",
25
+	},
26
+	203: {
27
+		"91.105.192.100:443",
28
+		"[2a0a:f280:0203:000a:5000:0000:0000:0100]:443",
29
+	},
30
+}

+ 1
- 1
go.mod 查看文件

@@ -27,6 +27,7 @@ require (
27 27
 )
28 28
 
29 29
 require (
30
+	github.com/beevik/ntp v1.5.0
30 31
 	github.com/ncruces/go-dns v1.3.2
31 32
 	github.com/pelletier/go-toml/v2 v2.2.4
32 33
 	github.com/pires/go-proxyproto v0.11.0
@@ -36,7 +37,6 @@ require (
36 37
 )
37 38
 
38 39
 require (
39
-	github.com/beevik/ntp v1.5.0 // indirect
40 40
 	github.com/beorn7/perks v1.0.1 // indirect
41 41
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
42 42
 	github.com/davecgh/go-spew v1.1.1 // indirect

+ 188
- 8
internal/cli/doctor.go 查看文件

@@ -1,13 +1,21 @@
1 1
 package cli
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"fmt"
6
+	"maps"
7
+	"net"
5 8
 	"os"
9
+	"slices"
10
+	"strings"
6 11
 	"text/template"
12
+	"time"
7 13
 
14
+	"github.com/9seconds/mtg/v2/essentials"
8 15
 	"github.com/9seconds/mtg/v2/internal/config"
9 16
 	"github.com/9seconds/mtg/v2/internal/utils"
10 17
 	"github.com/9seconds/mtg/v2/mtglib"
18
+	"github.com/9seconds/mtg/v2/network/v2"
11 19
 	"github.com/beevik/ntp"
12 20
 )
13 21
 
@@ -33,6 +41,20 @@ var (
33 41
 		template.New("").
34 42
 			Parse("  ❌ Time drift is {{ .drift }}, but tolerate-time-skewness is {{ .value }}. You will get many rejected connections!\n"),
35 43
 	)
44
+
45
+	tplODCConnect = template.Must(
46
+		template.New("").Parse("  ✅ DC {{ .dc }}\n"),
47
+	)
48
+	tplEDCConnect = template.Must(
49
+		template.New("").Parse("  ❌ DC {{ .dc }}: {{ .error }}\n"),
50
+	)
51
+
52
+	tplODNSSNIMatch = template.Must(
53
+		template.New("").Parse("  ✅ IP address {{ .ip }} matches secret hostname {{ .hostname }}\n"),
54
+	)
55
+	tplEDNSSNIMatch = template.Must(
56
+		template.New("").Parse("  ❌ Hostname {{ .hostname }} {{ if .resolved }}is resolved to {{ .resolved }} addresses, not {{ if .ip4 }}{{ .ip4 }}{{ else }}{{ .ip6 }}{{ end }}{{ else }}cannot be resolved to any host{{ end }}\n"),
57
+	)
36 58
 )
37 59
 
38 60
 type Doctor struct {
@@ -41,6 +63,28 @@ type Doctor struct {
41 63
 	ConfigPath string `kong:"arg,required,type='existingfile',help='Path to the configuration file.',name='config-path'"` //nolint: lll
42 64
 }
43 65
 
66
+type wrappedNetwork struct {
67
+	mtglib.Network
68
+}
69
+
70
+func (w wrappedNetwork) Dial(network, address string) (net.Conn, error) {
71
+	rv, err := w.Network.Dial(network, address)
72
+	if err != nil {
73
+		return nil, err
74
+	}
75
+
76
+	return rv.(net.Conn), nil
77
+}
78
+
79
+func (w wrappedNetwork) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
80
+	rv, err := w.Network.DialContext(ctx, network, address)
81
+	if err != nil {
82
+		return nil, err
83
+	}
84
+
85
+	return rv.(net.Conn), nil
86
+}
87
+
44 88
 func (d *Doctor) Run(cli *CLI, version string) error {
45 89
 	conf, err := utils.ReadConfig(d.ConfigPath)
46 90
 	if err != nil {
@@ -48,20 +92,42 @@ func (d *Doctor) Run(cli *CLI, version string) error {
48 92
 	}
49 93
 
50 94
 	d.conf = conf
51
-	everythingOK := true
52 95
 
53 96
 	fmt.Println("Deprecated options")
54
-	if !d.checkDeprecatedConfig() {
55
-		everythingOK = false
56
-	} else {
57
-		fmt.Println("  ✅ All good")
58
-	}
97
+	everythingOK := d.checkDeprecatedConfig()
59 98
 
60 99
 	fmt.Println("Time skewness")
61
-	if !d.checkTimeSkewness() {
62
-		everythingOK = false
100
+	everythingOK = d.checkTimeSkewness() && everythingOK
101
+
102
+	resolver, err := network.GetDNS(conf.GetDNS())
103
+	if err != nil {
104
+		return fmt.Errorf("cannot create DNS resolver: %w", err)
63 105
 	}
64 106
 
107
+	base := network.New(
108
+		resolver,
109
+		"",
110
+		conf.Network.Timeout.TCP.Get(10*time.Second),
111
+		conf.Network.Timeout.HTTP.Get(0),
112
+		conf.Network.Timeout.Idle.Get(0),
113
+	)
114
+
115
+	fmt.Println("Validate native network connectivity")
116
+	everythingOK = d.checkNetwork(base) && everythingOK
117
+
118
+	for _, url := range conf.Network.Proxies {
119
+		value, err := network.NewProxyNetwork(base, url.Get(nil))
120
+		if err != nil {
121
+			return err
122
+		}
123
+
124
+		fmt.Printf("Validate network connectivity with proxy %s\n", url.Get(nil))
125
+		everythingOK = d.checkNetwork(value) && everythingOK
126
+	}
127
+
128
+	fmt.Println("Validate SNI-DNS match")
129
+	everythingOK = d.checkSecretHost(resolver, base) && everythingOK
130
+
65 131
 	if !everythingOK {
66 132
 		os.Exit(1)
67 133
 	}
@@ -116,6 +182,10 @@ func (d *Doctor) checkDeprecatedConfig() bool {
116 182
 		})
117 183
 	}
118 184
 
185
+	if ok {
186
+		fmt.Println("  ✅ All good")
187
+	}
188
+
119 189
 	return ok
120 190
 }
121 191
 
@@ -149,3 +219,113 @@ func (d *Doctor) checkTimeSkewness() bool {
149 219
 
150 220
 	return false
151 221
 }
222
+
223
+func (d *Doctor) checkNetwork(ntw mtglib.Network) bool {
224
+	dcs := slices.Collect(maps.Keys(essentials.TelegramCoreAddresses))
225
+	slices.Sort(dcs)
226
+
227
+	ok := true
228
+
229
+	for _, dc := range dcs {
230
+		err := d.checkNetworkAddresses(ntw, essentials.TelegramCoreAddresses[dc])
231
+		if err == nil {
232
+			tplODCConnect.Execute(os.Stdout, map[string]any{
233
+				"dc": dc,
234
+			})
235
+		} else {
236
+			tplEDCConnect.Execute(os.Stdout, map[string]any{
237
+				"dc":    dc,
238
+				"error": err,
239
+			})
240
+			ok = false
241
+		}
242
+	}
243
+
244
+	return ok
245
+}
246
+
247
+func (d *Doctor) checkNetworkAddresses(ntw mtglib.Network, addresses []string) error {
248
+	checkAddresses := []string{}
249
+
250
+	switch d.conf.PreferIP.Get("prefer-ip4") {
251
+	case "only-ipv4":
252
+		for _, addr := range addresses {
253
+			host, _, err := net.SplitHostPort(addr)
254
+			if err != nil {
255
+				panic(err)
256
+			}
257
+
258
+			if ip := net.ParseIP(host); ip != nil && ip.To4() != nil {
259
+				checkAddresses = append(checkAddresses, addr)
260
+			}
261
+		}
262
+	case "only-ipv6":
263
+		for _, addr := range addresses {
264
+			host, _, err := net.SplitHostPort(addr)
265
+			if err != nil {
266
+				panic(err)
267
+			}
268
+
269
+			if ip := net.ParseIP(host); ip != nil && ip.To4() == nil {
270
+				checkAddresses = append(checkAddresses, addr)
271
+			}
272
+		}
273
+	default:
274
+		checkAddresses = addresses
275
+	}
276
+
277
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
278
+	defer cancel()
279
+
280
+	var (
281
+		conn net.Conn
282
+		err  error
283
+	)
284
+
285
+	for _, addr := range checkAddresses {
286
+		conn, err = ntw.DialContext(ctx, "tcp", addr)
287
+		if err != nil {
288
+			continue
289
+		}
290
+
291
+		conn.Close()
292
+
293
+		return nil
294
+	}
295
+
296
+	return err
297
+}
298
+
299
+func (d *Doctor) checkSecretHost(resolver *net.Resolver, ntw mtglib.Network) bool {
300
+	addresses, err := resolver.LookupIPAddr(context.Background(), d.conf.Secret.Host)
301
+	if err != nil {
302
+		// TODO
303
+		return false
304
+	}
305
+
306
+	access := &Access{}
307
+	ourIP4 := access.getIP(ntw, "tcp4")
308
+	ourIP6 := access.getIP(ntw, "tcp6")
309
+
310
+	strAddresses := []string{}
311
+	for _, value := range addresses {
312
+		if value.IP.String() == ourIP4.String() || value.IP.String() == ourIP6.String() {
313
+			tplODNSSNIMatch.Execute(os.Stdout, map[string]any{
314
+				"ip":       value.IP,
315
+				"hostname": d.conf.Secret.Host,
316
+			})
317
+			return true
318
+		}
319
+
320
+		strAddresses = append(strAddresses, `"`+value.IP.String()+`"`)
321
+	}
322
+
323
+	tplEDNSSNIMatch.Execute(os.Stdout, map[string]any{
324
+		"hostname": d.conf.Secret.Host,
325
+		"resolved": strings.Join(strAddresses, ", "),
326
+		"ip4":      ourIP4,
327
+		"ip6":      ourIP6,
328
+	})
329
+
330
+	return false
331
+}

二进制
mtg-linux 查看文件


+ 36
- 43
mtglib/internal/dc/init.go 查看文件

@@ -2,7 +2,10 @@ package dc
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"net"
5 6
 	"time"
7
+
8
+	"github.com/9seconds/mtg/v2/essentials"
6 9
 )
7 10
 
8 11
 type preferIP uint8
@@ -39,46 +42,36 @@ type Updater interface {
39 42
 }
40 43
 
41 44
 // https://github.com/telegramdesktop/tdesktop/blob/master/Telegram/SourceFiles/mtproto/mtproto_dc_options.cpp#L30
42
-var defaultDCAddrSet = dcAddrSet{
43
-	v4: map[int][]Addr{
44
-		1: {
45
-			{Network: "tcp4", Address: "149.154.175.50:443"},
46
-		},
47
-		2: {
48
-			{Network: "tcp4", Address: "149.154.167.51:443"},
49
-			{Network: "tcp4", Address: "95.161.76.100:443"},
50
-		},
51
-		3: {
52
-			{Network: "tcp4", Address: "149.154.175.100:443"},
53
-		},
54
-		4: {
55
-			{Network: "tcp4", Address: "149.154.167.91:443"},
56
-		},
57
-		5: {
58
-			{Network: "tcp4", Address: "149.154.171.5:443"},
59
-		},
60
-		203: {
61
-			{Network: "tcp4", Address: "91.105.192.100:443"},
62
-		},
63
-	},
64
-	v6: map[int][]Addr{
65
-		1: {
66
-			{Network: "tcp6", Address: "[2001:b28:f23d:f001::a]:443"},
67
-		},
68
-		2: {
69
-			{Network: "tcp6", Address: "[2001:67c:04e8:f002::a]:443"},
70
-		},
71
-		3: {
72
-			{Network: "tcp6", Address: "[2001:b28:f23d:f003::a]:443"},
73
-		},
74
-		4: {
75
-			{Network: "tcp6", Address: "[2001:67c:04e8:f004::a]:443"},
76
-		},
77
-		5: {
78
-			{Network: "tcp6", Address: "[2001:b28:f23f:f005::a]:443"},
79
-		},
80
-		203: {
81
-			{Network: "tcp6", Address: "[2a0a:f280:0203:000a:5000:0000:0000:0100]:443"},
82
-		},
83
-	},
84
-}
45
+var defaultDCAddrSet = (func() dcAddrSet {
46
+	addrSet := dcAddrSet{
47
+		v4: make(map[int][]Addr),
48
+		v6: make(map[int][]Addr),
49
+	}
50
+
51
+	for dcid, ips := range essentials.TelegramCoreAddresses {
52
+		for _, addr := range ips {
53
+			host, _, err := net.SplitHostPort(addr)
54
+			if err != nil {
55
+				panic(err)
56
+			}
57
+
58
+			ip := net.ParseIP(host)
59
+			if ip == nil {
60
+				panic(addr)
61
+			}
62
+			if ip.To4() == nil {
63
+				addrSet.v6[dcid] = append(addrSet.v6[dcid], Addr{
64
+					Network: "tcp6",
65
+					Address: addr,
66
+				})
67
+			} else {
68
+				addrSet.v4[dcid] = append(addrSet.v4[dcid], Addr{
69
+					Network: "tcp4",
70
+					Address: addr,
71
+				})
72
+			}
73
+		}
74
+	}
75
+
76
+	return addrSet
77
+})()

正在加载...
取消
保存