mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Add tun platform options
This commit is contained in:
parent
ed50257735
commit
7834d6bca7
13
box.go
13
box.go
|
@ -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, "]")
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,7 +16,6 @@ type _Options struct {
|
||||||
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
104
option/platform.go
Normal 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
|
||||||
|
}
|
|
@ -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
10
option/tun_platform.go
Normal 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
|
||||||
|
}
|
Loading…
Reference in a new issue