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