1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

Initial DNS Resolvers and SPF scaffolding (#123)

* Implemented Live and Preloaded resolvers

* Integrated Craig's parser.
This commit is contained in:
Tom Limoncelli
2017-05-25 12:27:36 -04:00
committed by GitHub
parent 20253130cf
commit 01a242480c
7 changed files with 398 additions and 0 deletions

28
dnsresolver/dnscache.go Normal file
View File

@ -0,0 +1,28 @@
package dnsresolver
// dnsCache implements a very simple DNS cache.
// It caches the entire answer (i.e. all TXT records), filtering
// out the non-SPF answers is done at a higher layer.
// At this time the only rtype is "TXT". Eventually we'll need
// to cache A/AAAA/CNAME records to to CNAME flattening.
type dnsCache map[string]map[string][]string // map[fqdn]map[rtype] -> answers
func (c dnsCache) get(label, rtype string) ([]string, bool) {
v1, ok := c[label]
if !ok {
return nil, false
}
v2, ok := v1[rtype]
if !ok {
return nil, false
}
return v2, true
}
func (c dnsCache) put(label, rtype string, answers []string) {
_, ok := c[label]
if !ok {
c[label] = make(map[string][]string)
}
c[label][rtype] = answers
}

View File

@ -0,0 +1,31 @@
package dnsresolver
import "testing"
func TestDnsCache(t *testing.T) {
cache := &dnsCache{}
cache.put("one", "txt", []string{"a", "b", "c"})
cache.put("two", "txt", []string{"d", "e", "f"})
a, b := cache.get("one", "txt")
if !(b == true && len(a) == 3 && a[0] == "a" && a[1] == "b" && a[2] == "c") {
t.Errorf("one-txt didn't work")
}
a, b = cache.get("two", "txt")
if !(b == true && len(a) == 3 && a[0] == "d" && a[1] == "e" && a[2] == "f") {
t.Errorf("one-txt didn't work")
}
a, b = cache.get("three", "txt")
if !(b == false) {
t.Errorf("three-txt didn't work")
}
a, b = cache.get("two", "not")
if !(b == false) {
t.Errorf("two-not didn't work")
}
}

83
dnsresolver/resolver.go Normal file
View File

@ -0,0 +1,83 @@
package dnsresolver
import (
"encoding/json"
"io/ioutil"
"net"
"github.com/pkg/errors"
)
// This file includes all the DNS Resolvers used by package spf.
// DnsResolver looks up txt strings associated with a FQDN.
type DnsResolver interface {
GetTxt(string) ([]string, error) // Given a DNS label, return the TXT values records.
}
// The "Live DNS" Resolver:
type dnsLive struct {
filename string
cache dnsCache
}
func NewResolverLive(filename string) *dnsLive {
// Does live DNS lookups. Records them. Writes file on Close.
c := &dnsLive{filename: filename}
c.cache = dnsCache{}
return c
}
func (c *dnsLive) GetTxt(label string) ([]string, error) {
// Try the cache.
txts, ok := c.cache.get(label, "txt")
if ok {
return txts, nil
}
// Populate the cache:
t, err := net.LookupTXT(label)
if err == nil {
c.cache.put(label, "txt", t)
}
return t, err
}
func (c *dnsLive) Close() {
// Write out and close the file.
m, _ := json.MarshalIndent(c.cache, "", " ")
m = append(m, "\n"...)
ioutil.WriteFile(c.filename, m, 0666)
}
// The "Pre-Cached DNS" Resolver:
type dnsPreloaded struct {
cache dnsCache
}
func NewResolverPreloaded(filename string) (*dnsPreloaded, error) {
c := &dnsPreloaded{}
c.cache = dnsCache{}
j, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
err = json.Unmarshal(j, &(*c).cache)
return c, err
}
func (c *dnsPreloaded) DumpCache() dnsCache {
return c.cache
}
func (c *dnsPreloaded) GetTxt(label string) ([]string, error) {
// Try the cache.
txts, ok := c.cache.get(label, "txt")
if ok {
return txts, nil
}
return nil, errors.Errorf("No preloaded DNS entry for: %#v", label)
}