Add config builder

This commit is contained in:
problematicconsumer
2023-09-01 14:52:30 +03:30
parent 2506403aec
commit 9a8b25076f
6 changed files with 354 additions and 319 deletions

335
shared/config.go Normal file
View File

@@ -0,0 +1,335 @@
package shared
import (
"fmt"
"net/netip"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
dns "github.com/sagernet/sing-dns"
)
type ConfigOptions struct {
ExecuteAsIs bool `json:"execute-config-as-is"`
LogLevel string `json:"log-level"`
ResolveDestination bool `json:"resolve-destination"`
IPv6Mode option.DomainStrategy `json:"ipv6-mode"`
RemoteDnsAddress string `json:"remote-dns-address"`
RemoteDnsDomainStrategy option.DomainStrategy `json:"remote-dns-domain-strategy"`
DirectDnsAddress string `json:"direct-dns-address"`
DirectDnsDomainStrategy option.DomainStrategy `json:"direct-dns-domain-strategy"`
MixedPort uint16 `json:"mixed-port"`
LocalDnsPort uint16 `json:"local-dns-port"`
MTU uint32 `json:"mtu"`
ConnectionTestUrl string `json:"connection-test-url"`
URLTestInterval option.Duration `json:"url-test-interval"`
EnableClashApi bool `json:"enable-clash-api"`
ClashApiPort uint16 `json:"clash-api-port"`
EnableTun bool `json:"enable-tun"`
SetSystemProxy bool `json:"set-system-proxy"`
}
func BuildConfig(configOpt ConfigOptions, input option.Options) option.Options {
if configOpt.ExecuteAsIs {
return applyOverrides(configOpt, input)
}
var options option.Options
fmt.Printf("%+v\n", configOpt)
if configOpt.EnableClashApi {
options.Experimental = &option.ExperimentalOptions{
ClashAPI: &option.ClashAPIOptions{
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", configOpt.ClashApiPort),
StoreSelected: true,
},
}
}
options.Log = &option.LogOptions{
Level: configOpt.LogLevel,
Output: "box.log",
Disabled: false,
Timestamp: false,
DisableColor: true,
}
options.DNS = &option.DNSOptions{
DNSClientOptions: option.DNSClientOptions{
IndependentCache: true,
},
Servers: []option.DNSServerOptions{
{
Tag: "dns-remote",
Address: configOpt.RemoteDnsAddress,
AddressResolver: "dns-direct",
Strategy: configOpt.RemoteDnsDomainStrategy,
Detour: "select",
},
{
Tag: "dns-direct",
Address: configOpt.DirectDnsAddress,
AddressResolver: "dns-local",
Strategy: configOpt.DirectDnsDomainStrategy,
Detour: "direct",
},
{
Tag: "dns-local",
Address: "local",
Detour: "direct",
},
{
Tag: "dns-block",
Address: "rcode://success",
},
},
Rules: []option.DNSRule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
Outbound: []string{"any"},
// Server: "dns-direct",
Server: "dns-local",
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
ClashMode: "Direct",
Server: "dns-local",
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
ClashMode: "Global",
Server: "dns-remote",
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultDNSRule{
DomainSuffix: []string{"ir"},
Server: "dns-local",
},
},
},
}
var inboundDomainStrategy option.DomainStrategy
if !configOpt.ResolveDestination {
inboundDomainStrategy = option.DomainStrategy(dns.DomainStrategyAsIS)
} else {
inboundDomainStrategy = configOpt.IPv6Mode
}
if configOpt.EnableTun {
tunInbound := option.Inbound{
Type: C.TypeTun,
Tag: "tun-in",
TunOptions: option.TunInboundOptions{
MTU: configOpt.MTU,
AutoRoute: true,
StrictRoute: true,
EndpointIndependentNat: true,
InboundOptions: option.InboundOptions{
SniffEnabled: true,
SniffOverrideDestination: true,
DomainStrategy: inboundDomainStrategy,
},
},
}
switch configOpt.IPv6Mode {
case option.DomainStrategy(dns.DomainStrategyUseIPv4):
tunInbound.TunOptions.Inet4Address = []option.ListenPrefix{
option.ListenPrefix(netip.MustParsePrefix("172.19.0.1/28")),
}
case option.DomainStrategy(dns.DomainStrategyUseIPv6):
tunInbound.TunOptions.Inet6Address = []option.ListenPrefix{
option.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126")),
}
default:
tunInbound.TunOptions.Inet4Address = []option.ListenPrefix{
option.ListenPrefix(netip.MustParsePrefix("172.19.0.1/28")),
}
tunInbound.TunOptions.Inet6Address = []option.ListenPrefix{
option.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126")),
}
}
options.Inbounds = append(options.Inbounds, tunInbound)
}
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: configOpt.MixedPort,
InboundOptions: option.InboundOptions{
SniffEnabled: true,
SniffOverrideDestination: true,
DomainStrategy: inboundDomainStrategy,
},
},
SetSystemProxy: configOpt.SetSystemProxy,
},
},
)
options.Inbounds = append(options.Inbounds,
option.Inbound{
Type: C.TypeDirect,
Tag: "dns-in",
DirectOptions: option.DirectInboundOptions{
ListenOptions: option.ListenOptions{
Listen: option.NewListenAddress(netip.MustParseAddr("127.0.0.1")),
ListenPort: configOpt.LocalDnsPort,
},
OverrideAddress: "8.8.8.8",
OverridePort: 53,
},
},
)
options.Route = &option.RouteOptions{
Rules: []option.Rule{
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultRule{
Inbound: []string{"dns-in"},
Outbound: "dns-out",
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultRule{
Port: []uint16{53},
Outbound: "dns-out",
},
},
{
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{
Geosite: []string{"category-ads-all"},
Outbound: "block",
},
},
{
Type: C.RuleTypeDefault,
DefaultOptions: option.DefaultRule{
GeoIP: []string{"ir", "private"},
DomainSuffix: []string{"ir"},
Outbound: "direct",
},
},
},
AutoDetectInterface: true,
OverrideAndroidVPN: true,
}
var outbounds []option.Outbound
var tags []string
for _, out := range input.Outbounds {
switch out.Type {
case C.TypeDirect, C.TypeBlock, C.TypeDNS:
continue
case C.TypeSelector, C.TypeURLTest:
continue
default:
tags = append(tags, out.Tag)
outbounds = append(outbounds, out)
}
}
urlTest := option.Outbound{
Type: C.TypeURLTest,
Tag: "auto",
URLTestOptions: option.URLTestOutboundOptions{
Outbounds: tags,
URL: configOpt.ConnectionTestUrl,
Interval: configOpt.URLTestInterval,
},
}
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...)
options.Outbounds = append(
outbounds,
[]option.Outbound{
{
Tag: "dns-out",
Type: C.TypeDNS,
},
{
Tag: "direct",
Type: C.TypeDirect,
},
{
Tag: "block",
Type: C.TypeBlock,
},
}...,
)
return options
}
func applyOverrides(overrides ConfigOptions, options option.Options) option.Options {
if overrides.EnableClashApi {
options.Experimental.ClashAPI = &option.ClashAPIOptions{
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", overrides.ClashApiPort),
StoreSelected: true,
}
}
options.Log = &option.LogOptions{
Level: overrides.LogLevel,
Output: "box.log",
Disabled: false,
}
var inbounds []option.Inbound
for _, inb := range options.Inbounds {
if inb.Type == C.TypeTun && !overrides.EnableTun {
continue
}
inbounds = append(inbounds, inb)
}
options.Inbounds = inbounds
return options
}

View File

@@ -1,102 +0,0 @@
package shared
import (
"fmt"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
type ConfigOverrides struct {
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(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,
},
}
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 base.Inbounds {
switch inb.Type {
case C.TypeTun:
if pointerOrDefaultBool(overrides.EnableTun, true) {
inbounds = append(inbounds, inb)
}
default:
inbounds = append(inbounds, inb)
}
}
base.Inbounds = inbounds
var outbounds []option.Outbound
var tags []string
for _, out := range options.Outbounds {
switch out.Type {
case C.TypeDirect, C.TypeBlock, C.TypeDNS:
continue
case C.TypeSelector, C.TypeURLTest:
continue
default:
tags = append(tags, out.Tag)
}
outbounds = append(outbounds, out)
}
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
}

View File

@@ -1,168 +0,0 @@
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
}

View File

@@ -1,35 +0,0 @@
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
}