/*- * SPDX-License-Identifier: ISC * * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995,1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef lint static const char rcsid[] = "$Id: ns_samedomain.c,v 1.6 2005/04/27 04:56:40 sra Exp $"; #endif #include "port_before.h" #include #include #include #include #include "port_after.h" /*% * Check whether a name belongs to a domain. * * Inputs: *\li a - the domain whose ancestry is being verified *\li b - the potential ancestor we're checking against * * Return: *\li boolean - is a at or below b? * * Notes: *\li Trailing dots are first removed from name and domain. * Always compare complete subdomains, not only whether the * domain name is the trailing string of the given name. * *\li "host.foobar.top" lies in "foobar.top" and in "top" and in "" * but NOT in "bar.top" */ int ns_samedomain(const char *a, const char *b) { size_t la, lb; int diff, i, escaped; const char *cp; la = strlen(a); lb = strlen(b); /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ if (la != 0U && a[la - 1] == '.') { escaped = 0; /* Note this loop doesn't get executed if la==1. */ for (i = la - 2; i >= 0; i--) if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) la--; } /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ if (lb != 0U && b[lb - 1] == '.') { escaped = 0; /* note this loop doesn't get executed if lb==1 */ for (i = lb - 2; i >= 0; i--) if (b[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (!escaped) lb--; } /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ if (lb == 0U) return (1); /* 'b' longer than 'a' means 'a' can't be in 'b'. */ if (lb > la) return (0); /* 'a' and 'b' being equal at this point indicates sameness. */ if (lb == la) return (strncasecmp(a, b, lb) == 0); /* Ok, we know la > lb. */ diff = la - lb; /* * If 'a' is only 1 character longer than 'b', then it can't be * a subdomain of 'b' (because of the need for the '.' label * separator). */ if (diff < 2) return (0); /* * If the character before the last 'lb' characters of 'b' * isn't '.', then it can't be a match (this lets us avoid * having "foobar.com" match "bar.com"). */ if (a[diff - 1] != '.') return (0); /* * We're not sure about that '.', however. It could be escaped * and thus not a really a label separator. */ escaped = 0; for (i = diff - 2; i >= 0; i--) if (a[i] == '\\') { if (escaped) escaped = 0; else escaped = 1; } else break; if (escaped) return (0); /* Now compare aligned trailing substring. */ cp = a + diff; return (strncasecmp(cp, b, lb) == 0); } #ifndef _LIBC /*% * is "a" a subdomain of "b"? */ int ns_subdomain(const char *a, const char *b) { return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); } #endif /*% * make a canonical copy of domain name "src" * * notes: * \code * foo -> foo. * foo. -> foo. * foo.. -> foo. * foo\. -> foo\.. * foo\\. -> foo\\. * \endcode */ int ns_makecanon(const char *src, char *dst, size_t dstsize) { size_t n = strlen(src); if (n + sizeof "." > dstsize) { /*%< Note: sizeof == 2 */ errno = EMSGSIZE; return (-1); } strcpy(dst, src); while (n >= 1U && dst[n - 1] == '.') /*%< Ends in "." */ if (n >= 2U && dst[n - 2] == '\\' && /*%< Ends in "\." */ (n < 3U || dst[n - 3] != '\\')) /*%< But not "\\." */ break; else dst[--n] = '\0'; dst[n++] = '.'; dst[n] = '\0'; return (0); } /*% * determine whether domain name "a" is the same as domain name "b" * * return: *\li -1 on error *\li 0 if names differ *\li 1 if names are the same */ int ns_samename(const char *a, const char *b) { char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; if (ns_makecanon(a, ta, sizeof ta) < 0 || ns_makecanon(b, tb, sizeof tb) < 0) return (-1); if (strcasecmp(ta, tb) == 0) return (1); else return (0); } /*! \file */