cmd/ethereum, cmd/mist, eth, p2p: use package p2p/nat
This deletes the old NAT implementation.
This commit is contained in:
parent
1543833ca0
commit
d0a2e655c9
@ -31,6 +31,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,8 +45,6 @@ var (
|
|||||||
StartWebSockets bool
|
StartWebSockets bool
|
||||||
RpcPort int
|
RpcPort int
|
||||||
WsPort int
|
WsPort int
|
||||||
NatType string
|
|
||||||
PMPGateway string
|
|
||||||
OutboundPort string
|
OutboundPort string
|
||||||
ShowGenesis bool
|
ShowGenesis bool
|
||||||
AddPeer string
|
AddPeer string
|
||||||
@ -53,6 +52,7 @@ var (
|
|||||||
GenAddr bool
|
GenAddr bool
|
||||||
BootNodes string
|
BootNodes string
|
||||||
NodeKey *ecdsa.PrivateKey
|
NodeKey *ecdsa.PrivateKey
|
||||||
|
NAT nat.Interface
|
||||||
SecretFile string
|
SecretFile string
|
||||||
ExportDir string
|
ExportDir string
|
||||||
NonInteractive bool
|
NonInteractive bool
|
||||||
@ -127,18 +127,21 @@ func Init() {
|
|||||||
var (
|
var (
|
||||||
nodeKeyFile = flag.String("nodekey", "", "network private key file")
|
nodeKeyFile = flag.String("nodekey", "", "network private key file")
|
||||||
nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
|
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(&Dial, "dial", true, "dial out connections (default on)")
|
||||||
flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)")
|
flag.BoolVar(&SHH, "shh", true, "run whisper protocol (default on)")
|
||||||
flag.StringVar(&OutboundPort, "port", "30303", "listening port")
|
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.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
|
||||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if NAT, err = nat.Parse(*natstr); err != nil {
|
||||||
|
log.Fatalf("-nat: %v", err)
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
||||||
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
|
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
|
||||||
|
@ -69,8 +69,7 @@ func main() {
|
|||||||
LogLevel: LogLevel,
|
LogLevel: LogLevel,
|
||||||
MaxPeers: MaxPeer,
|
MaxPeers: MaxPeer,
|
||||||
Port: OutboundPort,
|
Port: OutboundPort,
|
||||||
NATType: PMPGateway,
|
NAT: NAT,
|
||||||
PMPGateway: PMPGateway,
|
|
||||||
KeyRing: KeyRing,
|
KeyRing: KeyRing,
|
||||||
Shh: SHH,
|
Shh: SHH,
|
||||||
Dial: Dial,
|
Dial: Dial,
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"bitbucket.org/kardianos/osext"
|
"bitbucket.org/kardianos/osext"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/vm"
|
"github.com/ethereum/go-ethereum/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,12 +42,10 @@ var (
|
|||||||
Identifier string
|
Identifier string
|
||||||
KeyRing string
|
KeyRing string
|
||||||
KeyStore string
|
KeyStore string
|
||||||
PMPGateway string
|
|
||||||
StartRpc bool
|
StartRpc bool
|
||||||
StartWebSockets bool
|
StartWebSockets bool
|
||||||
RpcPort int
|
RpcPort int
|
||||||
WsPort int
|
WsPort int
|
||||||
NatType string
|
|
||||||
OutboundPort string
|
OutboundPort string
|
||||||
ShowGenesis bool
|
ShowGenesis bool
|
||||||
AddPeer string
|
AddPeer string
|
||||||
@ -54,6 +53,7 @@ var (
|
|||||||
GenAddr bool
|
GenAddr bool
|
||||||
BootNodes string
|
BootNodes string
|
||||||
NodeKey *ecdsa.PrivateKey
|
NodeKey *ecdsa.PrivateKey
|
||||||
|
NAT nat.Interface
|
||||||
SecretFile string
|
SecretFile string
|
||||||
ExportDir string
|
ExportDir string
|
||||||
NonInteractive bool
|
NonInteractive bool
|
||||||
@ -117,7 +117,6 @@ func Init() {
|
|||||||
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
flag.BoolVar(&StartWebSockets, "ws", false, "start websocket server")
|
||||||
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
flag.BoolVar(&NonInteractive, "y", false, "non-interactive mode (say yes to confirmations)")
|
||||||
flag.BoolVar(&GenAddr, "genaddr", false, "create a new priv/pub key")
|
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(&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(&ExportDir, "export", "", "exports the session keyring to files in the directory given")
|
||||||
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
flag.StringVar(&LogFile, "logfile", "", "log file (defaults to standard output)")
|
||||||
@ -132,15 +131,18 @@ func Init() {
|
|||||||
var (
|
var (
|
||||||
nodeKeyFile = flag.String("nodekey", "", "network private key file")
|
nodeKeyFile = flag.String("nodekey", "", "network private key file")
|
||||||
nodeKeyHex = flag.String("nodekeyhex", "", "network private key (for testing)")
|
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(&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.StringVar(&BootNodes, "bootnodes", "", "space-separated node URLs for discovery bootstrap")
|
||||||
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
flag.IntVar(&MaxPeer, "maxpeer", 30, "maximum desired peers")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
if NAT, err = nat.Parse(*natstr); err != nil {
|
||||||
|
log.Fatalf("-nat: %v", err)
|
||||||
|
}
|
||||||
switch {
|
switch {
|
||||||
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
case *nodeKeyFile != "" && *nodeKeyHex != "":
|
||||||
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
|
log.Fatal("Options -nodekey and -nodekeyhex are mutually exclusive")
|
||||||
|
@ -59,8 +59,7 @@ func run() error {
|
|||||||
LogLevel: LogLevel,
|
LogLevel: LogLevel,
|
||||||
MaxPeers: MaxPeer,
|
MaxPeers: MaxPeer,
|
||||||
Port: OutboundPort,
|
Port: OutboundPort,
|
||||||
NATType: NatType,
|
NAT: NAT,
|
||||||
PMPGateway: PMPGateway,
|
|
||||||
BootNodes: BootNodes,
|
BootNodes: BootNodes,
|
||||||
NodeKey: NodeKey,
|
NodeKey: NodeKey,
|
||||||
KeyRing: KeyRing,
|
KeyRing: KeyRing,
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
ethlogger "github.com/ethereum/go-ethereum/logger"
|
ethlogger "github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"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/pow/ezp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/whisper"
|
"github.com/ethereum/go-ethereum/whisper"
|
||||||
@ -30,8 +31,6 @@ type Config struct {
|
|||||||
|
|
||||||
MaxPeers int
|
MaxPeers int
|
||||||
Port string
|
Port string
|
||||||
NATType string
|
|
||||||
PMPGateway string
|
|
||||||
|
|
||||||
// This should be a space-separated list of
|
// This should be a space-separated list of
|
||||||
// discovery node URLs.
|
// discovery node URLs.
|
||||||
@ -41,6 +40,7 @@ type Config struct {
|
|||||||
// If nil, an ephemeral key is used.
|
// If nil, an ephemeral key is used.
|
||||||
NodeKey *ecdsa.PrivateKey
|
NodeKey *ecdsa.PrivateKey
|
||||||
|
|
||||||
|
NAT nat.Interface
|
||||||
Shh bool
|
Shh bool
|
||||||
Dial bool
|
Dial bool
|
||||||
|
|
||||||
@ -147,10 +147,6 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
|
|
||||||
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
ethProto := EthProtocol(eth.txPool, eth.chainManager, eth.blockPool)
|
||||||
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
protocols := []p2p.Protocol{ethProto, eth.whisper.Protocol()}
|
||||||
nat, err := p2p.ParseNAT(config.NATType, config.PMPGateway)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
netprv := config.NodeKey
|
netprv := config.NodeKey
|
||||||
if netprv == nil {
|
if netprv == nil {
|
||||||
if netprv, err = crypto.GenerateKey(); err != nil {
|
if netprv, err = crypto.GenerateKey(); err != nil {
|
||||||
@ -163,7 +159,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||||||
MaxPeers: config.MaxPeers,
|
MaxPeers: config.MaxPeers,
|
||||||
Protocols: protocols,
|
Protocols: protocols,
|
||||||
Blacklist: eth.blacklist,
|
Blacklist: eth.blacklist,
|
||||||
NAT: nat,
|
NAT: config.NAT,
|
||||||
NoDial: !config.Dial,
|
NoDial: !config.Dial,
|
||||||
BootstrapNodes: config.parseBootNodes(),
|
BootstrapNodes: config.parseBootNodes(),
|
||||||
}
|
}
|
||||||
|
23
p2p/nat.go
23
p2p/nat.go
@ -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
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
341
p2p/natupnp.go
341
p2p/natupnp.go
@ -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
|
|
||||||
}
|
|
@ -13,13 +13,12 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultDialTimeout = 10 * time.Second
|
defaultDialTimeout = 10 * time.Second
|
||||||
refreshPeersInterval = 30 * time.Second
|
refreshPeersInterval = 30 * time.Second
|
||||||
portMappingUpdateInterval = 15 * time.Minute
|
|
||||||
portMappingTimeout = 20 * time.Minute
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var srvlog = logger.NewLogger("P2P Server")
|
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
|
// If set to a non-nil value, the given NAT port mapper
|
||||||
// is used to make the listening port available to the
|
// is used to make the listening port available to the
|
||||||
// Internet.
|
// Internet.
|
||||||
NAT NAT
|
NAT nat.Interface
|
||||||
|
|
||||||
// If Dialer is set to a non-nil value, the given Dialer
|
// If Dialer is set to a non-nil value, the given Dialer
|
||||||
// is used to dial outbound peer connections.
|
// is used to dial outbound peer connections.
|
||||||
@ -89,7 +88,6 @@ type Server struct {
|
|||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
running bool
|
running bool
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
laddr *net.TCPAddr // real listen addr
|
|
||||||
peers map[discover.NodeID]*Peer
|
peers map[discover.NodeID]*Peer
|
||||||
|
|
||||||
ntab *discover.Table
|
ntab *discover.Table
|
||||||
@ -100,16 +98,6 @@ type Server struct {
|
|||||||
peerConnect chan *discover.Node
|
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 handshakeFunc func(io.ReadWriter, *ecdsa.PrivateKey, *discover.Node) (discover.NodeID, []byte, error)
|
||||||
type newPeerHook func(*Peer)
|
type newPeerHook func(*Peer)
|
||||||
|
|
||||||
@ -220,14 +208,17 @@ func (srv *Server) startListening() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
srv.ListenAddr = listener.Addr().String()
|
laddr := listener.Addr().(*net.TCPAddr)
|
||||||
srv.laddr = listener.Addr().(*net.TCPAddr)
|
srv.ListenAddr = laddr.String()
|
||||||
srv.listener = listener
|
srv.listener = listener
|
||||||
srv.loopWG.Add(1)
|
srv.loopWG.Add(1)
|
||||||
go srv.listenLoop()
|
go srv.listenLoop()
|
||||||
if !srv.laddr.IP.IsLoopback() && srv.NAT != nil {
|
if !laddr.IP.IsLoopback() && srv.NAT != nil {
|
||||||
srv.loopWG.Add(1)
|
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
|
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() {
|
func (srv *Server) dialLoop() {
|
||||||
defer srv.loopWG.Done()
|
defer srv.loopWG.Done()
|
||||||
refresh := time.NewTicker(refreshPeersInterval)
|
refresh := time.NewTicker(refreshPeersInterval)
|
||||||
|
Loading…
Reference in New Issue
Block a user