Compare commits

...

7 Commits

Author SHA1 Message Date
Aubrey Yang
70fb352b9c
Merge 256e9d8c3d into 2b4741fbc7 2024-06-12 17:18:20 +08:00
wwqgtxx
2b4741fbc7 chore: add inner dns proxied connection statistic to restful api 2024-06-12 17:10:47 +08:00
wwqgtxx
f317baa8de feat: add respect-rules for dns 2024-06-12 15:25:34 +08:00
wwqgtxx
5678131591 fix: wireguard server resolve when only a server in peers
Some checks are pending
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
2024-06-12 11:37:23 +08:00
xishang0128
10f8ba4434 chore: Disable the loop back detector for CMFA 2024-06-12 04:46:13 +08:00
Aubrey Yang
256e9d8c3d
Update loadbalance.go 2024-04-17 18:21:59 +09:00
Aubrey Yang
828ba83ef3
Optimizations on the Round Robin strategies
Implemented optimizations on the Round Robin proxy selection strategies to enhance performance and stability under varying network conditions and proxy availabilities.

Dynamic Update Mechanism: Integrated an event-driven approach that triggers the proxy list update process when significant changes in proxy status are detected, rather than on every touch.

Memory and Performance: Optimized the management of the available proxies list to update in-place where possible.

Load Distribution: Improved the fairness in proxy usage by introducing a weighted round-robin mechanism that accounts for proxy response times and error rates, ensuring a more balanced load across the proxies.
2024-04-17 18:20:30 +09:00
7 changed files with 153 additions and 86 deletions

View File

@ -4,14 +4,19 @@ import (
"context" "context"
"errors" "errors"
"net/netip" "net/netip"
"os"
"strconv"
N "github.com/metacubex/mihomo/common/net" N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/loopback"
"github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
) )
var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR"))
type Direct struct { type Direct struct {
*Base *Base
loopBack *loopback.Detector loopBack *loopback.Detector
@ -24,9 +29,11 @@ type DirectOption struct {
// DialContext implements C.ProxyAdapter // DialContext implements C.ProxyAdapter
func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
if !features.CMFA && !DisableLoopBackDetector {
if err := d.loopBack.CheckConn(metadata); err != nil { if err := d.loopBack.CheckConn(metadata); err != nil {
return nil, err return nil, err
} }
}
opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) opts = append(opts, dialer.WithResolver(resolver.DefaultResolver))
c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...)
if err != nil { if err != nil {
@ -38,9 +45,11 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...
// ListenPacketContext implements C.ProxyAdapter // ListenPacketContext implements C.ProxyAdapter
func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
if !features.CMFA && !DisableLoopBackDetector {
if err := d.loopBack.CheckPacketConn(metadata); err != nil { if err := d.loopBack.CheckPacketConn(metadata); err != nil {
return nil, err return nil, err
} }
}
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
if !metadata.Resolved() { if !metadata.Resolved() {
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver)

View File

@ -304,13 +304,16 @@ func (w *WireGuard) init(ctx context.Context) error {
ipcConf := "private_key=" + w.option.PrivateKey ipcConf := "private_key=" + w.option.PrivateKey
if len(w.option.Peers) > 0 { if len(w.option.Peers) > 0 {
for i, peer := range w.option.Peers { for i, peer := range w.option.Peers {
ipcConf += "\npublic_key=" + peer.PublicKey
destination, err := w.resolve(ctx, peer.Addr()) destination, err := w.resolve(ctx, peer.Addr())
if err != nil { if err != nil {
// !!! do not set initErr here !!! // !!! do not set initErr here !!!
// let us can retry domain resolve in next time // let us can retry domain resolve in next time
return E.Cause(err, "resolve endpoint domain for peer ", i) return E.Cause(err, "resolve endpoint domain for peer ", i)
} }
ipcConf += "\npublic_key=" + peer.PublicKey if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true
w.bind.SetConnectAddr(destination)
}
ipcConf += "\nendpoint=" + destination.String() ipcConf += "\nendpoint=" + destination.String()
if peer.PreSharedKey != "" { if peer.PreSharedKey != "" {
ipcConf += "\npreshared_key=" + peer.PreSharedKey ipcConf += "\npreshared_key=" + peer.PreSharedKey
@ -332,7 +335,7 @@ func (w *WireGuard) init(ctx context.Context) error {
// let us can retry domain resolve in next time // let us can retry domain resolve in next time
return E.Cause(err, "resolve endpoint domain") return E.Cause(err, "resolve endpoint domain")
} }
w.bind.SetConnectAddr(destination) w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true
ipcConf += "\nendpoint=" + destination.String() ipcConf += "\nendpoint=" + destination.String()
if w.option.PreSharedKey != "" { if w.option.PreSharedKey != "" {
ipcConf += "\npreshared_key=" + w.option.PreSharedKey ipcConf += "\npreshared_key=" + w.option.PreSharedKey

View File

@ -5,8 +5,8 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net"
"sync" "sync"
"net"
"time" "time"
"github.com/metacubex/mihomo/adapter/outbound" "github.com/metacubex/mihomo/adapter/outbound"
@ -134,32 +134,32 @@ func (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool {
} }
func strategyRoundRobin(url string) strategyFn { func strategyRoundRobin(url string) strategyFn {
var availableProxies []C.Proxy
idx := 0 idx := 0
idxMutex := sync.Mutex{} idxMutex := sync.Mutex{}
return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {
idxMutex.Lock() idxMutex.Lock()
defer idxMutex.Unlock() defer idxMutex.Unlock()
i := 0
length := len(proxies)
if touch { if touch {
defer func() { // check list
idx = (idx + i) % length availableProxies = []C.Proxy{}
}() for _, proxy := range proxies {
}
for ; i < length; i++ {
id := (idx + i) % length
proxy := proxies[id]
if proxy.AliveForTestUrl(url) { if proxy.AliveForTestUrl(url) {
i++ availableProxies = append(availableProxies, proxy)
return proxy
} }
} }
// fallback
if len(availableProxies) == 0 {
return proxies[0] return proxies[0]
} }
}
proxy := availableProxies[idx]
// reset idx
idx = (idx + 1) % len(availableProxies)
return proxy
}
} }
func strategyConsistentHashing(url string) strategyFn { func strategyConsistentHashing(url string) strategyFn {

View File

@ -212,6 +212,7 @@ type RawDNS struct {
IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"`
UseHosts bool `yaml:"use-hosts" json:"use-hosts"` UseHosts bool `yaml:"use-hosts" json:"use-hosts"`
UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-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"` NameServer []string `yaml:"nameserver" json:"nameserver"`
Fallback []string `yaml:"fallback" json:"fallback"` Fallback []string `yaml:"fallback" json:"fallback"`
FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"`
@ -1039,7 +1040,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) {
return net.JoinHostPort(hostname, port), nil return net.JoinHostPort(hostname, port), nil
} }
func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) { func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) {
var nameservers []dns.NameServer var nameservers []dns.NameServer
for idx, server := range servers { for idx, server := range servers {
@ -1114,6 +1115,10 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
} }
if respectRules && len(proxyName) == 0 {
proxyName = dns.RespectRules
}
nameservers = append( nameservers = append(
nameservers, nameservers,
dns.NameServer{ dns.NameServer{
@ -1130,7 +1135,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
func init() { func init() {
dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard
return parseNameServer(servers, false) return parseNameServer(servers, false, false)
} }
} }
@ -1156,7 +1161,7 @@ func parsePureDNSServer(server string) string {
} }
} }
} }
func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { 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]() policy := orderedmap.New[string, []dns.NameServer]()
updatedPolicy := orderedmap.New[string, any]() updatedPolicy := orderedmap.New[string, any]()
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
@ -1202,7 +1207,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro
if err != nil { if err != nil {
return nil, err return nil, err
} }
nameservers, err := parseNameServer(servers, preferH3) nameservers, err := parseNameServer(servers, respectRules, preferH3)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1296,6 +1301,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") 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{ dnsCfg := &DNS{
Enable: cfg.Enable, Enable: cfg.Enable,
Listen: cfg.Listen, Listen: cfg.Listen,
@ -1310,26 +1319,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
}, },
} }
var err error var err error
if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.PreferH3); err != nil { if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.PreferH3); err != nil { if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil { if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, cfg.PreferH3); err != nil { if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
if len(cfg.DefaultNameserver) == 0 { if len(cfg.DefaultNameserver) == 0 {
return nil, errors.New("default nameserver should have at least one nameserver") return nil, errors.New("default nameserver should have at least one nameserver")
} }
if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, cfg.PreferH3); err != nil { if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
// check default nameserver is pure ip addr // check default nameserver is pure ip addr

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"net" "net"
"net/netip" "net/netip"
"strconv"
"strings" "strings"
"time" "time"
@ -19,6 +18,7 @@ import (
C "github.com/metacubex/mihomo/constant" C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel"
"github.com/metacubex/mihomo/tunnel/statistic"
D "github.com/miekg/dns" D "github.com/miekg/dns"
"github.com/samber/lo" "github.com/samber/lo"
@ -175,6 +175,8 @@ func msgToDomain(msg *D.Msg) string {
return "" return ""
} }
const RespectRules = "RULES"
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
@ -183,54 +185,76 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
opts = append(opts, dialer.WithResolver(r)) opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...) return dialer.DialContext(ctx, network, addr, opts...)
} else { } else {
host, port, err := net.SplitHostPort(addr) metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.INNER,
}
err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote
if err != nil { if err != nil {
return nil, err return nil, err
} }
uintPort, err := strconv.ParseUint(port, 10, 16) if !strings.Contains(network, "tcp") {
metadata.NetWork = C.UDP
if !metadata.Resolved() {
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
metadata.DstIP = dstIP
}
}
var rule C.Rule
if proxyAdapter == nil { if proxyAdapter == nil {
if proxyName == RespectRules {
if !metadata.Resolved() {
// resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil {
return nil, err
}
metadata.DstIP = dstIP
}
proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata)
if err != nil {
return nil, err
}
} else {
var ok bool var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName] proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok { if !ok {
opts = append(opts, dialer.WithInterface(proxyName)) opts = append(opts, dialer.WithInterface(proxyName))
} }
} }
}
if strings.Contains(network, "tcp") { if strings.Contains(network, "tcp") {
// tcp can resolve host by remote if proxyAdapter == nil {
metadata := &C.Metadata{
NetWork: C.TCP,
Host: host,
DstPort: uint16(uintPort),
}
if proxyAdapter != nil {
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
}
metadata.Host = ""
metadata.DstIP = dstIP
}
return proxyAdapter.DialContext(ctx, metadata, opts...)
}
opts = append(opts, dialer.WithResolver(r)) opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...) return dialer.DialContext(ctx, network, addr, opts...)
} else { }
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
if !metadata.Resolved() {
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
metadata := &C.Metadata{ metadata.DstIP = dstIP
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: uint16(uintPort),
} }
metadata.Host = "" // clear host to avoid double resolve in proxy
}
conn, err := proxyAdapter.DialContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false)
return conn, nil
} else {
if proxyAdapter == nil { if proxyAdapter == nil {
return dialer.DialContext(ctx, network, addr, opts...) return dialer.DialContext(ctx, network, addr, opts...)
} }
@ -244,6 +268,8 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
return nil, err return nil, err
} }
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
} }
} }
@ -251,33 +277,38 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string,
} }
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
host, port, err := net.SplitHostPort(addr) metadata := &C.Metadata{
NetWork: C.UDP,
}
err := metadata.SetRemoteAddress(addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
uintPort, err := strconv.ParseUint(port, 10, 16) if !metadata.Resolved() {
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
metadata.DstIP = dstIP
}
var rule C.Rule
if proxyAdapter == nil { if proxyAdapter == nil {
if proxyName == RespectRules {
proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata)
if err != nil {
return nil, err
}
} else {
var ok bool var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName] proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok { if !ok {
opts = append(opts, dialer.WithInterface(proxyName)) opts = append(opts, dialer.WithInterface(proxyName))
} }
} }
}
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
}
metadata := &C.Metadata{
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: uint16(uintPort),
}
if proxyAdapter == nil { if proxyAdapter == nil {
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
} }
@ -286,7 +317,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
} }
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...) packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)
return packetConn, nil
} }
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {

View File

@ -209,7 +209,7 @@ tunnels: # one line config
dns: dns:
cache-algorithm: arc cache-algorithm: arc
enable: false # 关闭将使用系统 DNS enable: false # 关闭将使用系统 DNS
prefer-h3: true # 开启 DoH 支持 HTTP/3将并发尝试 prefer-h3: false # 是否开启 DoH 支持 HTTP/3将并发尝试
listen: 0.0.0.0:53 # 开启 DNS 服务器监听 listen: 0.0.0.0:53 # 开启 DNS 服务器监听
# ipv6: false # false 将返回 AAAA 的空结果 # ipv6: false # false 将返回 AAAA 的空结果
# ipv6-timeout: 300 # 单位ms内部双栈并发时向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms # ipv6-timeout: 300 # 单位ms内部双栈并发时向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms
@ -227,6 +227,13 @@ dns:
# use-hosts: true # 查询 hosts # use-hosts: true # 查询 hosts
# 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则
# 如果为false默认值则这三部分的dns服务器在未特别指定的情况下会直连
# 如果为true将会按照rules的规则匹配链接方式走代理或直连如果有特别指定则任然以指定值为准
# 仅当proxy-server-nameserver非空时可以开启此选项, 强烈不建议和prefer-h3一起使用
# 此外这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析也请确保正确配置default-nameserver
respect-rules: false
# 配置不使用 fake-ip 的域名 # 配置不使用 fake-ip 的域名
# fake-ip-filter: # fake-ip-filter:
# - '*.lan' # - '*.lan'
@ -244,6 +251,7 @@ dns:
- https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3
- dhcp://en0 # dns from dhcp - dhcp://en0 # dns from dhcp
- quic://dns.adguard.com:784 # DNS over QUIC - quic://dns.adguard.com:784 # DNS over QUIC
# - '8.8.8.8#RULES' # 效果同respect-rules但仅对该服务器生效
# - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡 # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡
# 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN非必要配置 # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN非必要配置

View File

@ -278,7 +278,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
return nil return nil
} }
func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {
if metadata.SpecialProxy != "" { if metadata.SpecialProxy != "" {
var exist bool var exist bool
proxy, exist = proxies[metadata.SpecialProxy] proxy, exist = proxies[metadata.SpecialProxy]
@ -375,7 +375,7 @@ func handleUDPConn(packet C.PacketAdapter) {
cond.Broadcast() cond.Broadcast()
}() }()
proxy, rule, err := resolveMetadata(metadata) proxy, rule, err := ResolveMetadata(metadata)
if err != nil { if err != nil {
log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) log.Warnln("[UDP] Parse metadata failed: %s", err.Error())
return return
@ -486,7 +486,7 @@ func handleTCPConn(connCtx C.ConnContext) {
}() }()
} }
proxy, rule, err := resolveMetadata(metadata) proxy, rule, err := ResolveMetadata(metadata)
if err != nil { if err != nil {
log.Warnln("[Metadata] parse failed: %s", err.Error()) log.Warnln("[Metadata] parse failed: %s", err.Error())
return return