ares_getnameinfo: Partially revert commit 85520d66e0ac7ac73411bc25e98769a88b2f
[platform/upstream/c-ares.git] / ares_getnameinfo.c
1
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_private.h"
62
63 struct nameinfo_query {
64   ares_nameinfo_callback callback;
65   void *arg;
66   union {
67     struct sockaddr_in addr4;
68     struct sockaddr_in6 addr6;
69   } addr;
70   int family;
71   int flags;
72   int timeouts;
73 };
74
75 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
76 #define IPBUFSIZ \
77         (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
78 #else
79 #define IPBUFSIZ \
80         (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
81 #endif
82
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);
90 #endif
91 static char *ares_striendstr(const char *s1, const char *s2);
92
93 void ares_getnameinfo(ares_channel channel, const struct sockaddr *sa,
94                       ares_socklen_t salen,
95                       int flags, ares_nameinfo_callback callback, void *arg)
96 {
97   struct sockaddr_in *addr = NULL;
98   struct sockaddr_in6 *addr6 = NULL;
99   struct nameinfo_query *niquery;
100   unsigned int port = 0;
101
102   /* Validate socket address family and length */
103   if ((sa->sa_family == AF_INET) &&
104       (salen == sizeof(struct sockaddr_in)))
105     {
106       addr = (struct sockaddr_in *)sa;
107       port = addr->sin_port;
108     }
109   else if ((sa->sa_family == AF_INET6) &&
110            (salen == sizeof(struct sockaddr_in6)))
111     {
112       addr6 = (struct sockaddr_in6 *)sa;
113       port = addr6->sin6_port;
114     }
115   else
116     {
117       callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
118       return;
119     }
120
121   /* If neither, assume they want a host */
122   if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
123     flags |= ARES_NI_LOOKUPHOST;
124
125   /* All they want is a service, no need for DNS */
126   if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST))
127     {
128       char buf[33], *service;
129
130       service = lookup_service((unsigned short)(port & 0xffff),
131                                flags, buf, sizeof(buf));
132       callback(arg, ARES_SUCCESS, 0, NULL, service);
133       return;
134     }
135
136   /* They want a host lookup */
137   if ((flags & ARES_NI_LOOKUPHOST))
138     {
139      /* A numeric host can be handled without DNS */
140      if ((flags & ARES_NI_NUMERICHOST))
141       {
142         char ipbuf[IPBUFSIZ];
143         char srvbuf[33];
144         char *service = NULL;
145         ipbuf[0] = 0;
146
147         /* Specifying not to lookup a host, but then saying a host
148          * is required has to be illegal.
149          */
150         if (flags & ARES_NI_NAMEREQD)
151           {
152             callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
153             return;
154           }
155         if (salen == sizeof(struct sockaddr_in6))
156           {
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));
161 #endif
162           }
163         else
164           {
165             ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
166           }
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);
172         return;
173       }
174     /* This is where a DNS lookup becomes necessary */
175     else
176       {
177         niquery = malloc(sizeof(struct nameinfo_query));
178         if (!niquery)
179           {
180             callback(arg, ARES_ENOMEM, 0, NULL, NULL);
181             return;
182           }
183         niquery->callback = callback;
184         niquery->arg = arg;
185         niquery->flags = flags;
186         niquery->timeouts = 0;
187         if (sa->sa_family == AF_INET)
188           {
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);
194           }
195         else
196           {
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);
202           }
203       }
204     }
205 }
206
207 static void nameinfo_callback(void *arg, int status, int timeouts,
208                               struct hostent *host)
209 {
210   struct nameinfo_query *niquery = (struct nameinfo_query *) arg;
211   char srvbuf[33];
212   char *service = NULL;
213
214   niquery->timeouts += timeouts;
215   if (status == ARES_SUCCESS)
216     {
217       /* They want a service too */
218       if (niquery->flags & ARES_NI_LOOKUPSERVICE)
219         {
220           if (niquery->family == AF_INET)
221             service = lookup_service(niquery->addr.addr4.sin_port,
222                                      niquery->flags, srvbuf, sizeof(srvbuf));
223           else
224             service = lookup_service(niquery->addr.addr6.sin6_port,
225                                      niquery->flags, srvbuf, sizeof(srvbuf));
226         }
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.
230        */
231 #ifdef HAVE_GETHOSTNAME
232       if (niquery->flags & ARES_NI_NOFQDN)
233         {
234            char buf[255];
235            char *domain;
236            gethostname(buf, 255);
237            if ((domain = strchr(buf, '.')) != NULL)
238              {
239                char *end = ares_striendstr(host->h_name, domain);
240                if (end)
241                  *end = 0;
242              }
243         }
244 #endif
245       niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts,
246                         (char *)(host->h_name),
247                         service);
248       free(niquery);
249       return;
250     }
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))
253     {
254       char ipbuf[IPBUFSIZ];
255       if (niquery->family == AF_INET)
256         ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf,
257                        IPBUFSIZ);
258       else
259         {
260           ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf,
261                          IPBUFSIZ);
262 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
263           append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
264                          sizeof(ipbuf));
265 #endif
266         }
267       /* They want a service too */
268       if (niquery->flags & ARES_NI_LOOKUPSERVICE)
269         {
270           if (niquery->family == AF_INET)
271             service = lookup_service(niquery->addr.addr4.sin_port,
272                                      niquery->flags, srvbuf, sizeof(srvbuf));
273           else
274             service = lookup_service(niquery->addr.addr6.sin6_port,
275                                      niquery->flags, srvbuf, sizeof(srvbuf));
276         }
277       niquery->callback(niquery->arg, ARES_SUCCESS, niquery->timeouts, ipbuf,
278                         service);
279       free(niquery);
280       return;
281     }
282   niquery->callback(niquery->arg, status, niquery->timeouts, NULL, NULL);
283   free(niquery);
284 }
285
286 static char *lookup_service(unsigned short port, int flags,
287                             char *buf, size_t buflen)
288 {
289   const char *proto;
290   struct servent *sep;
291 #ifdef HAVE_GETSERVBYPORT_R
292   struct servent se;
293 #endif
294   char tmpbuf[4096];
295
296   if (port)
297     {
298       if (flags & ARES_NI_NUMERICSERV)
299         sep = NULL;
300       else
301         {
302           if (flags & ARES_NI_UDP)
303             proto = "udp";
304           else if (flags & ARES_NI_SCTP)
305             proto = "sctp";
306           else if (flags & ARES_NI_DCCP)
307             proto = "dccp";
308           else
309             proto = "tcp";
310 #ifdef HAVE_GETSERVBYPORT_R
311           sep = &se;
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)
316             sep = NULL;
317 #elif GETSERVBYPORT_R_ARGS == 5
318           sep = getservbyport_r(port, proto, &se, (void *)tmpbuf,
319                                 sizeof(tmpbuf));
320 #elif GETSERVBYPORT_R_ARGS == 4
321           if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0)
322             sep = NULL;
323 #else
324           /* Lets just hope the OS uses TLS! */
325           sep = getservbyport(port, proto);
326 #endif
327 #else
328           /* Lets just hope the OS uses TLS! */
329 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
330           sep = getservbyport(port, (char*)proto);
331 #else
332           sep = getservbyport(port, proto);
333 #endif
334 #endif
335         }
336       if (sep && sep->s_name)
337         /* get service name */
338         strcpy(tmpbuf, sep->s_name);
339       else
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 */
344         strcpy(buf, tmpbuf);
345       else
346         /* avoid reusing previous one */
347         buf[0] = '\0';
348       return buf;
349     }
350   buf[0] = '\0';
351   return NULL;
352 }
353
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)
357 {
358 #ifdef HAVE_IF_INDEXTONAME
359   int is_ll, is_mcll;
360 #endif
361   static const char fmt_u[] = "%u";
362   static const char fmt_lu[] = "%lu";
363   char tmpbuf[IF_NAMESIZE + 2];
364   size_t bufl;
365   const char *fmt = (sizeof(addr6->sin6_scope_id) > sizeof(unsigned int))?
366     fmt_lu:fmt_u;
367
368   tmpbuf[0] = '%';
369
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))
375     {
376        sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
377     }
378   else
379     {
380       if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL)
381         sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
382     }
383 #else
384   sprintf(&tmpbuf[1], fmt, addr6->sin6_scope_id);
385   (void) flags;
386 #endif
387   tmpbuf[IF_NAMESIZE + 1] = '\0';
388   bufl = strlen(buf);
389
390   if(bufl + strlen(tmpbuf) < buflen)
391     /* only append the scopeid string if it fits in the target buffer */
392     strcpy(&buf[bufl], tmpbuf);
393 }
394 #endif
395
396 /* Determines if s1 ends with the string in s2 (case-insensitive) */
397 static char *ares_striendstr(const char *s1, const char *s2)
398 {
399   const char *c1, *c2, *c1_begin;
400   int lo1, lo2;
401   size_t s1_len = strlen(s1), s2_len = strlen(s2);
402
403   /* If the substr is longer than the full str, it can't match */
404   if (s2_len > s1_len)
405     return NULL;
406
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;
410   c2 = s2;
411   while (c2 < s2+s2_len)
412     {
413       lo1 = TOLOWER(*c1);
414       lo2 = TOLOWER(*c2);
415       if (lo1 != lo2)
416         return NULL;
417       else
418         {
419           c1++;
420           c2++;
421         }
422     }
423   if (c2 == c1 && c2 == NULL)
424     return (char *)c1_begin;
425   return NULL;
426 }