aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/ethereum/flags.go11
-rw-r--r--cmd/ethereum/main.go27
-rw-r--r--cmd/mist/flags.go10
-rw-r--r--cmd/mist/main.go25
-rw-r--r--eth/backend.go14
-rw-r--r--p2p/nat.go23
-rw-r--r--p2p/natpmp.go55
-rw-r--r--p2p/natupnp.go341
-rw-r--r--p2p/server.go70
9 files changed, 54 insertions, 522 deletions
diff --git a/cmd/ethereum/flags.go b/cmd/ethereum/flags.go
index 594acdf7f..99e094b47 100644
--- a/cmd/ethereum/flags.go
+++ b/cmd/ethereum/flags.go
@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/vm"
)
@@ -44,8 +45,6 @@ var (
StartWebSockets bool
RpcPort int
WsPort int
- NatType string
- PMPGateway string
OutboundPort string
ShowGenesis bool
AddPeer string
@@ -53,6 +52,7 @@ var (
GenAddr bool
BootNodes string
NodeKey *ecdsa.PrivateKey
+ NAT nat.Interface
SecretFile string
ExportDir string
NonInteractive bool
@@ -127,18 +127,21 @@ func Init() {
var (
nodeKeyFile = flag.String("nodekey", "", "network private key file")
nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
+ natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
)
flag.BoolVar(&Dial, "dial", true, "dial out connections (default on)")
flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)")
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
- flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
- flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for NAT-PMP")
+
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.Parse()
var err error
+ if NAT, err = nat.Parse(*natstr); err != nil {
+ log.Fatalf("-nat: %v", err)
+ }
switch {
case *nodeKeyFile != "" && *nodeKeyHex != "":
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
diff --git a/cmd/ethereum/main.go b/cmd/ethereum/main.go
index 3f616c094..d2b3418cd 100644
--- a/cmd/ethereum/main.go
+++ b/cmd/ethereum/main.go
@@ -62,20 +62,19 @@ func main() {
utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
ethereum, err := eth.New(&eth.Config{
- Name: p2p.MakeName(ClientIdentifier, Version),
- KeyStore: KeyStore,
- DataDir: Datadir,
- LogFile: LogFile,
- LogLevel: LogLevel,
- MaxPeers: MaxPeer,
- Port: OutboundPort,
- NATType: PMPGateway,
- PMPGateway: PMPGateway,
- KeyRing: KeyRing,
- Shh: SHH,
- Dial: Dial,
- BootNodes: BootNodes,
- NodeKey: NodeKey,
+ Name: p2p.MakeName(ClientIdentifier, Version),
+ KeyStore: KeyStore,
+ DataDir: Datadir,
+ LogFile: LogFile,
+ LogLevel: LogLevel,
+ MaxPeers: MaxPeer,
+ Port: OutboundPort,
+ NAT: NAT,
+ KeyRing: KeyRing,
+ Shh: SHH,
+ Dial: Dial,
+ BootNodes: BootNodes,
+ NodeKey: NodeKey,
})
if err != nil {
diff --git a/cmd/mist/flags.go b/cmd/mist/flags.go
index dd72b5043..eb280f71b 100644
--- a/cmd/mist/flags.go
+++ b/cmd/mist/flags.go
@@ -34,6 +34,7 @@ import (
"bitbucket.org/kardianos/osext"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/vm"
)
@@ -41,12 +42,10 @@ var (
Identifier string
KeyRing string
KeyStore string
- PMPGateway string
StartRpc bool
StartWebSockets bool
RpcPort int
WsPort int
- NatType string
OutboundPort string
ShowGenesis bool
AddPeer string
@@ -54,6 +53,7 @@ var (
GenAddr bool
BootNodes string
NodeKey *ecdsa.PrivateKey
+ NAT nat.Interface
SecretFile string
ExportDir string
NonInteractive bool
@@ -117,7 +117,6 @@ func Init() {
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
- flag.StringVar(&NatType, "nat", "", "NAT support (UPNP|PMP) (none)")
flag.StringVar(&SecretFile, "import", "", "imports the file given (hex or mnemonic formats)")
flag.StringVar(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
@@ -132,15 +131,18 @@ func Init() {
var (
nodeKeyFile = flag.String("nodekey", "", "network private key file")
nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
+ natstr = flag.String("nat", "any", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
)
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
- flag.StringVar(&PMPGateway, "pmp", "", "Gateway IP for NAT-PMP")
flag.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
flag.Parse()
var err error
+ if NAT, err = nat.Parse(*natstr); err != nil {
+ log.Fatalf("-nat: %v", err)
+ }
switch {
case *nodeKeyFile != "" && *nodeKeyHex != "":
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
diff --git a/cmd/mist/main.go b/cmd/mist/main.go
index 4d6bac9b7..52b5e4e94 100644
--- a/cmd/mist/main.go
+++ b/cmd/mist/main.go
@@ -52,19 +52,18 @@ func run() error {
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
ethereum, err := eth.New(&eth.Config{
- Name: p2p.MakeName(ClientIdentifier, Version),
- KeyStore: KeyStore,
- DataDir: Datadir,
- LogFile: LogFile,
- LogLevel: LogLevel,
- MaxPeers: MaxPeer,
- Port: OutboundPort,
- NATType: NatType,
- PMPGateway: PMPGateway,
- BootNodes: BootNodes,
- NodeKey: NodeKey,
- KeyRing: KeyRing,
- Dial: true,
+ Name: p2p.MakeName(ClientIdentifier, Version),
+ KeyStore: KeyStore,
+ DataDir: Datadir,
+ LogFile: LogFile,
+ LogLevel: LogLevel,
+ MaxPeers: MaxPeer,
+ Port: OutboundPort,
+ NAT: NAT,
+ BootNodes: BootNodes,
+ NodeKey: NodeKey,
+ KeyRing: KeyRing,
+ Dial: true,
})
if err != nil {
mainlogger.Fatalln(err)
diff --git a/eth/backend.go b/eth/backend.go
index f3e4842a7..29cf7d836 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -13,6 +13,7 @@ import (
ethlogger "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
+ "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/pow/ezp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/whisper"
@@ -28,10 +29,8 @@ type Config struct {
LogLevel int
KeyRing string
- MaxPeers int
- Port string
- NATType string
- PMPGateway string
+ MaxPeers int
+ Port string
// This should be a space-separated list of
// discovery node URLs.
@@ -41,6 +40,7 @@ type Config struct {
// If nil, an ephemeral key is used.
NodeKey *ecdsa.PrivateKey
+ NAT nat.Interface
Shh bool
Dial bool
@@ -147,10 +147,6 @@ func New(config *Config) (*Ethereum, error) {
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
- nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
- if err != nil {
- return nil, err
- }
netprv := config.NodeKey
if netprv == nil {
if netprv, err = crypto.GenerateKey(); err != nil {
@@ -163,7 +159,7 @@ func New(config *Config) (*Ethereum, error) {
MaxPeers: config.MaxPeers,
Protocols: protocols,
Blacklist: eth.blacklist,
- NAT: nat,
+ NAT: config.NAT,
NoDial: !config.Dial,
BootstrapNodes: config.parseBootNodes(),
}
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)