golang is a complete record of the steps taken to resolve a domain name

  • 2020-06-23 00:35:55
  • OfStack

Recently I encountered a problem.

Our ES3en-ES4en is configured with OIDC authentication, OIDC issuer added dns server record, but for some reason, I need to override dns server parsing and use IP address of hostAlias, but the actual measurement found that DNS parsing always went, although the /etc/hosts file has added custom hosts record. Domain names that are not registered with dns server can still be resolved by /etc/hosts.

The reason is that the basic image of ES24en-ES25en is busybox, which is different from centos. There is no /etc/ nsswitch.conf file for this cargo, so it always USES DNS parsing first and ignores /etc/hosts file.

The solution is as simple as adding the /etc/ nsswitch.conf file to the image in the parse order shown below.


hosts: files dns

That is, files first dns.

Along with the complete theory 1 linux system golang domain name resolution.

golang has two domain name resolution methods: the built-in Go parser; cgo - based system parser. Configuration is through the environment variable GODEBUG.


export GODEBUG=netdns=go # force pure Go resolver
export GODEBUG=netdns=cgo # force cgo resolver

The default is the built-in Go parser, because when the DNS parser blocks, the built-in Go parser blocks only 1 goroutine, while the cgo parser blocks 1 OS level thread.


func init() { netGo = true }

Failure to read ES65en. conf forces cgo to be used.


	confVal.resolv = dnsReadConfig("/etc/resolv.conf")
	if confVal.resolv.err != nil && !os.IsNotExist(confVal.resolv.err) &&
		!os.IsPermission(confVal.resolv.err) {
		// If we can't read the resolv.conf file, assume it
		// had something important in it and defer to cgo.
		// libc's resolver might then fail too, but at least
		// it wasn't our fault.
		confVal.forceCgoLookupHost = true
	}

When using the built-in Go parser, it is broken down into the following four categories, depending on the parsing priority.


const (
	// hostLookupCgo means defer to cgo.
	hostLookupCgo hostLookupOrder = iota
	hostLookupFilesDNS   // files first
	hostLookupDNSFiles   // dns first
	hostLookupFiles   // only files
	hostLookupDNS   // only DNS
)

When the /etc/ nsswitch.conf file does not exist or the file exists but the hosts field is not specified, hostLookupDNSFiles is used under linux, that is, dns parsing takes precedence over hosts parsing, so the initial problem occurs.


	nss := c.nss
	srcs := nss.sources["hosts"]
	// If /etc/nsswitch.conf doesn't exist or doesn't specify any
	// sources for "hosts", assume Go's DNS will work fine.
	if os.IsNotExist(nss.err) || (nss.err == nil && len(srcs) == 0) {
		if c.goos == "linux" {
			// glibc says the default is "dns [!UNAVAIL=return] files"
			// http://www.gnu.org/software/libc/manual/html_node/Notes-on-NSS-Configuration-File.html.
			return hostLookupDNSFiles
		}
		return hostLookupFilesDNS
 }

You can specify the parsing order via nsswitch.conf. The code is pretty simple.


	var mdnsSource, filesSource, dnsSource bool
	var first string
	for _, src := range srcs {
		if src.source == "files" || src.source == "dns" {
			if !src.standardCriteria() {
				return fallbackOrder // non-standard; let libc deal with it.
			}
			if src.source == "files" {
				filesSource = true
			} else if src.source == "dns" {
				dnsSource = true
			}
			if first == "" {
				first = src.source
			}
			continue
		}
		// Some source we don't know how to deal with.
		return fallbackOrder
	}

	// Cases where Go can handle it without cgo and C thread
	// overhead.
	switch {
	case filesSource && dnsSource:
		if first == "files" {
			return hostLookupFilesDNS
		} else {
			return hostLookupDNSFiles
		}
	case filesSource:
		return hostLookupFiles
	case dnsSource:
		return hostLookupDNS
	}

SuoYiZhiDing hosts: files dns,解析策略就是 hostLookupFilesDNS,即优先使用 /etc/hosts .

See hostLookupOrder for a detailed sequence of parsing.

conclusion


Related articles: