1*6e778a7eSPedro F. Giffuni /*-
2*6e778a7eSPedro F. Giffuni * SPDX-License-Identifier: ISC
3*6e778a7eSPedro F. Giffuni *
465e96449SHajimu UMEMOTO * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
565e96449SHajimu UMEMOTO * Copyright (c) 1995,1999 by Internet Software Consortium.
665e96449SHajimu UMEMOTO *
765e96449SHajimu UMEMOTO * Permission to use, copy, modify, and distribute this software for any
865e96449SHajimu UMEMOTO * purpose with or without fee is hereby granted, provided that the above
965e96449SHajimu UMEMOTO * copyright notice and this permission notice appear in all copies.
1065e96449SHajimu UMEMOTO *
1165e96449SHajimu UMEMOTO * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
1265e96449SHajimu UMEMOTO * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1365e96449SHajimu UMEMOTO * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
1465e96449SHajimu UMEMOTO * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1565e96449SHajimu UMEMOTO * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1665e96449SHajimu UMEMOTO * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1765e96449SHajimu UMEMOTO * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1865e96449SHajimu UMEMOTO */
1965e96449SHajimu UMEMOTO
2065e96449SHajimu UMEMOTO #include "port_before.h"
2165e96449SHajimu UMEMOTO
2265e96449SHajimu UMEMOTO #include <sys/types.h>
2365e96449SHajimu UMEMOTO #include <arpa/nameser.h>
2465e96449SHajimu UMEMOTO #include <errno.h>
2565e96449SHajimu UMEMOTO #include <string.h>
2665e96449SHajimu UMEMOTO
2765e96449SHajimu UMEMOTO #include "port_after.h"
2865e96449SHajimu UMEMOTO
29dde4a85dSHajimu UMEMOTO /*%
3065e96449SHajimu UMEMOTO * Check whether a name belongs to a domain.
31dde4a85dSHajimu UMEMOTO *
3265e96449SHajimu UMEMOTO * Inputs:
3332223c1bSPedro F. Giffuni *\li a - the domain whose ancestry is being verified
34dde4a85dSHajimu UMEMOTO *\li b - the potential ancestor we're checking against
35dde4a85dSHajimu UMEMOTO *
3665e96449SHajimu UMEMOTO * Return:
37dde4a85dSHajimu UMEMOTO *\li boolean - is a at or below b?
38dde4a85dSHajimu UMEMOTO *
3965e96449SHajimu UMEMOTO * Notes:
40dde4a85dSHajimu UMEMOTO *\li Trailing dots are first removed from name and domain.
4165e96449SHajimu UMEMOTO * Always compare complete subdomains, not only whether the
4265e96449SHajimu UMEMOTO * domain name is the trailing string of the given name.
4365e96449SHajimu UMEMOTO *
44dde4a85dSHajimu UMEMOTO *\li "host.foobar.top" lies in "foobar.top" and in "top" and in ""
4565e96449SHajimu UMEMOTO * but NOT in "bar.top"
4665e96449SHajimu UMEMOTO */
4765e96449SHajimu UMEMOTO
4865e96449SHajimu UMEMOTO int
ns_samedomain(const char * a,const char * b)4965e96449SHajimu UMEMOTO ns_samedomain(const char *a, const char *b) {
5065e96449SHajimu UMEMOTO size_t la, lb;
5165e96449SHajimu UMEMOTO int diff, i, escaped;
5265e96449SHajimu UMEMOTO const char *cp;
5365e96449SHajimu UMEMOTO
5465e96449SHajimu UMEMOTO la = strlen(a);
5565e96449SHajimu UMEMOTO lb = strlen(b);
5665e96449SHajimu UMEMOTO
5765e96449SHajimu UMEMOTO /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
5865e96449SHajimu UMEMOTO if (la != 0U && a[la - 1] == '.') {
5965e96449SHajimu UMEMOTO escaped = 0;
6065e96449SHajimu UMEMOTO /* Note this loop doesn't get executed if la==1. */
6165e96449SHajimu UMEMOTO for (i = la - 2; i >= 0; i--)
6265e96449SHajimu UMEMOTO if (a[i] == '\\') {
6365e96449SHajimu UMEMOTO if (escaped)
6465e96449SHajimu UMEMOTO escaped = 0;
6565e96449SHajimu UMEMOTO else
6665e96449SHajimu UMEMOTO escaped = 1;
6765e96449SHajimu UMEMOTO } else
6865e96449SHajimu UMEMOTO break;
6965e96449SHajimu UMEMOTO if (!escaped)
7065e96449SHajimu UMEMOTO la--;
7165e96449SHajimu UMEMOTO }
7265e96449SHajimu UMEMOTO
7365e96449SHajimu UMEMOTO /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
7465e96449SHajimu UMEMOTO if (lb != 0U && b[lb - 1] == '.') {
7565e96449SHajimu UMEMOTO escaped = 0;
7665e96449SHajimu UMEMOTO /* note this loop doesn't get executed if lb==1 */
7765e96449SHajimu UMEMOTO for (i = lb - 2; i >= 0; i--)
7865e96449SHajimu UMEMOTO if (b[i] == '\\') {
7965e96449SHajimu UMEMOTO if (escaped)
8065e96449SHajimu UMEMOTO escaped = 0;
8165e96449SHajimu UMEMOTO else
8265e96449SHajimu UMEMOTO escaped = 1;
8365e96449SHajimu UMEMOTO } else
8465e96449SHajimu UMEMOTO break;
8565e96449SHajimu UMEMOTO if (!escaped)
8665e96449SHajimu UMEMOTO lb--;
8765e96449SHajimu UMEMOTO }
8865e96449SHajimu UMEMOTO
8965e96449SHajimu UMEMOTO /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
9065e96449SHajimu UMEMOTO if (lb == 0U)
9165e96449SHajimu UMEMOTO return (1);
9265e96449SHajimu UMEMOTO
9365e96449SHajimu UMEMOTO /* 'b' longer than 'a' means 'a' can't be in 'b'. */
9465e96449SHajimu UMEMOTO if (lb > la)
9565e96449SHajimu UMEMOTO return (0);
9665e96449SHajimu UMEMOTO
9765e96449SHajimu UMEMOTO /* 'a' and 'b' being equal at this point indicates sameness. */
9865e96449SHajimu UMEMOTO if (lb == la)
9965e96449SHajimu UMEMOTO return (strncasecmp(a, b, lb) == 0);
10065e96449SHajimu UMEMOTO
10165e96449SHajimu UMEMOTO /* Ok, we know la > lb. */
10265e96449SHajimu UMEMOTO
10365e96449SHajimu UMEMOTO diff = la - lb;
10465e96449SHajimu UMEMOTO
10565e96449SHajimu UMEMOTO /*
10665e96449SHajimu UMEMOTO * If 'a' is only 1 character longer than 'b', then it can't be
10765e96449SHajimu UMEMOTO * a subdomain of 'b' (because of the need for the '.' label
10865e96449SHajimu UMEMOTO * separator).
10965e96449SHajimu UMEMOTO */
11065e96449SHajimu UMEMOTO if (diff < 2)
11165e96449SHajimu UMEMOTO return (0);
11265e96449SHajimu UMEMOTO
11365e96449SHajimu UMEMOTO /*
11465e96449SHajimu UMEMOTO * If the character before the last 'lb' characters of 'b'
11565e96449SHajimu UMEMOTO * isn't '.', then it can't be a match (this lets us avoid
11665e96449SHajimu UMEMOTO * having "foobar.com" match "bar.com").
11765e96449SHajimu UMEMOTO */
11865e96449SHajimu UMEMOTO if (a[diff - 1] != '.')
11965e96449SHajimu UMEMOTO return (0);
12065e96449SHajimu UMEMOTO
12165e96449SHajimu UMEMOTO /*
12265e96449SHajimu UMEMOTO * We're not sure about that '.', however. It could be escaped
12365e96449SHajimu UMEMOTO * and thus not a really a label separator.
12465e96449SHajimu UMEMOTO */
12565e96449SHajimu UMEMOTO escaped = 0;
12665e96449SHajimu UMEMOTO for (i = diff - 2; i >= 0; i--)
12765e96449SHajimu UMEMOTO if (a[i] == '\\') {
12865e96449SHajimu UMEMOTO if (escaped)
12965e96449SHajimu UMEMOTO escaped = 0;
13065e96449SHajimu UMEMOTO else
13165e96449SHajimu UMEMOTO escaped = 1;
13265e96449SHajimu UMEMOTO } else
13365e96449SHajimu UMEMOTO break;
13465e96449SHajimu UMEMOTO if (escaped)
13565e96449SHajimu UMEMOTO return (0);
13665e96449SHajimu UMEMOTO
13765e96449SHajimu UMEMOTO /* Now compare aligned trailing substring. */
13865e96449SHajimu UMEMOTO cp = a + diff;
13965e96449SHajimu UMEMOTO return (strncasecmp(cp, b, lb) == 0);
14065e96449SHajimu UMEMOTO }
14165e96449SHajimu UMEMOTO
142d808369aSHajimu UMEMOTO #ifndef _LIBC
143dde4a85dSHajimu UMEMOTO /*%
14465e96449SHajimu UMEMOTO * is "a" a subdomain of "b"?
14565e96449SHajimu UMEMOTO */
14665e96449SHajimu UMEMOTO int
ns_subdomain(const char * a,const char * b)14765e96449SHajimu UMEMOTO ns_subdomain(const char *a, const char *b) {
14865e96449SHajimu UMEMOTO return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
14965e96449SHajimu UMEMOTO }
150ab96eeabSHajimu UMEMOTO #endif
15165e96449SHajimu UMEMOTO
152dde4a85dSHajimu UMEMOTO /*%
15365e96449SHajimu UMEMOTO * make a canonical copy of domain name "src"
154dde4a85dSHajimu UMEMOTO *
15565e96449SHajimu UMEMOTO * notes:
156dde4a85dSHajimu UMEMOTO * \code
15765e96449SHajimu UMEMOTO * foo -> foo.
15865e96449SHajimu UMEMOTO * foo. -> foo.
15965e96449SHajimu UMEMOTO * foo.. -> foo.
16065e96449SHajimu UMEMOTO * foo\. -> foo\..
16165e96449SHajimu UMEMOTO * foo\\. -> foo\\.
162dde4a85dSHajimu UMEMOTO * \endcode
16365e96449SHajimu UMEMOTO */
16465e96449SHajimu UMEMOTO
16565e96449SHajimu UMEMOTO int
ns_makecanon(const char * src,char * dst,size_t dstsize)16665e96449SHajimu UMEMOTO ns_makecanon(const char *src, char *dst, size_t dstsize) {
16765e96449SHajimu UMEMOTO size_t n = strlen(src);
16865e96449SHajimu UMEMOTO
169dde4a85dSHajimu UMEMOTO if (n + sizeof "." > dstsize) { /*%< Note: sizeof == 2 */
17065e96449SHajimu UMEMOTO errno = EMSGSIZE;
17165e96449SHajimu UMEMOTO return (-1);
17265e96449SHajimu UMEMOTO }
17365e96449SHajimu UMEMOTO strcpy(dst, src);
174dde4a85dSHajimu UMEMOTO while (n >= 1U && dst[n - 1] == '.') /*%< Ends in "." */
175dde4a85dSHajimu UMEMOTO if (n >= 2U && dst[n - 2] == '\\' && /*%< Ends in "\." */
176dde4a85dSHajimu UMEMOTO (n < 3U || dst[n - 3] != '\\')) /*%< But not "\\." */
17765e96449SHajimu UMEMOTO break;
17865e96449SHajimu UMEMOTO else
17965e96449SHajimu UMEMOTO dst[--n] = '\0';
18065e96449SHajimu UMEMOTO dst[n++] = '.';
18165e96449SHajimu UMEMOTO dst[n] = '\0';
18265e96449SHajimu UMEMOTO return (0);
18365e96449SHajimu UMEMOTO }
18465e96449SHajimu UMEMOTO
185dde4a85dSHajimu UMEMOTO /*%
18665e96449SHajimu UMEMOTO * determine whether domain name "a" is the same as domain name "b"
187dde4a85dSHajimu UMEMOTO *
18865e96449SHajimu UMEMOTO * return:
189dde4a85dSHajimu UMEMOTO *\li -1 on error
190dde4a85dSHajimu UMEMOTO *\li 0 if names differ
191dde4a85dSHajimu UMEMOTO *\li 1 if names are the same
19265e96449SHajimu UMEMOTO */
19365e96449SHajimu UMEMOTO
19465e96449SHajimu UMEMOTO int
ns_samename(const char * a,const char * b)19565e96449SHajimu UMEMOTO ns_samename(const char *a, const char *b) {
19665e96449SHajimu UMEMOTO char ta[NS_MAXDNAME], tb[NS_MAXDNAME];
19765e96449SHajimu UMEMOTO
19865e96449SHajimu UMEMOTO if (ns_makecanon(a, ta, sizeof ta) < 0 ||
19965e96449SHajimu UMEMOTO ns_makecanon(b, tb, sizeof tb) < 0)
20065e96449SHajimu UMEMOTO return (-1);
20165e96449SHajimu UMEMOTO if (strcasecmp(ta, tb) == 0)
20265e96449SHajimu UMEMOTO return (1);
20365e96449SHajimu UMEMOTO else
20465e96449SHajimu UMEMOTO return (0);
20565e96449SHajimu UMEMOTO }
206dde4a85dSHajimu UMEMOTO
207dde4a85dSHajimu UMEMOTO /*! \file */
208