sanitize.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package dns
  2. // Dedup removes identical RRs from rrs. It preserves the original ordering.
  3. // The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
  4. // rrs.
  5. // m is used to store the RRs temporay. If it is nil a new map will be allocated.
  6. func Dedup(rrs []RR, m map[string]RR) []RR {
  7. if m == nil {
  8. m = make(map[string]RR)
  9. }
  10. // Save the keys, so we don't have to call normalizedString twice.
  11. keys := make([]*string, 0, len(rrs))
  12. for _, r := range rrs {
  13. key := normalizedString(r)
  14. keys = append(keys, &key)
  15. if _, ok := m[key]; ok {
  16. // Shortest TTL wins.
  17. if m[key].Header().Ttl > r.Header().Ttl {
  18. m[key].Header().Ttl = r.Header().Ttl
  19. }
  20. continue
  21. }
  22. m[key] = r
  23. }
  24. // If the length of the result map equals the amount of RRs we got,
  25. // it means they were all different. We can then just return the original rrset.
  26. if len(m) == len(rrs) {
  27. return rrs
  28. }
  29. j := 0
  30. for i, r := range rrs {
  31. // If keys[i] lives in the map, we should copy and remove it.
  32. if _, ok := m[*keys[i]]; ok {
  33. delete(m, *keys[i])
  34. rrs[j] = r
  35. j++
  36. }
  37. if len(m) == 0 {
  38. break
  39. }
  40. }
  41. return rrs[:j]
  42. }
  43. // normalizedString returns a normalized string from r. The TTL
  44. // is removed and the domain name is lowercased. We go from this:
  45. // DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
  46. // lowercasename<TAB>CLASS<TAB>TYPE...
  47. func normalizedString(r RR) string {
  48. // A string Go DNS makes has: domainname<TAB>TTL<TAB>...
  49. b := []byte(r.String())
  50. // find the first non-escaped tab, then another, so we capture where the TTL lives.
  51. esc := false
  52. ttlStart, ttlEnd := 0, 0
  53. for i := 0; i < len(b) && ttlEnd == 0; i++ {
  54. switch {
  55. case b[i] == '\\':
  56. esc = !esc
  57. case b[i] == '\t' && !esc:
  58. if ttlStart == 0 {
  59. ttlStart = i
  60. continue
  61. }
  62. if ttlEnd == 0 {
  63. ttlEnd = i
  64. }
  65. case b[i] >= 'A' && b[i] <= 'Z' && !esc:
  66. b[i] += 32
  67. default:
  68. esc = false
  69. }
  70. }
  71. // remove TTL.
  72. copy(b[ttlStart:], b[ttlEnd:])
  73. cut := ttlEnd - ttlStart
  74. return string(b[:len(b)-cut])
  75. }