132 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 The go-ethereum Authors
 | |
| // This file is part of the go-ethereum library.
 | |
| //
 | |
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU Lesser General Public License as published by
 | |
| // the Free Software Foundation, either version 3 of the License, or
 | |
| // (at your option) any later version.
 | |
| //
 | |
| // The go-ethereum library is distributed in the hope that it will be useful,
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| // GNU Lesser General Public License for more details.
 | |
| //
 | |
| // You should have received a copy of the GNU Lesser General Public License
 | |
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package nat
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/jackpal/go-nat-pmp"
 | |
| )
 | |
| 
 | |
| // natPMPClient adapts the NAT-PMP protocol implementation so it conforms to
 | |
| // the common interface.
 | |
| type pmp struct {
 | |
| 	gw net.IP
 | |
| 	c  *natpmp.Client
 | |
| }
 | |
| 
 | |
| func (n *pmp) String() string {
 | |
| 	return fmt.Sprintf("NAT-PMP(%v)", n.gw)
 | |
| }
 | |
| 
 | |
| func (n *pmp) ExternalIP() (net.IP, error) {
 | |
| 	response, err := n.c.GetExternalAddress()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return response.ExternalIPAddress[:], nil
 | |
| }
 | |
| 
 | |
| func (n *pmp) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error {
 | |
| 	if lifetime <= 0 {
 | |
| 		return fmt.Errorf("lifetime must not be <= 0")
 | |
| 	}
 | |
| 	// Note order of port arguments is switched between our
 | |
| 	// AddMapping and the client's AddPortMapping.
 | |
| 	_, err := n.c.AddPortMapping(strings.ToLower(protocol), intport, extport, int(lifetime/time.Second))
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (n *pmp) DeleteMapping(protocol string, extport, intport int) (err error) {
 | |
| 	// To destroy a mapping, send an add-port with an internalPort of
 | |
| 	// the internal port to destroy, an external port of zero and a
 | |
| 	// time of zero.
 | |
| 	_, err = n.c.AddPortMapping(strings.ToLower(protocol), intport, 0, 0)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func discoverPMP() Interface {
 | |
| 	// run external address lookups on all potential gateways
 | |
| 	gws := potentialGateways()
 | |
| 	found := make(chan *pmp, len(gws))
 | |
| 	for i := range gws {
 | |
| 		gw := gws[i]
 | |
| 		go func() {
 | |
| 			c := natpmp.NewClient(gw)
 | |
| 			if _, err := c.GetExternalAddress(); err != nil {
 | |
| 				found <- nil
 | |
| 			} else {
 | |
| 				found <- &pmp{gw, c}
 | |
| 			}
 | |
| 		}()
 | |
| 	}
 | |
| 	// return the one that responds first.
 | |
| 	// discovery needs to be quick, so we stop caring about
 | |
| 	// any responses after a very short timeout.
 | |
| 	timeout := time.NewTimer(1 * time.Second)
 | |
| 	defer timeout.Stop()
 | |
| 	for range gws {
 | |
| 		select {
 | |
| 		case c := <-found:
 | |
| 			if c != nil {
 | |
| 				return c
 | |
| 			}
 | |
| 		case <-timeout.C:
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// LAN IP ranges
 | |
| 	_, lan10, _  = net.ParseCIDR("10.0.0.0/8")
 | |
| 	_, lan176, _ = net.ParseCIDR("172.16.0.0/12")
 | |
| 	_, lan192, _ = net.ParseCIDR("192.168.0.0/16")
 | |
| )
 | |
| 
 | |
| // TODO: improve this. We currently assume that (on most networks)
 | |
| // the router is X.X.X.1 in a local LAN range.
 | |
| func potentialGateways() (gws []net.IP) {
 | |
| 	ifaces, err := net.Interfaces()
 | |
| 	if err != nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	for _, iface := range ifaces {
 | |
| 		ifaddrs, err := iface.Addrs()
 | |
| 		if err != nil {
 | |
| 			return gws
 | |
| 		}
 | |
| 		for _, addr := range ifaddrs {
 | |
| 			switch x := addr.(type) {
 | |
| 			case *net.IPNet:
 | |
| 				if lan10.Contains(x.IP) || lan176.Contains(x.IP) || lan192.Contains(x.IP) {
 | |
| 					ip := x.IP.Mask(x.Mask).To4()
 | |
| 					if ip != nil {
 | |
| 						ip[3] = ip[3] | 0x01
 | |
| 						gws = append(gws, ip)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return gws
 | |
| }
 |