• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* MIT License
2  *
3  * Copyright (c) 2005, 2013 Dominick Meglio
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to deal
7  * in the Software without restriction, including without limitation the rights
8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  * SPDX-License-Identifier: MIT
25  */
26 #include "ares_private.h"
27 
28 #ifdef HAVE_GETSERVBYPORT_R
29 #  if !defined(GETSERVBYPORT_R_ARGS) || (GETSERVBYPORT_R_ARGS < 4) || \
30     (GETSERVBYPORT_R_ARGS > 6)
31 #    error "you MUST specify a valid number of arguments for getservbyport_r"
32 #  endif
33 #endif
34 
35 #ifdef HAVE_NETINET_IN_H
36 #  include <netinet/in.h>
37 #endif
38 #ifdef HAVE_NETDB_H
39 #  include <netdb.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 #  include <arpa/inet.h>
43 #endif
44 
45 #include "ares_nameser.h"
46 
47 #ifdef HAVE_NET_IF_H
48 #  include <net/if.h>
49 #endif
50 #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H)
51 #  include <iphlpapi.h>
52 #endif
53 
54 #include "ares_ipv6.h"
55 
56 struct nameinfo_query {
57   ares_nameinfo_callback callback;
58   void                  *arg;
59 
60   union {
61     struct sockaddr_in  addr4;
62     struct sockaddr_in6 addr6;
63   } addr;
64 
65   int          family;
66   unsigned int flags;
67   size_t       timeouts;
68 };
69 
70 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
71 #  define IPBUFSIZ \
72     (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") + IF_NAMESIZE)
73 #else
74 #  define IPBUFSIZ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))
75 #endif
76 
77 static void  nameinfo_callback(void *arg, int status, int timeouts,
78                                struct hostent *host);
79 static char *lookup_service(unsigned short port, unsigned int flags, char *buf,
80                             size_t buflen);
81 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
82 static void append_scopeid(const struct sockaddr_in6 *addr6, unsigned int flags,
83                            char *buf, size_t buflen);
84 #endif
85 static char *ares_striendstr(const char *s1, const char *s2);
86 
ares_getnameinfo_int(ares_channel_t * channel,const struct sockaddr * sa,ares_socklen_t salen,int flags_int,ares_nameinfo_callback callback,void * arg)87 static void  ares_getnameinfo_int(ares_channel_t        *channel,
88                                   const struct sockaddr *sa,
89                                   ares_socklen_t salen, int flags_int,
90                                   ares_nameinfo_callback callback, void *arg)
91 {
92   const struct sockaddr_in  *addr  = NULL;
93   const struct sockaddr_in6 *addr6 = NULL;
94   struct nameinfo_query     *niquery;
95   unsigned short             port  = 0;
96   unsigned int               flags = (unsigned int)flags_int;
97 
98   /* Validate socket address family and length */
99   if (sa && sa->sa_family == AF_INET &&
100       salen >= (ares_socklen_t)sizeof(struct sockaddr_in)) {
101     addr = CARES_INADDR_CAST(const struct sockaddr_in *, sa);
102     port = addr->sin_port;
103   } else if (sa && sa->sa_family == AF_INET6 &&
104              salen >= (ares_socklen_t)sizeof(struct sockaddr_in6)) {
105     addr6 = CARES_INADDR_CAST(const struct sockaddr_in6 *, sa);
106     port  = addr6->sin6_port;
107   } else {
108     callback(arg, ARES_ENOTIMP, 0, NULL, NULL);
109     return;
110   }
111 
112   /* If neither, assume they want a host */
113   if (!(flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) {
114     flags |= ARES_NI_LOOKUPHOST;
115   }
116 
117   /* All they want is a service, no need for DNS */
118   if ((flags & ARES_NI_LOOKUPSERVICE) && !(flags & ARES_NI_LOOKUPHOST)) {
119     char  buf[33];
120     char *service;
121 
122     service =
123       lookup_service((unsigned short)(port & 0xffff), flags, buf, sizeof(buf));
124     callback(arg, ARES_SUCCESS, 0, NULL, service);
125     return;
126   }
127 
128   /* They want a host lookup */
129   if (flags & ARES_NI_LOOKUPHOST) {
130     /* A numeric host can be handled without DNS */
131     if (flags & ARES_NI_NUMERICHOST) {
132       char  ipbuf[IPBUFSIZ];
133       char  srvbuf[33];
134       char *service = NULL;
135       ipbuf[0]      = 0;
136 
137       /* Specifying not to lookup a host, but then saying a host
138        * is required has to be illegal.
139        */
140       if (flags & ARES_NI_NAMEREQD) {
141         callback(arg, ARES_EBADFLAGS, 0, NULL, NULL);
142         return;
143       }
144       if (sa->sa_family == AF_INET6) {
145         ares_inet_ntop(AF_INET6, &addr6->sin6_addr, ipbuf, IPBUFSIZ);
146         /* If the system supports scope IDs, use it */
147 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
148         append_scopeid(addr6, flags, ipbuf, sizeof(ipbuf));
149 #endif
150       } else {
151         ares_inet_ntop(AF_INET, &addr->sin_addr, ipbuf, IPBUFSIZ);
152       }
153       /* They also want a service */
154       if (flags & ARES_NI_LOOKUPSERVICE) {
155         service = lookup_service((unsigned short)(port & 0xffff), flags, srvbuf,
156                                  sizeof(srvbuf));
157       }
158       callback(arg, ARES_SUCCESS, 0, ipbuf, service);
159       return;
160     } else {
161       /* This is where a DNS lookup becomes necessary */
162       niquery = ares_malloc(sizeof(struct nameinfo_query));
163       if (!niquery) {
164         callback(arg, ARES_ENOMEM, 0, NULL, NULL);
165         return;
166       }
167       niquery->callback = callback;
168       niquery->arg      = arg;
169       niquery->flags    = flags;
170       niquery->timeouts = 0;
171       if (sa->sa_family == AF_INET) {
172         niquery->family = AF_INET;
173         memcpy(&niquery->addr.addr4, addr, sizeof(niquery->addr.addr4));
174         ares_gethostbyaddr_nolock(channel, &addr->sin_addr,
175                                   sizeof(struct in_addr), AF_INET,
176                                   nameinfo_callback, niquery);
177       } else {
178         niquery->family = AF_INET6;
179         memcpy(&niquery->addr.addr6, addr6, sizeof(niquery->addr.addr6));
180         ares_gethostbyaddr_nolock(channel, &addr6->sin6_addr,
181                                   sizeof(struct ares_in6_addr), AF_INET6,
182                                   nameinfo_callback, niquery);
183       }
184     }
185   }
186 }
187 
ares_getnameinfo(ares_channel_t * channel,const struct sockaddr * sa,ares_socklen_t salen,int flags_int,ares_nameinfo_callback callback,void * arg)188 void ares_getnameinfo(ares_channel_t *channel, const struct sockaddr *sa,
189                       ares_socklen_t salen, int flags_int,
190                       ares_nameinfo_callback callback, void *arg)
191 {
192   if (channel == NULL) {
193     return;
194   }
195 
196   ares_channel_lock(channel);
197   ares_getnameinfo_int(channel, sa, salen, flags_int, callback, arg);
198   ares_channel_unlock(channel);
199 }
200 
nameinfo_callback(void * arg,int status,int timeouts,struct hostent * host)201 static void nameinfo_callback(void *arg, int status, int timeouts,
202                               struct hostent *host)
203 {
204   struct nameinfo_query *niquery = (struct nameinfo_query *)arg;
205   char                   srvbuf[33];
206   char                  *service = NULL;
207 
208   niquery->timeouts += (size_t)timeouts;
209   if (status == ARES_SUCCESS) {
210     /* They want a service too */
211     if (niquery->flags & ARES_NI_LOOKUPSERVICE) {
212       if (niquery->family == AF_INET) {
213         service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags,
214                                  srvbuf, sizeof(srvbuf));
215       } else {
216         service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags,
217                                  srvbuf, sizeof(srvbuf));
218       }
219     }
220     /* NOFQDN means we have to strip off the domain name portion.  We do
221        this by determining our own domain name, then searching the string
222        for this domain name and removing it.
223      */
224 #ifdef HAVE_GETHOSTNAME
225     if (niquery->flags & ARES_NI_NOFQDN) {
226       char        buf[255];
227       const char *domain;
228       gethostname(buf, 255);
229       if ((domain = strchr(buf, '.')) != NULL) {
230         char *end = ares_striendstr(host->h_name, domain);
231         if (end) {
232           *end = 0;
233         }
234       }
235     }
236 #endif
237     niquery->callback(niquery->arg, ARES_SUCCESS, (int)niquery->timeouts,
238                       host->h_name, service);
239     ares_free(niquery);
240     return;
241   }
242   /* We couldn't find the host, but it's OK, we can use the IP */
243   else if (status == ARES_ENOTFOUND && !(niquery->flags & ARES_NI_NAMEREQD)) {
244     char ipbuf[IPBUFSIZ];
245     if (niquery->family == AF_INET) {
246       ares_inet_ntop(AF_INET, &niquery->addr.addr4.sin_addr, ipbuf, IPBUFSIZ);
247     } else {
248       ares_inet_ntop(AF_INET6, &niquery->addr.addr6.sin6_addr, ipbuf, IPBUFSIZ);
249 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
250       append_scopeid(&niquery->addr.addr6, niquery->flags, ipbuf,
251                      sizeof(ipbuf));
252 #endif
253     }
254     /* They want a service too */
255     if (niquery->flags & ARES_NI_LOOKUPSERVICE) {
256       if (niquery->family == AF_INET) {
257         service = lookup_service(niquery->addr.addr4.sin_port, niquery->flags,
258                                  srvbuf, sizeof(srvbuf));
259       } else {
260         service = lookup_service(niquery->addr.addr6.sin6_port, niquery->flags,
261                                  srvbuf, sizeof(srvbuf));
262       }
263     }
264     niquery->callback(niquery->arg, ARES_SUCCESS, (int)niquery->timeouts, ipbuf,
265                       service);
266     ares_free(niquery);
267     return;
268   }
269   niquery->callback(niquery->arg, status, (int)niquery->timeouts, NULL, NULL);
270   ares_free(niquery);
271 }
272 
lookup_service(unsigned short port,unsigned int flags,char * buf,size_t buflen)273 static char *lookup_service(unsigned short port, unsigned int flags, char *buf,
274                             size_t buflen)
275 {
276   const char     *proto;
277   struct servent *sep;
278 #ifdef HAVE_GETSERVBYPORT_R
279   struct servent se;
280 #endif
281   char        tmpbuf[4096];
282   const char *name;
283   size_t      name_len;
284 
285   if (port) {
286     if (flags & ARES_NI_NUMERICSERV) {
287       sep = NULL;
288     } else {
289       if (flags & ARES_NI_UDP) {
290         proto = "udp";
291       } else if (flags & ARES_NI_SCTP) {
292         proto = "sctp";
293       } else if (flags & ARES_NI_DCCP) {
294         proto = "dccp";
295       } else {
296         proto = "tcp";
297       }
298 #ifdef HAVE_GETSERVBYPORT_R
299       memset(&se, 0, sizeof(se));
300       sep = &se;
301       memset(tmpbuf, 0, sizeof(tmpbuf));
302 #  if GETSERVBYPORT_R_ARGS == 6
303       if (getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
304                           &sep) != 0) {
305         sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
306       }
307 #  elif GETSERVBYPORT_R_ARGS == 5
308       sep = getservbyport_r(port, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
309 #  elif GETSERVBYPORT_R_ARGS == 4
310       if (getservbyport_r(port, proto, &se, (void *)tmpbuf) != 0) {
311         sep = NULL;
312       }
313 #  else
314       /* Lets just hope the OS uses TLS! */
315       sep = getservbyport(port, proto);
316 #  endif
317 #else
318       /* Lets just hope the OS uses TLS! */
319 #  if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
320       sep = getservbyport(port, (char *)proto);
321 #  else
322       sep = getservbyport(port, proto);
323 #  endif
324 #endif
325     }
326     if (sep && sep->s_name) {
327       /* get service name */
328       name = sep->s_name;
329     } else {
330       /* get port as a string */
331       snprintf(tmpbuf, sizeof(tmpbuf), "%u", (unsigned int)ntohs(port));
332       name = tmpbuf;
333     }
334     name_len = ares_strlen(name);
335     if (name_len < buflen) {
336       /* return it if buffer big enough */
337       memcpy(buf, name, name_len + 1);
338     } else {
339       /* avoid reusing previous one */
340       buf[0] = '\0'; /* LCOV_EXCL_LINE: no real service names are too big */
341     }
342     return buf;
343   }
344   buf[0] = '\0';
345   return NULL;
346 }
347 
348 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
append_scopeid(const struct sockaddr_in6 * addr6,unsigned int flags,char * buf,size_t buflen)349 static void append_scopeid(const struct sockaddr_in6 *addr6, unsigned int flags,
350                            char *buf, size_t buflen)
351 {
352 #  ifdef HAVE_IF_INDEXTONAME
353   int is_ll;
354   int is_mcll;
355 #  endif
356   char   tmpbuf[IF_NAMESIZE + 2];
357   size_t bufl;
358 
359   tmpbuf[0] = '%';
360 
361 #  ifdef HAVE_IF_INDEXTONAME
362   is_ll   = IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr);
363   is_mcll = IN6_IS_ADDR_MC_LINKLOCAL(&addr6->sin6_addr);
364   if ((flags & ARES_NI_NUMERICSCOPE) || (!is_ll && !is_mcll)) {
365     snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu",
366              (unsigned long)addr6->sin6_scope_id);
367   } else {
368     if (if_indextoname(addr6->sin6_scope_id, &tmpbuf[1]) == NULL) {
369       snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu",
370                (unsigned long)addr6->sin6_scope_id);
371     }
372   }
373 #  else
374   snprintf(&tmpbuf[1], sizeof(tmpbuf) - 1, "%lu",
375            (unsigned long)addr6->sin6_scope_id);
376   (void)flags;
377 #  endif
378   tmpbuf[IF_NAMESIZE + 1] = '\0';
379   bufl                    = ares_strlen(buf);
380 
381   if (bufl + ares_strlen(tmpbuf) < buflen) {
382     /* only append the scopeid string if it fits in the target buffer */
383     ares_strcpy(&buf[bufl], tmpbuf, buflen - bufl);
384   }
385 }
386 #endif
387 
388 /* Determines if s1 ends with the string in s2 (case-insensitive) */
ares_striendstr(const char * s1,const char * s2)389 static char *ares_striendstr(const char *s1, const char *s2)
390 {
391   const char *c1;
392   const char *c2;
393   const char *c1_begin;
394   int         lo1;
395   int         lo2;
396   size_t      s1_len = ares_strlen(s1);
397   size_t      s2_len = ares_strlen(s2);
398 
399   if (s1 == NULL || s2 == NULL) {
400     return NULL;
401   }
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 
408   /* Jump to the end of s1 minus the length of s2 */
409   c1_begin = s1 + s1_len - s2_len;
410   c1       = c1_begin;
411   c2       = s2;
412   while (c2 < s2 + s2_len) {
413     lo1 = ares_tolower((unsigned char)*c1);
414     lo2 = ares_tolower((unsigned char)*c2);
415     if (lo1 != lo2) {
416       return NULL;
417     } else {
418       c1++;
419       c2++;
420     }
421   }
422   /* Cast off const */
423   return (char *)((size_t)c1_begin);
424 }
425 
ares_is_onion_domain(const char * name)426 ares_bool_t ares_is_onion_domain(const char *name)
427 {
428   if (ares_striendstr(name, ".onion")) {
429     return ARES_TRUE;
430   }
431 
432   if (ares_striendstr(name, ".onion.")) {
433     return ARES_TRUE;
434   }
435 
436   return ARES_FALSE;
437 }
438