DNS Golang  
		2025-01-18
		
- https://github.com/mr-karan/doggo
 
- https://doggo.mrkaran.dev/docs/
 
安装
$ go install github.com/mr-karan/doggo/cmd/doggo@latest
$ doggo
NAME:
  doggo 🐶 DNS Client for Humans
USAGE:
  doggo [--] [query options] [arguments...]
VERSION:
  unknown - unknown
EXAMPLES:
  doggo mrkaran.dev                             Query a domain using defaults.
  doggo mrkaran.dev CNAME                       Query for a CNAME record.
  doggo mrkaran.dev MX @9.9.9.9                 Uses a custom DNS resolver.
  doggo -q mrkaran.dev -t MX -n 1.1.1.1         Using named arguments.
  doggo mrkaran.dev --aa --ad                   Query with Authoritative Answer and Authenticated Data flags set.
  doggo mrkaran.dev --cd --do                   Query with Checking Disabled and DNSSEC OK flags set.
  doggo mrkaran.dev --gp-from Germany           Query using Globalping API from a specific location.
FREE FORM ARGUMENTS:
  Supply hostnames, query types, and classes without flags. Example:
  doggo mrkaran.dev A @1.1.1.1
TRANSPORT OPTIONS:
  Specify the protocol with a URL-type scheme.
  UDP is used if no scheme is specified.
  @udp://     eg: @1.1.1.1                                                        initiates a UDP query to 1.1.1.1:53.
  @tcp://     eg: @tcp://1.1.1.1                                                  initiates a TCP query to 1.1.1.1:53.
  @https://   eg: @https://cloudflare-dns.com/dns-query                           initiates a DOH query to Cloudflare via DoH.
  @tls://     eg: @tls://1.1.1.1                                                  initiates a DoT query to 1.1.1.1:853.
  @sdns://    initiates a DNSCrypt or DoH query using a DNS stamp.
  @quic://    initiates a DOQ query.
SUBCOMMANDS:
  completions [bash|zsh|fish]   Generate the shell completion script for the specified shell.
QUERY OPTIONS:
  -q, --query=HOSTNAME          Hostname to query the DNS records for (eg mrkaran.dev).
  -t, --type=TYPE               Type of the DNS Record (A, MX, NS etc).
  -n, --nameserver=ADDR         Address of a specific nameserver to send queries to (9.9.9.9, 8.8.8.8 etc).
  -c, --class=CLASS             Network class of the DNS record (IN, CH, HS etc).
  -x, --reverse                 Performs a DNS Lookup for an IPv4 or IPv6 address. Sets the query type and class to PTR and IN respectively.
  --any                         Query all supported DNS record types (A, AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT, CAA).
RESOLVER OPTIONS:
  --strategy=STRATEGY           Specify strategy to query nameserver listed in etc/resolv.conf. (all, random, first).
  --ndots=INT                   Specify ndots parameter. Takes value from /etc/resolv.conf if using the system namesever or 1 otherwise.
  --search                      Use the search list defined in resolv.conf. Defaults to true. Set --search=false to disable search list.
  --timeout=DURATION            Specify timeout for the resolver to return a response (e.g., 5s, 400ms, 1m).
  -4, --ipv4                    Use IPv4 only.
  -6, --ipv6                    Use IPv6 only.
  --tls-hostname=HOSTNAME       Provide a hostname for verification of the certificate if the provided DoT nameserver is an IP.
  --skip-hostname-verification  Skip TLS Hostname Verification in case of DOT Lookups.
QUERY FLAGS:
  --aa                          Set Authoritative Answer flag.
  --ad                          Set Authenticated Data flag.
  --cd                          Set Checking Disabled flag.
  --rd                          Set Recursion Desired flag (default: true).
  --z                           Set Z flag (reserved for future use).
  --do                          Set DNSSEC OK flag.
OUTPUT OPTIONS:
  -J, --json                    Format the output as JSON.
  --short                       Short output format. Shows only the response section.
  --color                       Defaults to true. Set --color=false to disable colored output.
  --debug                       Enable debug logging.
  --time                        Shows how long the response took from the server.
GLOBALPING OPTIONS:
  --gp-from=Germany             Query using Globalping API from a specific location.
  --gp-limit=INT                Limit the number of probes to use from Globalping.
DNS 查询
$ doggo sendcloud.net a @223.5.5.5
NAME            TYPE    CLASS   TTL     ADDRESS         NAMESERVER
sendcloud.net.  A       IN      60s     106.75.106.173  223.5.5.5:53
sendcloud.net.  A       IN      60s     106.75.106.166  223.5.5.5:53
$ doggo sendcloud.net a @223.5.5.5 --json
{
  "responses": [
    {
      "answers": [
        {
          "name": "sendcloud.net.",
          "type": "A",
          "class": "IN",
          "ttl": "60s",
          "address": "106.75.106.173",
          "status": "",
          "rtt": "67ms",
          "nameserver": "223.5.5.5:53"
        },
        {
          "name": "sendcloud.net.",
          "type": "A",
          "class": "IN",
          "ttl": "60s",
          "address": "106.75.106.166",
          "status": "",
          "rtt": "67ms",
          "nameserver": "223.5.5.5:53"
        }
      ],
      "authorities": null,
      "questions": [
        {
          "name": "sendcloud.net.",
          "type": "A",
          "class": "IN"
        }
      ]
    }
  ]
}
$ doggo sendcloud.net a @223.5.5.5 --json | jq ".responses[].answers[].address"
"106.75.106.166"
"106.75.106.173"
查反解
$ doggo --reverse 101.44.172.1 @223.5.5.5
NAME                            TYPE    CLASS   TTL     ADDRESS                         NAMESERVER
1.172.44.101.in-addr.arpa.      PTR     IN      300s    hwsg1c1.email.engagelab.com.    223.5.5.5:53
$ doggo hwsg1c1.email.engagelab.com. a @223.5.5.5
NAME                            TYPE    CLASS   TTL     ADDRESS         NAMESERVER
hwsg1c1.email.engagelab.com.    A       IN      600s    101.44.172.1    223.5.5.5:53
Global Ping
$ doggo markjour.com --gp-from Germany,Japan --gp-limit 2
LOCATION                        NAME            TYPE    CLASS   TTL     ADDRESS         NAMESERVER
Falkenstein, DE, EU, Hetzner
Online GmbH (AS24940)
                                markjour.com.   A       IN      600s    121.42.82.115   private
Osaka, JP, AS, Oracle
Corporation (AS31898)
                                markjour.com.   A       IN      600s    121.42.82.115   8.8.8.8
	
	
	
		
		 DNS  
		2024-04-29
		天涯社区将回归!创始人:明天,全面恢复业务!此前被申请破产审查,官网仍“无法访问”
说是 5/1 前回归,我刚查了一下,天涯现在还打不开。搜索一下域名解析的时候,发现了一个 HINFO 记录,这我还是第一次看到。
	
	
	
		
		 Golang DNS  
		2022-10-30
		A 记录
package main
import (
    "fmt"
    "net"
)
func main() {
    // 方法 1
    // func ResolveIPAddr(network, address string) (*IPAddr, error)
    ipAddr, err := net.ResolveIPAddr("ip", "www.google.com")
    if err != nil {
        fmt.Println("解析IP地址失败:", err)
    } else {
        fmt.Println("IP地址是:", ipAddr.IP)
    }
    // 方法 2
    // func LookupMX(name string) ([]*MX, error)
    // func LookupTXT(name string) ([]string, error)
    // func LookupIP(host string) ([]IP, error)
    // func LookupHost(host string) ([]string, error) // only IPv4
    ips, err := net.LookupHost("www.google.com")
    if err != nil {
        fmt.Println("解析主机名失败:", err)
    } else {
        for _, ip := range ips {
            fmt.Println("IP地址是:", ip)
        }
    }
}
MX 记录
package main
import (
    "fmt"
    "net"
)
func main() {
    records, err := net.LookupMX("qq.com")
    if err != nil {
        fmt.Println("解析MX记录失败:", err)
    } else {
        fmt.Printf("%#v", records)
    }
}
指定 DNS 服务器
func queryMX(dns string, domain string) ([]*net.MX, error) {
    resolver := &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            dialer := &net.Dialer{}
            return dialer.DialContext(ctx, "udp", dns)
        },
    }
    return resolver.LookupMX(context.Background(), domain)
}
完整版本
package main
import (
    "context"
    "fmt"
    "net"
    "strings"
    "time"
)
var dnsServers = []string{
    "223.5.5.5:53",
    "114.114.114.114:53",
    "8.8.8.8:53",
}
type IPType int
const (
    NotIP IPType = iota
    IPv4
    IPv6
)
const (
    DNSLookupTimeout = 500 * time.Millisecond // time.Duration
)
func GetIPType(host string) IPType {
    ip := net.ParseIP(host)
    if ip == nil {
        return NotIP
    }
    if ip.To4() != nil {
        return IPv4
    }
    if ip.To16() != nil {
        return IPv6
    }
    return NotIP
}
type Resolver struct {
    DNS      string
    Resolver *net.Resolver
}
func NewResolver(dns string) Resolver {
    resolver := Resolver{}
    resolver.DNS = dns
    resolver.Resolver = &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            dialer := &net.Dialer{}
            return dialer.DialContext(ctx, "udp", dns)
        },
    }
    return resolver
}
func (r Resolver) QueryA(domain string) ([]string, error) {
    return r.Resolver.LookupHost(context.Background(), domain)
}
func (r Resolver) QueryMX(domain string) ([]*net.MX, error) {
    ctx, cancel := context.WithTimeout(context.Background(), DNSLookupTimeout)
    defer cancel()
    return r.Resolver.LookupMX(ctx, domain)
}
func (r Resolver) Query(domain string) ([]*net.MX, error) {
    addrs, err := r.QueryMX(domain)
    if err != nil {
        return nil, err
    }
    var result []*net.MX
    for _, addr := range addrs {
        // hostname := strings.TrimRight(addr.Host, ".")
        ipType := GetIPType(addr.Host)
        fmt.Printf("%s: %#v\n", addr.Host, ipType)
        if ipType == NotIP {
            ips, err := r.QueryA(addr.Host)
            if err != nil {
                continue
            }
            for _, ip := range ips {
                result = append(result, &net.MX{Host: ip, Pref: addr.Pref})
            }
        }
    }
    return result, nil
}
var resolvers map[string]Resolver
func Query(domain string) ([]*net.MX, error) {
    var addrs []*net.MX
    var err error
    for _, dns := range dnsServers {
        var r Resolver
        if r, ok := resolvers[dns]; !ok {
            r = NewResolver(dns)
            resolvers[dns] = r
        }
        addrs, err = r.Query(domain)
        if err == nil {
            break
        }
        fmt.Printf("Error: %s: %s: %#v\n", dns, domain, err)
    }
    return addrs, err
}
func QueryAsync(domain string, ch chan<- []*net.MX, errCh chan<- error) {
    addrs, err := Query(domain)
    if err != nil {
        errCh <- err
        return
    }
    ch <- addrs
}
func init() {
    resolvers = make(map[string]Resolver)
}
func main() {
    {
        domains := []string{"qq.com", "gmail.com", "google.com"}
        for _, domain := range domains {
            fmt.Println(strings.Repeat("=", 100))
            addrs, err := Query(domain)
            if err != nil {
                fmt.Printf("Error: %#v\n", err)
            } else {
                for _, addr := range addrs {
                    fmt.Printf("%#v\n", addr)
                }
            }
        }
    }
    {
        fmt.Println(strings.Repeat("=", 100))
        ch := make(chan []*net.MX)
        errCh := make(chan error)
        go QueryAsync("google.com", ch, errCh)
        select {
        case addrs := <-ch:
            fmt.Println("MX Records:")
            for _, addr := range addrs {
                fmt.Printf("%#v\n", addr)
            }
        case err := <-errCh:
            fmt.Println("Error:", err)
        }
    }
}
	
	
	
		
		 DNS  
		2021-12-17
		:) 本文正在编辑中,暂时不提供浏览...
	
	
	
		
		 DNS 负载均衡  
		2021-12-17
		:) 本文正在编辑中,暂时不提供浏览...
	
	
	
		
		 Linux Curl dig 开发工具 DNS  
		2021-12-17
		:) 本文正在编辑中,暂时不提供浏览...
	
	
	
		
		 WebDev DNS  
		2021-12-14
		
- 浏览器突然打不开 zhihu.com, 报 
DNS_PROBE_FINISHED_NXDOMAIN。 
- Windows 网络诊断之后说是 DNS 不可用。
 
- 经过检查,使用 DHCP 获取到的 DNS 
172.16.0.1。 
- 改成 AliDNS: 
223.5.5.5, 223.6.6.6 之后就好了。 
- 然后再改回默认的 DNS 发现也能正常访问了。
 
我应该在出现问题的时候先尝试 nslookup 一下,看看 DNS 解析出来的到底是个什么结果。
下次遇到再继续更新。
C:\Users\Administrator>ipconfig /all | findstr DNS
   主 DNS 后缀 . . . . . . . . . . . :
   连接特定的 DNS 后缀 . . . . . . . :
   DNS 服务器  . . . . . . . . . . . : 172.16.0.1
   连接特定的 DNS 后缀 . . . . . . . :
   DNS 服务器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
   连接特定的 DNS 后缀 . . . . . . . :
   连接特定的 DNS 后缀 . . . . . . . :
   连接特定的 DNS 后缀 . . . . . . . :
   连接特定的 DNS 后缀 . . . . . . . :
C:\Users\Administrator>nslookup zhihu.com
服务器:  UnKnown
Address:  172.16.0.1
非权威应答:
名称:    zhihu.com
Address:  103.41.167.234
	
	
	
		
		 DNS  
		2019-07-22
		域名
Class rdataclass
RESERVED0 = 0
IN = 1
INTERNET = 1
CH = 3
CHAOS = 3
HESIOD = 4
HS = 4
NONE = 254
ANY = 255
Type rdatatype
A IPv4 Address 
AAAA IPv4 Address 
CNAME Canonical Name,“规范名称”,其实就是别名,指向另一个域名。MX 记录和 NS 记录不能指向一个 CNAME 记录。 
NS Name Server,权威 DNS 服务器位置。 
MX Mail Exchange,邮件服务位置。 
SRV 通用的服务类型记录,取代 MX 这种专用记录。 
_xmpp-client._tcp.example.net. 86400 IN SRV 5        0      5222 example.net.
_xmpp-server._tcp.example.net. 86400 IN SRV 5        0      5269 example.net.
#                              TTL          Priority Weight Port
TXT 
SOA Start Of Authority,权威记录起始 
PTR 反解记录 
反解记录
记录名称:IP 翻转过来,加上 .in-addr.arpa 后缀
记录类型:PTR
dig -x 106.75.80.125
dig +noall +answer -tPTR 125.80.75.106.in-addr.arpa
完整的 DNS 解析过程
- 找到权威服务器
 
Windows
nslookup
ipconfig /displaydns
ipconfig /flushdns
Linux
dig markjour.com A
dig @8.8.8.8 markjour.com
dig qq.com MX
dig MX qq.com
dig -t MX qq.com
dig +noall +answer qq.com
dig +short qq.com
dig qq.com ANY
dig +trace +nssearch markjour.com
DNS 请求和响应
| Field | 
Bits | 
Desc | 
| QR | 
1 | 
query 0, reply 1 | 
| OPCODE | 
4 | 
QUERY 0, IQUERY 1, STATUS 2 | 
| AA | 
1 | 
Authoritative Answer | 
| TC | 
1 | 
TrunCation | 
| RD | 
1 | 
Recursion Desired | 
| RA | 
1 | 
Recursion Available | 
| Z | 
3 | 
Zero 填零,保留 | 
| RCODE | 
4 | 
Response Code | 
RCODE:
- NOERROR (0)
 
- FORMERR (1, Format error)
 
- SERVFAIL (2)
 
- 
NXDOMAIN (3, Nonexistent domain)
 
- 
body
 
- 
question
| Field | 
Bits | 
Desc | 
| NAME | 
可变 | 
资源名称 | 
| TYPE | 
2 | 
记录类型 | 
| CLASS | 
2 | 
Class Code | 
CLASS Code rdata.class:
RESERVED0 = 0
IN = 1
INTERNET = 1
CH = 3
CHAOS = 3
HESIOD = 4
HS = 4
NONE = 254
ANY = 255
 
- 
answer
 
- authority
 
- additional space
 
请求
响应
参考资料与拓展阅读
	
	
	
		
		 架构 DNS  
		2018-05-02
		历史
从阿帕网 (ARPANET) 时代一直到互联网的早期,网络节点比较少,都是通过本地 hosts 文件来实现主机名到 IP 地址的映射。
根据维基百科的信息,斯坦福研究所负责维护了一个公共 hosts 文件,大家会找他同步 (rfc606, rfc608)。
PS: 这个时候如果有主机名重复了谁来管?打电话过去让他们改名?
这套机制一直运行了十几年,公共 hosts 文件已经变的很大了,变化也很频繁(IP 可能已经不再那么固定了),需要经常同步。这个时候,斯坦福研究所的网络压力也越来越大了。
后来人们开始设计域名和域名相关的公共设施 (rfc805, rfc830)。最后,在 1983 年,形成了下面两个 RFC 文档:
- RFC 882, DOMAIN NAMES - CONCEPTS and FACILITIES
 
- RFC 883, DOMAIN NAMES - IMPLEMENTATION and SPECIFICATION
 
几年后(1987),正式的 DNS 标准 RFC 1034 和 RFC 1035 推出。
这套标准一直运行到现在,可能有对其进行拓展(比如 DNS 记录类型不断添加,Unicode 字符引入),但是基本技术设计没有改变。
DNS 的管理权问题
https://zhidao.baidu.com/question/1386069665602139980.html
基本流程
比如本站域名 www.markjour.com, 其完整形式应该是 www.markjour.com. (后面多一个小数点)
DNS 软件
- BIND
 
- PowerDNS
 
- dnsmasq
 
- Unbound
 
- CoreDNS
 
- SmartDNS
 
Cache-Only DNS Server
新的发展
- 标准的 DNS 是运行在 UDP 53 端口上的。后来的 RFC 1123 增加了 TCP 的支持, 这个方案叫做 DNS over TCP, 还是在 53 端口。
 
- DNSCrypt, 2011 年设计的, 实现 DNS 的加密和验证,运行于 443 端口。注意:存在于 IETF 框架之外,但是好像有很多服务器支持。
 
- DNS over TLS (DoT), 2016 年 5 月成为规范。
   RFC 7858 Specification for DNS over Transport Layer Security (TLS)
   主要作用是加密传输,防止窃听。 
- DNS over HTTPS (DoH), 2018 年 10 月成为规范。
   RFC 8484 DNS Queries over HTTPS (DoH)
   作用和 DoT 一样。 
- DNS over TOR, 2019 年。
 
- Oblivious DNS-over-HTTPS (ODoH), 透过代理的方式,让 DoH 服务器无法获取客户端的真实 IP。同时代理无法获取 DNS 请求的内容。
 
参考资料与拓展阅读
	
	
	
		
		 DNS  
		2016-06-06
		$ curl -v "http://成都大运会.网址"
* Input domain encoded as `UTF-8'
* About to connect() to xn--6oqv8vrnhtp3c7hb.xn--ses554g port 80 (#0)
*   Trying 202.173.11.233... connected
* Connected to 成都大运会.网址 (202.173.11.233) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: xn--6oqv8vrnhtp3c7hb.xn--ses554g
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: openresty/1.21.4.3
< Date: Sat, 16 Dec 2023 06:09:33 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 61
< Connection: keep-alive
< Location: http://www.2021chengdu.com
<
<a href="http://www.2021chengdu.com">Moved Permanently</a>.
* Connection #0 to host 成都大运会.网址 left intact
* Closing connection #0
中文域名,中文顶级域名都已经支持很多年了,虽然看到不多。
上面示例中的域名 成都大运会.网址 实际上在域名系统中是以 xn--6oqv8vrnhtp3c7hb.xn--ses554g 形式存在的。
这种编码方式叫做 Punycode,非 ASCII 字符会被按照 Unicode 编号转换成 ASCII 字符。
国际化域名
域名系统中允许的字符集基于 ASCII,不允许以母语或字母表示多种语言的名称和单词。
ICANN 批准了国际化域名(IDNA)系统,该系统通过一种称为 Punycode 的编码将应用程序用户界面中使用的 Unicode 字符串映射到有效的 DNS 字符集。
Example of Greek IDN with domain name in non-Latin alphabet: ουτοπία.δπθ.gr (Punycode is xn--kxae4bafwg.xn--pxaix.gr)
Punycode
'I❤️U'.encode('punycode')
b'IU-ony8085h'
'baidu.com'.encode('idna').decode()
# baidu.com
'中国.com'.encode('idna').decode()
# 'xn--fiqs8s.com'
'编程.中国'.encode('idna').decode()
# 'xn--9nz56h.xn--fiqs8s'
先提取 ASCII 字符,再编码非 ASCII 字符。