Add tun platform options

This commit is contained in:
世界 2023-02-28 19:02:27 +08:00
parent ed50257735
commit 7834d6bca7
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
12 changed files with 213 additions and 26 deletions

13
box.go
View file

@ -11,6 +11,7 @@ import (
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant" C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental" "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/inbound"
"github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/option"
@ -36,7 +37,7 @@ type Box struct {
done chan 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() createdAt := time.Now()
logOptions := common.PtrValueOrDefault(options.Log) logOptions := common.PtrValueOrDefault(options.Log)
@ -61,7 +62,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
} else { } else {
switch logOptions.Output { switch logOptions.Output {
case "": case "":
if options.PlatformInterface != nil { if platformInterface != nil {
logWriter = io.Discard logWriter = io.Discard
} else { } else {
logWriter = os.Stdout 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", TimestampFormat: "-0700 2006-01-02 15:04:05",
} }
if needClashAPI { if needClashAPI {
observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, options.PlatformInterface) observableLogFactory = log.NewObservableFactory(logFormatter, logWriter, platformInterface)
logFactory = observableLogFactory logFactory = observableLogFactory
} else { } else {
logFactory = log.NewFactory(logFormatter, logWriter, options.PlatformInterface) logFactory = log.NewFactory(logFormatter, logWriter, platformInterface)
} }
if logOptions.Level != "" { if logOptions.Level != "" {
logLevel, err := log.ParseLevel(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.DNS),
common.PtrValueOrDefault(options.NTP), common.PtrValueOrDefault(options.NTP),
options.Inbounds, options.Inbounds,
options.PlatformInterface, platformInterface,
) )
if err != nil { if err != nil {
return nil, E.Cause(err, "parse route options") return nil, E.Cause(err, "parse route options")
@ -129,7 +130,7 @@ func New(ctx context.Context, options option.Options) (*Box, error) {
router, router,
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")), logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
inboundOptions, inboundOptions,
options.PlatformInterface, platformInterface,
) )
if err != nil { if err != nil {
return nil, E.Cause(err, "parse inbound[", i, "]") return nil, E.Cause(err, "parse inbound[", i, "]")

View file

@ -31,7 +31,7 @@ func check() error {
return err return err
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
_, err = box.New(ctx, options) _, err = box.New(ctx, options, nil)
cancel() cancel()
return err return err
} }

View file

@ -64,7 +64,7 @@ func create() (*box.Box, context.CancelFunc, error) {
options.Log.DisableColor = true options.Log.DisableColor = true
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
instance, err := box.New(ctx, options) instance, err := box.New(ctx, options, nil)
if err != nil { if err != nil {
cancel() cancel()
return nil, nil, E.Cause(err, "create service") return nil, nil, E.Cause(err, "create service")

View file

@ -1,5 +1,7 @@
package libbox package libbox
import "github.com/sagernet/sing-box/option"
type PlatformInterface interface { type PlatformInterface interface {
AutoDetectInterfaceControl(fd int32) error AutoDetectInterfaceControl(fd int32) error
OpenTun(options TunOptions) (int32, error) OpenTun(options TunOptions) (int32, error)
@ -14,3 +16,51 @@ type TunInterface interface {
FileDescriptor() int32 FileDescriptor() int32
Close() error 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
}

View file

@ -4,13 +4,14 @@ import (
"io" "io"
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
) )
type Interface interface { type Interface interface {
AutoDetectInterfaceControl() control.Func AutoDetectInterfaceControl() control.Func
OpenTun(options tun.Options) (tun.Tun, error) OpenTun(options tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
process.Searcher process.Searcher
io.Writer io.Writer
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing-box/common/process" "github.com/sagernet/sing-box/common/process"
"github.com/sagernet/sing-box/experimental/libbox/internal/procfs" "github.com/sagernet/sing-box/experimental/libbox/internal/procfs"
"github.com/sagernet/sing-box/experimental/libbox/platform" "github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control" "github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -28,9 +29,8 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
return nil, err return nil, err
} }
platformInterface.WriteLog("Hello " + runtime.GOOS + "/" + runtime.GOARCH) platformInterface.WriteLog("Hello " + runtime.GOOS + "/" + runtime.GOARCH)
options.PlatformInterface = &platformInterfaceWrapper{platformInterface, platformInterface.UseProcFS()}
ctx, cancel := context.WithCancel(context.Background()) 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 { if err != nil {
cancel() cancel()
return nil, E.Cause(err, "create service") 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 { if len(options.IncludeUID) > 0 || len(options.ExcludeUID) > 0 {
return nil, E.New("android: unsupported uid options") return nil, E.New("android: unsupported uid options")
} }
if len(options.IncludeAndroidUser) > 0 { if len(options.IncludeAndroidUser) > 0 {
return nil, E.New("android: unsupported android_user option") return nil, E.New("android: unsupported android_user option")
} }
tunFd, err := w.iif.OpenTun(&tunOptions{options, platformOptions})
optionsWrapper := tunOptions(options)
tunFd, err := w.iif.OpenTun(&optionsWrapper)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -4,6 +4,7 @@ import (
"net" "net"
"net/netip" "net/netip"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-tun" "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
@ -21,6 +22,9 @@ type TunOptions interface {
GetInet6RouteAddress() RoutePrefixIterator GetInet6RouteAddress() RoutePrefixIterator
GetIncludePackage() StringIterator GetIncludePackage() StringIterator
GetExcludePackage() StringIterator GetExcludePackage() StringIterator
IsHTTPProxyEnabled() bool
GetHTTPProxyServer() string
GetHTTPProxyServerPort() int32
} }
type RoutePrefix struct { type RoutePrefix struct {
@ -54,7 +58,10 @@ func mapRoutePrefix(prefixes []netip.Prefix) RoutePrefixIterator {
var _ TunOptions = (*tunOptions)(nil) var _ TunOptions = (*tunOptions)(nil)
type tunOptions tun.Options type tunOptions struct {
tun.Options
option.TunPlatformOptions
}
func (o *tunOptions) GetInet4Address() RoutePrefixIterator { func (o *tunOptions) GetInet4Address() RoutePrefixIterator {
return mapRoutePrefix(o.Inet4Address) return mapRoutePrefix(o.Inet4Address)
@ -98,3 +105,18 @@ func (o *tunOptions) GetIncludePackage() StringIterator {
func (o *tunOptions) GetExcludePackage() StringIterator { func (o *tunOptions) GetExcludePackage() StringIterator {
return newIterator(o.ExcludePackage) 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)
}

View file

@ -36,6 +36,7 @@ type Tun struct {
tunIf tun.Tun tunIf tun.Tun
tunStack tun.Stack tunStack tun.Stack
platformInterface platform.Interface 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) { 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, udpTimeout: udpTimeout,
stack: options.Stack, stack: options.Stack,
platformInterface: platformInterface, platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform),
}, nil }, nil
} }
@ -148,7 +150,7 @@ func (t *Tun) Start() error {
err error err error
) )
if t.platformInterface != nil { if t.platformInterface != nil {
tunInterface, err = t.platformInterface.OpenTun(t.tunOptions) tunInterface, err = t.platformInterface.OpenTun(t.tunOptions, t.platformOptions)
} else { } else {
tunInterface, err = tun.New(t.tunOptions) tunInterface, err = tun.New(t.tunOptions)
} }

View file

@ -5,19 +5,17 @@ import (
"strings" "strings"
"github.com/sagernet/sing-box/common/json" "github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/libbox/platform"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
) )
type _Options struct { type _Options struct {
Log *LogOptions `json:"log,omitempty"` Log *LogOptions `json:"log,omitempty"`
DNS *DNSOptions `json:"dns,omitempty"` DNS *DNSOptions `json:"dns,omitempty"`
NTP *NTPOptions `json:"ntp,omitempty"` NTP *NTPOptions `json:"ntp,omitempty"`
Inbounds []Inbound `json:"inbounds,omitempty"` Inbounds []Inbound `json:"inbounds,omitempty"`
Outbounds []Outbound `json:"outbounds,omitempty"` Outbounds []Outbound `json:"outbounds,omitempty"`
Route *RouteOptions `json:"route,omitempty"` Route *RouteOptions `json:"route,omitempty"`
Experimental *ExperimentalOptions `json:"experimental,omitempty"` Experimental *ExperimentalOptions `json:"experimental,omitempty"`
PlatformInterface platform.Interface `json:"-"`
} }
type Options _Options type Options _Options

104
option/platform.go Normal file
View file

@ -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
}

View file

@ -19,5 +19,6 @@ type TunInboundOptions struct {
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"` EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `json:"udp_timeout,omitempty"` UDPTimeout int64 `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"` Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions InboundOptions
} }

10
option/tun_platform.go Normal file
View file

@ -0,0 +1,10 @@
package option
type TunPlatformOptions struct {
HTTPProxy *HTTPProxyOptions `json:"http_proxy,omitempty"`
}
type HTTPProxyOptions struct {
Enabled bool `json:"enabled,omitempty"`
ServerOptions
}