3 /* Copyright 2005 by Dominick Meglio
5 * Permission to use, copy, modify, and distribute this
6 * software and its documentation for any purpose and without
7 * fee is hereby granted, provided that the above copyright
8 * notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting
10 * documentation, and that the name of M.I.T. not be used in
11 * advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission.
13 * M.I.T. makes no representations about the suitability of
14 * this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
19 #if defined(WIN32) && !defined(WATT32)
22 #ifdef HAVE_SYS_SOCKET_H
23 #include <sys/socket.h>
25 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h>
31 #ifdef HAVE_ARPA_INET_H
32 #include <arpa/inet.h>
34 #ifdef HAVE_ARPA_NAMESER_H
35 #include <arpa/nameser.h>
37 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
38 #include <arpa/nameser_compat.h>
55 #include "ares_ipv6.h"
56 #include "inet_ntop.h"
57 #include "ares_private.h"
63 struct nameinfo_query {
64 ares_nameinfo_callback callback;
67 struct sockaddr_in addr4;
68 struct sockaddr_in6 addr6;
75 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
76 #define IPBUFSIZ 40+IF_NAMESIZE
81 static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host);
82 static char *lookup_service(unsigned short port, int flags,
83 char *buf, size_t buflen);
84 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
85 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
86 char *buf, size_t buflen);
88 static char *ares_striendstr(const char *s1, const char *s2);
90 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, socklen_t salen,
91 int flags, ares_nameinfo_callback callback, void *arg)
93 struct sockaddr_in *addr = NULL;
94 struct sockaddr_in6 *addr6 = NULL;
95 struct nameinfo_query *niquery;
97 /* Verify the buffer size */
98 if (salen == sizeof(struct sockaddr_in))
99 addr = (struct sockaddr_in *)sa;
100 else if (salen == sizeof(struct sockaddr_in6))
101 addr6 = (struct sockaddr_in6 *)sa;
104 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
108 /* If neither, assume they want a host */
109 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
110 flags |= ARES_NI_LOOKUPHOST;
112 /* All they want is a service, no need for DNS */
113 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
115 char buf[33], *service;
116 unsigned int port = 0;
118 if (salen == sizeof(struct sockaddr_in))
119 port = addr->sin_port;
121 port = addr6->sin6_port;
122 service = lookup_service((unsigned short)(port & 0xffff),
123 flags, buf, sizeof(buf));
124 callback(arg, ARES_SUCCESS, 0, NULL, service);
128 /* They want a host lookup */
129 if ((flags & ARES_NI_LOOKUPHOST))
131 /* A numeric host can be handled without DNS */
132 if ((flags & ARES_NI_NUMERICHOST))
134 unsigned int port = 0;
135 char ipbuf[IPBUFSIZ];
137 char *service = NULL;
140 /* Specifying not to lookup a host, but then saying a host
141 * is required has to be illegal.
143 if (flags & ARES_NI_NAMEREQD)
145 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
148 if (salen == sizeof(struct sockaddr_in6))
150 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
151 port = addr6->sin6_port;
152 /* If the system supports scope IDs, use it */
153 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
154 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
159 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
160 port = addr->sin_port;
162 /* They also want a service */
163 if (flags & ARES_NI_LOOKUPSERVICE)
164 service = lookup_service((unsigned short)(port & 0xffff),
165 flags, srvbuf, sizeof(srvbuf));
166 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
169 /* This is where a DNS lookup becomes necessary */
172 niquery = malloc(sizeof(struct nameinfo_query));
175 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
178 niquery->callback = callback;
180 niquery->flags = flags;
181 niquery->timeouts = 0;
182 if (sa->sa_family == AF_INET)
184 niquery->family = AF_INET;
185 memcpy(&niquery->addr.addr4, addr, sizeof(addr));
186 ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
187 nameinfo_callback, niquery);
191 niquery->family = AF_INET6;
192 memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
193 ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
194 nameinfo_callback, niquery);
200 static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host)
202 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
204 char *service = NULL;
206 niquery->timeouts += timeouts;
207 if (status == ARES_SUCCESS)
209 /* They want a service too */
210 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
212 if (niquery->family == AF_INET)
213 service = lookup_service(niquery->addr.addr4.sin_port,
214 niquery->flags, srvbuf, sizeof(srvbuf));
216 service = lookup_service(niquery->addr.addr6.sin6_port,
217 niquery->flags, srvbuf, sizeof(srvbuf));
219 /* NOFQDN means we have to strip off the domain name portion.
220 We do this by determining our own domain name, then searching the string
221 for this domain name and removing it.
223 if (niquery->flags & ARES_NI_NOFQDN)
227 gethostname(buf, 255);
228 if ((domain = strchr(buf, '.')))
230 char *end = ares_striendstr(host->h_name, domain);
235 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, (char *)(host->h_name),
239 /* We couldn't find the host, but it's OK, we can use the IP */
240 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
242 char ipbuf[IPBUFSIZ];
243 if (niquery->family == AF_INET)
244 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
247 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
248 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
249 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, sizeof(ipbuf));
252 /* They want a service too */
253 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
255 if (niquery->family == AF_INET)
256 service = lookup_service(niquery->addr.addr4.sin_port,
257 niquery->flags, srvbuf, sizeof(srvbuf));
259 service = lookup_service(niquery->addr.addr6.sin6_port,
260 niquery->flags, srvbuf, sizeof(srvbuf));
262 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf, service);
265 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
269 static char *lookup_service(unsigned short port, int flags,
270 char *buf, size_t buflen)
274 #ifdef HAVE_GETSERVBYPORT_R
281 if (flags & ARES_NI_NUMERICSERV)
285 if (flags & ARES_NI_UDP)
287 else if (flags & ARES_NI_SCTP)
289 else if (flags & ARES_NI_DCCP)
293 #ifdef HAVE_GETSERVBYPORT_R
295 memset(tmpbuf, 0, sizeof(tmpbuf));
296 #if GETSERVBYPORT_R_ARGS == 6
297 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), &sep) != 0)
299 #elif GETSERVBYPORT_R_ARGS == 5
300 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
301 #elif GETSERVBYPORT_R_ARGS == 4
302 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
305 /* Lets just hope the OS uses TLS! */
306 sep = getservbyport(port, proto);
309 /* Lets just hope the OS uses TLS! */
310 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
311 sep = getservbyport(port, (char*)proto);
313 sep = getservbyport(port, proto);
317 if (sep && sep->s_name)
318 /* get service name */
319 strcpy(tmpbuf, sep->s_name);
321 /* get port as a string */
322 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
323 if (strlen(tmpbuf) < buflen)
324 /* return it if buffer big enough */
327 /* avoid reusing previous one */
335 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
336 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
337 char *buf, size_t buflen)
339 #ifdef HAVE_IF_INDEXTONAME
343 char fmt_lu[] = "%lu";
344 char tmpbuf[IF_NAMESIZE + 2];
346 char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?fmt_lu:fmt_u;
350 #ifdef HAVE_IF_INDEXTONAME
351 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
352 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
353 if ((flags & ARES_NI_NUMERICSCOPE) ||
354 (!is_ll && !is_mcll))
356 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
360 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
361 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
364 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
367 tmpbuf[IF_NAMESIZE + 1] = '\0';
370 if(bufl + strlen(tmpbuf) < buflen)
371 /* only append the scopeid string if it fits in the target buffer */
372 strcpy(&buf[bufl], tmpbuf);
376 /* Determines if s1 ends with the string in s2 (case-insensitive) */
377 static char *ares_striendstr(const char *s1, const char *s2)
379 const char *c1, *c2, *c1_begin;
381 size_t s1_len = strlen(s1), s2_len = strlen(s2);
383 /* If the substr is longer than the full str, it can't match */
387 /* Jump to the end of s1 minus the length of s2 */
388 c1_begin = s1+s1_len-s2_len;
389 c1 = (const char *)c1_begin;
391 while (c2 < s2+s2_len)
403 if (c2 == c1 && c2 == NULL)
404 return (char *)c1_begin;