Skip to content

Commit 8e8f85a

Browse files
authored
Merge pull request #181 from r4f4ss/stun
Add stun server for ip discovery and flag stun
2 parents ee38628 + 4549996 commit 8e8f85a

8 files changed

Lines changed: 203 additions & 2 deletions

File tree

cmd/shisui/main.go

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"slices"
1414
"strings"
1515
"syscall"
16+
"time"
1617

1718
"os"
1819

@@ -249,8 +250,13 @@ func initDiscV5(config Config, conn discover.UDPConn) (*discover.UDPv5, *enode.L
249250
}
250251

251252
localNode := enode.NewLocalNode(nodeDB, config.PrivateKey)
252-
localNode.SetFallbackIP(net.IP{127, 0, 0, 1})
253+
253254
localNode.Set(discover.Tag)
255+
listenerAddr := conn.LocalAddr().(*net.UDPAddr)
256+
nat := config.Protocol.NAT
257+
if nat != nil && !listenerAddr.IP.IsLoopback() {
258+
doPortMapping(nat, localNode, listenerAddr)
259+
}
254260

255261
discV5, err := discover.ListenV5(conn, localNode, discCfg)
256262
if err != nil {
@@ -259,6 +265,57 @@ func initDiscV5(config Config, conn discover.UDPConn) (*discover.UDPv5, *enode.L
259265
return discV5, localNode, nil
260266
}
261267

268+
func doPortMapping(natm nat.Interface, ln *enode.LocalNode, addr *net.UDPAddr) {
269+
const (
270+
protocol = "udp"
271+
name = "ethereum discovery"
272+
)
273+
274+
var (
275+
intport = addr.Port
276+
extaddr = &net.UDPAddr{IP: addr.IP, Port: addr.Port}
277+
mapTimeout = nat.DefaultMapTimeout
278+
)
279+
addMapping := func() {
280+
// Get the external address.
281+
var err error
282+
extaddr.IP, err = natm.ExternalIP()
283+
if err != nil {
284+
log.Debug("Couldn't get external IP", "err", err)
285+
return
286+
}
287+
// Create the mapping.
288+
p, err := natm.AddMapping(protocol, extaddr.Port, intport, name, mapTimeout)
289+
if err != nil {
290+
log.Debug("Couldn't add port mapping", "err", err)
291+
return
292+
}
293+
if p != uint16(extaddr.Port) {
294+
extaddr.Port = int(p)
295+
log.Info("NAT mapped alternative port")
296+
} else {
297+
log.Info("NAT mapped port")
298+
}
299+
// Update IP/port information of the local node.
300+
ln.SetStaticIP(extaddr.IP)
301+
ln.SetFallbackUDP(extaddr.Port)
302+
}
303+
304+
// Perform mapping once, synchronously.
305+
log.Info("Attempting port mapping")
306+
addMapping()
307+
308+
// Refresh the mapping periodically.
309+
go func() {
310+
refresh := time.NewTimer(mapTimeout)
311+
defer refresh.Stop()
312+
for range refresh.C {
313+
addMapping()
314+
refresh.Reset(mapTimeout)
315+
}
316+
}()
317+
}
318+
262319
func initHistory(config Config, server *rpc.Server, conn discover.UDPConn, localNode *enode.LocalNode, discV5 *discover.UDPv5) (*history.HistoryNetwork, error) {
263320
networkName := portalwire.History.Name()
264321
db, err := history.NewDB(config.DataDir, networkName)

cmd/shisui/main_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"net"
5+
"testing"
6+
7+
"github.com/ethereum/go-ethereum/crypto"
8+
"github.com/ethereum/go-ethereum/p2p/enode"
9+
"github.com/ethereum/go-ethereum/p2p/nat"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func newLocalNodeForTesting() (*enode.LocalNode, *enode.DB) {
14+
db, _ := enode.OpenDB("")
15+
key, _ := crypto.GenerateKey()
16+
return enode.NewLocalNode(db, key), db
17+
}
18+
19+
func TestDoPortMapping(t *testing.T) {
20+
nat := nat.ExtIP{33, 44, 55, 66}
21+
localNode, _ := newLocalNodeForTesting()
22+
listenerAddr := &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: 1234}
23+
24+
doPortMapping(nat, localNode, listenerAddr)
25+
26+
assert.Equal(t, localNode.Seq(), uint64(1))
27+
assert.Equal(t, localNode.Node().IP(), net.IP{33, 44, 55, 66})
28+
assert.Equal(t, localNode.Node().UDP(), 1234)
29+
assert.Equal(t, localNode.Node().TCP(), 0)
30+
31+
_ = localNode.Node().UDP()
32+
assert.Equal(t, localNode.Seq(), uint64(2))
33+
}

cmd/utils/flags.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
982982

983983
PortalNATFlag = &cli.StringFlag{
984984
Name: "nat",
985-
Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:<IP>|extip:<IP>)",
985+
Usage: "NAT port mapping mechanism (any|none|upnp|pmp|stun|pmp:<IP>|extip:<IP>|stun:<IP>)",
986986
Value: "any",
987987
Category: flags.PortalNetworkCategory,
988988
}

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ require (
5656
github.com/olekukonko/tablewriter v0.0.5
5757
github.com/optimism-java/utp-go v0.0.0-20240716050942-7583a3d702fd
5858
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
59+
github.com/pion/stun v0.6.1
5960
github.com/protolambda/bls12-381-util v0.1.0
6061
github.com/protolambda/zrnt v0.32.2
6162
github.com/protolambda/ztyp v0.2.2
@@ -136,6 +137,9 @@ require (
136137
github.com/mmcloughlin/addchain v0.4.0 // indirect
137138
github.com/naoina/go-stringutil v0.1.0 // indirect
138139
github.com/opentracing/opentracing-go v1.1.0 // indirect
140+
github.com/pion/dtls/v2 v2.2.7 // indirect
141+
github.com/pion/logging v0.2.2 // indirect
142+
github.com/pion/transport/v2 v2.2.1 // indirect
139143
github.com/pkg/errors v0.9.1 // indirect
140144
github.com/pmezard/go-difflib v1.0.0 // indirect
141145
github.com/prometheus/client_golang v1.12.0 // indirect

go.sum

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,14 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQm
433433
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
434434
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
435435
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
436+
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
437+
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
438+
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
439+
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
440+
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
441+
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
442+
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
443+
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
436444
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
437445
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
438446
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -494,11 +502,17 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt
494502
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
495503
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
496504
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
505+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
506+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
497507
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
498508
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
499509
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
500510
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
501511
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
512+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
513+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
514+
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
515+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
502516
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
503517
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
504518
github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk=
@@ -547,6 +561,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
547561
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
548562
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
549563
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
564+
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
550565
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
551566
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
552567
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -582,6 +597,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
582597
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
583598
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
584599
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
600+
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
585601
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
586602
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
587603
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -620,6 +636,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
620636
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
621637
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
622638
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
639+
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
640+
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
623641
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
624642
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
625643
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -640,6 +658,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
640658
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
641659
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
642660
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
661+
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
643662
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
644663
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
645664
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -699,7 +718,9 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
699718
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
700719
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
701720
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
721+
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
702722
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
723+
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
703724
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
704725
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
705726
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -708,6 +729,8 @@ golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
708729
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
709730
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
710731
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
732+
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
733+
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
711734
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
712735
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
713736
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -717,6 +740,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
717740
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
718741
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
719742
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
743+
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
744+
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
720745
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
721746
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
722747
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -769,6 +794,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
769794
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
770795
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
771796
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
797+
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
772798
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
773799
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
774800
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

p2p/discover/nat.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ func (p *PortalProtocol) setupPortMapping() {
4747
p.localNode.SetStaticIP(ip)
4848
go p.consumePortMappingRequests()
4949

50+
case nat.STUN:
51+
// STUN doesn't block, set the IP right away.
52+
ip, _ := p.NAT.ExternalIP()
53+
p.localNode.SetStaticIP(ip)
54+
go p.consumePortMappingRequests()
55+
5056
default:
5157
go p.portMappingLoop()
5258
}

p2p/nat/nat.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ type Interface interface {
5858
// "any" uses the first auto-detected mechanism
5959
// "upnp" uses the Universal Plug and Play protocol
6060
// "pmp" uses NAT-PMP with an auto-detected gateway address
61+
// "stun" uses stun protocol with default stun server address
6162
// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
63+
// "stun:192.168.0.1" uses stun protocol with stun server address 192.168.0.1
6264
func Parse(spec string) (Interface, error) {
6365
var (
6466
before, after, found = strings.Cut(spec, ":")
@@ -85,6 +87,12 @@ func Parse(spec string) (Interface, error) {
8587
return UPnP(), nil
8688
case "pmp", "natpmp", "nat-pmp":
8789
return PMP(ip), nil
90+
case "stun":
91+
var addr string
92+
if len(after) > 1 {
93+
addr = after
94+
}
95+
return NewSTUN(addr), nil
8896
default:
8997
return nil, fmt.Errorf("unknown mechanism %q", before)
9098
}

p2p/nat/nat_stun.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package nat
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"time"
7+
8+
"github.com/pion/stun"
9+
)
10+
11+
const STUNDefaultServerAddr = "159.223.0.83:3478"
12+
13+
type STUN struct {
14+
serverAddr string
15+
}
16+
17+
func NewSTUN(serverAddr string) STUN {
18+
if serverAddr == "" {
19+
serverAddr = STUNDefaultServerAddr
20+
}
21+
return STUN{serverAddr: serverAddr}
22+
}
23+
24+
func (s STUN) String() string {
25+
return fmt.Sprintf("STUN(%s)", s.serverAddr)
26+
}
27+
28+
func (STUN) SupportsMapping() bool {
29+
return false
30+
}
31+
32+
func (STUN) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) {
33+
return uint16(extport), nil
34+
}
35+
36+
func (STUN) DeleteMapping(string, int, int) error {
37+
return nil
38+
}
39+
40+
func (s STUN) ExternalIP() (net.IP, error) {
41+
conn, err := stun.Dial("udp4", s.serverAddr)
42+
if err != nil {
43+
return nil, err
44+
}
45+
defer func() {
46+
_ = conn.Close()
47+
}()
48+
49+
message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
50+
var response *stun.Event
51+
err = conn.Do(message, func(event stun.Event) {
52+
response = &event
53+
})
54+
if err != nil {
55+
return nil, err
56+
}
57+
if response.Error != nil {
58+
return nil, response.Error
59+
}
60+
61+
var mappedAddr stun.XORMappedAddress
62+
if err := mappedAddr.GetFrom(response.Message); err != nil {
63+
return nil, err
64+
}
65+
66+
return mappedAddr.IP, nil
67+
}

0 commit comments

Comments
 (0)