github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/gateway/ip.go (about)

     1  package gateway
     2  
     3  import (
     4  	"net"
     5  	"time"
     6  
     7  	"github.com/Synthesix/Sia/encoding"
     8  	"github.com/Synthesix/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  		g.mu.RLock()
    41  		peers := g.Peers()
    42  		g.mu.RUnlock()
    43  		// Check if there are enough peers. Otherwise wait.
    44  		if len(peers) < minPeersForIPDiscovery {
    45  			g.managedSleep(peerDiscoveryRetryInterval)
    46  			continue
    47  		}
    48  		// Ask all the peers about our ip in parallel
    49  		returnChan := make(chan string)
    50  		for _, peer := range peers {
    51  			go g.RPC(peer.NetAddress, "DiscoverIP", func(conn modules.PeerConn) error {
    52  				var address string
    53  				err := encoding.ReadObject(conn, &address, 100)
    54  				if err != nil {
    55  					returnChan <- ""
    56  					g.log.Debugf("DEBUG: failed to receive ip address: %v", err)
    57  					return err
    58  				}
    59  				addr := net.ParseIP(address)
    60  				if addr == nil {
    61  					returnChan <- ""
    62  					g.log.Debug("DEBUG: failed to parse ip address")
    63  					return errors.New("failed to parse ip address")
    64  				}
    65  				returnChan <- addr.String()
    66  				return err
    67  			})
    68  		}
    69  		// Wait for their responses
    70  		addresses := make(map[string]int)
    71  		successfulResponses := 0
    72  		for i := 0; i < len(peers); i++ {
    73  			addr := <-returnChan
    74  			if addr != "" {
    75  				addresses[addr]++
    76  				successfulResponses++
    77  			}
    78  		}
    79  		// If there haven't been enough successful responses we wait some time.
    80  		if successfulResponses < minPeersForIPDiscovery {
    81  			g.managedSleep(peerDiscoveryRetryInterval)
    82  			continue
    83  		}
    84  		// If an address was returned by more than half the peers we consider
    85  		// it valid.
    86  		for addr, count := range addresses {
    87  			if count > successfulResponses/2 {
    88  				g.log.Println("ip successfully discovered using peers:", addr)
    89  				return addr, nil
    90  			}
    91  		}
    92  		// Otherwise we wait before trying again.
    93  		g.managedSleep(peerDiscoveryRetryInterval)
    94  	}
    95  }