Bladeren bron

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 19 uur geleden
bovenliggende
commit
408e2c7150
1 gewijzigde bestanden met toevoegingen van 26 en 16 verwijderingen
  1. 26
    16
      internal/cli/doctor.go

+ 26
- 16
internal/cli/doctor.go Bestand weergeven

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

Laden…
Annuleren
Opslaan