// FromPunycode returns unicode domain name from provided punycode string. func FromPunycode(s string) string { tokens := dns.SplitDomainName(s) switch { case s == "": return "" case tokens == nil: // s == . return "." case s[len(s)-1] == '.': tokens = append(tokens, "") } for i := range tokens { tokens[i] = string(decode([]byte(tokens[i]))) } return strings.Join(tokens, ".") }
func (d dnsAPI) ServiceLookup(w dns.ResponseWriter, req *dns.Msg) { qName := req.Question[0].Name qType := req.Question[0].Qtype name := strings.TrimSuffix(strings.ToLower(dns.Fqdn(qName)), d.Domain) labels := dns.SplitDomainName(name) tcp := isTCP(w.RemoteAddr()) res := &dns.Msg{} res.Authoritative = true res.RecursionAvailable = len(d.Recursors) > 0 res.SetReply(req) defer func() { if res.Rcode == dns.RcodeSuccess && qType == dns.TypeSOA { // SOA answer if requested. at the end of the request to ensure we didn't hit NXDOMAIN res.Answer = []dns.RR{d.soaRecord()} } if len(res.Answer) == 0 { // Add authority section with SOA if the answer has no items res.Ns = []dns.RR{d.soaRecord()} } w.WriteMsg(res) }() nxdomain := func() { res.SetRcode(req, dns.RcodeNameError) } var service string var proto string var instanceID string var leader bool switch { case len(labels) == 1: // normal lookup service = labels[0] case len(labels) == 2 && strings.HasPrefix(labels[0], "_") && strings.HasPrefix(labels[1], "_"): // RFC 2782 request looks like _postgres._tcp service = labels[0][1:] proto = labels[1][1:] case len(labels) == 3 && labels[2] == "_i": // address lookup for instance in RFC 2782 SRV record service = labels[1] instanceID = labels[0] case len(labels) == 2 && labels[0] == "leader": // leader lookup leader = true service = labels[1] default: nxdomain() return } var instances []*discoverd.Instance if !leader { instances = d.Store.Get(service) if instances == nil { nxdomain() return } } if leader || instanceID != "" { // we're doing a lookup for a single instance var resInst *discoverd.Instance if leader { resInst = d.Store.GetLeader(service) } else { for _, inst := range instances { if inst.ID == instanceID { resInst = inst break } } } if resInst == nil { nxdomain() return } addr := parseAddr(resInst) if qType != dns.TypeA && qType != dns.TypeAAAA && qType != dns.TypeANY && qType != dns.TypeSRV || addr.IPv4 == nil && qType == dns.TypeA || addr.IPv6 == nil && qType == dns.TypeAAAA { // no results if we're looking up an record that doesn't match the // request type or the type is incorrect return } res.Answer = make([]dns.RR, 0, 2) if qType != dns.TypeSRV { res.Answer = append(res.Answer, addrRecord(qName, addr)) } if qType == dns.TypeSRV || qType == dns.TypeANY { res.Answer = append(res.Answer, d.srvRecord(qName, service, addr, false)) } if tcp && qType == dns.TypeSRV { res.Extra = []dns.RR{addrRecord(qName, addr)} } return } if qType == dns.TypeSOA { // We don't need to do any more processing, as NXDOMAIN can't be reached // beyond this point, the SOA answer is added in the deferred function // above return } addrs := make([]*addrData, 0, len(instances)) added := make(map[string]struct{}, len(instances)) for _, inst := range instances { if proto != "" && inst.Proto != proto { continue } addr := parseAddr(inst) if _, ok := added[addr.String]; ok { continue } if addr.IPv4 == nil && qType == dns.TypeA || addr.IPv6 == nil && qType == dns.TypeAAAA { // Skip instance if we have an IPv6 address but want IPv4 or vice versa continue } if qType != dns.TypeSRV { // skip duplicate IPs if we're not doing an SRV lookup added[addr.String] = struct{}{} } addrs = append(addrs, addr) } if len(addrs) == 0 { // return empty response return } shuffle(addrs) // Truncate the response if we're using UDP if !tcp && len(addrs) > maxUDPRecords { addrs = addrs[:maxUDPRecords] } res.Answer = make([]dns.RR, 0, len(addrs)*2) for _, addr := range addrs { if qType == dns.TypeANY || qType == dns.TypeA || qType == dns.TypeAAAA { res.Answer = append(res.Answer, addrRecord(qName, addr)) } } for _, addr := range addrs { if qType == dns.TypeANY || qType == dns.TypeSRV { res.Answer = append(res.Answer, d.srvRecord(qName, service, addr, true)) } } if qType == dns.TypeSRV && tcp { // Add extra records mapping instance IDs to addresses res.Extra = make([]dns.RR, len(addrs)) for i, addr := range addrs { res.Extra[i] = addrRecord(d.instanceDomain(service, addr.ID), addr) } } }