|
@@ -231,7 +231,7 @@ func (s *service) handle(stop chan struct{}) {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|
|
- c.SetDeadline(time.Now().Add(20 * time.Second))
|
|
|
|
|
|
+ _ = c.SetDeadline(time.Now().Add(20 * time.Second))
|
|
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
|
|
hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID))
|
|
if err != nil {
|
|
if err != nil {
|
|
if protocol.IsVersionMismatch(err) {
|
|
if protocol.IsVersionMismatch(err) {
|
|
@@ -255,7 +255,7 @@ func (s *service) handle(stop chan struct{}) {
|
|
c.Close()
|
|
c.Close()
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
- c.SetDeadline(time.Time{})
|
|
|
|
|
|
+ _ = c.SetDeadline(time.Time{})
|
|
|
|
|
|
// The Model will return an error for devices that we don't want to
|
|
// The Model will return an error for devices that we don't want to
|
|
// have a connection with for whatever reason, for example unknown devices.
|
|
// have a connection with for whatever reason, for example unknown devices.
|
|
@@ -850,8 +850,15 @@ func (s *service) dialParallel(deviceID protocol.DeviceID, dialTargets []dialTar
|
|
wg.Add(1)
|
|
wg.Add(1)
|
|
go func(tgt dialTarget) {
|
|
go func(tgt dialTarget) {
|
|
conn, err := tgt.Dial()
|
|
conn, err := tgt.Dial()
|
|
- s.setConnectionStatus(tgt.addr, err)
|
|
|
|
if err == nil {
|
|
if err == nil {
|
|
|
|
+ // Closes the connection on error
|
|
|
|
+ err = s.validateIdentity(conn, deviceID)
|
|
|
|
+ }
|
|
|
|
+ s.setConnectionStatus(tgt.addr, err)
|
|
|
|
+ if err != nil {
|
|
|
|
+ l.Debugln("dialing", deviceID, tgt.uri, "error:", err)
|
|
|
|
+ } else {
|
|
|
|
+ l.Debugln("dialing", deviceID, tgt.uri, "success:", conn)
|
|
res <- conn
|
|
res <- conn
|
|
}
|
|
}
|
|
wg.Done()
|
|
wg.Done()
|
|
@@ -884,3 +891,36 @@ func (s *service) dialParallel(deviceID protocol.DeviceID, dialTargets []dialTar
|
|
}
|
|
}
|
|
return internalConn{}, false
|
|
return internalConn{}, false
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+func (s *service) validateIdentity(c internalConn, expectedID protocol.DeviceID) error {
|
|
|
|
+ cs := c.ConnectionState()
|
|
|
|
+
|
|
|
|
+ // We should have received exactly one certificate from the other
|
|
|
|
+ // side. If we didn't, they don't have a device ID and we drop the
|
|
|
|
+ // connection.
|
|
|
|
+ certs := cs.PeerCertificates
|
|
|
|
+ if cl := len(certs); cl != 1 {
|
|
|
|
+ l.Infof("Got peer certificate list of length %d != 1 from peer at %s; protocol error", cl, c)
|
|
|
|
+ c.Close()
|
|
|
|
+ return fmt.Errorf("expected 1 certificate, got %d", cl)
|
|
|
|
+ }
|
|
|
|
+ remoteCert := certs[0]
|
|
|
|
+ remoteID := protocol.NewDeviceID(remoteCert.Raw)
|
|
|
|
+
|
|
|
|
+ // The device ID should not be that of ourselves. It can happen
|
|
|
|
+ // though, especially in the presence of NAT hairpinning, multiple
|
|
|
|
+ // clients between the same NAT gateway, and global discovery.
|
|
|
|
+ if remoteID == s.myID {
|
|
|
|
+ l.Infof("Connected to myself (%s) at %s - should not happen", remoteID, c)
|
|
|
|
+ c.Close()
|
|
|
|
+ return fmt.Errorf("connected to self")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // We should see the expected device ID
|
|
|
|
+ if !remoteID.Equals(expectedID) {
|
|
|
|
+ c.Close()
|
|
|
|
+ return fmt.Errorf("unexpected device id, expected %s got %s", expectedID, remoteID)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|