feat: add stun server list

This commit is contained in:
fearlessfe 2024-12-14 19:57:58 +08:00
parent 41b6e3c7c4
commit 945e9ae62c
4 changed files with 132 additions and 27 deletions

7
go.sum
View File

@ -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.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.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.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 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.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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 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= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -24,28 +24,58 @@ import (
stunV2 "github.com/pion/stun/v2" stunV2 "github.com/pion/stun/v2"
) )
// The code are from erigon p2p/nat/nat_stun.go var stunDefaultServerList = []string{
// This stun server is part of the mainnet infrastructure. "159.223.0.83:3478",
// The addr are from https://github.com/ethereum/trin/blob/master/portalnet/src/socket.rs "stun.l.google.com:19302",
const stunDefaultServerAddr = "159.223.0.83:3478" "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 { 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) { func newSTUN(serverAddr string) (Interface, error) {
serverList := make([]string, 0)
if serverAddr == "default" { if serverAddr == "default" {
serverAddr = stunDefaultServerAddr serverList = stunDefaultServerList
} else {
_, err := net.ResolveUDPAddr("udp4", serverAddr)
if err != nil {
return nil, err
}
serverList = append(serverList, serverAddr)
} }
addr, err := net.ResolveUDPAddr("udp4", serverAddr)
if err != nil { return &stun{
return nil, err serverList: serverList,
} }, nil
return stun{server: addr}, nil
} }
func (s stun) String() string { 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 { func (stun) SupportsMapping() bool {
@ -60,8 +90,51 @@ func (stun) DeleteMapping(string, int, int) error {
return nil return nil
} }
func (s stun) ExternalIP() (net.IP, error) { type stunResponse struct {
conn, err := stunV2.Dial("udp4", s.server.String()) 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 { if err != nil {
return nil, err return nil, err
} }

23
p2p/nat/nat_stun_test.go Normal file
View File

@ -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)
}

View File

@ -20,6 +20,8 @@ import (
"net" "net"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
// This test checks that autodisc doesn't hang and returns // This test checks that autodisc doesn't hang and returns
@ -63,17 +65,21 @@ func TestAutoDiscRace(t *testing.T) {
} }
// stun:default should work well // stun:default should work well
func TestStunDefault(t *testing.T) { func TestParseStun(t *testing.T) {
nat, err := Parse("stun:default") testcases := []struct {
if err != nil { natStr string
t.Errorf("should no err, but get %v", err) want *stun
}{
{"stun:default", &stun{serverList: stunDefaultServerList}},
{"stun:1.2.3.4:1234", &stun{serverList: []string{"1.2.3.4:1234"}}},
} }
stun := nat.(stun)
if stun.server.String() != stunDefaultServerAddr { for _, tc := range testcases {
t.Errorf("want addr %s, got addr %s", stunDefaultServerAddr, stun.server.String()) nat, err := Parse(tc.natStr)
} if err != nil {
_, err = stun.ExternalIP() t.Errorf("should no err, but get %v", err)
if err != nil { }
t.Errorf("get err: %v", err) stun := nat.(*stun)
assert.Equal(t, stun.serverList, tc.want.serverList)
} }
} }