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_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>
39 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
40 # include <arpa/nameser_compat.h>
48 #include "ares_ipv6.h"
49 #include "ares_nowarn.h"
50 #include "ares_private.h"
52 struct nameinfo_query {
53 ares_nameinfo_callback callback;
56 struct sockaddr_in addr4;
57 struct sockaddr_in6 addr6;
64 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
66 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
69 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
72 static void nameinfo_callback(void *arg, int status, int timeouts,
73 struct hostent *host);
74 static char *lookup_service(unsigned short port, int flags,
75 char *buf, size_t buflen);
76 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
77 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
78 char *buf, size_t buflen);
80 static char *ares_striendstr(const char *s1, const char *s2);
82 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
84 int flags, ares_nameinfo_callback callback, void *arg)
86 struct sockaddr_in *addr = NULL;
87 struct sockaddr_in6 *addr6 = NULL;
88 struct nameinfo_query *niquery;
89 unsigned int port = 0;
91 /* Validate socket address family and length */
92 if ((sa->sa_family == AF_INET) &&
93 (salen == sizeof(struct sockaddr_in)))
95 addr = (struct sockaddr_in *)sa;
96 port = addr->sin_port;
98 else if ((sa->sa_family == AF_INET6) &&
99 (salen == sizeof(struct sockaddr_in6)))
101 addr6 = (struct sockaddr_in6 *)sa;
102 port = addr6->sin6_port;
106 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
110 /* If neither, assume they want a host */
111 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
112 flags |= ARES_NI_LOOKUPHOST;
114 /* All they want is a service, no need for DNS */
115 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
117 char buf[33], *service;
119 service = lookup_service((unsigned short)(port & 0xffff),
120 flags, buf, sizeof(buf));
121 callback(arg, ARES_SUCCESS, 0, NULL, service);
125 /* They want a host lookup */
126 if ((flags & ARES_NI_LOOKUPHOST))
128 /* A numeric host can be handled without DNS */
129 if ((flags & ARES_NI_NUMERICHOST))
131 char ipbuf[IPBUFSIZ];
133 char *service = NULL;
136 /* Specifying not to lookup a host, but then saying a host
137 * is required has to be illegal.
139 if (flags & ARES_NI_NAMEREQD)
141 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
144 if (salen == sizeof(struct sockaddr_in6))
146 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
147 /* If the system supports scope IDs, use it */
148 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
149 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
154 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
156 /* They also want a service */
157 if (flags & ARES_NI_LOOKUPSERVICE)
158 service = lookup_service((unsigned short)(port & 0xffff),
159 flags, srvbuf, sizeof(srvbuf));
160 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
163 /* This is where a DNS lookup becomes necessary */
166 niquery = malloc(sizeof(struct nameinfo_query));
169 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
172 niquery->callback = callback;
174 niquery->flags = flags;
175 niquery->timeouts = 0;
176 if (sa->sa_family == AF_INET)
178 niquery->family = AF_INET;
179 memcpy(&niquery->addr.addr4, addr, sizeof(niquery->addr.addr4));
180 ares_gethostbyaddr(channel, &addr->sin_addr,
181 sizeof(struct in_addr), AF_INET,
182 nameinfo_callback, niquery);
186 niquery->family = AF_INET6;
187 memcpy(&niquery->addr.addr6, addr6, sizeof(niquery->addr.addr6));
188 ares_gethostbyaddr(channel, &addr6->sin6_addr,
189 sizeof(struct ares_in6_addr), AF_INET6,
190 nameinfo_callback, niquery);
196 static void nameinfo_callback(void *arg, int status, int timeouts,
197 struct hostent *host)
199 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
201 char *service = NULL;
203 niquery->timeouts += timeouts;
204 if (status == ARES_SUCCESS)
206 /* They want a service too */
207 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
209 if (niquery->family == AF_INET)
210 service = lookup_service(niquery->addr.addr4.sin_port,
211 niquery->flags, srvbuf, sizeof(srvbuf));
213 service = lookup_service(niquery->addr.addr6.sin6_port,
214 niquery->flags, srvbuf, sizeof(srvbuf));
216 /* NOFQDN means we have to strip off the domain name portion. We do
217 this by determining our own domain name, then searching the string
218 for this domain name and removing it.
220 #ifdef HAVE_GETHOSTNAME
221 if (niquery->flags & ARES_NI_NOFQDN)
225 gethostname(buf, 255);
226 if ((domain = strchr(buf, '.')) != NULL)
228 char *end = ares_striendstr(host->h_name, domain);
234 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
235 (char *)(host->h_name),
240 /* We couldn't find the host, but it's OK, we can use the IP */
241 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
243 char ipbuf[IPBUFSIZ];
244 if (niquery->family == AF_INET)
245 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
249 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
251 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
252 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
256 /* They want a service too */
257 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
259 if (niquery->family == AF_INET)
260 service = lookup_service(niquery->addr.addr4.sin_port,
261 niquery->flags, srvbuf, sizeof(srvbuf));
263 service = lookup_service(niquery->addr.addr6.sin6_port,
264 niquery->flags, srvbuf, sizeof(srvbuf));
266 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
271 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
275 static char *lookup_service(unsigned short port, int flags,
276 char *buf, size_t buflen)
280 #ifdef HAVE_GETSERVBYPORT_R
287 if (flags & ARES_NI_NUMERICSERV)
291 if (flags & ARES_NI_UDP)
293 else if (flags & ARES_NI_SCTP)
295 else if (flags & ARES_NI_DCCP)
299 #ifdef HAVE_GETSERVBYPORT_R
301 memset(tmpbuf, 0, sizeof(tmpbuf));
302 #if GETSERVBYPORT_R_ARGS == 6
303 if (getservbyport_r(port, proto, &se, (void *)tmpbuf,
304 sizeof(tmpbuf), &sep) != 0)
306 #elif GETSERVBYPORT_R_ARGS == 5
307 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
309 #elif GETSERVBYPORT_R_ARGS == 4
310 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
313 /* Lets just hope the OS uses TLS! */
314 sep = getservbyport(port, proto);
317 /* Lets just hope the OS uses TLS! */
318 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
319 sep = getservbyport(port, (char*)proto);
321 sep = getservbyport(port, proto);
325 if (sep && sep->s_name)
326 /* get service name */
327 strcpy(tmpbuf, sep->s_name);
329 /* get port as a string */
330 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
331 if (strlen(tmpbuf) < buflen)
332 /* return it if buffer big enough */
335 /* avoid reusing previous one */
343 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
344 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
345 char *buf, size_t buflen)
347 #ifdef HAVE_IF_INDEXTONAME
350 static const char fmt_u[] = "%u";
351 static const char fmt_lu[] = "%lu";
352 char tmpbuf[IF_NAMESIZE + 2];
354 const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
359 #ifdef HAVE_IF_INDEXTONAME
360 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
361 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
362 if ((flags & ARES_NI_NUMERICSCOPE) ||
363 (!is_ll && !is_mcll))
365 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
369 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
370 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
373 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
376 tmpbuf[IF_NAMESIZE + 1] = '\0';
379 if(bufl + strlen(tmpbuf) < buflen)
380 /* only append the scopeid string if it fits in the target buffer */
381 strcpy(&buf[bufl], tmpbuf);
385 /* Determines if s1 ends with the string in s2 (case-insensitive) */
386 static char *ares_striendstr(const char *s1, const char *s2)
388 const char *c1, *c2, *c1_begin;
390 size_t s1_len = strlen(s1), s2_len = strlen(s2);
392 /* If the substr is longer than the full str, it can't match */
396 /* Jump to the end of s1 minus the length of s2 */
397 c1_begin = s1+s1_len-s2_len;
398 c1 = (const char *)c1_begin;
400 while (c2 < s2+s2_len)
412 if (c2 == c1 && c2 == NULL)
413 return (char *)c1_begin;