Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / isc-dhcp / minires / ns_samedomain.c
1 /*
2  * Copyright (c) 1995,1999-2003 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 lint
19 static const char rcsid[] = "$Id: ns_samedomain.c,v 1.3.2.3 2003/01/14 23:15:06 dhankins Exp $";
20 #endif
21
22 #include <sys/types.h>
23 #include <errno.h>
24 #include <string.h>
25
26 #include <netinet/in.h>
27 #include <sys/socket.h>
28
29 #include "minires/minires.h"
30 #include "arpa/nameser.h"
31
32 /*
33  * int
34  * ns_samedomain(a, b)
35  *      Check whether a name belongs to a domain.
36  * Inputs:
37  *      a - the domain whose ancestory is being verified
38  *      b - the potential ancestor we're checking against
39  * Return:
40  *      boolean - is a at or below b?
41  * Notes:
42  *      Trailing dots are first removed from name and domain.
43  *      Always compare complete subdomains, not only whether the
44  *      domain name is the trailing string of the given name.
45  *
46  *      "host.foobar.top" lies in "foobar.top" and in "top" and in ""
47  *      but NOT in "bar.top"
48  */
49
50 int
51 ns_samedomain(const char *a, const char *b) {
52         size_t la, lb;
53         int diff, i, escaped;
54         const char *cp;
55
56         la = strlen(a);
57         lb = strlen(b);
58
59         /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
60         if (la != 0 && a[la - 1] == '.') {
61                 escaped = 0;
62                 /* Note this loop doesn't get executed if la==1. */
63                 for (i = la - 2; i >= 0; i--)
64                         if (a[i] == '\\') {
65                                 if (escaped)
66                                         escaped = 0;
67                                 else
68                                         escaped = 1;
69                         } else
70                                 break;
71                 if (!escaped)
72                         la--;
73         }
74
75         /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
76         if (lb != 0 && b[lb - 1] == '.') {
77                 escaped = 0;
78                 /* note this loop doesn't get executed if lb==1 */
79                 for (i = lb - 2; i >= 0; i--)
80                         if (b[i] == '\\') {
81                                 if (escaped)
82                                         escaped = 0;
83                                 else
84                                         escaped = 1;
85                         } else
86                                 break;
87                 if (!escaped)
88                         lb--;
89         }
90
91         /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
92         if (lb == 0)
93                 return (1);
94
95         /* 'b' longer than 'a' means 'a' can't be in 'b'. */
96         if (lb > la)
97                 return (0);
98
99         /* 'a' and 'b' being equal at this point indicates sameness. */
100         if (lb == la)
101                 return (strncasecmp(a, b, lb) == 0);
102
103         /* Ok, we know la > lb. */
104
105         diff = la - lb;
106
107         /*
108          * If 'a' is only 1 character longer than 'b', then it can't be
109          * a subdomain of 'b' (because of the need for the '.' label
110          * separator).
111          */
112         if (diff < 2)
113                 return (0);
114
115         /*
116          * If the character before the last 'lb' characters of 'b'
117          * isn't '.', then it can't be a match (this lets us avoid
118          * having "foobar.com" match "bar.com").
119          */
120         if (a[diff - 1] != '.')
121                 return (0);
122
123         /*
124          * We're not sure about that '.', however.  It could be escaped
125          * and thus not a really a label separator.
126          */
127         escaped = 0;
128         for (i = diff - 2; i >= 0; i--)
129                 if (a[i] == '\\') {
130                         if (escaped)
131                                 escaped = 0;
132                         else
133                                 escaped = 1;
134                 } else
135                         break;
136         if (escaped)
137                 return (0);
138           
139         /* Now compare aligned trailing substring. */
140         cp = a + diff;
141         return (strncasecmp(cp, b, lb) == 0);
142 }
143
144 /*
145  * int
146  * ns_subdomain(a, b)
147  *      is "a" a subdomain of "b"?
148  */
149 int
150 ns_subdomain(const char *a, const char *b) {
151         return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
152 }
153
154 /*
155  * int
156  * ns_makecanon(src, dst, dstsize)
157  *      make a canonical copy of domain name "src"
158  * notes:
159  *      foo -> foo.
160  *      foo. -> foo.
161  *      foo.. -> foo.
162  *      foo\. -> foo\..
163  *      foo\\. -> foo\\.
164  */
165
166 isc_result_t
167 ns_makecanon(const char *src, char *dst, size_t dstsize) {
168         size_t n = strlen(src);
169
170         if (n + sizeof "." > dstsize) {
171                 return ISC_R_NOSPACE;
172         }
173         strcpy(dst, src);
174         while (n > 0 && dst[n - 1] == '.')              /* Ends in "." */
175                 if (n > 1 && dst[n - 2] == '\\' &&      /* Ends in "\." */
176                     (n < 2 || dst[n - 3] != '\\'))      /* But not "\\." */
177                         break;
178                 else
179                         dst[--n] = '\0';
180         dst[n++] = '.';
181         dst[n] = '\0';
182         return ISC_R_SUCCESS;
183 }
184
185 /*
186  * int
187  * ns_samename(a, b)
188  *      determine whether domain name "a" is the same as domain name "b"
189  * return:
190  *      -1 on error
191  *      0 if names differ
192  *      1 if names are the same
193  */
194
195 int
196 ns_samename(const char *a, const char *b) {
197         char ta[NS_MAXDNAME], tb[NS_MAXDNAME];
198         isc_result_t status;
199
200         status = ns_makecanon(a, ta, sizeof ta);
201         if (status != ISC_R_SUCCESS)
202                 return status;
203         status = ns_makecanon(b, tb, sizeof tb);
204         if (status != ISC_R_SUCCESS)
205                 return (-1);
206         if (strcasecmp(ta, tb) == 0)
207                 return (1);
208         else
209                 return (0);
210 }