...

Source file src/github.com/miekg/dns/labels.go

Documentation: github.com/miekg/dns

     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  }
   213  

View as plain text