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:
28
dnsresolver/dnscache.go
Normal file
28
dnsresolver/dnscache.go
Normal 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
|
||||
}
|
31
dnsresolver/dnscache_test.go
Normal file
31
dnsresolver/dnscache_test.go
Normal 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
83
dnsresolver/resolver.go
Normal 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)
|
||||
}
|
Reference in New Issue
Block a user