Coverage Report

Created: 2023-09-25 07:14

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