Coverage Report

Created: 2023-09-25 07:14

/src/c-ares/src/lib/ares__readaddrinfo.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2019 by Andrew Selivanov
2
 *
3
 * Permission to use, copy, modify, and distribute this
4
 * software and its documentation for any purpose and without
5
 * fee is hereby granted, provided that the above copyright
6
 * notice appear in all copies and that both that copyright
7
 * notice and this permission notice appear in supporting
8
 * documentation, and that the name of M.I.T. not be used in
9
 * advertising or publicity pertaining to distribution of the
10
 * software without specific, written prior permission.
11
 * M.I.T. makes no representations about the suitability of
12
 * this software for any purpose.  It is provided "as is"
13
 * without express or implied warranty.
14
 *
15
 * SPDX-License-Identifier: MIT
16
 */
17
18
#include "ares_setup.h"
19
20
#ifdef HAVE_NETINET_IN_H
21
#  include <netinet/in.h>
22
#endif
23
#ifdef HAVE_NETDB_H
24
#  include <netdb.h>
25
#endif
26
#ifdef HAVE_ARPA_INET_H
27
#  include <arpa/inet.h>
28
#endif
29
30
#include "ares.h"
31
#include "ares_inet_net_pton.h"
32
#include "ares_nowarn.h"
33
#include "ares_private.h"
34
35
0
#define MAX_ALIASES 40
36
37
int ares__readaddrinfo(FILE *fp,
38
                       const char *name,
39
                       unsigned short port,
40
                       const struct ares_addrinfo_hints *hints,
41
                       struct ares_addrinfo *ai)
42
0
{
43
0
  char *line = NULL, *p, *q;
44
0
  char *txtaddr, *txthost, *txtalias;
45
0
  char *aliases[MAX_ALIASES];
46
0
  unsigned int i, alias_count;
47
0
  int status = ARES_SUCCESS;
48
0
  size_t linesize;
49
0
  struct ares_addrinfo_cname *cname = NULL, *cnames = NULL;
50
0
  struct ares_addrinfo_node *nodes = NULL;
51
0
  int match_with_alias, match_with_canonical;
52
0
  int want_cname = hints->ai_flags & ARES_AI_CANONNAME;
53
54
  /* Validate family */
55
0
  switch (hints->ai_family) {
56
0
    case AF_INET:
57
0
    case AF_INET6:
58
0
    case AF_UNSPEC:
59
0
      break;
60
0
    default:
61
0
      return ARES_EBADFAMILY;
62
0
  }
63
64
0
  ai->name = ares_strdup(name);
65
0
  if(!ai->name)
66
0
    {
67
0
      status = ARES_ENOMEM;
68
0
      goto fail;
69
0
    }
70
71
0
  while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
72
0
    {
73
0
      match_with_alias = 0;
74
0
      match_with_canonical = 0;
75
0
      alias_count = 0;
76
      /* Trim line comment. */
77
0
      p = line;
78
0
      while (*p && (*p != '#'))
79
0
        p++;
80
0
      *p = '\0';
81
82
      /* Trim trailing whitespace. */
83
0
      q = p - 1;
84
0
      while ((q >= line) && ISSPACE(*q))
85
0
        q--;
86
0
      *++q = '\0';
87
88
      /* Skip leading whitespace. */
89
0
      p = line;
90
0
      while (*p && ISSPACE(*p))
91
0
        p++;
92
0
      if (!*p)
93
        /* Ignore line if empty. */
94
0
        continue;
95
96
      /* Pointer to start of IPv4 or IPv6 address part. */
97
0
      txtaddr = p;
98
99
      /* Advance past address part. */
100
0
      while (*p && !ISSPACE(*p))
101
0
        p++;
102
0
      if (!*p)
103
        /* Ignore line if reached end of line. */
104
0
        continue;
105
106
      /* Null terminate address part. */
107
0
      *p = '\0';
108
109
      /* Advance to host name */
110
0
      p++;
111
0
      while (*p && ISSPACE(*p))
112
0
        p++;
113
0
      if (!*p)
114
        /* Ignore line if reached end of line. */
115
0
        continue;  /* LCOV_EXCL_LINE: trailing whitespace already stripped */
116
117
      /* Pointer to start of host name. */
118
0
      txthost = p;
119
120
      /* Advance past host name. */
121
0
      while (*p && !ISSPACE(*p))
122
0
        p++;
123
124
      /* Pointer to start of first alias. */
125
0
      txtalias = NULL;
126
0
      if (*p)
127
0
        {
128
0
          q = p + 1;
129
0
          while (*q && ISSPACE(*q))
130
0
            q++;
131
0
          if (*q)
132
0
            txtalias = q;
133
0
        }
134
135
      /* Null terminate host name. */
136
0
      *p = '\0';
137
138
      /* Find out if host name matches with canonical host name. */
139
0
      if (strcasecmp(txthost, name) == 0)
140
0
        {
141
0
          match_with_canonical = 1;
142
0
        }
143
144
      /* Find out if host name matches with one of the aliases. */
145
0
      while (txtalias)
146
0
        {
147
0
          p = txtalias;
148
0
          while (*p && !ISSPACE(*p))
149
0
            p++;
150
0
          q = p;
151
0
          while (*q && ISSPACE(*q))
152
0
            q++;
153
0
          *p = '\0';
154
0
          if (strcasecmp(txtalias, name) == 0)
155
0
            {
156
0
              match_with_alias = 1;
157
0
              if (!want_cname)
158
0
                break;
159
0
            }
160
0
          if (alias_count < MAX_ALIASES)
161
0
            {
162
0
              aliases[alias_count++] = txtalias;
163
0
            }
164
0
          txtalias = *q ? q : NULL;
165
0
        }
166
167
      /* Try next line if host does not match. */
168
0
      if (!match_with_alias && !match_with_canonical)
169
0
        {
170
0
          continue;
171
0
        }
172
173
      /*
174
       * Convert address string to network address for the requested families.
175
       * Actual address family possible values are AF_INET and AF_INET6 only.
176
       */
177
0
      if ((hints->ai_family == AF_INET) || (hints->ai_family == AF_UNSPEC))
178
0
        {
179
0
          struct in_addr addr4;
180
0
          if (ares_inet_pton(AF_INET, txtaddr, &addr4) == 1)
181
0
            {
182
0
              status = ares_append_ai_node(AF_INET, port, 0, &addr4, &nodes);
183
0
              if (status != ARES_SUCCESS)
184
0
                {
185
0
                  goto fail;
186
0
                }
187
0
            }
188
0
        }
189
0
      if ((hints->ai_family == AF_INET6) || (hints->ai_family == AF_UNSPEC))
190
0
        {
191
0
          struct ares_in6_addr addr6;
192
0
          if (ares_inet_pton(AF_INET6, txtaddr, &addr6) == 1)
193
0
            {
194
0
              status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &nodes);
195
0
              if (status != ARES_SUCCESS)
196
0
                {
197
0
                  goto fail;
198
0
                }
199
0
            }
200
0
        }
201
202
0
      if (want_cname)
203
0
        {
204
0
          for (i = 0; i < alias_count; ++i)
205
0
            {
206
0
              cname = ares__append_addrinfo_cname(&cnames);
207
0
              if (!cname)
208
0
                {
209
0
                  status = ARES_ENOMEM;
210
0
                  goto fail;
211
0
                }
212
0
              cname->alias = ares_strdup(aliases[i]);
213
0
              cname->name = ares_strdup(txthost);
214
0
            }
215
          /* No aliases, cname only. */
216
0
          if(!alias_count)
217
0
            {
218
0
              cname = ares__append_addrinfo_cname(&cnames);
219
0
              if (!cname)
220
0
                {
221
0
                  status = ARES_ENOMEM;
222
0
                  goto fail;
223
0
                }
224
0
              cname->name = ares_strdup(txthost);
225
0
            }
226
0
        }
227
0
    }
228
229
  /* Last read failed. */
230
0
  if (status == ARES_ENOMEM)
231
0
    {
232
0
      goto fail;
233
0
    }
234
235
  /* If no results, its a failure */
236
0
  if (!nodes)
237
0
    {
238
0
      status = ARES_ENOTFOUND;
239
0
      goto fail;
240
0
    }
241
242
  /* Free line buffer. */
243
0
  ares_free(line);
244
0
  ares__addrinfo_cat_cnames(&ai->cnames, cnames);
245
0
  ares__addrinfo_cat_nodes(&ai->nodes, nodes);
246
247
0
  return ARES_SUCCESS;
248
249
0
fail:
250
0
  ares_free(line);
251
0
  ares__freeaddrinfo_cnames(cnames);
252
0
  ares__freeaddrinfo_nodes(nodes);
253
0
  ares_free(ai->name);
254
0
  ai->name = NULL;
255
0
  return status;
256
0
}