SoFunction
Updated on 2025-03-04

Golang's complete record of the steps to resolve domain names

I encountered a problem recently.

Our kube-apiserver is configured with OIDC authentication. The OIDC issuer adds the dns server record, but for some reasons, I need to overwrite the dns server resolution and use the hostAlias ​​IP address instead. However, the actual test found that DNS resolution is always gone, although the /etc/hosts file has added a custom hosts record. And those domain names that are not registered with the dns server can still be parsed through /etc/hosts.

The reason is that the basic image of kube-apserver is busybox. Unlike centos, this guy does not have a /etc/ file, so DNS resolution is always preferred, and the /etc/hosts file is ignored.

The solution is very simple. Just add /etc/ file to the image to specify the parsing order, as follows.

hosts: files dns

That is, files preferentially dns.

By the way, let’s take a look at the domain name analysis of golang in the Linux system.

Golang has two domain name resolution methods: built-in Go parser; CG-based system resolver. Configure it through the environment variable GODEBUG.

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

The default is to use the built-in Go parser, because when DNS resolution blocks, the built-in Go parser just blocks a goroutine, while the cgo parser blocks an operating system-level thread.

func init() { netGo = true }

If the read fails, force cgo.

	 = dnsReadConfig("/etc/")
	if  != nil && !() &&
		!() {
		// If we can't read the  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.
		 = true
	}

When using the built-in Go parser, it will be subdivided into the following four types according to the resolution 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/ file does not exist or the file exists but the hosts field is not specified, hostLookupDNSFiles is used in Linux, that is, dns resolution is preferred for hosts resolution, so there will be problems that appear at the beginning.

	nss := 
	srcs := ["hosts"]
	// If /etc/ doesn't exist or doesn't specify any
	// sources for "hosts", assume Go's DNS will work fine.
	if () || ( == nil && len(srcs) == 0) {
		if  == "linux" {
			// glibc says the default is "dns [!UNAVAIL=return] files"
			// /software/libc/manual/html_node/.
			return hostLookupDNSFiles
		}
		return hostLookupFilesDNS
 }

The parsing order can be specified. The code is quite simple.

	var mdnsSource, filesSource, dnsSource bool
	var first string
	for _, src := range srcs {
		if  == "files" ||  == "dns" {
			if !() {
				return fallbackOrder // non-standard; let libc deal with it.
			}
			if  == "files" {
				filesSource = true
			} else if  == "dns" {
				dnsSource = true
			}
			if first == "" {
				first = 
			}
			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
	}

Therefore, specify hosts: files dns, and the resolution strategy is hostLookupFilesDNS, that is, /etc/hosts is preferred.

For detailed analysis order, please refer tohostLookupOrder

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.