From d0a2e655c9599f462bb20bd49bc69b8e1e330a21 Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Wed, 11 Feb 2015 17:19:31 +0100
Subject: cmd/ethereum, cmd/mist, eth, p2p: use package p2p/nat

This deletes the old NAT implementation.
---
 p2p/nat.go     |  23 ----
 p2p/natpmp.go  |  55 ----------
 p2p/natupnp.go | 341 ---------------------------------------------------------
 p2p/server.go  |  70 ++----------
 4 files changed, 11 insertions(+), 478 deletions(-)
 delete mode 100644 p2p/nat.go
 delete mode 100644 p2p/natpmp.go
 delete mode 100644 p2p/natupnp.go

(limited to 'p2p')

diff --git a/p2p/nat.go b/p2p/nat.go
deleted file mode 100644
index 9b771c3e8..000000000
--- a/p2p/nat.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package p2p
-
-import (
-	"fmt"
-	"net"
-)
-
-func ParseNAT(natType string, gateway string) (nat NAT, err error) {
-	switch natType {
-	case "UPNP":
-		nat = UPNP()
-	case "PMP":
-		ip := net.ParseIP(gateway)
-		if ip == nil {
-			return nil, fmt.Errorf("cannot resolve PMP gateway IP %s", gateway)
-		}
-		nat = PMP(ip)
-	case "":
-	default:
-		return nil, fmt.Errorf("unrecognised NAT type '%s'", natType)
-	}
-	return
-}
diff --git a/p2p/natpmp.go b/p2p/natpmp.go
deleted file mode 100644
index 6714678c4..000000000
--- a/p2p/natpmp.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package p2p
-
-import (
-	"fmt"
-	"net"
-	"time"
-
-	natpmp "github.com/jackpal/go-nat-pmp"
-)
-
-// Adapt the NAT-PMP protocol to the NAT interface
-
-// TODO:
-//  + Register for changes to the external address.
-//  + Re-register port mapping when router reboots.
-//  + A mechanism for keeping a port mapping registered.
-//  + Discover gateway address automatically.
-
-type natPMPClient struct {
-	client *natpmp.Client
-}
-
-// PMP returns a NAT traverser that uses NAT-PMP. The provided gateway
-// address should be the IP of your router.
-func PMP(gateway net.IP) (nat NAT) {
-	return &natPMPClient{natpmp.NewClient(gateway)}
-}
-
-func (*natPMPClient) String() string {
-	return "NAT-PMP"
-}
-
-func (n *natPMPClient) GetExternalAddress() (net.IP, error) {
-	response, err := n.client.GetExternalAddress()
-	if err != nil {
-		return nil, err
-	}
-	return response.ExternalIPAddress[:], nil
-}
-
-func (n *natPMPClient) AddPortMapping(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 AddPortMapping and the client's AddPortMapping.
-	_, err := n.client.AddPortMapping(protocol, intport, extport, int(lifetime/time.Second))
-	return err
-}
-
-func (n *natPMPClient) DeletePortMapping(protocol string, externalPort, internalPort 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.client.AddPortMapping(protocol, internalPort, 0, 0)
-	return
-}
diff --git a/p2p/natupnp.go b/p2p/natupnp.go
deleted file mode 100644
index 2e0d8ce8d..000000000
--- a/p2p/natupnp.go
+++ /dev/null
@@ -1,341 +0,0 @@
-package p2p
-
-// Just enough UPnP to be able to forward ports
-//
-
-import (
-	"bytes"
-	"encoding/xml"
-	"errors"
-	"fmt"
-	"net"
-	"net/http"
-	"os"
-	"strconv"
-	"strings"
-	"time"
-)
-
-const (
-	upnpDiscoverAttempts = 3
-	upnpDiscoverTimeout  = 5 * time.Second
-)
-
-// UPNP returns a NAT port mapper that uses UPnP. It will attempt to
-// discover the address of your router using UDP broadcasts.
-func UPNP() NAT {
-	return &upnpNAT{}
-}
-
-type upnpNAT struct {
-	serviceURL string
-	ourIP      string
-}
-
-func (n *upnpNAT) String() string {
-	return "UPNP"
-}
-
-func (n *upnpNAT) discover() error {
-	if n.serviceURL != "" {
-		// already discovered
-		return nil
-	}
-
-	ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
-	if err != nil {
-		return err
-	}
-	// TODO: try on all network interfaces simultaneously.
-	// Broadcasting on 0.0.0.0 could select a random interface
-	// to send on (platform specific).
-	conn, err := net.ListenPacket("udp4", ":0")
-	if err != nil {
-		return err
-	}
-	defer conn.Close()
-
-	conn.SetDeadline(time.Now().Add(10 * time.Second))
-	st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
-	buf := bytes.NewBufferString(
-		"M-SEARCH * HTTP/1.1\r\n" +
-			"HOST: 239.255.255.250:1900\r\n" +
-			st +
-			"MAN: \"ssdp:discover\"\r\n" +
-			"MX: 2\r\n\r\n")
-	message := buf.Bytes()
-	answerBytes := make([]byte, 1024)
-	for i := 0; i < upnpDiscoverAttempts; i++ {
-		_, err = conn.WriteTo(message, ssdp)
-		if err != nil {
-			return err
-		}
-		nn, _, err := conn.ReadFrom(answerBytes)
-		if err != nil {
-			continue
-		}
-		answer := string(answerBytes[0:nn])
-		if strings.Index(answer, "\r\n"+st) < 0 {
-			continue
-		}
-		// HTTP header field names are case-insensitive.
-		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
-		locString := "\r\nlocation: "
-		answer = strings.ToLower(answer)
-		locIndex := strings.Index(answer, locString)
-		if locIndex < 0 {
-			continue
-		}
-		loc := answer[locIndex+len(locString):]
-		endIndex := strings.Index(loc, "\r\n")
-		if endIndex < 0 {
-			continue
-		}
-		locURL := loc[0:endIndex]
-		var serviceURL string
-		serviceURL, err = getServiceURL(locURL)
-		if err != nil {
-			return err
-		}
-		var ourIP string
-		ourIP, err = getOurIP()
-		if err != nil {
-			return err
-		}
-		n.serviceURL = serviceURL
-		n.ourIP = ourIP
-		return nil
-	}
-	return errors.New("UPnP port discovery failed.")
-}
-
-func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
-	if err := n.discover(); err != nil {
-		return nil, err
-	}
-	info, err := n.getStatusInfo()
-	return net.ParseIP(info.externalIpAddress), err
-}
-
-func (n *upnpNAT) AddPortMapping(protocol string, extport, intport int, description string, lifetime time.Duration) error {
-	if err := n.discover(); err != nil {
-		return err
-	}
-
-	// A single concatenation would break ARM compilation.
-	message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
-		"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(extport)
-	message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
-	message += "<NewInternalPort>" + strconv.Itoa(extport) + "</NewInternalPort>" +
-		"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
-		"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
-	message += description +
-		"</NewPortMappingDescription><NewLeaseDuration>" + fmt.Sprint(lifetime/time.Second) +
-		"</NewLeaseDuration></u:AddPortMapping>"
-
-	// TODO: check response to see if the port was forwarded
-	_, err := soapRequest(n.serviceURL, "AddPortMapping", message)
-	return err
-}
-
-func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) error {
-	if err := n.discover(); err != nil {
-		return err
-	}
-
-	message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
-		"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
-		"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
-		"</u:DeletePortMapping>"
-
-	// TODO: check response to see if the port was deleted
-	_, err := soapRequest(n.serviceURL, "DeletePortMapping", message)
-	return err
-}
-
-type statusInfo struct {
-	externalIpAddress string
-}
-
-func (n *upnpNAT) getStatusInfo() (info statusInfo, err error) {
-	message := "<u:GetStatusInfo xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
-		"</u:GetStatusInfo>"
-
-	var response *http.Response
-	response, err = soapRequest(n.serviceURL, "GetStatusInfo", message)
-	if err != nil {
-		return
-	}
-
-	// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
-
-	response.Body.Close()
-	return
-}
-
-// service represents the Service type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type service struct {
-	ServiceType string `xml:"serviceType"`
-	ControlURL  string `xml:"controlURL"`
-}
-
-// deviceList represents the deviceList type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type deviceList struct {
-	XMLName xml.Name `xml:"deviceList"`
-	Device  []device `xml:"device"`
-}
-
-// serviceList represents the serviceList type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type serviceList struct {
-	XMLName xml.Name  `xml:"serviceList"`
-	Service []service `xml:"service"`
-}
-
-// device represents the device type in an UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type device struct {
-	XMLName     xml.Name    `xml:"device"`
-	DeviceType  string      `xml:"deviceType"`
-	DeviceList  deviceList  `xml:"deviceList"`
-	ServiceList serviceList `xml:"serviceList"`
-}
-
-// specVersion represents the specVersion in a UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type specVersion struct {
-	XMLName xml.Name `xml:"specVersion"`
-	Major   int      `xml:"major"`
-	Minor   int      `xml:"minor"`
-}
-
-// root represents the Root document for a UPnP xml description.
-// Only the parts we care about are present and thus the xml may have more
-// fields than present in the structure.
-type root struct {
-	XMLName     xml.Name `xml:"root"`
-	SpecVersion specVersion
-	Device      device
-}
-
-func getChildDevice(d *device, deviceType string) *device {
-	dl := d.DeviceList.Device
-	for i := 0; i < len(dl); i++ {
-		if dl[i].DeviceType == deviceType {
-			return &dl[i]
-		}
-	}
-	return nil
-}
-
-func getChildService(d *device, serviceType string) *service {
-	sl := d.ServiceList.Service
-	for i := 0; i < len(sl); i++ {
-		if sl[i].ServiceType == serviceType {
-			return &sl[i]
-		}
-	}
-	return nil
-}
-
-func getOurIP() (ip string, err error) {
-	hostname, err := os.Hostname()
-	if err != nil {
-		return
-	}
-	p, err := net.LookupIP(hostname)
-	if err != nil && len(p) > 0 {
-		return
-	}
-	return p[0].String(), nil
-}
-
-func getServiceURL(rootURL string) (url string, err error) {
-	r, err := http.Get(rootURL)
-	if err != nil {
-		return
-	}
-	defer r.Body.Close()
-	if r.StatusCode >= 400 {
-		err = errors.New(string(r.StatusCode))
-		return
-	}
-	var root root
-	err = xml.NewDecoder(r.Body).Decode(&root)
-
-	if err != nil {
-		return
-	}
-	a := &root.Device
-	if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
-		err = errors.New("No InternetGatewayDevice")
-		return
-	}
-	b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
-	if b == nil {
-		err = errors.New("No WANDevice")
-		return
-	}
-	c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
-	if c == nil {
-		err = errors.New("No WANConnectionDevice")
-		return
-	}
-	d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
-	if d == nil {
-		err = errors.New("No WANIPConnection")
-		return
-	}
-	url = combineURL(rootURL, d.ControlURL)
-	return
-}
-
-func combineURL(rootURL, subURL string) string {
-	protocolEnd := "://"
-	protoEndIndex := strings.Index(rootURL, protocolEnd)
-	a := rootURL[protoEndIndex+len(protocolEnd):]
-	rootIndex := strings.Index(a, "/")
-	return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
-}
-
-func soapRequest(url, function, message string) (r *http.Response, err error) {
-	fullMessage := "<?xml version=\"1.0\" ?>" +
-		"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
-		"<s:Body>" + message + "</s:Body></s:Envelope>"
-
-	req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
-	if err != nil {
-		return
-	}
-	req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
-	req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
-	//req.Header.Set("Transfer-Encoding", "chunked")
-	req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
-	req.Header.Set("Connection", "Close")
-	req.Header.Set("Cache-Control", "no-cache")
-	req.Header.Set("Pragma", "no-cache")
-
-	r, err = http.DefaultClient.Do(req)
-	if err != nil {
-		return
-	}
-
-	if r.Body != nil {
-		defer r.Body.Close()
-	}
-
-	if r.StatusCode >= 400 {
-		// log.Stderr(function, r.StatusCode)
-		err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
-		r = nil
-		return
-	}
-	return
-}
diff --git a/p2p/server.go b/p2p/server.go
index 3cab61102..a0f2dee23 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -13,13 +13,12 @@ import (
 
 	"github.com/ethereum/go-ethereum/logger"
 	"github.com/ethereum/go-ethereum/p2p/discover"
+	"github.com/ethereum/go-ethereum/p2p/nat"
 )
 
 const (
-	defaultDialTimeout        = 10 * time.Second
-	refreshPeersInterval      = 30 * time.Second
-	portMappingUpdateInterval = 15 * time.Minute
-	portMappingTimeout        = 20 * time.Minute
+	defaultDialTimeout   = 10 * time.Second
+	refreshPeersInterval = 30 * time.Second
 )
 
 var srvlog = logger.NewLogger("P2P Server")
@@ -72,7 +71,7 @@ type Server struct {
 	// If set to a non-nil value, the given NAT port mapper
 	// is used to make the listening port available to the
 	// Internet.
-	NAT NAT
+	NAT nat.Interface
 
 	// If Dialer is set to a non-nil value, the given Dialer
 	// is used to dial outbound peer connections.
@@ -89,7 +88,6 @@ type Server struct {
 	lock     sync.RWMutex
 	running  bool
 	listener net.Listener
-	laddr    *net.TCPAddr // real listen addr
 	peers    map[discover.NodeID]*Peer
 
 	ntab *discover.Table
@@ -100,16 +98,6 @@ type Server struct {
 	peerConnect chan *discover.Node
 }
 
-// NAT is implemented by NAT traversal methods.
-type NAT interface {
-	GetExternalAddress() (net.IP, error)
-	AddPortMapping(protocol string, extport, intport int, name string, lifetime time.Duration) error
-	DeletePortMapping(protocol string, extport, intport int) error
-
-	// Should return name of the method.
-	String() string
-}
-
 type handshakeFunc func(io.ReadWriter, *ecdsa.PrivateKey, *discover.Node) (discover.NodeID, []byte, error)
 type newPeerHook func(*Peer)
 
@@ -220,14 +208,17 @@ func (srv *Server) startListening() error {
 	if err != nil {
 		return err
 	}
-	srv.ListenAddr = listener.Addr().String()
-	srv.laddr = listener.Addr().(*net.TCPAddr)
+	laddr := listener.Addr().(*net.TCPAddr)
+	srv.ListenAddr = laddr.String()
 	srv.listener = listener
 	srv.loopWG.Add(1)
 	go srv.listenLoop()
-	if !srv.laddr.IP.IsLoopback() && srv.NAT != nil {
+	if !laddr.IP.IsLoopback() && srv.NAT != nil {
 		srv.loopWG.Add(1)
-		go srv.natLoop(srv.laddr.Port)
+		go func() {
+			nat.Map(srv.NAT, srv.quit, "tcp", laddr.Port, laddr.Port, "ethereum p2p")
+			srv.loopWG.Done()
+		}()
 	}
 	return nil
 }
@@ -276,45 +267,6 @@ func (srv *Server) listenLoop() {
 	}
 }
 
-func (srv *Server) natLoop(port int) {
-	defer srv.loopWG.Done()
-	for {
-		srv.updatePortMapping(port)
-		select {
-		case <-time.After(portMappingUpdateInterval):
-			// one more round
-		case <-srv.quit:
-			srv.removePortMapping(port)
-			return
-		}
-	}
-}
-
-func (srv *Server) updatePortMapping(port int) {
-	srvlog.Infoln("Attempting to map port", port, "with", srv.NAT)
-	err := srv.NAT.AddPortMapping("tcp", port, port, "ethereum p2p", portMappingTimeout)
-	if err != nil {
-		srvlog.Errorln("Port mapping error:", err)
-		return
-	}
-	extip, err := srv.NAT.GetExternalAddress()
-	if err != nil {
-		srvlog.Errorln("Error getting external IP:", err)
-		return
-	}
-	srv.lock.Lock()
-	extaddr := *(srv.listener.Addr().(*net.TCPAddr))
-	extaddr.IP = extip
-	srvlog.Infoln("Mapped port, external addr is", &extaddr)
-	srv.laddr = &extaddr
-	srv.lock.Unlock()
-}
-
-func (srv *Server) removePortMapping(port int) {
-	srvlog.Infoln("Removing port mapping for", port, "with", srv.NAT)
-	srv.NAT.DeletePortMapping("tcp", port, port)
-}
-
 func (srv *Server) dialLoop() {
 	defer srv.loopWG.Done()
 	refresh := time.NewTicker(refreshPeersInterval)
-- 
cgit v1.2.3