github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/upnp.go (about) 1 package host 2 3 import ( 4 "io" 5 "net" 6 "net/http" 7 "strconv" 8 "time" 9 10 "github.com/NebulousLabs/go-upnp" 11 12 "github.com/NebulousLabs/Sia/build" 13 "github.com/NebulousLabs/Sia/modules" 14 ) 15 16 // myExternalIP discovers the host's external IP by querying a centralized 17 // service, http://myexternalip.com. 18 func myExternalIP() (string, error) { 19 // timeout after 10 seconds 20 client := http.Client{Timeout: time.Duration(10 * time.Second)} 21 resp, err := client.Get("http://myexternalip.com/raw") 22 if err != nil { 23 return "", err 24 } 25 defer resp.Body.Close() 26 buf := make([]byte, 64) 27 n, err := resp.Body.Read(buf) 28 if err != nil && err != io.EOF { 29 return "", err 30 } 31 // trim newline 32 return string(buf[:n-1]), nil 33 } 34 35 // managedLearnHostname discovers the external IP of the Host. If the host's 36 // net address is blank and the host's auto address appears to have changed, 37 // the host will make an announcement on the blockchain. 38 func (h *Host) managedLearnHostname() { 39 if build.Release == "testing" { 40 return 41 } 42 h.mu.RLock() 43 netAddr := h.settings.NetAddress 44 h.mu.RUnlock() 45 // If the settings indicate that an address has been manually set, there is 46 // no reason to learn the hostname. 47 if netAddr != "" { 48 return 49 } 50 51 // try UPnP first, then fallback to myexternalip.com 52 var hostname string 53 d, err := upnp.Discover() 54 if err == nil { 55 hostname, err = d.ExternalIP() 56 } 57 if err != nil { 58 hostname, err = myExternalIP() 59 } 60 if err != nil { 61 h.log.Println("WARN: failed to discover external IP") 62 return 63 } 64 65 h.mu.Lock() 66 defer h.mu.Unlock() 67 autoAddress := modules.NetAddress(net.JoinHostPort(hostname, h.port)) 68 if err := autoAddress.IsValid(); err != nil { 69 h.log.Printf("WARN: discovered hostname %q is invalid: %v", autoAddress, err) 70 return 71 } 72 if autoAddress == h.autoAddress && h.announced { 73 // Nothing to do - the auto address has not changed and the previous 74 // annoucement was successful. 75 return 76 } 77 err = h.announce(autoAddress) 78 if err != nil { 79 // Set h.announced to false, as the address has changed yet the 80 // renewed annoucement has failed. 81 h.announced = false 82 h.log.Debugln(err) 83 } 84 h.autoAddress = autoAddress 85 err = h.save() 86 if err != nil { 87 h.log.Println(err) 88 } 89 } 90 91 // managedForwardPort adds a port mapping to the router. 92 func (h *Host) managedForwardPort() error { 93 // If the port is invalid, there is no need to perform any of the other 94 // tasks. 95 h.mu.RLock() 96 port := h.port 97 h.mu.RUnlock() 98 portInt, err := strconv.Atoi(port) 99 if err != nil { 100 return err 101 } 102 if build.Release == "testing" { 103 return nil 104 } 105 106 d, err := upnp.Discover() 107 if err != nil { 108 return err 109 } 110 err = d.Forward(uint16(portInt), "Sia Host") 111 if err != nil { 112 return err 113 } 114 115 h.log.Println("INFO: successfully forwarded port", port) 116 return nil 117 } 118 119 // managedClearPort removes a port mapping from the router. 120 func (h *Host) managedClearPort() error { 121 // If the port is invalid, there is no need to perform any of the other 122 // tasks. 123 h.mu.RLock() 124 port := h.port 125 h.mu.RUnlock() 126 portInt, err := strconv.Atoi(port) 127 if err != nil { 128 return err 129 } 130 if build.Release == "testing" { 131 return nil 132 } 133 134 d, err := upnp.Discover() 135 if err != nil { 136 return err 137 } 138 err = d.Clear(uint16(portInt)) 139 if err != nil { 140 return err 141 } 142 143 h.log.Println("INFO: successfully unforwarded port", port) 144 return nil 145 }