summaryrefslogtreecommitdiffstats
path: root/cmd/stfinddevice/main.go
blob: 48f7f862e79c9935030f13981d63ea7b3a51dba8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package main

import (
	"context"
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"time"

	"github.com/syncthing/syncthing/lib/config"
	"github.com/syncthing/syncthing/lib/discover"
	"github.com/syncthing/syncthing/lib/events"
	"github.com/syncthing/syncthing/lib/protocol"
	_ "go.uber.org/automaxprocs"
)

var timeout = 5 * time.Second

func main() {
	var server string

	flag.StringVar(&server, "server", "", "Announce server (blank for default set)")
	flag.DurationVar(&timeout, "timeout", timeout, "Query timeout")
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() != 1 {
		flag.Usage()
		os.Exit(64)
	}

	id, err := protocol.DeviceIDFromString(flag.Args()[0])
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	if server != "" {
		checkServers(id, server)
	} else {
		checkServers(id, config.DefaultDiscoveryServers...)
	}
}

type checkResult struct {
	server    string
	addresses []string
	error
}

func checkServers(deviceID protocol.DeviceID, servers ...string) {
	t0 := time.Now()
	resc := make(chan checkResult)
	for _, srv := range servers {
		srv := srv
		go func() {
			res := checkServer(deviceID, srv)
			res.server = srv
			resc <- res
		}()
	}

	for range servers {
		res := <-resc

		u, _ := url.Parse(res.server)
		fmt.Printf("%s (%v):\n", u.Host, time.Since(t0))

		if res.error != nil {
			fmt.Println("  " + res.error.Error())
		}
		for _, addr := range res.addresses {
			fmt.Println("  address:", addr)
		}
	}
}

func checkServer(deviceID protocol.DeviceID, server string) checkResult {
	disco, err := discover.NewGlobal(server, tls.Certificate{}, nil, events.NoopLogger, nil)
	if err != nil {
		return checkResult{error: err}
	}

	res := make(chan checkResult, 1)

	time.AfterFunc(timeout, func() {
		res <- checkResult{error: errors.New("timeout")}
	})

	go func() {
		addresses, err := disco.Lookup(context.Background(), deviceID)
		res <- checkResult{addresses: addresses, error: err}
	}()

	return <-res
}

func usage() {
	fmt.Printf("Usage:\n\t%s [options] <device ID>\n\nOptions:\n", os.Args[0])
	flag.PrintDefaults()
}