463 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			463 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package p2p
 | 
						|
 | 
						|
import (
 | 
						|
	"bufio"
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"net"
 | 
						|
	"sort"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/ethereum/go-ethereum/event"
 | 
						|
	"github.com/ethereum/go-ethereum/logger"
 | 
						|
)
 | 
						|
 | 
						|
// peerAddr is the structure of a peer list element.
 | 
						|
// It is also a valid net.Addr.
 | 
						|
type peerAddr struct {
 | 
						|
	IP     net.IP
 | 
						|
	Port   uint64
 | 
						|
	Pubkey []byte // optional
 | 
						|
}
 | 
						|
 | 
						|
func newPeerAddr(addr net.Addr, pubkey []byte) *peerAddr {
 | 
						|
	n := addr.Network()
 | 
						|
	if n != "tcp" && n != "tcp4" && n != "tcp6" {
 | 
						|
		// for testing with non-TCP
 | 
						|
		return &peerAddr{net.ParseIP("127.0.0.1"), 30303, pubkey}
 | 
						|
	}
 | 
						|
	ta := addr.(*net.TCPAddr)
 | 
						|
	return &peerAddr{ta.IP, uint64(ta.Port), pubkey}
 | 
						|
}
 | 
						|
 | 
						|
func (d peerAddr) Network() string {
 | 
						|
	if d.IP.To4() != nil {
 | 
						|
		return "tcp4"
 | 
						|
	} else {
 | 
						|
		return "tcp6"
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (d peerAddr) String() string {
 | 
						|
	return fmt.Sprintf("%v:%d", d.IP, d.Port)
 | 
						|
}
 | 
						|
 | 
						|
func (d *peerAddr) RlpData() interface{} {
 | 
						|
	return []interface{}{string(d.IP), d.Port, d.Pubkey}
 | 
						|
}
 | 
						|
 | 
						|
// Peer represents a remote peer.
 | 
						|
type Peer struct {
 | 
						|
	// Peers have all the log methods.
 | 
						|
	// Use them to display messages related to the peer.
 | 
						|
	*logger.Logger
 | 
						|
 | 
						|
	infolock   sync.Mutex
 | 
						|
	identity   ClientIdentity
 | 
						|
	caps       []Cap
 | 
						|
	listenAddr *peerAddr // what remote peer is listening on
 | 
						|
	dialAddr   *peerAddr // non-nil if dialing
 | 
						|
 | 
						|
	// The mutex protects the connection
 | 
						|
	// so only one protocol can write at a time.
 | 
						|
	writeMu sync.Mutex
 | 
						|
	conn    net.Conn
 | 
						|
	bufconn *bufio.ReadWriter
 | 
						|
 | 
						|
	// These fields maintain the running protocols.
 | 
						|
	protocols       []Protocol
 | 
						|
	runBaseProtocol bool // for testing
 | 
						|
 | 
						|
	runlock sync.RWMutex // protects running
 | 
						|
	running map[string]*proto
 | 
						|
 | 
						|
	protoWG  sync.WaitGroup
 | 
						|
	protoErr chan error
 | 
						|
	closed   chan struct{}
 | 
						|
	disc     chan DiscReason
 | 
						|
 | 
						|
	activity event.TypeMux // for activity events
 | 
						|
 | 
						|
	slot int // index into Server peer list
 | 
						|
 | 
						|
	// These fields are kept so base protocol can access them.
 | 
						|
	// TODO: this should be one or more interfaces
 | 
						|
	ourID         ClientIdentity        // client id of the Server
 | 
						|
	ourListenAddr *peerAddr             // listen addr of Server, nil if not listening
 | 
						|
	newPeerAddr   chan<- *peerAddr      // tell server about received peers
 | 
						|
	otherPeers    func() []*Peer        // should return the list of all peers
 | 
						|
	pubkeyHook    func(*peerAddr) error // called at end of handshake to validate pubkey
 | 
						|
}
 | 
						|
 | 
						|
// NewPeer returns a peer for testing purposes.
 | 
						|
func NewPeer(id ClientIdentity, caps []Cap) *Peer {
 | 
						|
	conn, _ := net.Pipe()
 | 
						|
	peer := newPeer(conn, nil, nil)
 | 
						|
	peer.setHandshakeInfo(id, nil, caps)
 | 
						|
	close(peer.closed)
 | 
						|
	return peer
 | 
						|
}
 | 
						|
 | 
						|
func newServerPeer(server *Server, conn net.Conn, dialAddr *peerAddr) *Peer {
 | 
						|
	p := newPeer(conn, server.Protocols, dialAddr)
 | 
						|
	p.ourID = server.Identity
 | 
						|
	p.newPeerAddr = server.peerConnect
 | 
						|
	p.otherPeers = server.Peers
 | 
						|
	p.pubkeyHook = server.verifyPeer
 | 
						|
	p.runBaseProtocol = true
 | 
						|
 | 
						|
	// laddr can be updated concurrently by NAT traversal.
 | 
						|
	// newServerPeer must be called with the server lock held.
 | 
						|
	if server.laddr != nil {
 | 
						|
		p.ourListenAddr = newPeerAddr(server.laddr, server.Identity.Pubkey())
 | 
						|
	}
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func newPeer(conn net.Conn, protocols []Protocol, dialAddr *peerAddr) *Peer {
 | 
						|
	p := &Peer{
 | 
						|
		Logger:    logger.NewLogger("P2P " + conn.RemoteAddr().String()),
 | 
						|
		conn:      conn,
 | 
						|
		dialAddr:  dialAddr,
 | 
						|
		bufconn:   bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)),
 | 
						|
		protocols: protocols,
 | 
						|
		running:   make(map[string]*proto),
 | 
						|
		disc:      make(chan DiscReason),
 | 
						|
		protoErr:  make(chan error),
 | 
						|
		closed:    make(chan struct{}),
 | 
						|
	}
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
// Identity returns the client identity of the remote peer. The
 | 
						|
// identity can be nil if the peer has not yet completed the
 | 
						|
// handshake.
 | 
						|
func (p *Peer) Identity() ClientIdentity {
 | 
						|
	p.infolock.Lock()
 | 
						|
	defer p.infolock.Unlock()
 | 
						|
	return p.identity
 | 
						|
}
 | 
						|
 | 
						|
// Caps returns the capabilities (supported subprotocols) of the remote peer.
 | 
						|
func (p *Peer) Caps() []Cap {
 | 
						|
	p.infolock.Lock()
 | 
						|
	defer p.infolock.Unlock()
 | 
						|
	return p.caps
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) setHandshakeInfo(id ClientIdentity, laddr *peerAddr, caps []Cap) {
 | 
						|
	p.infolock.Lock()
 | 
						|
	p.identity = id
 | 
						|
	p.listenAddr = laddr
 | 
						|
	p.caps = caps
 | 
						|
	p.infolock.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// RemoteAddr returns the remote address of the network connection.
 | 
						|
func (p *Peer) RemoteAddr() net.Addr {
 | 
						|
	return p.conn.RemoteAddr()
 | 
						|
}
 | 
						|
 | 
						|
// LocalAddr returns the local address of the network connection.
 | 
						|
func (p *Peer) LocalAddr() net.Addr {
 | 
						|
	return p.conn.LocalAddr()
 | 
						|
}
 | 
						|
 | 
						|
// Disconnect terminates the peer connection with the given reason.
 | 
						|
// It returns immediately and does not wait until the connection is closed.
 | 
						|
func (p *Peer) Disconnect(reason DiscReason) {
 | 
						|
	select {
 | 
						|
	case p.disc <- reason:
 | 
						|
	case <-p.closed:
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// String implements fmt.Stringer.
 | 
						|
func (p *Peer) String() string {
 | 
						|
	kind := "inbound"
 | 
						|
	p.infolock.Lock()
 | 
						|
	if p.dialAddr != nil {
 | 
						|
		kind = "outbound"
 | 
						|
	}
 | 
						|
	p.infolock.Unlock()
 | 
						|
	return fmt.Sprintf("Peer(%p %v %s)", p, p.conn.RemoteAddr(), kind)
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	// maximum amount of time allowed for reading a message
 | 
						|
	msgReadTimeout = 5 * time.Second
 | 
						|
	// maximum amount of time allowed for writing a message
 | 
						|
	msgWriteTimeout = 5 * time.Second
 | 
						|
	// messages smaller than this many bytes will be read at
 | 
						|
	// once before passing them to a protocol.
 | 
						|
	wholePayloadSize = 64 * 1024
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	inactivityTimeout     = 2 * time.Second
 | 
						|
	disconnectGracePeriod = 2 * time.Second
 | 
						|
)
 | 
						|
 | 
						|
func (p *Peer) loop() (reason DiscReason, err error) {
 | 
						|
	defer p.activity.Stop()
 | 
						|
	defer p.closeProtocols()
 | 
						|
	defer close(p.closed)
 | 
						|
	defer p.conn.Close()
 | 
						|
 | 
						|
	// read loop
 | 
						|
	readMsg := make(chan Msg)
 | 
						|
	readErr := make(chan error)
 | 
						|
	readNext := make(chan bool, 1)
 | 
						|
	protoDone := make(chan struct{}, 1)
 | 
						|
	go p.readLoop(readMsg, readErr, readNext)
 | 
						|
	readNext <- true
 | 
						|
 | 
						|
	if p.runBaseProtocol {
 | 
						|
		p.startBaseProtocol()
 | 
						|
	}
 | 
						|
 | 
						|
loop:
 | 
						|
	for {
 | 
						|
		select {
 | 
						|
		case msg := <-readMsg:
 | 
						|
			// a new message has arrived.
 | 
						|
			var wait bool
 | 
						|
			if wait, err = p.dispatch(msg, protoDone); err != nil {
 | 
						|
				p.Errorf("msg dispatch error: %v\n", err)
 | 
						|
				reason = discReasonForError(err)
 | 
						|
				break loop
 | 
						|
			}
 | 
						|
			if !wait {
 | 
						|
				// Msg has already been read completely, continue with next message.
 | 
						|
				readNext <- true
 | 
						|
			}
 | 
						|
			p.activity.Post(time.Now())
 | 
						|
		case <-protoDone:
 | 
						|
			// protocol has consumed the message payload,
 | 
						|
			// we can continue reading from the socket.
 | 
						|
			readNext <- true
 | 
						|
 | 
						|
		case err := <-readErr:
 | 
						|
			// read failed. there is no need to run the
 | 
						|
			// polite disconnect sequence because the connection
 | 
						|
			// is probably dead anyway.
 | 
						|
			// TODO: handle write errors as well
 | 
						|
			return DiscNetworkError, err
 | 
						|
		case err = <-p.protoErr:
 | 
						|
			reason = discReasonForError(err)
 | 
						|
			break loop
 | 
						|
		case reason = <-p.disc:
 | 
						|
			break loop
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// wait for read loop to return.
 | 
						|
	close(readNext)
 | 
						|
	<-readErr
 | 
						|
	// tell the remote end to disconnect
 | 
						|
	done := make(chan struct{})
 | 
						|
	go func() {
 | 
						|
		p.conn.SetDeadline(time.Now().Add(disconnectGracePeriod))
 | 
						|
		p.writeMsg(NewMsg(discMsg, reason), disconnectGracePeriod)
 | 
						|
		io.Copy(ioutil.Discard, p.conn)
 | 
						|
		close(done)
 | 
						|
	}()
 | 
						|
	select {
 | 
						|
	case <-done:
 | 
						|
	case <-time.After(disconnectGracePeriod):
 | 
						|
	}
 | 
						|
	return reason, err
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) readLoop(msgc chan<- Msg, errc chan<- error, unblock <-chan bool) {
 | 
						|
	for _ = range unblock {
 | 
						|
		p.conn.SetReadDeadline(time.Now().Add(msgReadTimeout))
 | 
						|
		if msg, err := readMsg(p.bufconn); err != nil {
 | 
						|
			errc <- err
 | 
						|
		} else {
 | 
						|
			msgc <- msg
 | 
						|
		}
 | 
						|
	}
 | 
						|
	close(errc)
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) dispatch(msg Msg, protoDone chan struct{}) (wait bool, err error) {
 | 
						|
	proto, err := p.getProto(msg.Code)
 | 
						|
	if err != nil {
 | 
						|
		return false, err
 | 
						|
	}
 | 
						|
	if msg.Size <= wholePayloadSize {
 | 
						|
		// optimization: msg is small enough, read all
 | 
						|
		// of it and move on to the next message
 | 
						|
		buf, err := ioutil.ReadAll(msg.Payload)
 | 
						|
		if err != nil {
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
		msg.Payload = bytes.NewReader(buf)
 | 
						|
		proto.in <- msg
 | 
						|
	} else {
 | 
						|
		wait = true
 | 
						|
		pr := &eofSignal{msg.Payload, int64(msg.Size), protoDone}
 | 
						|
		msg.Payload = pr
 | 
						|
		proto.in <- msg
 | 
						|
	}
 | 
						|
	return wait, nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) startBaseProtocol() {
 | 
						|
	p.runlock.Lock()
 | 
						|
	defer p.runlock.Unlock()
 | 
						|
	p.running[""] = p.startProto(0, Protocol{
 | 
						|
		Length: baseProtocolLength,
 | 
						|
		Run:    runBaseProtocol,
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// startProtocols starts matching named subprotocols.
 | 
						|
func (p *Peer) startSubprotocols(caps []Cap) {
 | 
						|
	sort.Sort(capsByName(caps))
 | 
						|
 | 
						|
	p.runlock.Lock()
 | 
						|
	defer p.runlock.Unlock()
 | 
						|
	offset := baseProtocolLength
 | 
						|
outer:
 | 
						|
	for _, cap := range caps {
 | 
						|
		for _, proto := range p.protocols {
 | 
						|
			if proto.Name == cap.Name &&
 | 
						|
				proto.Version == cap.Version &&
 | 
						|
				p.running[cap.Name] == nil {
 | 
						|
				p.running[cap.Name] = p.startProto(offset, proto)
 | 
						|
				offset += proto.Length
 | 
						|
				continue outer
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) startProto(offset uint64, impl Protocol) *proto {
 | 
						|
	rw := &proto{
 | 
						|
		in:      make(chan Msg),
 | 
						|
		offset:  offset,
 | 
						|
		maxcode: impl.Length,
 | 
						|
		peer:    p,
 | 
						|
	}
 | 
						|
	p.protoWG.Add(1)
 | 
						|
	go func() {
 | 
						|
		err := impl.Run(p, rw)
 | 
						|
		if err == nil {
 | 
						|
			p.Infof("protocol %q returned", impl.Name)
 | 
						|
			err = newPeerError(errMisc, "protocol returned")
 | 
						|
		} else {
 | 
						|
			p.Errorf("protocol %q error: %v\n", impl.Name, err)
 | 
						|
		}
 | 
						|
		select {
 | 
						|
		case p.protoErr <- err:
 | 
						|
		case <-p.closed:
 | 
						|
		}
 | 
						|
		p.protoWG.Done()
 | 
						|
	}()
 | 
						|
	return rw
 | 
						|
}
 | 
						|
 | 
						|
// getProto finds the protocol responsible for handling
 | 
						|
// the given message code.
 | 
						|
func (p *Peer) getProto(code uint64) (*proto, error) {
 | 
						|
	p.runlock.RLock()
 | 
						|
	defer p.runlock.RUnlock()
 | 
						|
	for _, proto := range p.running {
 | 
						|
		if code >= proto.offset && code < proto.offset+proto.maxcode {
 | 
						|
			return proto, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil, newPeerError(errInvalidMsgCode, "%d", code)
 | 
						|
}
 | 
						|
 | 
						|
func (p *Peer) closeProtocols() {
 | 
						|
	p.runlock.RLock()
 | 
						|
	for _, p := range p.running {
 | 
						|
		close(p.in)
 | 
						|
	}
 | 
						|
	p.runlock.RUnlock()
 | 
						|
	p.protoWG.Wait()
 | 
						|
}
 | 
						|
 | 
						|
// writeProtoMsg sends the given message on behalf of the given named protocol.
 | 
						|
func (p *Peer) writeProtoMsg(protoName string, msg Msg) error {
 | 
						|
	p.runlock.RLock()
 | 
						|
	proto, ok := p.running[protoName]
 | 
						|
	p.runlock.RUnlock()
 | 
						|
	if !ok {
 | 
						|
		return fmt.Errorf("protocol %s not handled by peer", protoName)
 | 
						|
	}
 | 
						|
	if msg.Code >= proto.maxcode {
 | 
						|
		return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName)
 | 
						|
	}
 | 
						|
	msg.Code += proto.offset
 | 
						|
	return p.writeMsg(msg, msgWriteTimeout)
 | 
						|
}
 | 
						|
 | 
						|
// writeMsg writes a message to the connection.
 | 
						|
func (p *Peer) writeMsg(msg Msg, timeout time.Duration) error {
 | 
						|
	p.writeMu.Lock()
 | 
						|
	defer p.writeMu.Unlock()
 | 
						|
	p.conn.SetWriteDeadline(time.Now().Add(timeout))
 | 
						|
	if err := writeMsg(p.bufconn, msg); err != nil {
 | 
						|
		return newPeerError(errWrite, "%v", err)
 | 
						|
	}
 | 
						|
	return p.bufconn.Flush()
 | 
						|
}
 | 
						|
 | 
						|
type proto struct {
 | 
						|
	name            string
 | 
						|
	in              chan Msg
 | 
						|
	maxcode, offset uint64
 | 
						|
	peer            *Peer
 | 
						|
}
 | 
						|
 | 
						|
func (rw *proto) WriteMsg(msg Msg) error {
 | 
						|
	if msg.Code >= rw.maxcode {
 | 
						|
		return newPeerError(errInvalidMsgCode, "not handled")
 | 
						|
	}
 | 
						|
	msg.Code += rw.offset
 | 
						|
	return rw.peer.writeMsg(msg, msgWriteTimeout)
 | 
						|
}
 | 
						|
 | 
						|
func (rw *proto) EncodeMsg(code uint64, data ...interface{}) error {
 | 
						|
	return rw.WriteMsg(NewMsg(code, data...))
 | 
						|
}
 | 
						|
 | 
						|
func (rw *proto) ReadMsg() (Msg, error) {
 | 
						|
	msg, ok := <-rw.in
 | 
						|
	if !ok {
 | 
						|
		return msg, io.EOF
 | 
						|
	}
 | 
						|
	msg.Code -= rw.offset
 | 
						|
	return msg, nil
 | 
						|
}
 | 
						|
 | 
						|
// eofSignal wraps a reader with eof signaling. the eof channel is
 | 
						|
// closed when the wrapped reader returns an error or when count bytes
 | 
						|
// have been read.
 | 
						|
//
 | 
						|
type eofSignal struct {
 | 
						|
	wrapped io.Reader
 | 
						|
	count   int64
 | 
						|
	eof     chan<- struct{}
 | 
						|
}
 | 
						|
 | 
						|
// note: when using eofSignal to detect whether a message payload
 | 
						|
// has been read, Read might not be called for zero sized messages.
 | 
						|
 | 
						|
func (r *eofSignal) Read(buf []byte) (int, error) {
 | 
						|
	n, err := r.wrapped.Read(buf)
 | 
						|
	r.count -= int64(n)
 | 
						|
	if (err != nil || r.count <= 0) && r.eof != nil {
 | 
						|
		r.eof <- struct{}{} // tell Peer that msg has been consumed
 | 
						|
		r.eof = nil
 | 
						|
	}
 | 
						|
	return n, err
 | 
						|
}
 |