github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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  	"time"
    11  
    12  	"github.com/NebulousLabs/go-upnp"
    13  
    14  	"github.com/NebulousLabs/Sia/build"
    15  	"github.com/NebulousLabs/Sia/modules"
    16  )
    17  
    18  // myExternalIP discovers the gateway's external IP by querying a centralized
    19  // service, http://myexternalip.com.
    20  func myExternalIP() (string, error) {
    21  	// timeout after 10 seconds
    22  	client := http.Client{Timeout: time.Duration(10 * time.Second)}
    23  	resp, err := client.Get("http://myexternalip.com/raw")
    24  	if err != nil {
    25  		return "", err
    26  	}
    27  	defer resp.Body.Close()
    28  	if resp.StatusCode != http.StatusOK {
    29  		errResp, _ := ioutil.ReadAll(resp.Body)
    30  		return "", errors.New(string(errResp))
    31  	}
    32  	buf := make([]byte, 64)
    33  	n, err := resp.Body.Read(buf)
    34  	if err != nil && err != io.EOF {
    35  		return "", err
    36  	}
    37  	// trim newline
    38  	return string(buf[:n-1]), nil
    39  }
    40  
    41  // learnHostname discovers the external IP of the Gateway. Once the IP has
    42  // been discovered, it registers the ShareNodes RPC to be called on new
    43  // connections, advertising the IP to other nodes.
    44  func (g *Gateway) learnHostname(port string) {
    45  	if build.Release == "testing" {
    46  		return
    47  	}
    48  
    49  	var host string
    50  
    51  	// try UPnP first, then fallback to myexternalip.com
    52  	d, err := upnp.Discover()
    53  	if err == nil {
    54  		host, err = d.ExternalIP()
    55  	}
    56  	if err != nil {
    57  		host, err = myExternalIP()
    58  	}
    59  	if err != nil {
    60  		g.log.Println("WARN: failed to discover external IP:", err)
    61  		return
    62  	}
    63  
    64  	addr := modules.NetAddress(net.JoinHostPort(host, port))
    65  	if err := addr.IsValid(); err != nil {
    66  		g.log.Printf("WARN: discovered hostname %q is invalid: %v", addr, err)
    67  		return
    68  	}
    69  
    70  	id := g.mu.Lock()
    71  	g.myAddr = addr
    72  	g.mu.Unlock(id)
    73  
    74  	g.log.Println("INFO: our address is", g.myAddr)
    75  
    76  	// now that we know our address, we can start advertising it
    77  	g.RegisterConnectCall("RelayNode", g.sendAddress)
    78  }
    79  
    80  // forwardPort adds a port mapping to the router.
    81  func (g *Gateway) forwardPort(port string) {
    82  	if build.Release == "testing" {
    83  		return
    84  	}
    85  
    86  	d, err := upnp.Discover()
    87  	if err != nil {
    88  		g.log.Printf("WARN: could not automatically forward port %s: no UPnP-enabled devices found", port)
    89  		return
    90  	}
    91  
    92  	portInt, _ := strconv.Atoi(port)
    93  	err = d.Forward(uint16(portInt), "Sia RPC")
    94  	if err != nil {
    95  		g.log.Printf("WARN: could not automatically forward port %s: %v", port, err)
    96  		return
    97  	}
    98  
    99  	g.log.Println("INFO: successfully forwarded port", port)
   100  }
   101  
   102  // clearPort removes a port mapping from the router.
   103  func (g *Gateway) clearPort(port string) {
   104  	if build.Release == "testing" {
   105  		return
   106  	}
   107  
   108  	//d, err := upnp.Load("http://192.168.1.1:5000/Public_UPNP_gatedesc.xml")
   109  	d, err := upnp.Discover()
   110  	if err != nil {
   111  		return
   112  	}
   113  
   114  	portInt, _ := strconv.Atoi(port)
   115  	err = d.Clear(uint16(portInt))
   116  	if err != nil {
   117  		g.log.Printf("WARN: could not automatically unforward port %s: %v", port, err)
   118  		return
   119  	}
   120  
   121  	g.log.Println("INFO: successfully unforwarded port", port)
   122  }