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 #include <sys/socket.h>
23 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <arpa/nameser.h>
27 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
28 #include <arpa/nameser_compat.h>
45 #include "ares_private.h"
46 #include "ares_ipv6.h"
47 #include "inet_ntop.h"
53 struct nameinfo_query {
54 ares_nameinfo_callback callback;
57 struct sockaddr_in addr4;
58 struct sockaddr_in6 addr6;
65 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
66 #define IPBUFSIZ 40+IF_NAMESIZE
71 static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host);
72 static char *lookup_service(unsigned short port, int flags,
73 char *buf, size_t buflen);
74 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
75 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
76 char *buf, size_t buflen);
78 static char *ares_striendstr(const char *s1, const char *s2);
80 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, socklen_t salen,
81 int flags, ares_nameinfo_callback callback, void *arg)
83 struct sockaddr_in *addr = NULL;
84 struct sockaddr_in6 *addr6 = NULL;
85 struct nameinfo_query *niquery;
87 /* Verify the buffer size */
88 if (salen == sizeof(struct sockaddr_in))
89 addr = (struct sockaddr_in *)sa;
90 else if (salen == sizeof(struct sockaddr_in6))
91 addr6 = (struct sockaddr_in6 *)sa;
94 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
98 /* If neither, assume they want a host */
99 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
100 flags |= ARES_NI_LOOKUPHOST;
102 /* All they want is a service, no need for DNS */
103 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
105 char buf[33], *service;
106 unsigned int port = 0;
108 if (salen == sizeof(struct sockaddr_in))
109 port = addr->sin_port;
111 port = addr6->sin6_port;
112 service = lookup_service((unsigned short)(port & 0xffff),
113 flags, buf, sizeof(buf));
114 callback(arg, ARES_SUCCESS, 0, NULL, service);
118 /* They want a host lookup */
119 if ((flags & ARES_NI_LOOKUPHOST))
121 /* A numeric host can be handled without DNS */
122 if ((flags & ARES_NI_NUMERICHOST))
124 unsigned int port = 0;
125 char ipbuf[IPBUFSIZ];
127 char *service = NULL;
130 /* Specifying not to lookup a host, but then saying a host
131 * is required has to be illegal.
133 if (flags & ARES_NI_NAMEREQD)
135 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
138 if (salen == sizeof(struct sockaddr_in6))
140 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
141 port = addr6->sin6_port;
142 /* If the system supports scope IDs, use it */
143 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
144 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
149 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
150 port = addr->sin_port;
152 /* They also want a service */
153 if (flags & ARES_NI_LOOKUPSERVICE)
154 service = lookup_service((unsigned short)(port & 0xffff),
155 flags, srvbuf, sizeof(srvbuf));
156 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
159 /* This is where a DNS lookup becomes necessary */
162 niquery = malloc(sizeof(struct nameinfo_query));
165 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
168 niquery->callback = callback;
170 niquery->flags = flags;
171 niquery->timeouts = 0;
172 if (sa->sa_family == AF_INET)
174 niquery->family = AF_INET;
175 memcpy(&niquery->addr.addr4, addr, sizeof(addr));
176 ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
177 nameinfo_callback, niquery);
181 niquery->family = AF_INET6;
182 memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
183 ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
184 nameinfo_callback, niquery);
190 static void nameinfo_callback(void *arg, int status, int timeouts, struct hostent *host)
192 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
194 char *service = NULL;
196 niquery->timeouts += timeouts;
197 if (status == ARES_SUCCESS)
199 /* They want a service too */
200 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
202 if (niquery->family == AF_INET)
203 service = lookup_service(niquery->addr.addr4.sin_port,
204 niquery->flags, srvbuf, sizeof(srvbuf));
206 service = lookup_service(niquery->addr.addr6.sin6_port,
207 niquery->flags, srvbuf, sizeof(srvbuf));
209 /* NOFQDN means we have to strip off the domain name portion.
210 We do this by determining our own domain name, then searching the string
211 for this domain name and removing it.
213 if (niquery->flags & ARES_NI_NOFQDN)
217 gethostname(buf, 255);
218 if ((domain = strchr(buf, '.')))
220 char *end = ares_striendstr(host->h_name, domain);
225 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, (char *)(host->h_name),
229 /* We couldn't find the host, but it's OK, we can use the IP */
230 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
232 char ipbuf[IPBUFSIZ];
233 if (niquery->family == AF_INET)
234 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
237 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
238 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
239 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, sizeof(ipbuf));
242 /* They want a service too */
243 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
245 if (niquery->family == AF_INET)
246 service = lookup_service(niquery->addr.addr4.sin_port,
247 niquery->flags, srvbuf, sizeof(srvbuf));
249 service = lookup_service(niquery->addr.addr6.sin6_port,
250 niquery->flags, srvbuf, sizeof(srvbuf));
252 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf, service);
255 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
259 static char *lookup_service(unsigned short port, int flags,
260 char *buf, size_t buflen)
264 #ifdef HAVE_GETSERVBYPORT_R
271 if (flags & ARES_NI_NUMERICSERV)
275 if (flags & ARES_NI_UDP)
277 else if (flags & ARES_NI_SCTP)
279 else if (flags & ARES_NI_DCCP)
283 #ifdef HAVE_GETSERVBYPORT_R
285 memset(tmpbuf, 0, sizeof(tmpbuf));
286 #if GETSERVBYPORT_R_ARGS == 6
287 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), &sep) != 0)
289 #elif GETSERVBYPORT_R_ARGS == 5
290 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
291 #elif GETSERVBYPORT_R_ARGS == 4
292 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
295 /* Lets just hope the OS uses TLS! */
296 sep = getservbyport(port, proto);
299 /* Lets just hope the OS uses TLS! */
300 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
301 sep = getservbyport(port, (char*)proto);
303 sep = getservbyport(port, proto);
307 if (sep && sep->s_name)
308 /* get service name */
309 strcpy(tmpbuf, sep->s_name);
311 /* get port as a string */
312 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
313 if (strlen(tmpbuf) < buflen)
314 /* return it if buffer big enough */
317 /* avoid reusing previous one */
325 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
326 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
327 char *buf, size_t buflen)
329 #ifdef HAVE_IF_INDEXTONAME
333 char fmt_lu[] = "%lu";
334 char tmpbuf[IF_NAMESIZE + 2];
336 char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?fmt_lu:fmt_u;
340 #ifdef HAVE_IF_INDEXTONAME
341 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
342 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
343 if ((flags & ARES_NI_NUMERICSCOPE) ||
344 (!is_ll && !is_mcll))
346 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
350 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
351 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
354 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
357 tmpbuf[IF_NAMESIZE + 1] = '\0';
360 if(bufl + strlen(tmpbuf) < buflen)
361 /* only append the scopeid string if it fits in the target buffer */
362 strcpy(&buf[bufl], tmpbuf);
366 /* Determines if s1 ends with the string in s2 (case-insensitive) */
367 static char *ares_striendstr(const char *s1, const char *s2)
369 const char *c1, *c2, *c1_begin;
371 size_t s1_len = strlen(s1), s2_len = strlen(s2);
373 /* If the substr is longer than the full str, it can't match */
377 /* Jump to the end of s1 minus the length of s2 */
378 c1_begin = s1+s1_len-s2_len;
379 c1 = (const char *)c1_begin;
381 while (c2 < s2+s2_len)
393 if (c2 == c1 && c2 == NULL)
394 return (char *)c1_begin;