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_setup.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.h" 47#include "ares_inet_net_pton.h" 48#include "ares_platform.h" 49#include "ares_private.h" 50 51static void sort_addresses(const struct hostent *host, 52 const struct apattern *sortlist, size_t nsort); 53static void sort6_addresses(const struct hostent *host, 54 const struct apattern *sortlist, size_t nsort); 55static size_t get_address_index(const struct in_addr *addr, 56 const struct apattern *sortlist, size_t nsort); 57static size_t get6_address_index(const struct ares_in6_addr *addr, 58 const struct apattern *sortlist, size_t nsort); 59 60struct host_query { 61 ares_host_callback callback; 62 void *arg; 63 ares_channel_t *channel; 64}; 65 66static void ares_gethostbyname_callback(void *arg, int status, int timeouts, 67 struct ares_addrinfo *result) 68{ 69 struct hostent *hostent = NULL; 70 struct host_query *ghbn_arg = arg; 71 72 if (status == ARES_SUCCESS) { 73 status = (int)ares__addrinfo2hostent(result, AF_UNSPEC, &hostent); 74 } 75 76 /* addrinfo2hostent will only return ENODATA if there are no addresses _and_ 77 * no cname/aliases. However, gethostbyname will return ENODATA even if there 78 * is cname/alias data */ 79 if (status == ARES_SUCCESS && hostent && 80 (!hostent->h_addr_list || !hostent->h_addr_list[0])) { 81 status = ARES_ENODATA; 82 } 83 84 if (status == ARES_SUCCESS && ghbn_arg->channel->nsort && hostent) { 85 if (hostent->h_addrtype == AF_INET6) { 86 sort6_addresses(hostent, ghbn_arg->channel->sortlist, 87 ghbn_arg->channel->nsort); 88 } 89 if (hostent->h_addrtype == AF_INET) { 90 sort_addresses(hostent, ghbn_arg->channel->sortlist, 91 ghbn_arg->channel->nsort); 92 } 93 } 94 95 ghbn_arg->callback(ghbn_arg->arg, status, timeouts, hostent); 96 97 ares_freeaddrinfo(result); 98 ares_free(ghbn_arg); 99 ares_free_hostent(hostent); 100} 101 102void ares_gethostbyname(ares_channel_t *channel, const char *name, int family, 103 ares_host_callback callback, void *arg) 104{ 105 const struct ares_addrinfo_hints hints = { ARES_AI_CANONNAME, family, 0, 0 }; 106 struct host_query *ghbn_arg; 107 108 if (!callback) { 109 return; 110 } 111 112 ghbn_arg = ares_malloc(sizeof(*ghbn_arg)); 113 if (!ghbn_arg) { 114 callback(arg, ARES_ENOMEM, 0, NULL); 115 return; 116 } 117 118 ghbn_arg->callback = callback; 119 ghbn_arg->arg = arg; 120 ghbn_arg->channel = channel; 121 122 /* NOTE: ares_getaddrinfo() locks the channel, we don't use the channel 123 * outside so no need to lock */ 124 ares_getaddrinfo(channel, name, NULL, &hints, ares_gethostbyname_callback, 125 ghbn_arg); 126} 127 128static void sort_addresses(const struct hostent *host, 129 const struct apattern *sortlist, size_t nsort) 130{ 131 struct in_addr a1; 132 struct in_addr a2; 133 int i1; 134 int i2; 135 size_t ind1; 136 size_t ind2; 137 138 /* This is a simple insertion sort, not optimized at all. i1 walks 139 * through the address list, with the loop invariant that everything 140 * to the left of i1 is sorted. In the loop body, the value at i1 is moved 141 * back through the list (via i2) until it is in sorted order. 142 */ 143 for (i1 = 0; host->h_addr_list[i1]; i1++) { 144 memcpy(&a1, host->h_addr_list[i1], sizeof(struct in_addr)); 145 ind1 = get_address_index(&a1, sortlist, nsort); 146 for (i2 = i1 - 1; i2 >= 0; i2--) { 147 memcpy(&a2, host->h_addr_list[i2], sizeof(struct in_addr)); 148 ind2 = get_address_index(&a2, sortlist, nsort); 149 if (ind2 <= ind1) { 150 break; 151 } 152 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct in_addr)); 153 } 154 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct in_addr)); 155 } 156} 157 158/* Find the first entry in sortlist which matches addr. Return nsort 159 * if none of them match. 160 */ 161static size_t get_address_index(const struct in_addr *addr, 162 const struct apattern *sortlist, size_t nsort) 163{ 164 size_t i; 165 struct ares_addr aaddr; 166 167 memset(&aaddr, 0, sizeof(aaddr)); 168 aaddr.family = AF_INET; 169 memcpy(&aaddr.addr.addr4, addr, 4); 170 171 for (i = 0; i < nsort; i++) { 172 if (sortlist[i].addr.family != AF_INET) { 173 continue; 174 } 175 176 if (ares__subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) { 177 break; 178 } 179 } 180 181 return i; 182} 183 184static void sort6_addresses(const struct hostent *host, 185 const struct apattern *sortlist, size_t nsort) 186{ 187 struct ares_in6_addr a1; 188 struct ares_in6_addr a2; 189 int i1; 190 int i2; 191 size_t ind1; 192 size_t ind2; 193 194 /* This is a simple insertion sort, not optimized at all. i1 walks 195 * through the address list, with the loop invariant that everything 196 * to the left of i1 is sorted. In the loop body, the value at i1 is moved 197 * back through the list (via i2) until it is in sorted order. 198 */ 199 for (i1 = 0; host->h_addr_list[i1]; i1++) { 200 memcpy(&a1, host->h_addr_list[i1], sizeof(struct ares_in6_addr)); 201 ind1 = get6_address_index(&a1, sortlist, nsort); 202 for (i2 = i1 - 1; i2 >= 0; i2--) { 203 memcpy(&a2, host->h_addr_list[i2], sizeof(struct ares_in6_addr)); 204 ind2 = get6_address_index(&a2, sortlist, nsort); 205 if (ind2 <= ind1) { 206 break; 207 } 208 memcpy(host->h_addr_list[i2 + 1], &a2, sizeof(struct ares_in6_addr)); 209 } 210 memcpy(host->h_addr_list[i2 + 1], &a1, sizeof(struct ares_in6_addr)); 211 } 212} 213 214/* Find the first entry in sortlist which matches addr. Return nsort 215 * if none of them match. 216 */ 217static size_t get6_address_index(const struct ares_in6_addr *addr, 218 const struct apattern *sortlist, size_t nsort) 219{ 220 size_t i; 221 struct ares_addr aaddr; 222 223 memset(&aaddr, 0, sizeof(aaddr)); 224 aaddr.family = AF_INET6; 225 memcpy(&aaddr.addr.addr6, addr, 16); 226 227 for (i = 0; i < nsort; i++) { 228 if (sortlist[i].addr.family != AF_INET6) { 229 continue; 230 } 231 232 if (ares__subnet_match(&aaddr, &sortlist[i].addr, sortlist[i].mask)) { 233 break; 234 } 235 } 236 return i; 237} 238 239static ares_status_t ares__hostent_localhost(const char *name, int family, 240 struct hostent **host_out) 241{ 242 ares_status_t status; 243 struct ares_addrinfo *ai = NULL; 244 struct ares_addrinfo_hints hints; 245 246 memset(&hints, 0, sizeof(hints)); 247 hints.ai_family = family; 248 249 ai = ares_malloc_zero(sizeof(*ai)); 250 if (ai == NULL) { 251 status = ARES_ENOMEM; 252 goto done; 253 } 254 255 status = ares__addrinfo_localhost(name, 0, &hints, ai); 256 if (status != ARES_SUCCESS) { 257 goto done; 258 } 259 260 status = ares__addrinfo2hostent(ai, family, host_out); 261 if (status != ARES_SUCCESS) { 262 goto done; 263 } 264 265done: 266 ares_freeaddrinfo(ai); 267 return status; 268} 269 270/* I really have no idea why this is exposed as a public function, but since 271 * it is, we can't kill this legacy function. */ 272static ares_status_t ares_gethostbyname_file_int(ares_channel_t *channel, 273 const char *name, int family, 274 struct hostent **host) 275{ 276 const ares_hosts_entry_t *entry; 277 ares_status_t status; 278 279 /* We only take the channel to ensure that ares_init() been called. */ 280 if (channel == NULL || name == NULL || host == NULL) { 281 /* Anything will do, really. This seems fine, and is consistent with 282 other error cases. */ 283 if (host != NULL) { 284 *host = NULL; 285 } 286 return ARES_ENOTFOUND; 287 } 288 289 /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ 290 if (ares__is_onion_domain(name)) { 291 return ARES_ENOTFOUND; 292 } 293 294 status = ares__hosts_search_host(channel, ARES_FALSE, name, &entry); 295 if (status != ARES_SUCCESS) { 296 goto done; 297 } 298 299 status = ares__hosts_entry_to_hostent(entry, family, host); 300 if (status != ARES_SUCCESS) { 301 goto done; 302 } 303 304done: 305 /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries 306 * SHOULD recognize localhost names as special and SHOULD always return the 307 * IP loopback address for address queries". 308 * We will also ignore ALL errors when trying to resolve localhost, such 309 * as permissions errors reading /etc/hosts or a malformed /etc/hosts */ 310 if (status != ARES_SUCCESS && status != ARES_ENOMEM && 311 ares__is_localhost(name)) { 312 return ares__hostent_localhost(name, family, host); 313 } 314 315 return status; 316} 317 318int ares_gethostbyname_file(ares_channel_t *channel, const char *name, 319 int family, struct hostent **host) 320{ 321 ares_status_t status; 322 if (channel == NULL) { 323 return ARES_ENOTFOUND; 324 } 325 326 ares__channel_lock(channel); 327 status = ares_gethostbyname_file_int(channel, name, family, host); 328 ares__channel_unlock(channel); 329 return (int)status; 330} 331