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