a (hopefully) lightweight, custom dns server implemented in c, designed to handle basic dns queries using predefined mappings from a json file. this server supports common dns record types such as a, aaaa, cname, mx, and ns records, and includes wildcard domain support.
- simple dns server in c
a lightweight, custom dns server implemented in c, designed to handle basic dns queries using predefined mappings from a json file. supports common dns record types such as a, aaaa, cname, mx, and ns records, and includes wildcard domain support. it also features a management interface for dynamically adding and removing dns records without restarting the server.
- c compiler: gcc or any compatible c compiler
- make: for building the project using the provided
makefile - libraries:
- cjson: included in the
lib/cjson/directory - uthash: included in the
lib/uthash/directory
- cjson: included in the
- system dependencies:
- posix threads (pthread)
- standard c libraries
- clone the repository:
git clone https://github.com/karol-broda/udp_dns_server_c.git cd udp_dns_server_c
-
clean previous builds:
make clean
-
compile the dns server:
make
this will compile the source files and produce an executable named
dns_server. -
install the dns server (optional):
sudo make install
this will install the dns server and management script to
/usr/local/bin/.
start the dns server:
./dns_serverthe server listens on port 2053 by default for dns queries and the management interface listens on port 8053 by default.
you can modify the following configuration options by editing include/dns_server.h:
#define DEFAULT_DNS_PORT 2053 // dns service port
#define DEFAULT_MGMT_PORT 8053 // management interface port
#define DEFAULT_MAPPINGS_FILE "dns_mappings.json" // path to dns mappings
#define DEFAULT_TTL 3600 // default ttl for dns records
#define DEFAULT_AUTH_TOKEN "change_this_token" // auth tokenimportant: be sure to change the default authentication token before deploying to production!
the dns server includes a management interface that allows you to dynamically add, delete, and modify dns records without restarting the server.
the easiest way to manage the dns server is using the included dns_mgmt.sh script:
# make the script executable (if not already)
chmod +x dns_mgmt.sh
# list all current dns records
./dns_mgmt.sh list
# add a new a record
./dns_mgmt.sh add example.com a base 192.168.1.1
# add a new mx record
./dns_mgmt.sh add example.com mx base "10 mail.example.com"
# delete a record
./dns_mgmt.sh delete example.com a base
# reload dns mappings from the configuration file
./dns_mgmt.sh reloadthe management interface uses a simple text-based protocol over tcp. each command follows this format:
<auth_token> <command> <parameters...>
available commands:
add <domain> <type> <scope> <value>- add a new dns recorddelete <domain> <type> <scope>- delete a dns recordlist- list all dns recordsreload- reload dns mappings from the configuration file
for example, to add a new a record manually:
echo "change_this_token add example.com a base 192.168.1.1" | nc localhost 8053when adding or deleting records, you need to specify a scope:
base- regular domain recordswildcard- wildcard domain records (*.domain)subdomain- specific subdomain records
the dns mappings are defined in a json file located at src/dns_mappings.json. this file specifies the dns records for various domains.
the json file is structured with a top-level "domains" object. each key within "domains" is a domain name, and its value is another object containing "records", and optionally "wildcards" and "subdomains".
{
"domains": {
"example.com": {
"records": {
"a": ["93.184.216.34"],
"aaaa": ["2606:2800:220:1:248:1893:25c8:1946"],
"cname": ["alias.example.com"],
"mx": [
{
"priority": 10,
"value": "mail.example.com"
}
],
"ns": ["ns1.example.com", "ns2.example.com"]
},
"wildcards": {
"records": {
"a": ["93.184.216.35"]
}
},
"subdomains": {
"subdomain1": {
"records": {
"a": ["93.184.216.36"]
}
}
}
}
}
}- a: ipv4 addresses
- aaaa: ipv6 addresses
- cname: canonical name records
- mx: mail exchange records, with
priorityandvalue - ns: name server records
- txt: text records
- srv: service records
-
a and aaaa records:
"records": { "a": ["93.184.216.34"], "aaaa": ["2606:2800:220:1:248:1893:25c8:1946"] }
-
cname record:
"records": { "cname": ["alias.example.com"] }
-
mx records:
"records": { "mx": [ { "priority": 10, "value": "mail.example.com" }, { "priority": 20, "value": "mail2.example.com" } ] }
-
ns records:
"records": { "ns": ["ns1.example.com", "ns2.example.com"] }
-
wildcard domains:
use the
"wildcards"key within a domain to define wildcard records."wildcards": { "records": { "a": ["93.184.216.35"] } }
use the dig command-line tool to test your dns server:
# query a record
dig @127.0.0.1 -p 2053 example.com a
# query aaaa record
dig @127.0.0.1 -p 2053 example.com aaaa
# query cname record
dig @127.0.0.1 -p 2053 example.com cname
# query mx record
dig @127.0.0.1 -p 2053 example.com mx
# query ns record
dig @127.0.0.1 -p 2053 example.com ns
# query wildcard domain
dig @127.0.0.1 -p 2053 sub.example.com ahere is an example of how the server handles queries:
-
start the server:
./dns_server
-
example server output:
[2025-05-21 10:15:32] [info] starting dns server... [2025-05-21 10:15:32] [info] loading dns mappings from dns_mappings.json [2025-05-21 10:15:32] [info] adding records for domain: example.com, type: a [2025-05-21 10:15:32] [info] added a record for example.com with 1 values [2025-05-21 10:15:32] [info] dns server started on port 2053 [2025-05-21 10:15:32] [info] dns management interface listening on port 8053 -
add a new record using the management interface:
./dns_mgmt.sh add test.com a base 192.168.1.10
-
result:
success: record added -
server log output:
[2025-05-21 10:16:45] [info] adding records for domain: test.com, type: a [2025-05-21 10:16:45] [info] added a record for test.com with 1 values -
client query:
dig @127.0.0.1 -p 2053 test.com a
-
server log output:
[2025-05-21 10:17:10] [info] received query for domain: test.com, type: a [2025-05-21 10:17:10] [info] exact match found for domain test.com and type a [2025-05-21 10:17:10] [info] resolved for: test.com, type: a [2025-05-21 10:17:10] [info] response sent to: 127.0.0.1 -
client output:
; <<>> dig 9.10.6 <<>> @127.0.0.1 -p 2053 test.com a ;; global options: +cmd ;; got answer: ;; ->>header<<- opcode: query, status: noerror, id: 12345 ;; flags: qr aa rd; query: 1, answer: 1, authority: 0, additional: 0 ;; question section: ;test.com. in a ;; answer section: test.com. 3600 in a 192.168.1.10 ;; query time: 1 msec ;; server: 127.0.0.1#2053(127.0.0.1) ;; when: wed may 21 10:17:10 utc 2025 ;; msg size rcvd: 45
if the server fails to start:
-
check that the specified ports (2053 and 8053 by default) are available:
netstat -tuln | grep 2053 netstat -tuln | grep 8053
-
ensure you have the necessary permissions to bind to the ports:
# if running on ports below 1024 sudo ./dns_server -
verify the dns_mappings.json file exists and is valid:
jq . src/dns_mappings.json
if you're having trouble with the management interface:
-
verify the authentication token is correct:
# check the token in the source code grep -r "DEFAULT_AUTH_TOKEN" include/
-
ensure tcp connectivity to the management port:
nc -zv localhost 8053
-
check server logs for any error messages related to the management interface.
-
authentication token: the management interface requires an authentication token. change the default token (
default_auth_tokenindns_server.h) before deploying. -
firewall rules: configure firewall rules to restrict access to the management port (8053 by default).
-
run as non-root: for production use, consider running the dns server as a non-privileged user after binding to the ports.
-
tls encryption: for added security, consider implementing tls encryption for the management interface.
contributions are welcome! please follow these steps:
-
fork the repository: click the "fork" button at the top right of the repository page.
-
clone your fork:
git clone https://github.com/karol-broda/udp_dns_server_c.git cd udp_dns_server_c -
create a new branch:
git checkout -b feature/your-fancy-feature-name
-
make changes and commit:
git add . git commit -m "add fancy feature"
-
push to your fork:
git push origin feature/your-fancy-feature-name
-
create a pull request: go to the original repository and open a pull request from your forked branch.
this project is licensed under the mit license.
disclaimer: this dns server is intended for educational and testing purposes. it is not recommended to use this server in a production environment without proper security reviews and testing. (although if you use it in a production environment, please contact me and tell me how it went/if you need spiritual welfare)