Skip to Content
Course content

202: DNS Resolution in Go

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I actually get an IP address from a domain?

In most cases, you don't need to overthink this. The net package provides net.LookupHost, which is the quickest way to resolve a hostname to its addresses. One thing to keep in mind: it returns a slice of strings, not a single value. This is because a single domain (like google.com) often points to multiple IP addresses for load balancing.

package main

import (
	"fmt"
	"net"
)

func main() {
	ips, err := net.LookupHost("github.com")
	if err != nil {
		fmt.Printf("Could not resolve: %v\n", err)
		return
	}

	for _, ip := range ips {
		fmt.Printf("Found IP: %s\n", ip)
	}
}

I usually start here. It's clean and uses the system's default resolver, meaning it respects the /etc/hosts file and whatever DNS settings the OS is currently using.

What if I need more than just the IP, like MX or TXT records?

If you're building something like a mail server or a domain validation tool, a simple IP isn't enough. Go provides specialized functions for different record types. For example, net.LookupMX gives you the mail exchange servers, and net.LookupTXT is what you'd use to verify SPF or DKIM records.

package main

import (
	"fmt"
	"net"
)

func main() {
	// Let's check where emails for gmail.com are actually routed
	mxRecords, err := net.LookupMX("gmail.com")
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	for _, record := range mxRecords {
		fmt.Printf("Host: %s, Priority: %d\n", record.Host, record.Pref)
	}
}

Just a heads up: these functions return specific types (like []*net.MX). You'll get a struct with the host and the preference (priority) value, which is exactly what you need if you're implementing the actual logic of mail delivery.

How do I use a specific DNS server instead of the system default?

This is where things get interesting. Sometimes you can't trust the local resolver—maybe you're testing a new DNS configuration or you need to query a private DNS server like Cloudflare (1.1.1.1) or Google (8.8.8.8) directly. To do this, you have to use a custom net.Resolver.

You define a Dial function that tells Go how to connect to the DNS server. If you don't do this, Go just asks the OS to handle it, and you lose control over which server is being queried.

package main

import (
	"context"
	"fmt"
	"net"
	"time"
)

func main() {
	// We create a custom resolver that explicitly talks to Google's DNS
	r := &net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, "udp", "8.8.8.8:53")
		},
	}

	ips, err := r.LookupHost(context.Background(), "example.com")
	if err != nil {
		fmt.Printf("Lookup failed: %v\n", err)
		return
	}
	fmt.Printf("Resolved via 8.8.8.8: %v\n", ips)
}

Notice the PreferGo: true setting. This tells Go to use its internal DNS implementation rather than calling the C library (cgo) functions. If you're doing custom dialing, you almost always want this set to true.

How do I stop a DNS lookup from hanging my program?

Network calls are unpredictable. A DNS server might be lagging or completely down, and you don't want your entire application to freeze while waiting for a response. Instead of the basic LookupHost, use the methods on the Resolver that accept a context.Context.

By wrapping your request in a context.WithTimeout, you can guarantee that your code moves on after a few seconds, regardless of whether the DNS server responded.

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

// Use the default resolver but pass our timed-out context
ips, err := net.DefaultResolver.LookupHost(ctx, "very-slow-dns-server.com")
if err != nil {
	// This will trigger if the 2-second timeout is hit
	fmt.Printf("DNS query timed out or failed: %v\n", err)
}



📋 Practical Task

Exercise: Build a Domain Health Checker

Your task is to write a small utility program that takes a domain name as a command-line argument and performs a "health check" on its DNS configuration. The program should:

  • Attempt to resolve the domain to an IP address.
  • Check for the existence of at least one MX (Mail Exchange) record.
  • Implement a strict 3-second timeout for the entire process using context.Context.
  • Print a clear report: "IPs: [found IPs]", "Email Config: [OK/Missing]", and "Status: [Healthy/Unhealthy]".

A domain should be considered "Healthy" only if both an IP address and at least one MX record are found within the time limit.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.