No description
Find a file
2026-06-05 12:35:11 -05:00
src Start 2026-06-05 12:35:11 -05:00
test Start 2026-06-05 12:35:11 -05:00
.gitignore Initial commit 2026-06-05 12:31:05 -05:00
LICENSE Initial commit 2026-06-05 12:31:05 -05:00
Project.toml Start 2026-06-05 12:35:11 -05:00
README.md Start 2026-06-05 12:35:11 -05:00

DomainNameSystem.jl

License: ISC Julia

DomainNameSystem.jl is a DNS toolkit for Julia, ported from the popular dnspython library (v2.9.0). It provides a comprehensive implementation of DNS protocol types, wire format encoding/decoding, and DNS client functionality.

Features

  • DNS Name manipulation — create, compare, relativize, canonicalize DNS names
  • Wire format encoding/decoding — full support for DNS message compression
  • RRtype/RRclass constants — all 80+ IANA-registered rdata types and classes
  • Protocol constants — opcodes, rcodes, flags, EDNS flags
  • Resource Record types — A, AAAA, NS, CNAME, SOA, MX, TXT, SRV, RRSIG, DNSKEY, DS, OPT, and more
  • Wire format Renderer — build DNS messages programmatically
  • DNS Message — parse and build query/response messages with EDNS and TSIG support
  • Tokeniser — parse DNS zone file format
  • TSIG — transaction signatures for authenticated DNS updates
  • EDNS Options — Client Subnet (ECS), Cookie, NSID, Extended DNS Error (EDE), Padding
  • Zone parsing — read and write zone files
  • Resolver — DNS stub resolver with caching, DNSSEC support (stub)
  • Query functions — UDP, TCP, TLS, and HTTPS transport
  • Serial number arithmetic — RFC 1982 comparison
  • IP address helpers — IPv4/IPv6 conversion and validation
  • E.164 — telephone number ↔ ENUM domain name conversion

Installation

# From the Julia REPL
import Pkg
Pkg.add(url="https://github.com/yourusername/DomainNameSystem.jl")

Or use the local development path:

import Pkg
Pkg.develop(path="/path/to/DomainNameSystem.jl")

Quick Start

using DomainNameSystem

# Create and manipulate DNS names
name = from_text("www.example.com.")
println(to_text(name))            # www.example.com.
println(is_absolute(name))        # true
println(is_subdomain(name, from_text("example.com.")))  # true

# Wire format round-trip
wire = to_wire(name)
name2 = from_wire(wire, 0)
println(to_text(name2[1]))        # www.example.com.

# Parse IP addresses
bytes = inet_aton("192.168.1.1")
println(inet_ntoa(bytes))         # 192.168.1.1
println(is_address("10.0.0.1"))   # true

# Check protocol constants
println(Int(A))                   # 1
println(Int(AAAA))                # 28
println(rdatatype_to_text(NS))    # "NS"

# Serial number arithmetic
s1 = Serial(100)
s2 = Serial(200)
println(s1 < s2)                  # true
s3 = s1 + 50
println(s3.value)                 # 150

# Create a DNS query message
query = make_query(from_text("example.com."), A, CLASS_IN)
println(to_text(query))

# Send the query over UDP
response = udp(query, "8.8.8.8")
println(to_text(response))

Architecture

The library is organised as a single Julia module (DomainNameSystem) with all types and functions defined at the module level for clean inter-operation. Key design choices:

Principle Implementation
Immutability Name, Serial, Rdata subtypes are immutable structs
Multiple dispatch Rdata type behaviour dispatched on concrete struct types
No external deps Uses only Julia's Base and Sockets stdlib
Performance Wire format parser uses efficient byte array operations

Module Structure

src/
├── DomainNameSystem.jl      # Main module, includes all sub-files
├── types.jl                  # Core enumerated types (NameRelation, MessageSection)
├── exception.jl              # Exception hierarchy
├── flags.jl                  # DNS flag constants
├── opcode.jl                 # DNS opcodes
├── rcode.jl                  # DNS response codes
├── rdatatype.jl              # Rdata type constants (A, AAAA, NS, ...)
├── rdataclass.jl             # Rdata class constants (IN, CH, HS, ...)
├── dnssectypes.jl            # DNSSEC algorithm and digest types
├── ipv4.jl / ipv6.jl / inet.jl  # IP address helpers
├── e164.jl                   # E.164 number conversion
├── serial.jl                 # RFC 1982 serial number arithmetic
├── wire.jl                   # Wire format parser
├── name.jl                   # DNS Name type
├── tokenizer.jl              # Zone file tokenizer
├── edns.jl                   # EDNS option types
├── rdata.jl                  # Rdata base type + dispatch
├── rdtypes/                  # Concrete rdata type implementations
│   ├── base.jl               # Base types (MXBase, NSBase, etc.)
│   └── rdata_types.jl        # 25+ concrete RR types
├── rdataset.jl               # Rdataset (set of rdata records)
├── rrset.jl                  # RRset (named rdataset)
├── node.jl                   # Zone node
├── renderer.jl               # Wire format message builder
├── message.jl                # DNS Message
├── update.jl                 # DNS Update
├── zone.jl                   # Zone (stub)
├── tsig.jl                   # TSIG signing
├── resolver.jl               # DNS resolver (stub)
└── query.jl                  # Query transport functions (stub)

API Reference

DNS Names

from_text(text; origin=root)           # Parse DNS name from text
from_wire(message, current)            # Parse name from wire format
to_text(name; omit_final_dot)          # Format name as text
to_wire(name; file, compress, origin)  # Serialize to wire format
is_absolute(name)                      # Does name end with root label?
is_wild(name)                          # Is name a wildcard (*.example.com)?
is_subdomain(a, b)                     # Is a a subdomain of b?
is_superdomain(a, b)                   # Is a a superdomain of b?
canonicalize(name)                     # Lowercase form for DNSSEC
relativize(name, origin)               # Remove origin from name
concatenate(a, b)                      # Concatenate two names
parent(name)                           # Remove least significant label

Protocol Constants

# Rdata types
A, NS, CNAME, SOA, MX, TXT, AAAA, PTR, RRSIG, DNSKEY, DS, OPT, ...

# Rdata classes
CLASS_IN, CLASS_CH, CLASS_HS, CLASS_NONE, CLASS_ANY

# Opcodes
OP_QUERY, OP_IQUERY, OP_STATUS, OP_NOTIFY, OP_UPDATE

# Response codes
RCODE_NOERROR, RCODE_FORMERR, RCODE_SERVFAIL, RCODE_NXDOMAIN, ...

# Flags
Flag_QR, Flag_AA, Flag_TC, Flag_RD, Flag_RA, Flag_AD, Flag_CD

IP Address Helpers

inet_aton("192.168.1.1")       # String → 4-byte vector
inet_ntoa(bytes)               # 4-byte vector → string
ipv6_aton("::1")               # IPv6 string → 16-byte vector
ipv6_ntoa(bytes)               # 16-byte vector → string
is_address(text)               # Check if text is an IPv4 or IPv6 address
is_multicast(text)             # Check if address is multicast
af_for_address(text)           # Determine address family
inet_canonicalize(text)        # Normalize address to canonical form

Serial Number Arithmetic

Serial(value, bits=32)         # Create serial number
s1 < s2                        # RFC 1982 comparison
s1 + delta                     # Add with modulus

Running Tests

import Pkg
Pkg.test(path="/path/to/DomainNameSystem.jl")

License

This project is ported from dnspython, licensed under the ISC License. See LICENSE for details.

Status

The core DNS library is functional with verified name parsing, wire format, and protocol constants. Transport functions (resolver, query) are structural stubs ready for implementation. See the issue tracker for upcoming work.