peer_error_handler.go 2.1 KB
Newer Older
Z
zelig 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
package p2p

import (
	"net"
)

const (
	severityThreshold = 10
)

type DisconnectRequest struct {
	addr   net.Addr
	reason DiscReason
}

type PeerErrorHandler struct {
	quit           chan chan bool
	address        net.Addr
	peerDisconnect chan DisconnectRequest
	severity       int
	peerErrorChan  chan *PeerError
	blacklist      Blacklist
}

func NewPeerErrorHandler(address net.Addr, peerDisconnect chan DisconnectRequest, peerErrorChan chan *PeerError, blacklist Blacklist) *PeerErrorHandler {
	return &PeerErrorHandler{
		quit:           make(chan chan bool),
		address:        address,
		peerDisconnect: peerDisconnect,
		peerErrorChan:  peerErrorChan,
		blacklist:      blacklist,
	}
}

func (self *PeerErrorHandler) Start() {
	go self.listen()
}

func (self *PeerErrorHandler) Stop() {
	q := make(chan bool)
	self.quit <- q
	<-q
}

func (self *PeerErrorHandler) listen() {
	for {
		select {
		case peerError, ok := <-self.peerErrorChan:
			if ok {
				logger.Debugf("error %v\n", peerError)
				go self.handle(peerError)
			} else {
				return
			}
		case q := <-self.quit:
			q <- true
			return
		}
	}
}

func (self *PeerErrorHandler) handle(peerError *PeerError) {
	reason := DiscReason(' ')
	switch peerError.Code {
	case P2PVersionMismatch:
		reason = DiscIncompatibleVersion
	case PubkeyMissing, PubkeyInvalid:
		reason = DiscInvalidIdentity
	case PubkeyForbidden:
		reason = DiscUselessPeer
	case InvalidMsgCode, PacketTooShort, PayloadTooShort, MagicTokenMismatch, EmptyPayload, ProtocolBreach:
		reason = DiscProtocolError
	case PingTimeout:
		reason = DiscReadTimeout
	case WriteError, MiscError:
		reason = DiscNetworkError
	case InvalidGenesis, InvalidNetworkId, InvalidProtocolVersion:
		reason = DiscSubprotocolError
	default:
		self.severity += self.getSeverity(peerError)
	}

	if self.severity >= severityThreshold {
		reason = DiscSubprotocolError
	}
	if reason != DiscReason(' ') {
		self.peerDisconnect <- DisconnectRequest{
			addr:   self.address,
			reason: reason,
		}
	}
}

func (self *PeerErrorHandler) getSeverity(peerError *PeerError) int {
	switch peerError.Code {
	case ReadError:
		return 4 //tolerate 3 :)
	default:
		return 1
	}
}