Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
dfa778fe
Commit
dfa778fe
authored
Feb 01, 2014
by
obscuren
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
UPNP wip
parent
8c4746a3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
484 additions
and
30 deletions
+484
-30
ethereum.go
ethereum.go
+59
-5
peer.go
peer.go
+20
-25
upnp.go
upnp.go
+405
-0
No files found.
ethereum.go
View file @
dfa778fe
...
...
@@ -8,6 +8,7 @@ import (
"github.com/ethereum/ethwire-go"
"log"
"net"
"strconv"
"sync/atomic"
"time"
)
...
...
@@ -40,6 +41,10 @@ type Ethereum struct {
peers
*
list
.
List
// Nonce
Nonce
uint64
Addr
net
.
Addr
nat
NAT
}
func
New
()
(
*
Ethereum
,
error
)
{
...
...
@@ -51,12 +56,20 @@ func New() (*Ethereum, error) {
ethutil
.
Config
.
Db
=
db
/*
nat, err := Discover()
if err != nil {
log.Printf("Can'them discover upnp: %v", err)
}
*/
nonce
,
_
:=
ethutil
.
RandomUint64
()
ethereum
:=
&
Ethereum
{
shutdownChan
:
make
(
chan
bool
),
db
:
db
,
peers
:
list
.
New
(),
Nonce
:
nonce
,
//nat: nat,
}
ethereum
.
TxPool
=
ethchain
.
NewTxPool
()
ethereum
.
TxPool
.
Speaker
=
ethereum
...
...
@@ -179,18 +192,59 @@ func (s *Ethereum) ReapDeadPeers() {
}
}
// FIXME
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
.
Printf
(
"can't add UPnP port mapping: %v
\n
"
,
err
)
}
if
first
&&
err
==
nil
{
externalip
,
err
:=
s
.
nat
.
GetExternalAddress
()
if
err
!=
nil
{
log
.
Printf
(
"UPnP can't get external address: %v
\n
"
,
err
)
continue
out
}
// externalip, listenport
log
.
Println
(
"Successfully bound via UPnP to"
,
externalip
,
listenPort
)
first
=
false
}
timer
.
Reset
(
time
.
Minute
*
15
)
case
<-
s
.
shutdownChan
:
break
out
}
}
timer
.
Stop
()
if
err
:=
s
.
nat
.
DeletePortMapping
(
"tcp"
,
int
(
lport
),
int
(
lport
));
err
!=
nil
{
log
.
Printf
(
"unable to remove UPnP port mapping: %v
\n
"
,
err
)
}
else
{
log
.
Printf
(
"succesfully disestablished UPnP port mapping
\n
"
)
}
}
// Start the ethereum
func
(
s
*
Ethereum
)
Start
()
{
// Bind to addr and port
ln
,
err
:=
net
.
Listen
(
"tcp"
,
":30303"
)
if
err
!=
nil
{
// This is mainly for testing to create a "network"
if
ethutil
.
Config
.
Debug
{
log
.
Println
(
"Connection listening disabled. Acting as client"
)
}
else
{
log
.
Fatal
(
err
)
}
//
if ethutil.Config.Debug {
//
log.Println("Connection listening disabled. Acting as client")
//
} else {
log
.
Fatal
(
err
)
//
}
}
else
{
s
.
Addr
=
ln
.
Addr
()
// Starting accepting connections
go
func
()
{
log
.
Println
(
"Ready and accepting connections"
)
...
...
peer.go
View file @
dfa778fe
...
...
@@ -253,22 +253,21 @@ out:
case
ethwire
.
MsgPeersTy
:
// Received a list of peers (probably because MsgGetPeersTy was send)
// Only act on message if we actually requested for a peers list
if
p
.
requestedPeerList
{
data
:=
msg
.
Data
// Create new list of possible peers for the ethereum to process
peers
:=
make
([]
string
,
data
.
Length
())
// Parse each possible peer
for
i
:=
0
;
i
<
data
.
Length
();
i
++
{
peers
[
i
]
=
unpackAddr
(
data
.
Get
(
i
)
.
Get
(
0
)
.
AsBytes
(),
data
.
Get
(
i
)
.
Get
(
1
)
.
AsUint
())
log
.
Println
(
peers
[
i
])
}
//if p.requestedPeerList {
data
:=
msg
.
Data
// Create new list of possible peers for the ethereum to process
peers
:=
make
([]
string
,
data
.
Length
())
// Parse each possible peer
for
i
:=
0
;
i
<
data
.
Length
();
i
++
{
peers
[
i
]
=
unpackAddr
(
data
.
Get
(
i
)
.
Get
(
0
),
data
.
Get
(
i
)
.
Get
(
1
)
.
AsUint
())
}
// Connect to the list of peers
p
.
ethereum
.
ProcessPeerList
(
peers
)
// Mark unrequested again
p
.
requestedPeerList
=
false
// Connect to the list of peers
p
.
ethereum
.
ProcessPeerList
(
peers
)
// Mark unrequested again
p
.
requestedPeerList
=
false
}
//
}
case
ethwire
.
MsgGetChainTy
:
var
parent
*
ethchain
.
Block
// Length minus one since the very last element in the array is a count
...
...
@@ -326,15 +325,11 @@ func packAddr(address, port string) ([]byte, uint16) {
return
host
,
uint16
(
prt
)
}
func
unpackAddr
(
h
[]
byte
,
p
uint64
)
string
{
if
len
(
h
)
!=
4
{
return
""
}
a
:=
strconv
.
Itoa
(
int
(
h
[
0
]))
b
:=
strconv
.
Itoa
(
int
(
h
[
1
]))
c
:=
strconv
.
Itoa
(
int
(
h
[
2
]))
d
:=
strconv
.
Itoa
(
int
(
h
[
3
]))
func
unpackAddr
(
value
*
ethutil
.
RlpValue
,
p
uint64
)
string
{
a
:=
strconv
.
Itoa
(
int
(
value
.
Get
(
0
)
.
AsUint
()))
b
:=
strconv
.
Itoa
(
int
(
value
.
Get
(
1
)
.
AsUint
()))
c
:=
strconv
.
Itoa
(
int
(
value
.
Get
(
2
)
.
AsUint
()))
d
:=
strconv
.
Itoa
(
int
(
value
.
Get
(
3
)
.
AsUint
()))
host
:=
strings
.
Join
([]
string
{
a
,
b
,
c
,
d
},
"."
)
port
:=
strconv
.
Itoa
(
int
(
p
))
...
...
@@ -349,9 +344,9 @@ func (p *Peer) Start(seed bool) {
if
peerHost
==
servHost
{
log
.
Println
(
"Connected to self"
)
//
p.Stop()
p
.
Stop
()
//
return
return
}
if
p
.
inbound
{
...
...
upnp.go
0 → 100644
View file @
dfa778fe
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
//
import
(
"bytes"
"encoding/xml"
"errors"
"net"
"net/http"
"os"
"strconv"
"strings"
"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
{
serviceURL
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
)
{
ssdp
,
err
:=
net
.
ResolveUDPAddr
(
"udp4"
,
"239.255.255.250:1900"
)
if
err
!=
nil
{
return
}
conn
,
err
:=
net
.
ListenPacket
(
"udp4"
,
":0"
)
if
err
!=
nil
{
return
}
socket
:=
conn
.
(
*
net
.
UDPConn
)
defer
socket
.
Close
()
err
=
socket
.
SetDeadline
(
time
.
Now
()
.
Add
(
3
*
time
.
Second
))
if
err
!=
nil
{
return
}
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
<
3
;
i
++
{
_
,
err
=
socket
.
WriteToUDP
(
message
,
ssdp
)
if
err
!=
nil
{
return
}
var
n
int
n
,
_
,
err
=
socket
.
ReadFromUDP
(
answerBytes
)
if
err
!=
nil
{
continue
// socket.Close()
// return
}
answer
:=
string
(
answerBytes
[
0
:
n
])
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\n
location: "
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
}
var
ourIP
string
ourIP
,
err
=
getOurIP
()
if
err
!=
nil
{
return
}
nat
=
&
upnpNAT
{
serviceURL
:
serviceURL
,
ourIP
:
ourIP
}
return
}
err
=
errors
.
New
(
"UPnP port discovery failed"
)
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
}
// getChildDevice searches the children of device for a device with the given
// type.
func
getChildDevice
(
d
*
device
,
deviceType
string
)
*
device
{
for
i
:=
range
d
.
DeviceList
.
Device
{
if
d
.
DeviceList
.
Device
[
i
]
.
DeviceType
==
deviceType
{
return
&
d
.
DeviceList
.
Device
[
i
]
}
}
return
nil
}
// getChildDevice searches the service list of device for a service with the
// given type.
func
getChildService
(
d
*
device
,
serviceType
string
)
*
service
{
for
i
:=
range
d
.
ServiceList
.
Service
{
if
d
.
ServiceList
.
Service
[
i
]
.
ServiceType
==
serviceType
{
return
&
d
.
ServiceList
.
Service
[
i
]
}
}
return
nil
}
// getOurIP returns a best guess at what the local IP is.
func
getOurIP
()
(
ip
string
,
err
error
)
{
hostname
,
err
:=
os
.
Hostname
()
if
err
!=
nil
{
return
}
return
net
.
LookupCNAME
(
hostname
)
}
// 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
)
{
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
}
// combineURL appends subURL onto rootURL.
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
}
// soapBody represents the <s:Body> element in a SOAP reply.
// 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
\"
?>"
+
"<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
nil
,
err
}
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
nil
,
err
}
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
}
var
reply
soapEnvelope
err
=
xml
.
NewDecoder
(
r
.
Body
)
.
Decode
(
&
reply
)
if
err
!=
nil
{
return
nil
,
err
}
return
reply
.
Body
.
Data
,
nil
}
// 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
)
{
message
:=
"<u:GetExternalIPAddress xmlns:u=
\"
urn:schemas-upnp-org:service:WANIPConnection:1
\"
/>
\r\n
"
response
,
err
:=
soapRequest
(
n
.
serviceURL
,
"GetExternalIPAddress"
,
message
)
if
err
!=
nil
{
return
nil
,
err
}
var
reply
getExternalIPAddressResponse
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
)
{
// 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
(
externalPort
)
message
+=
"</NewExternalPort><NewProtocol>"
+
protocol
+
"</NewProtocol>"
message
+=
"<NewInternalPort>"
+
strconv
.
Itoa
(
internalPort
)
+
"</NewInternalPort>"
+
"<NewInternalClient>"
+
n
.
ourIP
+
"</NewInternalClient>"
+
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
message
+=
description
+
"</NewPortMappingDescription><NewLeaseDuration>"
+
strconv
.
Itoa
(
timeout
)
+
"</NewLeaseDuration></u:AddPortMapping>"
response
,
err
:=
soapRequest
(
n
.
serviceURL
,
"AddPortMapping"
,
message
)
if
err
!=
nil
{
return
}
// 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
// it. Not sure about wildcard yet. miniupnpc just checks for error
// codes here.
mappedExternalPort
=
externalPort
_
=
response
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
)
{
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>"
response
,
err
:=
soapRequest
(
n
.
serviceURL
,
"DeletePortMapping"
,
message
)
if
err
!=
nil
{
return
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)
_
=
response
return
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment