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