labels.go 3.6 KB

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