ares_gethostbyname.c (10887B)
1 /* MIT License 2 * 3 * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology 4 * Copyright (c) The c-ares project and its contributors 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 * SPDX-License-Identifier: MIT 26 */ 27 28 #include "ares_private.h" 29 30 #ifdef HAVE_NETINET_IN_H 31 # include <netinet/in.h> 32 #endif 33 #ifdef HAVE_NETDB_H 34 # include <netdb.h> 35 #endif 36 #ifdef HAVE_ARPA_INET_H 37 # include <arpa/inet.h> 38 #endif 39 40 #include "ares_nameser.h" 41 42 #ifdef HAVE_STRINGS_H 43 # include <strings.h> 44 #endif 45 46 #include "ares_inet_net_pton.h" 47 48 static void sort_addresses(const struct hostent *host, 49 const struct apattern *sortlist, size_t nsort); 50 static void sort6_addresses(const struct hostent *host, 51 const struct apattern *sortlist, size_t nsort); 52 static size_t get_address_index(const struct in_addr *addr, 53 const struct apattern *sortlist, size_t nsort); 54 static size_t get6_address_index(const struct ares_in6_addr *addr, 55 const struct apattern *sortlist, size_t nsort); 56 57 struct host_query { 58 ares_host_callback callback; 59 void *arg; 60 ares_channel_t *channel; 61 }; 62 63 static void ares_gethostbyname_callback(void *arg, int status, int timeouts, 64 struct ares_addrinfo *result) 65 { 66 struct hostent *hostent = NULL; 67 struct host_query *ghbn_arg = arg; 68 69 if (status == ARES_SUCCESS) { 70 status = (int)ares_addrinfo2hostent(result, AF_UNSPEC, &hostent); 71 } 72 73 /* addrinfo2hostent will only return ENODATA if there are no addresses _and_ 74 * no cname/aliases. However, gethostbyname will return ENODATA even if there 75 * is cname/alias data */ 76 if (status == ARES_SUCCESS && hostent && 77 (!hostent->h_addr_list || !hostent->h_addr_list[0])) { 78 status = ARES_ENODATA; 79 } 80 81 if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent) { 82 if (hostent->h_addrtype == AF_INET6) { 83 sort6_addresses(hostent, ghbn_arg->channel->sortlist, 84 ghbn_arg->channel->nsort); 85 } 86 if (hostent->h_addrtype == AF_INET) { 87 sort_addresses(hostent, ghbn_arg->channel->sortlist, 88 ghbn_arg->channel->nsort); 89 } 90 } 91 92 ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent); 93 94 ares_freeaddrinfo(result); 95 ares_free(ghbn_arg); 96 ares_free_hostent(hostent); 97 } 98 99 void ares_gethostbyname(ares_channel_t *channel, const char *name, int family, 100 ares_host_callback callback, void *arg) 101 { 102 struct ares_addrinfo_hints hints; 103 struct host_query *ghbn_arg; 104 105 if (!callback) { 106 return; 107 } 108 109 memset(&hints, 0, sizeof(hints)); 110 hints.ai_flags = ARES_AI_CANONNAME; 111 hints.ai_family = family; 112 113 ghbn_arg = ares_malloc(sizeof(*ghbn_arg)); 114 if (!ghbn_arg) { 115 callback(arg, ARES_ENOMEM, 0, NULL); 116 return; 117 } 118 119 ghbn_arg->callback = callback; 120 ghbn_arg->arg = arg; 121 ghbn_arg->channel = channel; 122 123 /* NOTE: ares_getaddrinfo() locks the channel, we don't use the channel 124 * outside so no need to lock */ 125 ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback, 126 ghbn_arg); 127 } 128 129 static void sort_addresses(const struct hostent *host, 130 const struct apattern *sortlist, size_t nsort) 131 { 132 struct in_addr a1; 133 struct in_addr a2; 134 int i1; 135 int i2; 136 size_t ind1; 137 size_t ind2; 138 139 /* This is a simple insertion sort, not optimized at all. i1 walks 140 * through the address list, with the loop invariant that everything 141 * to the left of i1 is sorted. In the loop body, the value at i1 is moved 142 * back through the list (via i2) until it is in sorted order. 143 */ 144 for (i1 = 0; host->h_addr_list[i1]; i1++) { 145 memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr)); 146 ind1 = get_address_index(&a1, sortlist, nsort); 147 for (i2 = i1 - 1; i2 >= 0; i2--) { 148 memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr)); 149 ind2 = get_address_index(&a2, sortlist, nsort); 150 if (ind2 <= ind1) { 151 break; 152 } 153 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr)); 154 } 155 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr)); 156 } 157 } 158 159 /* Find the first entry in sortlist which matches addr. Return nsort 160 * if none of them match. 161 */ 162 static size_t get_address_index(const struct in_addr *addr, 163 const struct apattern *sortlist, size_t nsort) 164 { 165 size_t i; 166 struct ares_addr aaddr; 167 168 memset(&aaddr, 0, sizeof(aaddr)); 169 aaddr.family = AF_INET; 170 memcpy(&aaddr.addr.addr4, addr, 4); 171 172 for (i = 0; i < nsort; i++) { 173 if (sortlist[i].addr.family != AF_INET) { 174 continue; 175 } 176 177 if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) { 178 break; 179 } 180 } 181 182 return i; 183 } 184 185 static void sort6_addresses(const struct hostent *host, 186 const struct apattern *sortlist, size_t nsort) 187 { 188 struct ares_in6_addr a1; 189 struct ares_in6_addr a2; 190 int i1; 191 int i2; 192 size_t ind1; 193 size_t ind2; 194 195 /* This is a simple insertion sort, not optimized at all. i1 walks 196 * through the address list, with the loop invariant that everything 197 * to the left of i1 is sorted. In the loop body, the value at i1 is moved 198 * back through the list (via i2) until it is in sorted order. 199 */ 200 for (i1 = 0; host->h_addr_list[i1]; i1++) { 201 memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr)); 202 ind1 = get6_address_index(&a1, sortlist, nsort); 203 for (i2 = i1 - 1; i2 >= 0; i2--) { 204 memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr)); 205 ind2 = get6_address_index(&a2, sortlist, nsort); 206 if (ind2 <= ind1) { 207 break; 208 } 209 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr)); 210 } 211 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr)); 212 } 213 } 214 215 /* Find the first entry in sortlist which matches addr. Return nsort 216 * if none of them match. 217 */ 218 static size_t get6_address_index(const struct ares_in6_addr *addr, 219 const struct apattern *sortlist, size_t nsort) 220 { 221 size_t i; 222 struct ares_addr aaddr; 223 224 memset(&aaddr, 0, sizeof(aaddr)); 225 aaddr.family = AF_INET6; 226 memcpy(&aaddr.addr.addr6, addr, 16); 227 228 for (i = 0; i < nsort; i++) { 229 if (sortlist[i].addr.family != AF_INET6) { 230 continue; 231 } 232 233 if (ares_subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) { 234 break; 235 } 236 } 237 return i; 238 } 239 240 static ares_status_t ares_hostent_localhost(const char *name, int family, 241 struct hostent **host_out) 242 { 243 ares_status_t status; 244 struct ares_addrinfo *ai = NULL; 245 struct ares_addrinfo_hints hints; 246 247 memset(&hints, 0, sizeof(hints)); 248 hints.ai_family = family; 249 250 ai = ares_malloc_zero(sizeof(*ai)); 251 if (ai == NULL) { 252 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 253 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 254 } 255 256 status = ares_addrinfo_localhost(name, 0, &hints, ai); 257 if (status != ARES_SUCCESS) { 258 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 259 } 260 261 status = ares_addrinfo2hostent(ai, family, host_out); 262 if (status != ARES_SUCCESS) { 263 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 264 } 265 266 done: 267 ares_freeaddrinfo(ai); 268 return status; 269 } 270 271 /* I really have no idea why this is exposed as a public function, but since 272 * it is, we can't kill this legacy function. */ 273 static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel, 274 const char *name, int family, 275 struct hostent **host) 276 { 277 const ares_hosts_entry_t *entry; 278 ares_status_t status; 279 280 /* We only take the channel to ensure that ares_init() been called. */ 281 if (channel == NULL || name == NULL || host == NULL) { 282 /* Anything will do, really. This seems fine, and is consistent with 283 other error cases. */ 284 if (host != NULL) { 285 *host = NULL; 286 } 287 return ARES_ENOTFOUND; 288 } 289 290 *host = NULL; 291 292 /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ 293 if (ares_is_onion_domain(name)) { 294 return ARES_ENOTFOUND; 295 } 296 297 status = ares_hosts_search_host(channel, ARES_FALSE, name, &entry); 298 if (status != ARES_SUCCESS) { 299 goto done; 300 } 301 302 status = ares_hosts_entry_to_hostent(entry, family, host); 303 if (status != ARES_SUCCESS) { 304 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 305 } 306 307 done: 308 /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries 309 * SHOULD recognize localhost names as special and SHOULD always return the 310 * IP loopback address for address queries". 311 * We will also ignore ALL errors when trying to resolve localhost, such 312 * as permissions errors reading /etc/hosts or a malformed /etc/hosts. 313 * 314 * Also, just because the query itself returned success from /etc/hosts 315 * lookup doesn't mean it returned everything it needed to for all requested 316 * address families. As long as we're not on a critical out of memory 317 * condition pass it through to fill in any other address classes. */ 318 if (status != ARES_ENOMEM && ares_is_localhost(name)) { 319 return ares_hostent_localhost(name, family, host); 320 } 321 322 return status; 323 } 324 325 int ares_gethostbyname_file(ares_channel_t *channel, const char *name, 326 int family, struct hostent **host) 327 { 328 ares_status_t status; 329 if (channel == NULL) { 330 return ARES_ENOTFOUND; 331 } 332 333 ares_channel_lock(channel); 334 status = ares_gethostbyname_file_int(channel, name, family, host); 335 ares_channel_unlock(channel); 336 return (int)status; 337 }