xref: /netbsd-src/lib/libc/nameser/ns_samedomain.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: ns_samedomain.c,v 1.2 2004/05/20 20:35:05 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (c) 1995,1999 by Internet Software Consortium.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/cdefs.h>
21 #ifndef lint
22 #ifdef notdef
23 static const char rcsid[] = "Id: ns_samedomain.c,v 1.1.2.2.4.2 2004/03/16 12:34:17 marka Exp";
24 #else
25 __RCSID("$NetBSD: ns_samedomain.c,v 1.2 2004/05/20 20:35:05 christos Exp $");
26 #endif
27 #endif
28 
29 #include "port_before.h"
30 
31 #include <sys/types.h>
32 #include <arpa/nameser.h>
33 #include <errno.h>
34 #include <string.h>
35 
36 #include "port_after.h"
37 
38 #ifndef _LIBC
39 /*
40  * int
41  * ns_samedomain(a, b)
42  *	Check whether a name belongs to a domain.
43  * Inputs:
44  *	a - the domain whose ancestory is being verified
45  *	b - the potential ancestor we're checking against
46  * Return:
47  *	boolean - is a at or below b?
48  * Notes:
49  *	Trailing dots are first removed from name and domain.
50  *	Always compare complete subdomains, not only whether the
51  *	domain name is the trailing string of the given name.
52  *
53  *	"host.foobar.top" lies in "foobar.top" and in "top" and in ""
54  *	but NOT in "bar.top"
55  */
56 
57 int
58 ns_samedomain(const char *a, const char *b) {
59 	size_t la, lb;
60 	int diff, i, escaped;
61 	const char *cp;
62 
63 	la = strlen(a);
64 	lb = strlen(b);
65 
66 	/* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
67 	if (la != 0U && a[la - 1] == '.') {
68 		escaped = 0;
69 		/* Note this loop doesn't get executed if la==1. */
70 		for (i = la - 2; i >= 0; i--)
71 			if (a[i] == '\\') {
72 				if (escaped)
73 					escaped = 0;
74 				else
75 					escaped = 1;
76 			} else
77 				break;
78 		if (!escaped)
79 			la--;
80 	}
81 
82 	/* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
83 	if (lb != 0U && b[lb - 1] == '.') {
84 		escaped = 0;
85 		/* note this loop doesn't get executed if lb==1 */
86 		for (i = lb - 2; i >= 0; i--)
87 			if (b[i] == '\\') {
88 				if (escaped)
89 					escaped = 0;
90 				else
91 					escaped = 1;
92 			} else
93 				break;
94 		if (!escaped)
95 			lb--;
96 	}
97 
98 	/* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
99 	if (lb == 0U)
100 		return (1);
101 
102 	/* 'b' longer than 'a' means 'a' can't be in 'b'. */
103 	if (lb > la)
104 		return (0);
105 
106 	/* 'a' and 'b' being equal at this point indicates sameness. */
107 	if (lb == la)
108 		return (strncasecmp(a, b, lb) == 0);
109 
110 	/* Ok, we know la > lb. */
111 
112 	diff = la - lb;
113 
114 	/*
115 	 * If 'a' is only 1 character longer than 'b', then it can't be
116 	 * a subdomain of 'b' (because of the need for the '.' label
117 	 * separator).
118 	 */
119 	if (diff < 2)
120 		return (0);
121 
122 	/*
123 	 * If the character before the last 'lb' characters of 'b'
124 	 * isn't '.', then it can't be a match (this lets us avoid
125 	 * having "foobar.com" match "bar.com").
126 	 */
127 	if (a[diff - 1] != '.')
128 		return (0);
129 
130 	/*
131 	 * We're not sure about that '.', however.  It could be escaped
132          * and thus not a really a label separator.
133 	 */
134 	escaped = 0;
135 	for (i = diff - 2; i >= 0; i--)
136 		if (a[i] == '\\') {
137 			if (escaped)
138 				escaped = 0;
139 			else
140 				escaped = 1;
141 		} else
142 			break;
143 	if (escaped)
144 		return (0);
145 
146 	/* Now compare aligned trailing substring. */
147 	cp = a + diff;
148 	return (strncasecmp(cp, b, lb) == 0);
149 }
150 
151 /*
152  * int
153  * ns_subdomain(a, b)
154  *	is "a" a subdomain of "b"?
155  */
156 int
157 ns_subdomain(const char *a, const char *b) {
158 	return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
159 }
160 #endif
161 
162 /*
163  * int
164  * ns_makecanon(src, dst, dstsize)
165  *	make a canonical copy of domain name "src"
166  * notes:
167  *	foo -> foo.
168  *	foo. -> foo.
169  *	foo.. -> foo.
170  *	foo\. -> foo\..
171  *	foo\\. -> foo\\.
172  */
173 
174 int
175 ns_makecanon(const char *src, char *dst, size_t dstsize) {
176 	size_t n = strlen(src);
177 
178 	if (n + sizeof "." > dstsize) {			/* Note: sizeof == 2 */
179 		errno = EMSGSIZE;
180 		return (-1);
181 	}
182 	strcpy(dst, src);
183 	while (n >= 1U && dst[n - 1] == '.')		/* Ends in "." */
184 		if (n >= 2U && dst[n - 2] == '\\' &&	/* Ends in "\." */
185 		    (n < 3U || dst[n - 3] != '\\'))	/* But not "\\." */
186 			break;
187 		else
188 			dst[--n] = '\0';
189 	dst[n++] = '.';
190 	dst[n] = '\0';
191 	return (0);
192 }
193 
194 /*
195  * int
196  * ns_samename(a, b)
197  *	determine whether domain name "a" is the same as domain name "b"
198  * return:
199  *	-1 on error
200  *	0 if names differ
201  *	1 if names are the same
202  */
203 
204 int
205 ns_samename(const char *a, const char *b) {
206 	char ta[NS_MAXDNAME], tb[NS_MAXDNAME];
207 
208 	if (ns_makecanon(a, ta, sizeof ta) < 0 ||
209 	    ns_makecanon(b, tb, sizeof tb) < 0)
210 		return (-1);
211 	if (strcasecmp(ta, tb) == 0)
212 		return (1);
213 	else
214 		return (0);
215 }
216