This commit is contained in:
LovroG05
2025-03-12 15:18:15 +01:00
parent 310f5766e3
commit a7a814342e
5 changed files with 70 additions and 87 deletions

View File

@@ -2,9 +2,9 @@
This is my simple, barebones, **insecure** implementation of a simple DNS server meant for local use. The goal is to support RFC 1035, local records, ad blocking.
For now it successfully recursively resolves A records.
For now, it successfully recursively resolves A records.
NOTE: TTLs are hardcoded for now
Sends through correct TTL values.
## Testing

42
cache/Records.go vendored
View File

@@ -1,42 +0,0 @@
package cache
import (
"github.com/patrickmn/go-cache"
"golang.org/x/net/dns/dnsmessage"
"sync"
"time"
)
type RecordsCache struct {
records *cache.Cache
mu sync.Mutex
}
const (
defaultExpiration = 1 * time.Hour
purgeTime = 1 * time.Hour
)
func NewRecordsCache() *RecordsCache {
Cache := cache.New(defaultExpiration, purgeTime)
return &RecordsCache{
records: Cache,
}
}
func (rc *RecordsCache) Get(name string) (dnsmessage.Resource, bool) {
rc.mu.Lock()
defer rc.mu.Unlock()
record, ok := rc.records.Get(name)
if ok {
res := record.(dnsmessage.Resource)
return res, ok
}
return dnsmessage.Resource{}, false
}
func (rc *RecordsCache) Set(resource dnsmessage.Resource) {
rc.mu.Lock()
defer rc.mu.Unlock()
rc.records.Set(resource.Header.Name.String(), resource, time.Duration(resource.Header.TTL)*time.Second)
}

48
main.go
View File

@@ -2,6 +2,7 @@ package main
import (
"dnsthingymagik/resolver"
"dnsthingymagik/resolver/entities"
"fmt"
"golang.org/x/net/dns/dnsmessage"
"log"
@@ -13,7 +14,12 @@ func main() {
if err != nil {
log.Fatal(err)
}
defer udpServer.Close()
defer func(udpServer net.PacketConn) {
err := udpServer.Close()
if err != nil {
log.Fatal(err)
}
}(udpServer)
for {
buf := make([]byte, 514)
@@ -31,7 +37,7 @@ func process(udp net.PacketConn, addr net.Addr, buf []byte) {
log.Println(err)
}
faq := map[dnsmessage.Question][]net.IP{}
var faq []entities.Record
for _, q := range msg.Questions {
if q.Type == dnsmessage.TypeA {
nips, err := resolver.ResolveDN(q.Name.String(), msg.Header.ID, dnsmessage.TypeA)
@@ -44,33 +50,23 @@ func process(udp net.PacketConn, addr net.Addr, buf []byte) {
continue
}
faq[q] = nips
faq = append(faq, nips...)
}
}
answers := []dnsmessage.Resource{}
for _, q := range msg.Questions {
ips, found := faq[q]
if !found {
log.Printf("No answer for %s, skipping...", q.Name.String())
continue
}
for _, ip := range ips {
fmt.Println(ip)
answers = append(answers, dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: q.Name,
Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET,
TTL: 300, //TODO
},
Body: &dnsmessage.AResource{
A: [4]byte{ip[0], ip[1], ip[2], ip[3]},
},
})
}
var answers []dnsmessage.Resource
for _, record := range faq {
answers = append(answers, dnsmessage.Resource{
Header: dnsmessage.ResourceHeader{
Name: record.Name,
Type: record.RType,
Class: record.Class,
TTL: record.TTL,
},
Body: &dnsmessage.AResource{
A: [4]byte{record.IP[0], record.IP[1], record.IP[2], record.IP[3]},
},
})
}
response := dnsmessage.Message{

View File

@@ -1,6 +1,7 @@
package resolver
import (
"dnsthingymagik/resolver/entities"
"dnsthingymagik/resolver/query"
"golang.org/x/net/dns/dnsmessage"
"log"
@@ -8,7 +9,7 @@ import (
"strings"
)
func ResolveDN(domainName string, id uint16, rtype dnsmessage.Type) ([]net.IP, error) {
func ResolveDN(domainName string, id uint16, rtype dnsmessage.Type) ([]entities.Record, error) {
ROOT_SERVERS := []string{
"198.41.0.4",
}
@@ -21,8 +22,8 @@ func ResolveDN(domainName string, id uint16, rtype dnsmessage.Type) ([]net.IP, e
return result, nil
}
func resolveFromRoot(domainName string, id uint16, rtype dnsmessage.Type, ROOT_SERVERS []string) ([]net.IP, error) {
var IPs []net.IP
func resolveFromRoot(domainName string, id uint16, rtype dnsmessage.Type, ROOT_SERVERS []string) ([]entities.Record, error) {
var records []entities.Record
if !strings.HasSuffix(domainName, ".") {
domainName += "."
@@ -54,7 +55,14 @@ func resolveFromRoot(domainName string, id uint16, rtype dnsmessage.Type, ROOT_S
for _, answer := range response.Answers {
if answer.Header.Type == dnsmessage.TypeA && response.Header.Authoritative {
NSs = append(NSs, answer.Header.Name.String())
IPs = append(IPs, answer.Body.(*dnsmessage.AResource).A[:])
r := entities.Record{
IP: answer.Body.(*dnsmessage.AResource).A[:],
RType: answer.Header.Type,
TTL: answer.Header.TTL,
Class: answer.Header.Class,
Name: answer.Header.Name,
}
records = append(records, r)
} else if answer.Header.Type == dnsmessage.TypeNS {
NSs = append(NSs, answer.Header.Name.String())
}
@@ -75,15 +83,15 @@ func resolveFromRoot(domainName string, id uint16, rtype dnsmessage.Type, ROOT_S
}
}
if len(IPs) > 0 && response.Header.Authoritative {
return IPs, nil
if len(records) > 0 && response.Header.Authoritative {
return records, nil
}
//resolve from refferal
for nameserver, nsip := range NSIPmap {
if nsip == nil {
nsips, err := ResolveDN(nameserver, id, dnsmessage.TypeA) // resolve nameserver if no ip
nsip = nsips[0]
nsip = nsips[0].IP
if err != nil || len(nsip) == 0 {
log.Printf("DNS server %s not resolved: %s", nameserver, err)
continue
@@ -98,15 +106,15 @@ func resolveFromRoot(domainName string, id uint16, rtype dnsmessage.Type, ROOT_S
continue
}
IPs = append(IPs, ip...)
records = append(records, ip...)
break
}
}
return IPs, nil
return records, nil
}
func resolveFromRefferal(domainName string, id uint16, rtype dnsmessage.Type, server string) ([]net.IP, error) {
func resolveFromRefferal(domainName string, id uint16, rtype dnsmessage.Type, server string) ([]entities.Record, error) {
if !strings.HasSuffix(domainName, ".") {
domainName += "."
}
@@ -129,12 +137,19 @@ func resolveFromRefferal(domainName string, id uint16, rtype dnsmessage.Type, se
return nil, err
}
var IPs []net.IP
var records []entities.Record
NSIPmap := make(map[string]net.IP)
for _, answer := range response.Answers {
if answer.Header.Type == dnsmessage.TypeA && response.Header.Authoritative {
IPs = append(IPs, answer.Body.(*dnsmessage.AResource).A[:])
r := entities.Record{
IP: answer.Body.(*dnsmessage.AResource).A[:],
RType: answer.Header.Type,
TTL: answer.Header.TTL,
Class: answer.Header.Class,
Name: answer.Header.Name,
}
records = append(records, r)
} else if answer.Header.Type == dnsmessage.TypeNS {
NSIPmap[answer.Body.(*dnsmessage.NSResource).NS.String()] = nil
}
@@ -152,14 +167,14 @@ func resolveFromRefferal(domainName string, id uint16, rtype dnsmessage.Type, se
}
}
if len(IPs) > 0 && response.Header.Authoritative {
return IPs, nil
if len(records) > 0 && response.Header.Authoritative {
return records, nil
}
for nameserver, nsip := range NSIPmap {
if nsip == nil {
nsips, err := ResolveDN(nameserver, id, dnsmessage.TypeA) // resolve nameserver if no ip
nsip = nsips[0]
nsip = nsips[0].IP
if err != nil || len(nsip) == 0 {
log.Printf("DNS server %s not resolved: %s", nameserver, err)
continue
@@ -174,11 +189,11 @@ func resolveFromRefferal(domainName string, id uint16, rtype dnsmessage.Type, se
continue
}
IPs = append(IPs, ip...)
records = append(records, ip...)
break
}
return IPs, nil
return records, nil
}
//func ProcessDNSResponse(res dnsmessage.Message) ([]net.IP, []dnsmessage.Resource, map[string]net.IP, string, error) {

View File

@@ -0,0 +1,14 @@
package entities
import (
"golang.org/x/net/dns/dnsmessage"
"net"
)
type Record struct {
IP net.IP
RType dnsmessage.Type
TTL uint32
Class dnsmessage.Class
Name dnsmessage.Name
}