github.com/NebulousLabs/Sia@v1.3.7/modules/gateway/ip.go (about)

     1  package gateway
     2  
     3  import (
     4  	"net"
     5  	"time"
     6  
     7  	"github.com/NebulousLabs/Sia/encoding"
     8  	"github.com/NebulousLabs/Sia/modules"
     9  	"github.com/NebulousLabs/errors"
    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. In the worst case managedIPFromPeers will fail after a few minutes.
    27  func (g *Gateway) managedIPFromPeers() (string, error) {
    28  	// Stop after timeoutIPDiscovery time.
    29  	timeout := time.After(timeoutIPDiscovery)
    30  	for {
    31  		// Check for shutdown signal or timeout.
    32  		select {
    33  		case <-g.peerTG.StopChan():
    34  			return "", errors.New("interrupted by shutdown")
    35  		case <-timeout:
    36  			return "", errors.New("failed to discover ip in time")
    37  		default:
    38  		}
    39  		// Get peers
    40  		peers := g.Peers()
    41  		// Check if there are enough peers. Otherwise wait.
    42  		if len(peers) < minPeersForIPDiscovery {
    43  			g.managedSleep(peerDiscoveryRetryInterval)
    44  			continue
    45  		}
    46  		// Ask all the peers about our ip in parallel
    47  		returnChan := make(chan string)
    48  		for _, peer := range peers {
    49  			go g.RPC(peer.NetAddress, "DiscoverIP", func(conn modules.PeerConn) error {
    50  				var address string
    51  				err := encoding.ReadObject(conn, &address, 100)
    52  				if err != nil {
    53  					returnChan <- ""
    54  					g.log.Debugf("DEBUG: failed to receive ip address: %v", err)
    55  					return err
    56  				}
    57  				addr := net.ParseIP(address)
    58  				if addr == nil {
    59  					returnChan <- ""
    60  					g.log.Debug("DEBUG: failed to parse ip address")
    61  					return errors.New("failed to parse ip address")
    62  				}
    63  				returnChan <- addr.String()
    64  				return err
    65  			})
    66  		}
    67  		// Wait for their responses
    68  		addresses := make(map[string]int)
    69  		successfulResponses := 0
    70  		for i := 0; i < len(peers); i++ {
    71  			addr := <-returnChan
    72  			if addr != "" {
    73  				addresses[addr]++
    74  				successfulResponses++
    75  			}
    76  		}
    77  		// If there haven't been enough successful responses we wait some time.
    78  		if successfulResponses < minPeersForIPDiscovery {
    79  			g.managedSleep(peerDiscoveryRetryInterval)
    80  			continue
    81  		}
    82  		// If an address was returned by more than half the peers we consider
    83  		// it valid.
    84  		for addr, count := range addresses {
    85  			if count > successfulResponses/2 {
    86  				g.log.Println("ip successfully discovered using peers:", addr)
    87  				return addr, nil
    88  			}
    89  		}
    90  		// Otherwise we wait before trying again.
    91  		g.managedSleep(peerDiscoveryRetryInterval)
    92  	}
    93  }