// handleDonePeerMsg deals with peers that have signalled they are done. It is // invoked from the peerHandler goroutine. func (s *Server) handleDonePeerMsg(state *peerState, p *peer) { var list map[*peer]struct{} if p.persistent { list = state.persistentPeers } else if p.inbound { list = state.peers } else { list = state.outboundPeers } for e := range list { if e == p { // Issue an asynchronous reconnect if the peer was a // persistent outbound connection. if !p.inbound && p.persistent && atomic.LoadInt32(&s.shutdown) == 0 { delete(list, e) e = newOutboundPeer(s, p.addr, true, p.retryCount+1) list[e] = struct{}{} return } if !p.inbound { state.outboundGroups[addrmgr.GroupKey(p.na)]-- } delete(list, e) srvrLog.Debugf("Removed peer %s", p) return } } // If we get here it means that either we didn't know about the peer // or we purposefully deleted it. }
// handleAddPeerMsg deals with adding new peers. It is invoked from the // peerHandler goroutine. func (s *Server) handleAddPeerMsg(state *peerState, p *peer) bool { if p == nil { return false } // Ignore new peers if we're shutting down. if atomic.LoadInt32(&s.shutdown) != 0 { srvrLog.Infof("New peer %s ignored - server is shutting "+ "down", p) p.Shutdown() return false } // Disconnect banned peers. host, _, err := net.SplitHostPort(p.addr) if err != nil { srvrLog.Debugf("can't split hostport %v", err) p.Shutdown() return false } if banEnd, ok := state.banned[host]; ok { if time.Now().Before(banEnd) { srvrLog.Debugf("Peer %s is banned for another %v - "+ "disconnecting", host, banEnd.Sub(time.Now())) p.Shutdown() return false } srvrLog.Infof("Peer %s is no longer banned", host) delete(state.banned, host) } // TODO: Check for max peers from a single IP. // Limit max number of total peers. if state.Count() >= cfg.MaxPeers { srvrLog.Infof("Max peers reached [%d] - disconnecting "+ "peer %s", cfg.MaxPeers, p) p.Shutdown() // TODO(oga) how to handle permanent peers here? // they should be rescheduled. return false } // Add the new peer and start it. srvrLog.Debugf("New peer %s", p) if p.inbound { state.peers[p] = struct{}{} p.Start() } else { state.outboundGroups[addrmgr.GroupKey(p.na)]++ if p.persistent { state.persistentPeers[p] = struct{}{} } else { state.outboundPeers[p] = struct{}{} } } return true }
// peerHandler is used to handle peer operations such as adding and removing // peers to and from the server, banning peers, and broadcasting messages to // peers. It must be run in a goroutine. func (s *Server) peerHandler() { // Start the address manager and block manager, both of which are needed // by peers. This is done here since their lifecycle is closely tied // to this handler and rather than adding more channels to sychronize // things, it's easier and slightly faster to simply start and stop them // in this handler. s.addrManager.Start() s.blockManager.Start() srvrLog.Tracef("Starting peer handler") state := &peerState{ peers: make(map[*peer]struct{}), persistentPeers: make(map[*peer]struct{}), outboundPeers: make(map[*peer]struct{}), banned: make(map[string]time.Time), maxOutboundPeers: defaultMaxOutbound, outboundGroups: make(map[string]int), } if cfg.MaxPeers < state.maxOutboundPeers { state.maxOutboundPeers = cfg.MaxPeers } // Add peers discovered through DNS to the address manager. s.seedFromDNS() // Start up persistent peers. permanentPeers := cfg.ConnectPeers if len(permanentPeers) == 0 { permanentPeers = cfg.AddPeers } for _, addr := range permanentPeers { s.handleAddPeerMsg(state, newOutboundPeer(s, addr, true, 0)) } // if nothing else happens, wake us up soon. time.AfterFunc(10*time.Second, func() { s.wakeup <- struct{}{} }) out: for { select { // New peers connected to the server. case p := <-s.newPeers: s.handleAddPeerMsg(state, p) // Disconnected peers. case p := <-s.donePeers: s.handleDonePeerMsg(state, p) // Block accepted in mainchain or orphan, update peer height. case umsg := <-s.peerHeightsUpdate: s.handleUpdatePeerHeights(state, umsg) // Peer to ban. case p := <-s.banPeers: s.handleBanPeerMsg(state, p) // New inventory to potentially be relayed to other peers. case invMsg := <-s.relayInv: s.handleRelayInvMsg(state, invMsg) // Message to broadcast to all connected peers except those // which are excluded by the message. case bmsg := <-s.broadcast: s.handleBroadcastMsg(state, &bmsg) // Used by timers below to wake us back up. case <-s.wakeup: // this page left intentionally blank case qmsg := <-s.query: s.handleQuery(qmsg, state) // Shutdown the peer handler. case <-s.quit: // Shutdown peers. state.forAllPeers(func(p *peer) { p.Shutdown() }) break out } // Don't try to connect to more peers when running on the // simulation test network. The simulation network is only // intended to connect to specified peers and actively avoid // advertising and connecting to discovered peers. if cfg.SimNet { continue } // Only try connect to more peers if we actually need more. if !state.NeedMoreOutbound() || len(cfg.ConnectPeers) > 0 || atomic.LoadInt32(&s.shutdown) != 0 { continue } tries := 0 for state.NeedMoreOutbound() && atomic.LoadInt32(&s.shutdown) == 0 { nPeers := state.OutboundCount() if nPeers > 8 { nPeers = 8 } addr := s.addrManager.GetAddress("any") if addr == nil { break } key := addrmgr.GroupKey(addr.NetAddress()) // Address will not be invalid, local or unroutable // because addrmanager rejects those on addition. // Just check that we don't already have an address // in the same group so that we are not connecting // to the same network segment at the expense of // others. if state.outboundGroups[key] != 0 { break } tries++ // After 100 bad tries exit the loop and we'll try again // later. if tries > 100 { break } // XXX if we have limited that address skip // only allow recent nodes (10mins) after we failed 30 // times if tries < 30 && time.Now().Sub(addr.LastAttempt()) < 10*time.Minute { continue } // allow nondefault ports after 50 failed tries. if fmt.Sprintf("%d", addr.NetAddress().Port) != activeNetParams.DefaultPort && tries < 50 { continue } addrStr := addrmgr.NetAddressKey(addr.NetAddress()) tries = 0 // any failure will be due to banned peers etc. we have // already checked that we have room for more peers. if s.handleAddPeerMsg(state, newOutboundPeer(s, addrStr, false, 0)) { } } // We need more peers, wake up in ten seconds and try again. if state.NeedMoreOutbound() { time.AfterFunc(10*time.Second, func() { s.wakeup <- struct{}{} }) } } //if cfg.AddrIndex { //s.addrIndexer.Stop() //} s.blockManager.Stop() s.addrManager.Stop() s.wg.Done() srvrLog.Tracef("Peer handler done") }
// handleQuery is the central handler for all queries and commands from other // goroutines related to peer state. func (s *Server) handleQuery(querymsg interface{}, state *peerState) { switch msg := querymsg.(type) { case getConnCountMsg: nconnected := int32(0) state.forAllPeers(func(p *peer) { if p.Connected() { nconnected++ } }) msg.reply <- nconnected case getPeerInfoMsg: syncPeer := s.blockManager.SyncPeer() infos := make([]*GetPeerInfoResult, 0, len(state.peers)) state.forAllPeers(func(p *peer) { if !p.Connected() { return } // A lot of this will make the race detector go mad, // however it is statistics for purely informational purposes // and we don't really care if they are raced to get the new // version. p.StatsMtx.Lock() info := &GetPeerInfoResult{ ID: p.id, Addr: p.addr, Services: fmt.Sprintf("%08d", p.services), LastSend: p.lastSend.Unix(), LastRecv: p.lastRecv.Unix(), BytesSent: p.bytesSent, BytesRecv: p.bytesReceived, ConnTime: p.timeConnected.Unix(), TimeOffset: p.timeOffset, Version: p.protocolVersion, SubVer: p.userAgent, Inbound: p.inbound, StartingHeight: p.startingHeight, CurrentHeight: p.lastBlock, BanScore: 0, SyncNode: p == syncPeer, } info.PingTime = float64(p.lastPingMicros) if p.lastPingNonce != 0 { wait := float64(time.Now().Sub(p.lastPingTime).Nanoseconds()) // We actually want microseconds. info.PingWait = wait / 1000 } p.StatsMtx.Unlock() infos = append(infos, info) }) msg.reply <- infos case connectNodeMsg: // XXX(oga) duplicate oneshots? for peer := range state.persistentPeers { if peer.addr == msg.addr { if msg.permanent { msg.reply <- errors.New("peer already connected") } else { msg.reply <- errors.New("peer exists as a permanent peer") } return } } // TODO(oga) if too many, nuke a non-perm peer. if s.handleAddPeerMsg(state, newOutboundPeer(s, msg.addr, msg.permanent, 0)) { msg.reply <- nil } else { msg.reply <- errors.New("failed to add peer") } case removeNodeMsg: found := disconnectPeer(state.persistentPeers, msg.cmp, func(p *peer) { // Keep group counts ok since we remove from // the list now. state.outboundGroups[addrmgr.GroupKey(p.na)]-- }) if found { msg.reply <- nil } else { msg.reply <- errors.New("peer not found") } // Request a list of the persistent (added) peers. case getAddedNodesMsg: // Respond with a slice of the relavent peers. peers := make([]*peer, 0, len(state.persistentPeers)) for peer := range state.persistentPeers { peers = append(peers, peer) } msg.reply <- peers case disconnectNodeMsg: // Check inbound peers. We pass a nil callback since we don't // require any additional actions on disconnect for inbound peers. found := disconnectPeer(state.peers, msg.cmp, nil) if found { msg.reply <- nil return } // Check outbound peers. found = disconnectPeer(state.outboundPeers, msg.cmp, func(p *peer) { // Keep group counts ok since we remove from // the list now. state.outboundGroups[addrmgr.GroupKey(p.na)]-- }) if found { // If there are multiple outbound connections to the same // ip:port, continue disconnecting them all until no such // peers are found. for found { found = disconnectPeer(state.outboundPeers, msg.cmp, func(p *peer) { state.outboundGroups[addrmgr.GroupKey(p.na)]-- }) } msg.reply <- nil return } msg.reply <- errors.New("peer not found") } }