From f462f711bce0fcfc8fa3ff9eb94392db5df781d3 Mon Sep 17 00:00:00 2001 From: fearlessfe <505380967@qq.com> Date: Mon, 9 Dec 2024 22:00:41 +0800 Subject: [PATCH 1/6] p2p/nat:add stun protocol --- cmd/utils/flags.go | 2 +- go.mod | 4 +++ go.sum | 28 +++++++++++++-- p2p/nat/nat.go | 8 +++++ p2p/nat/nat_stun.go | 86 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 p2p/nat/nat_stun.go diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 8ef39e88dea3..769c9df00570 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -760,7 +760,7 @@ var ( } NATFlag = &cli.StringFlag{ Name: "nat", - Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:)", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:|stun|stun:)", Value: "any", Category: flags.NetworkingCategory, } diff --git a/go.mod b/go.mod index 35018fbe08b7..b018b610cb7b 100644 --- a/go.mod +++ b/go.mod @@ -128,6 +128,10 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect github.com/opentracing/opentracing-go v1.1.0 // indirect + github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/transport/v2 v2.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.0 // indirect diff --git a/go.sum b/go.sum index ded07f48675e..d319bbcf8c39 100644 --- a/go.sum +++ b/go.sum @@ -423,6 +423,14 @@ github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQm github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= +github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -485,11 +493,17 @@ github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobt github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= @@ -528,8 +542,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -563,6 +578,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -601,6 +617,8 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -621,6 +639,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -680,7 +699,9 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -689,6 +710,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -750,6 +773,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index c65604426833..c24ed9069ae6 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -59,6 +59,8 @@ type Interface interface { // "upnp" uses the Universal Plug and Play protocol // "pmp" uses NAT-PMP with an auto-detected gateway address // "pmp:192.168.0.1" uses NAT-PMP with the given gateway address +// "stun" uses stun protocol with default stun server +// "stun:192.168.0.1" uses stun protocol with stun server address 192.168.0.1 func Parse(spec string) (Interface, error) { var ( before, after, found = strings.Cut(spec, ":") @@ -85,6 +87,12 @@ func Parse(spec string) (Interface, error) { return UPnP(), nil case "pmp", "natpmp", "nat-pmp": return PMP(ip), nil + case "stun": + var addr string + if len(after) > 1 { + addr = after + } + return NewSTUN(addr), nil default: return nil, fmt.Errorf("unknown mechanism %q", before) } diff --git a/p2p/nat/nat_stun.go b/p2p/nat/nat_stun.go new file mode 100644 index 000000000000..54723ff4899b --- /dev/null +++ b/p2p/nat/nat_stun.go @@ -0,0 +1,86 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package nat + +import ( + "fmt" + "net" + "time" + + "github.com/pion/stun" +) + +// The code are from erigon p2p/nat/nat_stun.go +// This stun server is part of the mainnet infrastructure. +// The addr are from https://github.com/ethereum/trin/blob/master/portalnet/src/socket.rs +const STUNDefaultServerAddr = "159.223.0.83:3478" + +type STUN struct { + serverAddr string +} + +func NewSTUN(serverAddr string) STUN { + if serverAddr == "" { + serverAddr = STUNDefaultServerAddr + } + return STUN{serverAddr: serverAddr} +} + +func (s STUN) String() string { + return fmt.Sprintf("STUN(%s)", s.serverAddr) +} + +func (STUN) SupportsMapping() bool { + return false +} + +func (STUN) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) { + return uint16(extport), nil +} + +func (STUN) DeleteMapping(string, int, int) error { + return nil +} + +func (s STUN) ExternalIP() (net.IP, error) { + conn, err := stun.Dial("udp4", s.serverAddr) + if err != nil { + return nil, err + } + defer func() { + _ = conn.Close() + }() + + message := stun.MustBuild(stun.TransactionID, stun.BindingRequest) + var response *stun.Event + err = conn.Do(message, func(event stun.Event) { + response = &event + }) + if err != nil { + return nil, err + } + if response.Error != nil { + return nil, response.Error + } + + var mappedAddr stun.XORMappedAddress + if err := mappedAddr.GetFrom(response.Message); err != nil { + return nil, err + } + + return mappedAddr.IP, nil +} From 0fe51b09f3f2a149d0f2d9673b08c1d1a8af07dc Mon Sep 17 00:00:00 2001 From: fearlessfe <505380967@qq.com> Date: Mon, 9 Dec 2024 22:14:02 +0800 Subject: [PATCH 2/6] p2p/nat: fix lint error --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b018b610cb7b..537c63baa5bd 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 + github.com/pion/stun v0.6.1 github.com/protolambda/bls12-381-util v0.1.0 github.com/protolambda/zrnt v0.32.2 github.com/protolambda/ztyp v0.2.2 @@ -130,7 +131,6 @@ require ( github.com/opentracing/opentracing-go v1.1.0 // indirect github.com/pion/dtls/v2 v2.2.7 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect From bf46bca11252293c7aa268506e738cbecfb9e5ec Mon Sep 17 00:00:00 2001 From: fearlessfe <505380967@qq.com> Date: Mon, 9 Dec 2024 23:51:45 +0800 Subject: [PATCH 3/6] p2p/nat: do some optimization 1. upgrade to github.com/pion/stun/v2 2. check the server addr --- cmd/utils/flags.go | 2 +- go.mod | 3 ++- go.sum | 11 +++++++++-- p2p/nat/nat.go | 13 +++++-------- p2p/nat/nat_stun.go | 27 ++++++++++++++++----------- p2p/nat/nat_test.go | 15 +++++++++++++++ 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 769c9df00570..48143c5acbff 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -760,7 +760,7 @@ var ( } NATFlag = &cli.StringFlag{ Name: "nat", - Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:|stun|stun:)", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:|stun:default|stun:)", Value: "any", Category: flags.NetworkingCategory, } diff --git a/go.mod b/go.mod index 537c63baa5bd..3568289077e3 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 - github.com/pion/stun v0.6.1 + github.com/pion/stun/v2 v2.0.0 github.com/protolambda/bls12-381-util v0.1.0 github.com/protolambda/zrnt v0.32.2 github.com/protolambda/ztyp v0.2.2 @@ -132,6 +132,7 @@ require ( github.com/pion/dtls/v2 v2.2.7 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/transport/v2 v2.2.1 // indirect + github.com/pion/transport/v3 v3.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.0 // indirect diff --git a/go.sum b/go.sum index d319bbcf8c39..0982b92ecd30 100644 --- a/go.sum +++ b/go.sum @@ -427,10 +427,12 @@ github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= -github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -543,6 +545,7 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -619,6 +622,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -712,6 +717,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index c24ed9069ae6..387ddebca717 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -59,15 +59,16 @@ type Interface interface { // "upnp" uses the Universal Plug and Play protocol // "pmp" uses NAT-PMP with an auto-detected gateway address // "pmp:192.168.0.1" uses NAT-PMP with the given gateway address -// "stun" uses stun protocol with default stun server -// "stun:192.168.0.1" uses stun protocol with stun server address 192.168.0.1 +// "stun:default" uses stun protocol with default stun server +// "stun:192.168.0.1:1234" uses stun protocol with stun server address 192.168.0.1:1234 func Parse(spec string) (Interface, error) { var ( before, after, found = strings.Cut(spec, ":") mech = strings.ToLower(before) ip net.IP ) - if found { + // stun is not a valid ip + if found && mech != "stun" { ip = net.ParseIP(after) if ip == nil { return nil, errors.New("invalid IP address") @@ -88,11 +89,7 @@ func Parse(spec string) (Interface, error) { case "pmp", "natpmp", "nat-pmp": return PMP(ip), nil case "stun": - var addr string - if len(after) > 1 { - addr = after - } - return NewSTUN(addr), nil + return NewSTUN(after) default: return nil, fmt.Errorf("unknown mechanism %q", before) } diff --git a/p2p/nat/nat_stun.go b/p2p/nat/nat_stun.go index 54723ff4899b..8b7bdaf268c5 100644 --- a/p2p/nat/nat_stun.go +++ b/p2p/nat/nat_stun.go @@ -1,4 +1,4 @@ -// Copyright 2015 The go-ethereum Authors +// Copyright 2024 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ import ( "net" "time" - "github.com/pion/stun" + "github.com/pion/stun/v2" ) // The code are from erigon p2p/nat/nat_stun.go @@ -30,14 +30,18 @@ import ( const STUNDefaultServerAddr = "159.223.0.83:3478" type STUN struct { - serverAddr string + serverAddr *net.UDPAddr } -func NewSTUN(serverAddr string) STUN { - if serverAddr == "" { +func NewSTUN(serverAddr string) (Interface, error) { + if serverAddr == "default" { serverAddr = STUNDefaultServerAddr } - return STUN{serverAddr: serverAddr} + addr, err := net.ResolveUDPAddr("udp4", serverAddr) + if err != nil { + return nil, err + } + return STUN{serverAddr: addr}, nil } func (s STUN) String() string { @@ -57,15 +61,16 @@ func (STUN) DeleteMapping(string, int, int) error { } func (s STUN) ExternalIP() (net.IP, error) { - conn, err := stun.Dial("udp4", s.serverAddr) + conn, err := stun.Dial("udp4", s.serverAddr.String()) if err != nil { return nil, err } - defer func() { - _ = conn.Close() - }() + defer conn.Close() - message := stun.MustBuild(stun.TransactionID, stun.BindingRequest) + message, err := stun.Build(stun.TransactionID, stun.BindingRequest) + if err != nil { + return nil, err + } var response *stun.Event err = conn.Do(message, func(event stun.Event) { response = &event diff --git a/p2p/nat/nat_test.go b/p2p/nat/nat_test.go index 814e6d9e14c8..edf501624a26 100644 --- a/p2p/nat/nat_test.go +++ b/p2p/nat/nat_test.go @@ -61,3 +61,18 @@ func TestAutoDiscRace(t *testing.T) { } } } + +func TestStunDefault(t *testing.T) { + nat, err := Parse("stun:default") + if err != nil { + t.Errorf("should no err, but get %v", err) + } + stun := nat.(STUN) + if stun.serverAddr.String() != STUNDefaultServerAddr { + t.Errorf("want addr %s, got addr %s", STUNDefaultServerAddr, stun.serverAddr.String()) + } + _, err = stun.ExternalIP() + if err != nil { + t.Errorf("get err: %v", err) + } +} From 9bccaac318e615ed17f09656ae04642bfcfa5e7a Mon Sep 17 00:00:00 2001 From: pengzhen Date: Tue, 10 Dec 2024 10:26:56 +0800 Subject: [PATCH 4/6] feat: make stun server private --- cmd/utils/flags.go | 2 +- p2p/nat/nat.go | 2 +- p2p/nat/nat_stun.go | 36 ++++++++++++++++++------------------ p2p/nat/nat_test.go | 7 ++++--- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 48143c5acbff..b539ae6dfbfa 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -760,7 +760,7 @@ var ( } NATFlag = &cli.StringFlag{ Name: "nat", - Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:|stun:default|stun:)", + Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:|extip:|stun:default|stun:)", Value: "any", Category: flags.NetworkingCategory, } diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go index 387ddebca717..0a84eb309028 100644 --- a/p2p/nat/nat.go +++ b/p2p/nat/nat.go @@ -89,7 +89,7 @@ func Parse(spec string) (Interface, error) { case "pmp", "natpmp", "nat-pmp": return PMP(ip), nil case "stun": - return NewSTUN(after) + return newSTUN(after) default: return nil, fmt.Errorf("unknown mechanism %q", before) } diff --git a/p2p/nat/nat_stun.go b/p2p/nat/nat_stun.go index 8b7bdaf268c5..690610a65e9f 100644 --- a/p2p/nat/nat_stun.go +++ b/p2p/nat/nat_stun.go @@ -21,58 +21,58 @@ import ( "net" "time" - "github.com/pion/stun/v2" + stunV2 "github.com/pion/stun/v2" ) // The code are from erigon p2p/nat/nat_stun.go // This stun server is part of the mainnet infrastructure. // The addr are from https://github.com/ethereum/trin/blob/master/portalnet/src/socket.rs -const STUNDefaultServerAddr = "159.223.0.83:3478" +const stunDefaultServerAddr = "159.223.0.83:3478" -type STUN struct { - serverAddr *net.UDPAddr +type stun struct { + server *net.UDPAddr } -func NewSTUN(serverAddr string) (Interface, error) { +func newSTUN(serverAddr string) (Interface, error) { if serverAddr == "default" { - serverAddr = STUNDefaultServerAddr + serverAddr = stunDefaultServerAddr } addr, err := net.ResolveUDPAddr("udp4", serverAddr) if err != nil { return nil, err } - return STUN{serverAddr: addr}, nil + return stun{server: addr}, nil } -func (s STUN) String() string { - return fmt.Sprintf("STUN(%s)", s.serverAddr) +func (s stun) String() string { + return fmt.Sprintf("STUN(%s)", s.server) } -func (STUN) SupportsMapping() bool { +func (stun) SupportsMapping() bool { return false } -func (STUN) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) { +func (stun) AddMapping(protocol string, extport, intport int, name string, lifetime time.Duration) (uint16, error) { return uint16(extport), nil } -func (STUN) DeleteMapping(string, int, int) error { +func (stun) DeleteMapping(string, int, int) error { return nil } -func (s STUN) ExternalIP() (net.IP, error) { - conn, err := stun.Dial("udp4", s.serverAddr.String()) +func (s stun) ExternalIP() (net.IP, error) { + conn, err := stunV2.Dial("udp4", s.server.String()) if err != nil { return nil, err } defer conn.Close() - message, err := stun.Build(stun.TransactionID, stun.BindingRequest) + message, err := stunV2.Build(stunV2.TransactionID, stunV2.BindingRequest) if err != nil { return nil, err } - var response *stun.Event - err = conn.Do(message, func(event stun.Event) { + var response *stunV2.Event + err = conn.Do(message, func(event stunV2.Event) { response = &event }) if err != nil { @@ -82,7 +82,7 @@ func (s STUN) ExternalIP() (net.IP, error) { return nil, response.Error } - var mappedAddr stun.XORMappedAddress + var mappedAddr stunV2.XORMappedAddress if err := mappedAddr.GetFrom(response.Message); err != nil { return nil, err } diff --git a/p2p/nat/nat_test.go b/p2p/nat/nat_test.go index edf501624a26..3659af452454 100644 --- a/p2p/nat/nat_test.go +++ b/p2p/nat/nat_test.go @@ -62,14 +62,15 @@ func TestAutoDiscRace(t *testing.T) { } } +// stun:default should work well func TestStunDefault(t *testing.T) { nat, err := Parse("stun:default") if err != nil { t.Errorf("should no err, but get %v", err) } - stun := nat.(STUN) - if stun.serverAddr.String() != STUNDefaultServerAddr { - t.Errorf("want addr %s, got addr %s", STUNDefaultServerAddr, stun.serverAddr.String()) + stun := nat.(stun) + if stun.server.String() != stunDefaultServerAddr { + t.Errorf("want addr %s, got addr %s", stunDefaultServerAddr, stun.server.String()) } _, err = stun.ExternalIP() if err != nil { From 41b6e3c7c4c20249a70bceef7a7f60ddef0784a7 Mon Sep 17 00:00:00 2001 From: pengzhen Date: Tue, 10 Dec 2024 17:09:51 +0800 Subject: [PATCH 5/6] feat: handler ip in the closure --- p2p/nat/nat_stun.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/p2p/nat/nat_stun.go b/p2p/nat/nat_stun.go index 690610a65e9f..6716d5248716 100644 --- a/p2p/nat/nat_stun.go +++ b/p2p/nat/nat_stun.go @@ -71,20 +71,23 @@ func (s stun) ExternalIP() (net.IP, error) { if err != nil { return nil, err } - var response *stunV2.Event + + var responseError error + var mappedAddr stunV2.XORMappedAddress err = conn.Do(message, func(event stunV2.Event) { - response = &event + if event.Error != nil { + responseError = event.Error + return + } + if err := mappedAddr.GetFrom(event.Message); err != nil { + responseError = err + } }) if err != nil { return nil, err } - if response.Error != nil { - return nil, response.Error - } - - var mappedAddr stunV2.XORMappedAddress - if err := mappedAddr.GetFrom(response.Message); err != nil { - return nil, err + if responseError != nil { + return nil, responseError } return mappedAddr.IP, nil From 945e9ae62c2ea75713bbfbf8bc2961934693572f Mon Sep 17 00:00:00 2001 From: fearlessfe <505380967@qq.com> Date: Sat, 14 Dec 2024 19:57:58 +0800 Subject: [PATCH 6/6] feat: add stun server list --- go.sum | 7 ++- p2p/nat/nat_stun.go | 101 +++++++++++++++++++++++++++++++++------ p2p/nat/nat_stun_test.go | 23 +++++++++ p2p/nat/nat_test.go | 28 ++++++----- 4 files changed, 132 insertions(+), 27 deletions(-) create mode 100644 p2p/nat/nat_stun_test.go diff --git a/go.sum b/go.sum index 0982b92ecd30..b412b4dae412 100644 --- a/go.sum +++ b/go.sum @@ -546,8 +546,8 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -728,6 +728,9 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/p2p/nat/nat_stun.go b/p2p/nat/nat_stun.go index 6716d5248716..7277aa36e083 100644 --- a/p2p/nat/nat_stun.go +++ b/p2p/nat/nat_stun.go @@ -24,28 +24,58 @@ import ( stunV2 "github.com/pion/stun/v2" ) -// The code are from erigon p2p/nat/nat_stun.go -// This stun server is part of the mainnet infrastructure. -// The addr are from https://github.com/ethereum/trin/blob/master/portalnet/src/socket.rs -const stunDefaultServerAddr = "159.223.0.83:3478" +var stunDefaultServerList = []string{ + "159.223.0.83:3478", + "stun.l.google.com:19302", + "stun1.l.google.com:19302", + "stun2.l.google.com:19302", + "stun3.l.google.com:19302", + "stun4.l.google.com:19302", + "stun01.sipphone.com", + "stun.ekiga.net", + "stun.fwdnet.net", + "stun.ideasip.com", + "stun.iptel.org", + "stun.rixtelecom.se", + "stun.schlund.de", + "stunserver.org", + "stun.softjoys.com", + "stun.voiparound.com", + "stun.voipbuster.com", + "stun.voipstunt.com", + "stun.voxgratia.org", + "stun.xten.com", +} + +const requestLimit = 3 type stun struct { - server *net.UDPAddr + serverList []string + activeIndex int // the server index which return the IP + pendingRequests int // request in flight + askedIndex map[int]struct{} + replyCh chan stunResponse } func newSTUN(serverAddr string) (Interface, error) { + serverList := make([]string, 0) if serverAddr == "default" { - serverAddr = stunDefaultServerAddr - } - addr, err := net.ResolveUDPAddr("udp4", serverAddr) - if err != nil { - return nil, err + serverList = stunDefaultServerList + } else { + _, err := net.ResolveUDPAddr("udp4", serverAddr) + if err != nil { + return nil, err + } + serverList = append(serverList, serverAddr) } - return stun{server: addr}, nil + + return &stun{ + serverList: serverList, + }, nil } func (s stun) String() string { - return fmt.Sprintf("STUN(%s)", s.server) + return fmt.Sprintf("STUN(%s)", s.serverList[s.activeIndex]) } func (stun) SupportsMapping() bool { @@ -60,8 +90,51 @@ func (stun) DeleteMapping(string, int, int) error { return nil } -func (s stun) ExternalIP() (net.IP, error) { - conn, err := stunV2.Dial("udp4", s.server.String()) +type stunResponse struct { + ip net.IP + err error + index int +} + +func (s *stun) ExternalIP() (net.IP, error) { + var err error + s.replyCh = make(chan stunResponse, requestLimit) + s.askedIndex = make(map[int]struct{}) + for s.startQueries() { + response := <-s.replyCh + s.pendingRequests-- + if response.err != nil { + err = response.err + continue + } + s.activeIndex = response.index + return response.ip, nil + } + return nil, err +} + +func (s *stun) startQueries() bool { + for i := 0; s.pendingRequests < requestLimit && i < len(s.serverList); i++ { + _, exist := s.askedIndex[i] + if exist { + continue + } + s.pendingRequests++ + s.askedIndex[i] = struct{}{} + go func(index int, server string) { + ip, err := externalIP(server) + s.replyCh <- stunResponse{ + ip: ip, + index: index, + err: err, + } + }(i, s.serverList[i]) + } + return s.pendingRequests > 0 +} + +func externalIP(server string) (net.IP, error) { + conn, err := stunV2.Dial("udp4", server) if err != nil { return nil, err } diff --git a/p2p/nat/nat_stun_test.go b/p2p/nat/nat_stun_test.go new file mode 100644 index 000000000000..b681ba56b1f3 --- /dev/null +++ b/p2p/nat/nat_stun_test.go @@ -0,0 +1,23 @@ +package nat + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNatStun(t *testing.T) { + nat, err := newSTUN("default") + assert.NoError(t, err) + _, err = nat.ExternalIP() + assert.NoError(t, err) +} + +func TestUnreachedNatServer(t *testing.T) { + stun := &stun{ + serverList: []string{"1.2.3.4:1234", "1.2.3.4:1234", "1.2.3.4:1234"}, + } + stun.serverList = append(stun.serverList, stunDefaultServerList...) + _, err := stun.ExternalIP() + assert.NoError(t, err) +} diff --git a/p2p/nat/nat_test.go b/p2p/nat/nat_test.go index 3659af452454..6279566ad985 100644 --- a/p2p/nat/nat_test.go +++ b/p2p/nat/nat_test.go @@ -20,6 +20,8 @@ import ( "net" "testing" "time" + + "github.com/stretchr/testify/assert" ) // This test checks that autodisc doesn't hang and returns @@ -63,17 +65,21 @@ func TestAutoDiscRace(t *testing.T) { } // stun:default should work well -func TestStunDefault(t *testing.T) { - nat, err := Parse("stun:default") - if err != nil { - t.Errorf("should no err, but get %v", err) - } - stun := nat.(stun) - if stun.server.String() != stunDefaultServerAddr { - t.Errorf("want addr %s, got addr %s", stunDefaultServerAddr, stun.server.String()) +func TestParseStun(t *testing.T) { + testcases := []struct { + natStr string + want *stun + }{ + {"stun:default", &stun{serverList: stunDefaultServerList}}, + {"stun:1.2.3.4:1234", &stun{serverList: []string{"1.2.3.4:1234"}}}, } - _, err = stun.ExternalIP() - if err != nil { - t.Errorf("get err: %v", err) + + for _, tc := range testcases { + nat, err := Parse(tc.natStr) + if err != nil { + t.Errorf("should no err, but get %v", err) + } + stun := nat.(*stun) + assert.Equal(t, stun.serverList, tc.want.serverList) } }