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()) } }