From 7834d6bca7a169f6e2afae9f3bdd3ca57be73242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 28 Feb 2023 19:02:27 +0800 Subject: [PATCH] Add tun platform options --- box.go | 13 +-- cmd/sing-box/cmd_check.go | 2 +- cmd/sing-box/cmd_run.go | 2 +- experimental/libbox/platform.go | 50 +++++++++++ experimental/libbox/platform/interface.go | 3 +- experimental/libbox/service.go | 10 +-- experimental/libbox/tun.go | 24 ++++- inbound/tun.go | 4 +- option/config.go | 16 ++-- option/platform.go | 104 ++++++++++++++++++++++ option/tun.go | 1 + option/tun_platform.go | 10 +++ 12 files changed, 213 insertions(+), 26 deletions(-) create mode 100644 option/platform.go create mode 100644 option/tun_platform.go diff --git a/box.go b/box.go index 2ae960f4..2f83aa8b 100644 --- a/box.go +++ b/box.go @@ -11,6 +11,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/experimental" + "github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/inbound" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -36,7 +37,7 @@ type Box struct { done chan struct{} } -func New(ctx context.Context, options option.Options) (*Box, error) { +func New(ctx context.Context, options option.Options, platformInterface platform.Interface) (*Box, error) { createdAt := time.Now() logOptions := common.PtrValueOrDefault(options.Log) @@ -61,7 +62,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) { } else { switch logOptions.Output { case "": - if options.PlatformInterface != nil { + if platformInterface != nil { logWriter = io.Discard } else { logWriter = os.Stdout @@ -86,10 +87,10 @@ func New(ctx context.Context, options option.Options) (*Box, error) { TimestampFormat: "-0700 2006-01-02 15:04:05", } if needClashAPI { - observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface) + observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface) logFactory = observableLogFactory } else { - logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface) + logFactory = log.NewFactory(logFormatter, logWriter, platformInterface) } if logOptions.Level != "" { logLevel, err := log.ParseLevel(logOptions.Level) @@ -109,7 +110,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) { common.PtrValueOrDefault(options.DNS), common.PtrValueOrDefault(options.NTP), options.Inbounds, - options.PlatformInterface, + platformInterface, ) if err != nil { return nil, E.Cause(err, "parse route options") @@ -129,7 +130,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) { router, logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")), inboundOptions, - options.PlatformInterface, + platformInterface, ) if err != nil { return nil, E.Cause(err, "parse inbound[", i, "]") diff --git a/cmd/sing-box/cmd_check.go b/cmd/sing-box/cmd_check.go index e0bc3cae..0d7fb527 100644 --- a/cmd/sing-box/cmd_check.go +++ b/cmd/sing-box/cmd_check.go @@ -31,7 +31,7 @@ func check() error { return err } ctx, cancel := context.WithCancel(context.Background()) - _, err = box.New(ctx, options) + _, err = box.New(ctx, options, nil) cancel() return err } diff --git a/cmd/sing-box/cmd_run.go b/cmd/sing-box/cmd_run.go index 40d03df6..a0b02a2e 100644 --- a/cmd/sing-box/cmd_run.go +++ b/cmd/sing-box/cmd_run.go @@ -64,7 +64,7 @@ func create() (*box.Box, context.CancelFunc, error) { options.Log.DisableColor = true } ctx, cancel := context.WithCancel(context.Background()) - instance, err := box.New(ctx, options) + instance, err := box.New(ctx, options, nil) if err != nil { cancel() return nil, nil, E.Cause(err, "create service") diff --git a/experimental/libbox/platform.go b/experimental/libbox/platform.go index 527ee737..09195525 100644 --- a/experimental/libbox/platform.go +++ b/experimental/libbox/platform.go @@ -1,5 +1,7 @@ package libbox +import "github.com/sagernet/sing-box/option" + type PlatformInterface interface { AutoDetectInterfaceControl(fd int32) error OpenTun(options TunOptions) (int32, error) @@ -14,3 +16,51 @@ type TunInterface interface { FileDescriptor() int32 Close() error } + +type OnDemandRuleIterator interface { + Next() OnDemandRule + HasNext() bool +} + +type OnDemandRule interface { + Target() int32 + DNSSearchDomainMatch() StringIterator + DNSServerAddressMatch() StringIterator + InterfaceTypeMatch() int32 + SSIDMatch() StringIterator + ProbeURL() string +} + +type onDemandRule struct { + option.OnDemandRule +} + +func (r *onDemandRule) Target() int32 { + if r.OnDemandRule.Action == nil { + return -1 + } + return int32(*r.OnDemandRule.Action) +} + +func (r *onDemandRule) DNSSearchDomainMatch() StringIterator { + return newIterator(r.OnDemandRule.DNSSearchDomainMatch) +} + +func (r *onDemandRule) DNSServerAddressMatch() StringIterator { + return newIterator(r.OnDemandRule.DNSServerAddressMatch) +} + +func (r *onDemandRule) InterfaceTypeMatch() int32 { + if r.OnDemandRule.InterfaceTypeMatch == nil { + return -1 + } + return int32(*r.OnDemandRule.InterfaceTypeMatch) +} + +func (r *onDemandRule) SSIDMatch() StringIterator { + return newIterator(r.OnDemandRule.SSIDMatch) +} + +func (r *onDemandRule) ProbeURL() string { + return r.OnDemandRule.ProbeURL +} diff --git a/experimental/libbox/platform/interface.go b/experimental/libbox/platform/interface.go index 49d131ae..760b9587 100644 --- a/experimental/libbox/platform/interface.go +++ b/experimental/libbox/platform/interface.go @@ -4,13 +4,14 @@ import ( "io" "github.com/sagernet/sing-box/common/process" + "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/control" ) type Interface interface { AutoDetectInterfaceControl() control.Func - OpenTun(options tun.Options) (tun.Tun, error) + OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) process.Searcher io.Writer } diff --git a/experimental/libbox/service.go b/experimental/libbox/service.go index 1c89fd69..b0968443 100644 --- a/experimental/libbox/service.go +++ b/experimental/libbox/service.go @@ -10,6 +10,7 @@ import ( "github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/platform" + "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" @@ -28,9 +29,8 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box return nil, err } platformInterface.WriteLog("Hello " + runtime.GOOS + "/" + runtime.GOARCH) - options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()} ctx, cancel := context.WithCancel(context.Background()) - instance, err := box.New(ctx, options) + instance, err := box.New(ctx, options, &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}) if err != nil { cancel() return nil, E.Cause(err, "create service") @@ -66,16 +66,14 @@ func (w *platformInterfaceWrapper) AutoDetectInterfaceControl() control.Func { } } -func (w *platformInterfaceWrapper) OpenTun(options tun.Options) (tun.Tun, error) { +func (w *platformInterfaceWrapper) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error) { if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 { return nil, E.New("android: unsupported uid options") } if len(options.IncludeAndroidUser) > 0 { return nil, E.New("android: unsupported android_user option") } - - optionsWrapper := tunOptions(options) - tunFd, err := w.iif.OpenTun(&optionsWrapper) + tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions}) if err != nil { return nil, err } diff --git a/experimental/libbox/tun.go b/experimental/libbox/tun.go index d9013e59..e7db249c 100644 --- a/experimental/libbox/tun.go +++ b/experimental/libbox/tun.go @@ -4,6 +4,7 @@ import ( "net" "net/netip" + "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" @@ -21,6 +22,9 @@ type TunOptions interface { GetInet6RouteAddress() RoutePrefixIterator GetIncludePackage() StringIterator GetExcludePackage() StringIterator + IsHTTPProxyEnabled() bool + GetHTTPProxyServer() string + GetHTTPProxyServerPort() int32 } type RoutePrefix struct { @@ -54,7 +58,10 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator { var _ TunOptions = (*tunOptions)(nil) -type tunOptions tun.Options +type tunOptions struct { + tun.Options + option.TunPlatformOptions +} func (o *tunOptions) GetInet4Address() RoutePrefixIterator { return mapRoutePrefix(o.Inet4Address) @@ -98,3 +105,18 @@ func (o *tunOptions) GetIncludePackage() StringIterator { func (o *tunOptions) GetExcludePackage() StringIterator { return newIterator(o.ExcludePackage) } + +func (o *tunOptions) IsHTTPProxyEnabled() bool { + if o.TunPlatformOptions.HTTPProxy == nil { + return false + } + return o.TunPlatformOptions.HTTPProxy.Enabled +} + +func (o *tunOptions) GetHTTPProxyServer() string { + return o.TunPlatformOptions.HTTPProxy.Server +} + +func (o *tunOptions) GetHTTPProxyServerPort() int32 { + return int32(o.TunPlatformOptions.HTTPProxy.ServerPort) +} diff --git a/inbound/tun.go b/inbound/tun.go index c292ba91..30bd4f0b 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -36,6 +36,7 @@ type Tun struct { tunIf tun.Tun tunStack tun.Stack platformInterface platform.Interface + platformOptions option.TunPlatformOptions } func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) { @@ -96,6 +97,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger udpTimeout: udpTimeout, stack: options.Stack, platformInterface: platformInterface, + platformOptions: common.PtrValueOrDefault(options.Platform), }, nil } @@ -148,7 +150,7 @@ func (t *Tun) Start() error { err error ) if t.platformInterface != nil { - tunInterface, err = t.platformInterface.OpenTun(t.tunOptions) + tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions) } else { tunInterface, err = tun.New(t.tunOptions) } diff --git a/option/config.go b/option/config.go index 3accfa1d..974b60fa 100644 --- a/option/config.go +++ b/option/config.go @@ -5,19 +5,17 @@ import ( "strings" "github.com/sagernet/sing-box/common/json" - "github.com/sagernet/sing-box/experimental/libbox/platform" E "github.com/sagernet/sing/common/exceptions" ) type _Options struct { - Log *LogOptions `json:"log,omitempty"` - DNS *DNSOptions `json:"dns,omitempty"` - NTP *NTPOptions `json:"ntp,omitempty"` - Inbounds []Inbound `json:"inbounds,omitempty"` - Outbounds []Outbound `json:"outbounds,omitempty"` - Route *RouteOptions `json:"route,omitempty"` - Experimental *ExperimentalOptions `json:"experimental,omitempty"` - PlatformInterface platform.Interface `json:"-"` + Log *LogOptions `json:"log,omitempty"` + DNS *DNSOptions `json:"dns,omitempty"` + NTP *NTPOptions `json:"ntp,omitempty"` + Inbounds []Inbound `json:"inbounds,omitempty"` + Outbounds []Outbound `json:"outbounds,omitempty"` + Route *RouteOptions `json:"route,omitempty"` + Experimental *ExperimentalOptions `json:"experimental,omitempty"` } type Options _Options diff --git a/option/platform.go b/option/platform.go new file mode 100644 index 00000000..fd3d73f0 --- /dev/null +++ b/option/platform.go @@ -0,0 +1,104 @@ +package option + +import ( + "github.com/sagernet/sing-box/common/json" + E "github.com/sagernet/sing/common/exceptions" +) + +type OnDemandOptions struct { + Enabled bool `json:"enabled,omitempty"` + Rules []OnDemandRule `json:"rules,omitempty"` +} + +type OnDemandRule struct { + Action *OnDemandRuleAction `json:"action,omitempty"` + DNSSearchDomainMatch Listable[string] `json:"dns_search_domain_match,omitempty"` + DNSServerAddressMatch Listable[string] `json:"dns_server_address_match,omitempty"` + InterfaceTypeMatch *OnDemandRuleInterfaceType `json:"interface_type_match,omitempty"` + SSIDMatch Listable[string] `json:"ssid_match,omitempty"` + ProbeURL string `json:"probe_url,omitempty"` +} + +type OnDemandRuleAction int + +func (r *OnDemandRuleAction) MarshalJSON() ([]byte, error) { + if r == nil { + return nil, nil + } + value := *r + var actionName string + switch value { + case 1: + actionName = "connect" + case 2: + actionName = "disconnect" + case 3: + actionName = "evaluate_connection" + default: + return nil, E.New("unknown action: ", value) + } + return json.Marshal(actionName) +} + +func (r *OnDemandRuleAction) UnmarshalJSON(bytes []byte) error { + var actionName string + if err := json.Unmarshal(bytes, &actionName); err != nil { + return err + } + var actionValue int + switch actionName { + case "connect": + actionValue = 1 + case "disconnect": + actionValue = 2 + case "evaluate_connection": + actionValue = 3 + case "ignore": + actionValue = 4 + default: + return E.New("unknown action name: ", actionName) + } + *r = OnDemandRuleAction(actionValue) + return nil +} + +type OnDemandRuleInterfaceType int + +func (r *OnDemandRuleInterfaceType) MarshalJSON() ([]byte, error) { + if r == nil { + return nil, nil + } + value := *r + var interfaceTypeName string + switch value { + case 1: + interfaceTypeName = "any" + case 2: + interfaceTypeName = "wifi" + case 3: + interfaceTypeName = "cellular" + default: + return nil, E.New("unknown interface type: ", value) + } + return json.Marshal(interfaceTypeName) +} + +func (r *OnDemandRuleInterfaceType) UnmarshalJSON(bytes []byte) error { + var interfaceTypeName string + if err := json.Unmarshal(bytes, &interfaceTypeName); err != nil { + return err + } + var interfaceTypeValue int + switch interfaceTypeName { + case "any": + interfaceTypeValue = 1 + case "wifi": + interfaceTypeValue = 2 + case "cellular": + interfaceTypeValue = 3 + default: + return E.New("unknown interface type name: ", interfaceTypeName) + } + *r = OnDemandRuleInterfaceType(interfaceTypeValue) + return nil +} diff --git a/option/tun.go b/option/tun.go index a02ebaa7..731b6eed 100644 --- a/option/tun.go +++ b/option/tun.go @@ -19,5 +19,6 @@ type TunInboundOptions struct { EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"` Stack string `json:"stack,omitempty"` + Platform *TunPlatformOptions `json:"platform,omitempty"` InboundOptions } diff --git a/option/tun_platform.go b/option/tun_platform.go new file mode 100644 index 00000000..873d788a --- /dev/null +++ b/option/tun_platform.go @@ -0,0 +1,10 @@ +package option + +type TunPlatformOptions struct { + HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"` +} + +type HTTPProxyOptions struct { + Enabled bool `json:"enabled,omitempty"` + ServerOptions +}