Fix override logic
This commit is contained in:
@@ -37,8 +37,15 @@ func create(configPath *C.char) *C.char {
|
||||
if err != nil {
|
||||
return C.CString(err.Error())
|
||||
}
|
||||
overrides := shared.ConfigOverrides{ExcludeTunInbound: true, IncludeMixedInbound: true, IncludeLogOutput: true, LogLevel: "info", IncludeLogTimestamp: false, ClashApiPort: 9090}
|
||||
options = shared.ApplyOverrides(options, overrides)
|
||||
overrides := shared.ConfigOverrides{
|
||||
LogOutput: shared.StringAddr("box.log"),
|
||||
EnableTun: shared.BoolAddr(false),
|
||||
SetSystemProxy: shared.BoolAddr(true),
|
||||
}
|
||||
template := shared.DefaultTemplate(overrides)
|
||||
options = shared.ApplyOverrides(template, options, overrides)
|
||||
|
||||
shared.SaveCurrentConfig(sWorkingPath, options)
|
||||
|
||||
instance, err := NewService(options)
|
||||
if err != nil {
|
||||
|
||||
2
go.mod
2
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/sagernet/gomobile v0.0.0-20230728014906-3de089147f59
|
||||
github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a
|
||||
github.com/sagernet/sing-box v1.3.6
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659
|
||||
github.com/xmdhs/clash2singbox v0.0.0-20230810082059-5054938e1bfd
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -54,7 +55,6 @@ require (
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||
github.com/sagernet/quic-go v0.0.0-20230731012313-1327e4015111 // indirect
|
||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||
github.com/sagernet/sing-dns v0.1.9-0.20230731012726-ad50da89b659 // indirect
|
||||
github.com/sagernet/sing-mux v0.1.3-0.20230803070305-ea4a972acd21 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.4 // indirect
|
||||
github.com/sagernet/sing-shadowsocks2 v0.1.3 // indirect
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
// package mobile
|
||||
|
||||
// import (
|
||||
// "encoding/json"
|
||||
// "os"
|
||||
|
||||
// "github.com/hiddify/libcore/shared"
|
||||
// _ "github.com/sagernet/gomobile/event/key"
|
||||
// "github.com/sagernet/sing-box/option"
|
||||
// )
|
||||
|
||||
// func Parse(path string) error {
|
||||
// return shared.ParseConfig(path)
|
||||
// }
|
||||
|
||||
// func ApplyOverrides(path string) (string, error) {
|
||||
// fileContent, err := os.ReadFile(path)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// var options option.Options
|
||||
// err = options.UnmarshalJSON(fileContent)
|
||||
// if err != nil {
|
||||
// return "", err
|
||||
// }
|
||||
// overrides := shared.ConfigOverrides{ExcludeTunInbound: false, IncludeMixedInbound: false, IncludeLogOutput: false, LogLevel: "", IncludeLogTimestamp: false, ClashApiPort: 9090}
|
||||
// options = shared.ApplyOverrides(options, overrides)
|
||||
// config, err := json.Marshal(options)
|
||||
// return string(config), err
|
||||
// }
|
||||
|
||||
package mobile
|
||||
|
||||
import (
|
||||
@@ -23,8 +54,13 @@ func ApplyOverrides(path string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
overrides := shared.ConfigOverrides{ExcludeTunInbound: false, IncludeMixedInbound: false, IncludeLogOutput: false, LogLevel: "", IncludeLogTimestamp: false, ClashApiPort: 9090}
|
||||
options = shared.ApplyOverrides(options, overrides)
|
||||
overrides := shared.ConfigOverrides{
|
||||
EnableTun: shared.BoolAddr(true),
|
||||
SetSystemProxy: shared.BoolAddr(false),
|
||||
LogOutput: shared.StringAddr(""),
|
||||
}
|
||||
template := shared.DefaultTemplate(overrides)
|
||||
options = shared.ApplyOverrides(template, options, overrides)
|
||||
config, err := json.Marshal(options)
|
||||
return string(config), err
|
||||
}
|
||||
|
||||
@@ -1,127 +1,8 @@
|
||||
{
|
||||
"log": {},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{
|
||||
"tag": "remote",
|
||||
"address_resolver": "local",
|
||||
"address": "tcp://1.1.1.1",
|
||||
"strategy": "prefer_ipv4",
|
||||
"detour": "select"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "local",
|
||||
"detour": "direct"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"clash_mode": "global",
|
||||
"server": "remote"
|
||||
},
|
||||
{
|
||||
"clash_mode": "direct",
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"outbound": [
|
||||
"any"
|
||||
],
|
||||
"server": "local"
|
||||
},
|
||||
{
|
||||
"geosite": "ir",
|
||||
"server": "local"
|
||||
}
|
||||
],
|
||||
"strategy": "ipv4_only"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "tun",
|
||||
"inet4_address": "172.19.0.1/30",
|
||||
"sniff": true,
|
||||
"sniff_override_destination": true,
|
||||
"domain_strategy": "ipv4_only",
|
||||
"strict_route": true,
|
||||
"mtu": 9000,
|
||||
"endpoint_independent_nat": true,
|
||||
"auto_route": true
|
||||
},
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "socks-in",
|
||||
"listen": "127.0.0.1",
|
||||
"sniff": true,
|
||||
"sniff_override_destination": true,
|
||||
"domain_strategy": "ipv4_only",
|
||||
"listen_port": 2333,
|
||||
"users": []
|
||||
},
|
||||
{
|
||||
"type": "mixed",
|
||||
"tag": "mixed-in",
|
||||
"sniff": true,
|
||||
"sniff_override_destination": true,
|
||||
"domain_strategy": "ipv4_only",
|
||||
"listen": "127.0.0.1",
|
||||
"listen_port": 2334,
|
||||
"set_system_proxy": true,
|
||||
"users": []
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
},
|
||||
{
|
||||
"type": "dns",
|
||||
"tag": "dns-out"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"geosite": "category-ads-all",
|
||||
"outbound": "block"
|
||||
},
|
||||
{
|
||||
"protocol": "dns",
|
||||
"outbound": "dns-out"
|
||||
},
|
||||
{
|
||||
"clash_mode": "direct",
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"clash_mode": "global",
|
||||
"outbound": "select"
|
||||
},
|
||||
{
|
||||
"geoip": [
|
||||
"ir",
|
||||
"private"
|
||||
],
|
||||
"outbound": "direct"
|
||||
},
|
||||
{
|
||||
"geosite": "ir",
|
||||
"outbound": "direct"
|
||||
}
|
||||
],
|
||||
"auto_detect_interface": true
|
||||
},
|
||||
"experimental": {
|
||||
"clash_api": {
|
||||
"external_controller": "127.0.0.1:9090",
|
||||
"store_selected": true,
|
||||
"secret": ""
|
||||
}
|
||||
}
|
||||
"dns": {},
|
||||
"inbounds": [],
|
||||
"outbounds": [],
|
||||
"route": {},
|
||||
"experimental": {}
|
||||
}
|
||||
22
shared/debug.go
Normal file
22
shared/debug.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sagernet/sing-box/option"
|
||||
)
|
||||
|
||||
func SaveCurrentConfig(path string, options option.Options) error {
|
||||
var buffer bytes.Buffer
|
||||
json.NewEncoder(&buffer)
|
||||
encoder := json.NewEncoder(&buffer)
|
||||
encoder.SetIndent("", " ")
|
||||
err := encoder.Encode(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(path, "current-config.json"), buffer.Bytes(), 0777)
|
||||
}
|
||||
@@ -8,91 +8,95 @@ import (
|
||||
)
|
||||
|
||||
type ConfigOverrides struct {
|
||||
ExcludeTunInbound bool
|
||||
IncludeMixedInbound bool
|
||||
IncludeLogOutput bool
|
||||
IncludeLogTimestamp bool
|
||||
LogLevel string
|
||||
ClashApiPort int
|
||||
ClashApiPort *int `json:"clash-api-port"`
|
||||
EnableTun *bool `json:"enable-tun"`
|
||||
SetSystemProxy *bool `json:"set-system-proxy"`
|
||||
LogLevel *string `json:"log-level"`
|
||||
LogOutput *string `json:"log-output"`
|
||||
DNSRemote *string `json:"dns-remote"`
|
||||
MixedPort *int `json:"mixed-port"`
|
||||
}
|
||||
|
||||
func ApplyOverrides(options option.Options, overrides ConfigOverrides) option.Options {
|
||||
options.Log = &option.LogOptions{
|
||||
Disabled: false,
|
||||
Timestamp: overrides.IncludeLogTimestamp,
|
||||
DisableColor: true,
|
||||
}
|
||||
if overrides.LogLevel != "" {
|
||||
options.Log.Level = overrides.LogLevel
|
||||
}
|
||||
if overrides.IncludeLogOutput {
|
||||
options.Log.Output = "box.log"
|
||||
func ApplyOverrides(base option.Options, options option.Options, overrides ConfigOverrides) option.Options {
|
||||
clashApiPort := pointerOrDefaultInt(overrides.ClashApiPort, 9090)
|
||||
base.Experimental = &option.ExperimentalOptions{
|
||||
ClashAPI: &option.ClashAPIOptions{
|
||||
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", clashApiPort),
|
||||
StoreSelected: true,
|
||||
},
|
||||
}
|
||||
|
||||
options.Experimental.ClashAPI = &option.ClashAPIOptions{
|
||||
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", overrides.ClashApiPort),
|
||||
StoreSelected: true,
|
||||
Secret: "",
|
||||
base.Log = &option.LogOptions{
|
||||
Level: pointerOrDefaultString(overrides.LogLevel, "info"),
|
||||
Output: pointerOrDefaultString(overrides.LogOutput, ""),
|
||||
Disabled: false,
|
||||
Timestamp: false,
|
||||
DisableColor: true,
|
||||
}
|
||||
|
||||
var inbounds []option.Inbound
|
||||
for _, inb := range options.Inbounds {
|
||||
if overrides.ExcludeTunInbound && inb.Type == C.TypeTun {
|
||||
continue
|
||||
}
|
||||
if overrides.IncludeMixedInbound && inb.Type == C.TypeMixed {
|
||||
inb.MixedOptions.SetSystemProxy = true
|
||||
for _, inb := range base.Inbounds {
|
||||
switch inb.Type {
|
||||
case C.TypeTun:
|
||||
if pointerOrDefaultBool(overrides.EnableTun, true) {
|
||||
inbounds = append(inbounds, inb)
|
||||
}
|
||||
default:
|
||||
inbounds = append(inbounds, inb)
|
||||
continue
|
||||
}
|
||||
inbounds = append(inbounds, inb)
|
||||
}
|
||||
options.Inbounds = inbounds
|
||||
base.Inbounds = inbounds
|
||||
|
||||
hasSelector := false
|
||||
hasUrlTest := false
|
||||
var selectable []option.Outbound
|
||||
var urlTests []option.Outbound
|
||||
var outbounds []option.Outbound
|
||||
var tags []string
|
||||
for _, out := range options.Outbounds {
|
||||
if out.Type == C.TypeSelector {
|
||||
hasSelector = true
|
||||
} else if out.Type == C.TypeURLTest {
|
||||
hasUrlTest = true
|
||||
urlTests = append(urlTests, out)
|
||||
}
|
||||
switch out.Type {
|
||||
case C.TypeDirect, C.TypeBlock, C.TypeDNS:
|
||||
continue
|
||||
case C.TypeSelector, C.TypeURLTest:
|
||||
continue
|
||||
default:
|
||||
tags = append(tags, out.Tag)
|
||||
}
|
||||
selectable = append(selectable, out)
|
||||
}
|
||||
var generatedUrlTest *option.Outbound
|
||||
if !hasUrlTest {
|
||||
var urlSelectOuts []string
|
||||
for _, out := range selectable {
|
||||
urlSelectOuts = append(urlSelectOuts, out.Tag)
|
||||
}
|
||||
generatedUrlTest = &option.Outbound{Type: C.TypeURLTest, Tag: "urltest", URLTestOptions: option.URLTestOutboundOptions{Outbounds: urlSelectOuts}}
|
||||
urlTests = append(urlTests, *generatedUrlTest)
|
||||
}
|
||||
if !hasSelector {
|
||||
var selectorOuts []string
|
||||
for _, out := range selectable {
|
||||
selectorOuts = append(selectorOuts, out.Tag)
|
||||
}
|
||||
for _, out := range urlTests {
|
||||
selectorOuts = append(selectorOuts, out.Tag)
|
||||
}
|
||||
|
||||
defaultSelector := option.Outbound{Type: C.TypeSelector, Tag: "select", SelectorOptions: option.SelectorOutboundOptions{Outbounds: selectorOuts}}
|
||||
if generatedUrlTest != nil {
|
||||
defaultSelector.SelectorOptions.Default = generatedUrlTest.Tag
|
||||
}
|
||||
options.Outbounds = append([]option.Outbound{defaultSelector}, options.Outbounds...)
|
||||
}
|
||||
if generatedUrlTest != nil {
|
||||
options.Outbounds = append(options.Outbounds, *generatedUrlTest)
|
||||
outbounds = append(outbounds, out)
|
||||
}
|
||||
|
||||
return options
|
||||
urlTest := option.Outbound{
|
||||
Type: C.TypeURLTest,
|
||||
Tag: "auto",
|
||||
URLTestOptions: option.URLTestOutboundOptions{
|
||||
Outbounds: tags,
|
||||
},
|
||||
}
|
||||
|
||||
selector := option.Outbound{
|
||||
Type: C.TypeSelector,
|
||||
Tag: "select",
|
||||
SelectorOptions: option.SelectorOutboundOptions{
|
||||
Outbounds: append([]string{urlTest.Tag}, tags...),
|
||||
Default: urlTest.Tag,
|
||||
},
|
||||
}
|
||||
|
||||
outbounds = append([]option.Outbound{selector, urlTest}, outbounds...)
|
||||
|
||||
base.Outbounds = append(
|
||||
outbounds,
|
||||
[]option.Outbound{
|
||||
{
|
||||
Tag: "direct",
|
||||
Type: C.TypeDirect,
|
||||
},
|
||||
{
|
||||
Tag: "block",
|
||||
Type: C.TypeBlock,
|
||||
},
|
||||
{
|
||||
Tag: "dns-out",
|
||||
Type: C.TypeDNS,
|
||||
},
|
||||
}...,
|
||||
)
|
||||
|
||||
return base
|
||||
}
|
||||
|
||||
168
shared/template.go
Normal file
168
shared/template.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
dns "github.com/sagernet/sing-dns"
|
||||
)
|
||||
|
||||
func DefaultTemplate(overrides ConfigOverrides) option.Options {
|
||||
var options option.Options
|
||||
|
||||
options.Experimental = &option.ExperimentalOptions{
|
||||
ClashAPI: &option.ClashAPIOptions{
|
||||
ExternalController: "127.0.0.1:9090",
|
||||
StoreSelected: true,
|
||||
},
|
||||
}
|
||||
|
||||
options.Log = &option.LogOptions{
|
||||
Level: "warn",
|
||||
Disabled: false,
|
||||
Timestamp: false,
|
||||
DisableColor: true,
|
||||
}
|
||||
|
||||
options.DNS = &option.DNSOptions{
|
||||
DNSClientOptions: option.DNSClientOptions{
|
||||
Strategy: option.DomainStrategy(dns.DomainStrategyPreferIPv4),
|
||||
IndependentCache: true,
|
||||
},
|
||||
Servers: []option.DNSServerOptions{
|
||||
{
|
||||
Tag: "local",
|
||||
Address: "local",
|
||||
Detour: "direct",
|
||||
},
|
||||
{
|
||||
Tag: "dns-remote",
|
||||
Address: pointerOrDefaultString(overrides.DNSRemote, "tcp://1.1.1.1"),
|
||||
AddressResolver: "local",
|
||||
Strategy: option.DomainStrategy(dns.DomainStrategyPreferIPv4),
|
||||
Detour: "select",
|
||||
},
|
||||
},
|
||||
Rules: []option.DNSRule{
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
ClashMode: "direct",
|
||||
Server: "local",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
ClashMode: "global",
|
||||
Server: "dns-remote",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
DomainSuffix: []string{"ir"},
|
||||
Server: "local",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultDNSRule{
|
||||
Outbound: []string{"any"},
|
||||
Server: "local",
|
||||
},
|
||||
},
|
||||
},
|
||||
ReverseMapping: true,
|
||||
Final: "dns-remote",
|
||||
}
|
||||
|
||||
if pointerOrDefaultBool(overrides.EnableTun, true) {
|
||||
options.Inbounds = append(
|
||||
options.Inbounds,
|
||||
option.Inbound{
|
||||
Type: C.TypeTun,
|
||||
Tag: "tun-in",
|
||||
TunOptions: option.TunInboundOptions{
|
||||
Inet4Address: []option.ListenPrefix{
|
||||
option.ListenPrefix(netip.MustParsePrefix("172.19.0.1/30")),
|
||||
},
|
||||
MTU: 9000,
|
||||
AutoRoute: true,
|
||||
StrictRoute: true,
|
||||
EndpointIndependentNat: true,
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: true,
|
||||
DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
options.Inbounds = append(
|
||||
options.Inbounds,
|
||||
option.Inbound{
|
||||
Type: C.TypeMixed,
|
||||
Tag: "mixed-in",
|
||||
MixedOptions: option.HTTPMixedInboundOptions{
|
||||
ListenOptions: option.ListenOptions{
|
||||
Listen: option.NewListenAddress(netip.MustParseAddr("127.0.0.1")),
|
||||
ListenPort: uint16(pointerOrDefaultInt(overrides.MixedPort, 2334)),
|
||||
InboundOptions: option.InboundOptions{
|
||||
SniffEnabled: true,
|
||||
SniffOverrideDestination: true,
|
||||
DomainStrategy: option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
||||
},
|
||||
},
|
||||
SetSystemProxy: pointerOrDefaultBool(overrides.SetSystemProxy, true),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
options.Route = &option.RouteOptions{
|
||||
Rules: []option.Rule{
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Geosite: []string{"category-ads-all"},
|
||||
Outbound: "block",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
Protocol: []string{"dns"},
|
||||
Outbound: "dns-out",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
ClashMode: "direct",
|
||||
Outbound: "direct",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
ClashMode: "global",
|
||||
Outbound: "select",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: C.RuleTypeDefault,
|
||||
DefaultOptions: option.DefaultRule{
|
||||
GeoIP: []string{"ir", "private"},
|
||||
DomainSuffix: []string{"ir"},
|
||||
Outbound: "direct",
|
||||
},
|
||||
},
|
||||
},
|
||||
AutoDetectInterface: true,
|
||||
OverrideAndroidVPN: true,
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
35
shared/utils.go
Normal file
35
shared/utils.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package shared
|
||||
|
||||
func StringAddr(b string) *string {
|
||||
stringVar := b
|
||||
return &stringVar
|
||||
}
|
||||
|
||||
func BoolAddr(b bool) *bool {
|
||||
boolVar := b
|
||||
return &boolVar
|
||||
}
|
||||
|
||||
func pointerOrDefaultString(p *string, def string) string {
|
||||
if p != nil {
|
||||
return *p
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
func pointerOrDefaultInt(p *int, def int) int {
|
||||
if p != nil {
|
||||
return *p
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
func pointerOrDefaultBool(p *bool, def bool) bool {
|
||||
if p != nil {
|
||||
return *p
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
Reference in New Issue
Block a user