12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622 |
- package config
- import (
- "container/list"
- "errors"
- "fmt"
- "net"
- "net/netip"
- "net/url"
- "os"
- "path"
- "regexp"
- "strings"
- "time"
- "github.com/metacubex/mihomo/adapter"
- "github.com/metacubex/mihomo/adapter/outbound"
- "github.com/metacubex/mihomo/adapter/outboundgroup"
- "github.com/metacubex/mihomo/adapter/provider"
- N "github.com/metacubex/mihomo/common/net"
- "github.com/metacubex/mihomo/common/utils"
- "github.com/metacubex/mihomo/component/auth"
- "github.com/metacubex/mihomo/component/fakeip"
- "github.com/metacubex/mihomo/component/geodata"
- "github.com/metacubex/mihomo/component/geodata/router"
- P "github.com/metacubex/mihomo/component/process"
- "github.com/metacubex/mihomo/component/resolver"
- SNIFF "github.com/metacubex/mihomo/component/sniffer"
- tlsC "github.com/metacubex/mihomo/component/tls"
- "github.com/metacubex/mihomo/component/trie"
- "github.com/metacubex/mihomo/component/updater"
- C "github.com/metacubex/mihomo/constant"
- providerTypes "github.com/metacubex/mihomo/constant/provider"
- snifferTypes "github.com/metacubex/mihomo/constant/sniffer"
- "github.com/metacubex/mihomo/dns"
- L "github.com/metacubex/mihomo/listener"
- LC "github.com/metacubex/mihomo/listener/config"
- "github.com/metacubex/mihomo/log"
- R "github.com/metacubex/mihomo/rules"
- RP "github.com/metacubex/mihomo/rules/provider"
- T "github.com/metacubex/mihomo/tunnel"
- orderedmap "github.com/wk8/go-ordered-map/v2"
- "golang.org/x/exp/slices"
- "gopkg.in/yaml.v3"
- )
- // General config
- type General struct {
- Inbound
- Controller
- Mode T.TunnelMode `json:"mode"`
- UnifiedDelay bool
- LogLevel log.LogLevel `json:"log-level"`
- IPv6 bool `json:"ipv6"`
- Interface string `json:"interface-name"`
- RoutingMark int `json:"-"`
- GeoXUrl GeoXUrl `json:"geox-url"`
- GeoAutoUpdate bool `json:"geo-auto-update"`
- GeoUpdateInterval int `json:"geo-update-interval"`
- GeodataMode bool `json:"geodata-mode"`
- GeodataLoader string `json:"geodata-loader"`
- GeositeMatcher string `json:"geosite-matcher"`
- TCPConcurrent bool `json:"tcp-concurrent"`
- FindProcessMode P.FindProcessMode `json:"find-process-mode"`
- Sniffing bool `json:"sniffing"`
- EBpf EBpf `json:"-"`
- GlobalClientFingerprint string `json:"global-client-fingerprint"`
- GlobalUA string `json:"global-ua"`
- }
- // Inbound config
- type Inbound struct {
- Port int `json:"port"`
- SocksPort int `json:"socks-port"`
- RedirPort int `json:"redir-port"`
- TProxyPort int `json:"tproxy-port"`
- MixedPort int `json:"mixed-port"`
- Tun LC.Tun `json:"tun"`
- TuicServer LC.TuicServer `json:"tuic-server"`
- ShadowSocksConfig string `json:"ss-config"`
- VmessConfig string `json:"vmess-config"`
- Authentication []string `json:"authentication"`
- SkipAuthPrefixes []netip.Prefix `json:"skip-auth-prefixes"`
- LanAllowedIPs []netip.Prefix `json:"lan-allowed-ips"`
- LanDisAllowedIPs []netip.Prefix `json:"lan-disallowed-ips"`
- AllowLan bool `json:"allow-lan"`
- BindAddress string `json:"bind-address"`
- InboundTfo bool `json:"inbound-tfo"`
- InboundMPTCP bool `json:"inbound-mptcp"`
- }
- // Controller config
- type Controller struct {
- ExternalController string `json:"-"`
- ExternalControllerTLS string `json:"-"`
- ExternalControllerUnix string `json:"-"`
- ExternalUI string `json:"-"`
- ExternalDohServer string `json:"-"`
- Secret string `json:"-"`
- }
- // NTP config
- type NTP struct {
- Enable bool `yaml:"enable"`
- Server string `yaml:"server"`
- Port int `yaml:"port"`
- Interval int `yaml:"interval"`
- DialerProxy string `yaml:"dialer-proxy"`
- WriteToSystem bool `yaml:"write-to-system"`
- }
- // DNS config
- type DNS struct {
- Enable bool `yaml:"enable"`
- PreferH3 bool `yaml:"prefer-h3"`
- IPv6 bool `yaml:"ipv6"`
- IPv6Timeout uint `yaml:"ipv6-timeout"`
- UseSystemHosts bool `yaml:"use-system-hosts"`
- NameServer []dns.NameServer `yaml:"nameserver"`
- Fallback []dns.NameServer `yaml:"fallback"`
- FallbackFilter FallbackFilter `yaml:"fallback-filter"`
- Listen string `yaml:"listen"`
- EnhancedMode C.DNSMode `yaml:"enhanced-mode"`
- DefaultNameserver []dns.NameServer `yaml:"default-nameserver"`
- CacheAlgorithm string `yaml:"cache-algorithm"`
- FakeIPRange *fakeip.Pool
- Hosts *trie.DomainTrie[resolver.HostValue]
- NameServerPolicy *orderedmap.OrderedMap[string, []dns.NameServer]
- ProxyServerNameserver []dns.NameServer
- }
- // FallbackFilter config
- type FallbackFilter struct {
- GeoIP bool `yaml:"geoip"`
- GeoIPCode string `yaml:"geoip-code"`
- IPCIDR []netip.Prefix `yaml:"ipcidr"`
- Domain []string `yaml:"domain"`
- GeoSite []router.DomainMatcher `yaml:"geosite"`
- }
- // Profile config
- type Profile struct {
- StoreSelected bool `yaml:"store-selected"`
- StoreFakeIP bool `yaml:"store-fake-ip"`
- }
- type TLS struct {
- Certificate string `yaml:"certificate"`
- PrivateKey string `yaml:"private-key"`
- CustomTrustCert []string `yaml:"custom-certifactes"`
- }
- // IPTables config
- type IPTables struct {
- Enable bool `yaml:"enable" json:"enable"`
- InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"`
- Bypass []string `yaml:"bypass" json:"bypass"`
- DnsRedirect bool `yaml:"dns-redirect" json:"dns-redirect"`
- }
- type Sniffer struct {
- Enable bool
- Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
- ForceDomain *trie.DomainSet
- SkipDomain *trie.DomainSet
- ForceDnsMapping bool
- ParsePureIp bool
- }
- // Experimental config
- type Experimental struct {
- Fingerprints []string `yaml:"fingerprints"`
- QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"`
- QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"`
- IP4PEnable bool `yaml:"dialer-ip4p-convert"`
- }
- // Config is mihomo config manager
- type Config struct {
- General *General
- IPTables *IPTables
- NTP *NTP
- DNS *DNS
- Experimental *Experimental
- Hosts *trie.DomainTrie[resolver.HostValue]
- Profile *Profile
- Rules []C.Rule
- SubRules map[string][]C.Rule
- Users []auth.AuthUser
- Proxies map[string]C.Proxy
- Listeners map[string]C.InboundListener
- Providers map[string]providerTypes.ProxyProvider
- RuleProviders map[string]providerTypes.RuleProvider
- Tunnels []LC.Tunnel
- Sniffer *Sniffer
- TLS *TLS
- }
- type RawNTP struct {
- Enable bool `yaml:"enable"`
- Server string `yaml:"server"`
- ServerPort int `yaml:"server-port"`
- Interval int `yaml:"interval"`
- DialerProxy string `yaml:"dialer-proxy"`
- WriteToSystem bool `yaml:"write-to-system"`
- }
- type RawDNS struct {
- Enable bool `yaml:"enable" json:"enable"`
- PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"`
- IPv6 bool `yaml:"ipv6" json:"ipv6"`
- IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
- UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
- UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"`
- RespectRules bool `yaml:"respect-rules" json:"respect-rules"`
- NameServer []string `yaml:"nameserver" json:"nameserver"`
- Fallback []string `yaml:"fallback" json:"fallback"`
- FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
- Listen string `yaml:"listen" json:"listen"`
- EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"`
- FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"`
- FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"`
- DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"`
- CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"`
- NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"`
- ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"`
- }
- type RawFallbackFilter struct {
- GeoIP bool `yaml:"geoip" json:"geoip"`
- GeoIPCode string `yaml:"geoip-code" json:"geoip-code"`
- IPCIDR []string `yaml:"ipcidr" json:"ipcidr"`
- Domain []string `yaml:"domain" json:"domain"`
- GeoSite []string `yaml:"geosite" json:"geosite"`
- }
- type RawTun struct {
- Enable bool `yaml:"enable" json:"enable"`
- Device string `yaml:"device" json:"device"`
- Stack C.TUNStack `yaml:"stack" json:"stack"`
- DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
- AutoRoute bool `yaml:"auto-route" json:"auto-route"`
- AutoDetectInterface bool `yaml:"auto-detect-interface"`
- MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
- GSO bool `yaml:"gso" json:"gso,omitempty"`
- GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"`
- //Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
- Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
- IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"`
- IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"`
- AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"`
- AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"`
- AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"`
- StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
- RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"`
- RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"`
- RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"`
- RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"`
- IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"`
- ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"`
- IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
- IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
- ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
- ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
- IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
- IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
- ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
- EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
- UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
- FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"`
- Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"`
- Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"`
- Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"`
- Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"`
- }
- type RawTuicServer struct {
- Enable bool `yaml:"enable" json:"enable"`
- Listen string `yaml:"listen" json:"listen"`
- Token []string `yaml:"token" json:"token"`
- Users map[string]string `yaml:"users" json:"users,omitempty"`
- Certificate string `yaml:"certificate" json:"certificate"`
- PrivateKey string `yaml:"private-key" json:"private-key"`
- CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"`
- MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"`
- AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"`
- ALPN []string `yaml:"alpn" json:"alpn,omitempty"`
- MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"`
- CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
- }
- type RawConfig struct {
- Port int `yaml:"port" json:"port"`
- SocksPort int `yaml:"socks-port" json:"socks-port"`
- RedirPort int `yaml:"redir-port" json:"redir-port"`
- TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"`
- MixedPort int `yaml:"mixed-port" json:"mixed-port"`
- ShadowSocksConfig string `yaml:"ss-config"`
- VmessConfig string `yaml:"vmess-config"`
- InboundTfo bool `yaml:"inbound-tfo"`
- InboundMPTCP bool `yaml:"inbound-mptcp"`
- Authentication []string `yaml:"authentication" json:"authentication"`
- SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"`
- LanAllowedIPs []netip.Prefix `yaml:"lan-allowed-ips"`
- LanDisAllowedIPs []netip.Prefix `yaml:"lan-disallowed-ips"`
- AllowLan bool `yaml:"allow-lan" json:"allow-lan"`
- BindAddress string `yaml:"bind-address" json:"bind-address"`
- Mode T.TunnelMode `yaml:"mode" json:"mode"`
- UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"`
- LogLevel log.LogLevel `yaml:"log-level" json:"log-level"`
- IPv6 bool `yaml:"ipv6" json:"ipv6"`
- ExternalController string `yaml:"external-controller" json:"external-controller"`
- ExternalControllerUnix string `yaml:"external-controller-unix"`
- ExternalControllerTLS string `yaml:"external-controller-tls"`
- ExternalUI string `yaml:"external-ui"`
- ExternalUIURL string `yaml:"external-ui-url" json:"external-ui-url"`
- ExternalUIName string `yaml:"external-ui-name" json:"external-ui-name"`
- ExternalDohServer string `yaml:"external-doh-server"`
- Secret string `yaml:"secret"`
- Interface string `yaml:"interface-name"`
- RoutingMark int `yaml:"routing-mark"`
- Tunnels []LC.Tunnel `yaml:"tunnels"`
- GeoAutoUpdate bool `yaml:"geo-auto-update" json:"geo-auto-update"`
- GeoUpdateInterval int `yaml:"geo-update-interval" json:"geo-update-interval"`
- GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"`
- GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"`
- GeositeMatcher string `yaml:"geosite-matcher" json:"geosite-matcher"`
- TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"`
- FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"`
- GlobalClientFingerprint string `yaml:"global-client-fingerprint"`
- GlobalUA string `yaml:"global-ua" json:"global-ua"`
- KeepAliveInterval int `yaml:"keep-alive-interval" json:"keep-alive-interval"`
- Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"`
- ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
- RuleProvider map[string]map[string]any `yaml:"rule-providers"`
- Hosts map[string]any `yaml:"hosts" json:"hosts"`
- NTP RawNTP `yaml:"ntp" json:"ntp"`
- DNS RawDNS `yaml:"dns" json:"dns"`
- Tun RawTun `yaml:"tun"`
- TuicServer RawTuicServer `yaml:"tuic-server"`
- EBpf EBpf `yaml:"ebpf"`
- IPTables IPTables `yaml:"iptables"`
- Experimental Experimental `yaml:"experimental"`
- Profile Profile `yaml:"profile"`
- GeoXUrl GeoXUrl `yaml:"geox-url" json:"geox-url"`
- Proxy []map[string]any `yaml:"proxies"`
- ProxyGroup []map[string]any `yaml:"proxy-groups"`
- Rule []string `yaml:"rules"`
- SubRules map[string][]string `yaml:"sub-rules"`
- RawTLS TLS `yaml:"tls"`
- Listeners []map[string]any `yaml:"listeners"`
- }
- type GeoXUrl struct {
- GeoIp string `yaml:"geoip" json:"geoip"`
- Mmdb string `yaml:"mmdb" json:"mmdb"`
- ASN string `yaml:"asn" json:"asn"`
- GeoSite string `yaml:"geosite" json:"geosite"`
- }
- type RawSniffer struct {
- Enable bool `yaml:"enable" json:"enable"`
- OverrideDest bool `yaml:"override-destination" json:"override-destination"`
- Sniffing []string `yaml:"sniffing" json:"sniffing"`
- ForceDomain []string `yaml:"force-domain" json:"force-domain"`
- SkipDomain []string `yaml:"skip-domain" json:"skip-domain"`
- Ports []string `yaml:"port-whitelist" json:"port-whitelist"`
- ForceDnsMapping bool `yaml:"force-dns-mapping" json:"force-dns-mapping"`
- ParsePureIp bool `yaml:"parse-pure-ip" json:"parse-pure-ip"`
- Sniff map[string]RawSniffingConfig `yaml:"sniff" json:"sniff"`
- }
- type RawSniffingConfig struct {
- Ports []string `yaml:"ports" json:"ports"`
- OverrideDest *bool `yaml:"override-destination" json:"override-destination"`
- }
- // EBpf config
- type EBpf struct {
- RedirectToTun []string `yaml:"redirect-to-tun" json:"redirect-to-tun"`
- AutoRedir []string `yaml:"auto-redir" json:"auto-redir"`
- }
- var (
- GroupsList = list.New()
- ProxiesList = list.New()
- ParsingProxiesCallback func(groupsList *list.List, proxiesList *list.List)
- )
- // Parse config
- func Parse(buf []byte) (*Config, error) {
- rawCfg, err := UnmarshalRawConfig(buf)
- if err != nil {
- return nil, err
- }
- return ParseRawConfig(rawCfg)
- }
- func DefaultRawConfig() *RawConfig {
- return &RawConfig{
- AllowLan: false,
- BindAddress: "*",
- LanAllowedIPs: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")},
- IPv6: true,
- Mode: T.Rule,
- GeoAutoUpdate: false,
- GeoUpdateInterval: 24,
- GeodataMode: C.GeodataMode,
- GeodataLoader: "memconservative",
- UnifiedDelay: false,
- Authentication: []string{},
- LogLevel: log.INFO,
- Hosts: map[string]any{},
- Rule: []string{},
- Proxy: []map[string]any{},
- ProxyGroup: []map[string]any{},
- TCPConcurrent: false,
- FindProcessMode: P.FindProcessStrict,
- GlobalUA: "clash.meta/" + C.Version,
- Tun: RawTun{
- Enable: false,
- Device: "",
- Stack: C.TunGvisor,
- DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
- AutoRoute: true,
- AutoDetectInterface: true,
- Inet6Address: []netip.Prefix{netip.MustParsePrefix("fdfe:dcba:9876::1/126")},
- },
- TuicServer: RawTuicServer{
- Enable: false,
- Token: nil,
- Users: nil,
- Certificate: "",
- PrivateKey: "",
- Listen: "",
- CongestionController: "",
- MaxIdleTime: 15000,
- AuthenticationTimeout: 1000,
- ALPN: []string{"h3"},
- MaxUdpRelayPacketSize: 1500,
- },
- EBpf: EBpf{
- RedirectToTun: []string{},
- AutoRedir: []string{},
- },
- IPTables: IPTables{
- Enable: false,
- InboundInterface: "lo",
- Bypass: []string{},
- DnsRedirect: true,
- },
- NTP: RawNTP{
- Enable: false,
- WriteToSystem: false,
- Server: "time.apple.com",
- ServerPort: 123,
- Interval: 30,
- },
- DNS: RawDNS{
- Enable: false,
- IPv6: false,
- UseHosts: true,
- UseSystemHosts: true,
- IPv6Timeout: 100,
- EnhancedMode: C.DNSMapping,
- FakeIPRange: "198.18.0.1/16",
- FallbackFilter: RawFallbackFilter{
- GeoIP: true,
- GeoIPCode: "CN",
- IPCIDR: []string{},
- GeoSite: []string{},
- },
- DefaultNameserver: []string{
- "114.114.114.114",
- "223.5.5.5",
- "8.8.8.8",
- "1.0.0.1",
- },
- NameServer: []string{
- "https://doh.pub/dns-query",
- "tls://223.5.5.5:853",
- },
- FakeIPFilter: []string{
- "dns.msftnsci.com",
- "www.msftnsci.com",
- "www.msftconnecttest.com",
- },
- },
- Experimental: Experimental{
- // https://github.com/quic-go/quic-go/issues/4178
- // Quic-go currently cannot automatically fall back on platforms that do not support ecn, so this feature is turned off by default.
- QUICGoDisableECN: true,
- },
- Sniffer: RawSniffer{
- Enable: false,
- Sniff: map[string]RawSniffingConfig{},
- ForceDomain: []string{},
- SkipDomain: []string{},
- Ports: []string{},
- ForceDnsMapping: true,
- ParsePureIp: true,
- OverrideDest: true,
- },
- Profile: Profile{
- StoreSelected: true,
- },
- GeoXUrl: GeoXUrl{
- Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb",
- ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb",
- GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat",
- GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat",
- },
- ExternalUIURL: "https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip",
- }
- }
- func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
- // config with default value
- rawCfg := DefaultRawConfig()
- if err := yaml.Unmarshal(buf, rawCfg); err != nil {
- return nil, err
- }
- return rawCfg, nil
- }
- func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
- config := &Config{}
- log.Infoln("Start initial configuration in progress") //Segment finished in xxm
- startTime := time.Now()
- config.Experimental = &rawCfg.Experimental
- config.Profile = &rawCfg.Profile
- config.IPTables = &rawCfg.IPTables
- config.TLS = &rawCfg.RawTLS
- general, err := parseGeneral(rawCfg)
- if err != nil {
- return nil, err
- }
- config.General = general
- if len(config.General.GlobalClientFingerprint) != 0 {
- log.Debugln("GlobalClientFingerprint: %s", config.General.GlobalClientFingerprint)
- tlsC.SetGlobalUtlsClient(config.General.GlobalClientFingerprint)
- }
- proxies, providers, err := parseProxies(rawCfg)
- if err != nil {
- return nil, err
- }
- config.Proxies = proxies
- config.Providers = providers
- listener, err := parseListeners(rawCfg)
- if err != nil {
- return nil, err
- }
- config.Listeners = listener
- log.Infoln("Geodata Loader mode: %s", geodata.LoaderName())
- log.Infoln("Geosite Matcher implementation: %s", geodata.SiteMatcherName())
- ruleProviders, err := parseRuleProviders(rawCfg)
- if err != nil {
- return nil, err
- }
- config.RuleProviders = ruleProviders
- subRules, err := parseSubRules(rawCfg, proxies, ruleProviders)
- if err != nil {
- return nil, err
- }
- config.SubRules = subRules
- rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules")
- if err != nil {
- return nil, err
- }
- config.Rules = rules
- hosts, err := parseHosts(rawCfg)
- if err != nil {
- return nil, err
- }
- config.Hosts = hosts
- ntpCfg := paresNTP(rawCfg)
- config.NTP = ntpCfg
- dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
- if err != nil {
- return nil, err
- }
- config.DNS = dnsCfg
- err = parseTun(rawCfg.Tun, config.General)
- err = parseTuicServer(rawCfg.TuicServer, config.General)
- if err != nil {
- return nil, err
- }
- config.Users = parseAuthentication(rawCfg.Authentication)
- config.Tunnels = rawCfg.Tunnels
- // verify tunnels
- for _, t := range config.Tunnels {
- if len(t.Proxy) > 0 {
- if _, ok := config.Proxies[t.Proxy]; !ok {
- return nil, fmt.Errorf("tunnel proxy %s not found", t.Proxy)
- }
- }
- }
- config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
- if err != nil {
- return nil, err
- }
- elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms
- log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm
- return config, nil
- }
- func parseGeneral(cfg *RawConfig) (*General, error) {
- geodata.SetGeodataMode(cfg.GeodataMode)
- geodata.SetGeoAutoUpdate(cfg.GeoAutoUpdate)
- geodata.SetGeoUpdateInterval(cfg.GeoUpdateInterval)
- geodata.SetLoader(cfg.GeodataLoader)
- geodata.SetSiteMatcher(cfg.GeositeMatcher)
- C.GeoAutoUpdate = cfg.GeoAutoUpdate
- C.GeoUpdateInterval = cfg.GeoUpdateInterval
- C.GeoIpUrl = cfg.GeoXUrl.GeoIp
- C.GeoSiteUrl = cfg.GeoXUrl.GeoSite
- C.MmdbUrl = cfg.GeoXUrl.Mmdb
- C.ASNUrl = cfg.GeoXUrl.ASN
- C.GeodataMode = cfg.GeodataMode
- C.UA = cfg.GlobalUA
- if cfg.KeepAliveInterval != 0 {
- N.KeepAliveInterval = time.Duration(cfg.KeepAliveInterval) * time.Second
- }
- updater.ExternalUIPath = cfg.ExternalUI
- // checkout externalUI exist
- if updater.ExternalUIPath != "" {
- updater.ExternalUIPath = C.Path.Resolve(updater.ExternalUIPath)
- if _, err := os.Stat(updater.ExternalUIPath); os.IsNotExist(err) {
- defaultUIpath := path.Join(C.Path.HomeDir(), "ui")
- log.Warnln("external-ui: %s does not exist, creating folder in %s", updater.ExternalUIPath, defaultUIpath)
- if err := os.MkdirAll(defaultUIpath, os.ModePerm); err != nil {
- return nil, err
- }
- updater.ExternalUIPath = defaultUIpath
- cfg.ExternalUI = defaultUIpath
- }
- }
- // checkout UIpath/name exist
- if cfg.ExternalUIName != "" {
- updater.ExternalUIName = cfg.ExternalUIName
- } else {
- updater.ExternalUIFolder = updater.ExternalUIPath
- }
- if cfg.ExternalUIURL != "" {
- updater.ExternalUIURL = cfg.ExternalUIURL
- }
- return &General{
- Inbound: Inbound{
- Port: cfg.Port,
- SocksPort: cfg.SocksPort,
- RedirPort: cfg.RedirPort,
- TProxyPort: cfg.TProxyPort,
- MixedPort: cfg.MixedPort,
- ShadowSocksConfig: cfg.ShadowSocksConfig,
- VmessConfig: cfg.VmessConfig,
- AllowLan: cfg.AllowLan,
- SkipAuthPrefixes: cfg.SkipAuthPrefixes,
- LanAllowedIPs: cfg.LanAllowedIPs,
- LanDisAllowedIPs: cfg.LanDisAllowedIPs,
- BindAddress: cfg.BindAddress,
- InboundTfo: cfg.InboundTfo,
- InboundMPTCP: cfg.InboundMPTCP,
- },
- Controller: Controller{
- ExternalController: cfg.ExternalController,
- ExternalUI: cfg.ExternalUI,
- Secret: cfg.Secret,
- ExternalControllerUnix: cfg.ExternalControllerUnix,
- ExternalControllerTLS: cfg.ExternalControllerTLS,
- ExternalDohServer: cfg.ExternalDohServer,
- },
- UnifiedDelay: cfg.UnifiedDelay,
- Mode: cfg.Mode,
- LogLevel: cfg.LogLevel,
- IPv6: cfg.IPv6,
- Interface: cfg.Interface,
- RoutingMark: cfg.RoutingMark,
- GeoXUrl: cfg.GeoXUrl,
- GeoAutoUpdate: cfg.GeoAutoUpdate,
- GeoUpdateInterval: cfg.GeoUpdateInterval,
- GeodataMode: cfg.GeodataMode,
- GeodataLoader: cfg.GeodataLoader,
- TCPConcurrent: cfg.TCPConcurrent,
- FindProcessMode: cfg.FindProcessMode,
- EBpf: cfg.EBpf,
- GlobalClientFingerprint: cfg.GlobalClientFingerprint,
- GlobalUA: cfg.GlobalUA,
- }, nil
- }
- func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]providerTypes.ProxyProvider, err error) {
- proxies = make(map[string]C.Proxy)
- providersMap = make(map[string]providerTypes.ProxyProvider)
- proxiesConfig := cfg.Proxy
- groupsConfig := cfg.ProxyGroup
- providersConfig := cfg.ProxyProvider
- var (
- proxyList []string
- AllProxies []string
- hasGlobal bool
- )
- proxiesList := list.New()
- groupsList := list.New()
- proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
- proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
- proxies["REJECT-DROP"] = adapter.NewProxy(outbound.NewRejectDrop())
- proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible())
- proxies["PASS"] = adapter.NewProxy(outbound.NewPass())
- proxyList = append(proxyList, "DIRECT", "REJECT")
- // parse proxy
- for idx, mapping := range proxiesConfig {
- proxy, err := adapter.ParseProxy(mapping)
- if err != nil {
- return nil, nil, fmt.Errorf("proxy %d: %w", idx, err)
- }
- if _, exist := proxies[proxy.Name()]; exist {
- return nil, nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name())
- }
- proxies[proxy.Name()] = proxy
- proxyList = append(proxyList, proxy.Name())
- AllProxies = append(AllProxies, proxy.Name())
- proxiesList.PushBack(mapping)
- }
- // keep the original order of ProxyGroups in config file
- for idx, mapping := range groupsConfig {
- groupName, existName := mapping["name"].(string)
- if !existName {
- return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
- }
- if groupName == "GLOBAL" {
- hasGlobal = true
- }
- proxyList = append(proxyList, groupName)
- groupsList.PushBack(mapping)
- }
- // check if any loop exists and sort the ProxyGroups
- if err := proxyGroupsDagSort(groupsConfig); err != nil {
- return nil, nil, err
- }
- var AllProviders []string
- // parse and initial providers
- for name, mapping := range providersConfig {
- if name == provider.ReservedName {
- return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName)
- }
- pd, err := provider.ParseProxyProvider(name, mapping)
- if err != nil {
- return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err)
- }
- providersMap[name] = pd
- AllProviders = append(AllProviders, name)
- }
- slices.Sort(AllProxies)
- slices.Sort(AllProviders)
- // parse proxy group
- for idx, mapping := range groupsConfig {
- group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap, AllProxies, AllProviders)
- if err != nil {
- return nil, nil, fmt.Errorf("proxy group[%d]: %w", idx, err)
- }
- groupName := group.Name()
- if _, exist := proxies[groupName]; exist {
- return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName)
- }
- proxies[groupName] = adapter.NewProxy(group)
- }
- var ps []C.Proxy
- for _, v := range proxyList {
- if proxies[v].Type() == C.Pass {
- continue
- }
- ps = append(ps, proxies[v])
- }
- hc := provider.NewHealthCheck(ps, "", 5000, 0, true, nil)
- pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
- providersMap[provider.ReservedName] = pd
- if !hasGlobal {
- global := outboundgroup.NewSelector(
- &outboundgroup.GroupCommonOption{
- Name: "GLOBAL",
- },
- []providerTypes.ProxyProvider{pd},
- )
- proxies["GLOBAL"] = adapter.NewProxy(global)
- }
- ProxiesList = proxiesList
- GroupsList = groupsList
- if ParsingProxiesCallback != nil {
- // refresh tray menu
- go ParsingProxiesCallback(GroupsList, ProxiesList)
- }
- return proxies, providersMap, nil
- }
- func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err error) {
- listeners = make(map[string]C.InboundListener)
- for index, mapping := range cfg.Listeners {
- listener, err := L.ParseListener(mapping)
- if err != nil {
- return nil, fmt.Errorf("proxy %d: %w", index, err)
- }
- if _, exist := mapping[listener.Name()]; exist {
- return nil, fmt.Errorf("listener %s is the duplicate name", listener.Name())
- }
- listeners[listener.Name()] = listener
- }
- return
- }
- func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) {
- RP.SetTunnel(T.Tunnel)
- ruleProviders = map[string]providerTypes.RuleProvider{}
- // parse rule provider
- for name, mapping := range cfg.RuleProvider {
- rp, err := RP.ParseRuleProvider(name, mapping, R.ParseRule)
- if err != nil {
- return nil, err
- }
- ruleProviders[name] = rp
- }
- return
- }
- func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) {
- subRules = map[string][]C.Rule{}
- for name := range cfg.SubRules {
- subRules[name] = make([]C.Rule, 0)
- }
- for name, rawRules := range cfg.SubRules {
- if len(name) == 0 {
- return nil, fmt.Errorf("sub-rule name is empty")
- }
- var rules []C.Rule
- rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name))
- if err != nil {
- return nil, err
- }
- subRules[name] = rules
- }
- if err = verifySubRule(subRules); err != nil {
- return nil, err
- }
- return
- }
- func verifySubRule(subRules map[string][]C.Rule) error {
- for name := range subRules {
- err := verifySubRuleCircularReferences(name, subRules, []string{})
- if err != nil {
- return err
- }
- }
- return nil
- }
- func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr []string) error {
- isInArray := func(v string, array []string) bool {
- for _, c := range array {
- if v == c {
- return true
- }
- }
- return false
- }
- arr = append(arr, n)
- for i, rule := range subRules[n] {
- if rule.RuleType() == C.SubRules {
- if _, ok := subRules[rule.Adapter()]; !ok {
- return fmt.Errorf("sub-rule[%d:%s] error: [%s] not found", i, n, rule.Adapter())
- }
- if isInArray(rule.Adapter(), arr) {
- arr = append(arr, rule.Adapter())
- return fmt.Errorf("sub-rule error: circular references [%s]", strings.Join(arr, "->"))
- }
- if err := verifySubRuleCircularReferences(rule.Adapter(), subRules, arr); err != nil {
- return err
- }
- }
- }
- return nil
- }
- func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {
- var rules []C.Rule
- // parse rules
- for idx, line := range rulesConfig {
- rule := trimArr(strings.Split(line, ","))
- var (
- payload string
- target string
- params []string
- ruleName = strings.ToUpper(rule[0])
- )
- l := len(rule)
- if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" || ruleName == "DOMAIN-REGEX" || ruleName == "PROCESS-NAME-REGEX" || ruleName == "PROCESS-PATH-REGEX" {
- target = rule[l-1]
- payload = strings.Join(rule[1:l-1], ",")
- } else {
- if l < 2 {
- return nil, fmt.Errorf("%s[%d] [%s] error: format invalid", format, idx, line)
- }
- if l < 4 {
- rule = append(rule, make([]string, 4-l)...)
- }
- if ruleName == "MATCH" {
- l = 2
- }
- if l >= 3 {
- l = 3
- payload = rule[1]
- }
- target = rule[l-1]
- params = rule[l:]
- }
- if _, ok := proxies[target]; !ok {
- if ruleName != "SUB-RULE" {
- return nil, fmt.Errorf("%s[%d] [%s] error: proxy [%s] not found", format, idx, line, target)
- } else if _, ok = subRules[target]; !ok {
- return nil, fmt.Errorf("%s[%d] [%s] error: sub-rule [%s] not found", format, idx, line, target)
- }
- }
- params = trimArr(params)
- parsed, parseErr := R.ParseRule(ruleName, payload, target, params, subRules)
- if parseErr != nil {
- return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error())
- }
- for _, name := range parsed.ProviderNames() {
- if _, ok := ruleProviders[name]; !ok {
- return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name)
- }
- }
- rules = append(rules, parsed)
- }
- return rules, nil
- }
- func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {
- tree := trie.New[resolver.HostValue]()
- // add default hosts
- hostValue, _ := resolver.NewHostValueByIPs(
- []netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1})})
- if err := tree.Insert("localhost", hostValue); err != nil {
- log.Errorln("insert localhost to host error: %s", err.Error())
- }
- if len(cfg.Hosts) != 0 {
- for domain, anyValue := range cfg.Hosts {
- if str, ok := anyValue.(string); ok && str == "lan" {
- if addrs, err := net.InterfaceAddrs(); err != nil {
- log.Errorln("insert lan to host error: %s", err)
- } else {
- ips := make([]netip.Addr, 0)
- for _, addr := range addrs {
- if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {
- if ip, err := netip.ParseAddr(ipnet.IP.String()); err == nil {
- ips = append(ips, ip)
- }
- }
- }
- anyValue = ips
- }
- }
- value, err := resolver.NewHostValue(anyValue)
- if err != nil {
- return nil, fmt.Errorf("%s is not a valid value", anyValue)
- }
- if value.IsDomain {
- node := tree.Search(value.Domain)
- for node != nil && node.Data().IsDomain {
- if node.Data().Domain == domain {
- return nil, fmt.Errorf("%s, there is a cycle in domain name mapping", domain)
- }
- node = tree.Search(node.Data().Domain)
- }
- }
- _ = tree.Insert(domain, value)
- }
- }
- tree.Optimize()
- return tree, nil
- }
- func hostWithDefaultPort(host string, defPort string) (string, error) {
- hostname, port, err := net.SplitHostPort(host)
- if err != nil {
- if !strings.Contains(err.Error(), "missing port in address") {
- return "", err
- }
- host = host + ":" + defPort
- if hostname, port, err = net.SplitHostPort(host); err != nil {
- return "", err
- }
- }
- return net.JoinHostPort(hostname, port), nil
- }
- func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) {
- var nameservers []dns.NameServer
- for idx, server := range servers {
- if strings.HasPrefix(server, "dhcp://") {
- nameservers = append(
- nameservers,
- dns.NameServer{
- Net: "dhcp",
- Addr: server[len("dhcp://"):],
- },
- )
- continue
- }
- server = parsePureDNSServer(server)
- u, err := url.Parse(server)
- if err != nil {
- return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
- }
- proxyName := u.Fragment
- var addr, dnsNetType string
- params := map[string]string{}
- switch u.Scheme {
- case "udp":
- addr, err = hostWithDefaultPort(u.Host, "53")
- dnsNetType = "" // UDP
- case "tcp":
- addr, err = hostWithDefaultPort(u.Host, "53")
- dnsNetType = "tcp" // TCP
- case "tls":
- addr, err = hostWithDefaultPort(u.Host, "853")
- dnsNetType = "tcp-tls" // DNS over TLS
- case "http", "https":
- addr, err = hostWithDefaultPort(u.Host, "443")
- dnsNetType = "https" // DNS over HTTPS
- if u.Scheme == "http" {
- addr, err = hostWithDefaultPort(u.Host, "80")
- }
- if err == nil {
- proxyName = ""
- clearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User}
- addr = clearURL.String()
- if len(u.Fragment) != 0 {
- for _, s := range strings.Split(u.Fragment, "&") {
- arr := strings.Split(s, "=")
- if len(arr) == 0 {
- continue
- } else if len(arr) == 1 {
- proxyName = arr[0]
- } else if len(arr) == 2 {
- params[arr[0]] = arr[1]
- } else {
- params[arr[0]] = strings.Join(arr[1:], "=")
- }
- }
- }
- }
- case "quic":
- addr, err = hostWithDefaultPort(u.Host, "853")
- dnsNetType = "quic" // DNS over QUIC
- case "system":
- dnsNetType = "system" // System DNS
- case "rcode":
- dnsNetType = "rcode"
- addr = u.Host
- switch addr {
- case "success",
- "format_error",
- "server_failure",
- "name_error",
- "not_implemented",
- "refused":
- default:
- err = fmt.Errorf("unsupported RCode type: %s", addr)
- }
- default:
- return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
- }
- if err != nil {
- return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
- }
- if respectRules && len(proxyName) == 0 {
- proxyName = dns.RespectRules
- }
- nameservers = append(
- nameservers,
- dns.NameServer{
- Net: dnsNetType,
- Addr: addr,
- ProxyName: proxyName,
- Params: params,
- PreferH3: preferH3,
- },
- )
- }
- return nameservers, nil
- }
- func init() {
- dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
- return parseNameServer(servers, false, false)
- }
- }
- func parsePureDNSServer(server string) string {
- addPre := func(server string) string {
- return "udp://" + server
- }
- if server == "system" {
- return "system://"
- }
- if ip, err := netip.ParseAddr(server); err != nil {
- if strings.Contains(server, "://") {
- return server
- }
- return addPre(server)
- } else {
- if ip.Is4() {
- return addPre(server)
- } else {
- return addPre("[" + server + "]")
- }
- }
- }
- func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) {
- policy := orderedmap.New[string, []dns.NameServer]()
- updatedPolicy := orderedmap.New[string, any]()
- re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
- for pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() {
- k, v := pair.Key, pair.Value
- if strings.Contains(strings.ToLower(k), ",") {
- if strings.Contains(k, "geosite:") {
- subkeys := strings.Split(k, ":")
- subkeys = subkeys[1:]
- subkeys = strings.Split(subkeys[0], ",")
- for _, subkey := range subkeys {
- newKey := "geosite:" + subkey
- updatedPolicy.Store(newKey, v)
- }
- } else if strings.Contains(strings.ToLower(k), "rule-set:") {
- subkeys := strings.Split(k, ":")
- subkeys = subkeys[1:]
- subkeys = strings.Split(subkeys[0], ",")
- for _, subkey := range subkeys {
- newKey := "rule-set:" + subkey
- updatedPolicy.Store(newKey, v)
- }
- } else if re.MatchString(k) {
- subkeys := strings.Split(k, ",")
- for _, subkey := range subkeys {
- updatedPolicy.Store(subkey, v)
- }
- }
- } else {
- if strings.Contains(strings.ToLower(k), "geosite:") {
- updatedPolicy.Store("geosite:"+k[8:], v)
- } else if strings.Contains(strings.ToLower(k), "rule-set:") {
- updatedPolicy.Store("rule-set:"+k[9:], v)
- }
- updatedPolicy.Store(k, v)
- }
- }
- for pair := updatedPolicy.Oldest(); pair != nil; pair = pair.Next() {
- domain, server := pair.Key, pair.Value
- servers, err := utils.ToStringSlice(server)
- if err != nil {
- return nil, err
- }
- nameservers, err := parseNameServer(servers, respectRules, preferH3)
- if err != nil {
- return nil, err
- }
- if _, valid := trie.ValidAndSplitDomain(domain); !valid {
- return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
- }
- if strings.HasPrefix(domain, "rule-set:") {
- domainSetName := domain[9:]
- if provider, ok := ruleProviders[domainSetName]; !ok {
- return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
- } else {
- switch provider.Behavior() {
- case providerTypes.IPCIDR:
- return nil, fmt.Errorf("rule provider type error, except domain,actual %s", provider.Behavior())
- case providerTypes.Classical:
- log.Warnln("%s provider is %s, only matching it contain domain rule", provider.Name(), provider.Behavior())
- }
- }
- }
- policy.Store(domain, nameservers)
- }
- return policy, nil
- }
- func parseFallbackIPCIDR(ips []string) ([]netip.Prefix, error) {
- var ipNets []netip.Prefix
- for idx, ip := range ips {
- ipnet, err := netip.ParsePrefix(ip)
- if err != nil {
- return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error())
- }
- ipNets = append(ipNets, ipnet)
- }
- return ipNets, nil
- }
- func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]router.DomainMatcher, error) {
- var sites []router.DomainMatcher
- if len(countries) > 0 {
- if err := geodata.InitGeoSite(); err != nil {
- return nil, fmt.Errorf("can't initial GeoSite: %s", err)
- }
- log.Warnln("replace fallback-filter.geosite with nameserver-policy, it will be removed in the future")
- }
- for _, country := range countries {
- found := false
- for _, rule := range rules {
- if rule.RuleType() == C.GEOSITE {
- if strings.EqualFold(country, rule.Payload()) {
- found = true
- sites = append(sites, rule.(C.RuleGeoSite).GetDomainMatcher())
- log.Infoln("Start initial GeoSite dns fallback filter from rule `%s`", country)
- }
- }
- }
- if !found {
- matcher, recordsCount, err := geodata.LoadGeoSiteMatcher(country)
- if err != nil {
- return nil, err
- }
- sites = append(sites, matcher)
- log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount)
- }
- }
- return sites, nil
- }
- func paresNTP(rawCfg *RawConfig) *NTP {
- cfg := rawCfg.NTP
- ntpCfg := &NTP{
- Enable: cfg.Enable,
- Server: cfg.Server,
- Port: cfg.ServerPort,
- Interval: cfg.Interval,
- DialerProxy: cfg.DialerProxy,
- WriteToSystem: cfg.WriteToSystem,
- }
- return ntpCfg
- }
- func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) {
- cfg := rawCfg.DNS
- if cfg.Enable && len(cfg.NameServer) == 0 {
- return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
- }
- if cfg.RespectRules && len(cfg.ProxyServerNameserver) == 0 {
- return nil, fmt.Errorf("if “respect-rules” is turned on, “proxy-server-nameserver” cannot be empty")
- }
- dnsCfg := &DNS{
- Enable: cfg.Enable,
- Listen: cfg.Listen,
- PreferH3: cfg.PreferH3,
- IPv6Timeout: cfg.IPv6Timeout,
- IPv6: cfg.IPv6,
- UseSystemHosts: cfg.UseSystemHosts,
- EnhancedMode: cfg.EnhancedMode,
- FallbackFilter: FallbackFilter{
- IPCIDR: []netip.Prefix{},
- GeoSite: []router.DomainMatcher{},
- },
- }
- var err error
- if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil {
- return nil, err
- }
- if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil {
- return nil, err
- }
- if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil {
- return nil, err
- }
- if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil {
- return nil, err
- }
- if len(cfg.DefaultNameserver) == 0 {
- return nil, errors.New("default nameserver should have at least one nameserver")
- }
- if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil {
- return nil, err
- }
- // check default nameserver is pure ip addr
- for _, ns := range dnsCfg.DefaultNameserver {
- if ns.Net == "system" {
- continue
- }
- host, _, err := net.SplitHostPort(ns.Addr)
- if err != nil || net.ParseIP(host) == nil {
- u, err := url.Parse(ns.Addr)
- if err == nil && net.ParseIP(u.Host) == nil {
- if ip, _, err := net.SplitHostPort(u.Host); err != nil || net.ParseIP(ip) == nil {
- return nil, errors.New("default nameserver should be pure IP")
- }
- }
- }
- }
- fakeIPRange, err := netip.ParsePrefix(cfg.FakeIPRange)
- T.SetFakeIPRange(fakeIPRange)
- if cfg.EnhancedMode == C.DNSFakeIP {
- if err != nil {
- return nil, err
- }
- var host *trie.DomainTrie[struct{}]
- // fake ip skip host filter
- if len(cfg.FakeIPFilter) != 0 {
- host = trie.New[struct{}]()
- for _, domain := range cfg.FakeIPFilter {
- _ = host.Insert(domain, struct{}{})
- }
- host.Optimize()
- }
- if len(dnsCfg.Fallback) != 0 {
- if host == nil {
- host = trie.New[struct{}]()
- }
- for _, fb := range dnsCfg.Fallback {
- if net.ParseIP(fb.Addr) != nil {
- continue
- }
- _ = host.Insert(fb.Addr, struct{}{})
- }
- host.Optimize()
- }
- pool, err := fakeip.New(fakeip.Options{
- IPNet: fakeIPRange,
- Size: 1000,
- Host: host,
- Persistence: rawCfg.Profile.StoreFakeIP,
- })
- if err != nil {
- return nil, err
- }
- dnsCfg.FakeIPRange = pool
- }
- if len(cfg.Fallback) != 0 {
- dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP
- dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode
- if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil {
- dnsCfg.FallbackFilter.IPCIDR = fallbackip
- }
- dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain
- fallbackGeoSite, err := parseFallbackGeoSite(cfg.FallbackFilter.GeoSite, rules)
- if err != nil {
- return nil, fmt.Errorf("load GeoSite dns fallback filter error, %w", err)
- }
- dnsCfg.FallbackFilter.GeoSite = fallbackGeoSite
- }
- if cfg.UseHosts {
- dnsCfg.Hosts = hosts
- }
- if cfg.CacheAlgorithm == "" || cfg.CacheAlgorithm == "lru" {
- dnsCfg.CacheAlgorithm = "lru"
- } else {
- dnsCfg.CacheAlgorithm = "arc"
- }
- return dnsCfg, nil
- }
- func parseAuthentication(rawRecords []string) []auth.AuthUser {
- var users []auth.AuthUser
- for _, line := range rawRecords {
- if user, pass, found := strings.Cut(line, ":"); found {
- users = append(users, auth.AuthUser{User: user, Pass: pass})
- }
- }
- return users
- }
- func parseTun(rawTun RawTun, general *General) error {
- tunAddressPrefix := T.FakeIPRange()
- if !tunAddressPrefix.IsValid() {
- tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16")
- }
- tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30)
- if !general.IPv6 || !verifyIP6() {
- rawTun.Inet6Address = nil
- }
- general.Tun = LC.Tun{
- Enable: rawTun.Enable,
- Device: rawTun.Device,
- Stack: rawTun.Stack,
- DNSHijack: rawTun.DNSHijack,
- AutoRoute: rawTun.AutoRoute,
- AutoDetectInterface: rawTun.AutoDetectInterface,
- MTU: rawTun.MTU,
- GSO: rawTun.GSO,
- GSOMaxSize: rawTun.GSOMaxSize,
- Inet4Address: []netip.Prefix{tunAddressPrefix},
- Inet6Address: rawTun.Inet6Address,
- IPRoute2TableIndex: rawTun.IPRoute2TableIndex,
- IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex,
- AutoRedirect: rawTun.AutoRedirect,
- AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
- AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
- StrictRoute: rawTun.StrictRoute,
- RouteAddress: rawTun.RouteAddress,
- RouteAddressSet: rawTun.RouteAddressSet,
- RouteExcludeAddress: rawTun.RouteExcludeAddress,
- RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet,
- IncludeInterface: rawTun.IncludeInterface,
- ExcludeInterface: rawTun.ExcludeInterface,
- IncludeUID: rawTun.IncludeUID,
- IncludeUIDRange: rawTun.IncludeUIDRange,
- ExcludeUID: rawTun.ExcludeUID,
- ExcludeUIDRange: rawTun.ExcludeUIDRange,
- IncludeAndroidUser: rawTun.IncludeAndroidUser,
- IncludePackage: rawTun.IncludePackage,
- ExcludePackage: rawTun.ExcludePackage,
- EndpointIndependentNat: rawTun.EndpointIndependentNat,
- UDPTimeout: rawTun.UDPTimeout,
- FileDescriptor: rawTun.FileDescriptor,
- Inet4RouteAddress: rawTun.Inet4RouteAddress,
- Inet6RouteAddress: rawTun.Inet6RouteAddress,
- Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress,
- Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress,
- }
- return nil
- }
- func parseTuicServer(rawTuic RawTuicServer, general *General) error {
- general.TuicServer = LC.TuicServer{
- Enable: rawTuic.Enable,
- Listen: rawTuic.Listen,
- Token: rawTuic.Token,
- Users: rawTuic.Users,
- Certificate: rawTuic.Certificate,
- PrivateKey: rawTuic.PrivateKey,
- CongestionController: rawTuic.CongestionController,
- MaxIdleTime: rawTuic.MaxIdleTime,
- AuthenticationTimeout: rawTuic.AuthenticationTimeout,
- ALPN: rawTuic.ALPN,
- MaxUdpRelayPacketSize: rawTuic.MaxUdpRelayPacketSize,
- CWND: rawTuic.CWND,
- }
- return nil
- }
- func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
- sniffer := &Sniffer{
- Enable: snifferRaw.Enable,
- ForceDnsMapping: snifferRaw.ForceDnsMapping,
- ParsePureIp: snifferRaw.ParsePureIp,
- }
- loadSniffer := make(map[snifferTypes.Type]SNIFF.SnifferConfig)
- if len(snifferRaw.Sniff) != 0 {
- for sniffType, sniffConfig := range snifferRaw.Sniff {
- find := false
- ports, err := utils.NewUnsignedRangesFromList[uint16](sniffConfig.Ports)
- if err != nil {
- return nil, err
- }
- overrideDest := snifferRaw.OverrideDest
- if sniffConfig.OverrideDest != nil {
- overrideDest = *sniffConfig.OverrideDest
- }
- for _, snifferType := range snifferTypes.List {
- if snifferType.String() == strings.ToUpper(sniffType) {
- find = true
- loadSniffer[snifferType] = SNIFF.SnifferConfig{
- Ports: ports,
- OverrideDest: overrideDest,
- }
- }
- }
- if !find {
- return nil, fmt.Errorf("not find the sniffer[%s]", sniffType)
- }
- }
- } else {
- if sniffer.Enable && len(snifferRaw.Sniffing) != 0 {
- // Deprecated: Use Sniff instead
- log.Warnln("Deprecated: Use Sniff instead")
- }
- globalPorts, err := utils.NewUnsignedRangesFromList[uint16](snifferRaw.Ports)
- if err != nil {
- return nil, err
- }
- for _, snifferName := range snifferRaw.Sniffing {
- find := false
- for _, snifferType := range snifferTypes.List {
- if snifferType.String() == strings.ToUpper(snifferName) {
- find = true
- loadSniffer[snifferType] = SNIFF.SnifferConfig{
- Ports: globalPorts,
- OverrideDest: snifferRaw.OverrideDest,
- }
- }
- }
- if !find {
- return nil, fmt.Errorf("not find the sniffer[%s]", snifferName)
- }
- }
- }
- sniffer.Sniffers = loadSniffer
- forceDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.ForceDomain {
- err := forceDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
- }
- sniffer.ForceDomain = forceDomainTrie.NewDomainSet()
- skipDomainTrie := trie.New[struct{}]()
- for _, domain := range snifferRaw.SkipDomain {
- err := skipDomainTrie.Insert(domain, struct{}{})
- if err != nil {
- return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
- }
- }
- sniffer.SkipDomain = skipDomainTrie.NewDomainSet()
- return sniffer, nil
- }
|