Skip to content
Snippets Groups Projects
labels.go 4.11 KiB
Newer Older
  • Learn to ignore specific revisions
  • package dns
    
    // Holds a bunch of helper functions for dealing with labels.
    
    // SplitDomainName splits a name string into it's labels.
    // www.miek.nl. returns []string{"www", "miek", "nl"}
    
    ale's avatar
    ale committed
    // .www.miek.nl. returns []string{"", "www", "miek", "nl"},
    
    // The root label (.) returns nil. Note that using
    // strings.Split(s) will work in most cases, but does not handle
    // escaped dots (\.) for instance.
    
    ale's avatar
    ale committed
    // s must be a syntactically valid domain name, see IsDomainName.
    
    func SplitDomainName(s string) (labels []string) {
    
    		return nil
    	}
    	fqdnEnd := 0 // offset of the final '.' or the length of the name
    	idx := Split(s)
    	begin := 0
    
    	if IsFqdn(s) {
    
    		fqdnEnd = len(s) - 1
    	} else {
    		fqdnEnd = len(s)
    	}
    
    	switch len(idx) {
    	case 0:
    		return nil
    	case 1:
    		// no-op
    	default:
    
    		for _, end := range idx[1:] {
    
    			labels = append(labels, s[begin:end-1])
    			begin = end
    		}
    	}
    
    
    	return append(labels, s[begin:fqdnEnd])
    
    }
    
    // CompareDomainName compares the names s1 and s2 and
    // returns how many labels they have in common starting from the *right*.
    
    ale's avatar
    ale committed
    // The comparison stops at the first inequality. The names are downcased
    
    // before the comparison.
    //
    // www.miek.nl. and miek.nl. have two labels in common: miek and nl
    // www.miek.nl. and www.bla.nl. have one label in common: nl
    
    ale's avatar
    ale committed
    //
    // s1 and s2 must be syntactically valid domain names.
    
    func CompareDomainName(s1, s2 string) (n int) {
    	// the first check: root label
    
    ale's avatar
    ale committed
    	if s1 == "." || s2 == "." {
    		return 0
    
    ale's avatar
    ale committed
    	l1 := Split(s1)
    	l2 := Split(s2)
    
    
    	j1 := len(l1) - 1 // end
    	i1 := len(l1) - 2 // start
    	j2 := len(l2) - 1
    	i2 := len(l2) - 2
    	// the second check can be done here: last/only label
    	// before we fall through into the for-loop below
    
    ale's avatar
    ale committed
    	if equal(s1[l1[j1]:], s2[l2[j2]:]) {
    
    		n++
    	} else {
    		return
    	}
    	for {
    		if i1 < 0 || i2 < 0 {
    			break
    		}
    
    ale's avatar
    ale committed
    		if equal(s1[l1[i1]:l1[j1]], s2[l2[i2]:l2[j2]]) {
    
    // CountLabel counts the number of labels in the string s.
    
    ale's avatar
    ale committed
    // s must be a syntactically valid domain name.
    
    func CountLabel(s string) (labels int) {
    	if s == "." {
    		return
    	}
    	off := 0
    	end := false
    	for {
    		off, end = NextLabel(s, off)
    		labels++
    		if end {
    			return
    		}
    	}
    }
    
    // Split splits a name s into its label indexes.
    // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
    // The root name (.) returns nil. Also see SplitDomainName.
    
    ale's avatar
    ale committed
    // s must be a syntactically valid domain name.
    
    func Split(s string) []int {
    	if s == "." {
    		return nil
    	}
    	idx := make([]int, 1, 3)
    	off := 0
    	end := false
    
    	for {
    		off, end = NextLabel(s, off)
    		if end {
    			return idx
    		}
    		idx = append(idx, off)
    	}
    }
    
    // NextLabel returns the index of the start of the next label in the
    // string s starting at offset.
    // The bool end is true when the end of the string has been reached.
    // Also see PrevLabel.
    func NextLabel(s string, offset int) (i int, end bool) {
    
    	if s == "" {
    		return 0, true
    	}
    
    	for i = offset; i < len(s)-1; i++ {
    
    		if s[i] != '.' {
    			continue
    		}
    		j := i - 1
    		for j >= 0 && s[j] == '\\' {
    			j--
    		}
    
    		if (j-i)%2 == 0 {
    			continue
    
    
    		return i + 1, false
    
    	}
    	return i + 1, true
    }
    
    // PrevLabel returns the index of the label when starting from the right and
    // jumping n labels to the left.
    // The bool start is true when the start of the string has been overshot.
    // Also see NextLabel.
    func PrevLabel(s string, n int) (i int, start bool) {
    
    	if s == "" {
    		return 0, true
    	}
    
    	if n == 0 {
    		return len(s), false
    	}
    
    
    	l := len(s) - 1
    	if s[l] == '.' {
    		l--
    
    
    	for ; l >= 0 && n > 0; l-- {
    		if s[l] != '.' {
    			continue
    		}
    		j := l - 1
    		for j >= 0 && s[j] == '\\' {
    			j--
    		}
    
    		if (j-l)%2 == 0 {
    			continue
    		}
    
    		n--
    		if n == 0 {
    			return l + 1, false
    		}
    
    
    	return 0, n > 1
    
    ale's avatar
    ale committed
    
    // equal compares a and b while ignoring case. It returns true when equal otherwise false.
    func equal(a, b string) bool {
    	// might be lifted into API function.
    	la := len(a)
    	lb := len(b)
    	if la != lb {
    		return false
    	}
    
    	for i := la - 1; i >= 0; i-- {
    		ai := a[i]
    		bi := b[i]
    		if ai >= 'A' && ai <= 'Z' {
    			ai |= 'a' - 'A'
    		}
    		if bi >= 'A' && bi <= 'Z' {
    			bi |= 'a' - 'A'
    		}
    		if ai != bi {
    			return false
    		}
    	}
    	return true
    }