github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/transport.go (about)

     1  package p2p
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/evdatsion/aphelion-dpos-bft/crypto"
    10  	"github.com/evdatsion/aphelion-dpos-bft/p2p/conn"
    11  )
    12  
    13  const (
    14  	defaultDialTimeout      = time.Second
    15  	defaultFilterTimeout    = 5 * time.Second
    16  	defaultHandshakeTimeout = 3 * time.Second
    17  )
    18  
    19  // IPResolver is a behaviour subset of net.Resolver.
    20  type IPResolver interface {
    21  	LookupIPAddr(context.Context, string) ([]net.IPAddr, error)
    22  }
    23  
    24  // accept is the container to carry the upgraded connection and NodeInfo from an
    25  // asynchronously running routine to the Accept method.
    26  type accept struct {
    27  	netAddr  *NetAddress
    28  	conn     net.Conn
    29  	nodeInfo NodeInfo
    30  	err      error
    31  }
    32  
    33  // peerConfig is used to bundle data we need to fully setup a Peer with an
    34  // MConn, provided by the caller of Accept and Dial (currently the Switch). This
    35  // a temporary measure until reactor setup is less dynamic and we introduce the
    36  // concept of PeerBehaviour to communicate about significant Peer lifecycle
    37  // events.
    38  // TODO(xla): Refactor out with more static Reactor setup and PeerBehaviour.
    39  type peerConfig struct {
    40  	chDescs     []*conn.ChannelDescriptor
    41  	onPeerError func(Peer, interface{})
    42  	outbound    bool
    43  	// isPersistent allows you to set a function, which, given socket address
    44  	// (for outbound peers) OR self-reported address (for inbound peers), tells
    45  	// if the peer is persistent or not.
    46  	isPersistent func(*NetAddress) bool
    47  	reactorsByCh map[byte]Reactor
    48  	metrics      *Metrics
    49  }
    50  
    51  // Transport emits and connects to Peers. The implementation of Peer is left to
    52  // the transport. Each transport is also responsible to filter establishing
    53  // peers specific to its domain.
    54  type Transport interface {
    55  	// Listening address.
    56  	NetAddress() NetAddress
    57  
    58  	// Accept returns a newly connected Peer.
    59  	Accept(peerConfig) (Peer, error)
    60  
    61  	// Dial connects to the Peer for the address.
    62  	Dial(NetAddress, peerConfig) (Peer, error)
    63  
    64  	// Cleanup any resources associated with Peer.
    65  	Cleanup(Peer)
    66  }
    67  
    68  // transportLifecycle bundles the methods for callers to control start and stop
    69  // behaviour.
    70  type transportLifecycle interface {
    71  	Close() error
    72  	Listen(NetAddress) error
    73  }
    74  
    75  // ConnFilterFunc to be implemented by filter hooks after a new connection has
    76  // been established. The set of exisiting connections is passed along together
    77  // with all resolved IPs for the new connection.
    78  type ConnFilterFunc func(ConnSet, net.Conn, []net.IP) error
    79  
    80  // ConnDuplicateIPFilter resolves and keeps all ips for an incoming connection
    81  // and refuses new ones if they come from a known ip.
    82  func ConnDuplicateIPFilter() ConnFilterFunc {
    83  	return func(cs ConnSet, c net.Conn, ips []net.IP) error {
    84  		for _, ip := range ips {
    85  			if cs.HasIP(ip) {
    86  				return ErrRejected{
    87  					conn:        c,
    88  					err:         fmt.Errorf("IP<%v> already connected", ip),
    89  					isDuplicate: true,
    90  				}
    91  			}
    92  		}
    93  
    94  		return nil
    95  	}
    96  }
    97  
    98  // MultiplexTransportOption sets an optional parameter on the
    99  // MultiplexTransport.
   100  type MultiplexTransportOption func(*MultiplexTransport)
   101  
   102  // MultiplexTransportConnFilters sets the filters for rejection new connections.
   103  func MultiplexTransportConnFilters(
   104  	filters ...ConnFilterFunc,
   105  ) MultiplexTransportOption {
   106  	return func(mt *MultiplexTransport) { mt.connFilters = filters }
   107  }
   108  
   109  // MultiplexTransportFilterTimeout sets the timeout waited for filter calls to
   110  // return.
   111  func MultiplexTransportFilterTimeout(
   112  	timeout time.Duration,
   113  ) MultiplexTransportOption {
   114  	return func(mt *MultiplexTransport) { mt.filterTimeout = timeout }
   115  }
   116  
   117  // MultiplexTransportResolver sets the Resolver used for ip lokkups, defaults to
   118  // net.DefaultResolver.
   119  func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption {
   120  	return func(mt *MultiplexTransport) { mt.resolver = resolver }
   121  }
   122  
   123  // MultiplexTransport accepts and dials tcp connections and upgrades them to
   124  // multiplexed peers.
   125  type MultiplexTransport struct {
   126  	netAddr  NetAddress
   127  	listener net.Listener
   128  
   129  	acceptc chan accept
   130  	closec  chan struct{}
   131  
   132  	// Lookup table for duplicate ip and id checks.
   133  	conns       ConnSet
   134  	connFilters []ConnFilterFunc
   135  
   136  	dialTimeout      time.Duration
   137  	filterTimeout    time.Duration
   138  	handshakeTimeout time.Duration
   139  	nodeInfo         NodeInfo
   140  	nodeKey          NodeKey
   141  	resolver         IPResolver
   142  
   143  	// TODO(xla): This config is still needed as we parameterise peerConn and
   144  	// peer currently. All relevant configuration should be refactored into options
   145  	// with sane defaults.
   146  	mConfig conn.MConnConfig
   147  }
   148  
   149  // Test multiplexTransport for interface completeness.
   150  var _ Transport = (*MultiplexTransport)(nil)
   151  var _ transportLifecycle = (*MultiplexTransport)(nil)
   152  
   153  // NewMultiplexTransport returns a tcp connected multiplexed peer.
   154  func NewMultiplexTransport(
   155  	nodeInfo NodeInfo,
   156  	nodeKey NodeKey,
   157  	mConfig conn.MConnConfig,
   158  ) *MultiplexTransport {
   159  	return &MultiplexTransport{
   160  		acceptc:          make(chan accept),
   161  		closec:           make(chan struct{}),
   162  		dialTimeout:      defaultDialTimeout,
   163  		filterTimeout:    defaultFilterTimeout,
   164  		handshakeTimeout: defaultHandshakeTimeout,
   165  		mConfig:          mConfig,
   166  		nodeInfo:         nodeInfo,
   167  		nodeKey:          nodeKey,
   168  		conns:            NewConnSet(),
   169  		resolver:         net.DefaultResolver,
   170  	}
   171  }
   172  
   173  // NetAddress implements Transport.
   174  func (mt *MultiplexTransport) NetAddress() NetAddress {
   175  	return mt.netAddr
   176  }
   177  
   178  // Accept implements Transport.
   179  func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) {
   180  	select {
   181  	// This case should never have any side-effectful/blocking operations to
   182  	// ensure that quality peers are ready to be used.
   183  	case a := <-mt.acceptc:
   184  		if a.err != nil {
   185  			return nil, a.err
   186  		}
   187  
   188  		cfg.outbound = false
   189  
   190  		return mt.wrapPeer(a.conn, a.nodeInfo, cfg, a.netAddr), nil
   191  	case <-mt.closec:
   192  		return nil, ErrTransportClosed{}
   193  	}
   194  }
   195  
   196  // Dial implements Transport.
   197  func (mt *MultiplexTransport) Dial(
   198  	addr NetAddress,
   199  	cfg peerConfig,
   200  ) (Peer, error) {
   201  	c, err := addr.DialTimeout(mt.dialTimeout)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	// TODO(xla): Evaluate if we should apply filters if we explicitly dial.
   207  	if err := mt.filterConn(c); err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	secretConn, nodeInfo, err := mt.upgrade(c, &addr)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	cfg.outbound = true
   217  
   218  	p := mt.wrapPeer(secretConn, nodeInfo, cfg, &addr)
   219  
   220  	return p, nil
   221  }
   222  
   223  // Close implements transportLifecycle.
   224  func (mt *MultiplexTransport) Close() error {
   225  	close(mt.closec)
   226  
   227  	if mt.listener != nil {
   228  		return mt.listener.Close()
   229  	}
   230  
   231  	return nil
   232  }
   233  
   234  // Listen implements transportLifecycle.
   235  func (mt *MultiplexTransport) Listen(addr NetAddress) error {
   236  	ln, err := net.Listen("tcp", addr.DialString())
   237  	if err != nil {
   238  		return err
   239  	}
   240  
   241  	mt.netAddr = addr
   242  	mt.listener = ln
   243  
   244  	go mt.acceptPeers()
   245  
   246  	return nil
   247  }
   248  
   249  func (mt *MultiplexTransport) acceptPeers() {
   250  	for {
   251  		c, err := mt.listener.Accept()
   252  		if err != nil {
   253  			// If Close() has been called, silently exit.
   254  			select {
   255  			case _, ok := <-mt.closec:
   256  				if !ok {
   257  					return
   258  				}
   259  			default:
   260  				// Transport is not closed
   261  			}
   262  
   263  			mt.acceptc <- accept{err: err}
   264  			return
   265  		}
   266  
   267  		// Connection upgrade and filtering should be asynchronous to avoid
   268  		// Head-of-line blocking[0].
   269  		// Reference:  https://github.com/evdatsion/aphelion-dpos-bft/issues/2047
   270  		//
   271  		// [0] https://en.wikipedia.org/wiki/Head-of-line_blocking
   272  		go func(c net.Conn) {
   273  			var (
   274  				nodeInfo   NodeInfo
   275  				secretConn *conn.SecretConnection
   276  				netAddr    *NetAddress
   277  			)
   278  
   279  			err := mt.filterConn(c)
   280  			if err == nil {
   281  				secretConn, nodeInfo, err = mt.upgrade(c, nil)
   282  				if err == nil {
   283  					addr := c.RemoteAddr()
   284  					id := PubKeyToID(secretConn.RemotePubKey())
   285  					netAddr = NewNetAddress(id, addr)
   286  				}
   287  			}
   288  
   289  			select {
   290  			case mt.acceptc <- accept{netAddr, secretConn, nodeInfo, err}:
   291  				// Make the upgraded peer available.
   292  			case <-mt.closec:
   293  				// Give up if the transport was closed.
   294  				_ = c.Close()
   295  				return
   296  			}
   297  		}(c)
   298  	}
   299  }
   300  
   301  // Cleanup removes the given address from the connections set and
   302  // closes the connection.
   303  func (mt *MultiplexTransport) Cleanup(p Peer) {
   304  	mt.conns.RemoveAddr(p.RemoteAddr())
   305  	_ = p.CloseConn()
   306  }
   307  
   308  func (mt *MultiplexTransport) cleanup(c net.Conn) error {
   309  	mt.conns.Remove(c)
   310  
   311  	return c.Close()
   312  }
   313  
   314  func (mt *MultiplexTransport) filterConn(c net.Conn) (err error) {
   315  	defer func() {
   316  		if err != nil {
   317  			_ = c.Close()
   318  		}
   319  	}()
   320  
   321  	// Reject if connection is already present.
   322  	if mt.conns.Has(c) {
   323  		return ErrRejected{conn: c, isDuplicate: true}
   324  	}
   325  
   326  	// Resolve ips for incoming conn.
   327  	ips, err := resolveIPs(mt.resolver, c)
   328  	if err != nil {
   329  		return err
   330  	}
   331  
   332  	errc := make(chan error, len(mt.connFilters))
   333  
   334  	for _, f := range mt.connFilters {
   335  		go func(f ConnFilterFunc, c net.Conn, ips []net.IP, errc chan<- error) {
   336  			errc <- f(mt.conns, c, ips)
   337  		}(f, c, ips, errc)
   338  	}
   339  
   340  	for i := 0; i < cap(errc); i++ {
   341  		select {
   342  		case err := <-errc:
   343  			if err != nil {
   344  				return ErrRejected{conn: c, err: err, isFiltered: true}
   345  			}
   346  		case <-time.After(mt.filterTimeout):
   347  			return ErrFilterTimeout{}
   348  		}
   349  
   350  	}
   351  
   352  	mt.conns.Set(c, ips)
   353  
   354  	return nil
   355  }
   356  
   357  func (mt *MultiplexTransport) upgrade(
   358  	c net.Conn,
   359  	dialedAddr *NetAddress,
   360  ) (secretConn *conn.SecretConnection, nodeInfo NodeInfo, err error) {
   361  	defer func() {
   362  		if err != nil {
   363  			_ = mt.cleanup(c)
   364  		}
   365  	}()
   366  
   367  	secretConn, err = upgradeSecretConn(c, mt.handshakeTimeout, mt.nodeKey.PrivKey)
   368  	if err != nil {
   369  		return nil, nil, ErrRejected{
   370  			conn:          c,
   371  			err:           fmt.Errorf("secret conn failed: %v", err),
   372  			isAuthFailure: true,
   373  		}
   374  	}
   375  
   376  	// For outgoing conns, ensure connection key matches dialed key.
   377  	connID := PubKeyToID(secretConn.RemotePubKey())
   378  	if dialedAddr != nil {
   379  		if dialedID := dialedAddr.ID; connID != dialedID {
   380  			return nil, nil, ErrRejected{
   381  				conn: c,
   382  				id:   connID,
   383  				err: fmt.Errorf(
   384  					"conn.ID (%v) dialed ID (%v) mismatch",
   385  					connID,
   386  					dialedID,
   387  				),
   388  				isAuthFailure: true,
   389  			}
   390  		}
   391  	}
   392  
   393  	nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo)
   394  	if err != nil {
   395  		return nil, nil, ErrRejected{
   396  			conn:          c,
   397  			err:           fmt.Errorf("handshake failed: %v", err),
   398  			isAuthFailure: true,
   399  		}
   400  	}
   401  
   402  	if err := nodeInfo.Validate(); err != nil {
   403  		return nil, nil, ErrRejected{
   404  			conn:              c,
   405  			err:               err,
   406  			isNodeInfoInvalid: true,
   407  		}
   408  	}
   409  
   410  	// Ensure connection key matches self reported key.
   411  	if connID != nodeInfo.ID() {
   412  		return nil, nil, ErrRejected{
   413  			conn: c,
   414  			id:   connID,
   415  			err: fmt.Errorf(
   416  				"conn.ID (%v) NodeInfo.ID (%v) mismatch",
   417  				connID,
   418  				nodeInfo.ID(),
   419  			),
   420  			isAuthFailure: true,
   421  		}
   422  	}
   423  
   424  	// Reject self.
   425  	if mt.nodeInfo.ID() == nodeInfo.ID() {
   426  		return nil, nil, ErrRejected{
   427  			addr:   *NewNetAddress(nodeInfo.ID(), c.RemoteAddr()),
   428  			conn:   c,
   429  			id:     nodeInfo.ID(),
   430  			isSelf: true,
   431  		}
   432  	}
   433  
   434  	if err := mt.nodeInfo.CompatibleWith(nodeInfo); err != nil {
   435  		return nil, nil, ErrRejected{
   436  			conn:           c,
   437  			err:            err,
   438  			id:             nodeInfo.ID(),
   439  			isIncompatible: true,
   440  		}
   441  	}
   442  
   443  	return secretConn, nodeInfo, nil
   444  }
   445  
   446  func (mt *MultiplexTransport) wrapPeer(
   447  	c net.Conn,
   448  	ni NodeInfo,
   449  	cfg peerConfig,
   450  	socketAddr *NetAddress,
   451  ) Peer {
   452  
   453  	persistent := false
   454  	if cfg.isPersistent != nil {
   455  		if cfg.outbound {
   456  			persistent = cfg.isPersistent(socketAddr)
   457  		} else {
   458  			selfReportedAddr, err := ni.NetAddress()
   459  			if err == nil {
   460  				persistent = cfg.isPersistent(selfReportedAddr)
   461  			}
   462  		}
   463  	}
   464  
   465  	peerConn := newPeerConn(
   466  		cfg.outbound,
   467  		persistent,
   468  		c,
   469  		socketAddr,
   470  	)
   471  
   472  	p := newPeer(
   473  		peerConn,
   474  		mt.mConfig,
   475  		ni,
   476  		cfg.reactorsByCh,
   477  		cfg.chDescs,
   478  		cfg.onPeerError,
   479  		PeerMetrics(cfg.metrics),
   480  	)
   481  
   482  	return p
   483  }
   484  
   485  func handshake(
   486  	c net.Conn,
   487  	timeout time.Duration,
   488  	nodeInfo NodeInfo,
   489  ) (NodeInfo, error) {
   490  	if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	var (
   495  		errc = make(chan error, 2)
   496  
   497  		peerNodeInfo DefaultNodeInfo
   498  		ourNodeInfo  = nodeInfo.(DefaultNodeInfo)
   499  	)
   500  
   501  	go func(errc chan<- error, c net.Conn) {
   502  		_, err := cdc.MarshalBinaryLengthPrefixedWriter(c, ourNodeInfo)
   503  		errc <- err
   504  	}(errc, c)
   505  	go func(errc chan<- error, c net.Conn) {
   506  		_, err := cdc.UnmarshalBinaryLengthPrefixedReader(
   507  			c,
   508  			&peerNodeInfo,
   509  			int64(MaxNodeInfoSize()),
   510  		)
   511  		errc <- err
   512  	}(errc, c)
   513  
   514  	for i := 0; i < cap(errc); i++ {
   515  		err := <-errc
   516  		if err != nil {
   517  			return nil, err
   518  		}
   519  	}
   520  
   521  	return peerNodeInfo, c.SetDeadline(time.Time{})
   522  }
   523  
   524  func upgradeSecretConn(
   525  	c net.Conn,
   526  	timeout time.Duration,
   527  	privKey crypto.PrivKey,
   528  ) (*conn.SecretConnection, error) {
   529  	if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
   530  		return nil, err
   531  	}
   532  
   533  	sc, err := conn.MakeSecretConnection(c, privKey)
   534  	if err != nil {
   535  		return nil, err
   536  	}
   537  
   538  	return sc, sc.SetDeadline(time.Time{})
   539  }
   540  
   541  func resolveIPs(resolver IPResolver, c net.Conn) ([]net.IP, error) {
   542  	host, _, err := net.SplitHostPort(c.RemoteAddr().String())
   543  	if err != nil {
   544  		return nil, err
   545  	}
   546  
   547  	addrs, err := resolver.LookupIPAddr(context.Background(), host)
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  
   552  	ips := []net.IP{}
   553  
   554  	for _, addr := range addrs {
   555  		ips = append(ips, addr.IP)
   556  	}
   557  
   558  	return ips, nil
   559  }