ares_getnameinfo.c (13962B)
1 /* MIT License 2 * 3 * Copyright (c) 2005, 2013 Dominick Meglio 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 * SPDX-License-Identifier: MIT 25 */ 26 #include "ares_private.h" 27 28 #ifdef HAVE_GETSERVBYPORT_R 29 # if !defined(GETSERVBYPORT_R_ARGS) || (GETSERVBYPORT_R_ARGS < 4) || \ 30 (GETSERVBYPORT_R_ARGS > 6) 31 # error "you MUST specify a valid number of arguments for getservbyport_r" 32 # endif 33 #endif 34 35 #ifdef HAVE_NETINET_IN_H 36 # include <netinet/in.h> 37 #endif 38 #ifdef HAVE_NETDB_H 39 # include <netdb.h> 40 #endif 41 #ifdef HAVE_ARPA_INET_H 42 # include <arpa/inet.h> 43 #endif 44 45 #include "ares_nameser.h" 46 47 #ifdef HAVE_NET_IF_H 48 # include <net/if.h> 49 #endif 50 #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H) 51 # include <iphlpapi.h> 52 #endif 53 54 #include "ares_ipv6.h" 55 56 struct nameinfo_query { 57 ares_nameinfo_callback callback; 58 void *arg; 59 60 union { 61 struct sockaddr_in addr4; 62 struct sockaddr_in6 addr6; 63 } addr; 64 65 int family; 66 unsigned int flags; 67 size_t timeouts; 68 }; 69 70 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 71 # define IPBUFSIZ \ 72 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE) 73 #else 74 # define IPBUFSIZ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")) 75 #endif 76 77 static void nameinfo_callback(void *arg, int status, int timeouts, 78 struct hostent *host); 79 static char *lookup_service(unsigned short port, unsigned int flags, char *buf, 80 size_t buflen); 81 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 82 static void append_scopeid(const struct sockaddr_in6 *addr6, unsigned int flags, 83 char *buf, size_t buflen); 84 #endif 85 static char *ares_striendstr(const char *s1, const char *s2); 86 87 static void ares_getnameinfo_int(ares_channel_t *channel, 88 const struct sockaddr *sa, 89 ares_socklen_t salen, int flags_int, 90 ares_nameinfo_callback callback, void *arg) 91 { 92 const struct sockaddr_in *addr = NULL; 93 const struct sockaddr_in6 *addr6 = NULL; 94 struct nameinfo_query *niquery; 95 unsigned short port = 0; 96 unsigned int flags = (unsigned int)flags_int; 97 98 /* Validate socket address family and length */ 99 if (sa && sa->sa_family == AF_INET && 100 salen >= (ares_socklen_t)sizeof(struct sockaddr_in)) { 101 addr = CARES_INADDR_CAST(const struct sockaddr_in *, sa); 102 port = addr->sin_port; 103 } else if (sa && sa->sa_family == AF_INET6 && 104 salen >= (ares_socklen_t)sizeof(struct sockaddr_in6)) { 105 addr6 = CARES_INADDR_CAST(const struct sockaddr_in6 *, sa); 106 port = addr6->sin6_port; 107 } else { 108 callback(arg, ARES_ENOTIMP, 0, NULL, NULL); 109 return; 110 } 111 112 /* If neither, assume they want a host */ 113 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) { 114 flags |= ARES_NI_LOOKUPHOST; 115 } 116 117 /* All they want is a service, no need for DNS */ 118 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) { 119 char buf[33]; 120 char *service; 121 122 service = 123 lookup_service((unsigned short)(port & 0xffff), flags, buf, sizeof(buf)); 124 callback(arg, ARES_SUCCESS, 0, NULL, service); 125 return; 126 } 127 128 /* They want a host lookup */ 129 if (flags & ARES_NI_LOOKUPHOST) { 130 /* A numeric host can be handled without DNS */ 131 if (flags & ARES_NI_NUMERICHOST) { 132 char ipbuf[IPBUFSIZ]; 133 char srvbuf[33]; 134 char *service = NULL; 135 ipbuf[0] = 0; 136 137 /* Specifying not to lookup a host, but then saying a host 138 * is required has to be illegal. 139 */ 140 if (flags & ARES_NI_NAMEREQD) { 141 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL); 142 return; 143 } 144 if (sa->sa_family == AF_INET6) { 145 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ); 146 /* If the system supports scope IDs, use it */ 147 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 148 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf)); 149 #endif 150 } else { 151 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ); 152 } 153 /* They also want a service */ 154 if (flags & ARES_NI_LOOKUPSERVICE) { 155 service = lookup_service((unsigned short)(port & 0xffff), flags, srvbuf, 156 sizeof(srvbuf)); 157 } 158 callback(arg, ARES_SUCCESS, 0, ipbuf, service); 159 return; 160 } else { 161 /* This is where a DNS lookup becomes necessary */ 162 niquery = ares_malloc(sizeof(struct nameinfo_query)); 163 if (!niquery) { 164 callback(arg, ARES_ENOMEM, 0, NULL, NULL); 165 return; 166 } 167 niquery->callback = callback; 168 niquery->arg = arg; 169 niquery->flags = flags; 170 niquery->timeouts = 0; 171 if (sa->sa_family == AF_INET) { 172 niquery->family = AF_INET; 173 memcpy(&niquery->addr.addr4, addr, sizeof(niquery->addr.addr4)); 174 ares_gethostbyaddr_nolock(channel, &addr->sin_addr, 175 sizeof(struct in_addr), AF_INET, 176 nameinfo_callback, niquery); 177 } else { 178 niquery->family = AF_INET6; 179 memcpy(&niquery->addr.addr6, addr6, sizeof(niquery->addr.addr6)); 180 ares_gethostbyaddr_nolock(channel, &addr6->sin6_addr, 181 sizeof(struct ares_in6_addr), AF_INET6, 182 nameinfo_callback, niquery); 183 } 184 } 185 } 186 } 187 188 void ares_getnameinfo(ares_channel_t *channel, const struct sockaddr *sa, 189 ares_socklen_t salen, int flags_int, 190 ares_nameinfo_callback callback, void *arg) 191 { 192 if (channel == NULL) { 193 return; 194 } 195 196 ares_channel_lock(channel); 197 ares_getnameinfo_int(channel, sa, salen, flags_int, callback, arg); 198 ares_channel_unlock(channel); 199 } 200 201 static void nameinfo_callback(void *arg, int status, int timeouts, 202 struct hostent *host) 203 { 204 struct nameinfo_query *niquery = (struct nameinfo_query *)arg; 205 char srvbuf[33]; 206 char *service = NULL; 207 208 niquery->timeouts += (size_t)timeouts; 209 if (status == ARES_SUCCESS) { 210 /* They want a service too */ 211 if (niquery->flags & ARES_NI_LOOKUPSERVICE) { 212 if (niquery->family == AF_INET) { 213 service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags, 214 srvbuf, sizeof(srvbuf)); 215 } else { 216 service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags, 217 srvbuf, sizeof(srvbuf)); 218 } 219 } 220 /* NOFQDN means we have to strip off the domain name portion. We do 221 this by determining our own domain name, then searching the string 222 for this domain name and removing it. 223 */ 224 #ifdef HAVE_GETHOSTNAME 225 if (niquery->flags & ARES_NI_NOFQDN) { 226 char buf[255]; 227 const char *domain; 228 gethostname(buf, 255); 229 if ((domain = strchr(buf, '.')) != NULL) { 230 char *end = ares_striendstr(host->h_name, domain); 231 if (end) { 232 *end = 0; 233 } 234 } 235 } 236 #endif 237 niquery->callback(niquery->arg, ARES_SUCCESS, (int)niquery->timeouts, 238 host->h_name, service); 239 ares_free(niquery); 240 return; 241 } 242 /* We couldn't find the host, but it's OK, we can use the IP */ 243 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD)) { 244 char ipbuf[IPBUFSIZ]; 245 if (niquery->family == AF_INET) { 246 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ); 247 } else { 248 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ); 249 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 250 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, 251 sizeof(ipbuf)); 252 #endif 253 } 254 /* They want a service too */ 255 if (niquery->flags & ARES_NI_LOOKUPSERVICE) { 256 if (niquery->family == AF_INET) { 257 service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags, 258 srvbuf, sizeof(srvbuf)); 259 } else { 260 service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags, 261 srvbuf, sizeof(srvbuf)); 262 } 263 } 264 niquery->callback(niquery->arg, ARES_SUCCESS, (int)niquery->timeouts, ipbuf, 265 service); 266 ares_free(niquery); 267 return; 268 } 269 niquery->callback(niquery->arg, status, (int)niquery->timeouts, NULL, NULL); 270 ares_free(niquery); 271 } 272 273 static char *lookup_service(unsigned short port, unsigned int flags, char *buf, 274 size_t buflen) 275 { 276 const char *proto; 277 struct servent *sep; 278 #ifdef HAVE_GETSERVBYPORT_R 279 struct servent se; 280 #endif 281 char tmpbuf[4096]; 282 const char *name; 283 size_t name_len; 284 285 if (port) { 286 if (flags & ARES_NI_NUMERICSERV) { 287 sep = NULL; 288 } else { 289 if (flags & ARES_NI_UDP) { 290 proto = "udp"; 291 } else if (flags & ARES_NI_SCTP) { 292 proto = "sctp"; 293 } else if (flags & ARES_NI_DCCP) { 294 proto = "dccp"; 295 } else { 296 proto = "tcp"; 297 } 298 #ifdef HAVE_GETSERVBYPORT_R 299 memset(&se, 0, sizeof(se)); 300 sep = &se; 301 memset(tmpbuf, 0, sizeof(tmpbuf)); 302 # if GETSERVBYPORT_R_ARGS == 6 303 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), 304 &sep) != 0) { 305 sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */ 306 } 307 # elif GETSERVBYPORT_R_ARGS == 5 308 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf)); 309 # elif GETSERVBYPORT_R_ARGS == 4 310 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0) { 311 sep = NULL; 312 } 313 # else 314 /* Lets just hope the OS uses TLS! */ 315 sep = getservbyport(port, proto); 316 # endif 317 #else 318 /* Lets just hope the OS uses TLS! */ 319 # if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) 320 sep = getservbyport(port, (char *)proto); 321 # else 322 sep = getservbyport(port, proto); 323 # endif 324 #endif 325 } 326 if (sep && sep->s_name) { 327 /* get service name */ 328 name = sep->s_name; 329 } else { 330 /* get port as a string */ 331 snprintf(tmpbuf, sizeof(tmpbuf), "%u", (unsigned int)ntohs(port)); 332 name = tmpbuf; 333 } 334 name_len = ares_strlen(name); 335 if (name_len < buflen) { 336 /* return it if buffer big enough */ 337 memcpy(buf, name, name_len + 1); 338 } else { 339 /* avoid reusing previous one */ 340 buf[0] = '\0'; /* LCOV_EXCL_LINE: no real service names are too big */ 341 } 342 return buf; 343 } 344 buf[0] = '\0'; 345 return NULL; 346 } 347 348 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 349 static void append_scopeid(const struct sockaddr_in6 *addr6, unsigned int flags, 350 char *buf, size_t buflen) 351 { 352 # ifdef HAVE_IF_INDEXTONAME 353 int is_ll; 354 int is_mcll; 355 # endif 356 char tmpbuf[IF_NAMESIZE + 2]; 357 size_t bufl; 358 359 tmpbuf[0] = '%'; 360 361 # ifdef HAVE_IF_INDEXTONAME 362 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr); 363 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr); 364 if ((flags & ARES_NI_NUMERICSCOPE) || (!is_ll && !is_mcll)) { 365 snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu", 366 (unsigned long)addr6->sin6_scope_id); 367 } else { 368 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL) { 369 snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu", 370 (unsigned long)addr6->sin6_scope_id); 371 } 372 } 373 # else 374 snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu", 375 (unsigned long)addr6->sin6_scope_id); 376 (void)flags; 377 # endif 378 tmpbuf[IF_NAMESIZE + 1] = '\0'; 379 bufl = ares_strlen(buf); 380 381 if (bufl + ares_strlen(tmpbuf) < buflen) { 382 /* only append the scopeid string if it fits in the target buffer */ 383 ares_strcpy(&buf[bufl], tmpbuf, buflen - bufl); 384 } 385 } 386 #endif 387 388 /* Determines if s1 ends with the string in s2 (case-insensitive) */ 389 static char *ares_striendstr(const char *s1, const char *s2) 390 { 391 const char *c1; 392 const char *c2; 393 const char *c1_begin; 394 int lo1; 395 int lo2; 396 size_t s1_len = ares_strlen(s1); 397 size_t s2_len = ares_strlen(s2); 398 399 if (s1 == NULL || s2 == NULL) { 400 return NULL; 401 } 402 403 /* If the substr is longer than the full str, it can't match */ 404 if (s2_len > s1_len) { 405 return NULL; 406 } 407 408 /* Jump to the end of s1 minus the length of s2 */ 409 c1_begin = s1 + s1_len - s2_len; 410 c1 = c1_begin; 411 c2 = s2; 412 while (c2 < s2 + s2_len) { 413 lo1 = ares_tolower((unsigned char)*c1); 414 lo2 = ares_tolower((unsigned char)*c2); 415 if (lo1 != lo2) { 416 return NULL; 417 } else { 418 c1++; 419 c2++; 420 } 421 } 422 /* Cast off const */ 423 return (char *)((size_t)c1_begin); 424 } 425 426 ares_bool_t ares_is_onion_domain(const char *name) 427 { 428 if (ares_striendstr(name, ".onion")) { 429 return ARES_TRUE; 430 } 431 432 if (ares_striendstr(name, ".onion.")) { 433 return ARES_TRUE; 434 } 435 436 return ARES_FALSE; 437 }