我的DNS配置技巧

25 年 7 月 26 日 星期六 (已编辑)
1211 字
7 分钟

观点

  • 代理的情况下,本地 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 配置建议(redirhost 模式下,fakeip 还没研究明白) 以下两条只是为了防止直连的域名进行两次本地域名解析(虽然说二次的会走第一次的缓存,但是其实可以通过配置规避)

  • 使用 nameserver-policy 事先分流所有直连的域名
  • 不要配置 direct-nameserver

PS

文档存在一定误导性,因为 DNS 阶段不会匹配任何规则

Pasted image 20250726105758.png

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  
       }  
    }  
}  
Pasted image 20250726155106.png

非 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

文章标题:我的DNS配置技巧

文章作者:violet

文章链接:https://www.vio.vin/posts/wo-de-dns-pei-zhi-ji-qiao[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。