github.com/mailgun/holster/v4@v4.20.0/discovery/srv.go (about) 1 package discovery 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/mailgun/holster/v4/errors" 9 "github.com/miekg/dns" 10 ) 11 12 // Given a DNS name return a list of addresses returned by the 13 // getting the SRV records from DNS for that name. 14 func GetSRVAddresses(dnsName, dnsServer string) ([]string, error) { 15 if dnsServer != "" { 16 return directLookupSRV(dnsName, dnsServer) 17 } 18 return builtinLookupSRV(dnsName) 19 } 20 21 // Queries the DNS server, by passing /etc/resolv.conf 22 func directLookupSRV(dnsName, dnsAddr string) ([]string, error) { 23 if !dns.IsFqdn(dnsName) { 24 dnsName += "." 25 } 26 27 c := new(dns.Client) 28 c.Timeout = time.Second * 3 29 30 m := new(dns.Msg) 31 m.SetQuestion(dnsName, dns.TypeSRV) 32 r, _, err := c.Exchange(m, dnsAddr) 33 if err != nil { 34 return nil, err 35 } 36 37 if r.Rcode != dns.RcodeSuccess { 38 return nil, fmt.Errorf("no SRV records found for '%s'", dnsName) 39 } 40 41 var results []string 42 for _, a := range r.Answer { 43 srv := a.(*dns.SRV) 44 if net.ParseIP(srv.Target) == nil { 45 srv.Target = findARecord(srv.Target, r.Extra) 46 } 47 results = append(results, fmt.Sprintf("%s:%d", srv.Target, srv.Port)) 48 } 49 return results, nil 50 } 51 52 // Attempts to find an 'A' record within the extra (additional answer section) of the DNS response 53 func findARecord(target string, extra []dns.RR) string { 54 for _, item := range extra { 55 if a, ok := item.(*dns.A); ok { 56 if a.Hdr.Name == target { 57 return a.A.String() 58 } 59 } 60 } 61 return target 62 } 63 64 // Uses the builtin golang net.LookupSRV on systems that have their 65 // /etc/resolv.conf configured correctly 66 func builtinLookupSRV(dnsName string) ([]string, error) { 67 _, records, err := net.LookupSRV("", "", dnsName) 68 if err != nil { 69 return nil, err 70 } 71 72 var results []string 73 for _, srv := range records { 74 if net.ParseIP(srv.Target) == nil { 75 addresses, err := net.LookupHost(srv.Target) 76 if err != nil { 77 return results, errors.Wrapf(err, "while looking up A record for '%s'", srv.Target) 78 } 79 if len(addresses) == 0 { 80 return results, errors.Wrapf(err, "no A records found for '%s'", srv.Target) 81 } 82 srv.Target = addresses[0] 83 } 84 results = append(results, fmt.Sprintf("%s:%d", srv.Target, srv.Port)) 85 } 86 return results, nil 87 }