1 /*	$NetBSD: domaincmp.c,v 1.1.1.1 2015/07/08 15:37:48 christos Exp $	*/
2 
3 /*****************************************************************
4 **
5 **	@(#) domaincmp.c -- compare two domain names
6 **
7 **	Copyright (c) Aug 2005, Karle Boss, Holger Zuleger (kaho).
8 **	isparentdomain() (c) Mar 2010 by Holger Zuleger
9 **	All rights reserved.
10 **
11 **	This software is open source.
12 **
13 **	Redistribution and use in source and binary forms, with or without
14 **	modification, are permitted provided that the following conditions
15 **	are met:
16 **
17 **	Redistributions of source code must retain the above copyright notice,
18 **	this list of conditions and the following disclaimer.
19 **
20 **	Redistributions in binary form must reproduce the above copyright notice,
21 **	this list of conditions and the following disclaimer in the documentation
22 **	and/or other materials provided with the distribution.
23 **
24 **	Neither the name of Karle Boss or Holger Zuleger (kaho) nor the
25 **	names of its contributors may be used to endorse or promote products
26 **	derived from this software without specific prior written permission.
27 **
28 **	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 **	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 **	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 **	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
32 **	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 **	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 **	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 **	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 **	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 **	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 **	POSSIBILITY OF SUCH DAMAGE.
39 **
40 *****************************************************************/
41 # include <stdio.h>
42 # include <string.h>
43 # include <assert.h>
44 # include <ctype.h>
45 #define extern
46 # include "domaincmp.h"
47 #undef extern
48 
49 
50 #define	goto_labelstart(str, p)	while ( (p) > (str) && *((p)-1) != '.' ) \
51 					(p)--
52 
53 /*****************************************************************
54 **      int domaincmp (a, b)
55 **      compare a and b as fqdns.
56 **      return <0 | 0 | >0 as in strcmp
57 **      A subdomain is less than the corresponding parent domain,
58 **      thus domaincmp ("z.example.net", "example.net") return < 0 !!
59 *****************************************************************/
60 int     domaincmp (const char *a, const char *b)
61 {
62 	return domaincmp_dir (a, b, 1);
63 }
64 
65 /*****************************************************************
66 **      int domaincmp_dir (a, b, subdomain_above)
67 **      compare a and b as fqdns.
68 **      return <0 | 0 | >0 as in strcmp
69 **      A subdomain is less than the corresponding parent domain,
70 **      thus domaincmp ("z.example.net", "example.net") return < 0 !!
71 *****************************************************************/
72 int     domaincmp_dir (const char *a, const char *b, int subdomain_above)
73 {
74 	register const  char    *pa;
75 	register const  char    *pb;
76 	int	dir;
77 
78 	if ( a == NULL ) return -1;
79 	if ( b == NULL ) return 1;
80 
81 	if ( subdomain_above )
82 		dir = 1;
83 	else
84 		dir = -1;
85 
86 	if ( *a == '.' )	/* skip a leading dot */
87 		a++;
88 	if ( *b == '.' )	/* same at the other string */
89 		b++;
90 
91 	/* let pa and pb point to the last non dot char */
92 	pa = a + strlen (a);
93 	do
94 		pa--;
95 	while ( pa > a && *pa == '.' );
96 
97 	pb = b + strlen (b);
98 	do
99 		pb--;
100 	while ( pb > b && *pb == '.' );
101 
102 	/* cmp  both domains starting at the end */
103 	while ( *pa == *pb && pa > a && pb > b )
104 		pa--, pb--;
105 
106 	if ( *pa != *pb )	/* both domains are different ? */
107 	{
108 		if ( *pa == '.' )
109 			pa++;			/* set to beginning of next label */
110 		else
111 			goto_labelstart (a, pa);	/* find begin of current label */
112 		if ( *pb == '.' )
113 			pb++;			/* set to beginning of next label */
114 		else
115 			goto_labelstart (b, pb);	/* find begin of current label */
116 	}
117 	else		/* maybe one of them has a subdomain */
118 	{
119 		if ( pa > a )
120 			if ( pa[-1] == '.' )
121 				return -1 * dir;
122 			else
123 				goto_labelstart (a, pa);
124 		else if ( pb > b )
125 			if ( pb[-1] == '.' )
126 				return 1 * dir;
127 			else
128 				goto_labelstart (b, pb);
129 		else
130 			return 0;	/* both are at the beginning, so they are equal */
131 	}
132 
133 	/* both domains are definitly unequal */
134 	while ( *pa == *pb )	/* so we have to look at the point where they differ */
135 		pa++, pb++;
136 
137 	return *pa - *pb;
138 }
139 
140 /*****************************************************************
141 **
142 **	int	issubdomain ("child", "parent")
143 **
144 **	"child" and "parent" are standardized domain names in such
145 **	a way that even both domain names are ending with a dot,
146 **	or none of them.
147 **
148 **	returns 1 if "child" is a subdomain of "parent"
149 **	returns 0 if "child" is not a subdomain of "parent"
150 **
151 *****************************************************************/
152 int	issubdomain (const char *child, const char *parent)
153 {
154 	const	char	*p;
155 	const	char	*cdot;
156 	int	ccnt;
157 	int	pcnt;
158 
159 	if ( !child || !parent || *child == '\0' || *parent == '\0' )
160 		return 0;
161 
162 	cdot = NULL;
163 	pcnt = 0;
164 	for ( p = parent; *p; p++ )
165 		if ( *p == '.' )
166 			pcnt++;
167 
168 	ccnt = 0;
169 	for ( p = child; *p; p++ )
170 		if ( *p == '.' )
171 		{
172 			if ( ccnt == 0 )
173 				cdot = p;
174 			ccnt++;
175 		}
176 	if ( ccnt == 0 )	/* child is not a fqdn or is not deep enough ? */
177 		return 0;
178 	if ( pcnt == 0 )	/* parent is not a fqdn ? */
179 		return 0;
180 
181 	if ( pcnt >= ccnt )	/* parent has more levels than child ? */
182 		return 0;
183 
184 	/* is child a (one level) subdomain of parent ? */
185 	if ( strcmp (cdot+1, parent) == 0 )	/* the domains are equal ? */
186 		return 1;
187 
188 	return 0;
189 }
190 
191 /*****************************************************************
192 **
193 **	int	isparentdomain ("child", "parent", level)
194 **
195 **	"child" and "parent" are standardized domain names in such
196 **	a way that even both domain names are ending with a dot,
197 **	or none of them.
198 **
199 **	returns 1 if "child" is a subdomain of "parent"
200 **	returns 0 if "child" is not a subdomain of "parent"
201 **	returns -1 if "child" and "parent" are the same domain
202 **
203 *****************************************************************/
204 int	isparentdomain (const char *child, const char *parent, int level)
205 {
206 	const	char	*p;
207 	const	char	*cdot;
208 	const	char	*pdot;
209 	int	ccnt;
210 	int	pcnt;
211 
212 	if ( !child || !parent || *child == '\0' || *parent == '\0' )
213 		return 0;
214 
215 	pdot = cdot = NULL;
216 	pcnt = 0;
217 	for ( p = parent; *p; p++ )
218 		if ( *p == '.' )
219 		{
220 			if ( pcnt == 0 )
221 				pdot = p;
222 			pcnt++;
223 		}
224 
225 	ccnt = 0;
226 	for ( p = child; *p; p++ )
227 		if ( *p == '.' )
228 		{
229 			if ( ccnt == 0 )
230 				cdot = p;
231 			ccnt++;
232 		}
233 	if ( ccnt == 0 || ccnt < level )	/* child is not a fqdn or is not deep enough ? */
234 		return 0;
235 	if ( pcnt == 0 )	/* parent is not a fqdn ? */
236 		return 0;
237 
238 	if ( pcnt > ccnt )	/* parent has more levels than child ? */
239 		return 0;
240 
241 	if ( pcnt == ccnt )	/* both are at the same level ? */
242 	{
243 		/* let's check the domain part */
244 		if ( strcmp (cdot, pdot) == 0 )	/* the domains are equal ? */
245 			return -1;
246 		return 0;
247 	}
248 
249 	if ( pcnt > ccnt )	/* parent has more levels than child ? */
250 		return 0;
251 
252 	/* is child a (one level) subdomain of parent ? */
253 	if ( strcmp (cdot+1, parent) == 0 )	/* the domains are equal ? */
254 		return 1;
255 
256 	return 0;
257 }
258 
259 #ifdef DOMAINCMP_TEST
260 static  struct {
261          char    *a;
262          char    *b;
263          int     res;
264 } ex[] = {
265          { ".",          ".",    0 },
266          { "test",       "",   1 },
267          { "",			 "test2", -1 },
268          { "",			 "",     0 },
269          { "de",         "de",   0 },
270          { ".de",         "de",   0 },
271          { "de.",        "de.",  0 },
272          { ".de",        ".de",  0 },
273          { ".de.",       ".de.", 0 },
274          { ".de",        "zde",  -1 },
275          { ".de",        "ade",  1 },
276          { "zde",        ".de",  1 },
277          { "ade",        ".de",  -1 },
278          { "a.de",       ".de",  -1 },
279          { ".de",        "a.de",  1 },
280          { "a.de",       "b.de", -1 },
281          { "a.de.",       "b.de", -1 },
282          { "a.de",       "b.de.", -1 },
283          { "a.de",       "a.de.", 0 },
284          { "aa.de",      "b.de", -1 },
285          { "ba.de",      "b.de", 1 },
286          { "a.de",       "a.dk", -1 },
287          { "anna.example.de",    "anna.example.de",      0 },
288          { "anna.example.de",    "annamirl.example.de",  -1 },
289          { "anna.example.de",    "ann.example.de",       1 },
290          { "example.de.",        "xy.example.de.",       1 },
291          { "example.de.",        "ab.example.de.",       1 },
292          { "example.de",        "ab.example.de",       1 },
293          { "xy.example.de.",        "example.de.",       -1 },
294          { "ab.example.de.",        "example.de.",       -1 },
295          { "ab.example.de",        "example.de",       -1 },
296          { "ab.mast.de",          "axt.de",             1 },
297          { "ab.mast.de",          "obt.de",             -1 },
298          { "abc.example.de.",    "xy.example.de.",       -1 },
299          { NULL, NULL,   0 }
300 };
301 
302 const char	*progname;
303 main (int argc, char *argv[])
304 {
305 
306 	int	expect;
307 	int	res;
308 	int	c;
309 	int	i;
310 
311 	progname = *argv;
312 
313 	for ( i = 0; ex[i].a; i++ )
314 	{
315 		expect = ex[i].res;
316 		if ( expect < 0 )
317 			c = '<';
318 		else if ( expect > 0 )
319 			c = '>';
320 		else
321 			c = '=';
322 		printf ("%-20s %-20s ", ex[i].a, ex[i].b);
323 		printf ("%3d  ", issubdomain (ex[i].a, ex[i].b));
324 		printf ("\t==> 0 %c ", c);
325 		fflush (stdout);
326 		res = domaincmp (ex[i].a, ex[i].b);
327 		printf ("%3d  ", res);
328 		if ( res < 0 && expect < 0 || res > 0 && expect > 0 || res == 0 && expect == 0 )
329 			puts ("ok");
330 		else
331 			puts ("not ok");
332 	}
333 }
334 #endif
335