blob: 82e261dae41a0a3c7fef4cebba65386a3ca2d4a2 [file] [log] [blame]
Gilad Arnold968bf192015-07-17 00:23:30 -07001
2/* Copyright 2005 by Dominick Meglio
3 *
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.
15 */
16#include "ares_setup.h"
17
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"
22# endif
23#endif
24
25#ifdef HAVE_SYS_SOCKET_H
26# include <sys/socket.h>
27#endif
28#ifdef HAVE_NETINET_IN_H
29# include <netinet/in.h>
30#endif
31#ifdef HAVE_NETDB_H
32# include <netdb.h>
33#endif
34#ifdef HAVE_ARPA_INET_H
35# include <arpa/inet.h>
36#endif
37#ifdef HAVE_ARPA_NAMESER_H
38# include <arpa/nameser.h>
39#else
40# include "nameser.h"
41#endif
42#ifdef HAVE_ARPA_NAMESER_COMPAT_H
43# include <arpa/nameser_compat.h>
44#endif
45
46#ifdef HAVE_NET_IF_H
47#include <net/if.h>
48#endif
49
50#ifdef HAVE_UNISTD_H
51#include <unistd.h>
52#endif
53
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57
58#include "ares.h"
59#include "ares_ipv6.h"
60#include "inet_ntop.h"
61#include "ares_nowarn.h"
62#include "ares_private.h"
63
64struct nameinfo_query {
65 ares_nameinfo_callback callback;
66 void *arg;
67 union {
68 struct sockaddr_in addr4;
69 struct sockaddr_in6 addr6;
70 } addr;
71 int family;
72 int flags;
73 int timeouts;
74};
75
76#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
77#define IPBUFSIZ \
78 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
79#else
80#define IPBUFSIZ \
81 (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
82#endif
83
84static void nameinfo_callback(void *arg, int status, int timeouts,
85 struct hostent *host);
86static char *lookup_service(unsigned short port, int flags,
87 char *buf, size_t buflen);
88#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
89static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
90 char *buf, size_t buflen);
91#endif
92static char *ares_striendstr(const char *s1, const char *s2);
93
94void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
95 ares_socklen_t salen,
96 int flags, ares_nameinfo_callback callback, void *arg)
97{
98 struct sockaddr_in *addr = NULL;
99 struct sockaddr_in6 *addr6 = NULL;
100 struct nameinfo_query *niquery;
101 unsigned int port = 0;
102
103 /* Validate socket address family and length */
104 if ((sa->sa_family == AF_INET) &&
105 (salen == sizeof(struct sockaddr_in)))
106 {
107 addr = (struct sockaddr_in *)sa;
108 port = addr->sin_port;
109 }
110 else if ((sa->sa_family == AF_INET6) &&
111 (salen == sizeof(struct sockaddr_in6)))
112 {
113 addr6 = (struct sockaddr_in6 *)sa;
114 port = addr6->sin6_port;
115 }
116 else
117 {
118 callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
119 return;
120 }
121
122 /* If neither, assume they want a host */
123 if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
124 flags |= ARES_NI_LOOKUPHOST;
125
126 /* All they want is a service, no need for DNS */
127 if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
128 {
129 char buf[33], *service;
130
131 service = lookup_service((unsigned short)(port & 0xffff),
132 flags, buf, sizeof(buf));
133 callback(arg, ARES_SUCCESS, 0, NULL, service);
134 return;
135 }
136
137 /* They want a host lookup */
138 if ((flags & ARES_NI_LOOKUPHOST))
139 {
140 /* A numeric host can be handled without DNS */
141 if ((flags & ARES_NI_NUMERICHOST))
142 {
143 char ipbuf[IPBUFSIZ];
144 char srvbuf[33];
145 char *service = NULL;
146 ipbuf[0] = 0;
147
148 /* Specifying not to lookup a host, but then saying a host
149 * is required has to be illegal.
150 */
151 if (flags & ARES_NI_NAMEREQD)
152 {
153 callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
154 return;
155 }
156 if (salen == sizeof(struct sockaddr_in6))
157 {
158 ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
159 /* If the system supports scope IDs, use it */
160#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
161 append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
162#endif
163 }
164 else
165 {
166 ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
167 }
168 /* They also want a service */
169 if (flags & ARES_NI_LOOKUPSERVICE)
170 service = lookup_service((unsigned short)(port & 0xffff),
171 flags, srvbuf, sizeof(srvbuf));
172 callback(arg, ARES_SUCCESS, 0, ipbuf, service);
173 return;
174 }
175 /* This is where a DNS lookup becomes necessary */
176 else
177 {
178 niquery = malloc(sizeof(struct nameinfo_query));
179 if (!niquery)
180 {
181 callback(arg, ARES_ENOMEM, 0, NULL, NULL);
182 return;
183 }
184 niquery->callback = callback;
185 niquery->arg = arg;
186 niquery->flags = flags;
187 niquery->timeouts = 0;
188 if (sa->sa_family == AF_INET)
189 {
190 niquery->family = AF_INET;
191 memcpy(&niquery->addr.addr4, addr, sizeof(struct in_addr));
192 ares_gethostbyaddr(channel, &addr->sin_addr,
193 sizeof(struct in_addr), AF_INET,
194 nameinfo_callback, niquery);
195 }
196 else
197 {
198 niquery->family = AF_INET6;
199 memcpy(&niquery->addr.addr6, addr6, sizeof(struct ares_in6_addr));
200 ares_gethostbyaddr(channel, &addr6->sin6_addr,
201 sizeof(struct ares_in6_addr), AF_INET6,
202 nameinfo_callback, niquery);
203 }
204 }
205 }
206}
207
208static void nameinfo_callback(void *arg, int status, int timeouts,
209 struct hostent *host)
210{
211 struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
212 char srvbuf[33];
213 char *service = NULL;
214
215 niquery->timeouts += timeouts;
216 if (status == ARES_SUCCESS)
217 {
218 /* They want a service too */
219 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
220 {
221 if (niquery->family == AF_INET)
222 service = lookup_service(niquery->addr.addr4.sin_port,
223 niquery->flags, srvbuf, sizeof(srvbuf));
224 else
225 service = lookup_service(niquery->addr.addr6.sin6_port,
226 niquery->flags, srvbuf, sizeof(srvbuf));
227 }
228 /* NOFQDN means we have to strip off the domain name portion. We do
229 this by determining our own domain name, then searching the string
230 for this domain name and removing it.
231 */
232#ifdef HAVE_GETHOSTNAME
233 if (niquery->flags & ARES_NI_NOFQDN)
234 {
235 char buf[255];
236 char *domain;
237 gethostname(buf, 255);
238 if ((domain = strchr(buf, '.')) != NULL)
239 {
240 char *end = ares_striendstr(host->h_name, domain);
241 if (end)
242 *end = 0;
243 }
244 }
245#endif
246 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
247 (char *)(host->h_name),
248 service);
249 free(niquery);
250 return;
251 }
252 /* We couldn't find the host, but it's OK, we can use the IP */
253 else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD))
254 {
255 char ipbuf[IPBUFSIZ];
256 if (niquery->family == AF_INET)
257 ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
258 IPBUFSIZ);
259 else
260 {
261 ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
262 IPBUFSIZ);
263#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
264 append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
265 sizeof(ipbuf));
266#endif
267 }
268 /* They want a service too */
269 if (niquery->flags & ARES_NI_LOOKUPSERVICE)
270 {
271 if (niquery->family == AF_INET)
272 service = lookup_service(niquery->addr.addr4.sin_port,
273 niquery->flags, srvbuf, sizeof(srvbuf));
274 else
275 service = lookup_service(niquery->addr.addr6.sin6_port,
276 niquery->flags, srvbuf, sizeof(srvbuf));
277 }
278 niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
279 service);
280 free(niquery);
281 return;
282 }
283 niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
284 free(niquery);
285}
286
287static char *lookup_service(unsigned short port, int flags,
288 char *buf, size_t buflen)
289{
290 const char *proto;
291 struct servent *sep;
292#ifdef HAVE_GETSERVBYPORT_R
293 struct servent se;
294#endif
295 char tmpbuf[4096];
296
297 if (port)
298 {
299 if (flags & ARES_NI_NUMERICSERV)
300 sep = NULL;
301 else
302 {
303 if (flags & ARES_NI_UDP)
304 proto = "udp";
305 else if (flags & ARES_NI_SCTP)
306 proto = "sctp";
307 else if (flags & ARES_NI_DCCP)
308 proto = "dccp";
309 else
310 proto = "tcp";
311#ifdef HAVE_GETSERVBYPORT_R
312 sep = &se;
313 memset(tmpbuf, 0, sizeof(tmpbuf));
314#if GETSERVBYPORT_R_ARGS == 6
315 if (getservbyport_r(port, proto, &se, (void *)tmpbuf,
316 sizeof(tmpbuf), &sep) != 0)
317 sep = NULL;
318#elif GETSERVBYPORT_R_ARGS == 5
319 sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
320 sizeof(tmpbuf));
321#elif GETSERVBYPORT_R_ARGS == 4
322 if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
323 sep = NULL;
324#else
325 /* Lets just hope the OS uses TLS! */
326 sep = getservbyport(port, proto);
327#endif
328#else
329 /* Lets just hope the OS uses TLS! */
330#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
331 sep = getservbyport(port, (char*)proto);
332#else
333 sep = getservbyport(port, proto);
334#endif
335#endif
336 }
337 if (sep && sep->s_name)
338 /* get service name */
339 strcpy(tmpbuf, sep->s_name);
340 else
341 /* get port as a string */
342 sprintf(tmpbuf, "%u", (unsigned int)ntohs(port));
343 if (strlen(tmpbuf) < buflen)
344 /* return it if buffer big enough */
345 strcpy(buf, tmpbuf);
346 else
347 /* avoid reusing previous one */
348 buf[0] = '\0';
349 return buf;
350 }
351 buf[0] = '\0';
352 return NULL;
353}
354
355#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
356static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int flags,
357 char *buf, size_t buflen)
358{
359#ifdef HAVE_IF_INDEXTONAME
360 int is_ll, is_mcll;
361#endif
362 static const char fmt_u[] = "%u";
363 static const char fmt_lu[] = "%lu";
364 char tmpbuf[IF_NAMESIZE + 2];
365 size_t bufl;
366 const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
367 fmt_lu:fmt_u;
368
369 tmpbuf[0] = '%';
370
371#ifdef HAVE_IF_INDEXTONAME
372 is_ll = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
373 is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
374 if ((flags & ARES_NI_NUMERICSCOPE) ||
375 (!is_ll && !is_mcll))
376 {
377 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
378 }
379 else
380 {
381 if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
382 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
383 }
384#else
385 sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
386 (void) flags;
387#endif
388 tmpbuf[IF_NAMESIZE + 1] = '\0';
389 bufl = strlen(buf);
390
391 if(bufl + strlen(tmpbuf) < buflen)
392 /* only append the scopeid string if it fits in the target buffer */
393 strcpy(&buf[bufl], tmpbuf);
394}
395#endif
396
397/* Determines if s1 ends with the string in s2 (case-insensitive) */
398static char *ares_striendstr(const char *s1, const char *s2)
399{
400 const char *c1, *c2, *c1_begin;
401 int lo1, lo2;
402 size_t s1_len = strlen(s1), s2_len = strlen(s2);
403
404 /* If the substr is longer than the full str, it can't match */
405 if (s2_len > s1_len)
406 return NULL;
407
408 /* Jump to the end of s1 minus the length of s2 */
409 c1_begin = s1+s1_len-s2_len;
410 c1 = (const char *)c1_begin;
411 c2 = s2;
412 while (c2 < s2+s2_len)
413 {
414 lo1 = TOLOWER(*c1);
415 lo2 = TOLOWER(*c2);
416 if (lo1 != lo2)
417 return NULL;
418 else
419 {
420 c1++;
421 c2++;
422 }
423 }
424 if (c2 == c1 && c2 == NULL)
425 return (char *)c1_begin;
426 return NULL;
427}