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  }