summaryrefslogtreecommitdiffstats
path: root/cmd/stdisco/main.go
blob: d0e52d0311c7e6d1e7c4769be60705f5ef6cec85 (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
111
112
113
114
115
116
117
// Copyright (C) 2016 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/rand"
	"encoding/binary"
	"flag"
	"log"
	"strings"
	"time"

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

var (
	all  = false // print all packets, not just first from each device/source
	fake = false // send fake packets to lure out other devices faster
	mc   = "[ff12::8384]:21027"
	bc   = 21027
)

var (
	// Static prefix that we use when generating fake device IDs, so that we
	// can recognize them ourselves. Also makes the device ID start with
	// "STPROBE-" which is humanly recognizable.
	randomPrefix = []byte{148, 223, 23, 4, 148}

	// Our random, fake, device ID that we use when sending announcements.
	myID = randomDeviceID()
)

func main() {
	flag.BoolVar(&all, "all", all, "Print all received announcements (not only first)")
	flag.BoolVar(&fake, "fake", fake, "Send fake announcements")
	flag.StringVar(&mc, "mc", mc, "IPv6 multicast address")
	flag.IntVar(&bc, "bc", bc, "IPv4 broadcast port number")
	flag.Parse()

	if fake {
		log.Println("My ID:", myID)
	}

	ctx := context.Background()

	runbeacon(ctx, beacon.NewMulticast(mc), fake)
	runbeacon(ctx, beacon.NewBroadcast(bc), fake)

	select {}
}

func runbeacon(ctx context.Context, bc beacon.Interface, fake bool) {
	go bc.Serve(ctx)
	go recv(bc)
	if fake {
		go send(bc)
	}
}

// receives and prints discovery announcements
func recv(bc beacon.Interface) {
	seen := make(map[string]bool)
	for {
		data, src := bc.Recv()
		if m := binary.BigEndian.Uint32(data); m != discover.Magic {
			log.Printf("Incorrect magic %x in announcement from %v", m, src)
			continue
		}

		var ann discover.Announce
		ann.Unmarshal(data[4:])

		if ann.ID == myID {
			// This is one of our own fake packets, don't print it.
			continue
		}

		// Print announcement details for the first packet from a given
		// device ID and source address, or if -all was given.
		key := ann.ID.String() + src.String()
		if all || !seen[key] {
			log.Printf("Announcement from %v\n", src)
			log.Printf(" %v at %s\n", ann.ID, strings.Join(ann.Addresses, ", "))
			seen[key] = true
		}
	}
}

// sends fake discovery announcements once every second
func send(bc beacon.Interface) {
	ann := discover.Announce{
		ID:        myID,
		Addresses: []string{"tcp://fake.example.com:12345"},
	}
	bs, _ := ann.Marshal()

	for {
		bc.Send(bs)
		time.Sleep(time.Second)
	}
}

// returns a random but recognizable device ID
func randomDeviceID() protocol.DeviceID {
	var id protocol.DeviceID
	copy(id[:], randomPrefix)
	rand.Read(id[len(randomPrefix):])
	return id
}