TTLs
This commit is contained in:
@@ -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
42
cache/Records.go
vendored
@@ -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
48
main.go
@@ -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{
|
||||
|
||||
@@ -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) {
|
||||
|
||||
14
resolver/entities/Record.go
Normal file
14
resolver/entities/Record.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user