2 /* Copyright 2005 by Dominick Meglio
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.
16 #include "ares_setup.h"
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"
25 #ifdef HAVE_SYS_SOCKET_H
26 # include <sys/socket.h>
28 #ifdef HAVE_NETINET_IN_H
29 # include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 # include <arpa/inet.h>
37 #ifdef HAVE_ARPA_NAMESER_H
38 # include <arpa/nameser.h>
42 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
43 # include <arpa/nameser_compat.h>
59 #include "ares_ipv6.h"
60 #include "inet_ntop.h"
61 #include "ares_nowarn.h"
62 #include "ares_private.h"
64 struct nameinfo_query {
65 ares_nameinfo_callback callback;
68 struct sockaddr_in addr4;
69 struct sockaddr_in6 addr6;
76 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
78 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
81 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
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);
92 static char *ares_striendstr(const char *s1, const char *s2);
94 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
96 int flags, ares_nameinfo_callback callback, void *arg)
98 struct sockaddr_in *addr = NULL;
99 struct sockaddr_in6 *addr6 = NULL;
100 struct nameinfo_query *niquery;
101 unsigned int port = 0;
103 /* Validate socket address family and length */
104 if ((sa->sa_family == AF_INET) &&
105 (salen == sizeof(struct sockaddr_in)))
107 addr = (struct sockaddr_in *)sa;
108 port = addr->sin_port;
110 else if ((sa->sa_family == AF_INET6) &&
111 (salen == sizeof(struct sockaddr_in6)))
113 addr6 = (struct sockaddr_in6 *)sa;
114 port = addr6->sin6_port;
118 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
122 /* If neither, assume they want a host */
123 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
124 flags |= ARES_NI_LOOKUPHOST;
126 /* All they want is a service, no need for DNS */
127 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
129 char buf[33], *service;
131 service = lookup_service((unsigned short)(port & 0xffff),
132 flags, buf, sizeof(buf));
133 callback(arg, ARES_SUCCESS, 0, NULL, service);
137 /* They want a host lookup */
138 if ((flags & ARES_NI_LOOKUPHOST))
140 /* A numeric host can be handled without DNS */
141 if ((flags & ARES_NI_NUMERICHOST))
143 char ipbuf[IPBUFSIZ];
145 char *service = NULL;
148 /* Specifying not to lookup a host, but then saying a host
149 * is required has to be illegal.
151 if (flags & ARES_NI_NAMEREQD)
153 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
156 if (salen == sizeof(struct sockaddr_in6))
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));
166 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
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);
175 /* This is where a DNS lookup becomes necessary */
178 niquery = malloc(sizeof(struct nameinfo_query));
181 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
184 niquery->callback = callback;
186 niquery->flags = flags;
187 niquery->timeouts = 0;
188 if (sa->sa_family == AF_INET)
190 niquery->family = AF_INET;
191 memcpy(&niquery->addr.addr4, addr, sizeof(addr));
192 ares_gethostbyaddr(channel, &addr->sin_addr,
193 sizeof(struct in_addr), AF_INET,
194 nameinfo_callback, niquery);
198 niquery->family = AF_INET6;
199 memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
200 ares_gethostbyaddr(channel, &addr6->sin6_addr,
201 sizeof(struct ares_in6_addr), AF_INET6,
202 nameinfo_callback, niquery);
208 static void nameinfo_callback(void *arg, int status, int timeouts,
209 struct hostent *host)
211 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
213 char *service = NULL;
215 niquery->timeouts += timeouts;
216 if (status == ARES_SUCCESS)
218 /* They want a service too */
219 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
221 if (niquery->family == AF_INET)
222 service = lookup_service(niquery->addr.addr4.sin_port,
223 niquery->flags, srvbuf, sizeof(srvbuf));
225 service = lookup_service(niquery->addr.addr6.sin6_port,
226 niquery->flags, srvbuf, sizeof(srvbuf));
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.
232 #ifdef HAVE_GETHOSTNAME
233 if (niquery->flags & ARES_NI_NOFQDN)
237 gethostname(buf, 255);
238 if ((domain = strchr(buf, '.')) != NULL)
240 char *end = ares_striendstr(host->h_name, domain);
246 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
247 (char *)(host->h_name),
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))
255 char ipbuf[IPBUFSIZ];
256 if (niquery->family == AF_INET)
257 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
261 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
263 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
264 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
268 /* They want a service too */
269 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
271 if (niquery->family == AF_INET)
272 service = lookup_service(niquery->addr.addr4.sin_port,
273 niquery->flags, srvbuf, sizeof(srvbuf));
275 service = lookup_service(niquery->addr.addr6.sin6_port,
276 niquery->flags, srvbuf, sizeof(srvbuf));
278 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
283 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
287 static char *lookup_service(unsigned short port, int flags,
288 char *buf, size_t buflen)
292 #ifdef HAVE_GETSERVBYPORT_R
299 if (flags & ARES_NI_NUMERICSERV)
303 if (flags & ARES_NI_UDP)
305 else if (flags & ARES_NI_SCTP)
307 else if (flags & ARES_NI_DCCP)
311 #ifdef HAVE_GETSERVBYPORT_R
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)
318 #elif GETSERVBYPORT_R_ARGS == 5
319 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
321 #elif GETSERVBYPORT_R_ARGS == 4
322 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
325 /* Lets just hope the OS uses TLS! */
326 sep = getservbyport(port, proto);
329 /* Lets just hope the OS uses TLS! */
330 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
331 sep = getservbyport(port, (char*)proto);
333 sep = getservbyport(port, proto);
337 if (sep && sep->s_name)
338 /* get service name */
339 strcpy(tmpbuf, sep->s_name);
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 */
347 /* avoid reusing previous one */
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)
359 #ifdef HAVE_IF_INDEXTONAME
362 static const char fmt_u[] = "%u";
363 static const char fmt_lu[] = "%lu";
364 char tmpbuf[IF_NAMESIZE + 2];
366 const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
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))
377 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
381 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
382 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
385 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
388 tmpbuf[IF_NAMESIZE + 1] = '\0';
391 if(bufl + strlen(tmpbuf) < buflen)
392 /* only append the scopeid string if it fits in the target buffer */
393 strcpy(&buf[bufl], tmpbuf);
397 /* Determines if s1 ends with the string in s2 (case-insensitive) */
398 static char *ares_striendstr(const char *s1, const char *s2)
400 const char *c1, *c2, *c1_begin;
402 size_t s1_len = strlen(s1), s2_len = strlen(s2);
404 /* If the substr is longer than the full str, it can't match */
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;
412 while (c2 < s2+s2_len)
424 if (c2 == c1 && c2 == NULL)
425 return (char *)c1_begin;