func (s *Service) handle() { next: for c := range s.conns { cs := c.ConnectionState() // We should have negotiated the next level protocol "bep/1.0" as part // of the TLS handshake. Unfortunately this can't be a hard error, // because there are implementations out there that don't support // protocol negotiation (iOS for one...). if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != s.bepProtocolName { l.Infof("Peer %s did not negotiate bep/1.0", c.RemoteAddr()) } // 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 %s; protocol error", cl, c.RemoteAddr()) c.Close() continue } 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) - should not happen", remoteID) c.Close() continue } c.SetDeadline(time.Now().Add(20 * time.Second)) hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID)) if err != nil { if protocol.IsVersionMismatch(err) { // The error will be a relatively user friendly description // of what's wrong with the version compatibility. By // default identify the other side by device ID and IP. remote := fmt.Sprintf("%v (%v)", remoteID, c.RemoteAddr()) if hello.DeviceName != "" { // If the name was set in the hello return, use that to // give the user more info about which device is the // affected one. It probably says more than the remote // IP. remote = fmt.Sprintf("%q (%s %s, %v)", hello.DeviceName, hello.ClientName, hello.ClientVersion, remoteID) } msg := fmt.Sprintf("Connecting to %s: %s", remote, err) warningFor(remoteID, msg) } else { // It's something else - connection reset or whatever l.Infof("Failed to exchange Hello messages with %s (%s): %s", remoteID, c.RemoteAddr(), err) } c.Close() continue } c.SetDeadline(time.Time{}) // 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. if err := s.model.OnHello(remoteID, c.RemoteAddr(), hello); err != nil { l.Infof("Connection from %s at %s (%s) rejected: %v", remoteID, c.RemoteAddr(), c.Type(), err) c.Close() continue } // If we have a relay connection, and the new incoming connection is // not a relay connection, we should drop that, and prefer the this one. connected := s.model.ConnectedTo(remoteID) s.curConMut.Lock() ct, ok := s.currentConnection[remoteID] s.curConMut.Unlock() priorityKnown := ok && connected // Lower priority is better, just like nice etc. if priorityKnown && ct.internalConn.priority > c.priority { l.Debugln("Switching connections", remoteID) } else if connected { // We should not already be connected to the other party. TODO: This // could use some better handling. If the old connection is dead but // hasn't timed out yet we may want to drop *that* connection and keep // this one. But in case we are two devices connecting to each other // in parallel we don't want to do that or we end up with no // connections still established... l.Infof("Connected to already connected device (%s)", remoteID) c.Close() continue } deviceCfg, ok := s.cfg.Device(remoteID) if !ok { panic("bug: unknown device should already have been rejected") } // Verify the name on the certificate. By default we set it to // "syncthing" when generating, but the user may have replaced // the certificate and used another name. certName := deviceCfg.CertName if certName == "" { certName = s.tlsDefaultCommonName } if err := remoteCert.VerifyHostname(certName); err != nil { // Incorrect certificate name is something the user most // likely wants to know about, since it's an advanced // config. Warn instead of Info. l.Warnf("Bad certificate from %s (%v): %v", remoteID, c.RemoteAddr(), err) c.Close() continue next } // Wrap the connection in rate limiters. The limiter itself will // keep up with config changes to the rate and whether or not LAN // connections are limited. isLAN := s.isLAN(c.RemoteAddr()) wr := s.limiter.newWriteLimiter(c, isLAN) rd := s.limiter.newReadLimiter(c, isLAN) name := fmt.Sprintf("%s-%s (%s)", c.LocalAddr(), c.RemoteAddr(), c.Type()) protoConn := protocol.NewConnection(remoteID, rd, wr, s.model, name, deviceCfg.Compression) modelConn := completeConn{c, protoConn} l.Infof("Established secure connection to %s at %s (%s)", remoteID, name, tlsCipherSuiteNames[c.ConnectionState().CipherSuite]) s.model.AddConnection(modelConn, hello) s.curConMut.Lock() s.currentConnection[remoteID] = modelConn s.curConMut.Unlock() continue next } }
func (s *Service) handle() { next: for c := range s.conns { cs := c.ConnectionState() // We should have negotiated the next level protocol "bep/1.0" as part // of the TLS handshake. Unfortunately this can't be a hard error, // because there are implementations out there that don't support // protocol negotiation (iOS for one...). if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != s.bepProtocolName { l.Infof("Peer %s did not negotiate bep/1.0", c.RemoteAddr()) } // 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 %s; protocol error", cl, c.RemoteAddr()) c.Close() continue } 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) - should not happen", remoteID) c.Close() continue } c.SetDeadline(time.Now().Add(20 * time.Second)) hello, err := protocol.ExchangeHello(c, s.model.GetHello(remoteID)) if err != nil { if protocol.IsVersionMismatch(err) { // The error will be a relatively user friendly description // of what's wrong with the version compatibility msg := fmt.Sprintf("Connecting to %s (%s): %s", remoteID, c.RemoteAddr(), err) warningFor(remoteID, msg) } else { // It's something else - connection reset or whatever l.Infof("Failed to exchange Hello messages with %s (%s): %s", remoteID, c.RemoteAddr(), err) } c.Close() continue } c.SetDeadline(time.Time{}) s.model.OnHello(remoteID, c.RemoteAddr(), hello) // If we have a relay connection, and the new incoming connection is // not a relay connection, we should drop that, and prefer the this one. s.curConMut.Lock() ct, ok := s.currentConnection[remoteID] s.curConMut.Unlock() // Lower priority is better, just like nice etc. if ok && ct.Priority > c.Priority { l.Debugln("Switching connections", remoteID) s.model.Close(remoteID, protocol.ErrSwitchingConnections) } else if s.model.ConnectedTo(remoteID) { // We should not already be connected to the other party. TODO: This // could use some better handling. If the old connection is dead but // hasn't timed out yet we may want to drop *that* connection and keep // this one. But in case we are two devices connecting to each other // in parallel we don't want to do that or we end up with no // connections still established... l.Infof("Connected to already connected device (%s)", remoteID) c.Close() continue } else if s.model.IsPaused(remoteID) { l.Infof("Connection from paused device (%s)", remoteID) c.Close() continue } for deviceID, deviceCfg := range s.cfg.Devices() { if deviceID == remoteID { // Verify the name on the certificate. By default we set it to // "syncthing" when generating, but the user may have replaced // the certificate and used another name. certName := deviceCfg.CertName if certName == "" { certName = s.tlsDefaultCommonName } err := remoteCert.VerifyHostname(certName) if err != nil { // Incorrect certificate name is something the user most // likely wants to know about, since it's an advanced // config. Warn instead of Info. l.Warnf("Bad certificate from %s (%v): %v", remoteID, c.RemoteAddr(), err) c.Close() continue next } // If rate limiting is set, and based on the address we should // limit the connection, then we wrap it in a limiter. limit := s.shouldLimit(c.RemoteAddr()) wr := io.Writer(c) if limit && s.writeRateLimit != nil { wr = NewWriteLimiter(c, s.writeRateLimit) } rd := io.Reader(c) if limit && s.readRateLimit != nil { rd = NewReadLimiter(c, s.readRateLimit) } name := fmt.Sprintf("%s-%s (%s)", c.LocalAddr(), c.RemoteAddr(), c.Type) protoConn := protocol.NewConnection(remoteID, rd, wr, s.model, name, deviceCfg.Compression) modelConn := Connection{c, protoConn} l.Infof("Established secure connection to %s at %s", remoteID, name) l.Debugf("cipher suite: %04X in lan: %t", c.ConnectionState().CipherSuite, !limit) s.model.AddConnection(modelConn, hello) s.curConMut.Lock() s.currentConnection[remoteID] = modelConn s.curConMut.Unlock() continue next } } l.Infof("Connection from %s (%s) with ignored device ID %s", c.RemoteAddr(), c.Type, remoteID) c.Close() } }