mirror of https://github.com/ethereum/go-ethereum
This deletes the old NAT implementation.pull/292/head
parent
1543833ca0
commit
d0a2e655c9
@ -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 |
|
||||||
} |
|
@ -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 |
|
||||||
} |
|
Loading…
Reference in new issue