336 lines
8.6 KiB
Go
336 lines
8.6 KiB
Go
|
|
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
|
||
|
|
}
|