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"
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, "]")

View file

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

View file

@ -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")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,7 +5,6 @@ import (
"strings"
"github.com/sagernet/sing-box/common/json"
"github.com/sagernet/sing-box/experimental/libbox/platform"
E "github.com/sagernet/sing/common/exceptions"
)
@ -17,7 +16,6 @@ type _Options struct {
Outbounds []Outbound `json:"outbounds,omitempty"`
Route *RouteOptions `json:"route,omitempty"`
Experimental *ExperimentalOptions `json:"experimental,omitempty"`
PlatformInterface platform.Interface `json:"-"`
}
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"`
UDPTimeout int64 `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
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
}