XINF: Adding a DNS Service

Avi Gupta
hackerLog
Published in
4 min readApr 2, 2023

What’s up everyone! In this year’s blog post, I’m going to show you how I made a DNS service in XINF.dev!

What is a DNS, anyways?

A DNS stands for Domain Name Service. It’s essentially a giant lookup table which stores different types of records for each domain name. Record types include:

  • The IP Address. A records store the IPv4 address, while AAAA records store the IPv6 address.
  • The mail exchange (MX) address for the domain name.
  • The canonical name (CNAME) for the domain name, which essentially maps the different subdomains to the main domain name.
  • The Name Server (NS), which specifies the address of the DNS that stores the records for that domain name.
  • A pointer (PTR), which gets the domain name associated with the IP address.
  • Text (TXT) records, which stores information about the domain itself, and can be used to verify the domain itself. (Idk how that fully works, though.)

The introduction to this concept really opened my eyes up to how the web actually works. When you enter in a website name, the internet doesn’t know that website — it only knows IP addresses. A DNS, however, sends that website domain name through an already known IP address, and then returns the IP address of that website.

At least, I think so. Don’t take what I say for real information — I’m just a 16 year old who likes to code.

You can actually try out using a DNS yourself! Type in 8.8.8.8 in your browser, and you’ll get access to Google’s public DNS.

Google’s public DNS.

Or enter 1.1.1.1 to get Cloudflare’s.

Cloudflare’s dns. Not on web, but the app is free for just about every platform.

Making a DNS service in Go

To get data from a DNS, you’re gonna need a resolver. Golang has that with the net package.

So first, import your packages:

package main

import (
"context"
"net"
)

These are really the only packages you need.

Then, define your constants. I’ve chosen XINF’s defaults as an example.

const host = "8.8.8.8" // can be any DNS ip address you like
const name = "xinf.dev" // can be any domain name you like
var bg = context.Background() // important for getting stuff from the d

To get different types of records from a DNS, you can use net's resolver struct:

r := &net.Resolver{ // hehe pointers
PreferGo: true, // no using C here
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
d := net.Dialer{
Timeout: time.Millisecond * time.Duration(5000),
}
return d.DialContext(ctx, network, host+":53") // :53 is the protocol
},
}

This allows you to get records from a DNS server, but returns a timeout after 5000 milliseconds (5 seconds). This is because speed is of utmost essence.

We’ll also make a map[string]any that can store the different types of records, regardless of what we get back. (We can also use this as a nice and simple JSON object.)

func Foo() map[string]any{

const host = "8.8.8.8" // can be any DNS ip address you like
const name = "xinf.dev" // can be any domain name you like
var bg = context.Background() // important

res := map[string]any{}

r := &net.Resolver{ // hehe pointers
PreferGo: true, // no using C here
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
d := net.Dialer{
Timeout: time.Millisecond * time.Duration(5000),
}
return d.DialContext(ctx, network, host+":53") // :53 is the protocol
},
}

return res

}

A and AAAA records

To get A and AAAA records, simply use r.LookupIP(ctx context.Context, network string, host string), where ctx is the bg context specified earlier, network is either "ip4" for A records or "ip6" for AAAA records, and host is actually the domain name, specified by the const name from earlier.

For A records:

   ips, err := r.LookupIP(bg, "ip4", name) 
// err is of type error.
// will not be nil if LookupIP() could not get a result.
if err != nil {
// handle error
}
res["A"] = ips[0]

For AAAA records:

   ips, err := r.LookupIP(bg, "ip6", name)
if err != nil {
// handle error
}
res["AAAA"] = ips[0]

For CNAME records:

   cn, err := r.LookupCNAME(bg, name)
if err != nil {
// handle error
}
res["CNAME"] = cn

For PTR records:

   ptr, err := r.LookupAddr(bg, name)
if err != nil {
// hande error
}
res["PTR"] = ptr

For NS records:

   ns, err := r.LookupNS(bg, name)
if err != nil {
// handle error
}
res["NS"] = ns

For MX records:

   mxrecs, err := r.LookupMX(bg, name)
if err != nil {
// handle error
}
res["MX"] = mxrecs

For TXT records:

   txrecs, err := r.LookupTXT(bg, name)
if err != nil {
// handle error
}
res["TXT"] = txrecs

You can then put these in the function for any records that you want to get from the DNS.

XINF will return all record types if no record types are specified.

You can check my service out at xinf.dev/xdns. And be sure to check out plenty more services (with documentation included) at xinf.dev.

Have a good one,

Avi Gupta

--

--

Avi Gupta
hackerLog

Named after the first 2 games I got on my xBox, Forza and Minecraft. Also, i have a blog. Real name is Avi.