srv.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package dnsutils
  2. import (
  3. "math/rand"
  4. "net"
  5. "sort"
  6. )
  7. // OrderedSRV returns a count of the results and a map keyed on the order they should be used.
  8. // This based on the records' priority and randomised selection based on their relative weighting.
  9. // The function's inputs are the same as those for net.LookupSRV
  10. // To use in the correct order:
  11. //
  12. // count, orderedSRV, err := OrderedSRV(service, proto, name)
  13. // i := 1
  14. // for i <= count {
  15. // srv := orderedSRV[i]
  16. // // Do something such as dial this SRV. If fails move on the the next or break if it succeeds.
  17. // i += 1
  18. // }
  19. func OrderedSRV(service, proto, name string) (int, map[int]*net.SRV, error) {
  20. _, addrs, err := net.LookupSRV(service, proto, name)
  21. if err != nil {
  22. return 0, make(map[int]*net.SRV), err
  23. }
  24. index, osrv := orderSRV(addrs)
  25. return index, osrv, nil
  26. }
  27. func orderSRV(addrs []*net.SRV) (int, map[int]*net.SRV) {
  28. // Initialise the ordered map
  29. var o int
  30. osrv := make(map[int]*net.SRV)
  31. prioMap := make(map[int][]*net.SRV, 0)
  32. for _, srv := range addrs {
  33. prioMap[int(srv.Priority)] = append(prioMap[int(srv.Priority)], srv)
  34. }
  35. priorities := make([]int, 0)
  36. for p := range prioMap {
  37. priorities = append(priorities, p)
  38. }
  39. var count int
  40. sort.Ints(priorities)
  41. for _, p := range priorities {
  42. tos := weightedOrder(prioMap[p])
  43. for i, s := range tos {
  44. count += 1
  45. osrv[o+i] = s
  46. }
  47. o += len(tos)
  48. }
  49. return count, osrv
  50. }
  51. func weightedOrder(srvs []*net.SRV) map[int]*net.SRV {
  52. // Get the total weight
  53. var tw int
  54. for _, s := range srvs {
  55. tw += int(s.Weight)
  56. }
  57. // Initialise the ordered map
  58. o := 1
  59. osrv := make(map[int]*net.SRV)
  60. // Whilst there are still entries to be ordered
  61. l := len(srvs)
  62. for l > 0 {
  63. i := rand.Intn(l)
  64. s := srvs[i]
  65. var rw int
  66. if tw > 0 {
  67. // Greater the weight the more likely this will be zero or less
  68. rw = rand.Intn(tw) - int(s.Weight)
  69. }
  70. if rw <= 0 {
  71. // Put entry in position
  72. osrv[o] = s
  73. if len(srvs) > 1 {
  74. // Remove the entry from the source slice by swapping with the last entry and truncating
  75. srvs[len(srvs)-1], srvs[i] = srvs[i], srvs[len(srvs)-1]
  76. srvs = srvs[:len(srvs)-1]
  77. l = len(srvs)
  78. } else {
  79. l = 0
  80. }
  81. o += 1
  82. tw = tw - int(s.Weight)
  83. }
  84. }
  85. return osrv
  86. }