1 2 /* Copyright 2005 by Dominick Meglio 3 * 4 * Permission to use, copy, modify, and distribute this 5 * software and its documentation for any purpose and without 6 * fee is hereby granted, provided that the above copyright 7 * notice appear in all copies and that both that copyright 8 * notice and this permission notice appear in supporting 9 * documentation, and that the name of M.I.T. not be used in 10 * advertising or publicity pertaining to distribution of the 11 * software without specific, written prior permission. 12 * M.I.T. makes no representations about the suitability of 13 * this software for any purpose. It is provided "as is" 14 * without express or implied warranty. 15 */ 16 #include "ares_setup.h" 17 18 #ifdef HAVE_GETSERVBYPORT_R 19 # if !defined(GETSERVBYPORT_R_ARGS) || \ 20 (GETSERVBYPORT_R_ARGS < 4) || (GETSERVBYPORT_R_ARGS > 6) 21 # error "you MUST specifiy a valid number of arguments for getservbyport_r" 22 # endif 23 #endif 24 25 #ifdef HAVE_SYS_SOCKET_H 26 # include <sys/socket.h> 27 #endif 28 #ifdef HAVE_NETINET_IN_H 29 # include <netinet/in.h> 30 #endif 31 #ifdef HAVE_NETDB_H 32 # include <netdb.h> 33 #endif 34 #ifdef HAVE_ARPA_INET_H 35 # include <arpa/inet.h> 36 #endif 37 #ifdef HAVE_ARPA_NAMESER_H 38 # include <arpa/nameser.h> 39 #else 40 # include "nameser.h" 41 #endif 42 #ifdef HAVE_ARPA_NAMESER_COMPAT_H 43 # include <arpa/nameser_compat.h> 44 #endif 45 46 #ifdef HAVE_NET_IF_H 47 #include <net/if.h> 48 #endif 49 50 #ifdef HAVE_UNISTD_H 51 #include <unistd.h> 52 #endif 53 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 58 #include "ares.h" 59 #include "ares_ipv6.h" 60 #include "inet_ntop.h" 61 #include "ares_nowarn.h" 62 #include "ares_private.h" 63 64 struct nameinfo_query { 65 ares_nameinfo_callback callback; 66 void *arg; 67 union { 68 struct sockaddr_in addr4; 69 struct sockaddr_in6 addr6; 70 } addr; 71 int family; 72 int flags; 73 int timeouts; 74 }; 75 76 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 77 #define IPBUFSIZ \ 78 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE) 79 #else 80 #define IPBUFSIZ \ 81 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")) 82 #endif 83 84 static void nameinfo_callback(void *arg, int status, int timeouts, 85 struct hostent *host); 86 static char *lookup_service(unsigned short port, int flags, 87 char *buf, size_t buflen); 88 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 89 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid, 90 char *buf, size_t buflen); 91 #endif 92 static char *ares_striendstr(const char *s1, const char *s2); 93 94 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, 95 ares_socklen_t salen, 96 int flags, ares_nameinfo_callback callback, void *arg) 97 { 98 struct sockaddr_in *addr = NULL; 99 struct sockaddr_in6 *addr6 = NULL; 100 struct nameinfo_query *niquery; 101 unsigned int port = 0; 102 103 /* Validate socket address family and length */ 104 if ((sa->sa_family == AF_INET) && 105 (salen == sizeof(struct sockaddr_in))) 106 { 107 addr = (struct sockaddr_in *)sa; 108 port = addr->sin_port; 109 } 110 else if ((sa->sa_family == AF_INET6) && 111 (salen == sizeof(struct sockaddr_in6))) 112 { 113 addr6 = (struct sockaddr_in6 *)sa; 114 port = addr6->sin6_port; 115 } 116 else 117 { 118 callback(arg, ARES_ENOTIMP, 0, NULL, NULL); 119 return; 120 } 121 122 /* If neither, assume they want a host */ 123 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) 124 flags |= ARES_NI_LOOKUPHOST; 125 126 /* All they want is a service, no need for DNS */ 127 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) 128 { 129 char buf[33], *service; 130 131 service = lookup_service((unsigned short)(port & 0xffff), 132 flags, buf, sizeof(buf)); 133 callback(arg, ARES_SUCCESS, 0, NULL, service); 134 return; 135 } 136 137 /* They want a host lookup */ 138 if ((flags & ARES_NI_LOOKUPHOST)) 139 { 140 /* A numeric host can be handled without DNS */ 141 if ((flags & ARES_NI_NUMERICHOST)) 142 { 143 char ipbuf[IPBUFSIZ]; 144 char srvbuf[33]; 145 char *service = NULL; 146 ipbuf[0] = 0; 147 148 /* Specifying not to lookup a host, but then saying a host 149 * is required has to be illegal. 150 */ 151 if (flags & ARES_NI_NAMEREQD) 152 { 153 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL); 154 return; 155 } 156 if (salen == sizeof(struct sockaddr_in6)) 157 { 158 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ); 159 /* If the system supports scope IDs, use it */ 160 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 161 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf)); 162 #endif 163 } 164 else 165 { 166 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ); 167 } 168 /* They also want a service */ 169 if (flags & ARES_NI_LOOKUPSERVICE) 170 service = lookup_service((unsigned short)(port & 0xffff), 171 flags, srvbuf, sizeof(srvbuf)); 172 callback(arg, ARES_SUCCESS, 0, ipbuf, service); 173 return; 174 } 175 /* This is where a DNS lookup becomes necessary */ 176 else 177 { 178 niquery = malloc(sizeof(struct nameinfo_query)); 179 if (!niquery) 180 { 181 callback(arg, ARES_ENOMEM, 0, NULL, NULL); 182 return; 183 } 184 niquery->callback = callback; 185 niquery->arg = arg; 186 niquery->flags = flags; 187 niquery->timeouts = 0; 188 if (sa->sa_family == AF_INET) 189 { 190 niquery->family = AF_INET; 191 memcpy(&niquery->addr.addr4, addr, sizeof(struct in_addr)); 192 ares_gethostbyaddr(channel, &addr->sin_addr, 193 sizeof(struct in_addr), AF_INET, 194 nameinfo_callback, niquery); 195 } 196 else 197 { 198 niquery->family = AF_INET6; 199 memcpy(&niquery->addr.addr6, addr6, sizeof(struct ares_in6_addr)); 200 ares_gethostbyaddr(channel, &addr6->sin6_addr, 201 sizeof(struct ares_in6_addr), AF_INET6, 202 nameinfo_callback, niquery); 203 } 204 } 205 } 206 } 207 208 static void nameinfo_callback(void *arg, int status, int timeouts, 209 struct hostent *host) 210 { 211 struct nameinfo_query *niquery = (struct nameinfo_query *) arg; 212 char srvbuf[33]; 213 char *service = NULL; 214 215 niquery->timeouts += timeouts; 216 if (status == ARES_SUCCESS) 217 { 218 /* They want a service too */ 219 if (niquery->flags & ARES_NI_LOOKUPSERVICE) 220 { 221 if (niquery->family == AF_INET) 222 service = lookup_service(niquery->addr.addr4.sin_port, 223 niquery->flags, srvbuf, sizeof(srvbuf)); 224 else 225 service = lookup_service(niquery->addr.addr6.sin6_port, 226 niquery->flags, srvbuf, sizeof(srvbuf)); 227 } 228 /* NOFQDN means we have to strip off the domain name portion. We do 229 this by determining our own domain name, then searching the string 230 for this domain name and removing it. 231 */ 232 #ifdef HAVE_GETHOSTNAME 233 if (niquery->flags & ARES_NI_NOFQDN) 234 { 235 char buf[255]; 236 char *domain; 237 gethostname(buf, 255); 238 if ((domain = strchr(buf, '.')) != NULL) 239 { 240 char *end = ares_striendstr(host->h_name, domain); 241 if (end) 242 *end = 0; 243 } 244 } 245 #endif 246 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, 247 (char *)(host->h_name), 248 service); 249 free(niquery); 250 return; 251 } 252 /* We couldn't find the host, but it's OK, we can use the IP */ 253 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD)) 254 { 255 char ipbuf[IPBUFSIZ]; 256 if (niquery->family == AF_INET) 257 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, 258 IPBUFSIZ); 259 else 260 { 261 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, 262 IPBUFSIZ); 263 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 264 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, 265 sizeof(ipbuf)); 266 #endif 267 } 268 /* They want a service too */ 269 if (niquery->flags & ARES_NI_LOOKUPSERVICE) 270 { 271 if (niquery->family == AF_INET) 272 service = lookup_service(niquery->addr.addr4.sin_port, 273 niquery->flags, srvbuf, sizeof(srvbuf)); 274 else 275 service = lookup_service(niquery->addr.addr6.sin6_port, 276 niquery->flags, srvbuf, sizeof(srvbuf)); 277 } 278 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf, 279 service); 280 free(niquery); 281 return; 282 } 283 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL); 284 free(niquery); 285 } 286 287 static char *lookup_service(unsigned short port, int flags, 288 char *buf, size_t buflen) 289 { 290 const char *proto; 291 struct servent *sep; 292 #ifdef HAVE_GETSERVBYPORT_R 293 struct servent se; 294 #endif 295 char tmpbuf[4096]; 296 297 if (port) 298 { 299 if (flags & ARES_NI_NUMERICSERV) 300 sep = NULL; 301 else 302 { 303 if (flags & ARES_NI_UDP) 304 proto = "udp"; 305 else if (flags & ARES_NI_SCTP) 306 proto = "sctp"; 307 else if (flags & ARES_NI_DCCP) 308 proto = "dccp"; 309 else 310 proto = "tcp"; 311 #ifdef HAVE_GETSERVBYPORT_R 312 sep = &se; 313 memset(tmpbuf, 0, sizeof(tmpbuf)); 314 #if GETSERVBYPORT_R_ARGS == 6 315 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, 316 sizeof(tmpbuf), &sep) != 0) 317 sep = NULL; 318 #elif GETSERVBYPORT_R_ARGS == 5 319 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, 320 sizeof(tmpbuf)); 321 #elif GETSERVBYPORT_R_ARGS == 4 322 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0) 323 sep = NULL; 324 #else 325 /* Lets just hope the OS uses TLS! */ 326 sep = getservbyport(port, proto); 327 #endif 328 #else 329 /* Lets just hope the OS uses TLS! */ 330 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) 331 sep = getservbyport(port, (char*)proto); 332 #else 333 sep = getservbyport(port, proto); 334 #endif 335 #endif 336 } 337 if (sep && sep->s_name) 338 /* get service name */ 339 strcpy(tmpbuf, sep->s_name); 340 else 341 /* get port as a string */ 342 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port)); 343 if (strlen(tmpbuf) < buflen) 344 /* return it if buffer big enough */ 345 strcpy(buf, tmpbuf); 346 else 347 /* avoid reusing previous one */ 348 buf[0] = '\0'; 349 return buf; 350 } 351 buf[0] = '\0'; 352 return NULL; 353 } 354 355 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 356 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags, 357 char *buf, size_t buflen) 358 { 359 #ifdef HAVE_IF_INDEXTONAME 360 int is_ll, is_mcll; 361 #endif 362 static const char fmt_u[] = "%u"; 363 static const char fmt_lu[] = "%lu"; 364 char tmpbuf[IF_NAMESIZE + 2]; 365 size_t bufl; 366 const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))? 367 fmt_lu:fmt_u; 368 369 tmpbuf[0] = '%'; 370 371 #ifdef HAVE_IF_INDEXTONAME 372 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr); 373 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr); 374 if ((flags & ARES_NI_NUMERICSCOPE) || 375 (!is_ll && !is_mcll)) 376 { 377 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); 378 } 379 else 380 { 381 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL) 382 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); 383 } 384 #else 385 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id); 386 (void) flags; 387 #endif 388 tmpbuf[IF_NAMESIZE + 1] = '\0'; 389 bufl = strlen(buf); 390 391 if(bufl + strlen(tmpbuf) < buflen) 392 /* only append the scopeid string if it fits in the target buffer */ 393 strcpy(&buf[bufl], tmpbuf); 394 } 395 #endif 396 397 /* Determines if s1 ends with the string in s2 (case-insensitive) */ 398 static char *ares_striendstr(const char *s1, const char *s2) 399 { 400 const char *c1, *c2, *c1_begin; 401 int lo1, lo2; 402 size_t s1_len = strlen(s1), s2_len = strlen(s2); 403 404 /* If the substr is longer than the full str, it can't match */ 405 if (s2_len > s1_len) 406 return NULL; 407 408 /* Jump to the end of s1 minus the length of s2 */ 409 c1_begin = s1+s1_len-s2_len; 410 c1 = (const char *)c1_begin; 411 c2 = s2; 412 while (c2 < s2+s2_len) 413 { 414 lo1 = TOLOWER(*c1); 415 lo2 = TOLOWER(*c2); 416 if (lo1 != lo2) 417 return NULL; 418 else 419 { 420 c1++; 421 c2++; 422 } 423 } 424 if (c2 == c1 && c2 == NULL) 425 return (char *)c1_begin; 426 return NULL; 427 } 428