-
Notifications
You must be signed in to change notification settings - Fork 18
/
peer_connector.go
202 lines (158 loc) · 4.57 KB
/
peer_connector.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package meshboi
import (
"net"
"time"
"inet.af/netaddr"
log "github.com/sirupsen/logrus"
)
type PeerConnector struct {
store *PeerConnStore
listenerDialer VpnMeshListenerDialer
myOutsideAddr netaddr.IPPort
tun TunConn
}
// Simple comparison to see if this member should be the server or if the remote member should be
func (pc *PeerConnector) AmServer(other netaddr.IPPort) bool {
ipCompare := pc.myOutsideAddr.IP.Compare(other.IP)
switch ipCompare {
case -1:
return false
case 0:
if pc.myOutsideAddr.Port > other.Port {
return true
} else if pc.myOutsideAddr.Port < other.Port {
return false
} else {
panic("Remote IPPort == Local IPPort")
}
case 1:
return true
default:
panic("Unexpected comparison result")
}
}
func NewPeerConnector(listenerDialer VpnMeshListenerDialer, store *PeerConnStore, tun TunConn) PeerConnector {
return PeerConnector{
listenerDialer: listenerDialer,
store: store,
tun: tun,
}
}
func (pc *PeerConnector) OnNetworkMapUpdate(network NetworkMap) {
pc.myOutsideAddr = network.Addresses[network.YourIndex]
pc.newAddresses(network.Addresses)
}
func (pc *PeerConnector) readAllFromAddr(address net.Addr, timeout time.Duration) error {
conn, err := pc.listenerDialer.Dial(address)
if err != nil {
log.Warn("Error connecting to peer unencrypted ", err)
return err
}
defer conn.Close()
buf := make([]byte, 1024)
conn.SetReadDeadline(time.Now().Add(timeout))
n, err := conn.Read(buf)
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("Read timeout:", err)
} else {
log.Println("Read error:", err)
return err
}
} else {
log.Info("Read: ", string(buf[:n]))
}
return nil
}
func (pc *PeerConnector) connectToNewPeer(address netaddr.IPPort) error {
// We are going to initiate a dTLS connection to the other mesh member
// however, for it to open a hole in its firewall it has sent us an initial
// message if we have our own firewall then this message will likely not be
// received but if it does get through it can wait here to receive it before
// continuing
err := pc.readAllFromAddr(address.UDPAddr(), time.Second)
if err != nil {
return err
}
conn, err := pc.listenerDialer.DialMesh(address.UDPAddr())
if err != nil {
return err
}
return pc.OnNewPeerConnection(conn)
}
func (pc *PeerConnector) OnNewPeerConnection(conn MeshConn) error {
outsideAddr, err := netaddr.ParseIPPort(conn.RemoteAddr().String())
if err != nil {
conn.Close()
return err
}
log.Info("Succesfully accepted connection from ", conn.RemoteAddr())
peer := NewPeerConn(conn.RemoteMeshAddr(), outsideAddr, conn, pc.tun)
pc.store.Add(&peer)
go peer.readLoop()
go peer.sendLoop()
return nil
}
func (pc *PeerConnector) openFirewallToPeer(addr net.Addr) error {
conn, err := pc.listenerDialer.Dial(addr)
defer conn.Close()
if err != nil {
log.Warn("Error connecting to peer unencrypted ", err)
return err
}
// It doesn't really matter what is sent here - the important part is
// something is sent. We're effectively telling any and all (stateful)
// firewalls on our path to the peer to allow any future traffic that has
// originated from that peer
_, err = conn.Write([]byte("hello"))
if err != nil {
log.Warn("Error writing to peer unencrypted ", err)
return err
}
return nil
}
func (pc *PeerConnector) newAddresses(addreses []netaddr.IPPort) {
for _, address := range addreses {
_, ok := pc.store.GetByOutsideIpPort(address)
if ok {
// we already know of this peer
continue
}
if address == pc.myOutsideAddr {
// don't connect to myself
continue
}
if pc.AmServer(address) {
peer := NewPeerConn(netaddr.IP{}, address, nil, pc.tun)
pc.store.Add(&peer)
// As the peer will initiate connection to our dTLS server we first
// need to make sure our firewall(s) are open to allow the peer to
// contact us
err := pc.openFirewallToPeer(address.UDPAddr())
if err != nil {
log.Warn("Error opening firewall: ", err)
// Remove the peer so we can try again later
pc.store.RemoveByOutsideIPPort(address)
}
} else {
log.Info("Going to try to connect to ", address)
if err := pc.connectToNewPeer(address); err != nil {
log.Warn("Could not connect to ", address, err)
continue
}
}
}
}
func (pc *PeerConnector) ListenForPeers() {
for {
conn, err := pc.listenerDialer.AcceptMesh()
if err != nil {
log.Warn("Error accepting: ", err)
continue
}
pc.OnNewPeerConnection(conn)
}
}
func (pc *PeerConnector) Stop() {
// todo: Properly shut down all the spawned peers
}