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  }