func NewQuestion(ques dns.Question) (q *Question) { q = &Question{ Question: ques, } q.IsIpQuery = q.isIpQuery() name := strings.ToLower(q.Name) if dns.IsFqdn(name) { q.NameTrim = name[:len(name)-1] } else { q.NameTrim = name } n := strings.LastIndex(q.NameTrim, ".") if n == -1 { return } q.TopDomain = q.NameTrim[n+1:] q.Domain = q.NameTrim[:n] if q.Qtype == dns.TypePTR { addr := strings.Replace(name, ".in-addr.arpa.", "", 1) addrSpl := strings.Split(addr, ".") for i, x := range addrSpl { y, _ := strconv.ParseInt(x, 10, 64) q.AddressNum += y << uint(8*i) } } return }
func NewQuestion(ques dns.Question) (q *Question) { q = &Question{ Question: ques, } q.IsIpQuery = q.isIpQuery() if dns.IsFqdn(q.Name) { q.NameTrim = q.Name[:len(q.Name)-1] } else { q.NameTrim = q.Name } name := q.NameTrim n := strings.LastIndex(name, ".") if n == -1 { return } q.TopDomain = name[n+1:] q.Domain = name[:n] return }
func validate(node *etcd.Node) (bool, string) { recordType := path.Base(node.Key) switch recordType { case "A": return net.ParseIP(node.Value) != nil, "Invalid ip" case "CNAME", "PTR": return dns.IsFqdn(node.Value), "Domain name not fully-qualified" default: return false, "Record type not supported" } }
func (dom Domain) addHandler(tld string) { fqdn := dom.Name if tld != "" { fqdn = dom.Name + "." + tld } FqdnParts, _ := dns.IsDomainName(fqdn) fmt.Printf("Adding: %v - %v nums\n", fqdn, FqdnParts) // Handle dns requests if it is really a fqdn if dns.IsFqdn(fqdn) { dns.HandleFunc(fqdn, func(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) m.Compress = true for i, q := range req.Question { fmt.Printf("Requested: %s, Type: %v\n", req.Question[i].Name, req.Question[i].Qtype) switch q.Qtype { case 1: fmt.Printf("Adding a record %v with ip %v", q.Name, dom.A.Ip) m.Answer = append(m.Answer, NewA(q.Name, dom.A.Ip, uint32(dom.A.Ttl))) case 15: fmt.Printf("Adding a record %v with ip %v", q.Name, dom.A.Ip) m.Answer = append(m.Answer, NewMX(q.Name, dom.Mx.Content, dom.Mx.Priority, uint32(dom.Mx.Ttl))) } } w.WriteMsg(m) }) } // add subdomains.. for _, d := range dom.Domains { d.addHandler(fqdn) } }
func validateStaticEntryFile(sef string) (StaticEntryConfig, error) { if len(sef) == 0 { return StaticEntryConfig{}, nil } if _, err := os.Stat(sef); os.IsNotExist(err) { return StaticEntryConfig{}, fmt.Errorf("StaticEntryFile not found: %s", sef) } conf, err := ParseStaticConfig(sef) for _, entry := range conf.Entries { switch entry.Type { case "A": ip := net.ParseIP(entry.Value) if ip == nil { return conf, fmt.Errorf("Invalid IP on StaticEntry: %q", entry.Value) } if !dns.IsFqdn(entry.Fqdn) { return conf, fmt.Errorf("Invalid FQDN: %s", entry.Fqdn) } break case "SRV": if _, ok := dns.IsDomainName(entry.Fqdn); !ok { return conf, fmt.Errorf("Invalid SRV FQDN: %s", entry.Fqdn) } if match, _ := regexp.MatchString(ValidHostPortRegex, entry.Value); !match { return conf, fmt.Errorf("Invalid (Host:Port) tuple: %s", entry.Value) } break default: return conf, fmt.Errorf("Unsupported Record Type: %s", entry.Type) } } return conf, err }
// AddDomain adds origin to s if s is not already a FQDN. // Note that the result may not be a FQDN. If origin does not end // with a ".", the result won't either. // This implements the zonefile convention (specified in RFC 1035, // Section "5.1. Format") that "@" represents the // apex (bare) domain. i.e. AddOrigin("@", "foo.com.") returns "foo.com.". func AddOrigin(s, origin string) string { // ("foo.", "origin.") -> "foo." (already a FQDN) // ("foo", "origin.") -> "foo.origin." // ("foo"), "origin" -> "foo.origin" // ("@", "origin.") -> "origin." (@ represents the apex (bare) domain) // ("", "origin.") -> "origin." (not obvious) // ("foo", "") -> "foo" (not obvious) if dns.IsFqdn(s) { return s // s is already a FQDN, no need to mess with it. } if len(origin) == 0 { return s // Nothing to append. } if s == "@" || len(s) == 0 { return origin // Expand apex. } if origin == "." { return s + origin // AddOrigin(s, ".") is an expensive way to add a ".". } return s + "." + origin // The simple case. }
func UnFqdn(s string) string { if dns.IsFqdn(s) { return s[:len(s)-1] } return s }
func dnsQuery(r *dns.Msg) (*dns.Msg, error) { dnsServers := GConf.LocalDNS.TrustedDNS var record *dnsCacheRecord var domain string useTrustedDNS := true if len(r.Question) == 1 && dns.IsFqdn(r.Question[0].Name) { domain = r.Question[0].Name domain = domain[0 : len(domain)-1] if nil != dnsCache { item, exist := dnsCache.Get(domain) if exist { record = item.(*dnsCacheRecord) if time.Now().After(record.expireAt) { record = nil dnsCache.Remove(domain) } else { if r.Question[0].Qtype == dns.TypeA && nil != record.ipv4Res { return record.ipv4Res, nil } else if r.Question[0].Qtype == dns.TypeAAAA && nil != record.ipv6Res { return record.ipv6Res, nil } } } } if nil != mygfwlist { connReq, _ := http.NewRequest("CONNECT", "https://"+domain, nil) isBlocked, _ := mygfwlist.FastMatchDoamin(connReq) if !isBlocked { dnsServers = GConf.LocalDNS.FastDNS useTrustedDNS = false } } } else { log.Printf("###DNS with %v", r.Question) } if len(dnsServers) == 0 { dnsServers = GConf.LocalDNS.TrustedDNS useTrustedDNS = true } if len(dnsServers) == 0 { log.Printf("At least one DNS server need to be configured in 'FastDNS/TrustedDNS'") return nil, errNoDNServer } server := selectDNSServer(dnsServers) network := "udp" if GConf.LocalDNS.TCPConnect && useTrustedDNS { network = "tcp" } log.Printf("DNS query %s to %s", domain, server) for retry := 0; retry < 3; retry++ { c, err := netx.DialTimeout(network, server, 1*time.Second) if nil != err { return nil, err } dnsConn := new(dns.Conn) if pc, ok := c.(getConnIntf); ok { c = pc.GetConn() } dnsConn.Conn = c dnsConn.WriteMsg(r) dnsConn.SetReadDeadline(time.Now().Add(1 * time.Second)) res, err1 := dnsConn.ReadMsg() if nil == err1 && nil != dnsCache && len(domain) > 0 { record = newDNSCacheRecord(record, res) dnsCache.Add(domain, record) } c.Close() if nil == err1 { return res, nil } } return nil, errDNSQuryFail }
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]) }
func findA(ednsModel *EDNSModel) string { var domain_a string var server string if len(ednsModel.NS) != 0 { server = ednsModel.NS[0] } else if len(ednsModel.SOA) != 0 { server = ednsModel.SOA[0] } else { server = OPEN_DNS_SERVER } if dns.IsFqdn(server) { server = server[0 : len(server)-1] } if !strings.HasSuffix(server, ":53") { server += ":53" } domain := dns.Fqdn(ednsModel.CName[len(ednsModel.CName)-1]) msg := new(dns.Msg) msg.SetQuestion(domain, dns.TypeA) msg.RecursionDesired = true if ednsModel.ClientIP != "" { opt := new(dns.OPT) opt.Hdr.Name = "." opt.Hdr.Rrtype = dns.TypeOPT e := new(dns.EDNS0_SUBNET) e.Code = dns.EDNS0SUBNET e.Family = 1 // ipv4 e.SourceNetmask = 32 e.SourceScope = 0 e.Address = net.ParseIP(ednsModel.ClientIP).To4() opt.Option = append(opt.Option, e) msg.Extra = []dns.RR{opt} } client := &dns.Client{ DialTimeout: 5 * time.Second, ReadTimeout: 20 * time.Second, WriteTimeout: 20 * time.Second, } resp, rtt, err := client.Exchange(msg, server) // fmt.Println(resp.Answer) if err != nil { fmt.Println(rtt, err) // 记录日志 rtt是查询耗时 return "" } for i := len(resp.Answer) - 1; i >= 0; i-- { switch resp.Answer[i].Header().Rrtype { case dns.TypeA: temp_a := resp.Answer[i].(*dns.A) domain_a += fmt.Sprint(temp_a.A, ":", temp_a.Hdr.Ttl, ",") break case dns.TypeCNAME: temp_cname := resp.Answer[i].(*dns.CNAME) ednsModel.CName = append(ednsModel.CName, temp_cname.Target) break } } return domain_a }