gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }