123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- // 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
- }
|