diff --git a/extension/system/admin_service_vpn/admin_tun_service.go b/extension/system/admin_service_vpn/admin_tun_service.go index 83d9c02db..daa4bc0e4 100644 --- a/extension/system/admin_service_vpn/admin_tun_service.go +++ b/extension/system/admin_service_vpn/admin_tun_service.go @@ -38,6 +38,13 @@ func (b *AdminServiceExtension) OnMainServicePreStart(singconfig *option.Options if d, ok := inb.Options.(option.SocksInboundOptions); ok { b.socksOptions = &d } + } else if inb.Type == C.TypeMixed { + if d, ok := inb.Options.(*option.HTTPMixedInboundOptions); ok && d != nil { + b.socksOptions = &option.SocksInboundOptions{ + ListenOptions: option.ListenOptions{ListenPort: d.ListenPort}, + Users: d.Users, + } + } } newInbounds = append(newInbounds, inb) } diff --git a/v2/config/builder.go b/v2/config/builder.go index d37e8b66f..efe75686e 100644 --- a/v2/config/builder.go +++ b/v2/config/builder.go @@ -2,6 +2,7 @@ package config import ( context "context" + crand "crypto/rand" "encoding/base64" "fmt" "math/rand" @@ -17,6 +18,7 @@ import ( C "github.com/sagernet/sing-box/constant" sdns "github.com/sagernet/sing-box/dns" "github.com/sagernet/sing-box/option" + "github.com/sagernet/sing/common/auth" "github.com/sagernet/sing/common/json/badoption" "github.com/sagernet/wireguard-go/hiddify" ) @@ -78,6 +80,9 @@ func BuildConfig(ctx context.Context, hopts *HiddifyOptions, inputOpt *ReadOptio setExperimental(&options, hopts) setLog(&options, hopts) + if hopts.MixedPort > 0 && hopts.MixedUser == "" { + hopts.MixedUser, hopts.MixedPassword = generateLocalProxyCredentials() + } setInbound(&options, hopts) staticIPs := make(map[string][]string) // staticIPs["api.cloudflareclient.com"] = []string{"104.16.192.82", "2606:4700::6810:1854", getRandomWarpIP()} @@ -510,6 +515,7 @@ func setInbound(options *option.Options, hopt *HiddifyOptions) { // DomainStrategy: inboundDomainStrategy, // }, }, + Users: []auth.User{{Username: hopt.MixedUser, Password: hopt.MixedPassword}}, SetSystemProxy: hopt.SetSystemProxy, }, }, @@ -1224,3 +1230,12 @@ func generateRandomString(length int) string { // Trim padding characters and return the string return randomString[:length] } + +func generateLocalProxyCredentials() (user, password string) { + b := make([]byte, 24) + if _, err := crand.Read(b); err != nil { + rand.Read(b) + } + return base64.RawURLEncoding.EncodeToString(b[:12]), + base64.RawURLEncoding.EncodeToString(b[12:]) +} diff --git a/v2/config/hiddify_option.go b/v2/config/hiddify_option.go index c3fa9bfc2..bf12bc1b3 100644 --- a/v2/config/hiddify_option.go +++ b/v2/config/hiddify_option.go @@ -51,6 +51,8 @@ type InboundOptions struct { EnableTunService bool `json:"enable-tun-service,omitempty"` SetSystemProxy bool `json:"set-system-proxy,omitempty"` MixedPort uint16 `json:"mixed-port,omitempty"` + MixedUser string `json:"-"` // generated at runtime, never persisted + MixedPassword string `json:"-"` // generated at runtime, never persisted TProxyPort uint16 `json:"tproxy-port,omitempty"` RedirectPort uint16 `json:"redirect-port,omitempty"` DirectPort uint16 `json:"direct-port,omitempty"` diff --git a/v2/hcommon/request/url_request.go b/v2/hcommon/request/url_request.go index 8d2890d2c..57508aca7 100644 --- a/v2/hcommon/request/url_request.go +++ b/v2/hcommon/request/url_request.go @@ -27,6 +27,8 @@ type Request struct { Method Method Url string SocksPort uint16 + SocksUser string + SocksPass string Timeout time.Duration } @@ -45,7 +47,11 @@ func Send(req Request) (*Response, error) { var transport *http.Transport if req.SocksPort > 0 { - dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", req.SocksPort), nil, proxy.Direct) + var socksAuth *proxy.Auth + if req.SocksUser != "" { + socksAuth = &proxy.Auth{User: req.SocksUser, Password: req.SocksPass} + } + dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", req.SocksPort), socksAuth, proxy.Direct) if err != nil { return nil, err } diff --git a/v2/hcore/independent_instance.go b/v2/hcore/independent_instance.go index 4e38d067b..d86650285 100644 --- a/v2/hcore/independent_instance.go +++ b/v2/hcore/independent_instance.go @@ -65,7 +65,10 @@ func RunInstance(ctx context.Context, hiddifySettings *config.HiddifyOptions, si <-time.After(250 * time.Millisecond) hservice := &HiddifyInstance{ StartedService: instance, - ListenPort: hiddifySettings.InboundOptions.MixedPort} + ListenPort: hiddifySettings.InboundOptions.MixedPort, + ListenUser: hiddifySettings.InboundOptions.MixedUser, + ListenPassword: hiddifySettings.InboundOptions.MixedPassword, + } hservice.PingCloudflare() return hservice, nil } @@ -93,7 +96,11 @@ func (s *HiddifyInstance) ContentFromURL(method string, url string, timeout time return "", err } - dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", s.ListenPort), nil, proxy.Direct) + var socksAuth *proxy.Auth + if s.ListenUser != "" { + socksAuth = &proxy.Auth{User: s.ListenUser, Password: s.ListenPassword} + } + dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("127.0.0.1:%d", s.ListenPort), socksAuth, proxy.Direct) if err != nil { return "", err } diff --git a/v2/hcore/start.go b/v2/hcore/start.go index d089f8a65..fdefa3250 100644 --- a/v2/hcore/start.go +++ b/v2/hcore/start.go @@ -151,6 +151,16 @@ func StartService(ctx context.Context, in *StartRequest) (coreResponse *CoreInfo for inb := range options.Inbounds { if opts, ok := options.Inbounds[inb].Options.(option.SocksInboundOptions); ok { static.ListenPort = opts.ListenPort + if len(opts.Users) > 0 { + static.ListenUser = opts.Users[0].Username + static.ListenPassword = opts.Users[0].Password + } + } else if opts, ok := options.Inbounds[inb].Options.(*option.HTTPMixedInboundOptions); ok && opts != nil { + static.ListenPort = opts.ListenPort + if len(opts.Users) > 0 { + static.ListenUser = opts.Users[0].Username + static.ListenPassword = opts.Users[0].Password + } } } diff --git a/v2/hcore/static_data.go b/v2/hcore/static_data.go index abda008b0..ae4e752cf 100644 --- a/v2/hcore/static_data.go +++ b/v2/hcore/static_data.go @@ -28,6 +28,8 @@ type HiddifyInstance struct { previousStartRequest *StartRequest debug bool ListenPort uint16 + ListenUser string + ListenPassword string BaseContext context.Context endPauseTimer *time.Timer // only for ios