gitlab.com/jokerrs1/Sia@v1.3.2/modules/gateway/peers.go (about)

     1  package gateway
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/NebulousLabs/Sia/build"
    10  	"github.com/NebulousLabs/Sia/encoding"
    11  	"github.com/NebulousLabs/Sia/modules"
    12  	"github.com/NebulousLabs/Sia/types"
    13  	"github.com/NebulousLabs/fastrand"
    14  )
    15  
    16  var (
    17  	errPeerExists       = errors.New("already connected to this peer")
    18  	errPeerRejectedConn = errors.New("peer rejected connection")
    19  )
    20  
    21  // insufficientVersionError indicates a peer's version is insufficient.
    22  type insufficientVersionError string
    23  
    24  // Error implements the error interface for insufficientVersionError.
    25  func (s insufficientVersionError) Error() string {
    26  	return "unacceptable version: " + string(s)
    27  }
    28  
    29  // invalidVersionError indicates a peer's version is not a valid version number.
    30  type invalidVersionError string
    31  
    32  // Error implements the error interface for invalidVersionError.
    33  func (s invalidVersionError) Error() string {
    34  	return "invalid version: " + string(s)
    35  }
    36  
    37  type peer struct {
    38  	modules.Peer
    39  	sess streamSession
    40  }
    41  
    42  // sessionHeader is sent after the initial version exchange. It prevents peers
    43  // on different blockchains from connecting to each other, and prevents the
    44  // gateway from connecting to itself.
    45  type sessionHeader struct {
    46  	GenesisID  types.BlockID
    47  	UniqueID   gatewayID
    48  	NetAddress modules.NetAddress
    49  }
    50  
    51  func (p *peer) open() (modules.PeerConn, error) {
    52  	conn, err := p.sess.Open()
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	return &peerConn{conn, p.NetAddress}, nil
    57  }
    58  
    59  func (p *peer) accept() (modules.PeerConn, error) {
    60  	conn, err := p.sess.Accept()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return &peerConn{conn, p.NetAddress}, nil
    65  }
    66  
    67  // addPeer adds a peer to the Gateway's peer list, spawns a listener thread to
    68  // handle its requests and increments the remotePeers accordingly
    69  func (g *Gateway) addPeer(p *peer) {
    70  	g.peers[p.NetAddress] = p
    71  	go g.threadedListenPeer(p)
    72  }
    73  
    74  // randomOutboundPeer returns a random outbound peer.
    75  func (g *Gateway) randomOutboundPeer() (modules.NetAddress, error) {
    76  	// Get the list of outbound peers.
    77  	var addrs []modules.NetAddress
    78  	for addr, peer := range g.peers {
    79  		if peer.Inbound {
    80  			continue
    81  		}
    82  		addrs = append(addrs, addr)
    83  	}
    84  	if len(addrs) == 0 {
    85  		return "", errNoPeers
    86  	}
    87  
    88  	// Of the remaining options, select one at random.
    89  	return addrs[fastrand.Intn(len(addrs))], nil
    90  }
    91  
    92  // permanentListen handles incoming connection requests. If the connection is
    93  // accepted, the peer will be added to the Gateway's peer list.
    94  func (g *Gateway) permanentListen(closeChan chan struct{}) {
    95  	// Signal that the permanentListen thread has completed upon returning.
    96  	defer close(closeChan)
    97  
    98  	for {
    99  		conn, err := g.listener.Accept()
   100  		if err != nil {
   101  			g.log.Debugln("[PL] Closing permanentListen:", err)
   102  			return
   103  		}
   104  
   105  		go g.threadedAcceptConn(conn)
   106  
   107  		// Sleep after each accept. This limits the rate at which the Gateway
   108  		// will accept new connections. The intent here is to prevent new
   109  		// incoming connections from kicking out old ones before they have a
   110  		// chance to request additional nodes.
   111  		select {
   112  		case <-time.After(acceptInterval):
   113  		case <-g.threads.StopChan():
   114  			return
   115  		}
   116  	}
   117  }
   118  
   119  // threadedAcceptConn adds a connecting node as a peer.
   120  func (g *Gateway) threadedAcceptConn(conn net.Conn) {
   121  	if g.threads.Add() != nil {
   122  		conn.Close()
   123  		return
   124  	}
   125  	defer g.threads.Done()
   126  	conn.SetDeadline(time.Now().Add(connStdDeadline))
   127  
   128  	addr := modules.NetAddress(conn.RemoteAddr().String())
   129  	g.log.Debugf("INFO: %v wants to connect", addr)
   130  
   131  	remoteVersion, err := acceptVersionHandshake(conn, build.Version)
   132  	if err != nil {
   133  		g.log.Debugf("INFO: %v wanted to connect but version handshake failed: %v", addr, err)
   134  		conn.Close()
   135  		return
   136  	}
   137  
   138  	if build.VersionCmp(remoteVersion, minimumAcceptablePeerVersion) >= 0 {
   139  		err = g.managedAcceptConnPeer(conn, remoteVersion)
   140  	} else {
   141  		err = errors.New("version number is below threshold")
   142  	}
   143  	if err != nil {
   144  		g.log.Debugf("INFO: %v wanted to connect, but failed: %v", addr, err)
   145  		conn.Close()
   146  		return
   147  	}
   148  
   149  	// Handshake successful, remove the deadline.
   150  	conn.SetDeadline(time.Time{})
   151  
   152  	g.log.Debugf("INFO: accepted connection from new peer %v (v%v)", addr, remoteVersion)
   153  }
   154  
   155  // acceptableSessionHeader returns an error if remoteHeader indicates a peer
   156  // that should not be connected to.
   157  func acceptableSessionHeader(ourHeader, remoteHeader sessionHeader, remoteAddr string) error {
   158  	if remoteHeader.GenesisID != ourHeader.GenesisID {
   159  		return errPeerGenesisID
   160  	} else if remoteHeader.UniqueID == ourHeader.UniqueID {
   161  		return errOurAddress
   162  	} else if err := remoteHeader.NetAddress.IsStdValid(); err != nil {
   163  		return fmt.Errorf("invalid remote address: %v", err)
   164  	}
   165  	return nil
   166  }
   167  
   168  // managedAcceptConnPeer accepts connection requests from peers >= v1.3.1.
   169  // The requesting peer is added as a node and a peer. The peer is only added if
   170  // a nil error is returned.
   171  func (g *Gateway) managedAcceptConnPeer(conn net.Conn, remoteVersion string) error {
   172  	g.log.Debugln("Sending sessionHeader with address", g.myAddr, g.myAddr.IsLocal())
   173  	// Perform header handshake.
   174  	g.mu.RLock()
   175  	ourHeader := sessionHeader{
   176  		GenesisID:  types.GenesisID,
   177  		UniqueID:   g.id,
   178  		NetAddress: g.myAddr,
   179  	}
   180  	g.mu.RUnlock()
   181  
   182  	remoteHeader, err := exchangeRemoteHeader(conn, ourHeader)
   183  	if err != nil {
   184  		return err
   185  	}
   186  	if err := exchangeOurHeader(conn, ourHeader); err != nil {
   187  		return err
   188  	}
   189  
   190  	// Get the remote address on which the connecting peer is listening on.
   191  	// This means we need to combine the incoming connections ip address with
   192  	// the announced open port of the peer.
   193  	remoteIP := modules.NetAddress(conn.RemoteAddr().String()).Host()
   194  	remotePort := remoteHeader.NetAddress.Port()
   195  	remoteAddr := modules.NetAddress(net.JoinHostPort(remoteIP, remotePort))
   196  
   197  	// Accept the peer.
   198  	peer := &peer{
   199  		Peer: modules.Peer{
   200  			Inbound: true,
   201  			// NOTE: local may be true even if the supplied NetAddress is not
   202  			// actually reachable.
   203  			Local: remoteAddr.IsLocal(),
   204  			// Ignoring claimed IP address (which should be == to the socket address)
   205  			// by the host but keeping note of the port number so we can call back
   206  			NetAddress: remoteAddr,
   207  			Version:    remoteVersion,
   208  		},
   209  		sess: newServerStream(conn, remoteVersion),
   210  	}
   211  	g.mu.Lock()
   212  	g.acceptPeer(peer)
   213  	g.mu.Unlock()
   214  
   215  	// Attempt to ping the supplied address. If successful, we will add
   216  	// remoteHeader.NetAddress to our node list after accepting the peer. We
   217  	// do this in a goroutine so that we can begin communicating with the peer
   218  	// immediately.
   219  	go func() {
   220  		err := g.pingNode(remoteAddr)
   221  		if err == nil {
   222  			g.mu.Lock()
   223  			g.addNode(remoteAddr)
   224  			g.mu.Unlock()
   225  		}
   226  	}()
   227  
   228  	return nil
   229  }
   230  
   231  // acceptPeer makes room for the peer if necessary by kicking out existing
   232  // peers, then adds the peer to the peer list.
   233  func (g *Gateway) acceptPeer(p *peer) {
   234  	// If we are not fully connected, add the peer without kicking any out.
   235  	if len(g.peers) < fullyConnectedThreshold {
   236  		g.addPeer(p)
   237  		return
   238  	}
   239  
   240  	// Select a peer to kick. Outbound peers and local peers are not
   241  	// available to be kicked.
   242  	var addrs []modules.NetAddress
   243  	for addr, peer := range g.peers {
   244  		// Do not kick outbound peers or local peers.
   245  		if !peer.Inbound || peer.Local {
   246  			continue
   247  		}
   248  
   249  		// Prefer kicking a peer with the same hostname.
   250  		if addr.Host() == p.NetAddress.Host() {
   251  			addrs = []modules.NetAddress{addr}
   252  			break
   253  		}
   254  		addrs = append(addrs, addr)
   255  	}
   256  	if len(addrs) == 0 {
   257  		// There is nobody suitable to kick, therefore do not kick anyone.
   258  		g.addPeer(p)
   259  		return
   260  	}
   261  
   262  	// Of the remaining options, select one at random.
   263  	kick := addrs[fastrand.Intn(len(addrs))]
   264  
   265  	g.peers[kick].sess.Close()
   266  	delete(g.peers, kick)
   267  	g.log.Printf("INFO: disconnected from %v to make room for %v\n", kick, p.NetAddress)
   268  	g.addPeer(p)
   269  }
   270  
   271  // acceptableVersion returns an error if the version is unacceptable.
   272  func acceptableVersion(version string) error {
   273  	if !build.IsVersion(version) {
   274  		return invalidVersionError(version)
   275  	}
   276  	if build.VersionCmp(version, minAcceptableVersion) < 0 {
   277  		return insufficientVersionError(version)
   278  	}
   279  	return nil
   280  }
   281  
   282  // connectVersionHandshake performs the version handshake and should be called
   283  // on the side making the connection request. The remote version is only
   284  // returned if err == nil.
   285  func connectVersionHandshake(conn net.Conn, version string) (remoteVersion string, err error) {
   286  	// Send our version.
   287  	if err := encoding.WriteObject(conn, version); err != nil {
   288  		return "", fmt.Errorf("failed to write version: %v", err)
   289  	}
   290  	// Read remote version.
   291  	if err := encoding.ReadObject(conn, &remoteVersion, build.MaxEncodedVersionLength); err != nil {
   292  		return "", fmt.Errorf("failed to read remote version: %v", err)
   293  	}
   294  	// Check that their version is acceptable.
   295  	if remoteVersion == "reject" {
   296  		return "", errPeerRejectedConn
   297  	}
   298  	if err := acceptableVersion(remoteVersion); err != nil {
   299  		return "", err
   300  	}
   301  	return remoteVersion, nil
   302  }
   303  
   304  // acceptVersionHandshake performs the version handshake and should be
   305  // called on the side accepting a connection request. The remote version is
   306  // only returned if err == nil.
   307  func acceptVersionHandshake(conn net.Conn, version string) (remoteVersion string, err error) {
   308  	// Read remote version.
   309  	if err := encoding.ReadObject(conn, &remoteVersion, build.MaxEncodedVersionLength); err != nil {
   310  		return "", fmt.Errorf("failed to read remote version: %v", err)
   311  	}
   312  	// Check that their version is acceptable.
   313  	if err := acceptableVersion(remoteVersion); err != nil {
   314  		if err := encoding.WriteObject(conn, "reject"); err != nil {
   315  			return "", fmt.Errorf("failed to write reject: %v", err)
   316  		}
   317  		return "", err
   318  	}
   319  	// Send our version.
   320  	if err := encoding.WriteObject(conn, version); err != nil {
   321  		return "", fmt.Errorf("failed to write version: %v", err)
   322  	}
   323  	return remoteVersion, nil
   324  }
   325  
   326  // exchangeOurHeader writes ourHeader and reads the remote's error response.
   327  func exchangeOurHeader(conn net.Conn, ourHeader sessionHeader) error {
   328  	// Send our header.
   329  	if err := encoding.WriteObject(conn, ourHeader); err != nil {
   330  		return fmt.Errorf("failed to write header: %v", err)
   331  	}
   332  
   333  	// Read remote response.
   334  	var response string
   335  	if err := encoding.ReadObject(conn, &response, 100); err != nil {
   336  		return fmt.Errorf("failed to read header acceptance: %v", err)
   337  	} else if response == modules.StopResponse {
   338  		return errors.New("peer did not want a connection")
   339  	} else if response != modules.AcceptResponse {
   340  		return fmt.Errorf("peer rejected our header: %v", response)
   341  	}
   342  	return nil
   343  }
   344  
   345  // exchangeRemoteHeader reads the remote header and writes an error response.
   346  func exchangeRemoteHeader(conn net.Conn, ourHeader sessionHeader) (sessionHeader, error) {
   347  	// Read remote header.
   348  	var remoteHeader sessionHeader
   349  	if err := encoding.ReadObject(conn, &remoteHeader, maxEncodedSessionHeaderSize); err != nil {
   350  		return sessionHeader{}, fmt.Errorf("failed to read remote header: %v", err)
   351  	}
   352  
   353  	// Validate remote header and write acceptance or rejection.
   354  	err := acceptableSessionHeader(ourHeader, remoteHeader, conn.RemoteAddr().String())
   355  	if err != nil {
   356  		encoding.WriteObject(conn, err.Error()) // error can be ignored
   357  		return sessionHeader{}, fmt.Errorf("peer's header was not acceptable: %v", err)
   358  	} else if err := encoding.WriteObject(conn, modules.AcceptResponse); err != nil {
   359  		return sessionHeader{}, fmt.Errorf("failed to write header acceptance: %v", err)
   360  	}
   361  
   362  	return remoteHeader, nil
   363  }
   364  
   365  // managedConnectPeer connects to peers >= v1.3.1. The peer is added as a
   366  // node and a peer. The peer is only added if a nil error is returned.
   367  func (g *Gateway) managedConnectPeer(conn net.Conn, remoteVersion string, remoteAddr modules.NetAddress) error {
   368  	g.log.Debugln("Sending sessionHeader with address", g.myAddr, g.myAddr.IsLocal())
   369  	// Perform header handshake.
   370  	g.mu.RLock()
   371  	ourHeader := sessionHeader{
   372  		GenesisID:  types.GenesisID,
   373  		UniqueID:   g.id,
   374  		NetAddress: g.myAddr,
   375  	}
   376  	g.mu.RUnlock()
   377  
   378  	if err := exchangeOurHeader(conn, ourHeader); err != nil {
   379  		return err
   380  	} else if _, err := exchangeRemoteHeader(conn, ourHeader); err != nil {
   381  		return err
   382  	}
   383  	return nil
   384  }
   385  
   386  // managedConnect establishes a persistent connection to a peer, and adds it to
   387  // the Gateway's peer list.
   388  func (g *Gateway) managedConnect(addr modules.NetAddress) error {
   389  	// Perform verification on the input address.
   390  	g.mu.RLock()
   391  	gaddr := g.myAddr
   392  	g.mu.RUnlock()
   393  	if addr == gaddr {
   394  		return errors.New("can't connect to our own address")
   395  	}
   396  	if err := addr.IsStdValid(); err != nil {
   397  		return errors.New("can't connect to invalid address")
   398  	}
   399  	if net.ParseIP(addr.Host()) == nil {
   400  		return errors.New("address must be an IP address")
   401  	}
   402  	g.mu.RLock()
   403  	_, exists := g.peers[addr]
   404  	g.mu.RUnlock()
   405  	if exists {
   406  		return errPeerExists
   407  	}
   408  
   409  	// Dial the peer and perform peer initialization.
   410  	conn, err := g.dial(addr)
   411  	if err != nil {
   412  		return err
   413  	}
   414  
   415  	// Perform peer initialization.
   416  	remoteVersion, err := connectVersionHandshake(conn, build.Version)
   417  	if err != nil {
   418  		conn.Close()
   419  		return err
   420  	}
   421  
   422  	if build.VersionCmp(remoteVersion, minimumAcceptablePeerVersion) >= 0 {
   423  		err = g.managedConnectPeer(conn, remoteVersion, addr)
   424  	} else {
   425  		err = errors.New("version number is below threshold")
   426  	}
   427  	if err != nil {
   428  		conn.Close()
   429  		return err
   430  	}
   431  
   432  	// Connection successful, clear the timeout as to maintain a persistent
   433  	// connection to this peer.
   434  	conn.SetDeadline(time.Time{})
   435  
   436  	// Add the peer.
   437  	g.mu.Lock()
   438  	defer g.mu.Unlock()
   439  
   440  	g.addPeer(&peer{
   441  		Peer: modules.Peer{
   442  			Inbound:    false,
   443  			Local:      addr.IsLocal(),
   444  			NetAddress: addr,
   445  			Version:    remoteVersion,
   446  		},
   447  		sess: newClientStream(conn, remoteVersion),
   448  	})
   449  	g.addNode(addr)
   450  	g.nodes[addr].WasOutboundPeer = true
   451  
   452  	if err := g.saveSync(); err != nil {
   453  		g.log.Println("ERROR: Unable to save new outbound peer to gateway:", err)
   454  	}
   455  
   456  	g.log.Debugln("INFO: connected to new peer", addr)
   457  
   458  	// call initRPCs
   459  	for name, fn := range g.initRPCs {
   460  		go func(name string, fn modules.RPCFunc) {
   461  			if g.threads.Add() != nil {
   462  				return
   463  			}
   464  			defer g.threads.Done()
   465  
   466  			err := g.managedRPC(addr, name, fn)
   467  			if err != nil {
   468  				g.log.Debugf("INFO: RPC %q on peer %q failed: %v", name, addr, err)
   469  			}
   470  		}(name, fn)
   471  	}
   472  
   473  	return nil
   474  }
   475  
   476  // Connect establishes a persistent connection to a peer, and adds it to the
   477  // Gateway's peer list.
   478  func (g *Gateway) Connect(addr modules.NetAddress) error {
   479  	if err := g.threads.Add(); err != nil {
   480  		return err
   481  	}
   482  	defer g.threads.Done()
   483  	return g.managedConnect(addr)
   484  }
   485  
   486  // Disconnect terminates a connection to a peer and removes it from the
   487  // Gateway's peer list. The peer's address remains in the node list.
   488  func (g *Gateway) Disconnect(addr modules.NetAddress) error {
   489  	if err := g.threads.Add(); err != nil {
   490  		return err
   491  	}
   492  	defer g.threads.Done()
   493  
   494  	g.mu.RLock()
   495  	p, exists := g.peers[addr]
   496  	g.mu.RUnlock()
   497  	if !exists {
   498  		return errors.New("not connected to that node")
   499  	}
   500  
   501  	p.sess.Close()
   502  	g.mu.Lock()
   503  	// Peer is removed from the peer list as well as the node list, to prevent
   504  	// the node from being re-connected while looking for a replacement peer.
   505  	delete(g.peers, addr)
   506  	delete(g.nodes, addr)
   507  	g.mu.Unlock()
   508  
   509  	g.log.Println("INFO: disconnected from peer", addr)
   510  	return nil
   511  }
   512  
   513  // Peers returns the addresses currently connected to the Gateway.
   514  func (g *Gateway) Peers() []modules.Peer {
   515  	g.mu.RLock()
   516  	defer g.mu.RUnlock()
   517  	var peers []modules.Peer
   518  	for _, p := range g.peers {
   519  		peers = append(peers, p.Peer)
   520  	}
   521  	return peers
   522  }
   523  
   524  // Online returns true if the node is connected to the internet. During testing
   525  // we always assume that the node is online
   526  func (g *Gateway) Online() bool {
   527  	if build.Release == "dev" || build.Release == "testing" {
   528  		return true
   529  	}
   530  
   531  	g.mu.RLock()
   532  	defer g.mu.RUnlock()
   533  	for _, p := range g.peers {
   534  		if !p.Local {
   535  			return true
   536  		}
   537  	}
   538  	return false
   539  }