github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/gateway/ip.go (about) 1 package gateway 2 3 import ( 4 "net" 5 "time" 6 7 "SiaPrime/encoding" 8 "SiaPrime/modules" 9 "gitlab.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. 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 }