Skip to content

Commit

Permalink
tidy code up and make the project more user friendly overall
Browse files Browse the repository at this point in the history
  • Loading branch information
oddmario committed Sep 8, 2024
1 parent 95015d3 commit 92cdb21
Show file tree
Hide file tree
Showing 16 changed files with 325 additions and 181 deletions.
48 changes: 31 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
# Simple DNS Server
# 🌎 Simple DNS server
A tiny DNS server that is capable of serving records configured in a MySQL table, or configured statically in a JSON file

## Installation
1. Create a file with the path `/etc/systemd/system/argondns.service` and with the following content:
## 🧐 Configuration documentation

- `mode`: Can be either `db` if your records are stored in a MySQL database, or `static_records` if your records are static and stored in the configuration JSON file.

- `db`: The MySQL server & database credentials. This works only if `mode` is set to `db`

- `listener`: The listening/bind settings for the DNS server (usually has to be kept binding on port 53 to be able to accept DNS requests).

- `process_unstored_dns_queries`: Should the DNS server also accept queries of records that are not stored in your database table/static records configuration? Enable this if yes.

- `static_records`: Configure your static records here, one per JSON array. This works only if `mode` is set to `static_records`

## 🛠️ Installation as a service

1. Store your configuration file at `/etc/simpledns/config.json`
You can copy the example configuration file and change it to serve your needs.
2. If running Simple DNS server in the `db` mode, use this database structure for your records table: https://github.com/oddmario/simple-dns-server/blob/main/db_structure.sql
3. Place the binary file of Simple DNS server at `/usr/local/bin` (e.g. `/usr/local/bin/simpledns`)
4. Make the binary file executable: `chmod u+x /usr/local/bin/simpledns`
5. Create a systemd service for the application. This can be done by creating /etc/systemd/system/simpledns.service to have this content:
```
[Unit]
Description=ArgonDNS
Description=SimpleDNSserver
[Service]
User=root
WorkingDirectory=/root/argondns
WorkingDirectory=/usr/local/bin
LimitNOFILE=2097152
TasksMax=infinity
ExecStart=/root/argondns/simpledns_linux_amd64
ExecStart=/usr/local/bin/simpledns /etc/simpledns/config.json
Restart=on-failure
StartLimitInterval=180
StartLimitBurst=30
RestartSec=5s
[Install]
WantedBy=multi-user.target
```

2. Put the `simpledns_linux_amd64` executable file at a directory with path `/root/argondns/` - so the final path of the executable will be `/root/argondns/simpledns_linux_amd64`

3. Run `chmod -R 777 /root/argondns/simpledns_linux_amd64`

4. Place the `config.json` file found in this repository at `/root/argondns` along with the `simpledns_linux_amd64` executable file

5. Run `systemctl enable argondns`
```
6. Port 53 (the DNS server port) is usually in use by default. To solve this, follow https://unix.stackexchange.com/a/676977/405697 then run `systemctl restart systemd-resolved`

7. Make sure that there are no other DNS servers (such as bind9) are running, then run `systemctl start argondns` to start our DNS server :)
7. Make sure that there are no other DNS servers (such as bind9) are running
8. Enable the Simple DNS server service on startup & start it now:
```
systemctl enable --now simpledns.service
```
15 changes: 14 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"mode": "db",
"db": {
"host": "localhost",
"username": "root",
Expand All @@ -14,5 +15,17 @@
"process_unstored_dns_queries": {
"is_enabled": false,
"dns_server": "8.8.8.8:53"
}
},
"static_records": [
{
"type": "A",
"name": "",
"value": "",
"ttl": 0,
"srv_priority": 0,
"srv_weight": 0,
"srv_port": 0,
"srv_target": ""
}
]
}
4 changes: 4 additions & 0 deletions constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package constants

var ConfigFilePath string = ""
var Version string = "v1.2"
30 changes: 30 additions & 0 deletions db/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package db

import (
"database/sql"
"errors"
"time"
)

func EasyQuery(query string, args ...any) (*sql.Rows, error) {
Expand All @@ -23,3 +25,31 @@ func EasyExec(query string, args ...any) (sql.Result, error) {

return res, nil
}

func RetriedDbQuery(retries int, query string, args ...any) (*sql.Rows, error) {
for range retries {
res, err := EasyQuery(query, args...)
if err != nil {
time.Sleep(1 * time.Second)
continue
} else {
return res, err
}
}

return nil, errors.New("failed")
}

func RetriedDbExec(retries int, query string, args ...any) (sql.Result, error) {
for range retries {
res, err := EasyExec(query, args...)
if err != nil {
time.Sleep(1 * time.Second)
continue
} else {
return res, err
}
}

return nil, errors.New("failed")
}
42 changes: 11 additions & 31 deletions dnsparser/A.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,32 @@ package dnsparser

import (
"mario/simple-dns-server/db"
"mario/simple-dns-server/records"
"net"

"github.com/miekg/dns"
)

func A(m *dns.Msg, name_dot, name_nodot string) bool {
res, err := db.EasyQuery("SELECT id, record_type, record_name, record_value, record_ttl, is_disposable FROM dns_records WHERE record_name = ? AND record_type = 'A'", name_nodot)
if err != nil {
// an error has occured while preparing the SQL statement
return false
}
defer res.Close()

var recordsFound bool = false
recordsFound, records := records.GetDNSRecord(name_nodot, "A")

for res.Next() {
recordsFound = true

var record_id int64
var record_type string
var record_name string
var record_value string
var record_ttl int64
var record_isdisposable int64
if !recordsFound {
// DNS record(s) was/were not found

err = res.Scan(&record_id, &record_type, &record_name, &record_value, &record_ttl, &record_isdisposable)
if err != nil {
// an error has occured
return false
}
return false
}

for _, record := range records {
r := new(dns.A)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: uint32(record_ttl)}
r.A = net.ParseIP(record_value)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: uint32(record.TTL)}
r.A = net.ParseIP(record.Value)

m.Answer = append(m.Answer, r)

if record_isdisposable > 0 {
db.EasyExec("DELETE FROM dns_records WHERE id = ?", record_id)
if record.IsDisposable {
db.RetriedDbExec(10, "DELETE FROM dns_records WHERE id = ?", record.ID)
}
}

if !recordsFound {
// DNS record not found in the database
return false
}

return true
}
42 changes: 11 additions & 31 deletions dnsparser/CNAME.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,31 @@ package dnsparser

import (
"mario/simple-dns-server/db"
"mario/simple-dns-server/records"

"github.com/miekg/dns"
)

func CNAME(m *dns.Msg, name_dot, name_nodot string) bool {
res, err := db.EasyQuery("SELECT id, record_type, record_name, record_value, record_ttl, is_disposable FROM dns_records WHERE record_name = ? AND record_type = 'CNAME'", name_nodot)
if err != nil {
// an error has occured while preparing the SQL statement
return false
}
defer res.Close()

var recordsFound bool = false
recordsFound, records := records.GetDNSRecord(name_nodot, "CNAME")

for res.Next() {
recordsFound = true

var record_id int64
var record_type string
var record_name string
var record_value string
var record_ttl int64
var record_isdisposable int64
if !recordsFound {
// DNS record(s) was/were not found

err = res.Scan(&record_id, &record_type, &record_name, &record_value, &record_ttl, &record_isdisposable)
if err != nil {
// an error has occured
return false
}
return false
}

for _, record := range records {
r := new(dns.CNAME)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: uint32(record_ttl)}
r.Target = dns.Fqdn(record_value)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: uint32(record.TTL)}
r.Target = dns.Fqdn(record.Value)

m.Answer = append(m.Answer, r)

if record_isdisposable > 0 {
db.EasyExec("DELETE FROM dns_records WHERE id = ?", record_id)
if record.IsDisposable {
db.RetriedDbExec(10, "DELETE FROM dns_records WHERE id = ?", record.ID)
}
}

if !recordsFound {
// DNS record not found in the database
return false
}

return true
}
42 changes: 11 additions & 31 deletions dnsparser/NS.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,31 @@ package dnsparser

import (
"mario/simple-dns-server/db"
"mario/simple-dns-server/records"

"github.com/miekg/dns"
)

func NS(m *dns.Msg, name_dot, name_nodot string) bool {
res, err := db.EasyQuery("SELECT id, record_type, record_name, record_value, record_ttl, is_disposable FROM dns_records WHERE record_name = ? AND record_type = 'NS'", name_nodot)
if err != nil {
// an error has occured while preparing the SQL statement
return false
}
defer res.Close()

var recordsFound bool = false
recordsFound, records := records.GetDNSRecord(name_nodot, "NS")

for res.Next() {
recordsFound = true

var record_id int64
var record_type string
var record_name string
var record_value string
var record_ttl int64
var record_isdisposable int64
if !recordsFound {
// DNS record(s) was/were not found

err = res.Scan(&record_id, &record_type, &record_name, &record_value, &record_ttl, &record_isdisposable)
if err != nil {
// an error has occured
return false
}
return false
}

for _, record := range records {
r := new(dns.NS)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: uint32(record_ttl)}
r.Ns = dns.Fqdn(record_value)
r.Hdr = dns.RR_Header{Name: name_dot, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: uint32(record.TTL)}
r.Ns = dns.Fqdn(record.Value)

m.Answer = append(m.Answer, r)

if record_isdisposable > 0 {
db.EasyExec("DELETE FROM dns_records WHERE id = ?", record_id)
if record.IsDisposable {
db.RetriedDbExec(10, "DELETE FROM dns_records WHERE id = ?", record.ID)
}
}

if !recordsFound {
// DNS record not found in the database
return false
}

return true
}
Loading

0 comments on commit 92cdb21

Please sign in to comment.