1package 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. 12func SplitDomainName(s string) (labels []string) { 13 if len(s) == 0 { 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 s[len(s)-1] == '.' { 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 end := 0 32 for i := 1; i < len(idx); i++ { 33 end = idx[i] 34 labels = append(labels, s[begin:end-1]) 35 begin = end 36 } 37 } 38 39 labels = append(labels, s[begin:fqdnEnd]) 40 return labels 41} 42 43// CompareDomainName compares the names s1 and s2 and 44// returns how many labels they have in common starting from the *right*. 45// The comparison stops at the first inequality. The names are downcased 46// before the comparison. 47// 48// www.miek.nl. and miek.nl. have two labels in common: miek and nl 49// www.miek.nl. and www.bla.nl. have one label in common: nl 50// 51// s1 and s2 must be syntactically valid domain names. 52func CompareDomainName(s1, s2 string) (n int) { 53 // the first check: root label 54 if s1 == "." || s2 == "." { 55 return 0 56 } 57 58 l1 := Split(s1) 59 l2 := Split(s2) 60 61 j1 := len(l1) - 1 // end 62 i1 := len(l1) - 2 // start 63 j2 := len(l2) - 1 64 i2 := len(l2) - 2 65 // the second check can be done here: last/only label 66 // before we fall through into the for-loop below 67 if equal(s1[l1[j1]:], s2[l2[j2]:]) { 68 n++ 69 } else { 70 return 71 } 72 for { 73 if i1 < 0 || i2 < 0 { 74 break 75 } 76 if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) { 77 n++ 78 } else { 79 break 80 } 81 j1-- 82 i1-- 83 j2-- 84 i2-- 85 } 86 return 87} 88 89// CountLabel counts the the number of labels in the string s. 90// s must be a syntactically valid domain name. 91func CountLabel(s string) (labels int) { 92 if s == "." { 93 return 94 } 95 off := 0 96 end := false 97 for { 98 off, end = NextLabel(s, off) 99 labels++ 100 if end { 101 return 102 } 103 } 104} 105 106// Split splits a name s into its label indexes. 107// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. 108// The root name (.) returns nil. Also see SplitDomainName. 109// s must be a syntactically valid domain name. 110func Split(s string) []int { 111 if s == "." { 112 return nil 113 } 114 idx := make([]int, 1, 3) 115 off := 0 116 end := false 117 118 for { 119 off, end = NextLabel(s, off) 120 if end { 121 return idx 122 } 123 idx = append(idx, off) 124 } 125} 126 127// NextLabel returns the index of the start of the next label in the 128// string s starting at offset. 129// The bool end is true when the end of the string has been reached. 130// Also see PrevLabel. 131func NextLabel(s string, offset int) (i int, end bool) { 132 quote := false 133 for i = offset; i < len(s)-1; i++ { 134 switch s[i] { 135 case '\\': 136 quote = !quote 137 default: 138 quote = false 139 case '.': 140 if quote { 141 quote = !quote 142 continue 143 } 144 return i + 1, false 145 } 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. 154func PrevLabel(s string, n int) (i int, start bool) { 155 if n == 0 { 156 return len(s), false 157 } 158 lab := Split(s) 159 if lab == nil { 160 return 0, true 161 } 162 if n > len(lab) { 163 return 0, true 164 } 165 return lab[len(lab)-n], false 166} 167 168// equal compares a and b while ignoring case. It returns true when equal otherwise false. 169func equal(a, b string) bool { 170 // might be lifted into API function. 171 la := len(a) 172 lb := len(b) 173 if la != lb { 174 return false 175 } 176 177 for i := la - 1; i >= 0; i-- { 178 ai := a[i] 179 bi := b[i] 180 if ai >= 'A' && ai <= 'Z' { 181 ai |= ('a' - 'A') 182 } 183 if bi >= 'A' && bi <= 'Z' { 184 bi |= ('a' - 'A') 185 } 186 if ai != bi { 187 return false 188 } 189 } 190 return true 191} 192