From 4e74a4108d490a11f357b2531da15049ac9129c2 Mon Sep 17 00:00:00 2001 From: Sergei Maklagin Date: Sun, 8 Jun 2025 19:51:17 +0300 Subject: [PATCH] refactor: WARP --- adapter/experimental.go | 6 +- common/cloudflare/api.go | 38 ++-- common/cloudflare/option.go | 4 +- common/cloudflare/profile.go | 21 +-- constant/warp.go | 20 ++ experimental/cachefile/cache.go | 44 +++-- go.mod | 45 +++-- go.sum | 109 +++++------ include/wireguard.go | 2 +- option/experimental.go | 13 +- option/wireguard.go | 25 +-- protocol/wireguard/endpoint_warp.go | 272 ++++++++++++++++------------ 12 files changed, 346 insertions(+), 253 deletions(-) create mode 100644 constant/warp.go diff --git a/adapter/experimental.go b/adapter/experimental.go index fc00b78a..09fb8da7 100644 --- a/adapter/experimental.go +++ b/adapter/experimental.go @@ -33,6 +33,8 @@ type CacheFile interface { StoreRDRC() bool dns.RDRCStore + StoreWARPConfig() bool + LoadMode() string StoreMode(mode string) error LoadSelected(group string) string @@ -41,8 +43,8 @@ type CacheFile interface { StoreGroupExpand(group string, expand bool) error LoadRuleSet(tag string) *SavedBinary SaveRuleSet(tag string, set *SavedBinary) error - LoadCloudflareProfile(tag string) *SavedBinary - SaveCloudflareProfile(tag string, set *SavedBinary) error + LoadWARPConfig(tag string) *SavedBinary + SaveWARPConfig(tag string, set *SavedBinary) error } type SavedBinary struct { diff --git a/common/cloudflare/api.go b/common/cloudflare/api.go index 136ed192..2bd63343 100644 --- a/common/cloudflare/api.go +++ b/common/cloudflare/api.go @@ -10,31 +10,26 @@ import ( "time" "github.com/tidwall/gjson" - "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) -type CloudeflareApi struct { +type CloudflareApi struct { client http.Client } -func NewCloudeflareApi(opts ...CloudflareApiOption) *CloudeflareApi { - api := &CloudeflareApi{http.Client{Timeout: 30 * time.Second}} +func NewCloudflareApi(opts ...CloudflareApiOption) *CloudflareApi { + api := &CloudflareApi{http.Client{Timeout: 30 * time.Second}} for _, opt := range opts { opt(api) } return api } -func (api *CloudeflareApi) CreateProfile(ctx context.Context) (*CloudflareProfile, error) { - privateKey, err := wgtypes.GeneratePrivateKey() - if err != nil { - return nil, err - } +func (api *CloudflareApi) CreateProfile(ctx context.Context, publicKey string) (*CloudflareProfile, error) { request, err := http.NewRequest("POST", "https://api.cloudflareclient.com/v0i1909051800/reg", strings.NewReader( fmt.Sprintf( "{\"install_id\":\"\",\"tos\":\"%s\",\"key\":\"%s\",\"fcm_token\":\"\",\"type\":\"ios\",\"locale\":\"en_US\"}", time.Now().Format("2006-01-02T15:04:05.000Z"), - privateKey.PublicKey().String(), + publicKey, ), )) if err != nil { @@ -53,6 +48,27 @@ func (api *CloudeflareApi) CreateProfile(ctx context.Context) (*CloudflareProfil return nil, err } profile := new(CloudflareProfile) - profile.Config.PrivateKey = privateKey.String() + return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile) +} + +func (api *CloudflareApi) GetProfile(ctx context.Context, authToken string, id string) (*CloudflareProfile, error) { + request, err := http.NewRequest("GET", "https://api.cloudflareclient.com/v0i1909051800/reg/"+id, nil) + if err != nil { + return nil, err + } + request.Header.Set("Authorization", "Bearer "+authToken) + response, err := api.client.Do(request.WithContext(ctx)) + if err != nil { + return nil, err + } + defer response.Body.Close() + if response.StatusCode != 200 { + return nil, fmt.Errorf("status code is not 200") + } + content, err := io.ReadAll(response.Body) + if err != nil { + return nil, err + } + profile := new(CloudflareProfile) return profile, json.NewDecoder(strings.NewReader(gjson.Get(string(content), "result").Raw)).Decode(profile) } diff --git a/common/cloudflare/option.go b/common/cloudflare/option.go index 00c0d6d4..929e91b7 100644 --- a/common/cloudflare/option.go +++ b/common/cloudflare/option.go @@ -6,10 +6,10 @@ import ( "net/http" ) -type CloudflareApiOption func(api *CloudeflareApi) +type CloudflareApiOption func(api *CloudflareApi) func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) CloudflareApiOption { - return func(api *CloudeflareApi) { + return func(api *CloudflareApi) { api.client.Transport = &http.Transport{ DialContext: dialContext, } diff --git a/common/cloudflare/profile.go b/common/cloudflare/profile.go index f9cf76d2..bc011e06 100644 --- a/common/cloudflare/profile.go +++ b/common/cloudflare/profile.go @@ -15,7 +15,7 @@ type CloudflareProfile struct { PremiumData int `json:"premium_data"` Quota int `json:"quota"` Usage int `json:"usage"` - WarpPlus bool `json:"warp_plus"` + WARPPlus bool `json:"warp_plus"` ReferralCount int `json:"referral_count"` ReferralRenewalCountdown int `json:"referral_renewal_countdown"` Role string `json:"role"` @@ -23,9 +23,14 @@ type CloudflareProfile struct { TTL time.Time `json:"ttl"` } `json:"account"` Config struct { - ClientID string `json:"client_id"` - PrivateKey string `json:"private_key"` - Peers []struct { + ClientID string `json:"client_id"` + Interface struct { + Addresses struct { + V4 string `json:"v4"` + V6 string `json:"v6"` + } `json:"addresses"` + } `json:"interface"` + Peers []struct { PublicKey string `json:"public_key"` Endpoint struct { V4 string `json:"v4"` @@ -34,12 +39,6 @@ type CloudflareProfile struct { Ports []int `json:"ports"` } `json:"endpoint"` } `json:"peers"` - Interface struct { - Addresses struct { - V4 string `json:"v4"` - V6 string `json:"v6"` - } `json:"addresses"` - } `json:"interface"` Services struct { HTTPProxy string `json:"http_proxy"` } `json:"services"` @@ -49,7 +48,7 @@ type CloudflareProfile struct { } `json:"metrics"` } `json:"config"` Token string `json:"token"` - WarpEnabled bool `json:"warp_enabled"` + WARPEnabled bool `json:"warp_enabled"` WaitlistEnabled bool `json:"waitlist_enabled"` Created time.Time `json:"created"` Updated time.Time `json:"updated"` diff --git a/constant/warp.go b/constant/warp.go new file mode 100644 index 00000000..038ce346 --- /dev/null +++ b/constant/warp.go @@ -0,0 +1,20 @@ +package constant + +type WARPConfig struct { + PrivateKey string `json:"private_key"` + Interface struct { + Addresses struct { + V4 string `json:"v4"` + V6 string `json:"v6"` + } `json:"addresses"` + } `json:"interface"` + Peers []struct { + PublicKey string `json:"public_key"` + Endpoint struct { + V4 string `json:"v4"` + V6 string `json:"v6"` + Host string `json:"host"` + Ports []int `json:"ports"` + } `json:"endpoint"` + } `json:"peers"` +} diff --git a/experimental/cachefile/cache.go b/experimental/cachefile/cache.go index 7ac324e2..3d9177d4 100644 --- a/experimental/cachefile/cache.go +++ b/experimental/cachefile/cache.go @@ -43,6 +43,7 @@ type CacheFile struct { cacheID []byte storeFakeIP bool storeRDRC bool + storeWARPConfig bool rdrcTimeout time.Duration DB *bbolt.DB saveMetadataTimer *time.Timer @@ -80,16 +81,17 @@ func New(ctx context.Context, options option.CacheFileOptions) *CacheFile { } } return &CacheFile{ - ctx: ctx, - path: filemanager.BasePath(ctx, path), - cacheID: cacheIDBytes, - storeFakeIP: options.StoreFakeIP, - storeRDRC: options.StoreRDRC, - rdrcTimeout: rdrcTimeout, - saveDomain: make(map[netip.Addr]string), - saveAddress4: make(map[string]netip.Addr), - saveAddress6: make(map[string]netip.Addr), - saveRDRC: make(map[saveRDRCCacheKey]bool), + ctx: ctx, + path: filemanager.BasePath(ctx, path), + cacheID: cacheIDBytes, + storeFakeIP: options.StoreFakeIP, + storeRDRC: options.StoreRDRC, + storeWARPConfig: options.StoreWARPConfig, + rdrcTimeout: rdrcTimeout, + saveDomain: make(map[netip.Addr]string), + saveAddress4: make(map[string]netip.Addr), + saveAddress6: make(map[string]netip.Addr), + saveRDRC: make(map[saveRDRCCacheKey]bool), } } @@ -317,35 +319,39 @@ func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error { }) } -func (c *CacheFile) LoadCloudflareProfile(tag string) *adapter.SavedBinary { - var savedProfile adapter.SavedBinary +func (c *CacheFile) StoreWARPConfig() bool { + return c.storeWARPConfig +} + +func (c *CacheFile) LoadWARPConfig(tag string) *adapter.SavedBinary { + var savedConfig adapter.SavedBinary err := c.DB.View(func(t *bbolt.Tx) error { bucket := c.bucket(t, bucketRuleSet) if bucket == nil { return os.ErrNotExist } - profileBinary := bucket.Get([]byte(tag)) - if len(profileBinary) == 0 { + configBinary := bucket.Get([]byte(tag)) + if len(configBinary) == 0 { return os.ErrInvalid } - return savedProfile.UnmarshalBinary(profileBinary) + return savedConfig.UnmarshalBinary(configBinary) }) if err != nil { return nil } - return &savedProfile + return &savedConfig } -func (c *CacheFile) SaveCloudflareProfile(tag string, set *adapter.SavedBinary) error { +func (c *CacheFile) SaveWARPConfig(tag string, set *adapter.SavedBinary) error { return c.DB.Batch(func(t *bbolt.Tx) error { bucket, err := c.createBucket(t, bucketRuleSet) if err != nil { return err } - profileBinary, err := set.MarshalBinary() + configBinary, err := set.MarshalBinary() if err != nil { return err } - return bucket.Put([]byte(tag), profileBinary) + return bucket.Put([]byte(tag), configBinary) }) } diff --git a/go.mod b/go.mod index 4daa087a..29b4554d 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/sagernet/sing-box -go 1.23.6 +go 1.24 toolchain go1.24.3 require ( github.com/caddyserver/certmagic v0.20.0 - github.com/cloudflare/circl v1.3.7 + github.com/cloudflare/circl v1.6.1 github.com/cretz/bine v0.2.0 github.com/go-chi/chi/v5 v5.2.1 github.com/go-chi/render v1.0.3 @@ -17,7 +17,7 @@ require ( github.com/logrusorgru/aurora v2.0.3+incompatible github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 github.com/mholt/acmez v1.2.0 - github.com/miekg/dns v1.1.63 + github.com/miekg/dns v1.1.66 github.com/oschwald/maxminddb-golang v1.12.0 github.com/sagernet/asc-go v0.0.0-20241217030726-d563060fe4e1 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a @@ -45,14 +45,14 @@ require ( github.com/stretchr/testify v1.10.0 go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.38.0 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/mod v0.20.0 - golang.org/x/net v0.34.0 - golang.org/x/sys v0.30.0 + golang.org/x/mod v0.24.0 + golang.org/x/net v0.40.0 + golang.org/x/sys v0.33.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 - google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.72.1 + google.golang.org/protobuf v1.36.6 howett.net/plist v1.0.1 ) @@ -65,32 +65,29 @@ require ( require ( github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.0.6 // indirect + github.com/andybalholm/brotli v1.1.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/libdns/libdns v0.2.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/onsi/ginkgo/v2 v2.9.7 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect github.com/sagernet/nftables v0.3.0-beta.4 // indirect @@ -98,18 +95,18 @@ require ( github.com/tevino/abool/v2 v2.1.0 // indirect github.com/tidwall/gjson v1.18.0 github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect - github.com/vishvananda/netns v0.0.4 // indirect + github.com/vishvananda/netns v0.0.5 // indirect github.com/zeebo/blake3 v0.2.3 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/text v0.25.0 // indirect + golang.org/x/time v0.7.0 + golang.org/x/tools v0.32.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.3.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect ) replace github.com/sagernet/wireguard-go => github.com/getlantern/wireguard-go v0.0.1-beta.5.0.20250303165430-793006c422ec diff --git a/go.sum b/go.sum index 09c49c61..e6aa774e 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,13 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= -github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo= github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI= @@ -24,12 +24,12 @@ github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4= github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= @@ -41,12 +41,12 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= -github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -57,11 +57,11 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -83,22 +83,18 @@ github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVr github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= -github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs= github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY= github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -156,7 +152,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= @@ -174,14 +169,26 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= -github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= -github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -191,18 +198,18 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -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/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -210,32 +217,32 @@ 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.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= -google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= -google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ= +google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA= +google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -245,5 +252,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= -lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= diff --git a/include/wireguard.go b/include/wireguard.go index ead47ced..1e2b92a0 100644 --- a/include/wireguard.go +++ b/include/wireguard.go @@ -14,5 +14,5 @@ func registerWireGuardOutbound(registry *outbound.Registry) { func registerWireGuardEndpoint(registry *endpoint.Registry) { wireguard.RegisterEndpoint(registry) - wireguard.RegisterWarpEndpoint(registry) + wireguard.RegisterWARPEndpoint(registry) } diff --git a/option/experimental.go b/option/experimental.go index bf0df9e7..730740a4 100644 --- a/option/experimental.go +++ b/option/experimental.go @@ -10,12 +10,13 @@ type ExperimentalOptions struct { } type CacheFileOptions struct { - Enabled bool `json:"enabled,omitempty"` - Path string `json:"path,omitempty"` - CacheID string `json:"cache_id,omitempty"` - StoreFakeIP bool `json:"store_fakeip,omitempty"` - StoreRDRC bool `json:"store_rdrc,omitempty"` - RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Path string `json:"path,omitempty"` + CacheID string `json:"cache_id,omitempty"` + StoreFakeIP bool `json:"store_fakeip,omitempty"` + StoreRDRC bool `json:"store_rdrc,omitempty"` + StoreWARPConfig bool `json:"store_warp_config,omitempty"` + RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"` } type ClashAPIOptions struct { diff --git a/option/wireguard.go b/option/wireguard.go index f819ca8f..f0b49181 100644 --- a/option/wireguard.go +++ b/option/wireguard.go @@ -30,20 +30,23 @@ type WireGuardPeer struct { Reserved []uint8 `json:"reserved,omitempty"` } -type WireGuardWarpEndpointOptions struct { - System bool `json:"system,omitempty"` - Name string `json:"name,omitempty"` - ListenPort uint16 `json:"listen_port,omitempty"` - UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"` - Workers int `json:"workers,omitempty"` - Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"` - Profile *WireGuardWarpProfile `json:"profile,omitempty"` +type WireGuardWARPEndpointOptions struct { + System bool `json:"system,omitempty"` + Name string `json:"name,omitempty"` + ListenPort uint16 `json:"listen_port,omitempty"` + UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"` + Workers int `json:"workers,omitempty"` + Amnezia *WireGuardAmnezia `json:"amnezia,omitempty"` + Profile *WireGuardCloudflareProfile `json:"profile,omitempty"` DialerOptions } -type WireGuardWarpProfile struct { - Detour string `json:"detour,omitempty"` - Recreate bool `json:"recreate,omitempty"` +type WireGuardCloudflareProfile struct { + ID string `json:"id,omitempty"` + PrivateKey string `json:"private_key,omitempty"` + AuthToken string `json:"auth_token,omitempty"` + Recreate bool `json:"recreate,omitempty"` + Detour string `json:"detour,omitempty"` } type LegacyWireGuardOutboundOptions struct { diff --git a/protocol/wireguard/endpoint_warp.go b/protocol/wireguard/endpoint_warp.go index 8b4d0281..9773cd01 100644 --- a/protocol/wireguard/endpoint_warp.go +++ b/protocol/wireguard/endpoint_warp.go @@ -7,6 +7,7 @@ import ( "net" "net/netip" "strings" + "sync" "time" "github.com/sagernet/sing-box/adapter" @@ -20,23 +21,22 @@ import ( M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" "github.com/sagernet/sing/service" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) -func RegisterWarpEndpoint(registry *endpoint.Registry) { - endpoint.Register[option.WireGuardWarpEndpointOptions](registry, C.TypeWARP, NewWarpEndpoint) +func RegisterWARPEndpoint(registry *endpoint.Registry) { + endpoint.Register[option.WireGuardWARPEndpointOptions](registry, C.TypeWARP, NewWARPEndpoint) } -type WarpEndpoint struct { +type WARPEndpoint struct { endpoint.Adapter - endpoint adapter.Endpoint - ctx context.Context - router adapter.Router - logger log.ContextLogger - tag string - options option.WireGuardWarpEndpointOptions + endpoint adapter.Endpoint + startHandler func() + + mtx sync.Mutex } -func NewWarpEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardWarpEndpointOptions) (adapter.Endpoint, error) { +func NewWARPEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardWARPEndpointOptions) (adapter.Endpoint, error) { var dependencies []string if options.Detour != "" { dependencies = append(dependencies, options.Detour) @@ -46,128 +46,170 @@ func NewWarpEndpoint(ctx context.Context, router adapter.Router, logger log.Cont dependencies = append(dependencies, options.Profile.Detour) } } - return &WarpEndpoint{ + warpEndpoint := &WARPEndpoint{ Adapter: endpoint.NewAdapter(C.TypeWARP, tag, []string{N.NetworkTCP, N.NetworkUDP}, dependencies), - ctx: ctx, - router: router, - logger: logger, - tag: tag, - options: options, - }, nil + } + warpEndpoint.mtx.Lock() + warpEndpoint.startHandler = func() { + defer warpEndpoint.mtx.Unlock() + cacheFile := service.FromContext[adapter.CacheFile](ctx) + var config *C.WARPConfig + var err error + if !options.Profile.Recreate && cacheFile != nil && cacheFile.StoreWARPConfig() { + savedProfile := cacheFile.LoadWARPConfig(tag) + if savedProfile != nil { + if err = json.Unmarshal(savedProfile.Content, &config); err != nil { + logger.ErrorContext(ctx, err) + return + } + } + } + if config == nil { + var privateKey wgtypes.Key + if options.Profile.PrivateKey != "" { + privateKey, err = wgtypes.ParseKey(options.Profile.PrivateKey) + if err != nil { + logger.ErrorContext(ctx, err) + return + } + } else { + privateKey, err = wgtypes.GeneratePrivateKey() + if err != nil { + logger.ErrorContext(ctx, err) + return + } + } + opts := make([]cloudflare.CloudflareApiOption, 0, 1) + if options.Profile != nil { + if options.Profile.Detour != "" { + detour, ok := service.FromContext[adapter.OutboundManager](ctx).Outbound(options.Profile.Detour) + if !ok { + logger.ErrorContext(ctx, E.New("outbound detour not found: ", options.Profile.Detour)) + return + } + opts = append(opts, cloudflare.WithDialContext(func(ctx context.Context, network, addr string) (net.Conn, error) { + return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) + })) + } + } + api := cloudflare.NewCloudflareApi(opts...) + var profile *cloudflare.CloudflareProfile + if options.Profile.AuthToken != "" && options.Profile.ID != "" { + profile, err = api.GetProfile(ctx, options.Profile.AuthToken, options.Profile.ID) + if err != nil { + logger.ErrorContext(ctx, err) + return + } + } else { + profile, err = api.CreateProfile(ctx, privateKey.PublicKey().String()) + if err != nil { + logger.ErrorContext(ctx, err) + return + } + } + config = &C.WARPConfig{ + PrivateKey: privateKey.String(), + Interface: profile.Config.Interface, + Peers: profile.Config.Peers, + } + if cacheFile != nil && cacheFile.StoreWARPConfig() { + content, err := json.Marshal(config) + if err != nil { + logger.ErrorContext(ctx, err) + return + } + cacheFile.SaveWARPConfig(tag, &adapter.SavedBinary{ + LastUpdated: time.Now(), + Content: content, + LastEtag: "", + }) + } + } + peer := config.Peers[0] + hostParts := strings.Split(peer.Endpoint.Host, ":") + warpEndpoint.endpoint, err = NewEndpoint( + ctx, + router, + logger, + tag, + option.WireGuardEndpointOptions{ + System: options.System, + Name: options.Name, + ListenPort: options.ListenPort, + UDPTimeout: options.UDPTimeout, + Workers: options.Workers, + Amnezia: options.Amnezia, + DialerOptions: options.DialerOptions, + + Address: badoption.Listable[netip.Prefix]{ + netip.MustParsePrefix(config.Interface.Addresses.V4 + "/32"), + netip.MustParsePrefix(config.Interface.Addresses.V6 + "/128"), + }, + PrivateKey: config.PrivateKey, + Peers: []option.WireGuardPeer{ + { + Address: hostParts[0], + Port: uint16(peer.Endpoint.Ports[rand.Intn(len(peer.Endpoint.Ports))]), + PublicKey: peer.PublicKey, + AllowedIPs: badoption.Listable[netip.Prefix]{ + netip.MustParsePrefix("0.0.0.0/0"), + netip.MustParsePrefix("::/0"), + }, + }, + }, + MTU: 1280, + }, + ) + if err != nil { + logger.ErrorContext(ctx, err) + return + } + if err = warpEndpoint.endpoint.Start(adapter.StartStateStart); err != nil { + logger.ErrorContext(ctx, err) + return + } + if err = warpEndpoint.endpoint.Start(adapter.StartStatePostStart); err != nil { + logger.ErrorContext(ctx, err) + return + } + } + return warpEndpoint, nil } -func (w *WarpEndpoint) Start(stage adapter.StartStage) error { +func (w *WARPEndpoint) Start(stage adapter.StartStage) error { if stage != adapter.StartStatePostStart { return nil } - cacheFile := service.FromContext[adapter.CacheFile](w.ctx) - var profile *cloudflare.CloudflareProfile - var err error - if !w.options.Profile.Recreate { - if cacheFile != nil { - savedProfile := cacheFile.LoadCloudflareProfile(w.tag) - if savedProfile != nil { - err := json.Unmarshal(savedProfile.Content, &profile) - if err != nil { - return err - } - } - } - } - if profile == nil { - opts := make([]cloudflare.CloudflareApiOption, 0, 1) - if w.options.Profile != nil { - if w.options.Profile.Detour != "" { - detour, ok := service.FromContext[adapter.OutboundManager](w.ctx).Outbound(w.options.Profile.Detour) - if !ok { - return E.New("outbound detour not found: ", w.options.Profile.Detour) - } - opts = append(opts, cloudflare.WithDialContext(func(ctx context.Context, network, addr string) (net.Conn, error) { - return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) - })) - } - } - api := cloudflare.NewCloudeflareApi(opts...) - profile, err = api.CreateProfile(w.ctx) - if err != nil { - return err - } - if cacheFile != nil { - content, err := json.Marshal(profile) - if err != nil { - return err - } - cacheFile.SaveCloudflareProfile(w.tag, &adapter.SavedBinary{ - LastUpdated: time.Now(), - Content: content, - LastEtag: "", - }) - } - } - peer := profile.Config.Peers[0] - hostParts := strings.Split(peer.Endpoint.Host, ":") - w.endpoint, err = NewEndpoint( - w.ctx, - w.router, - w.logger, - w.tag, - option.WireGuardEndpointOptions{ - System: w.options.System, - Name: w.options.Name, - ListenPort: w.options.ListenPort, - UDPTimeout: w.options.UDPTimeout, - Workers: w.options.Workers, - Amnezia: w.options.Amnezia, - DialerOptions: w.options.DialerOptions, - - Address: badoption.Listable[netip.Prefix]{ - netip.MustParsePrefix(profile.Config.Interface.Addresses.V4 + "/32"), - netip.MustParsePrefix(profile.Config.Interface.Addresses.V6 + "/128"), - }, - PrivateKey: profile.Config.PrivateKey, - Peers: []option.WireGuardPeer{ - { - Address: hostParts[0], - Port: uint16(peer.Endpoint.Ports[rand.Intn(len(peer.Endpoint.Ports))]), - PublicKey: peer.PublicKey, - AllowedIPs: badoption.Listable[netip.Prefix]{ - netip.MustParsePrefix("0.0.0.0/0"), - netip.MustParsePrefix("::/0"), - }, - }, - }, - MTU: 1280, - }, - ) - if err != nil { - return err - } - if err := w.endpoint.Start(adapter.StartStateStart); err != nil { - return err - } - if err := w.endpoint.Start(adapter.StartStatePostStart); err != nil { - return err - } + go w.startHandler() return nil } -func (w *WarpEndpoint) Close() error { - if w.endpoint == nil { - return E.New("endpoint not initialized") +func (w *WARPEndpoint) Close() error { + if err := w.isEndpointInitialized(); err != nil { + return err } return w.endpoint.Close() } -func (w *WarpEndpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - if w.endpoint == nil { - return nil, E.New("endpoint not initialized") +func (w *WARPEndpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + if err := w.isEndpointInitialized(); err != nil { + return nil, err } return w.endpoint.DialContext(ctx, network, destination) } -func (w *WarpEndpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - if w.endpoint == nil { - return nil, E.New("endpoint not initialized") +func (w *WARPEndpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + if err := w.isEndpointInitialized(); err != nil { + return nil, err } return w.endpoint.ListenPacket(ctx, destination) } + +func (w *WARPEndpoint) isEndpointInitialized() error { + w.mtx.Lock() + defer w.mtx.Unlock() + if w.endpoint == nil { + return E.New("endpoint not initialized") + } + return nil +}