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, whileAAAA
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.
Or enter 1.1.1.1 to get Cloudflare’s.
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