nebula/dns_server.go

126 lines
2.6 KiB
Go

package nebula
import (
"fmt"
"net"
"strconv"
"sync"
"github.com/miekg/dns"
)
// This whole thing should be rewritten to use context
var dnsR *dnsRecords
type dnsRecords struct {
sync.RWMutex
dnsMap map[string]string
hostMap *HostMap
}
func newDnsRecords(hostMap *HostMap) *dnsRecords {
return &dnsRecords{
dnsMap: make(map[string]string),
hostMap: hostMap,
}
}
func (d *dnsRecords) Query(data string) string {
d.RLock()
if r, ok := d.dnsMap[data]; ok {
d.RUnlock()
return r
}
d.RUnlock()
return ""
}
func (d *dnsRecords) QueryCert(data string) string {
ip := net.ParseIP(data[:len(data)-1])
if ip == nil {
return ""
}
iip := ip2int(ip)
hostinfo, err := d.hostMap.QueryVpnIP(iip)
if err != nil {
return ""
}
q := hostinfo.GetCert()
if q == nil {
return ""
}
cert := q.Details
c := fmt.Sprintf("\"Name: %s\" \"Ips: %s\" \"Subnets %s\" \"Groups %s\" \"NotBefore %s\" \"NotAFter %s\" \"PublicKey %x\" \"IsCA %t\" \"Issuer %s\"", cert.Name, cert.Ips, cert.Subnets, cert.Groups, cert.NotBefore, cert.NotAfter, cert.PublicKey, cert.IsCA, cert.Issuer)
return c
}
func (d *dnsRecords) Add(host, data string) {
d.Lock()
d.dnsMap[host] = data
d.Unlock()
}
func parseQuery(m *dns.Msg, w dns.ResponseWriter) {
for _, q := range m.Question {
switch q.Qtype {
case dns.TypeA:
l.Debugf("Query for A %s", q.Name)
ip := dnsR.Query(q.Name)
if ip != "" {
rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, ip))
if err == nil {
m.Answer = append(m.Answer, rr)
}
}
case dns.TypeTXT:
a, _, _ := net.SplitHostPort(w.RemoteAddr().String())
b := net.ParseIP(a)
// We don't answer these queries from non nebula nodes or localhost
//l.Debugf("Does %s contain %s", b, dnsR.hostMap.vpnCIDR)
if !dnsR.hostMap.vpnCIDR.Contains(b) && a != "127.0.0.1" {
return
}
l.Debugf("Query for TXT %s", q.Name)
ip := dnsR.QueryCert(q.Name)
if ip != "" {
rr, err := dns.NewRR(fmt.Sprintf("%s TXT %s", q.Name, ip))
if err == nil {
m.Answer = append(m.Answer, rr)
}
}
}
}
}
func handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
m.Compress = false
switch r.Opcode {
case dns.OpcodeQuery:
parseQuery(m, w)
}
w.WriteMsg(m)
}
func dnsMain(hostMap *HostMap) {
dnsR = newDnsRecords(hostMap)
// attach request handler func
dns.HandleFunc(".", handleDnsRequest)
// start server
port := 53
server := &dns.Server{Addr: ":" + strconv.Itoa(port), Net: "udp"}
l.Debugf("Starting DNS responder at %d\n", port)
err := server.ListenAndServe()
defer server.Shutdown()
if err != nil {
l.Errorf("Failed to start server: %s\n ", err.Error())
}
}