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