Skip to content

Commit

Permalink
Add tun platform options
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Feb 28, 2023
1 parent ed50257 commit 7834d6b
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 26 deletions.
13 changes: 7 additions & 6 deletions box.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)

Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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")
Expand All @@ -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, "]")
Expand Down
2 changes: 1 addition & 1 deletion cmd/sing-box/cmd_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion cmd/sing-box/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
50 changes: 50 additions & 0 deletions experimental/libbox/platform.go
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
}
3 changes: 2 additions & 1 deletion experimental/libbox/platform/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
10 changes: 4 additions & 6 deletions experimental/libbox/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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")
Expand Down Expand Up @@ -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
}
Expand Down
24 changes: 23 additions & 1 deletion experimental/libbox/tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -21,6 22,9 @@ type TunOptions interface {
GetInet6RouteAddress() RoutePrefixIterator
GetIncludePackage() StringIterator
GetExcludePackage() StringIterator
IsHTTPProxyEnabled() bool
GetHTTPProxyServer() string
GetHTTPProxyServerPort() int32
}

type RoutePrefix struct {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
4 changes: 3 additions & 1 deletion inbound/tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down
16 changes: 7 additions & 9 deletions option/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 104 additions & 0 deletions option/platform.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 7834d6b

Please sign in to comment.