| name | dns |
| description | DNS protocol and domain name resolution |
| category | networking |
| difficulty | intermediate |
| tags | ["dns","domain","resolution","nameserver"] |
| author | OpenCode Community |
| version | 1 |
| last_updated | 2024-01-15T00:00:00.000Z |
DNS (Domain Name System)
What I Do
I am DNS, the hierarchical naming system translating human-readable domain names into machine-readable IP addresses. I provide distributed, scalable hostname resolution through a global network of authoritative name servers, recursive resolvers, and caching servers. I support multiple record types (A, AAAA, CNAME, MX, TXT, NS, SRV) for various purposes. I implement caching at multiple levels to improve performance and reduce load. I support DNSSEC for authenticated responses, DNS-over-HTTPS for encrypted queries, and dynamic updates for real-time record management. I form the foundation of internet addressing, enabling users to access services using memorable names rather than numeric IP addresses.
When to Use Me
- Implementing custom DNS resolution
- Configuring DNS records for services
- Building DNS-based service discovery
- DNS troubleshooting and diagnostics
- High-availability DNS infrastructure
- DNS-based load balancing (GeoDNS, latency-based)
- Domain registration and management
- DNSSEC implementation
- Private DNS zones
Core Concepts
Record Types: A (IPv4), AAAA (IPv6), CNAME (alias), MX (mail), TXT (text), NS (nameserver), SRV (service), SOA (authority).
DNS Hierarchy: Root servers → TLD servers → Authoritative servers → Recursive resolvers.
Caching: TTL (Time to Live) controlling how long records are cached.
Zones: Portions of the DNS namespace managed by authoritative servers.
DNSSEC: DNS Security Extensions providing authentication for DNS responses.
Anycast: Multiple servers sharing the same IP for geographic distribution.
Round-Robin: Multiple IP addresses rotated for load distribution.
Code Examples
Example 1: DNS Client with Cache (Python)
import socket
import struct
import time
import threading
from typing import Optional, Dict, List
from dataclasses import dataclass, field
from enum import Enum
class DNSRecordType(Enum):
A = 1
AAAA = 28
CNAME = 5
MX = 15
NS = 2
TXT = 16
SOA = 6
SRV = 33
@dataclass
class DNSRecord:
name: str
rtype: DNSRecordType
rdata: str
ttl: int
timestamp: float = field(default_factory=time.time)
class DNSCache:
def __init__(self, max_size: int = 1000, default_ttl: int = 300):
self.cache: Dict[str, List[DNSRecord]] = {}
self.lock = threading.RLock()
self.max_size = max_size
self.default_ttl = default_ttl
def get(self, name: , rtype: DNSRecordType) -> [[DNSRecord]]:
.lock:
key =
key .cache:
records = .cache[key]
now = time.time()
valid = [r r records r.ttl == (now - r.timestamp) < r.ttl]
valid:
.cache[key]
(valid) < (records):
.cache[key] = valid
valid
():
.lock:
key =
(.cache) >= .max_size:
.evict_oldest()
.cache[key] = records
():
.cache:
oldest_key = (.cache.keys(),
key= k: (r.timestamp r .cache[k]))
.cache[oldest_key]
():
.lock:
.cache.clear()
() -> :
.lock:
{
: (.cache),
: ((v) v .cache.values())
}
:
DNS_SERVER =
DNS_PORT =
TIMEOUT =
():
.server = server .DNS_SERVER
.cache = cache DNSCache()
.socket =
():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(.TIMEOUT)
sock
() -> [DNSRecord]:
use_cache:
cached = .cache.get(name, rtype)
cached:
cached
records = ._query(name, rtype)
use_cache records:
.cache.(name, rtype, records)
records
() -> [DNSRecord]]:
sock = ._create_socket()
:
transaction_id = struct.pack(, )
flags = struct.pack(, )
qdcount = struct.pack(, )
ancount = struct.pack(, )
nscount = struct.pack(, )
arcount = struct.pack(, )
qname =
label name.split():
qname += struct.pack(, (label)) + label.encode()
qname +=
qtype = struct.pack(, rtype.value)
qclass = struct.pack(, )
query = transaction_id + flags + qdcount nscount + + ancount + arcount + qname + qtype + qclass
sock.sendto(query, (.server, .DNS_PORT))
response = sock.recv()
:
sock.close()
._parse_response(response, name)
() -> [DNSRecord]:
records = []
transaction_id = struct.unpack(, response[:])[]
flags = struct.unpack(, response[:])[]
qdcount = struct.unpack(, response[:])[]
ancount = struct.unpack(, response[:])[]
offset =
_ (qdcount):
response[offset] != :
offset += + response[offset]
offset +=
_ (ancount):
name, offset = ._parse_name(response, offset)
rtype = DNSRecordType(struct.unpack(, response[offset:offset+])[])
offset +=
rclass = struct.unpack(, response[offset:offset+])[]
offset +=
ttl = struct.unpack(, response[offset:offset+])[]
offset +=
rdlength = struct.unpack(, response[offset:offset+])[]
offset +=
rdata = response[offset:offset+rdlength]
offset += rdlength
parsed_rdata = ._parse_rdata(rdata, rtype, original_name)
records.append(DNSRecord(
name=name,
rtype=rtype,
rdata=parsed_rdata,
ttl=ttl
))
records
() -> (, ):
name = []
original_offset = offset
:
length = response[offset]
length == :
offset +=
(length & ) == :
pointer = struct.unpack(, response[offset:offset+])[] &
pointed_name, _ = ._parse_name(response, pointer)
name.append(pointed_name)
offset +=
:
offset +=
name.append(response[offset:offset+length].decode())
offset += length
.join(name), offset
() -> :
rtype == DNSRecordType.A:
.join((b) b rdata)
rtype == DNSRecordType.AAAA:
.join( b rdata[::])
rtype == DNSRecordType.CNAME:
name, _ = ._parse_name(rdata, )
name
rtype == DNSRecordType.MX:
preference = struct.unpack(, rdata[:])[]
name, _ = ._parse_name(rdata, )
rtype == DNSRecordType.TXT:
length = rdata[]
rdata[:+length].decode()
:
rdata.()
() -> []:
records = .resolve(name, DNSRecordType.A, use_cache)
[r.rdata r records]
() -> []:
records = .resolve(name, DNSRecordType.AAAA, use_cache)
[r.rdata r records]
() -> []:
records = .resolve(name, DNSRecordType.MX, use_cache)
[((r.rdata.split()[]), r.rdata.split()[]) r records]
() -> []:
records = .resolve(name, DNSRecordType.TXT, use_cache)
[r.rdata r records]
__name__ == :
client = DNSClient()
()
a_records = client.resolve_a()
ip a_records:
()
()
mx_records = client.resolve_mx()
pref, server mx_records:
()
()
(client.cache.get_stats())
Example 2: DNS Server with Zone Files (Go)
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"os"
"strings"
"sync"
"time"
)
type DNSRecord struct {
Name string
Type string
RData string
TTL int
}
type Zone struct {
Origin string
Records []DNSRecord
SOA DNSRecord
mutex sync.RWMutex
}
type DNSServer struct {
zones map[string]*Zone
cache map[string][]DNSRecord
cacheMutex sync.RWMutex
udpSocket *net.UDPConn
tcpListener *net.TCPListener
}
const (
TYPE_A = 1
TYPE_NS = 2
TYPE_CNAME = 5
TYPE_SOA = 6
TYPE_PTR = 12
TYPE_MX = 15
TYPE_TXT = 16
TYPE_AAAA = 28
TYPE_SRV = 33
)
func NewDNSServer() *DNSServer {
return &DNSServer{
zones: make(map[string]*Zone),
cache: make(map[string][]DNSRecord),
}
}
LoadZoneFile(filename , origin ) {
file, err := os.Open(filename)
err != {
fmt.Errorf(, err)
}
file.Close()
zone := &Zone{
Origin: origin,
Records: ([]DNSRecord, ),
}
scanner := bufio.NewScanner(file)
scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
line == || strings.HasPrefix(line, ) {
}
parts := strings.Fields(line)
(parts) < {
}
record := DNSRecord{
Name: parts[],
Type: strings.ToUpper(parts[]),
RData: strings.Join(parts[:], ),
TTL: ,
}
parts[] != {
}
zone.Records = (zone.Records, record)
}
s.zones[origin] = zone
}
StartUDP(address ) {
addr, err := net.ResolveUDPAddr(, address)
err != {
fmt.Errorf(, err)
}
s.udpSocket, err = net.ListenUDP(, addr)
err != {
fmt.Errorf(, err)
}
s.handleUDP()
}
StartTCP(address ) {
addr, err := net.ResolveTCPAddr(, address)
err != {
fmt.Errorf(, err)
}
s.tcpListener, err = net.ListenTCP(, addr)
err != {
fmt.Errorf(, err)
}
s.handleTCP()
}
handleUDP() {
buffer := ([], )
{
n, clientAddr, err := s.udpSocket.ReadFromUDP(buffer)
err != {
}
response := s.processQuery(buffer[:n])
s.udpSocket.WriteToUDP(response, clientAddr)
}
}
handleTCP() {
{
conn, err := s.tcpListener.AcceptTCP()
err != {
}
{
conn.Close()
lengthBuf := ([], )
_, err := conn.Read(lengthBuf); err != {
}
length := ((lengthBuf[])<< | (lengthBuf[]))
request := ([], length)
_, err := conn.Read(request); err != {
}
response := s.processQuery(request)
responseLength := ([], )
responseLength[] = ((response) >> )
responseLength[] = ((response))
conn.Write(responseLength)
conn.Write(response)
}(conn)
}
}
processQuery(query []) [] {
(query) < {
s.createErrorResponse(, )
}
transactionID := query[:]
flags := query[:]
qdcount := ((query[])<< | (query[]))
offset :=
name
{
length := (query[offset])
length == {
offset++
}
(length & ) == {
offset +=
}
name != {
name +=
}
name += (query[offset+:offset++length])
offset += + length
}
qtype := (query[offset])<< | (query[offset+])
qclass := (query[offset+])<< | (query[offset+])
offset +=
response bytes.Buffer
response.Write(transactionID)
flags[] &=
response.Write(flags)
qdcountBytes := []{(qdcount >> ), (qdcount & )}
response.Write(qdcountBytes)
response.Write([]{, })
response.Write([]{, })
response.Write([]{, })
response.Write(query[:offset])
answers := s.lookupRecords(name, qtype)
_, answer := answers {
answerBytes := s.encodeRecord(answer, name, offset)
response.Write(answerBytes)
ancountBytes := []{
((answers) >> ),
((answers) & )
}
(response[:], ancountBytes)
}
response.Bytes()
}
lookupRecords(name , qtype ) []DNSRecord {
name = strings.ToLower(name)
zone, ok := s.zones[name]; ok {
zone.mutex.RLock()
zone.mutex.RUnlock()
answers []DNSRecord
_, record := zone.Records {
record.Name == name || record.Name == {
typeNum := s.typeStringToNumber(record.Type)
typeNum == qtype || qtype == TYPE_ANY {
answers = (answers, record)
}
}
}
answers
}
}
encodeRecord(record DNSRecord, name , offset ) [] {
encoded bytes.Buffer
encoded.WriteByte()
encoded.WriteByte((offset))
typeBytes := s.typeStringToBytes(record.Type)
encoded.Write(typeBytes)
encoded.Write([]{, })
ttlBytes := []{(record.TTL >> ), (record.TTL >> ),
(record.TTL >> ), (record.TTL & )}
encoded.Write(ttlBytes)
rdata := s.encodeRData(record.RData, record.Type)
length := []{((rdata) >> ), ((rdata) & )}
encoded.Write(length)
encoded.Write(rdata)
encoded.Bytes()
}
encodeRData(rdata , rtype ) [] {
rtype == {
parts := strings.Split(rdata, )
result []
_, part := parts {
result = (result, (strings.ToInt(part)))
}
result
}
[](rdata)
}
typeStringToNumber(typeStr ) {
strings.ToUpper(typeStr) {
: TYPE_A
: TYPE_NS
: TYPE_CNAME
: TYPE_SOA
: TYPE_PTR
: TYPE_MX
: TYPE_TXT
: TYPE_AAAA
: TYPE_SRV
:
}
}
typeStringToBytes(typeStr ) [] {
t := s.typeStringToNumber(typeStr)
[]{(t >> ), (t & )}
}
createErrorResponse(id [], errorCode ) [] {
response := ([], )
(response[:], id)
response[] = | (errorCode>>)
response[] =
response
}
Example 3: Service Discovery with DNS SRV Records
interface SRVRecord {
priority: number;
weight: number;
port: number;
target: string;
}
class DNSServiceDiscovery {
private client: DNSClient;
constructor() {
this.client = new DNSClient();
}
async discoverService(
service: string,
protocol: string = 'tcp',
domain: string = 'local'
): Promise<SRVRecord[]> {
const queryName = `_${service}._${protocol}.${domain}`;
const records = await this.client.resolve(queryName, DNSRecordType.SRV);
return records.map(record => {
const parts = record.rdata.split();
{
: (parts[]),
: (parts[]),
: (parts[]),
: parts[]
};
}).( {
(a. !== b.) {
a. - b.;
}
a. - b.;
});
}
(: ): <[]> {
..(srv.);
}
(
: ,
: = ,
: =
): <{ : ; : } | > {
srvRecords = .(service, protocol, domain);
(srvRecords. === ) {
;
}
totalWeight = srvRecords.( sum + r., );
random = .() * totalWeight;
: | = ;
( record srvRecords) {
random -= record.;
(random <= ) {
selected = record;
;
}
}
(!selected) {
selected = srvRecords[];
}
addresses = .(selected);
(addresses. === ) {
;
}
{
: addresses[],
: selected.
};
}
}
Example 4: Dynamic DNS Update Client
import hashlib
import hmac
import base64
import requests
from typing import Optional
class DynamicDNSClient:
def __init__(
self,
provider_url: str,
hostname: str,
api_key: str,
secret: Optional[str] = None
):
self.provider_url = provider_url
self.hostname = hostname
self.api_key = api_key
self.secret = secret
def get_current_ip(self) -> str:
"""Get current public IP address."""
try:
response = requests.get('https://api.ipify.org?format=json', timeout=10)
return response.json()['ip']
except Exception:
raise RuntimeError("Failed to get current IP address")
def create_auth_header(self, timestamp: str) -> str:
"""Create authentication header for TSIG-style authentication."""
if not self.secret:
return f"DDNS "
message =
signature = hmac.new(
.secret.encode(),
message.encode(),
hashlib.sha256
).digest()
() -> :
ip_address :
ip_address = .get_current_ip()
timestamp = ((time.time()))
headers = {
: ,
: .create_auth_header(timestamp)
}
payload = {
: .hostname,
: ip_address,
: record_type,
: ttl,
: timestamp
}
:
response = requests.post(
.provider_url,
json=payload,
headers=headers,
timeout=
)
response.raise_for_status()
response.json()
requests.exceptions.RequestException e:
RuntimeError()
() -> :
timestamp = ((time.time()))
headers = {
: ,
: .create_auth_header(timestamp)
}
payload = {
: .hostname,
: timestamp
}
:
response = requests.delete(
.provider_url,
json=payload,
headers=headers,
timeout=
)
response.raise_for_status()
response.json()
requests.exceptions.RequestException e:
RuntimeError()
:
():
.client = client
.check_interval = check_interval
.last_ip: [] =
.running =
.thread: [threading.Thread] =
():
.running =
.thread = threading.Thread(target=._run, daemon=)
.thread.start()
():
.running =
.thread:
.thread.join(timeout=)
():
.running:
:
current_ip = .client.get_current_ip()
current_ip != .last_ip:
()
result = .client.update(current_ip)
()
.last_ip = current_ip
:
()
Exception e:
()
_ (.check_interval):
.running:
time.sleep()
Best Practices
- Use short TTLs for records that may change frequently
- Implement DNSSEC for production DNS infrastructure
- Use multiple authoritative nameservers for redundancy
- Monitor DNS propagation after changes
- Implement rate limiting on DNS servers to prevent abuse
- Use anycast for geographic distribution
- Consider DNS-over-HTTPS for privacy-sensitive applications
- Maintain proper SOA records for zone management
- Implement proper zone transfers for secondary servers
- Use monitoring to detect DNS issues early
Core Competencies
- DNS record types and their uses
- DNS resolution process
- Zone file management
- DNSSEC implementation
- DNS caching strategies
- DNS-based load balancing
- Service discovery patterns
- Dynamic DNS updates
- DNS monitoring and troubleshooting
- DNS security considerations
- DNS propagation
- Anycast configuration
- DNS query optimization