// Resolve wraps Unbound's ub_resolve. func (u *Unbound) Resolve(name string, rrtype, rrclass uint16) (*Result, error) { res := C.new_ub_result() r := new(Result) defer C.ub_resolve_free(res) i := C.ub_resolve(u.ctx, C.CString(name), C.int(rrtype), C.int(rrclass), &res) err := newError(int(i)) if err != nil { return nil, err } r.Qname = C.GoString(res.qname) r.Qtype = uint16(res.qtype) r.Qclass = uint16(res.qclass) r.CanonName = C.GoString(res.canonname) r.Rcode = int(res.rcode) r.AnswerPacket = new(dns.Msg) r.AnswerPacket.Unpack(C.GoBytes(res.answer_packet, res.answer_len)) // Should always work r.HaveData = res.havedata == 1 r.NxDomain = res.nxdomain == 1 r.Secure = res.secure == 1 r.Bogus = res.bogus == 1 r.WhyBogus = C.GoString(res.why_bogus) // Re-create the RRs var h dns.RR_Header h.Name = r.Qname h.Rrtype = r.Qtype h.Class = r.Qclass h.Ttl = 0 r.Data = make([][]byte, 0) r.Rr = make([]dns.RR, 0) j := 0 if r.HaveData { b := C.GoBytes(unsafe.Pointer(C.array_elem_char(res.data, C.int(j))), C.array_elem_int(res.len, C.int(j))) for len(b) != 0 { // Create the RR h.Rdlength = uint16(len(b)) msg := make([]byte, 20+len(h.Name)) // Long enough off, _ := dns.PackStruct(&h, msg, 0) msg = msg[:off] rrbuf := append(msg, b...) rr, _, err := dns.UnpackRR(rrbuf, 0) if err == nil { r.Rr = append(r.Rr, rr) } r.Data = append(r.Data, b) j++ b = C.GoBytes(unsafe.Pointer(C.array_elem_char(res.data, C.int(j))), C.array_elem_int(res.len, C.int(j))) } } return r, err }
func setupZoneData(data map[string]interface{}, Zone *Zone) { recordTypes := map[string]uint16{ "a": dns.TypeA, "aaaa": dns.TypeAAAA, "ns": dns.TypeNS, "cname": dns.TypeCNAME, "alias": dns.TypeMF, } for dk, dv_inter := range data { dv := dv_inter.(map[string]interface{}) //log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv) dk = strings.ToLower(dk) Zone.Labels[dk] = new(Label) label := Zone.Labels[dk] label.Label = dk label.Ttl = Zone.Options.Ttl label.MaxHosts = Zone.Options.MaxHosts if ttl, ok := dv["ttl"]; ok { label.Ttl = valueToInt(ttl) } if maxHosts, ok := dv["max_hosts"]; ok { label.MaxHosts = valueToInt(maxHosts) } for rType, dnsType := range recordTypes { rdata := dv[rType] if rdata == nil { //log.Printf("No %s records for label %s\n", rType, dk) continue } //log.Printf("rdata %s TYPE-R %T\n", rdata, rdata) records := make(map[string][]interface{}) switch rdata.(type) { case map[string]interface{}: // Handle NS map syntax, map[ns2.example.net:<nil> ns1.example.net:<nil>] tmp := make([]interface{}, 0) for rdata_k, rdata_v := range rdata.(map[string]interface{}) { if rdata_v == nil { rdata_v = "" } tmp = append(tmp, []string{rdata_k, rdata_v.(string)}) } records[rType] = tmp case string: // CNAME and alias tmp := make([]interface{}, 1) tmp[0] = rdata.(string) records[rType] = tmp default: records[rType] = rdata.([]interface{}) } //log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records) if label.Records == nil { label.Records = make(map[uint16]Records) label.Weight = make(map[uint16]int) } label.Records[dnsType] = make(Records, len(records[rType])) for i := 0; i < len(records[rType]); i++ { //log.Printf("RT %T %#v\n", records[rType][i], records[rType][i]) record := new(Record) var h dns.RR_Header // log.Println("TTL OPTIONS", Zone.Options.Ttl) h.Ttl = uint32(label.Ttl) h.Class = dns.ClassINET h.Rrtype = dnsType h.Name = label.Label + "." + Zone.Origin + "." switch dnsType { case dns.TypeA, dns.TypeAAAA: rec := records[rType][i].([]interface{}) ip := rec[0].(string) var err error switch rec[1].(type) { case string: record.Weight, err = strconv.Atoi(rec[1].(string)) if err != nil { panic("Error converting weight to integer") } label.Weight[dnsType] += record.Weight case float64: record.Weight = int(rec[1].(float64)) label.Weight[dnsType] += record.Weight } switch dnsType { case dns.TypeA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.RR_A{Hdr: h, A: x} break } panic("Bad A record") case dns.TypeAAAA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.RR_AAAA{Hdr: h, AAAA: x} break } panic("Bad AAAA record") } case dns.TypeCNAME: rec := records[rType][i] record.RR = &dns.RR_CNAME{Hdr: h, Target: dns.Fqdn(rec.(string))} case dns.TypeMF: rec := records[rType][i] // MF records (how we store aliases) are not FQDNs record.RR = &dns.RR_MF{Hdr: h, Mf: rec.(string)} case dns.TypeNS: rec := records[rType][i] if h.Ttl < 86400 { h.Ttl = 86400 } var ns string switch rec.(type) { case string: ns = rec.(string) case []string: recl := rec.([]string) ns = recl[0] if len(recl[1]) > 0 { log.Println("NS records with names syntax not supported") } default: log.Printf("Data: %T %#v\n", rec, rec) panic("Unrecognized NS format/syntax") } rr := &dns.RR_NS{Hdr: h, Ns: dns.Fqdn(ns)} record.RR = rr default: log.Println("type:", rType) panic("Don't know how to handle this type") } if record.RR == nil { panic("record.RR is nil") } label.Records[dnsType][i] = *record } if label.Weight[dnsType] > 0 { sort.Sort(RecordsByWeight{label.Records[dnsType]}) } } } setupSOA(Zone) //log.Println(Zones[k]) }
func setupZoneData(data map[string]interface{}, Zone *Zone) { recordTypes := map[string]uint16{ "a": dns.TypeA, "aaaa": dns.TypeAAAA, "alias": dns.TypeMF, "cname": dns.TypeCNAME, "mx": dns.TypeMX, "ns": dns.TypeNS, "txt": dns.TypeTXT, "spf": dns.TypeSPF, "srv": dns.TypeSRV, "ptr": dns.TypePTR, } for dk, dv_inter := range data { dv := dv_inter.(map[string]interface{}) //log.Printf("K %s V %s TYPE-V %T\n", dk, dv, dv) label := Zone.AddLabel(dk) for rType, rdata := range dv { switch rType { case "max_hosts": label.MaxHosts = valueToInt(rdata) continue case "ttl": label.Ttl = valueToInt(rdata) continue } dnsType, ok := recordTypes[rType] if !ok { log.Printf("Unsupported record type '%s'\n", rType) continue } if rdata == nil { //log.Printf("No %s records for label %s\n", rType, dk) continue } //log.Printf("rdata %s TYPE-R %T\n", rdata, rdata) records := make(map[string][]interface{}) switch rdata.(type) { case map[string]interface{}: // Handle NS map syntax, map[ns2.example.net:<nil> ns1.example.net:<nil>] tmp := make([]interface{}, 0) for rdataK, rdataV := range rdata.(map[string]interface{}) { if rdataV == nil { rdataV = "" } tmp = append(tmp, []string{rdataK, rdataV.(string)}) } records[rType] = tmp case string: // CNAME and alias tmp := make([]interface{}, 1) tmp[0] = rdata.(string) records[rType] = tmp default: records[rType] = rdata.([]interface{}) } //log.Printf("RECORDS %s TYPE-REC %T\n", Records, Records) label.Records[dnsType] = make(Records, len(records[rType])) for i := 0; i < len(records[rType]); i++ { //log.Printf("RT %T %#v\n", records[rType][i], records[rType][i]) record := new(Record) var h dns.RR_Header h.Class = dns.ClassINET h.Rrtype = dnsType // We add the TTL as a last pass because we might not have // processed it yet when we process the record data. switch len(label.Label) { case 0: h.Name = Zone.Origin + "." default: h.Name = label.Label + "." + Zone.Origin + "." } switch dnsType { case dns.TypeA, dns.TypeAAAA, dns.TypePTR: str, weight := getStringWeight(records[rType][i].([]interface{})) ip := str record.Weight = weight switch dnsType { case dns.TypePTR: record.RR = &dns.PTR{Hdr: h, Ptr: ip} break case dns.TypeA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.A{Hdr: h, A: x} break } panic(fmt.Errorf("Bad A record %s for %s", ip, dk)) case dns.TypeAAAA: if x := net.ParseIP(ip); x != nil { record.RR = &dns.AAAA{Hdr: h, AAAA: x} break } panic(fmt.Errorf("Bad AAAA record %s for %s", ip, dk)) } case dns.TypeMX: rec := records[rType][i].(map[string]interface{}) pref := uint16(0) mx := rec["mx"].(string) if !strings.HasSuffix(mx, ".") { mx = mx + "." } if rec["weight"] != nil { record.Weight = valueToInt(rec["weight"]) } if rec["preference"] != nil { pref = uint16(valueToInt(rec["preference"])) } record.RR = &dns.MX{ Hdr: h, Mx: mx, Preference: pref} case dns.TypeSRV: rec := records[rType][i].(map[string]interface{}) priority := uint16(0) srv_weight := uint16(0) port := uint16(0) target := rec["target"].(string) if !dns.IsFqdn(target) { target = target + "." + Zone.Origin } if rec["srv_weight"] != nil { srv_weight = uint16(valueToInt(rec["srv_weight"])) } if rec["port"] != nil { port = uint16(valueToInt(rec["port"])) } if rec["priority"] != nil { priority = uint16(valueToInt(rec["priority"])) } record.RR = &dns.SRV{ Hdr: h, Priority: priority, Weight: srv_weight, Port: port, Target: target} case dns.TypeCNAME: rec := records[rType][i] var target string var weight int switch rec.(type) { case string: target = rec.(string) case []interface{}: target, weight = getStringWeight(rec.([]interface{})) } if !dns.IsFqdn(target) { target = target + "." + Zone.Origin } record.Weight = weight record.RR = &dns.CNAME{Hdr: h, Target: dns.Fqdn(target)} case dns.TypeMF: rec := records[rType][i] // MF records (how we store aliases) are not FQDNs record.RR = &dns.MF{Hdr: h, Mf: rec.(string)} case dns.TypeNS: rec := records[rType][i] if h.Ttl < 86400 { h.Ttl = 86400 } var ns string switch rec.(type) { case string: ns = rec.(string) case []string: recl := rec.([]string) ns = recl[0] if len(recl[1]) > 0 { log.Println("NS records with names syntax not supported") } default: log.Printf("Data: %T %#v\n", rec, rec) panic("Unrecognized NS format/syntax") } rr := &dns.NS{Hdr: h, Ns: dns.Fqdn(ns)} record.RR = rr case dns.TypeTXT: rec := records[rType][i] var txt string switch rec.(type) { case string: txt = rec.(string) case map[string]interface{}: recmap := rec.(map[string]interface{}) if weight, ok := recmap["weight"]; ok { record.Weight = valueToInt(weight) } if t, ok := recmap["txt"]; ok { txt = t.(string) } } if len(txt) > 0 { rr := &dns.TXT{Hdr: h, Txt: []string{txt}} record.RR = rr } else { log.Printf("Zero length txt record for '%s' in '%s'\n", label.Label, Zone.Origin) continue } // Initial SPF support added here, cribbed from the TypeTXT case definition - SPF records should be handled identically case dns.TypeSPF: rec := records[rType][i] var spf string switch rec.(type) { case string: spf = rec.(string) case map[string]interface{}: recmap := rec.(map[string]interface{}) if weight, ok := recmap["weight"]; ok { record.Weight = valueToInt(weight) } if t, ok := recmap["spf"]; ok { spf = t.(string) } } if len(spf) > 0 { rr := &dns.SPF{Hdr: h, Txt: []string{spf}} record.RR = rr } else { log.Printf("Zero length SPF record for '%s' in '%s'\n", label.Label, Zone.Origin) continue } default: log.Println("type:", rType) panic("Don't know how to handle this type") } if record.RR == nil { panic("record.RR is nil") } label.Weight[dnsType] += record.Weight label.Records[dnsType][i] = *record } if label.Weight[dnsType] > 0 { sort.Sort(RecordsByWeight{label.Records[dnsType]}) } } } // loop over exisiting labels, create zone records for missing sub-domains // and set TTLs for k := range Zone.Labels { if strings.Contains(k, ".") { subLabels := strings.Split(k, ".") for i := 1; i < len(subLabels); i++ { subSubLabel := strings.Join(subLabels[i:], ".") if _, ok := Zone.Labels[subSubLabel]; !ok { Zone.AddLabel(subSubLabel) } } } if Zone.Labels[k].Ttl > 0 { for _, records := range Zone.Labels[k].Records { for _, r := range records { r.RR.Header().Ttl = uint32(Zone.Labels[k].Ttl) } } } } setupSOA(Zone) //log.Println(Zones[k]) }