Background: In the actual use of the Internet, it is well known that the domain name is used to access a service directly, but with the continuous optimization of the Internet business structure, it may be a simple step for users to access a domain name to obtain related resources, but in fact, for the entire request process of the Internet is to doMany calls, and the first step is dns resolution.Of course, there are many tools for dns resolution in linux environment, such as dig and nslookup, but it is obviously unrealistic to directly investigate complex problems on the machine, so we intend to use golang's interface to encapsulate the domain name resolution service to provide later operation.
1. Use of net packages
Structural Methods Related to dns
# nameserver structure type NS struct { Host string } # The srv record specifies which DNS server resolves the domain name type SRV struct { Target string Port uint16 Priority uint16 Weight uint16 } # dns forward resolution (domain name resolved to cname or actual ip address) ## Return only the cname address of the specified domain name func LookupCNAME(name string) (cname string, err error) ## Return directly to the domain name to resolve to the address. func LookupHost(host string) (addrs []string, err error) ## Return the domain name directly to the address, [] ip structure. You can do related operations on specific IPS (whether loopback address, subnet, network number, etc.) # type IP []byte func LookupIP(host string) (addrs []IP, err error) ## DNS Reverse Resolution (ip Address Reverse Resolution Lookup Resolved Domain Name) # Find hostname address based on ip address (must be resolvable domain name) [dig-x ipaddress] func LookupAddr(addr string) (name []string, err error)
dns parsing query using net package
$ cat dns-test.go package main import ( "net" "fmt" "os" ) func main() { dns := "xxbandy.github.io" // Resolve cname cname,_ := net.LookupCNAME(dns) // Resolve ip address ns, err := net.LookupHost(dns) if err != nil { fmt.Fprintf(os.Stderr, "Err: %s", err.Error()) return } // Reverse Resolution (Host must be able to resolve to address) dnsname,_ := net.LookupAddr("127.0.0.1") fmt.Println("hostname:",dnsname) // Control and Judgment of Domain Name Resolution // Some domain names usually resolve to an individual name using cname before resolving to the actual ip address switch { case cname != "": fmt.Println("cname:",cname) if len(ns) != 0 { fmt.Println("vips:") for _, n := range ns { fmt.Fprintf(os.Stdout, "%s\n", n) } } case len(ns) != 0: for _, n := range ns { fmt.Fprintf(os.Stdout, "%s\n", n) } default: fmt.Println(cname,ns) } } # Output host name for local 127.0.0.1 # The cname domain name of xxbandy.github.io and the actual resolved ip address $ go run dns-test.go hostname: [localhost] cname: xxbandy.github.io. vips: 185.199.110.153 185.199.111.153 185.199.109.153 185.199.108.153
2. Analyzing dns parsing process and system calls
Note: You can use dig +trace to track domain name resolution in a linux Environment
We all know that in the world of computers, connections are made on a five-tuple basis (source ip, source port, destination ip, destination port, protocol). While in real-world use, browsers help us identify and manage source IP and ports and protocols (http,https). When a protocol is determined, its destination port is also determined (80).Or 443). Therefore, the problem that the whole DNS system needs to solve is to convert the domain name that the user enters in the browser into a recognizable destination ip, and then connect to communicate.Here is a simple example of the DNS resolution process.
$ cat dns-test2.go package main import ( "net" "fmt" "os" ) func main() { dns := "xxbandy.github.io" ns, err := net.LookupHost(dns) if err != nil { fmt.Fprintf(os.Stderr, "Err: %s", err.Error()) return } fmt.Println(ns) } # Build program $ go build -o dns-test dns-test2.go # Run the program bash-4.1# ./dns-test [185.199.110.153 185.199.111.153 185.199.109.153 185.199.108.153] # Use the linux system tool trace to analyze system calls for the entire dns parsing process # strace ./dns-test ## Start executing program execve("./dns-test", ["./dns-test"], [/* 24 vars */]) = 0 brk(0) = 0x5ab000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6da42000 ## Start accessing system related libraries access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=35815, ...}) = 0 mmap(NULL, 35815, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5c6da39000 close(3) = 0 open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220]\0\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=805909, ...}) = 0 mmap(NULL, 2212768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5c6d605000 mprotect(0x7f5c6d61d000, 2093056, PROT_NONE) = 0 mmap(0x7f5c6d81c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7f5c6d81c000 mmap(0x7f5c6d81e000, 13216, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d81e000 close(3) = 0 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\371\1\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=10163394, ...}) = 0 mmap(NULL, 3861032, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f5c6d256000 mprotect(0x7f5c6d3fc000, 2093056, PROT_NONE) = 0 mmap(0x7f5c6d5fb000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a5000) = 0x7f5c6d5fb000 mmap(0x7f5c6d601000, 14888, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d601000 close(3) = 0 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6da38000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6da37000 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6da36000 arch_prctl(ARCH_SET_FS, 0x7f5c6da37700) = 0 mprotect(0x7f5c6d5fb000, 16384, PROT_READ) = 0 mprotect(0x7f5c6d81c000, 4096, PROT_READ) = 0 mprotect(0x7f5c6da43000, 4096, PROT_READ) = 0 munmap(0x7f5c6da39000, 35815) = 0 set_tid_address(0x7f5c6da379d0) = 31369 set_robust_list(0x7f5c6da379e0, 0x18) = 0 futex(0x7fffb36496ec, FUTEX_WAKE_PRIVATE, 1) = 0 futex(0x7fffb36496ec, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 7f5c6da37700) = -1 EAGAIN (Resource temporarily unavailable) rt_sigaction(SIGRTMIN, {0x7f5c6d60ac10, [], SA_RESTORER|SA_SIGINFO, 0x7f5c6d614520}, NULL, 8) = 0 rt_sigaction(SIGRT_1, {0x7f5c6d60aca0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7f5c6d614520}, NULL, 8) = 0 rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0 getrlimit(RLIMIT_STACK, {rlim_cur=10240*1024, rlim_max=RLIM_INFINITY}) = 0 brk(0) = 0x5ab000 brk(0x5cc000) = 0x5cc000 sched_getaffinity(0, 8192, { f, 0 }) = 16 mmap(0xc000000000, 65536, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000 munmap(0xc000000000, 65536) = 0 mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d9f6000 mmap(0xc420000000, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc420000000 mmap(0xc41fff8000, 32768, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc41fff8000 mmap(0xc000000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000 mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d9e6000 clock_gettime(CLOCK_MONOTONIC, {4672175, 535562591}) = 0 mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d9d6000 clock_gettime(CLOCK_MONOTONIC, {4672175, 535651726}) = 0 clock_gettime(CLOCK_MONOTONIC, {4672175, 535669542}) = 0 rt_sigprocmask(SIG_SETMASK, NULL, [], 8) = 0 clock_gettime(CLOCK_MONOTONIC, {4672175, 535849510}) = 0 clock_gettime(CLOCK_MONOTONIC, {4672175, 535878262}) = 0 sigaltstack(NULL, {ss_sp=0, ss_flags=SS_DISABLE, ss_size=0}) = 0 sigaltstack({ss_sp=0xc420002000, ss_flags=0, ss_size=32672}, NULL) = 0 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 gettid() = 31369 ## Report some real-time signals rt_sigaction(SIGHUP, NULL, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGHUP, {0x4555b0, ~[], SA_RESTORER|SA_STACK|SA_RESTART|SA_SIGINFO, 0x7f5c6d614520}, NULL, 8) = 0 rt_sigaction(SIGINT, NULL, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGINT, {0x4555b0, ~[], SA_RESTORER|SA_STACK|SA_RESTART|SA_SIGINFO, 0x7f5c6d614520}, NULL, 8) = 0 rt_sigaction(SIGQUIT, NULL, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGRT_32, {0x4555b0, ~[], SA_RESTORER|SA_STACK|SA_RESTART|SA_SIGINFO, 0x7f5c6d614520}, NULL, 8) = 0 ..... ..... clock_gettime(CLOCK_MONOTONIC, {4672175, 560948747}) = 0 rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0 mmap(NULL, 10489856, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5c6c855000 mprotect(0x7f5c6c855000, 4096, PROT_NONE) = 0 clone(child_stack=0x7f5c6d254ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f5c6d2559d0, tls=0x7f5c6d255700, child_tidptr=0x7f5c6d2559d0) = 31370 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0 mmap(NULL, 10489856, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5c6be54000 mprotect(0x7f5c6be54000, 4096, PROT_NONE) = 0 clone(child_stack=0x7f5c6c853ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f5c6c8549d0, tls=0x7f5c6c854700, child_tidptr=0x7f5c6c8549d0) = 31371 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0 mmap(NULL, 10489856, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f5c6b453000 mprotect(0x7f5c6b453000, 4096, PROT_NONE) = 0 clone(child_stack=0x7f5c6be52ff0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f5c6be539d0, tls=0x7f5c6be53700, child_tidptr=0x7f5c6be539d0) = 31372 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 ## Fast Synchronization futex(0xc420060110, FUTEX_WAKE, 1) = 1 futex(0xc42002cd10, FUTEX_WAKE, 1) = 1 futex(0x58bd50, FUTEX_WAIT, 0, NULL) = -1 EAGAIN (Resource temporarily unavailable) ## Reader Execution Command readlinkat(AT_FDCWD, "/proc/self/exe", "/root/dns-test", 128) = 14 futex(0xc420060110, FUTEX_WAKE, 1) = 1 mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5c6d996000 ## Read system configuration (maximum listening queue length for port: TCP backlog) openat(AT_FDCWD, "/proc/sys/net/core/somaxconn", O_RDONLY|O_CLOEXEC) = 3 read(3, "65535\n", 4096) = 6 read(3, "", 4090) = 0 close(3) = 0 ## Start creating socket s for invocation socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 close(3) = 0 socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP) = 3 setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [1], 4) = 0 bind(3, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP) = 4 setsockopt(4, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0 bind(4, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 close(4) = 0 close(3) = 0 ## Read network-related configuration (Name Service Switch configuration) ## Read hosts: files dns (that is, get the domain name and host address from the / etc/hosts file or use the dns service) openat(AT_FDCWD, "/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3 read(3, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 1024) = 1024 read(3, "w: files\ngroup: files\n\n"..., 1024) = 664 read(3, "", 1024) = 0 close(3) = 0 ## Read/etc/resolv.conf to get dnsserver openat(AT_FDCWD, "/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=176, ...}) = 0 read(3, "search 10.13.6.2"..., 4096) = 176 read(3, "", 3920) = 0 read(3, "", 4096) = 0 close(3) = 0 ## Check and read/etc/hosts file for parsing records stat("/etc/hosts", {st_mode=S_IFREG|0644, st_size=517, ...}) = 0 openat(AT_FDCWD, "/etc/hosts", O_RDONLY|O_CLOEXEC) = 3 read(3, "127.0.0.1 localhost localhost."..., 4096) = 517 read(3, "", 3579) = 0 read(3, "", 4096) = 0 close(3) = 0 .... .... ## Start building a create link to request dns socket(PF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5 setsockopt(5, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 ## Link to local dnsserver(/etc/resolv.conf) connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.13.6.2")}, 16) = 0 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT|EPOLLET|0x2000, {u32=1838509632, u64=140034952228416}}) = 0 getsockname(5, {sa_family=AF_INET, sin_port=htons(38024), sin_addr=inet_addr("10.13.15.218")}, [16]) = 0 getpeername(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.13.6.2")}, [16]) = 0 clock_gettime(CLOCK_REALTIME, {1555730258, 381131545}) = 0 clock_gettime(CLOCK_MONOTONIC, {4672175, 563905109}) = 0 clock_gettime(CLOCK_MONOTONIC, {4672175, 563982851}) = 0 futex(0x58b3d8, FUTEX_WAKE, 1) = 1 futex(0x58b3c0, FUTEX_WAKE, 1) = 1 clock_gettime(CLOCK_REALTIME, {1555730258, 381309493}) = 0 ## Write xxbandy.github.io(tcp data transfer) to link write(5, "\20z\1\0\0\1\0\0\0\0\0\0\7xxbandy\6github\2io\0\0"..., 35) = 35 read(5, 0xc4200aa000, 512) = -1 EAGAIN (Resource temporarily unavailable) futex(0x58bd50, FUTEX_WAIT, 0, NULL) = 0 epoll_wait(4, {}, 128, 0) = 0 ## Get addresses parsed from dnsserver epoll_wait(4, [185.199.111.153 185.199.110.153 185.199.108.153 185.199.109.153] <unfinished ... exit status 0>
As with the whole system call process above, you can basically re-validate the entire DNS query process:
- 1. Check the local hosts file for parsing records and return the parsing address if it exists
- 2. Non-existent initiates recursive queries based on dnsserver read from resolv.conf
- 3.dnsserver continuously initiates iterative queries to superior dnsserver
- 4.dnsserver eventually returns the query results to the requester
In fact, throughout the above analysis process, you can also try to validate the entire query process by modifying the / etc/hosts,/etc/resolv.conf configuration file, which will not be repeated here. Welcome to my Public Number