package dnsutils import ( "math/rand" "net" "sort" ) // OrderedSRV returns a count of the results and a map keyed on the order they should be used. // This based on the records' priority and randomised selection based on their relative weighting. // The function's inputs are the same as those for net.LookupSRV // To use in the correct order: // // count, orderedSRV, err := OrderedSRV(service, proto, name) // i := 1 // for i <= count { // srv := orderedSRV[i] // // Do something such as dial this SRV. If fails move on the the next or break if it succeeds. // i += 1 // } func OrderedSRV(service, proto, name string) (int, map[int]*net.SRV, error) { _, addrs, err := net.LookupSRV(service, proto, name) if err != nil { return 0, make(map[int]*net.SRV), err } index, osrv := orderSRV(addrs) return index, osrv, nil } func orderSRV(addrs []*net.SRV) (int, map[int]*net.SRV) { // Initialise the ordered map var o int osrv := make(map[int]*net.SRV) prioMap := make(map[int][]*net.SRV, 0) for _, srv := range addrs { prioMap[int(srv.Priority)] = append(prioMap[int(srv.Priority)], srv) } priorities := make([]int, 0) for p := range prioMap { priorities = append(priorities, p) } var count int sort.Ints(priorities) for _, p := range priorities { tos := weightedOrder(prioMap[p]) for i, s := range tos { count += 1 osrv[o+i] = s } o += len(tos) } return count, osrv } func weightedOrder(srvs []*net.SRV) map[int]*net.SRV { // Get the total weight var tw int for _, s := range srvs { tw += int(s.Weight) } // Initialise the ordered map o := 1 osrv := make(map[int]*net.SRV) // Whilst there are still entries to be ordered l := len(srvs) for l > 0 { i := rand.Intn(l) s := srvs[i] var rw int if tw > 0 { // Greater the weight the more likely this will be zero or less rw = rand.Intn(tw) - int(s.Weight) } if rw <= 0 { // Put entry in position osrv[o] = s if len(srvs) > 1 { // Remove the entry from the source slice by swapping with the last entry and truncating srvs[len(srvs)-1], srvs[i] = srvs[i], srvs[len(srvs)-1] srvs = srvs[:len(srvs)-1] l = len(srvs) } else { l = 0 } o += 1 tw = tw - int(s.Weight) } } return osrv }