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_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
77 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
80 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
83 static void nameinfo_callback(void *arg, int status, int timeouts,
84 struct hostent *host);
85 static char *lookup_service(unsigned short port, int flags,
86 char *buf, size_t buflen);
87 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
88 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
89 char *buf, size_t buflen);
91 static char *ares_striendstr(const char *s1, const char *s2);
93 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
95 int flags, ares_nameinfo_callback callback, void *arg)
97 struct sockaddr_in *addr = NULL;
98 struct sockaddr_in6 *addr6 = NULL;
99 struct nameinfo_query *niquery;
100 unsigned int port = 0;
102 /* Validate socket address family and length */
103 if ((sa->sa_family == AF_INET) &&
104 (salen == sizeof(struct sockaddr_in)))
106 addr = (struct sockaddr_in *)sa;
107 port = addr->sin_port;
109 else if ((sa->sa_family == AF_INET6) &&
110 (salen == sizeof(struct sockaddr_in6)))
112 addr6 = (struct sockaddr_in6 *)sa;
113 port = addr6->sin6_port;
117 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
121 /* If neither, assume they want a host */
122 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
123 flags |= ARES_NI_LOOKUPHOST;
125 /* All they want is a service, no need for DNS */
126 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
128 char buf[33], *service;
130 service = lookup_service((unsigned short)(port & 0xffff),
131 flags, buf, sizeof(buf));
132 callback(arg, ARES_SUCCESS, 0, NULL, service);
136 /* They want a host lookup */
137 if ((flags & ARES_NI_LOOKUPHOST))
139 /* A numeric host can be handled without DNS */
140 if ((flags & ARES_NI_NUMERICHOST))
142 char ipbuf[IPBUFSIZ];
144 char *service = NULL;
147 /* Specifying not to lookup a host, but then saying a host
148 * is required has to be illegal.
150 if (flags & ARES_NI_NAMEREQD)
152 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
155 if (salen == sizeof(struct sockaddr_in6))
157 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
158 /* If the system supports scope IDs, use it */
159 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
160 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
165 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
167 /* They also want a service */
168 if (flags & ARES_NI_LOOKUPSERVICE)
169 service = lookup_service((unsigned short)(port & 0xffff),
170 flags, srvbuf, sizeof(srvbuf));
171 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
174 /* This is where a DNS lookup becomes necessary */
177 niquery = malloc(sizeof(struct nameinfo_query));
180 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
183 niquery->callback = callback;
185 niquery->flags = flags;
186 niquery->timeouts = 0;
187 if (sa->sa_family == AF_INET)
189 niquery->family = AF_INET;
190 memcpy(&niquery->addr.addr4, addr, sizeof(addr));
191 ares_gethostbyaddr(channel, &addr->sin_addr,
192 sizeof(struct in_addr), AF_INET,
193 nameinfo_callback, niquery);
197 niquery->family = AF_INET6;
198 memcpy(&niquery->addr.addr6, addr6, sizeof(addr6));
199 ares_gethostbyaddr(channel, &addr6->sin6_addr,
200 sizeof(struct ares_in6_addr), AF_INET6,
201 nameinfo_callback, niquery);
207 static void nameinfo_callback(void *arg, int status, int timeouts,
208 struct hostent *host)
210 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
212 char *service = NULL;
214 niquery->timeouts += timeouts;
215 if (status == ARES_SUCCESS)
217 /* They want a service too */
218 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
220 if (niquery->family == AF_INET)
221 service = lookup_service(niquery->addr.addr4.sin_port,
222 niquery->flags, srvbuf, sizeof(srvbuf));
224 service = lookup_service(niquery->addr.addr6.sin6_port,
225 niquery->flags, srvbuf, sizeof(srvbuf));
227 /* NOFQDN means we have to strip off the domain name portion. We do
228 this by determining our own domain name, then searching the string
229 for this domain name and removing it.
231 #ifdef HAVE_GETHOSTNAME
232 if (niquery->flags & ARES_NI_NOFQDN)
236 gethostname(buf, 255);
237 if ((domain = strchr(buf, '.')) != NULL)
239 char *end = ares_striendstr(host->h_name, domain);
245 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
246 (char *)(host->h_name),
251 /* We couldn't find the host, but it's OK, we can use the IP */
252 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
254 char ipbuf[IPBUFSIZ];
255 if (niquery->family == AF_INET)
256 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
260 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
262 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
263 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
267 /* They want a service too */
268 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
270 if (niquery->family == AF_INET)
271 service = lookup_service(niquery->addr.addr4.sin_port,
272 niquery->flags, srvbuf, sizeof(srvbuf));
274 service = lookup_service(niquery->addr.addr6.sin6_port,
275 niquery->flags, srvbuf, sizeof(srvbuf));
277 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
282 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
286 static char *lookup_service(unsigned short port, int flags,
287 char *buf, size_t buflen)
291 #ifdef HAVE_GETSERVBYPORT_R
298 if (flags & ARES_NI_NUMERICSERV)
302 if (flags & ARES_NI_UDP)
304 else if (flags & ARES_NI_SCTP)
306 else if (flags & ARES_NI_DCCP)
310 #ifdef HAVE_GETSERVBYPORT_R
312 memset(tmpbuf, 0, sizeof(tmpbuf));
313 #if GETSERVBYPORT_R_ARGS == 6
314 if (getservbyport_r(port, proto, &se, (void *)tmpbuf,
315 sizeof(tmpbuf), &sep) != 0)
317 #elif GETSERVBYPORT_R_ARGS == 5
318 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
320 #elif GETSERVBYPORT_R_ARGS == 4
321 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
324 /* Lets just hope the OS uses TLS! */
325 sep = getservbyport(port, proto);
328 /* Lets just hope the OS uses TLS! */
329 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
330 sep = getservbyport(port, (char*)proto);
332 sep = getservbyport(port, proto);
336 if (sep && sep->s_name)
337 /* get service name */
338 strcpy(tmpbuf, sep->s_name);
340 /* get port as a string */
341 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
342 if (strlen(tmpbuf) < buflen)
343 /* return it if buffer big enough */
346 /* avoid reusing previous one */
354 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
355 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
356 char *buf, size_t buflen)
358 #ifdef HAVE_IF_INDEXTONAME
361 static const char fmt_u[] = "%u";
362 static const char fmt_lu[] = "%lu";
363 char tmpbuf[IF_NAMESIZE + 2];
365 const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
370 #ifdef HAVE_IF_INDEXTONAME
371 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
372 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
373 if ((flags & ARES_NI_NUMERICSCOPE) ||
374 (!is_ll && !is_mcll))
376 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
380 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
381 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
384 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
387 tmpbuf[IF_NAMESIZE + 1] = '\0';
390 if(bufl + strlen(tmpbuf) < buflen)
391 /* only append the scopeid string if it fits in the target buffer */
392 strcpy(&buf[bufl], tmpbuf);
396 /* Determines if s1 ends with the string in s2 (case-insensitive) */
397 static char *ares_striendstr(const char *s1, const char *s2)
399 const char *c1, *c2, *c1_begin;
401 size_t s1_len = strlen(s1), s2_len = strlen(s2);
403 /* If the substr is longer than the full str, it can't match */
407 /* Jump to the end of s1 minus the length of s2 */
408 c1_begin = s1+s1_len-s2_len;
409 c1 = (const char *)c1_begin;
411 while (c2 < s2+s2_len)
423 if (c2 == c1 && c2 == NULL)
424 return (char *)c1_begin;