gitlab.com/jokerrs1/Sia@v1.3.2/modules/gateway/upnp.go (about) 1 package gateway 2 3 import ( 4 "errors" 5 "io" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/NebulousLabs/go-upnp" 14 15 "github.com/NebulousLabs/Sia/build" 16 "github.com/NebulousLabs/Sia/modules" 17 ) 18 19 // myExternalIP discovers the gateway's external IP by querying a centralized 20 // service, http://myexternalip.com. 21 func myExternalIP() (string, error) { 22 // timeout after 10 seconds 23 client := http.Client{Timeout: time.Duration(10 * time.Second)} 24 resp, err := client.Get("http://myexternalip.com/raw") 25 if err != nil { 26 return "", err 27 } 28 defer resp.Body.Close() 29 if resp.StatusCode != http.StatusOK { 30 errResp, _ := ioutil.ReadAll(resp.Body) 31 return "", errors.New(string(errResp)) 32 } 33 buf, err := ioutil.ReadAll(io.LimitReader(resp.Body, 64)) 34 if err != nil { 35 return "", err 36 } 37 if len(buf) == 0 { 38 return "", errors.New("myexternalip.com returned a 0 length IP address") 39 } 40 // trim newline 41 return strings.TrimSpace(string(buf)), nil 42 } 43 44 // threadedLearnHostname discovers the external IP of the Gateway. Once the IP 45 // has been discovered, it registers the ShareNodes RPC to be called on new 46 // connections, advertising the IP to other nodes. 47 func (g *Gateway) threadedLearnHostname() { 48 if err := g.threads.Add(); err != nil { 49 return 50 } 51 defer g.threads.Done() 52 53 if build.Release == "testing" { 54 return 55 } 56 57 // try UPnP first, then fallback to myexternalip.com 58 var host string 59 d, err := upnp.Discover() 60 if err == nil { 61 host, err = d.ExternalIP() 62 } 63 if err != nil { 64 host, err = myExternalIP() 65 } 66 if err != nil { 67 g.log.Println("WARN: failed to discover external IP:", err) 68 return 69 } 70 71 g.mu.RLock() 72 addr := modules.NetAddress(net.JoinHostPort(host, g.port)) 73 g.mu.RUnlock() 74 if err := addr.IsValid(); err != nil { 75 g.log.Printf("WARN: discovered hostname %q is invalid: %v", addr, err) 76 return 77 } 78 79 g.mu.Lock() 80 g.myAddr = addr 81 g.mu.Unlock() 82 83 g.log.Println("INFO: our address is", addr) 84 } 85 86 // threadedForwardPort adds a port mapping to the router. 87 func (g *Gateway) threadedForwardPort(port string) { 88 if err := g.threads.Add(); err != nil { 89 return 90 } 91 defer g.threads.Done() 92 93 if build.Release == "testing" { 94 return 95 } 96 97 d, err := upnp.Discover() 98 if err != nil { 99 g.log.Printf("WARN: could not automatically forward port %s: no UPnP-enabled devices found: %v", port, err) 100 return 101 } 102 103 portInt, _ := strconv.Atoi(port) 104 err = d.Forward(uint16(portInt), "Sia RPC") 105 if err != nil { 106 g.log.Printf("WARN: could not automatically forward port %s: %v", port, err) 107 return 108 } 109 110 g.log.Println("INFO: successfully forwarded port", port) 111 112 // Establish port-clearing at shutdown. 113 g.threads.AfterStop(func() { 114 g.managedClearPort(port) 115 }) 116 } 117 118 // managedClearPort removes a port mapping from the router. 119 func (g *Gateway) managedClearPort(port string) { 120 if build.Release == "testing" { 121 return 122 } 123 124 d, err := upnp.Discover() 125 if err != nil { 126 return 127 } 128 129 portInt, _ := strconv.Atoi(port) 130 err = d.Clear(uint16(portInt)) 131 if err != nil { 132 g.log.Printf("WARN: could not automatically unforward port %s: %v", port, err) 133 return 134 } 135 136 g.log.Println("INFO: successfully unforwarded port", port) 137 }