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