gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/gateway/ip.go (about)

     1  package gateway
     2  
     3  import (
     4  	"net"
     5  	"time"
     6  
     7  	"gitlab.com/NebulousLabs/errors"
     8  	"gitlab.com/SiaPrime/SiaPrime/encoding"
     9  	"gitlab.com/SiaPrime/SiaPrime/modules"
    10  )
    11  
    12  // discoverPeerIP is the handler for the discoverPeer RPC. It returns the
    13  // public ip of the caller back to the caller. This allows for peer-to-peer ip
    14  // discovery without centralized services.
    15  func (g *Gateway) discoverPeerIP(conn modules.PeerConn) error {
    16  	conn.SetDeadline(time.Now().Add(connStdDeadline))
    17  	host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
    18  	if err != nil {
    19  		return errors.AddContext(err, "failed to split host from port")
    20  	}
    21  	return encoding.WriteObject(conn, host)
    22  }
    23  
    24  // managedIPFromPeers asks the peers the node is connected to for the node's
    25  // public ip address. If not enough peers are available we wait a bit and try
    26  // again. If no cancelation channel is provided, managedIPFromPeers will time
    27  // out after timeoutIPDiscovery time. Otherwise it will time out when cancel is
    28  // closed. The method might return with a short delay of
    29  // peerDiscoveryRetryInterval.
    30  func (g *Gateway) managedIPFromPeers(cancel <-chan struct{}) (string, error) {
    31  	// Choose default if cancel is nil.
    32  	var timeout <-chan time.Time
    33  	if cancel == nil {
    34  		timer := time.NewTimer(timeoutIPDiscovery)
    35  		defer timer.Stop()
    36  		timeout = timer.C
    37  	}
    38  	for {
    39  		// Check for shutdown signal or timeout.
    40  		select {
    41  		case <-g.peerTG.StopChan():
    42  			return "", errors.New("interrupted by shutdown")
    43  		case <-timeout:
    44  			return "", errors.New("failed to discover ip in time")
    45  		case <-cancel:
    46  			return "", errors.New("failed to discover ip in time")
    47  		default:
    48  		}
    49  		// Get peers
    50  		peers := g.Peers()
    51  		// Check if there are enough peers. Otherwise wait.
    52  		if len(peers) < minPeersForIPDiscovery {
    53  			g.managedSleep(peerDiscoveryRetryInterval)
    54  			continue
    55  		}
    56  		// Ask all the peers about our ip in parallel
    57  		returnChan := make(chan string)
    58  		for _, peer := range peers {
    59  			go g.RPC(peer.NetAddress, "DiscoverIP", func(conn modules.PeerConn) error {
    60  				var address string
    61  				err := encoding.ReadObject(conn, &address, 100)
    62  				if err != nil {
    63  					returnChan <- ""
    64  					g.log.Debugf("DEBUG: failed to receive ip address: %v", err)
    65  					return err
    66  				}
    67  				addr := net.ParseIP(address)
    68  				if addr == nil {
    69  					returnChan <- ""
    70  					g.log.Debug("DEBUG: failed to parse ip address")
    71  					return errors.New("failed to parse ip address")
    72  				}
    73  				returnChan <- addr.String()
    74  				return err
    75  			})
    76  		}
    77  		// Wait for their responses
    78  		addresses := make(map[string]int)
    79  		successfulResponses := 0
    80  		for i := 0; i < len(peers); i++ {
    81  			addr := <-returnChan
    82  			if addr != "" {
    83  				addresses[addr]++
    84  				successfulResponses++
    85  			}
    86  		}
    87  		// If there haven't been enough successful responses we wait some time.
    88  		if successfulResponses < minPeersForIPDiscovery {
    89  			g.managedSleep(peerDiscoveryRetryInterval)
    90  			continue
    91  		}
    92  		// If an address was returned by more than half the peers we consider
    93  		// it valid.
    94  		for addr, count := range addresses {
    95  			if count > successfulResponses/2 {
    96  				g.log.Println("ip successfully discovered using peers:", addr)
    97  				return addr, nil
    98  			}
    99  		}
   100  		// Otherwise we wait before trying again.
   101  		g.managedSleep(peerDiscoveryRetryInterval)
   102  	}
   103  }