819 lines
22 KiB
Go
819 lines
22 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand"
|
|
"net"
|
|
"net/netip"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/option"
|
|
dns "github.com/sagernet/sing-dns"
|
|
)
|
|
|
|
const (
|
|
DNSRemoteTag = "dns-remote"
|
|
DNSLocalTag = "dns-local"
|
|
DNSDirectTag = "dns-direct"
|
|
DNSBlockTag = "dns-block"
|
|
DNSFakeTag = "dns-fake"
|
|
DNSTricksDirectTag = "dns-trick-direct"
|
|
|
|
OutboundDirectTag = "direct"
|
|
OutboundBypassTag = "bypass"
|
|
OutboundBlockTag = "block"
|
|
OutboundSelectTag = "select"
|
|
OutboundURLTestTag = "auto"
|
|
OutboundDNSTag = "dns-out"
|
|
OutboundDirectFragmentTag = "direct-fragment"
|
|
|
|
InboundTUNTag = "tun-in"
|
|
InboundMixedTag = "mixed-in"
|
|
InboundDNSTag = "dns-in"
|
|
)
|
|
|
|
var OutboundMainProxyTag = OutboundSelectTag
|
|
|
|
func BuildConfigJson(configOpt ConfigOptions, input option.Options) (string, error) {
|
|
|
|
options, err := BuildConfig(configOpt, input)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var buffer bytes.Buffer
|
|
json.NewEncoder(&buffer)
|
|
encoder := json.NewEncoder(&buffer)
|
|
encoder.SetIndent("", " ")
|
|
err = encoder.Encode(options)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return buffer.String(), nil
|
|
}
|
|
|
|
// TODO include selectors
|
|
func BuildConfig(opt ConfigOptions, input option.Options) (*option.Options, error) {
|
|
fmt.Printf("config options: %+v\n", opt)
|
|
|
|
var options option.Options
|
|
if opt.EnableFullConfig {
|
|
options.Inbounds = input.Inbounds
|
|
options.DNS = input.DNS
|
|
options.Route = input.Route
|
|
}
|
|
directDNSDomains := make(map[string]bool)
|
|
|
|
var bind string
|
|
if opt.AllowConnectionFromLAN {
|
|
bind = "0.0.0.0"
|
|
} else {
|
|
bind = "127.0.0.1"
|
|
}
|
|
|
|
if opt.EnableClashApi {
|
|
if opt.ClashApiSecret == "" {
|
|
opt.ClashApiSecret = generateRandomString(16)
|
|
}
|
|
options.Experimental = &option.ExperimentalOptions{
|
|
ClashAPI: &option.ClashAPIOptions{
|
|
ExternalController: fmt.Sprintf("%s:%d", "127.0.0.1", opt.ClashApiPort),
|
|
Secret: opt.ClashApiSecret,
|
|
},
|
|
|
|
CacheFile: &option.CacheFileOptions{
|
|
Enabled: true,
|
|
Path: "clash.db",
|
|
},
|
|
}
|
|
}
|
|
|
|
options.Log = &option.LogOptions{
|
|
Level: opt.LogLevel,
|
|
Output: "box.log",
|
|
Disabled: false,
|
|
Timestamp: true,
|
|
DisableColor: true,
|
|
}
|
|
|
|
options.DNS = &option.DNSOptions{
|
|
StaticIPs: map[string][]string{},
|
|
DNSClientOptions: option.DNSClientOptions{
|
|
IndependentCache: opt.IndependentDNSCache,
|
|
},
|
|
Final: DNSRemoteTag,
|
|
Servers: []option.DNSServerOptions{
|
|
{
|
|
Tag: DNSRemoteTag,
|
|
Address: opt.RemoteDnsAddress,
|
|
AddressResolver: DNSDirectTag,
|
|
Strategy: opt.RemoteDnsDomainStrategy,
|
|
},
|
|
{
|
|
Tag: DNSTricksDirectTag,
|
|
Address: "https://sky.rethinkdns.com/",
|
|
// AddressResolver: "dns-local",
|
|
Strategy: opt.DirectDnsDomainStrategy,
|
|
Detour: OutboundDirectFragmentTag,
|
|
},
|
|
{
|
|
Tag: DNSDirectTag,
|
|
Address: opt.DirectDnsAddress,
|
|
AddressResolver: DNSLocalTag,
|
|
Strategy: opt.DirectDnsDomainStrategy,
|
|
Detour: OutboundDirectTag,
|
|
},
|
|
{
|
|
Tag: DNSLocalTag,
|
|
Address: "local",
|
|
Detour: OutboundDirectTag,
|
|
},
|
|
{
|
|
Tag: DNSBlockTag,
|
|
Address: "rcode://success",
|
|
},
|
|
},
|
|
}
|
|
sky_rethinkdns := getIPs([]string{"www.speedtest.net", "sky.rethinkdns.com"})
|
|
if len(sky_rethinkdns) > 0 {
|
|
options.DNS.StaticIPs["sky.rethinkdns.com"] = sky_rethinkdns
|
|
}
|
|
var inboundDomainStrategy option.DomainStrategy
|
|
if !opt.ResolveDestination {
|
|
inboundDomainStrategy = option.DomainStrategy(dns.DomainStrategyAsIS)
|
|
} else {
|
|
inboundDomainStrategy = opt.IPv6Mode
|
|
}
|
|
if opt.EnableTunService {
|
|
ActivateTunnelService(opt)
|
|
} else if opt.EnableTun {
|
|
tunInbound := option.Inbound{
|
|
Type: C.TypeTun,
|
|
Tag: InboundTUNTag,
|
|
TunOptions: option.TunInboundOptions{
|
|
Stack: opt.TUNStack,
|
|
MTU: opt.MTU,
|
|
AutoRoute: true,
|
|
StrictRoute: opt.StrictRoute,
|
|
EndpointIndependentNat: true,
|
|
// GSO: runtime.GOOS != "windows",
|
|
InboundOptions: option.InboundOptions{
|
|
SniffEnabled: true,
|
|
SniffOverrideDestination: true,
|
|
DomainStrategy: inboundDomainStrategy,
|
|
},
|
|
},
|
|
}
|
|
switch opt.IPv6Mode {
|
|
case option.DomainStrategy(dns.DomainStrategyUseIPv4):
|
|
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
|
netip.MustParsePrefix("172.19.0.1/28"),
|
|
}
|
|
case option.DomainStrategy(dns.DomainStrategyUseIPv6):
|
|
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
|
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
|
}
|
|
default:
|
|
tunInbound.TunOptions.Inet4Address = []netip.Prefix{
|
|
netip.MustParsePrefix("172.19.0.1/28"),
|
|
}
|
|
tunInbound.TunOptions.Inet6Address = []netip.Prefix{
|
|
netip.MustParsePrefix("fdfe:dcba:9876::1/126"),
|
|
}
|
|
}
|
|
options.Inbounds = append(options.Inbounds, tunInbound)
|
|
|
|
}
|
|
|
|
options.Inbounds = append(
|
|
options.Inbounds,
|
|
option.Inbound{
|
|
Type: C.TypeMixed,
|
|
Tag: InboundMixedTag,
|
|
MixedOptions: option.HTTPMixedInboundOptions{
|
|
ListenOptions: option.ListenOptions{
|
|
Listen: option.NewListenAddress(netip.MustParseAddr(bind)),
|
|
ListenPort: opt.MixedPort,
|
|
InboundOptions: option.InboundOptions{
|
|
SniffEnabled: true,
|
|
SniffOverrideDestination: true,
|
|
DomainStrategy: inboundDomainStrategy,
|
|
},
|
|
},
|
|
SetSystemProxy: opt.SetSystemProxy,
|
|
},
|
|
},
|
|
)
|
|
|
|
options.Inbounds = append(
|
|
options.Inbounds,
|
|
option.Inbound{
|
|
Type: C.TypeDirect,
|
|
Tag: InboundDNSTag,
|
|
DirectOptions: option.DirectInboundOptions{
|
|
ListenOptions: option.ListenOptions{
|
|
Listen: option.NewListenAddress(netip.MustParseAddr(bind)),
|
|
ListenPort: opt.LocalDnsPort,
|
|
},
|
|
// OverrideAddress: "1.1.1.1",
|
|
// OverridePort: 53,
|
|
},
|
|
},
|
|
)
|
|
|
|
remoteDNSAddress := opt.RemoteDnsAddress
|
|
if strings.Contains(remoteDNSAddress, "://") {
|
|
remoteDNSAddress = strings.SplitAfter(remoteDNSAddress, "://")[1]
|
|
}
|
|
parsedUrl, err := url.Parse(fmt.Sprintf("https://%s", remoteDNSAddress))
|
|
if err == nil && net.ParseIP(parsedUrl.Host) == nil {
|
|
directDNSDomains["full:"+parsedUrl.Host] = true
|
|
//TODO: IS it really needed
|
|
}
|
|
setRoutingOptions(&options, &opt)
|
|
setFakeDns(&options, &opt)
|
|
var outbounds []option.Outbound
|
|
var tags []string
|
|
OutboundMainProxyTag = OutboundSelectTag
|
|
//inbound==warp over proxies
|
|
//outbound==proxies over warp
|
|
if opt.Warp.EnableWarp {
|
|
for _, out := range input.Outbounds {
|
|
if out.Type == C.TypeCustom {
|
|
if warp, ok := out.CustomOptions["warp"].(map[string]interface{}); ok {
|
|
key, _ := warp["key"].(string)
|
|
if key == "p1" {
|
|
opt.Warp.EnableWarp = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if out.Type == C.TypeWireGuard && (out.WireGuardOptions.PrivateKey == opt.Warp.WireguardConfig.PrivateKey || out.WireGuardOptions.PrivateKey == "p1") {
|
|
opt.Warp.EnableWarp = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if opt.Warp.EnableWarp && (opt.Warp.Mode == "warp_over_proxy" || opt.Warp.Mode == "proxy_over_warp") {
|
|
out, err := GenerateWarpSingbox(opt.Warp.WireguardConfig, opt.Warp.CleanIP, opt.Warp.CleanPort, opt.Warp.FakePackets, opt.Warp.FakePacketSize, opt.Warp.FakePacketDelay, opt.Warp.FakePacketMode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate warp config: %v", err)
|
|
}
|
|
out.Tag = "Hiddify Warp ✅"
|
|
if opt.Warp.Mode == "warp_over_proxy" {
|
|
out.WireGuardOptions.Detour = OutboundSelectTag
|
|
OutboundMainProxyTag = out.Tag
|
|
} else {
|
|
out.WireGuardOptions.Detour = OutboundDirectTag
|
|
}
|
|
patchWarp(out, &opt, true, nil)
|
|
outbounds = append(outbounds, *out)
|
|
// tags = append(tags, out.Tag)
|
|
}
|
|
for _, out := range input.Outbounds {
|
|
outbound, serverDomain, err := patchOutbound(out, opt, options.DNS.StaticIPs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if serverDomain != "" {
|
|
directDNSDomains[serverDomain] = true
|
|
}
|
|
out = *outbound
|
|
|
|
switch out.Type {
|
|
case C.TypeDirect, C.TypeBlock, C.TypeDNS:
|
|
continue
|
|
case C.TypeSelector, C.TypeURLTest:
|
|
continue
|
|
case C.TypeCustom:
|
|
continue
|
|
default:
|
|
if !strings.Contains(out.Tag, "§hide§") {
|
|
tags = append(tags, out.Tag)
|
|
}
|
|
out = patchHiddifyWarpFromConfig(out, opt)
|
|
outbounds = append(outbounds, out)
|
|
}
|
|
}
|
|
|
|
urlTest := option.Outbound{
|
|
Type: C.TypeURLTest,
|
|
Tag: OutboundURLTestTag,
|
|
URLTestOptions: option.URLTestOutboundOptions{
|
|
Outbounds: tags,
|
|
URL: opt.ConnectionTestUrl,
|
|
Interval: option.Duration(opt.URLTestInterval.Duration()),
|
|
// IdleTimeout: option.Duration(opt.URLTestIdleTimeout.Duration()),
|
|
Tolerance: 1,
|
|
IdleTimeout: option.Duration(opt.URLTestInterval.Duration().Nanoseconds() * 3),
|
|
},
|
|
}
|
|
defaultSelect := urlTest.Tag
|
|
|
|
for _, tag := range tags {
|
|
if strings.Contains(tag, "§default§") {
|
|
defaultSelect = "§default§"
|
|
}
|
|
}
|
|
selector := option.Outbound{
|
|
Type: C.TypeSelector,
|
|
Tag: OutboundSelectTag,
|
|
SelectorOptions: option.SelectorOutboundOptions{
|
|
Outbounds: append([]string{urlTest.Tag}, tags...),
|
|
Default: defaultSelect,
|
|
},
|
|
}
|
|
|
|
outbounds = append([]option.Outbound{selector, urlTest}, outbounds...)
|
|
|
|
options.Outbounds = append(
|
|
outbounds,
|
|
[]option.Outbound{
|
|
{
|
|
Tag: OutboundDNSTag,
|
|
Type: C.TypeDNS,
|
|
},
|
|
{
|
|
Tag: OutboundDirectTag,
|
|
Type: C.TypeDirect,
|
|
},
|
|
{
|
|
Tag: OutboundDirectFragmentTag,
|
|
Type: C.TypeDirect,
|
|
DirectOptions: option.DirectOutboundOptions{
|
|
DialerOptions: option.DialerOptions{
|
|
TCPFastOpen: false,
|
|
TLSFragment: &option.TLSFragmentOptions{
|
|
Enabled: true,
|
|
Size: opt.TLSTricks.FragmentSize,
|
|
Sleep: opt.TLSTricks.FragmentSleep,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Tag: OutboundBypassTag,
|
|
Type: C.TypeDirect,
|
|
},
|
|
{
|
|
Tag: OutboundBlockTag,
|
|
Type: C.TypeBlock,
|
|
},
|
|
}...,
|
|
)
|
|
if len(directDNSDomains) > 0 {
|
|
// trickDnsDomains := []string{}
|
|
// directDNSDomains = removeDuplicateStr(directDNSDomains)
|
|
// b, _ := batch.New(context.Background(), batch.WithConcurrencyNum[bool](10))
|
|
// for _, d := range directDNSDomains {
|
|
// b.Go(d, func() (bool, error) {
|
|
// return isBlockedDomain(d), nil
|
|
// })
|
|
// }
|
|
// b.Wait()
|
|
// for domain, isBlock := range b.Result() {
|
|
// if isBlock.Value {
|
|
// trickDnsDomains = append(trickDnsDomains, domain)
|
|
// }
|
|
// }
|
|
|
|
// trickDomains := strings.Join(trickDnsDomains, ",")
|
|
// trickRule := Rule{Domains: trickDomains, Outbound: OutboundBypassTag}
|
|
// trickDnsRule := trickRule.MakeDNSRule()
|
|
// trickDnsRule.Server = DNSTricksDirectTag
|
|
// options.DNS.Rules = append([]option.DNSRule{{Type: C.RuleTypeDefault, DefaultOptions: trickDnsRule}}, options.DNS.Rules...)
|
|
|
|
directDNSDomainskeys := make([]string, 0, len(directDNSDomains))
|
|
for key := range directDNSDomains {
|
|
directDNSDomainskeys = append(directDNSDomainskeys, key)
|
|
}
|
|
|
|
domains := strings.Join(directDNSDomainskeys, ",")
|
|
directRule := Rule{Domains: domains, Outbound: OutboundBypassTag}
|
|
dnsRule := directRule.MakeDNSRule()
|
|
dnsRule.Server = DNSDirectTag
|
|
options.DNS.Rules = append([]option.DNSRule{{Type: C.RuleTypeDefault, DefaultOptions: dnsRule}}, options.DNS.Rules...)
|
|
}
|
|
options.Route.Final = OutboundMainProxyTag
|
|
return &options, nil
|
|
}
|
|
|
|
func setFakeDns(options *option.Options, opt *ConfigOptions) {
|
|
if opt.EnableFakeDNS {
|
|
inet4Range := netip.MustParsePrefix("198.18.0.0/15")
|
|
inet6Range := netip.MustParsePrefix("fc00::/18")
|
|
options.DNS.FakeIP = &option.DNSFakeIPOptions{
|
|
Enabled: true,
|
|
Inet4Range: &inet4Range,
|
|
Inet6Range: &inet6Range,
|
|
}
|
|
options.DNS.Servers = append(
|
|
options.DNS.Servers,
|
|
option.DNSServerOptions{
|
|
Tag: DNSFakeTag,
|
|
Address: "fakeip",
|
|
Strategy: option.DomainStrategy(dns.DomainStrategyUseIPv4),
|
|
},
|
|
)
|
|
options.DNS.Rules = append(
|
|
options.DNS.Rules,
|
|
option.DNSRule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultDNSRule{
|
|
Inbound: []string{InboundTUNTag},
|
|
Server: DNSFakeTag,
|
|
DisableCache: true,
|
|
},
|
|
},
|
|
)
|
|
|
|
}
|
|
}
|
|
|
|
func setRoutingOptions(options *option.Options, opt *ConfigOptions) {
|
|
dnsRules := []option.DefaultDNSRule{}
|
|
routeRules := []option.Rule{}
|
|
rulesets := []option.RuleSet{}
|
|
|
|
routeRules = append(routeRules, option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
Inbound: []string{InboundDNSTag},
|
|
Outbound: OutboundDNSTag,
|
|
},
|
|
})
|
|
routeRules = append(routeRules, option.Rule{
|
|
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
Port: []uint16{53},
|
|
Outbound: OutboundDNSTag,
|
|
},
|
|
})
|
|
if opt.EnableTun && false {
|
|
routeRules = append(
|
|
routeRules,
|
|
option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
ProcessName: []string{"Hiddify", "Hiddify.exe", "HiddifyCli", "HiddifyCli.exe"},
|
|
Outbound: OutboundBypassTag,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
// {
|
|
// Type: C.RuleTypeDefault,
|
|
// DefaultOptions: option.DefaultRule{
|
|
// ClashMode: "Direct",
|
|
// Outbound: OutboundDirectTag,
|
|
// },
|
|
// },
|
|
// {
|
|
// Type: C.RuleTypeDefault,
|
|
// DefaultOptions: option.DefaultRule{
|
|
// ClashMode: "Global",
|
|
// Outbound: OutboundMainProxyTag,
|
|
// },
|
|
// }, }
|
|
|
|
if opt.BypassLAN {
|
|
routeRules = append(
|
|
routeRules,
|
|
option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
// GeoIP: []string{"private"},
|
|
IPIsPrivate: true,
|
|
Outbound: OutboundBypassTag,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
|
|
for _, rule := range opt.Rules {
|
|
routeRule := rule.MakeRule()
|
|
switch rule.Outbound {
|
|
case "bypass":
|
|
routeRule.Outbound = OutboundBypassTag
|
|
case "block":
|
|
routeRule.Outbound = OutboundBlockTag
|
|
case "proxy":
|
|
routeRule.Outbound = OutboundDNSTag
|
|
}
|
|
|
|
if routeRule.IsValid() {
|
|
routeRules = append(
|
|
routeRules,
|
|
option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: routeRule,
|
|
},
|
|
)
|
|
}
|
|
|
|
dnsRule := rule.MakeDNSRule()
|
|
switch rule.Outbound {
|
|
case "bypass":
|
|
dnsRule.Server = DNSDirectTag
|
|
case "block":
|
|
dnsRule.Server = DNSBlockTag
|
|
dnsRule.DisableCache = true
|
|
case "proxy":
|
|
if opt.EnableFakeDNS {
|
|
fakeDnsRule := dnsRule
|
|
fakeDnsRule.Server = DNSFakeTag
|
|
fakeDnsRule.Inbound = []string{InboundTUNTag, InboundMixedTag}
|
|
dnsRules = append(dnsRules, fakeDnsRule)
|
|
}
|
|
dnsRule.Server = DNSRemoteTag
|
|
}
|
|
dnsRules = append(dnsRules, dnsRule)
|
|
}
|
|
|
|
var dnsCPttl uint32 = 3000
|
|
parsedURL, err := url.Parse(opt.ConnectionTestUrl)
|
|
if err == nil {
|
|
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
|
Domain: []string{parsedURL.Host},
|
|
Server: DNSRemoteTag,
|
|
RewriteTTL: &dnsCPttl,
|
|
DisableCache: false,
|
|
})
|
|
}
|
|
|
|
if opt.BlockAds {
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geosite-ads",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-category-ads-all.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geosite-malware",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-malware.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geosite-phishing",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-phishing.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geosite-cryptominers",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geosite-cryptominers.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geoip-phishing",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geoip-phishing.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geoip-malware",
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/block/geoip-malware.srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
|
|
routeRules = append(routeRules, option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
RuleSet: []string{
|
|
"geosite-ads",
|
|
"geosite-malware",
|
|
"geosite-phishing",
|
|
"geosite-cryptominers",
|
|
"geoip-malware",
|
|
"geoip-phishing",
|
|
},
|
|
Outbound: OutboundBlockTag,
|
|
},
|
|
})
|
|
|
|
}
|
|
if opt.Region != "other" {
|
|
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
|
RuleSet: []string{
|
|
"geoip-" + opt.Region,
|
|
"geosite-" + opt.Region,
|
|
},
|
|
Server: DNSDirectTag,
|
|
})
|
|
dnsRules = append(dnsRules, option.DefaultDNSRule{
|
|
DomainSuffix: []string{"." + opt.Region},
|
|
Server: DNSDirectTag,
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geoip-" + opt.Region,
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geoip-" + opt.Region + ".srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
rulesets = append(rulesets, option.RuleSet{
|
|
Type: C.RuleSetTypeRemote,
|
|
Tag: "geosite-" + opt.Region,
|
|
Format: C.RuleSetFormatBinary,
|
|
RemoteOptions: option.RemoteRuleSet{
|
|
URL: "https://raw.githubusercontent.com/hiddify/hiddify-geo/rule-set/country/geosite-" + opt.Region + ".srs",
|
|
UpdateInterval: option.Duration(5 * time.Hour * 24),
|
|
},
|
|
})
|
|
|
|
routeRules = append(routeRules, option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
RuleSet: []string{
|
|
"geoip-" + opt.Region,
|
|
"geosite-" + opt.Region,
|
|
},
|
|
Outbound: OutboundDirectTag,
|
|
},
|
|
})
|
|
|
|
routeRules = append(routeRules, option.Rule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: option.DefaultRule{
|
|
DomainSuffix: []string{"." + opt.Region},
|
|
Outbound: OutboundDirectTag,
|
|
},
|
|
})
|
|
|
|
}
|
|
options.Route = &option.RouteOptions{
|
|
Rules: routeRules,
|
|
Final: OutboundMainProxyTag,
|
|
AutoDetectInterface: true,
|
|
OverrideAndroidVPN: true,
|
|
RuleSet: rulesets,
|
|
// GeoIP: &option.GeoIPOptions{
|
|
// Path: opt.GeoIPPath,
|
|
// },
|
|
// Geosite: &option.GeositeOptions{
|
|
// Path: opt.GeoSitePath,
|
|
// },
|
|
}
|
|
if opt.EnableDNSRouting {
|
|
for _, dnsRule := range dnsRules {
|
|
if dnsRule.IsValid() {
|
|
options.DNS.Rules = append(
|
|
options.DNS.Rules,
|
|
option.DNSRule{
|
|
Type: C.RuleTypeDefault,
|
|
DefaultOptions: dnsRule,
|
|
},
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func patchHiddifyWarpFromConfig(out option.Outbound, opt ConfigOptions) option.Outbound {
|
|
if opt.Warp.EnableWarp && opt.Warp.Mode == "proxy_over_warp" {
|
|
if out.DirectOptions.Detour == "" {
|
|
out.DirectOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.HTTPOptions.Detour == "" {
|
|
out.HTTPOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.Hysteria2Options.Detour == "" {
|
|
out.Hysteria2Options.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.HysteriaOptions.Detour == "" {
|
|
out.HysteriaOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.SSHOptions.Detour == "" {
|
|
out.SSHOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.ShadowTLSOptions.Detour == "" {
|
|
out.ShadowTLSOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.ShadowsocksOptions.Detour == "" {
|
|
out.ShadowsocksOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.ShadowsocksROptions.Detour == "" {
|
|
out.ShadowsocksROptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.SocksOptions.Detour == "" {
|
|
out.SocksOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.TUICOptions.Detour == "" {
|
|
out.TUICOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.TorOptions.Detour == "" {
|
|
out.TorOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.TrojanOptions.Detour == "" {
|
|
out.TrojanOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.VLESSOptions.Detour == "" {
|
|
out.VLESSOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.VMessOptions.Detour == "" {
|
|
out.VMessOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
if out.WireGuardOptions.Detour == "" {
|
|
out.WireGuardOptions.Detour = "Hiddify Warp ✅"
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func getIPs(domains []string) []string {
|
|
res := []string{}
|
|
for _, d := range domains {
|
|
ips, err := net.LookupHost(d)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, ip := range ips {
|
|
if !strings.HasPrefix(ip, "10.") {
|
|
res = append(res, ip)
|
|
}
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func isBlockedDomain(domain string) bool {
|
|
if strings.HasPrefix("full:", domain) {
|
|
return false
|
|
}
|
|
ips, err := net.LookupHost(domain)
|
|
if err != nil {
|
|
// fmt.Println(err)
|
|
return true
|
|
}
|
|
|
|
// Print the IP addresses associated with the domain
|
|
fmt.Printf("IP addresses for %s:\n", domain)
|
|
for _, ip := range ips {
|
|
if strings.HasPrefix(ip, "10.") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func removeDuplicateStr(strSlice []string) []string {
|
|
allKeys := make(map[string]bool)
|
|
list := []string{}
|
|
for _, item := range strSlice {
|
|
if _, value := allKeys[item]; !value {
|
|
allKeys[item] = true
|
|
list = append(list, item)
|
|
}
|
|
}
|
|
return list
|
|
}
|
|
|
|
func generateRandomString(length int) string {
|
|
// Determine the number of bytes needed
|
|
bytesNeeded := (length*6 + 7) / 8
|
|
|
|
// Generate random bytes
|
|
randomBytes := make([]byte, bytesNeeded)
|
|
_, err := rand.Read(randomBytes)
|
|
if err != nil {
|
|
return "hiddify"
|
|
}
|
|
|
|
// Encode random bytes to base64
|
|
randomString := base64.URLEncoding.EncodeToString(randomBytes)
|
|
|
|
// Trim padding characters and return the string
|
|
return randomString[:length]
|
|
}
|