github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/transport.go (about)

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