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