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