good morning!!!!
Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
B
bor
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package Registry
Container Registry
Harbor Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
open
bor
Commits
3f503ffc
Commit
3f503ffc
authored
11 years ago
by
Jeffrey Wilcke
Browse files
Options
Downloads
Patches
Plain Diff
Implemented support for UPnP
parent
ae0d4eb7
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
ethereum.go
+62
-0
62 additions, 0 deletions
ethereum.go
nat.go
+12
-0
12 additions, 0 deletions
nat.go
natpmp.go
+54
-0
54 additions, 0 deletions
natpmp.go
natupnp.go
+52
-123
52 additions, 123 deletions
natupnp.go
with
180 additions
and
123 deletions
ethereum.go
+
62
−
0
View file @
3f503ffc
...
...
@@ -8,6 +8,7 @@ import (
"github.com/ethereum/ethwire-go"
"log"
"net"
"strconv"
"sync"
"sync/atomic"
"time"
...
...
@@ -29,6 +30,7 @@ const (
type
Ethereum
struct
{
// Channel for shutting down the ethereum
shutdownChan
chan
bool
quit
chan
bool
// DB interface
//db *ethdb.LDBDatabase
db
*
ethdb
.
MemDatabase
...
...
@@ -48,6 +50,8 @@ type Ethereum struct {
// Capabilities for outgoing peers
serverCaps
Caps
nat
NAT
}
func
New
(
caps
Caps
)
(
*
Ethereum
,
error
)
{
...
...
@@ -57,15 +61,30 @@ func New(caps Caps) (*Ethereum, error) {
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
nonce
,
_
:=
ethutil
.
RandomUint64
()
ethereum
:=
&
Ethereum
{
shutdownChan
:
make
(
chan
bool
),
quit
:
make
(
chan
bool
),
db
:
db
,
peers
:
list
.
New
(),
Nonce
:
nonce
,
serverCaps
:
caps
,
nat
:
nat
,
}
ethereum
.
TxPool
=
ethchain
.
NewTxPool
()
ethereum
.
TxPool
.
Speaker
=
ethereum
...
...
@@ -217,6 +236,8 @@ func (s *Ethereum) Start() {
go
s
.
peerHandler
(
ln
)
}
go
s
.
upnpUpdateThread
()
// Start the reaping processes
go
s
.
ReapDeadPeerHandler
()
...
...
@@ -245,6 +266,8 @@ func (s *Ethereum) Stop() {
p
.
Stop
()
})
close
(
s
.
quit
)
s
.
shutdownChan
<-
true
s
.
TxPool
.
Stop
()
...
...
@@ -254,3 +277,42 @@ func (s *Ethereum) Stop() {
func
(
s
*
Ethereum
)
WaitForShutdown
()
{
<-
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"
)
}
}
This diff is collapsed.
Click to expand it.
nat.go
0 → 100644
+
12
−
0
View file @
3f503ffc
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
)
}
This diff is collapsed.
Click to expand it.
natpmp.go
0 → 100644
+
54
−
0
View file @
3f503ffc
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
}
This diff is collapsed.
Click to expand it.
upnp.go
→
nat
upnp.go
+
52
−
123
View file @
3f503ffc
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
//
...
...
@@ -44,27 +15,11 @@ import (
"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
{
...
...
@@ -77,7 +32,7 @@ func Discover() (nat NAT, err error) {
socket
:=
conn
.
(
*
net
.
UDPConn
)
defer
socket
.
Close
()
err
=
socket
.
SetDeadline
(
time
.
Now
()
.
Add
(
3
*
time
.
Second
))
err
=
socket
.
SetDeadline
(
time
.
Now
()
.
Add
(
10
*
time
.
Second
))
if
err
!=
nil
{
return
}
...
...
@@ -134,7 +89,7 @@ func Discover() (nat NAT, err error) {
nat
=
&
upnpNAT
{
serviceURL
:
serviceURL
,
ourIP
:
ourIP
}
return
}
err
=
errors
.
New
(
"UPnP port discovery failed"
)
err
=
errors
.
New
(
"UPnP port discovery failed
.
"
)
return
}
...
...
@@ -190,39 +145,38 @@ type root struct {
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
]
dl
:=
d
.
DeviceList
.
Device
for
i
:=
0
;
i
<
len
(
dl
);
i
++
{
if
dl
[
i
]
.
DeviceType
==
deviceType
{
return
&
dl
[
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
]
sl
:=
d
.
ServiceList
.
Service
for
i
:=
0
;
i
<
len
(
sl
);
i
++
{
if
sl
[
i
]
.
ServiceType
==
serviceType
{
return
&
sl
[
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
)
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
)
{
r
,
err
:=
http
.
Get
(
rootURL
)
if
err
!=
nil
{
...
...
@@ -235,34 +189,34 @@ func getServiceURL(rootURL string) (url string, err error) {
}
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
(
"
n
o InternetGatewayDevice"
)
err
=
errors
.
New
(
"
N
o InternetGatewayDevice"
)
return
}
b
:=
getChildDevice
(
a
,
"urn:schemas-upnp-org:device:WANDevice:1"
)
if
b
==
nil
{
err
=
errors
.
New
(
"
n
o WANDevice"
)
err
=
errors
.
New
(
"
N
o WANDevice"
)
return
}
c
:=
getChildDevice
(
b
,
"urn:schemas-upnp-org:device:WANConnectionDevice:1"
)
if
c
==
nil
{
err
=
errors
.
New
(
"
n
o WANConnectionDevice"
)
err
=
errors
.
New
(
"
N
o WANConnectionDevice"
)
return
}
d
:=
getChildService
(
c
,
"urn:schemas-upnp-org:service:WANIPConnection:1"
)
if
d
==
nil
{
err
=
errors
.
New
(
"
n
o WANIPConnection"
)
err
=
errors
.
New
(
"
N
o 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
)
...
...
@@ -271,24 +225,7 @@ func combineURL(rootURL, subURL string) string {
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
)
{
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>"
...
...
@@ -305,10 +242,10 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
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
}
// log.Stderr("soapRequest ",
req)
//fmt.Println(fullMessage)
r
,
err
=
http
.
DefaultClient
.
Do
(
req
)
if
r
.
Body
!=
nil
{
defer
r
.
Body
.
Close
()
}
...
...
@@ -319,45 +256,39 @@ func soapRequest(url, function, message string) (replyXML []byte, err error) {
r
=
nil
return
}
var
reply
soapEnvelope
err
=
xml
.
NewDecoder
(
r
.
Body
)
.
Decode
(
&
reply
)
if
err
!=
nil
{
return
nil
,
err
}
return
reply
.
Body
.
Data
,
nil
return
}
// getExternalIPAddressResponse represents the XML response to a
// GetExternalIPAddress SOAP request.
type
getExternalIPAddressResponse
struct
{
XMLName
xml
.
Name
`xml:"GetExternalIPAddressResponse"`
ExternalIPAddress
string
`xml:"NewExternalIPAddress"`
type
statusInfo
struct
{
externalIpAddress
string
}
// 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
}
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
re
ply
getExternalIPAddress
Response
err
=
xml
.
Unmarshal
(
response
,
&
reply
)
var
re
sponse
*
http
.
Response
response
,
err
=
soapRequest
(
n
.
serviceURL
,
"GetStatusInfo"
,
message
)
if
err
!=
nil
{
return
nil
,
err
return
}
addr
=
net
.
ParseIP
(
reply
.
ExternalIPAddress
)
if
addr
==
nil
{
return
nil
,
errors
.
New
(
"unable to parse ip address"
)
// TODO: Write a soap reply parser. It has to eat the Body and envelope tags...
response
.
Body
.
Close
()
return
}
func
(
n
*
upnpNAT
)
GetExternalAddress
()
(
addr
net
.
IP
,
err
error
)
{
info
,
err
:=
n
.
getStatusInfo
()
if
err
!=
nil
{
return
}
return
addr
,
nil
addr
=
net
.
ParseIP
(
info
.
externalIpAddress
)
return
}
// 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
"
+
...
...
@@ -370,22 +301,19 @@ func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int
"</NewPortMappingDescription><NewLeaseDuration>"
+
strconv
.
Itoa
(
timeout
)
+
"</NewLeaseDuration></u:AddPortMapping>"
response
,
err
:=
soapRequest
(
n
.
serviceURL
,
"AddPortMapping"
,
message
)
var
response
*
http
.
Response
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.
// log.Println(message, response)
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
"
+
...
...
@@ -393,7 +321,8 @@ func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort
"</NewExternalPort><NewProtocol>"
+
protocol
+
"</NewProtocol>"
+
"</u:DeletePortMapping>"
response
,
err
:=
soapRequest
(
n
.
serviceURL
,
"DeletePortMapping"
,
message
)
var
response
*
http
.
Response
response
,
err
=
soapRequest
(
n
.
serviceURL
,
"DeletePortMapping"
,
message
)
if
err
!=
nil
{
return
}
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment