// 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 *list.List if p.persistent { list = state.persistentPeers } else if p.inbound { list = state.peers } else { list = state.outboundPeers } for e := list.Front(); e != nil; e = e.Next() { if e.Value == p { // Issue an asynchronous reconnect if the peer was a // persistent outbound connection. if !p.inbound && p.persistent && atomic.LoadInt32(&s.shutdown) == 0 { e.Value = newOutboundPeer(s, p.addr, true, p.retryCount+1) return } if !p.inbound { state.outboundGroups[addrmgr.GroupKey(p.na)]-- } list.Remove(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. }
// 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: list.New(), persistentPeers: list.New(), outboundPeers: list.New(), 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) // 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 { time.Sleep(time.Second / 10) // We bias like bitcoind does, 10 for no outgoing // up to 90 (8) for the selection of new vs tried //addresses. nPeers := state.OutboundCount() if nPeers > 8 { nPeers = 8 } addr := s.addrManager.GetAddress("any", 10+nPeers*10) 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 time.Now().After(addr.LastAttempt().Add(10*time.Minute)) && tries < 30 { continue time.Sleep(time.Second) } // 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++ } }) srvrLog.Infof("nconnected= %d", nconnected) //srvrLog.Info(fmt.Sprintf("nconnected= %d", nconnected)) msg.reply <- nconnected case getPeerInfoMsg: syncPeer := s.blockManager.SyncPeer() infos := make([]*btcjson.GetPeerInfoResult, 0, state.peers.Len()) 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 := &btcjson.GetPeerInfoResult{ 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(), Version: p.protocolVersion, SubVer: p.userAgent, Inbound: p.inbound, StartingHeight: 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 addNodeMsg: // XXX(oga) duplicate oneshots? if msg.permanent { for e := state.persistentPeers.Front(); e != nil; e = e.Next() { peer := e.Value.(*peer) if peer.addr == msg.addr { msg.reply <- errors.New("peer already connected") 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 delNodeMsg: found := false for e := state.persistentPeers.Front(); e != nil; e = e.Next() { peer := e.Value.(*peer) if peer.addr == msg.addr { // Keep group counts ok since we remove from // the list now. state.outboundGroups[addrmgr.GroupKey(peer.na)]-- // This is ok because we are not continuing // to iterate so won't corrupt the loop. state.persistentPeers.Remove(e) peer.Disconnect() peerCount := state.Count() srvrLog.Infof("nconnected= %d (peerCount) after Disconnect()", peerCount) found = true break } } 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, state.persistentPeers.Len()) for e := state.persistentPeers.Front(); e != nil; e = e.Next() { peer := e.Value.(*peer) peers = append(peers, peer) } msg.reply <- peers } }
// 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. // count peers peerCount := state.Count() if prev_connected != peerCount { srvrLog.Infof("nconnected= %d (peerCount)", peerCount) prev_connected = peerCount } // 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.PushBack(p) p.Start() } else { state.outboundGroups[addrmgr.GroupKey(p.na)]++ if p.persistent { state.persistentPeers.PushBack(p) } else { state.outboundPeers.PushBack(p) } } return true }