labels.go (4246B)
1 package dns 2 3 // Holds a bunch of helper functions for dealing with labels. 4 5 // SplitDomainName splits a name string into it's labels. 6 // www.miek.nl. returns []string{"www", "miek", "nl"} 7 // .www.miek.nl. returns []string{"", "www", "miek", "nl"}, 8 // The root label (.) returns nil. Note that using 9 // strings.Split(s) will work in most cases, but does not handle 10 // escaped dots (\.) for instance. 11 // s must be a syntactically valid domain name, see IsDomainName. 12 func SplitDomainName(s string) (labels []string) { 13 if s == "" { 14 return nil 15 } 16 fqdnEnd := 0 // offset of the final '.' or the length of the name 17 idx := Split(s) 18 begin := 0 19 if IsFqdn(s) { 20 fqdnEnd = len(s) - 1 21 } else { 22 fqdnEnd = len(s) 23 } 24 25 switch len(idx) { 26 case 0: 27 return nil 28 case 1: 29 // no-op 30 default: 31 for _, end := range idx[1:] { 32 labels = append(labels, s[begin:end-1]) 33 begin = end 34 } 35 } 36 37 return append(labels, s[begin:fqdnEnd]) 38 } 39 40 // CompareDomainName compares the names s1 and s2 and 41 // returns how many labels they have in common starting from the *right*. 42 // The comparison stops at the first inequality. The names are downcased 43 // before the comparison. 44 // 45 // www.miek.nl. and miek.nl. have two labels in common: miek and nl 46 // www.miek.nl. and www.bla.nl. have one label in common: nl 47 // 48 // s1 and s2 must be syntactically valid domain names. 49 func CompareDomainName(s1, s2 string) (n int) { 50 // the first check: root label 51 if s1 == "." || s2 == "." { 52 return 0 53 } 54 55 l1 := Split(s1) 56 l2 := Split(s2) 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 equal(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 equal(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 86 // CountLabel counts the number of labels in the string s. 87 // s must be a syntactically valid domain name. 88 func CountLabel(s string) (labels int) { 89 if s == "." { 90 return 91 } 92 off := 0 93 end := false 94 for { 95 off, end = NextLabel(s, off) 96 labels++ 97 if end { 98 return 99 } 100 } 101 } 102 103 // Split splits a name s into its label indexes. 104 // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. 105 // The root name (.) returns nil. Also see SplitDomainName. 106 // s must be a syntactically valid domain name. 107 func Split(s string) []int { 108 if s == "." { 109 return nil 110 } 111 idx := make([]int, 1, 3) 112 off := 0 113 end := false 114 115 for { 116 off, end = NextLabel(s, off) 117 if end { 118 return idx 119 } 120 idx = append(idx, off) 121 } 122 } 123 124 // NextLabel returns the index of the start of the next label in the 125 // string s starting at offset. A negative offset will cause a panic. 126 // The bool end is true when the end of the string has been reached. 127 // Also see PrevLabel. 128 func NextLabel(s string, offset int) (i int, end bool) { 129 if s == "" { 130 return 0, true 131 } 132 for i = offset; i < len(s)-1; i++ { 133 if s[i] != '.' { 134 continue 135 } 136 j := i - 1 137 for j >= 0 && s[j] == '\\' { 138 j-- 139 } 140 141 if (j-i)%2 == 0 { 142 continue 143 } 144 145 return i + 1, false 146 } 147 return i + 1, true 148 } 149 150 // PrevLabel returns the index of the label when starting from the right and 151 // jumping n labels to the left. 152 // The bool start is true when the start of the string has been overshot. 153 // Also see NextLabel. 154 func PrevLabel(s string, n int) (i int, start bool) { 155 if s == "" { 156 return 0, true 157 } 158 if n == 0 { 159 return len(s), false 160 } 161 162 l := len(s) - 1 163 if s[l] == '.' { 164 l-- 165 } 166 167 for ; l >= 0 && n > 0; l-- { 168 if s[l] != '.' { 169 continue 170 } 171 j := l - 1 172 for j >= 0 && s[j] == '\\' { 173 j-- 174 } 175 176 if (j-l)%2 == 0 { 177 continue 178 } 179 180 n-- 181 if n == 0 { 182 return l + 1, false 183 } 184 } 185 186 return 0, n > 1 187 } 188 189 // equal compares a and b while ignoring case. It returns true when equal otherwise false. 190 func equal(a, b string) bool { 191 // might be lifted into API function. 192 la := len(a) 193 lb := len(b) 194 if la != lb { 195 return false 196 } 197 198 for i := la - 1; i >= 0; i-- { 199 ai := a[i] 200 bi := b[i] 201 if ai >= 'A' && ai <= 'Z' { 202 ai |= 'a' - 'A' 203 } 204 if bi >= 'A' && bi <= 'Z' { 205 bi |= 'a' - 'A' 206 } 207 if ai != bi { 208 return false 209 } 210 } 211 return true 212 }