# 12.6 实现DNS查询

DNS全称**Domain Name System（域名系统）**，它的作用是将IP地址转换为类似`packt.com`的域名，或者将域名转换为IP地址。本节中开发的`DNS.go`程序的处理逻辑非常简单：如果程序执行时的命令行参数是一个有效的IP地址，则程序将查询该IP地址对应的主机名；其他情况下，程序将假定它处理的是一个主机名，并将其转换成一个或多个IP地址。

程序`DNS.go`的代码将分三部分介绍。第一部分程序包含以下Go代码：

> ```go
> package main
>
> import (
>     "fmt"
>     "net"
>     "os"
> )
>
> func lookIP(address string) ([]string, error) {
>     hosts, err := net.LookupAddr(address)
>     if err != nil {
>         return nil, err
>     }
>     return hosts, nil
> }
>
> func lookHostname(hostname string) ([]string, error) {
>     IPs, err := net.LookupHost(hostname)
>     if err != nil {
>         return nil, err
>     }
>     return IPs, nil
> }
> ```

函数`lookIP()`将一个IP地址作为输入，然后返回与该IP地址匹配的主机列表，这个功能通过函数`net.LookupAddr()`的帮助来实现。

而函数`lookHostname()`将主机名作为输入，使用`net.LookupHost()`函数进行处理，返回一个相关IP地址的列表。

程序`DNS.go`的第二部分是以下Go代码：

> ```go
> func main() {
>     arguments := os.Args
>     if len(arguments) == 1 {
>         fmt.Println("Please provide an argument!")
>         return
>     }
>
>     input := arguments[1]
>     IPaddress := net.ParseIP(input)
> ```

函数`net.ParseIP()`可以将输入字符串解析为IPv4或IPv6地址。如果输入一个非法的IP地址，函数`net.ParseIP()`将返回`nil`。

程序`DNS.go`的剩余Go代码如下：

> ```go
>     if IPaddress == nil {
>         IPs, err := lookHostname(input)
>         if err == nil {
>             for _, singleIP := range IPs {
>                 fmt.Println(singleIP)
>             }
>         }
>     } else {
>         hosts, err := lookIP(input)
>         if err == nil {
>             for _, hostname := range hosts {
>                 fmt.Println(hostname)
>             }
>         }
>     }
> }
> ```

执行`DNS.go`程序，并携带不同的输入参数，将生成以下输出：

> ```
> $ go run DNS.go 127.0.0.1
> localhost
> $ go run DNS.go 192.168.1.1
> cisco
> $ go run DNS.go packtpub.com
> 83.166.169.231
> $ go run DNS.go google.com
> 2a00:1450:4001:816::200e
> 216.58.210.14
> $ go run DNS.go www.google.com
> 2a00:1450:4001:816::2004
> 216.58.214.36
> $ go run DNS.go cnn.com
> 2a04:4e42::323
> 2a04:4e42:600::323
> 2a04:4e42:400::323
> 2a04:4e42:200::323
> 151.101.193.67
> 151.101.1.67
> 151.101.129.67
> 151.101.65.67
> ```

可以看到`go run DNS.go 192.168.1.1`命令的输出来自`/etc/hosts`文件，因为在`/etc/hosts`文件中配置了IP地址`192.168.1.1`的别名`cisco`。

最后一个命令的输出演示了域名（`cnn.com`）可能有多个公网IP地址映射。请特别注意*公网*这个词，尽管`www.google.com`有多个IP地址，但是只有地址（`216.58.214.36`）是公网IP地址。
