labels.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package dns
  2. // Holds a bunch of helper functions for dealing with labels.
  3. // SplitDomainName splits a name string into it's labels.
  4. // www.miek.nl. returns []string{"www", "miek", "nl"}
  5. // .www.miek.nl. returns []string{"", "www", "miek", "nl"},
  6. // The root label (.) returns nil. Note that using
  7. // strings.Split(s) will work in most cases, but does not handle
  8. // escaped dots (\.) for instance.
  9. func SplitDomainName(s string) (labels []string) {
  10. if len(s) == 0 {
  11. return nil
  12. }
  13. fqdnEnd := 0 // offset of the final '.' or the length of the name
  14. idx := Split(s)
  15. begin := 0
  16. if s[len(s)-1] == '.' {
  17. fqdnEnd = len(s) - 1
  18. } else {
  19. fqdnEnd = len(s)
  20. }
  21. switch len(idx) {
  22. case 0:
  23. return nil
  24. case 1:
  25. // no-op
  26. default:
  27. end := 0
  28. for i := 1; i < len(idx); i++ {
  29. end = idx[i]
  30. labels = append(labels, s[begin:end-1])
  31. begin = end
  32. }
  33. }
  34. labels = append(labels, s[begin:fqdnEnd])
  35. return labels
  36. }
  37. // CompareDomainName compares the names s1 and s2 and
  38. // returns how many labels they have in common starting from the *right*.
  39. // The comparison stops at the first inequality. The names are not downcased
  40. // before the comparison.
  41. //
  42. // www.miek.nl. and miek.nl. have two labels in common: miek and nl
  43. // www.miek.nl. and www.bla.nl. have one label in common: nl
  44. func CompareDomainName(s1, s2 string) (n int) {
  45. s1 = Fqdn(s1)
  46. s2 = Fqdn(s2)
  47. l1 := Split(s1)
  48. l2 := Split(s2)
  49. // the first check: root label
  50. if l1 == nil || l2 == nil {
  51. return
  52. }
  53. j1 := len(l1) - 1 // end
  54. i1 := len(l1) - 2 // start
  55. j2 := len(l2) - 1
  56. i2 := len(l2) - 2
  57. // the second check can be done here: last/only label
  58. // before we fall through into the for-loop below
  59. if s1[l1[j1]:] == s2[l2[j2]:] {
  60. n++
  61. } else {
  62. return
  63. }
  64. for {
  65. if i1 < 0 || i2 < 0 {
  66. break
  67. }
  68. if s1[l1[i1]:l1[j1]] == s2[l2[i2]:l2[j2]] {
  69. n++
  70. } else {
  71. break
  72. }
  73. j1--
  74. i1--
  75. j2--
  76. i2--
  77. }
  78. return
  79. }
  80. // CountLabel counts the the number of labels in the string s.
  81. func CountLabel(s string) (labels int) {
  82. if s == "." {
  83. return
  84. }
  85. off := 0
  86. end := false
  87. for {
  88. off, end = NextLabel(s, off)
  89. labels++
  90. if end {
  91. return
  92. }
  93. }
  94. }
  95. // Split splits a name s into its label indexes.
  96. // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
  97. // .www.miek.nl. returns []int{0, 1, 5, 10}
  98. // The root name (.) returns nil. Also see SplitDomainName.
  99. func Split(s string) []int {
  100. if s == "." {
  101. return nil
  102. }
  103. idx := make([]int, 1, 3)
  104. off := 0
  105. end := false
  106. for {
  107. off, end = NextLabel(s, off)
  108. if end {
  109. return idx
  110. }
  111. idx = append(idx, off)
  112. }
  113. }
  114. // NextLabel returns the index of the start of the next label in the
  115. // string s starting at offset.
  116. // The bool end is true when the end of the string has been reached.
  117. // Also see PrevLabel.
  118. func NextLabel(s string, offset int) (i int, end bool) {
  119. quote := false
  120. for i = offset; i < len(s)-1; i++ {
  121. switch s[i] {
  122. case '\\':
  123. quote = !quote
  124. default:
  125. quote = false
  126. case '.':
  127. if quote {
  128. quote = !quote
  129. continue
  130. }
  131. return i + 1, false
  132. }
  133. }
  134. return i + 1, true
  135. }
  136. // PrevLabel returns the index of the label when starting from the right and
  137. // jumping n labels to the left.
  138. // The bool start is true when the start of the string has been overshot.
  139. // Also see NextLabel.
  140. func PrevLabel(s string, n int) (i int, start bool) {
  141. if n == 0 {
  142. return len(s), false
  143. }
  144. lab := Split(s)
  145. if lab == nil {
  146. return 0, true
  147. }
  148. if n > len(lab) {
  149. return 0, true
  150. }
  151. return lab[len(lab)-n], false
  152. }