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_nowarn.h"
62 #include "ares_private.h"
63 
64 struct 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 
84 static void nameinfo_callback(void *arg, int status, int timeouts,
85                               struct hostent *host);
86 static char *lookup_service(unsigned short port, int flags,
87                             char *buf, size_t buflen);
88 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
89 static void append_scopeid(struct sockaddr_in6 *addr6, unsigned int scopeid,
90                            char *buf, size_t buflen);
91 #endif
92 static char *ares_striendstr(const char *s1, const char *s2);
93 
ares_getnameinfo(ares_channel channel,const struct sockaddr * sa,ares_socklen_t salen,int flags,ares_nameinfo_callback callback,void * arg)94 void 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 
nameinfo_callback(void * arg,int status,int timeouts,struct hostent * host)208 static 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 
lookup_service(unsigned short port,int flags,char * buf,size_t buflen)287 static 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
append_scopeid(struct sockaddr_in6 * addr6,unsigned int flags,char * buf,size_t buflen)356 static 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) */
ares_striendstr(const char * s1,const char * s2)398 static 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 }
428