// acceptConn adds a connecting node as a peer. func (g *Gateway) acceptConn(conn net.Conn) { addr := modules.NetAddress(conn.RemoteAddr().String()) g.log.Printf("INFO: %v wants to connect", addr) // read version var remoteVersion string if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil { conn.Close() g.log.Printf("INFO: %v wanted to connect, but we could not read their version: %v", addr, err) return } // check that version is acceptable // NOTE: this version must be bumped whenever the gateway or consensus // breaks compatibility. if build.VersionCmp(remoteVersion, "0.3.3") < 0 { encoding.WriteObject(conn, "reject") conn.Close() g.log.Printf("INFO: %v wanted to connect, but their version (%v) was unacceptable", addr, remoteVersion) return } // respond with our version if err := encoding.WriteObject(conn, build.Version); err != nil { conn.Close() g.log.Printf("INFO: could not write version ack to %v: %v", addr, err) return } // If we are already fully connected, kick out an old peer to make room // for the new one. Importantly, prioritize kicking a peer with the same // IP as the connecting peer. This protects against Sybil attacks. id := g.mu.Lock() if len(g.peers) >= fullyConnectedThreshold { // first choose a random peer, preferably inbound. If have only // outbound peers, we'll wind up kicking an outbound peer; but // subsequent inbound connections will kick each other instead of // continuing to replace outbound peers. kick, err := g.randomInboundPeer() if err != nil { kick, _ = g.randomPeer() } // if another peer shares this IP, choose that one instead for p := range g.peers { if p.Host() == addr.Host() { kick = p break } } g.peers[kick].sess.Close() delete(g.peers, kick) g.log.Printf("INFO: disconnected from %v to make room for %v", kick, addr) } // add the peer g.addPeer(&peer{addr: addr, sess: muxado.Server(conn), inbound: true}) g.mu.Unlock(id) g.log.Printf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion) }
// handleMultiplex is used to multiplex a single incoming connection func (s *Server) handleMultiplex(conn net.Conn) { defer conn.Close() server := muxado.Server(conn) for { sub, err := server.Accept() if err != nil { if !strings.Contains(err.Error(), "closed") { s.logger.Printf("[ERR] consul.rpc: multiplex conn accept failed: %v", err) } return } go s.handleConsulConn(sub) } }
func (t transport) NewConn(nc net.Conn, isServer bool) (smux.Conn, error) { var s muxado.Session if isServer { s = muxado.Server(nc) } else { s = muxado.Client(nc) } cl := make(chan struct{}) go func() { s.Wait() close(cl) }() return &conn{ms: s, closed: cl}, nil }
// acceptConn adds a connecting node as a peer. func (g *Gateway) acceptConn(conn net.Conn) { addr := modules.NetAddress(conn.RemoteAddr().String()) g.log.Printf("INFO: %v wants to connect", addr) // don't connect to an IP address more than once if build.Release != "testing" { id := g.mu.RLock() for p := range g.peers { if p.Host() == addr.Host() { g.mu.RUnlock(id) conn.Close() g.log.Printf("INFO: rejected connection from %v: already connected", addr) return } } g.mu.RUnlock(id) } // read version var remoteVersion string if err := encoding.ReadObject(conn, &remoteVersion, maxAddrLength); err != nil { conn.Close() g.log.Printf("INFO: %v wanted to connect, but we could not read their version: %v", addr, err) return } // decide whether to accept // NOTE: this version must be bumped whenever the gateway or consensus // breaks compatibility. if build.VersionCmp(remoteVersion, "0.3.3") < 0 { encoding.WriteObject(conn, "reject") conn.Close() g.log.Printf("INFO: %v wanted to connect, but their version (%v) was unacceptable", addr, remoteVersion) return } // respond with our version if err := encoding.WriteObject(conn, "0.3.3"); err != nil { conn.Close() g.log.Printf("INFO: could not write version ack to %v: %v", addr, err) return } // If we are already fully connected, kick out an old inbound peer to make // room for the new one. Among other things, this ensures that bootstrap // nodes will always be connectible. Worst case, you'll connect, receive a // node list, and immediately get booted. But once you have the node list // you should be able to connect to less full peers. id := g.mu.Lock() if len(g.peers) >= fullyConnectedThreshold { oldPeer, err := g.randomInboundPeer() if err == nil { g.peers[oldPeer].sess.Close() delete(g.peers, oldPeer) g.log.Printf("INFO: disconnected from %v to make room for %v", oldPeer, addr) } } // add the peer g.addPeer(&peer{addr: addr, sess: muxado.Server(conn), inbound: true}) g.mu.Unlock(id) g.log.Printf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion) }