forked from forks/go-ethereum
p2p/nat: add stun protocol (#31064)
This implements a basic mechanism to query the node's external IP using a STUN server. There is a built-in list of public STUN servers for convenience. The new detection mechanism must be selected explicitly using `--nat=stun` and is not enabled by default in Geth. Fixes #30881 --------- Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
3003a13440
commit
75526bb8e0
9 changed files with 354 additions and 2 deletions
|
|
@ -760,7 +760,7 @@ var (
|
|||
}
|
||||
NATFlag = &cli.StringFlag{
|
||||
Name: "nat",
|
||||
Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:<IP>|extip:<IP>)",
|
||||
Usage: "NAT port mapping mechanism (any|none|upnp|pmp|pmp:<IP>|extip:<IP>|stun:<IP:PORT>)",
|
||||
Value: "any",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
|
|
|
|||
5
go.mod
5
go.mod
|
|
@ -51,6 +51,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/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
|
||||
|
|
@ -126,6 +127,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/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
|
||||
|
|
|
|||
34
go.sum
34
go.sum
|
|
@ -423,6 +423,16 @@ 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/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=
|
||||
|
|
@ -485,11 +495,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,6 +544,8 @@ 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.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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
|
@ -563,6 +581,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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -601,6 +620,10 @@ 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
|
|
@ -621,6 +644,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 +704,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 +715,10 @@ golang.org/x/sys v0.29.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/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=
|
||||
|
|
@ -698,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=
|
||||
|
|
@ -750,6 +783,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.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
|
|||
|
|
@ -59,13 +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: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")
|
||||
|
|
@ -85,6 +88,8 @@ func Parse(spec string) (Interface, error) {
|
|||
return UPnP(), nil
|
||||
case "pmp", "natpmp", "nat-pmp":
|
||||
return PMP(ip), nil
|
||||
case "stun":
|
||||
return newSTUN(after)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown mechanism %q", before)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,11 @@ package nat
|
|||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// This test checks that autodisc doesn't hang and returns
|
||||
|
|
@ -61,3 +64,23 @@ func TestAutoDiscRace(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stun should work well
|
||||
func TestParseStun(t *testing.T) {
|
||||
testcases := []struct {
|
||||
natStr string
|
||||
want *stun
|
||||
}{
|
||||
{"stun", &stun{serverList: strings.Split(stunDefaultServers, "\n")}},
|
||||
{"stun:1.2.3.4:1234", &stun{serverList: []string{"1.2.3.4:1234"}}},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
5
p2p/nat/stun-list-update.sh
Executable file
5
p2p/nat/stun-list-update.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
rm -f stun-list.txt
|
||||
curl https://raw.githubusercontent.com/pradt2/always-online-stun/refs/heads/master/valid_ipv4s.txt | sort -n >> stun-list.txt
|
||||
curl https://raw.githubusercontent.com/pradt2/always-online-stun/refs/heads/master/valid_ipv6s.txt | sort -n >> stun-list.txt
|
||||
96
p2p/nat/stun-list.txt
Normal file
96
p2p/nat/stun-list.txt
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
3.132.228.249:3478
|
||||
3.78.237.53:3478
|
||||
5.161.52.174:3478
|
||||
5.161.57.75:3478
|
||||
18.185.125.152:3478
|
||||
20.93.239.167:3478
|
||||
23.21.199.62:3478
|
||||
23.21.92.55:3478
|
||||
24.204.48.11:3478
|
||||
31.184.236.23:3478
|
||||
34.195.177.19:3478
|
||||
34.197.205.39:3478
|
||||
34.206.168.53:3478
|
||||
34.74.124.204:3478
|
||||
35.158.233.7:3478
|
||||
35.177.202.92:3478
|
||||
35.180.81.93:3478
|
||||
44.230.252.214:3478
|
||||
45.15.102.34:3478
|
||||
49.12.125.53:3478
|
||||
51.255.31.35:3478
|
||||
51.68.112.203:3478
|
||||
51.68.45.75:3478
|
||||
51.83.15.212:3478
|
||||
51.83.201.84:3478
|
||||
52.24.174.49:3478
|
||||
52.26.251.34:3478
|
||||
52.47.70.236:3478
|
||||
52.52.70.85:3478
|
||||
54.197.117.0:3478
|
||||
62.72.83.10:3478
|
||||
66.228.54.23:3478
|
||||
69.20.59.115:3478
|
||||
79.140.42.88:3478
|
||||
80.155.54.123:3478
|
||||
80.156.214.187:3478
|
||||
81.3.27.44:3478
|
||||
81.82.206.117:3478
|
||||
81.83.12.46:3478
|
||||
85.197.87.182:3478
|
||||
87.253.140.133:3478
|
||||
88.198.151.128:3478
|
||||
88.218.220.40:3478
|
||||
88.99.67.241:3478
|
||||
90.145.158.66:3478
|
||||
91.212.41.85:3478
|
||||
92.205.106.161:3478
|
||||
94.140.180.141:3478
|
||||
94.23.17.185:3478
|
||||
95.216.145.84:3478
|
||||
95.216.78.222:3478
|
||||
129.153.212.128:3478
|
||||
136.243.59.79:3478
|
||||
137.74.112.113:3478
|
||||
143.198.60.79:3478
|
||||
147.182.188.245:3478
|
||||
157.161.10.32:3478
|
||||
159.69.191.124:3478
|
||||
159.69.191.124:443
|
||||
172.233.245.118:3478
|
||||
176.9.24.184:3478
|
||||
185.125.180.70:3478
|
||||
185.88.236.76:3478
|
||||
188.138.90.169:3478
|
||||
188.40.18.246:3478
|
||||
188.40.203.74:3478
|
||||
192.172.233.145:3478
|
||||
192.76.120.66:3478
|
||||
193.182.111.151:3478
|
||||
193.22.17.97:3478
|
||||
194.149.74.157:3478
|
||||
195.145.93.141:3478
|
||||
195.201.132.113:3478
|
||||
195.208.107.138:3478
|
||||
198.100.144.121:3478
|
||||
209.251.63.76:3478
|
||||
212.103.68.7:3478
|
||||
212.144.246.197:3478
|
||||
212.18.0.14:3478
|
||||
212.53.40.40:3478
|
||||
212.53.40.43:3478
|
||||
213.239.206.5:3478
|
||||
213.251.48.147:3478
|
||||
217.146.224.74:3478
|
||||
217.91.243.229:3478
|
||||
[2001:1538:1::224:74]:3478
|
||||
[2001:4060:1:1005::10:32]:3478
|
||||
[2001:4060:1:1005::10:32]:3478
|
||||
[2001:41d0:2:12b9::1]:3478
|
||||
[2001:678:b28::118]:3478
|
||||
[2600:1f16:8c5:101:80b:b58b:828:8df4]:3478
|
||||
[2a00:1169:11b:a6b0::]:3478
|
||||
[2a01:4f8:242:56ca::2]:3478
|
||||
[2a01:4f8:c17:8f74::1]:3478
|
||||
[2a01:4f8:c17:8f74::1]:443
|
||||
[2a03:8600::89]:3478
|
||||
144
p2p/nat/stun.go
Normal file
144
p2p/nat/stun.go
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package nat
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
stunV2 "github.com/pion/stun/v2"
|
||||
)
|
||||
|
||||
//go:embed stun-list.txt
|
||||
var stunDefaultServers string
|
||||
|
||||
const requestLimit = 3
|
||||
|
||||
var errSTUNFailed = errors.New("STUN requests failed")
|
||||
|
||||
type stun struct {
|
||||
serverList []string
|
||||
}
|
||||
|
||||
func newSTUN(serverAddr string) (Interface, error) {
|
||||
s := new(stun)
|
||||
if serverAddr == "" {
|
||||
s.serverList = strings.Split(stunDefaultServers, "\n")
|
||||
} else {
|
||||
_, err := net.ResolveUDPAddr("udp4", serverAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.serverList = []string{serverAddr}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s stun) String() string {
|
||||
if len(s.serverList) == 1 {
|
||||
return fmt.Sprintf("stun:%s", s.serverList[0])
|
||||
}
|
||||
return "stun"
|
||||
}
|
||||
|
||||
func (s stun) MarshalText() ([]byte, error) {
|
||||
return []byte(s.String()), nil
|
||||
}
|
||||
|
||||
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) {
|
||||
for _, server := range s.randomServers(requestLimit) {
|
||||
ip, err := s.externalIP(server)
|
||||
if err != nil {
|
||||
log.Debug("STUN request failed", "server", server, "err", err)
|
||||
continue
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
return nil, errSTUNFailed
|
||||
}
|
||||
|
||||
func (s *stun) randomServers(n int) []string {
|
||||
n = min(n, len(s.serverList))
|
||||
m := make(map[int]struct{}, n)
|
||||
list := make([]string, 0, n)
|
||||
for i := 0; i < len(s.serverList)*2 && len(list) < n; i++ {
|
||||
index := rand.Intn(len(s.serverList))
|
||||
if _, alreadyHit := m[index]; alreadyHit {
|
||||
continue
|
||||
}
|
||||
list = append(list, s.serverList[index])
|
||||
m[index] = struct{}{}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (s *stun) externalIP(server string) (net.IP, error) {
|
||||
_, _, err := net.SplitHostPort(server)
|
||||
if err != nil {
|
||||
server += fmt.Sprintf(":%d", stunV2.DefaultPort)
|
||||
}
|
||||
|
||||
log.Trace("Attempting STUN binding request", "server", server)
|
||||
conn, err := stunV2.Dial("udp4", server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
message, err := stunV2.Build(stunV2.TransactionID, stunV2.BindingRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var responseError error
|
||||
var mappedAddr stunV2.XORMappedAddress
|
||||
err = conn.Do(message, func(event stunV2.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 responseError != nil {
|
||||
return nil, responseError
|
||||
}
|
||||
log.Trace("STUN returned IP", "server", server, "ip", mappedAddr.IP)
|
||||
return mappedAddr.IP, nil
|
||||
}
|
||||
40
p2p/nat/stun_test.go
Normal file
40
p2p/nat/stun_test.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2025 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package nat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNatStun(t *testing.T) {
|
||||
nat, err := newSTUN("")
|
||||
assert.NoError(t, err)
|
||||
_, err = nat.ExternalIP()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestUnreachedNatServer(t *testing.T) {
|
||||
stun := &stun{
|
||||
serverList: []string{"198.51.100.2:1234", "198.51.100.5"},
|
||||
}
|
||||
_, err := stun.ExternalIP()
|
||||
if err != errSTUNFailed {
|
||||
t.Fatal("wrong error:", err)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue