Implemented support for UPnP
This commit is contained in:
parent
ae0d4eb7aa
commit
3f503ffc7f
62
ethereum.go
62
ethereum.go
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/ethereum/ethwire-go"
|
"github.com/ethereum/ethwire-go"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -29,6 +30,7 @@ const (
|
|||||||
type Ethereum struct {
|
type Ethereum struct {
|
||||||
// Channel for shutting down the ethereum
|
// Channel for shutting down the ethereum
|
||||||
shutdownChan chan bool
|
shutdownChan chan bool
|
||||||
|
quit chan bool
|
||||||
// DB interface
|
// DB interface
|
||||||
//db *ethdb.LDBDatabase
|
//db *ethdb.LDBDatabase
|
||||||
db *ethdb.MemDatabase
|
db *ethdb.MemDatabase
|
||||||
@ -48,6 +50,8 @@ type Ethereum struct {
|
|||||||
|
|
||||||
// Capabilities for outgoing peers
|
// Capabilities for outgoing peers
|
||||||
serverCaps Caps
|
serverCaps Caps
|
||||||
|
|
||||||
|
nat NAT
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(caps Caps) (*Ethereum, error) {
|
func New(caps Caps) (*Ethereum, error) {
|
||||||
@ -57,15 +61,30 @@ func New(caps Caps) (*Ethereum, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gateway := net.ParseIP("192.168.192.1")
|
||||||
|
nat := NewNatPMP(gateway)
|
||||||
|
port, err := nat.AddPortMapping("tcp", 30303, 30303, "", 60)
|
||||||
|
log.Println(port, err)
|
||||||
|
*/
|
||||||
|
|
||||||
|
nat, err := Discover()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("UPnP failed", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ethutil.Config.Db = db
|
ethutil.Config.Db = db
|
||||||
|
|
||||||
nonce, _ := ethutil.RandomUint64()
|
nonce, _ := ethutil.RandomUint64()
|
||||||
ethereum := &Ethereum{
|
ethereum := &Ethereum{
|
||||||
shutdownChan: make(chan bool),
|
shutdownChan: make(chan bool),
|
||||||
|
quit: make(chan bool),
|
||||||
db: db,
|
db: db,
|
||||||
peers: list.New(),
|
peers: list.New(),
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
serverCaps: caps,
|
serverCaps: caps,
|
||||||
|
nat: nat,
|
||||||
}
|
}
|
||||||
ethereum.TxPool = ethchain.NewTxPool()
|
ethereum.TxPool = ethchain.NewTxPool()
|
||||||
ethereum.TxPool.Speaker = ethereum
|
ethereum.TxPool.Speaker = ethereum
|
||||||
@ -217,6 +236,8 @@ func (s *Ethereum) Start() {
|
|||||||
go s.peerHandler(ln)
|
go s.peerHandler(ln)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go s.upnpUpdateThread()
|
||||||
|
|
||||||
// Start the reaping processes
|
// Start the reaping processes
|
||||||
go s.ReapDeadPeerHandler()
|
go s.ReapDeadPeerHandler()
|
||||||
|
|
||||||
@ -245,6 +266,8 @@ func (s *Ethereum) Stop() {
|
|||||||
p.Stop()
|
p.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
close(s.quit)
|
||||||
|
|
||||||
s.shutdownChan <- true
|
s.shutdownChan <- true
|
||||||
|
|
||||||
s.TxPool.Stop()
|
s.TxPool.Stop()
|
||||||
@ -254,3 +277,42 @@ func (s *Ethereum) Stop() {
|
|||||||
func (s *Ethereum) WaitForShutdown() {
|
func (s *Ethereum) WaitForShutdown() {
|
||||||
<-s.shutdownChan
|
<-s.shutdownChan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Ethereum) upnpUpdateThread() {
|
||||||
|
// Go off immediately to prevent code duplication, thereafter we renew
|
||||||
|
// lease every 15 minutes.
|
||||||
|
timer := time.NewTimer(0 * time.Second)
|
||||||
|
lport, _ := strconv.ParseInt("30303", 10, 16)
|
||||||
|
first := true
|
||||||
|
out:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
listenPort, err := s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("can't add UPnP port mapping:", err)
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
if first && err == nil {
|
||||||
|
externalip, err := s.nat.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("UPnP can't get external address:", err)
|
||||||
|
continue out
|
||||||
|
}
|
||||||
|
log.Println("Successfully bound via UPnP to", externalip, listenPort)
|
||||||
|
first = false
|
||||||
|
}
|
||||||
|
timer.Reset(time.Minute * 15)
|
||||||
|
case <-s.quit:
|
||||||
|
break out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
|
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
|
||||||
|
log.Println("unable to remove UPnP port mapping:", err)
|
||||||
|
} else {
|
||||||
|
log.Println("succesfully disestablished UPnP port mapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
12
nat.go
Normal file
12
nat.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// protocol is either "udp" or "tcp"
|
||||||
|
type NAT interface {
|
||||||
|
GetExternalAddress() (addr net.IP, err error)
|
||||||
|
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
||||||
|
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
||||||
|
}
|
54
natpmp.go
Normal file
54
natpmp.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
natpmp "code.google.com/p/go-nat-pmp"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
type natPMPClient struct {
|
||||||
|
client *natpmp.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNatPMP(gateway net.IP) (nat NAT) {
|
||||||
|
return &natPMPClient{natpmp.NewClient(gateway)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *natPMPClient) GetExternalAddress() (addr net.IP, err error) {
|
||||||
|
response, err := n.client.GetExternalAddress()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip := response.ExternalIPAddress
|
||||||
|
addr = net.IPv4(ip[0], ip[1], ip[2], ip[3])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *natPMPClient) AddPortMapping(protocol string, externalPort, internalPort int,
|
||||||
|
description string, timeout int) (mappedExternalPort int, err error) {
|
||||||
|
if timeout <= 0 {
|
||||||
|
err = fmt.Errorf("timeout must not be <= 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Note order of port arguments is switched between our AddPortMapping and the client's AddPortMapping.
|
||||||
|
response, err := n.client.AddPortMapping(protocol, internalPort, externalPort, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mappedExternalPort = int(response.MappedExternalPort)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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,34 +1,5 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
// Upnp code taken from Taipei Torrent license is below:
|
|
||||||
// Copyright (c) 2010 Jack Palevich. All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// Just enough UPnP to be able to forward ports
|
// Just enough UPnP to be able to forward ports
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -44,27 +15,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NAT is an interface representing a NAT traversal options for example UPNP or
|
|
||||||
// NAT-PMP. It provides methods to query and manipulate this traversal to allow
|
|
||||||
// access to services.
|
|
||||||
type NAT interface {
|
|
||||||
// Get the external address from outside the NAT.
|
|
||||||
GetExternalAddress() (addr net.IP, err error)
|
|
||||||
// Add a port mapping for protocol ("udp" or "tcp") from externalport to
|
|
||||||
// internal port with description lasting for timeout.
|
|
||||||
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
|
|
||||||
// Remove a previously added port mapping from externalport to
|
|
||||||
// internal port.
|
|
||||||
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type upnpNAT struct {
|
type upnpNAT struct {
|
||||||
serviceURL string
|
serviceURL string
|
||||||
ourIP string
|
ourIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover searches the local network for a UPnP router returning a NAT
|
|
||||||
// for the network if so, nil if not.
|
|
||||||
func Discover() (nat NAT, err error) {
|
func Discover() (nat NAT, err error) {
|
||||||
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -77,7 +32,7 @@ func Discover() (nat NAT, err error) {
|
|||||||
socket := conn.(*net.UDPConn)
|
socket := conn.(*net.UDPConn)
|
||||||
defer socket.Close()
|
defer socket.Close()
|
||||||
|
|
||||||
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
|
err = socket.SetDeadline(time.Now().Add(10 * time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -134,7 +89,7 @@ func Discover() (nat NAT, err error) {
|
|||||||
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
|
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = errors.New("UPnP port discovery failed")
|
err = errors.New("UPnP port discovery failed.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,39 +145,38 @@ type root struct {
|
|||||||
Device device
|
Device device
|
||||||
}
|
}
|
||||||
|
|
||||||
// getChildDevice searches the children of device for a device with the given
|
|
||||||
// type.
|
|
||||||
func getChildDevice(d *device, deviceType string) *device {
|
func getChildDevice(d *device, deviceType string) *device {
|
||||||
for i := range d.DeviceList.Device {
|
dl := d.DeviceList.Device
|
||||||
if d.DeviceList.Device[i].DeviceType == deviceType {
|
for i := 0; i < len(dl); i++ {
|
||||||
return &d.DeviceList.Device[i]
|
if dl[i].DeviceType == deviceType {
|
||||||
|
return &dl[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getChildDevice searches the service list of device for a service with the
|
|
||||||
// given type.
|
|
||||||
func getChildService(d *device, serviceType string) *service {
|
func getChildService(d *device, serviceType string) *service {
|
||||||
for i := range d.ServiceList.Service {
|
sl := d.ServiceList.Service
|
||||||
if d.ServiceList.Service[i].ServiceType == serviceType {
|
for i := 0; i < len(sl); i++ {
|
||||||
return &d.ServiceList.Service[i]
|
if sl[i].ServiceType == serviceType {
|
||||||
|
return &sl[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOurIP returns a best guess at what the local IP is.
|
|
||||||
func getOurIP() (ip string, err error) {
|
func getOurIP() (ip string, err error) {
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return net.LookupCNAME(hostname)
|
p, err := net.LookupIP(hostname)
|
||||||
|
if err != nil && len(p) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return p[0].String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getServiceURL parses the xml description at the given root url to find the
|
|
||||||
// url for the WANIPConnection service to be used for port forwarding.
|
|
||||||
func getServiceURL(rootURL string) (url string, err error) {
|
func getServiceURL(rootURL string) (url string, err error) {
|
||||||
r, err := http.Get(rootURL)
|
r, err := http.Get(rootURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -235,34 +189,34 @@ func getServiceURL(rootURL string) (url string, err error) {
|
|||||||
}
|
}
|
||||||
var root root
|
var root root
|
||||||
err = xml.NewDecoder(r.Body).Decode(&root)
|
err = xml.NewDecoder(r.Body).Decode(&root)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a := &root.Device
|
a := &root.Device
|
||||||
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
|
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
|
||||||
err = errors.New("no InternetGatewayDevice")
|
err = errors.New("No InternetGatewayDevice")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
|
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
|
||||||
if b == nil {
|
if b == nil {
|
||||||
err = errors.New("no WANDevice")
|
err = errors.New("No WANDevice")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
|
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
|
||||||
if c == nil {
|
if c == nil {
|
||||||
err = errors.New("no WANConnectionDevice")
|
err = errors.New("No WANConnectionDevice")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
|
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
|
||||||
if d == nil {
|
if d == nil {
|
||||||
err = errors.New("no WANIPConnection")
|
err = errors.New("No WANIPConnection")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
url = combineURL(rootURL, d.ControlURL)
|
url = combineURL(rootURL, d.ControlURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// combineURL appends subURL onto rootURL.
|
|
||||||
func combineURL(rootURL, subURL string) string {
|
func combineURL(rootURL, subURL string) string {
|
||||||
protocolEnd := "://"
|
protocolEnd := "://"
|
||||||
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
protoEndIndex := strings.Index(rootURL, protocolEnd)
|
||||||
@ -271,24 +225,7 @@ func combineURL(rootURL, subURL string) string {
|
|||||||
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
|
||||||
}
|
}
|
||||||
|
|
||||||
// soapBody represents the <s:Body> element in a SOAP reply.
|
func soapRequest(url, function, message string) (r *http.Response, err error) {
|
||||||
// fields we don't care about are elided.
|
|
||||||
type soapBody struct {
|
|
||||||
XMLName xml.Name `xml:"Body"`
|
|
||||||
Data []byte `xml:",innerxml"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// soapEnvelope represents the <s:Envelope> element in a SOAP reply.
|
|
||||||
// fields we don't care about are elided.
|
|
||||||
type soapEnvelope struct {
|
|
||||||
XMLName xml.Name `xml:"Envelope"`
|
|
||||||
Body soapBody `xml:"Body"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// soapRequests performs a soap request with the given parameters and returns
|
|
||||||
// the xml replied stripped of the soap headers. in the case that the request is
|
|
||||||
// unsuccessful the an error is returned.
|
|
||||||
func soapRequest(url, function, message string) (replyXML []byte, err error) {
|
|
||||||
fullMessage := "<?xml version=\"1.0\" ?>" +
|
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: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>"
|
"<s:Body>" + message + "</s:Body></s:Envelope>"
|
||||||
@ -305,10 +242,10 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
|
|||||||
req.Header.Set("Cache-Control", "no-cache")
|
req.Header.Set("Cache-Control", "no-cache")
|
||||||
req.Header.Set("Pragma", "no-cache")
|
req.Header.Set("Pragma", "no-cache")
|
||||||
|
|
||||||
r, err := http.DefaultClient.Do(req)
|
// log.Stderr("soapRequest ", req)
|
||||||
if err != nil {
|
//fmt.Println(fullMessage)
|
||||||
return nil, err
|
|
||||||
}
|
r, err = http.DefaultClient.Do(req)
|
||||||
if r.Body != nil {
|
if r.Body != nil {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
}
|
}
|
||||||
@ -319,45 +256,39 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
|
|||||||
r = nil
|
r = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var reply soapEnvelope
|
return
|
||||||
err = xml.NewDecoder(r.Body).Decode(&reply)
|
}
|
||||||
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
return reply.Body.Data, nil
|
|
||||||
|
// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
|
||||||
|
|
||||||
|
response.Body.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// getExternalIPAddressResponse represents the XML response to a
|
|
||||||
// GetExternalIPAddress SOAP request.
|
|
||||||
type getExternalIPAddressResponse struct {
|
|
||||||
XMLName xml.Name `xml:"GetExternalIPAddressResponse"`
|
|
||||||
ExternalIPAddress string `xml:"NewExternalIPAddress"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetExternalAddress implements the NAT interface by fetching the external IP
|
|
||||||
// from the UPnP router.
|
|
||||||
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
|
||||||
message := "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>\r\n"
|
info, err := n.getStatusInfo()
|
||||||
response, err := soapRequest(n.serviceURL, "GetExternalIPAddress", message)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
addr = net.ParseIP(info.externalIpAddress)
|
||||||
var reply getExternalIPAddressResponse
|
return
|
||||||
err = xml.Unmarshal(response, &reply)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = net.ParseIP(reply.ExternalIPAddress)
|
|
||||||
if addr == nil {
|
|
||||||
return nil, errors.New("unable to parse ip address")
|
|
||||||
}
|
|
||||||
return addr, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPortMapping implements the NAT interface by setting up a port forwarding
|
|
||||||
// from the UPnP router to the local machine with the given ports and protocol.
|
|
||||||
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
|
||||||
// A single concatenation would break ARM compilation.
|
// A single concatenation would break ARM compilation.
|
||||||
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||||
@ -370,22 +301,19 @@ func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int
|
|||||||
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
|
||||||
"</NewLeaseDuration></u:AddPortMapping>"
|
"</NewLeaseDuration></u:AddPortMapping>"
|
||||||
|
|
||||||
response, err := soapRequest(n.serviceURL, "AddPortMapping", message)
|
var response *http.Response
|
||||||
|
response, err = soapRequest(n.serviceURL, "AddPortMapping", message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check response to see if the port was forwarded
|
// TODO: check response to see if the port was forwarded
|
||||||
// If the port was not wildcard we don't get an reply with the port in
|
// log.Println(message, response)
|
||||||
// it. Not sure about wildcard yet. miniupnpc just checks for error
|
|
||||||
// codes here.
|
|
||||||
mappedExternalPort = externalPort
|
mappedExternalPort = externalPort
|
||||||
_ = response
|
_ = response
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPortMapping implements the NAT interface by removing up a port forwarding
|
|
||||||
// from the UPnP router to the local machine with the given ports and.
|
|
||||||
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
|
||||||
|
|
||||||
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
|
||||||
@ -393,7 +321,8 @@ func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort
|
|||||||
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
|
||||||
"</u:DeletePortMapping>"
|
"</u:DeletePortMapping>"
|
||||||
|
|
||||||
response, err := soapRequest(n.serviceURL, "DeletePortMapping", message)
|
var response *http.Response
|
||||||
|
response, err = soapRequest(n.serviceURL, "DeletePortMapping", message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user