dns/labels.go

220 lines
4.6 KiB
Go
Raw Normal View History

2012-01-11 01:55:52 +11:00
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"}
// .www.miek.nl. returns []string{"", "www", "miek", "nl"},
2014-09-21 16:43:46 +10:00
// The root label (.) returns nil. Note that using
// strings.Split(s) will work in most cases, but does not handle
// escaped dots (\.) for instance.
// s must be a syntactically valid domain name, see IsDomainName.
2013-06-28 04:46:26 +10:00
func SplitDomainName(s string) (labels []string) {
2014-01-19 09:02:49 +11:00
if len(s) == 0 {
return nil
}
if s == "." {
return nil
}
// offset of the final '.' or the length of the name
var fqdnEnd int
if IsFqdn(s) {
fqdnEnd = len(s) - 1
} else {
fqdnEnd = len(s)
}
var (
begin int
off int
end bool
)
for {
off, end = NextLabel(s, off)
if end {
break
2012-01-11 01:55:52 +11:00
}
labels = append(labels, s[begin:off-1])
begin = off
2012-01-11 01:55:52 +11:00
}
return append(labels, s[begin:fqdnEnd])
2012-01-11 01:55:52 +11:00
}
// CompareDomainName compares the names s1 and s2 and
// returns how many labels they have in common starting from the *right*.
// The comparison stops at the first inequality. The names are downcased
2012-09-05 04:08:55 +10:00
// before the comparison.
2012-01-11 02:04:32 +11:00
//
// 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
//
// s1 and s2 must be syntactically valid domain names.
func CompareDomainName(s1, s2 string) (n int) {
2013-06-21 18:53:24 +10:00
// the first check: root label
if s1 == "." || s2 == "." {
return 0
2013-06-21 06:36:13 +10:00
}
j1 := len(s1)
if s1[j1-1] == '.' {
j1--
}
j2 := len(s2)
if s2[j2-1] == '.' {
j2--
2013-06-21 06:36:13 +10:00
}
var i1, i2 int
2012-01-11 01:55:52 +11:00
for {
i1 = prevLabel(s1, j1-1)
i2 = prevLabel(s2, j2-1)
if equal(s1[i1:j1], s2[i2:j2]) {
n++
} else {
break
}
if i1 == 0 || i2 == 0 {
break
}
j1 = i1 - 2
j2 = i2 - 2
}
return
}
// CountLabel counts the the number of labels in the string s.
// s must be a syntactically valid domain name.
func CountLabel(s string) int {
if s == "." {
return 0
}
labels := 1
for i := 0; i < len(s)-1; i++ {
c := s[i]
if c == '\\' {
i++
continue
}
if c == '.' {
labels++
}
}
return labels
}
2013-06-21 06:36:13 +10:00
// Split splits a name s into its label indexes.
2013-09-12 18:03:09 +10:00
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
2016-05-12 16:43:16 +10:00
// The root name (.) returns nil. Also see SplitDomainName.
// s must be a syntactically valid domain name.
2013-06-21 06:36:13 +10:00
func Split(s string) []int {
if s == "." {
2013-06-21 06:36:13 +10:00
return nil
}
2014-01-26 06:21:46 +11:00
idx := make([]int, 1, 3)
off := 0
end := false
2013-06-21 06:36:13 +10:00
for {
2013-06-22 01:31:19 +10:00
off, end = NextLabel(s, off)
if end {
2013-06-21 06:36:13 +10:00
return idx
}
2013-06-21 06:36:13 +10:00
idx = append(idx, off)
}
}
2013-06-22 01:31:19 +10:00
// 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.
2015-08-23 21:55:01 +10:00
// Also see PrevLabel.
2013-06-22 01:23:31 +10:00
func NextLabel(s string, offset int) (i int, end bool) {
2013-06-21 06:36:13 +10:00
for i = offset; i < len(s)-1; i++ {
c := s[i]
if c == '\\' {
i++
continue
}
if c == '.' {
2013-06-21 06:36:13 +10:00
return i + 1, false
}
}
2013-06-21 06:36:13 +10:00
return i + 1, true
}
func prevLabel(s string, offset int) int {
for i := offset; i >= 0; i-- {
if s[i] == '.' {
if i == 0 || s[i-1] != '\\' {
return i + 1 // the '.' is not escaped
}
// We are at '\.' and need to check if the '\' itself is escaped.
// We do this by walking backwards from '\.' and counting the
// number of '\' we encounter. If the number of '\' is even
// (though here it's actually odd since we start at '\.') the '\'
// is escaped.
j := i - 2
for ; j >= 0 && s[j] == '\\'; j-- {
}
// An odd number here indicates that the '\' preceding the '.'
// is escaped.
if (i-j)&1 == 1 {
return i + 1
}
i = j + 1
}
}
return 0
}
// PrevLabel returns the index of the label when starting from the right and
// jumping n labels to the left.
2013-09-12 18:58:42 +10:00
// The bool start is true when the start of the string has been overshot.
2015-08-23 21:55:01 +10:00
// Also see NextLabel.
func PrevLabel(s string, n int) (i int, start bool) {
if s == "." {
return 0, true
}
2013-09-12 18:58:42 +10:00
if n == 0 {
return len(s), false
}
i = len(s) - 1
if s[i] == '.' {
i--
}
for ; n > 0; n-- {
i = prevLabel(s, i)
if i == 0 {
break
}
i -= 2
}
if n > 0 {
return 0, true
}
return i + 2, false
}
// 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
}
if a != b {
// case-insensitive comparison
for i := la - 1; i >= 0; i-- {
ai := a[i]
bi := b[i]
if ai != bi {
if bi < ai {
bi, ai = ai, bi
}
if !('A' <= ai && ai <= 'Z' && bi == ai+'a'-'A') {
return false
}
}
}
}
return true
}