观点
- 代理的情况下,本地 DNS解析必要性很低,准确性要求不高,不论 fakeip 还是 redirhost 模式
- 本地 DNS 解析仅用于客户端发起连接使用
- 代理的情况下,不论fakeip 还是redirhost 模式,返回的 IP 都不会用于实际代理
- DNS 解析返回的 IP 只用于 IP 和域名映射,用于客户端发起连接后通过 DNS 解析时,事先保存 IP 和域名映射关系反推出域名,使用域名进行规则匹配,如果匹配到代理规则,会将域名发送至代理服务器(此处为强制发送域名,不可配置,不可跳过服务端DNS解析,而不是 IP,纯 IP 连接除外),由代理服务器重新进行 DNS 解析
- 假如使用了还是redirhost 模式,整个流程如下,fakeip 同理
- ①客户端发起 DNS 查询获取
google.com
的 IP 是1.1.1.1
,此时内核记录一个映射关系key=1.1.1.1, value=google.com
(此处涉及到一对一和多对一的问题,下文详细解释) - ②客户端发起 TCP/UDP 连接,内核根据之前保留的映射关系得知实际访问的域名是
google.com
- ③客户端使用
google.com
匹配规则,匹配到代理 - ④客户端向服务端发送代理请求,请求中包含被代理的域名
google.com
, - ⑤服务器使用本地 DNS 服务器进行解析,假如返回的 IP 是
2.2.2.2
,此时的2.2.2.2
才是客户端访问 Google 真正的地址,但是客户端实际上是看不到这个地址的
- ①客户端发起 DNS 查询获取
- 客户端多次解析,如果 DNS 服务器相同,第二次解析会使用缓存,此处使用缓存为内核强制,可以配置缓存算法,但是不能配置是否开启缓存
结论
这里提出一种我认为最优的 DNS 配置建议(redirhost 模式下,fakeip 还没研究明白) 以下两条只是为了防止直连的域名进行两次本地域名解析(虽然说二次的会走第一次的缓存,但是其实可以通过配置规避)
- 使用
nameserver-policy
事先分流所有直连的域名 - 不要配置
direct-nameserver
PS
文档存在一定误导性,因为 DNS 阶段不会匹配任何规则

DNS 解析流程
dns/middleware.go:221
func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
middlewares := []middleware{}
// 查询系统host文件
if resolver.hosts != nil {
middlewares = append(middlewares, withHosts(R.NewHosts(resolver.hosts), mapper.mapping))
}
// 查询FakeIP
if mapper.mode == C.DNSFakeIP {
middlewares = append(middlewares, withFakeIP(mapper.fakePool))
}
// 常规查询DNS
if mapper.mode != C.DNSNormal {
middlewares = append(middlewares, withMapping(mapper.mapping))
}
return compose(middlewares, withResolver(resolver))
}
FakeIP 模式查询
func withFakeIP(fakePool *fakeip.Pool) middleware {
return func(next handler) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
q := r.Question[0]
host := strings.TrimRight(q.Name, ".")
if fakePool.ShouldSkipped(host) { // 域名是否在fake-ip-filter中,如果是,查询实际IP
return next(ctx, r)
}
switch q.Qtype {
case D.TypeAAAA, D.TypeSVCB, D.TypeHTTPS:
return handleMsgWithEmptyAnswer(r), nil
}
if q.Qtype != D.TypeA {
return next(ctx, r)
}
rr := &D.A{}
rr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL}
ip := fakePool.Lookup(host) // 获取下一个fakeip(域名-IP)
rr.A = ip.AsSlice()
msg := r.Copy()
msg.Answer = []D.RR{rr}
ctx.SetType(context.DNSTypeFakeIP)
setMsgTTL(msg, 1)
msg.SetRcode(r, D.RcodeSuccess)
msg.Authoritative = true
msg.RecursionAvailable = true
return msg, nil
}
}
}

非 FakeIP 模式查询
func withResolver(resolver *Resolver) handler {
return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) {
ctx.SetType(context.DNSTypeRaw)
q := r.Question[0]
// return a empty AAAA msg when ipv6 disabled
if !resolver.ipv6 && q.Qtype == D.TypeAAAA {
return handleMsgWithEmptyAnswer(r), nil
}
// 实际执行DNS查询,规则匹配
msg, err := resolver.ExchangeContext(ctx, r)
if err != nil {
log.Debugln("[DNS Server] Exchange %s failed: %v", q.String(), err)
return msg, err
}
msg.SetRcode(r, msg.Rcode)
msg.Authoritative = true
return msg, nil
}
}
配置
这个配置一定存在 DNS 泄露,甚至没有配置任何国外的 DNS 服务器。但是,我个人会使用付费 DNS 解析服务,所以不在乎泄露
理由:
- 没有必要在客户端解析任何需要 PROXY 的 IP,因为结果会被丢弃,解析完全是浪费时间
- nameserver 使用国内 DNS,保证不在规则中的 DIRECT 域名也能获得 CDN 优化
DNS 泄露:首先定义下什么是 DNS 泄露,我认为,出现 DNS 解析结果与实际连接结果不符的时候,才算泄露,即如果 DNS 解析使用了国内的 DNS 服务器,但是实际连接的时候匹配到的规则是 PROXY
什么情况下会发生泄露:
- 访问不在 rule 中的网站
实际上,rule 中已经包含了大多数网站,剩下泄露的也是小众网站,也就是说,访问 google、youtube 的时候,DNS 服务提供商不会知道,但是访问某个从未被记录的小众网站时,会使用国内 DNS 进行解析
dns:
enable: true
cache-algorithm: arc
respect-rules: true
prefer-h3: false
use-system-hosts: true
ipv6: false
listen: "[::]:1053"
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
use-hosts: true
rebind: false
fake-ip-filter:
- "rule-set:violet-ruleset-direct"
- "geosite:connectivity-check"
- "geosite:private"
- "geosite:category-games"
- "geosite:category-game-platforms-download"
- "geosite:cn"
default-nameserver:
- 223.5.5.5
proxy-server-nameserver:
- 223.5.5.5
nameserver:
- 223.5.5.5
rules:
- GEOSITE,category-games,GAME
- GEOSITE,category-game-platforms-download,GAME
- GEOSITE,category-game-accelerator-cn,GAME
- GEOSITE,geolocation-!cn@cn,DIRECT
- GEOSITE,geolocation-!cn,PROXY
- GEOSITE,geolocation-cn@!cn,PROXY
- GEOSITE,geolocation-cn,DIRECT
- GEOSITE,tld-cn,DIRECT
- GEOSITE,category-dev,PROXY
- GEOIP,CN,DIRECT
- GEOIP,private,DIRECT
- MATCH,MATCH