mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-26 17:03:02 +00:00
Implement WireGuard protocol as outbound (client) (#1344)
* implement WireGuard protocol for Outbound * upload license * fix build for openbsd & dragonfly os * updated wireguard-go * fix up * switch to another wireguard fork * fix * switch to upstream * open connection through internet.Dialer (#1) * use internet.Dialer * maybe better code * fix * real fix Co-authored-by: nanoda0523 <nanoda0523@users.noreply.github.com> * fix bugs & add ability to recover during connection reset on UDP over TCP parent protocols * improve performance improve performance * dns lookup endpoint && remove unused code * interface address fallback * better code && add config test case Co-authored-by: nanoda0523 <nanoda0523@users.noreply.github.com>
This commit is contained in:
parent
691b2b1c73
commit
e18b52a5df
6
go.mod
6
go.mod
@ -16,6 +16,7 @@ require (
|
|||||||
github.com/refraction-networking/utls v1.2.0
|
github.com/refraction-networking/utls v1.2.0
|
||||||
github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc
|
github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1
|
||||||
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||||
@ -27,6 +28,7 @@ require (
|
|||||||
golang.org/x/sys v0.2.0
|
golang.org/x/sys v0.2.0
|
||||||
google.golang.org/grpc v1.51.0
|
google.golang.org/grpc v1.51.0
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,16 +38,20 @@ require (
|
|||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||||
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/pprof v0.0.0-20221112000123-84eb7ad69597 // indirect
|
github.com/google/pprof v0.0.0-20221112000123-84eb7ad69597 // indirect
|
||||||
github.com/klauspost/compress v1.15.12 // indirect
|
github.com/klauspost/compress v1.15.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.0 // indirect
|
||||||
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.5.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.5.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab // indirect
|
golang.org/x/exp v0.0.0-20221111204811-129d8d6c17ab // indirect
|
||||||
golang.org/x/mod v0.7.0 // indirect
|
golang.org/x/mod v0.7.0 // indirect
|
||||||
golang.org/x/text v0.4.0 // indirect
|
golang.org/x/text v0.4.0 // indirect
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||||
golang.org/x/tools v0.3.0 // indirect
|
golang.org/x/tools v0.3.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20221111202108-142d8a6fa32e // indirect
|
google.golang.org/genproto v0.0.0-20221111202108-142d8a6fa32e // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
13
go.sum
13
go.sum
@ -61,6 +61,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
|
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@ -94,8 +96,9 @@ github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrD
|
|||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.0 h1:4ZexSFt8agMNzNisrsilL6RClWDC5YJnLHNIfTy4iuc=
|
github.com/klauspost/cpuid/v2 v2.2.0 h1:4ZexSFt8agMNzNisrsilL6RClWDC5YJnLHNIfTy4iuc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
@ -143,6 +146,8 @@ github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc h1:x7H64IiqyrpxPWl/K
|
|||||||
github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
github.com/sagernet/sing v0.0.0-20220801112236-1bb95f9661fc/go.mod h1:GbtQfZSpmtD3cXeD1qX2LCMwY8dH+bnnInDTqd92IsM=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1 h1:RYvOc69eSNMN0dwVugrDts41Nn7Ar/C/n/fvytvFcp4=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220801112336-a91eacdd01e1/go.mod h1:NqZjiXszgVCMQ4gVDa2V+drhS8NMfGqUqDF86EacEFc=
|
||||||
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
|
||||||
|
github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
@ -191,6 +196,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 h1:5/KzhcSqd4UgY51l17r7C5g/JiE6DRw1Vq7VJfQHuMc=
|
go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 h1:5/KzhcSqd4UgY51l17r7C5g/JiE6DRw1Vq7VJfQHuMc=
|
||||||
go.starlark.net v0.0.0-20221028183056-acb66ad56dd2/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk=
|
go.starlark.net v0.0.0-20221028183056-acb66ad56dd2/go.mod h1:kIVgS18CjmEC3PqMd5kaJSGEifyV/CeB9x506ZJ1Vbk=
|
||||||
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -263,6 +270,8 @@ golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
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=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@ -327,6 +336,8 @@ gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||||
|
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||||
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo=
|
||||||
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
111
infra/conf/wireguard.go
Normal file
111
infra/conf/wireguard.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/xtls/xray-core/proxy/wireguard"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WireGuardPeerConfig struct {
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
PreSharedKey string `json:"preSharedKey"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
KeepAlive int `json:"keepAlive"`
|
||||||
|
AllowedIPs []string `json:"allowedIPs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WireGuardPeerConfig) Build() (proto.Message, error) {
|
||||||
|
var err error
|
||||||
|
config := new(wireguard.PeerConfig)
|
||||||
|
|
||||||
|
config.PublicKey, err = parseWireGuardKey(c.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.PreSharedKey != "" {
|
||||||
|
config.PreSharedKey, err = parseWireGuardKey(c.PreSharedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config.PreSharedKey = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Endpoint = c.Endpoint
|
||||||
|
// default 0
|
||||||
|
config.KeepAlive = int32(c.KeepAlive)
|
||||||
|
if c.AllowedIPs == nil {
|
||||||
|
config.AllowedIps = []string{"0.0.0.0/0", "::0/0"}
|
||||||
|
} else {
|
||||||
|
config.AllowedIps = c.AllowedIPs
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WireGuardConfig struct {
|
||||||
|
SecretKey string `json:"secretKey"`
|
||||||
|
Address []string `json:"address"`
|
||||||
|
Peers []*WireGuardPeerConfig `json:"peers"`
|
||||||
|
MTU int `json:"mtu"`
|
||||||
|
NumWorkers int `json:"workers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WireGuardConfig) Build() (proto.Message, error) {
|
||||||
|
config := new(wireguard.DeviceConfig)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
config.SecretKey, err = parseWireGuardKey(c.SecretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Address == nil {
|
||||||
|
// bogon ips
|
||||||
|
config.Endpoint = []string{"10.0.0.1", "fd59:7153:2388:b5fd:0000:0000:0000:0001"}
|
||||||
|
} else {
|
||||||
|
config.Endpoint = c.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Peers != nil {
|
||||||
|
config.Peers = make([]*wireguard.PeerConfig, len(c.Peers))
|
||||||
|
for i, p := range c.Peers {
|
||||||
|
msg, err := p.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
config.Peers[i] = msg.(*wireguard.PeerConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.MTU == 0 {
|
||||||
|
config.Mtu = 1420
|
||||||
|
} else {
|
||||||
|
config.Mtu = int32(c.MTU)
|
||||||
|
}
|
||||||
|
// these a fallback code exists in github.com/nanoda0523/wireguard-go code,
|
||||||
|
// we don't need to process fallback manually
|
||||||
|
config.NumWorkers = int32(c.NumWorkers)
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseWireGuardKey(str string) (string, error) {
|
||||||
|
if len(str) != 64 {
|
||||||
|
// may in base64 form
|
||||||
|
dat, err := base64.StdEncoding.DecodeString(str)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if len(dat) != 32 {
|
||||||
|
return "", newError("key should be 32 bytes: " + str)
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(dat), err
|
||||||
|
} else {
|
||||||
|
// already hex form
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
}
|
49
infra/conf/wireguard_test.go
Normal file
49
infra/conf/wireguard_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package conf_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/xtls/xray-core/infra/conf"
|
||||||
|
"github.com/xtls/xray-core/proxy/wireguard"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWireGuardOutbound(t *testing.T) {
|
||||||
|
creator := func() Buildable {
|
||||||
|
return new(WireGuardConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
runMultiTestCase(t, []TestCase{
|
||||||
|
{
|
||||||
|
Input: `{
|
||||||
|
"secretKey": "uJv5tZMDltsiYEn+kUwb0Ll/CXWhMkaSCWWhfPEZM3A=",
|
||||||
|
"address": ["10.1.1.1", "fd59:7153:2388:b5fd:0000:0000:1234:0001"],
|
||||||
|
"peers": [
|
||||||
|
{
|
||||||
|
"publicKey": "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
|
||||||
|
"endpoint": "127.0.0.1:1234"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mtu": 1300,
|
||||||
|
"workers": 2
|
||||||
|
}`,
|
||||||
|
Parser: loadJSON(creator),
|
||||||
|
Output: &wireguard.DeviceConfig{
|
||||||
|
// key converted into hex form
|
||||||
|
SecretKey: "b89bf9b5930396db226049fe914c1bd0b97f0975a13246920965a17cf1193370",
|
||||||
|
Endpoint: []string{"10.1.1.1", "fd59:7153:2388:b5fd:0000:0000:1234:0001"},
|
||||||
|
Peers: []*wireguard.PeerConfig{
|
||||||
|
{
|
||||||
|
// also can read from hex form directly
|
||||||
|
PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a",
|
||||||
|
PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
Endpoint: "127.0.0.1:1234",
|
||||||
|
KeepAlive: 0,
|
||||||
|
AllowedIps: []string{"0.0.0.0/0", "::0/0"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mtu: 1300,
|
||||||
|
NumWorkers: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -40,6 +40,7 @@ var (
|
|||||||
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
"trojan": func() interface{} { return new(TrojanClientConfig) },
|
||||||
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
|
"mtproto": func() interface{} { return new(MTProtoClientConfig) },
|
||||||
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
"dns": func() interface{} { return new(DNSOutboundConfig) },
|
||||||
|
"wireguard": func() interface{} { return new(WireGuardConfig) },
|
||||||
}, "protocol", "settings")
|
}, "protocol", "settings")
|
||||||
|
|
||||||
ctllog = log.New(os.Stderr, "xctl> ", 0)
|
ctllog = log.New(os.Stderr, "xctl> ", 0)
|
||||||
|
@ -48,6 +48,7 @@ import (
|
|||||||
_ "github.com/xtls/xray-core/proxy/vless/outbound"
|
_ "github.com/xtls/xray-core/proxy/vless/outbound"
|
||||||
_ "github.com/xtls/xray-core/proxy/vmess/inbound"
|
_ "github.com/xtls/xray-core/proxy/vmess/inbound"
|
||||||
_ "github.com/xtls/xray-core/proxy/vmess/outbound"
|
_ "github.com/xtls/xray-core/proxy/vmess/outbound"
|
||||||
|
_ "github.com/xtls/xray-core/proxy/wireguard"
|
||||||
|
|
||||||
// Transports
|
// Transports
|
||||||
_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
_ "github.com/xtls/xray-core/transport/internet/domainsocket"
|
||||||
|
254
proxy/wireguard/bind.go
Normal file
254
proxy/wireguard/bind.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sagernet/wireguard-go/conn"
|
||||||
|
xnet "github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type netReadInfo struct {
|
||||||
|
// status
|
||||||
|
waiter sync.WaitGroup
|
||||||
|
// param
|
||||||
|
buff []byte
|
||||||
|
// result
|
||||||
|
bytes int
|
||||||
|
endpoint conn.Endpoint
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type netBindClient struct {
|
||||||
|
workers int
|
||||||
|
dialer internet.Dialer
|
||||||
|
dns dns.Client
|
||||||
|
dnsOption dns.IPOption
|
||||||
|
|
||||||
|
readQueue chan *netReadInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *netBindClient) ParseEndpoint(s string) (conn.Endpoint, error) {
|
||||||
|
ipStr, port, _, err := splitAddrPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr net.IP
|
||||||
|
if IsDomainName(ipStr) {
|
||||||
|
ips, err := n.dns.LookupIP(ipStr, n.dnsOption)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(ips) == 0 {
|
||||||
|
return nil, dns.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
addr = ips[0]
|
||||||
|
} else {
|
||||||
|
addr = net.ParseIP(ipStr)
|
||||||
|
}
|
||||||
|
if addr == nil {
|
||||||
|
return nil, errors.New("failed to parse ip: " + ipStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip xnet.Address
|
||||||
|
if p4 := addr.To4(); len(p4) == net.IPv4len {
|
||||||
|
ip = xnet.IPAddress(p4[:])
|
||||||
|
} else {
|
||||||
|
ip = xnet.IPAddress(addr[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := xnet.Destination{
|
||||||
|
Address: ip,
|
||||||
|
Port: xnet.Port(port),
|
||||||
|
Network: xnet.Network_UDP,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &netEndpoint{
|
||||||
|
dst: dst,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bind *netBindClient) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
|
||||||
|
bind.readQueue = make(chan *netReadInfo)
|
||||||
|
|
||||||
|
fun := func(buff []byte) (cap int, ep conn.Endpoint, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
cap = 0
|
||||||
|
ep = nil
|
||||||
|
err = errors.New("channel closed")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r := &netReadInfo{
|
||||||
|
buff: buff,
|
||||||
|
}
|
||||||
|
r.waiter.Add(1)
|
||||||
|
bind.readQueue <- r
|
||||||
|
r.waiter.Wait() // wait read goroutine done, or we will miss the result
|
||||||
|
return r.bytes, r.endpoint, r.err
|
||||||
|
}
|
||||||
|
workers := bind.workers
|
||||||
|
if workers <= 0 {
|
||||||
|
workers = 1
|
||||||
|
}
|
||||||
|
arr := make([]conn.ReceiveFunc, workers)
|
||||||
|
for i := 0; i < workers; i++ {
|
||||||
|
arr[i] = fun
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr, uint16(uport), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bind *netBindClient) Close() error {
|
||||||
|
if bind.readQueue != nil {
|
||||||
|
close(bind.readQueue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bind *netBindClient) connectTo(endpoint *netEndpoint) error {
|
||||||
|
c, err := bind.dialer.Dial(context.Background(), endpoint.dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
endpoint.conn = c
|
||||||
|
|
||||||
|
go func(readQueue <-chan *netReadInfo, endpoint *netEndpoint) {
|
||||||
|
for {
|
||||||
|
v, ok := <-readQueue
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i, err := c.Read(v.buff)
|
||||||
|
v.bytes = i
|
||||||
|
v.endpoint = endpoint
|
||||||
|
v.err = err
|
||||||
|
v.waiter.Done()
|
||||||
|
if err != nil && errors.Is(err, io.EOF) {
|
||||||
|
endpoint.conn = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(bind.readQueue, endpoint)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bind *netBindClient) Send(buff []byte, endpoint conn.Endpoint) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
nend, ok := endpoint.(*netEndpoint)
|
||||||
|
if !ok {
|
||||||
|
return conn.ErrWrongEndpointType
|
||||||
|
}
|
||||||
|
|
||||||
|
if nend.conn == nil {
|
||||||
|
err = bind.connectTo(nend)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = nend.conn.Write(buff)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bind *netBindClient) SetMark(mark uint32) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type netEndpoint struct {
|
||||||
|
dst xnet.Destination
|
||||||
|
conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (netEndpoint) ClearSrc() {}
|
||||||
|
|
||||||
|
func (e netEndpoint) DstIP() netip.Addr {
|
||||||
|
return toNetIpAddr(e.dst.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e netEndpoint) SrcIP() netip.Addr {
|
||||||
|
return netip.Addr{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e netEndpoint) DstToBytes() []byte {
|
||||||
|
var dat []byte
|
||||||
|
if e.dst.Address.Family().IsIPv4() {
|
||||||
|
dat = e.dst.Address.IP().To4()[:]
|
||||||
|
} else {
|
||||||
|
dat = e.dst.Address.IP().To16()[:]
|
||||||
|
}
|
||||||
|
dat = append(dat, byte(e.dst.Port), byte(e.dst.Port>>8))
|
||||||
|
return dat
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e netEndpoint) DstToString() string {
|
||||||
|
return e.dst.NetAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e netEndpoint) SrcToString() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func toNetIpAddr(addr xnet.Address) netip.Addr {
|
||||||
|
if addr.Family().IsIPv4() {
|
||||||
|
ip := addr.IP()
|
||||||
|
return netip.AddrFrom4([4]byte{ip[0], ip[1], ip[2], ip[3]})
|
||||||
|
} else {
|
||||||
|
ip := addr.IP()
|
||||||
|
arr := [16]byte{}
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
arr[i] = ip[i]
|
||||||
|
}
|
||||||
|
return netip.AddrFrom16(arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringsLastIndexByte(s string, b byte) int {
|
||||||
|
for i := len(s) - 1; i >= 0; i-- {
|
||||||
|
if s[i] == b {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitAddrPort(s string) (ip string, port uint16, v6 bool, err error) {
|
||||||
|
i := stringsLastIndexByte(s, ':')
|
||||||
|
if i == -1 {
|
||||||
|
return "", 0, false, errors.New("not an ip:port")
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = s[:i]
|
||||||
|
portStr := s[i+1:]
|
||||||
|
if len(ip) == 0 {
|
||||||
|
return "", 0, false, errors.New("no IP")
|
||||||
|
}
|
||||||
|
if len(portStr) == 0 {
|
||||||
|
return "", 0, false, errors.New("no port")
|
||||||
|
}
|
||||||
|
port64, err := strconv.ParseUint(portStr, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, false, errors.New("invalid port " + strconv.Quote(portStr) + " parsing " + strconv.Quote(s))
|
||||||
|
}
|
||||||
|
port = uint16(port64)
|
||||||
|
if ip[0] == '[' {
|
||||||
|
if len(ip) < 2 || ip[len(ip)-1] != ']' {
|
||||||
|
return "", 0, false, errors.New("missing ]")
|
||||||
|
}
|
||||||
|
ip = ip[1 : len(ip)-1]
|
||||||
|
v6 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return ip, port, v6, nil
|
||||||
|
}
|
294
proxy/wireguard/config.pb.go
Normal file
294
proxy/wireguard/config.pb.go
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc v3.21.9
|
||||||
|
// source: proxy/wireguard/config.proto
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type PeerConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||||
|
PreSharedKey string `protobuf:"bytes,2,opt,name=pre_shared_key,json=preSharedKey,proto3" json:"pre_shared_key,omitempty"`
|
||||||
|
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||||
|
KeepAlive int32 `protobuf:"varint,4,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"`
|
||||||
|
AllowedIps []string `protobuf:"bytes,5,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) Reset() {
|
||||||
|
*x = PeerConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proxy_wireguard_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PeerConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PeerConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_wireguard_config_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PeerConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PeerConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_wireguard_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetPublicKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PublicKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetPreSharedKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PreSharedKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetEndpoint() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Endpoint
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetKeepAlive() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.KeepAlive
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetAllowedIps() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AllowedIps
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceConfig struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"`
|
||||||
|
Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"`
|
||||||
|
Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"`
|
||||||
|
Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"`
|
||||||
|
NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) Reset() {
|
||||||
|
*x = DeviceConfig{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_proxy_wireguard_config_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*DeviceConfig) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_proxy_wireguard_config_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use DeviceConfig.ProtoReflect.Descriptor instead.
|
||||||
|
func (*DeviceConfig) Descriptor() ([]byte, []int) {
|
||||||
|
return file_proxy_wireguard_config_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetSecretKey() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.SecretKey
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetEndpoint() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Endpoint
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetPeers() []*PeerConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.Peers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetMtu() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Mtu
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetNumWorkers() int32 {
|
||||||
|
if x != nil {
|
||||||
|
return x.NumWorkers
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_proxy_wireguard_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_proxy_wireguard_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x1c, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72,
|
||||||
|
0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
|
||||||
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67,
|
||||||
|
0x75, 0x61, 0x72, 0x64, 0x22, 0xad, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
||||||
|
0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65,
|
||||||
|
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
|
||||||
|
0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64,
|
||||||
|
0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53,
|
||||||
|
0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70,
|
||||||
|
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70,
|
||||||
|
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69,
|
||||||
|
0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c,
|
||||||
|
0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69,
|
||||||
|
0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65,
|
||||||
|
0x64, 0x49, 0x70, 0x73, 0x22, 0xb4, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43,
|
||||||
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f,
|
||||||
|
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65,
|
||||||
|
0x74, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
|
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
|
0x12, 0x36, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
|
0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72,
|
||||||
|
0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18,
|
||||||
|
0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6d, 0x74, 0x75, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x75,
|
||||||
|
0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||||
|
0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69,
|
||||||
|
0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||||
|
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
||||||
|
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67,
|
||||||
|
0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78,
|
||||||
|
0x79, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||||
|
0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_proxy_wireguard_config_proto_rawDescOnce sync.Once
|
||||||
|
file_proxy_wireguard_config_proto_rawDescData = file_proxy_wireguard_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_proxy_wireguard_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_proxy_wireguard_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_proxy_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_proxy_wireguard_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_proxy_wireguard_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_proxy_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_proxy_wireguard_config_proto_goTypes = []interface{}{
|
||||||
|
(*PeerConfig)(nil), // 0: xray.proxy.wireguard.PeerConfig
|
||||||
|
(*DeviceConfig)(nil), // 1: xray.proxy.wireguard.DeviceConfig
|
||||||
|
}
|
||||||
|
var file_proxy_wireguard_config_proto_depIdxs = []int32{
|
||||||
|
0, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig
|
||||||
|
1, // [1:1] is the sub-list for method output_type
|
||||||
|
1, // [1:1] is the sub-list for method input_type
|
||||||
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_proxy_wireguard_config_proto_init() }
|
||||||
|
func file_proxy_wireguard_config_proto_init() {
|
||||||
|
if File_proxy_wireguard_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_proxy_wireguard_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PeerConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_proxy_wireguard_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*DeviceConfig); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_proxy_wireguard_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_proxy_wireguard_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_proxy_wireguard_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_proxy_wireguard_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_proxy_wireguard_config_proto = out.File
|
||||||
|
file_proxy_wireguard_config_proto_rawDesc = nil
|
||||||
|
file_proxy_wireguard_config_proto_goTypes = nil
|
||||||
|
file_proxy_wireguard_config_proto_depIdxs = nil
|
||||||
|
}
|
23
proxy/wireguard/config.proto
Normal file
23
proxy/wireguard/config.proto
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.proxy.wireguard;
|
||||||
|
option csharp_namespace = "Xray.Proxy.WireGuard";
|
||||||
|
option go_package = "github.com/xtls/xray-core/proxy/wireguard";
|
||||||
|
option java_package = "com.xray.proxy.wireguard";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message PeerConfig {
|
||||||
|
string public_key = 1;
|
||||||
|
string pre_shared_key = 2;
|
||||||
|
string endpoint = 3;
|
||||||
|
int32 keep_alive = 4;
|
||||||
|
repeated string allowed_ips = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeviceConfig {
|
||||||
|
string secret_key = 1;
|
||||||
|
repeated string endpoint = 2;
|
||||||
|
repeated PeerConfig peers = 3;
|
||||||
|
int32 mtu = 4;
|
||||||
|
int32 num_workers = 5;
|
||||||
|
}
|
9
proxy/wireguard/errors.generated.go
Normal file
9
proxy/wireguard/errors.generated.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import "github.com/xtls/xray-core/common/errors"
|
||||||
|
|
||||||
|
type errPathObjHolder struct{}
|
||||||
|
|
||||||
|
func newError(values ...interface{}) *errors.Error {
|
||||||
|
return errors.New(values...).WithPathObj(errPathObjHolder{})
|
||||||
|
}
|
303
proxy/wireguard/tun.go
Normal file
303
proxy/wireguard/tun.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/wireguard-go/tun"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||||
|
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type netTun struct {
|
||||||
|
ep *channel.Endpoint
|
||||||
|
stack *stack.Stack
|
||||||
|
events chan tun.Event
|
||||||
|
incomingPacket chan *bufferv2.View
|
||||||
|
mtu int
|
||||||
|
dnsClient dns.Client
|
||||||
|
hasV4, hasV6 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Net netTun
|
||||||
|
|
||||||
|
func CreateNetTUN(localAddresses []netip.Addr, dnsClient dns.Client, mtu int) (tun.Device, *Net, error) {
|
||||||
|
opts := stack.Options{
|
||||||
|
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||||
|
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||||
|
HandleLocal: true,
|
||||||
|
}
|
||||||
|
dev := &netTun{
|
||||||
|
ep: channel.New(1024, uint32(mtu), ""),
|
||||||
|
stack: stack.New(opts),
|
||||||
|
events: make(chan tun.Event, 10),
|
||||||
|
incomingPacket: make(chan *bufferv2.View),
|
||||||
|
dnsClient: dnsClient,
|
||||||
|
mtu: mtu,
|
||||||
|
}
|
||||||
|
dev.ep.AddNotify(dev)
|
||||||
|
tcpipErr := dev.stack.CreateNIC(1, dev.ep)
|
||||||
|
if tcpipErr != nil {
|
||||||
|
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||||
|
}
|
||||||
|
for _, ip := range localAddresses {
|
||||||
|
var protoNumber tcpip.NetworkProtocolNumber
|
||||||
|
if ip.Is4() {
|
||||||
|
protoNumber = ipv4.ProtocolNumber
|
||||||
|
} else if ip.Is6() {
|
||||||
|
protoNumber = ipv6.ProtocolNumber
|
||||||
|
}
|
||||||
|
protoAddr := tcpip.ProtocolAddress{
|
||||||
|
Protocol: protoNumber,
|
||||||
|
AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(),
|
||||||
|
}
|
||||||
|
tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{})
|
||||||
|
if tcpipErr != nil {
|
||||||
|
return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr)
|
||||||
|
}
|
||||||
|
if ip.Is4() {
|
||||||
|
dev.hasV4 = true
|
||||||
|
} else if ip.Is6() {
|
||||||
|
dev.hasV6 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dev.hasV4 {
|
||||||
|
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1})
|
||||||
|
}
|
||||||
|
if dev.hasV6 {
|
||||||
|
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
dev.events <- tun.EventUp
|
||||||
|
return dev, (*Net)(dev), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Name() (string, error) {
|
||||||
|
return "go", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) File() *os.File {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Events() chan tun.Event {
|
||||||
|
return tun.events
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Read(buf []byte, offset int) (int, error) {
|
||||||
|
view, ok := <-tun.incomingPacket
|
||||||
|
if !ok {
|
||||||
|
return 0, os.ErrClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
return view.Read(buf[offset:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Write(buf []byte, offset int) (int, error) {
|
||||||
|
packet := buf[offset:]
|
||||||
|
if len(packet) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: bufferv2.MakeWithData(packet)})
|
||||||
|
switch packet[0] >> 4 {
|
||||||
|
case 4:
|
||||||
|
tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb)
|
||||||
|
case 6:
|
||||||
|
tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) WriteNotify() {
|
||||||
|
pkt := tun.ep.Read()
|
||||||
|
if pkt == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
view := pkt.ToView()
|
||||||
|
pkt.DecRef()
|
||||||
|
|
||||||
|
tun.incomingPacket <- view
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Flush() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) Close() error {
|
||||||
|
tun.stack.RemoveNIC(1)
|
||||||
|
|
||||||
|
if tun.events != nil {
|
||||||
|
close(tun.events)
|
||||||
|
}
|
||||||
|
|
||||||
|
tun.ep.Close()
|
||||||
|
|
||||||
|
if tun.incomingPacket != nil {
|
||||||
|
close(tun.incomingPacket)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tun *netTun) MTU() (int, error) {
|
||||||
|
return tun.mtu, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||||
|
var protoNumber tcpip.NetworkProtocolNumber
|
||||||
|
if endpoint.Addr().Is4() {
|
||||||
|
protoNumber = ipv4.ProtocolNumber
|
||||||
|
} else {
|
||||||
|
protoNumber = ipv6.ProtocolNumber
|
||||||
|
}
|
||||||
|
return tcpip.FullAddress{
|
||||||
|
NIC: 1,
|
||||||
|
Addr: tcpip.Address(endpoint.Addr().AsSlice()),
|
||||||
|
Port: endpoint.Port(),
|
||||||
|
}, protoNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||||
|
fa, pn := convertToFullAddr(addr)
|
||||||
|
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||||
|
if addr == nil {
|
||||||
|
return net.DialContextTCPAddrPort(ctx, netip.AddrPort{})
|
||||||
|
}
|
||||||
|
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||||
|
return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||||
|
fa, pn := convertToFullAddr(addr)
|
||||||
|
return gonet.DialTCP(net.stack, fa, pn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||||
|
if addr == nil {
|
||||||
|
return net.DialTCPAddrPort(netip.AddrPort{})
|
||||||
|
}
|
||||||
|
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||||
|
return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) {
|
||||||
|
fa, pn := convertToFullAddr(addr)
|
||||||
|
return gonet.ListenTCP(net.stack, fa, pn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||||
|
if addr == nil {
|
||||||
|
return net.ListenTCPAddrPort(netip.AddrPort{})
|
||||||
|
}
|
||||||
|
ip, _ := netip.AddrFromSlice(addr.IP)
|
||||||
|
return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||||
|
var lfa, rfa *tcpip.FullAddress
|
||||||
|
var pn tcpip.NetworkProtocolNumber
|
||||||
|
if laddr.IsValid() || laddr.Port() > 0 {
|
||||||
|
var addr tcpip.FullAddress
|
||||||
|
addr, pn = convertToFullAddr(laddr)
|
||||||
|
lfa = &addr
|
||||||
|
}
|
||||||
|
if raddr.IsValid() || raddr.Port() > 0 {
|
||||||
|
var addr tcpip.FullAddress
|
||||||
|
addr, pn = convertToFullAddr(raddr)
|
||||||
|
rfa = &addr
|
||||||
|
}
|
||||||
|
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||||
|
return net.DialUDPAddrPort(laddr, netip.AddrPort{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||||
|
var la, ra netip.AddrPort
|
||||||
|
if laddr != nil {
|
||||||
|
ip, _ := netip.AddrFromSlice(laddr.IP)
|
||||||
|
la = netip.AddrPortFrom(ip, uint16(laddr.Port))
|
||||||
|
}
|
||||||
|
if raddr != nil {
|
||||||
|
ip, _ := netip.AddrFromSlice(raddr.IP)
|
||||||
|
ra = netip.AddrPortFrom(ip, uint16(raddr.Port))
|
||||||
|
}
|
||||||
|
return net.DialUDPAddrPort(la, ra)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||||
|
return net.DialUDP(laddr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Net) HasV4() bool {
|
||||||
|
return n.hasV4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Net) HasV6() bool {
|
||||||
|
return n.hasV6
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsDomainName(s string) bool {
|
||||||
|
l := len(s)
|
||||||
|
if l == 0 || l > 254 || l == 254 && s[l-1] != '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
last := byte('.')
|
||||||
|
nonNumeric := false
|
||||||
|
partlen := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_':
|
||||||
|
nonNumeric = true
|
||||||
|
partlen++
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
partlen++
|
||||||
|
case c == '-':
|
||||||
|
if last == '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
partlen++
|
||||||
|
nonNumeric = true
|
||||||
|
case c == '.':
|
||||||
|
if last == '.' || last == '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if partlen > 63 || partlen == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
partlen = 0
|
||||||
|
}
|
||||||
|
last = c
|
||||||
|
}
|
||||||
|
if last == '-' || partlen > 63 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return nonNumeric
|
||||||
|
}
|
263
proxy/wireguard/wireguard.go
Normal file
263
proxy/wireguard/wireguard.go
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Some of codes are copied from https://github.com/octeep/wireproxy, license below.
|
||||||
|
|
||||||
|
Copyright (c) 2022 Wind T.F. Wong <octeep@pm.me>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sagernet/wireguard-go/device"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/log"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
"github.com/xtls/xray-core/common/session"
|
||||||
|
"github.com/xtls/xray-core/common/signal"
|
||||||
|
"github.com/xtls/xray-core/common/task"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/features/policy"
|
||||||
|
"github.com/xtls/xray-core/transport"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is an outbound connection that silently swallow the entire payload.
|
||||||
|
type Handler struct {
|
||||||
|
conf *DeviceConfig
|
||||||
|
net *Net
|
||||||
|
bind *netBindClient
|
||||||
|
policyManager policy.Manager
|
||||||
|
dns dns.Client
|
||||||
|
// cached configuration
|
||||||
|
ipc string
|
||||||
|
endpoints []netip.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new wireguard handler.
|
||||||
|
func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
||||||
|
v := core.MustFromContext(ctx)
|
||||||
|
|
||||||
|
endpoints, err := parseEndpoints(conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Handler{
|
||||||
|
conf: conf,
|
||||||
|
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
|
||||||
|
dns: v.GetFeature(dns.ClientType()).(dns.Client),
|
||||||
|
ipc: createIPCRequest(conf),
|
||||||
|
endpoints: endpoints,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process implements OutboundHandler.Dispatch().
|
||||||
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
||||||
|
if h.bind == nil || h.bind.dialer != dialer || h.net == nil {
|
||||||
|
log.Record(&log.GeneralMessage{
|
||||||
|
Severity: log.Severity_Info,
|
||||||
|
Content: "switching dialer",
|
||||||
|
})
|
||||||
|
// bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer
|
||||||
|
bind := &netBindClient{
|
||||||
|
dialer: dialer,
|
||||||
|
workers: int(h.conf.NumWorkers),
|
||||||
|
dns: h.dns,
|
||||||
|
}
|
||||||
|
|
||||||
|
net, err := h.makeVirtualTun(bind)
|
||||||
|
if err != nil {
|
||||||
|
bind.Close()
|
||||||
|
return newError("failed to create virtual tun interface").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.net = net
|
||||||
|
if h.bind != nil {
|
||||||
|
h.bind.Close()
|
||||||
|
}
|
||||||
|
h.bind = bind
|
||||||
|
}
|
||||||
|
|
||||||
|
outbound := session.OutboundFromContext(ctx)
|
||||||
|
if outbound == nil || !outbound.Target.IsValid() {
|
||||||
|
return newError("target not specified")
|
||||||
|
}
|
||||||
|
// Destination of the inner request.
|
||||||
|
destination := outbound.Target
|
||||||
|
command := protocol.RequestCommandTCP
|
||||||
|
if destination.Network == net.Network_UDP {
|
||||||
|
command = protocol.RequestCommandUDP
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve dns
|
||||||
|
addr := destination.Address
|
||||||
|
if addr.Family().IsDomain() {
|
||||||
|
ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{
|
||||||
|
IPv4Enable: h.net.HasV4(),
|
||||||
|
IPv6Enable: h.net.HasV6(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to lookup DNS").Base(err)
|
||||||
|
} else if len(ips) == 0 {
|
||||||
|
return dns.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
addr = net.IPAddress(ips[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
p := h.policyManager.ForLevel(0)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle)
|
||||||
|
addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value())
|
||||||
|
|
||||||
|
var requestFunc func() error
|
||||||
|
var responseFunc func() error
|
||||||
|
|
||||||
|
if command == protocol.RequestCommandTCP {
|
||||||
|
conn, err := h.net.DialContextTCPAddrPort(ctx, addrPort)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create TCP connection").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestFunc = func() error {
|
||||||
|
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
||||||
|
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
responseFunc = func() error {
|
||||||
|
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||||
|
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
} else if command == protocol.RequestCommandUDP {
|
||||||
|
conn, err := h.net.DialUDPAddrPort(netip.AddrPort{}, addrPort)
|
||||||
|
if err != nil {
|
||||||
|
return newError("failed to create UDP connection").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestFunc = func() error {
|
||||||
|
defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
|
||||||
|
return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
responseFunc = func() error {
|
||||||
|
defer timer.SetTimeout(p.Timeouts.UplinkOnly)
|
||||||
|
return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
|
||||||
|
if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
|
||||||
|
return newError("connection ends").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialize the config into an IPC request
|
||||||
|
func createIPCRequest(conf *DeviceConfig) string {
|
||||||
|
var request bytes.Buffer
|
||||||
|
|
||||||
|
request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey))
|
||||||
|
|
||||||
|
for _, peer := range conf.Peers {
|
||||||
|
request.WriteString(fmt.Sprintf("public_key=%s\nendpoint=%s\npersistent_keepalive_interval=%d\npreshared_key=%s\n",
|
||||||
|
peer.PublicKey, peer.Endpoint, peer.KeepAlive, peer.PreSharedKey))
|
||||||
|
|
||||||
|
for _, ip := range peer.AllowedIps {
|
||||||
|
request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.String()[:request.Len()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert endpoint string to netip.Addr
|
||||||
|
func parseEndpoints(conf *DeviceConfig) ([]netip.Addr, error) {
|
||||||
|
endpoints := make([]netip.Addr, len(conf.Endpoint))
|
||||||
|
for i, str := range conf.Endpoint {
|
||||||
|
var addr netip.Addr
|
||||||
|
if strings.Contains(str, "/") {
|
||||||
|
prefix, err := netip.ParsePrefix(str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
addr = prefix.Addr()
|
||||||
|
if prefix.Bits() != addr.BitLen() {
|
||||||
|
return nil, newError("interface address subnet should be /32 for IPv4 and /128 for IPv6")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
addr, err = netip.ParseAddr(str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endpoints[i] = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// creates a tun interface on netstack given a configuration
|
||||||
|
func (h *Handler) makeVirtualTun(bind *netBindClient) (*Net, error) {
|
||||||
|
tun, tnet, err := CreateNetTUN(h.endpoints, h.dns, int(h.conf.Mtu))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bind.dnsOption.IPv4Enable = tnet.HasV4()
|
||||||
|
bind.dnsOption.IPv6Enable = tnet.HasV6()
|
||||||
|
|
||||||
|
// dev := device.NewDevice(tun, conn.NewDefaultBind(), nil /* device.NewLogger(device.LogLevelVerbose, "") */)
|
||||||
|
dev := device.NewDevice(tun, bind, &device.Logger{
|
||||||
|
Verbosef: func(format string, args ...any) {
|
||||||
|
log.Record(&log.GeneralMessage{
|
||||||
|
Severity: log.Severity_Debug,
|
||||||
|
Content: fmt.Sprintf(format, args...),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Errorf: func(format string, args ...any) {
|
||||||
|
log.Record(&log.GeneralMessage{
|
||||||
|
Severity: log.Severity_Error,
|
||||||
|
Content: fmt.Sprintf(format, args...),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}, int(h.conf.NumWorkers))
|
||||||
|
err = dev.IpcSet(h.ipc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tnet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*DeviceConfig))
|
||||||
|
}))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user