Просмотр исходного кода

doctor: deepen DC verification with MTProto handshake probe

Closes #494.

After a successful TCP connect, run an unauthenticated req_pq_multi ->
resPQ exchange via mtglib/dcprobe. This rejects generic listeners that
happen to bind 443 but cannot speak MTProto.

Output now shows "(rpc <rtt>)" on success; on failure the wrapped error
distinguishes "tcp connect to ...: ..." from "rpc handshake to ...: ...".
The probe runs by default — an opt-in flag would defeat the purpose,
since the existing TCP-only check is what motivated the issue.
pull/496/head
Alexey Dolotov 2 недель назад
Родитель
Сommit
eaff7007fd
1 измененных файлов: 26 добавлений и 16 удалений
  1. 26
    16
      internal/cli/doctor.go

+ 26
- 16
internal/cli/doctor.go Просмотреть файл

@@ -18,6 +18,7 @@ import (
18 18
 	"github.com/9seconds/mtg/v2/internal/config"
19 19
 	"github.com/9seconds/mtg/v2/internal/utils"
20 20
 	"github.com/9seconds/mtg/v2/mtglib"
21
+	"github.com/9seconds/mtg/v2/mtglib/dcprobe"
21 22
 	"github.com/9seconds/mtg/v2/network/v2"
22 23
 	"github.com/beevik/ntp"
23 24
 )
@@ -46,7 +47,7 @@ var (
46 47
 	)
47 48
 
48 49
 	tplODCConnect = template.Must(
49
-		template.New("").Parse("  ✅ DC {{ .dc }}\n"),
50
+		template.New("").Parse("  ✅ DC {{ .dc }} (rpc {{ .rtt }})\n"),
50 51
 	)
51 52
 	tplEDCConnect = template.Must(
52 53
 		template.New("").Parse("  ❌ DC {{ .dc }}: {{ .error }}\n"),
@@ -238,17 +239,21 @@ func (d *Doctor) checkNetwork(ntw mtglib.Network) bool {
238 239
 	dcs := slices.Collect(maps.Keys(essentials.TelegramCoreAddresses))
239 240
 	slices.Sort(dcs)
240 241
 
241
-	errs := make([]error, len(dcs))
242
+	type dcResult struct {
243
+		rtt time.Duration
244
+		err error
245
+	}
246
+	results := make([]dcResult, len(dcs))
242 247
 
243 248
 	var wg sync.WaitGroup
244 249
 	for i, dc := range dcs {
245 250
 		wg.Go(func() {
246 251
 			defer func() {
247 252
 				if r := recover(); r != nil {
248
-					errs[i] = fmt.Errorf("panic: %v", r)
253
+					results[i].err = fmt.Errorf("panic: %v", r)
249 254
 				}
250 255
 			}()
251
-			errs[i] = d.checkNetworkAddresses(ntw, essentials.TelegramCoreAddresses[dc])
256
+			results[i].rtt, results[i].err = d.checkNetworkAddresses(ntw, dc, essentials.TelegramCoreAddresses[dc])
252 257
 		})
253 258
 	}
254 259
 	wg.Wait()
@@ -256,14 +261,15 @@ func (d *Doctor) checkNetwork(ntw mtglib.Network) bool {
256 261
 	ok := true
257 262
 
258 263
 	for i, dc := range dcs {
259
-		if errs[i] == nil {
264
+		if results[i].err == nil {
260 265
 			tplODCConnect.Execute(os.Stdout, map[string]any{ //nolint: errcheck
261
-				"dc": dc,
266
+				"dc":  dc,
267
+				"rtt": results[i].rtt.Round(time.Microsecond),
262 268
 			})
263 269
 		} else {
264 270
 			tplEDCConnect.Execute(os.Stdout, map[string]any{ //nolint: errcheck
265 271
 				"dc":    dc,
266
-				"error": errs[i],
272
+				"error": results[i].err,
267 273
 			})
268 274
 			ok = false
269 275
 		}
@@ -272,7 +278,7 @@ func (d *Doctor) checkNetwork(ntw mtglib.Network) bool {
272 278
 	return ok
273 279
 }
274 280
 
275
-func (d *Doctor) checkNetworkAddresses(ntw mtglib.Network, addresses []string) error {
281
+func (d *Doctor) checkNetworkAddresses(ntw mtglib.Network, dc int, addresses []string) (time.Duration, error) {
276 282
 	checkAddresses := []string{}
277 283
 
278 284
 	switch d.conf.PreferIP.Get("prefer-ip4") {
@@ -303,29 +309,33 @@ func (d *Doctor) checkNetworkAddresses(ntw mtglib.Network, addresses []string) e
303 309
 	}
304 310
 
305 311
 	if len(checkAddresses) == 0 {
306
-		return fmt.Errorf("no suitable addresses after IP version filtering")
312
+		return 0, fmt.Errorf("no suitable addresses after IP version filtering")
307 313
 	}
308 314
 
309 315
 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
310 316
 	defer cancel()
311 317
 
312
-	var (
313
-		conn net.Conn
314
-		err  error
315
-	)
318
+	var lastErr error
316 319
 
317 320
 	for _, addr := range checkAddresses {
318
-		conn, err = ntw.DialContext(ctx, "tcp", addr)
321
+		conn, err := ntw.DialContext(ctx, "tcp", addr)
319 322
 		if err != nil {
323
+			lastErr = fmt.Errorf("tcp connect to %s: %w", addr, err)
320 324
 			continue
321 325
 		}
322 326
 
327
+		rtt, err := dcprobe.Probe(ctx, conn, dc)
323 328
 		conn.Close() //nolint: errcheck
324 329
 
325
-		return nil
330
+		if err != nil {
331
+			lastErr = fmt.Errorf("rpc handshake to %s: %w", addr, err)
332
+			continue
333
+		}
334
+
335
+		return rtt, nil
326 336
 	}
327 337
 
328
-	return err
338
+	return 0, lastErr
329 339
 }
330 340
 
331 341
 func (d *Doctor) checkFrontingDomain(ntw mtglib.Network) bool {

Загрузка…
Отмена
Сохранить