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.
18 #include <sys/types.h>
21 #if defined(WIN32) && !defined(WATT32)
24 #include <sys/socket.h>
25 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <arpa/nameser.h>
29 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
30 #include <arpa/nameser_compat.h>
47 #include "ares_private.h"
48 #include "ares_ipv6.h"
49 #include "inet_ntop.h"
55 struct nameinfo_query {
56 ares_nameinfo_callback callback;
59 struct sockaddr_in addr4;
60 struct sockaddr_in6 addr6;
66 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
67 #define IPBUFSIZ 40+IF_NAMESIZE
72 static void nameinfo_callback(void *arg, int status, struct hostent *host);
73 static char *lookup_service(unsigned short port, int flags,
74 char *buf, size_t buflen);
75 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
76 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
77 char *buf, size_t buflen);
79 static char *ares_striendstr(const char *s1, const char *s2);
81 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa, socklen_t salen,
82 int flags, ares_nameinfo_callback callback, void *arg)
84 struct sockaddr_in *addr = NULL;
85 struct sockaddr_in6 *addr6 = NULL;
86 struct nameinfo_query *niquery;
88 /* Verify the buffer size */
89 if (salen == sizeof(struct sockaddr_in))
90 addr = (struct sockaddr_in *)sa;
91 else if (salen == sizeof(struct sockaddr_in6))
92 addr6 = (struct sockaddr_in6 *)sa;
95 callback(arg, ARES_ENOTIMP, NULL, NULL);
99 /* If neither, assume they want a host */
100 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
101 flags |= ARES_NI_LOOKUPHOST;
103 /* All they want is a service, no need for DNS */
104 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
106 char buf[33], *service;
107 unsigned int port = 0;
109 if (salen == sizeof(struct sockaddr_in))
110 port = addr->sin_port;
112 port = addr6->sin6_port;
113 service = lookup_service(port, flags, buf, sizeof(buf));
114 callback(arg, ARES_SUCCESS, 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, 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(port, flags, srvbuf, sizeof(srvbuf));
155 callback(arg, ARES_SUCCESS, ipbuf, service);
158 /* This is where a DNS lookup becomes necessary */
161 niquery = malloc(sizeof(struct nameinfo_query));
164 callback(arg, ARES_ENOMEM, NULL, NULL);
167 niquery->callback = callback;
169 niquery->flags = flags;
170 if (sa->sa_family == AF_INET)
172 niquery->family = AF_INET;
173 memcpy(&niquery->addr.addr4, addr, sizeof(addr));
174 ares_gethostbyaddr(channel, &addr->sin_addr, sizeof(struct in_addr), AF_INET,
175 nameinfo_callback, niquery);
179 niquery->family = AF_INET6;
180 memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
181 ares_gethostbyaddr(channel, &addr6->sin6_addr, sizeof(struct in6_addr), AF_INET6,
182 nameinfo_callback, niquery);
188 static void nameinfo_callback(void *arg, int status, struct hostent *host)
190 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
192 char *service = NULL;
195 if (status == ARES_SUCCESS)
197 /* They want a service too */
198 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
200 if (niquery->family == AF_INET)
201 service = lookup_service(niquery->addr.addr4.sin_port,
202 niquery->flags, srvbuf, sizeof(srvbuf));
204 service = lookup_service(niquery->addr.addr6.sin6_port,
205 niquery->flags, srvbuf, sizeof(srvbuf));
207 /* NOFQDN means we have to strip off the domain name portion.
208 We do this by determining our own domain name, then searching the string
209 for this domain name and removing it.
211 if (niquery->flags & ARES_NI_NOFQDN)
215 gethostname(buf, 255);
216 if ((domain = strchr(buf, '.')))
218 char *end = ares_striendstr(host->h_name, domain);
223 niquery->callback(niquery->arg, ARES_SUCCESS, host->h_name, service);
226 /* We couldn't find the host, but it's OK, we can use the IP */
227 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
229 char ipbuf[IPBUFSIZ];
230 if (niquery->family == AF_INET)
231 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
234 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
235 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
236 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf, sizeof(ipbuf));
239 /* They want a service too */
240 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
242 if (niquery->family == AF_INET)
243 service = lookup_service(niquery->addr.addr4.sin_port,
244 niquery->flags, srvbuf, sizeof(srvbuf));
246 service = lookup_service(niquery->addr.addr6.sin6_port,
247 niquery->flags, srvbuf, sizeof(srvbuf));
249 niquery->callback(niquery->arg, ARES_SUCCESS, ipbuf, service);
252 niquery->callback(niquery->arg, status, NULL, NULL);
256 static char *lookup_service(unsigned short port, int flags,
257 char *buf, size_t buflen)
261 #ifdef HAVE_GETSERVBYPORT_R
268 if (flags & ARES_NI_NUMERICSERV)
272 if (flags & ARES_NI_UDP)
274 else if (flags & ARES_NI_SCTP)
276 else if (flags & ARES_NI_DCCP)
280 #ifdef HAVE_GETSERVBYPORT_R
282 memset(tmpbuf, 0, sizeof(tmpbuf));
283 #if GETSERVBYPORT_R_ARGS == 6
284 if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), &sep) != 0)
286 #elif GETSERVBYPORT_R_ARGS == 5
287 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
288 #elif GETSERVBYPORT_R_ARGS == 4
289 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
292 /* Lets just hope the OS uses TLS! */
293 sep = getservbyport(port, proto);
296 /* Lets just hope the OS uses TLS! */
297 sep = getservbyport(port, proto);
300 if (sep && sep->s_name)
301 /* get service name */
302 strcpy(tmpbuf, sep->s_name);
304 /* get port as a string */
305 sprintf(tmpbuf, "%u", ntohs(port));
306 if (strlen(tmpbuf) < buflen)
307 /* return it if buffer big enough */
310 /* avoid reusing previous one */
318 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
319 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
320 char *buf, size_t buflen)
323 char fmt_lu[] = "%lu";
324 char tmpbuf[IF_NAMESIZE + 2];
326 char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?fmt_lu:fmt_u;
330 #ifdef HAVE_IF_INDEXTONAME
331 if ((flags & ARES_NI_NUMERICSCOPE) ||
332 (!IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)
333 && !IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr)))
335 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
339 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
340 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
343 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
346 tmpbuf[IF_NAMESIZE + 1] = '\0';
349 if(bufl + strlen(tmpbuf) < buflen)
350 /* only append the scopeid string if it fits in the target buffer */
351 strcpy(&buf[bufl], tmpbuf);
355 /* Determines if s1 ends with the string in s2 (case-insensitive) */
356 static char *ares_striendstr(const char *s1, const char *s2)
358 const char *c1, *c2, *c1_begin;
359 size_t s1_len = strlen(s1), s2_len = strlen(s2);
361 /* If the substr is longer than the full str, it can't match */
365 /* Jump to the end of s1 minus the length of s2 */
366 c1_begin = s1+s1_len-s2_len;
367 c1 = (const char *)c1_begin;
369 while (c2 < s2+s2_len)
371 if (tolower(*c1) != tolower(*c2))
379 if (c2 == c1 && c2 == NULL)
380 return (char *)c1_begin;