• 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;
46   size_t linesize;
47   ares_sockaddr addr;
48   struct ares_addrinfo_cname *cname = NULL, *cnames = NULL;
49   struct ares_addrinfo_node *node = NULL, *nodes = NULL;
50   int match_with_alias, match_with_canonical;
51   int want_cname = hints->ai_flags & ARES_AI_CANONNAME;
52 
53   /* Validate family */
54   switch (hints->ai_family) {
55     case AF_INET:
56     case AF_INET6:
57     case AF_UNSPEC:
58       break;
59     default:
60       return ARES_EBADFAMILY;
61   }
62 
63 
64   while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
65     {
66       match_with_alias = 0;
67       match_with_canonical = 0;
68       alias_count = 0;
69       /* Trim line comment. */
70       p = line;
71       while (*p && (*p != '#'))
72         p++;
73       *p = '\0';
74 
75       /* Trim trailing whitespace. */
76       q = p - 1;
77       while ((q >= line) && ISSPACE(*q))
78         q--;
79       *++q = '\0';
80 
81       /* Skip leading whitespace. */
82       p = line;
83       while (*p && ISSPACE(*p))
84         p++;
85       if (!*p)
86         /* Ignore line if empty. */
87         continue;
88 
89       /* Pointer to start of IPv4 or IPv6 address part. */
90       txtaddr = p;
91 
92       /* Advance past address part. */
93       while (*p && !ISSPACE(*p))
94         p++;
95       if (!*p)
96         /* Ignore line if reached end of line. */
97         continue;
98 
99       /* Null terminate address part. */
100       *p = '\0';
101 
102       /* Advance to host name */
103       p++;
104       while (*p && ISSPACE(*p))
105         p++;
106       if (!*p)
107         /* Ignore line if reached end of line. */
108         continue;  /* LCOV_EXCL_LINE: trailing whitespace already stripped */
109 
110       /* Pointer to start of host name. */
111       txthost = p;
112 
113       /* Advance past host name. */
114       while (*p && !ISSPACE(*p))
115         p++;
116 
117       /* Pointer to start of first alias. */
118       txtalias = NULL;
119       if (*p)
120         {
121           q = p + 1;
122           while (*q && ISSPACE(*q))
123             q++;
124           if (*q)
125             txtalias = q;
126         }
127 
128       /* Null terminate host name. */
129       *p = '\0';
130 
131       /* Find out if host name matches with canonical host name. */
132       if (strcasecmp(txthost, name) == 0)
133         {
134           match_with_canonical = 1;
135         }
136 
137       /* Find out if host name matches with one of the aliases. */
138       while (txtalias)
139         {
140           p = txtalias;
141           while (*p && !ISSPACE(*p))
142             p++;
143           q = p;
144           while (*q && ISSPACE(*q))
145             q++;
146           *p = '\0';
147           if (strcasecmp(txtalias, name) == 0)
148             {
149               match_with_alias = 1;
150               if (!want_cname)
151                 break;
152             }
153           if (alias_count < MAX_ALIASES)
154             {
155               aliases[alias_count++] = txtalias;
156             }
157           txtalias = *q ? q : NULL;
158         }
159 
160       /* Try next line if host does not match. */
161       if (!match_with_alias && !match_with_canonical)
162         {
163           continue;
164         }
165 
166       /*
167        * Convert address string to network address for the requested families.
168        * Actual address family possible values are AF_INET and AF_INET6 only.
169        */
170       if ((hints->ai_family == AF_INET) || (hints->ai_family == AF_UNSPEC))
171         {
172           addr.sa4.sin_port = htons(port);
173           if (ares_inet_pton(AF_INET, txtaddr, &addr.sa4.sin_addr) > 0)
174             {
175               node = ares__append_addrinfo_node(&nodes);
176               if(!node)
177                 {
178                   goto enomem;
179                 }
180 
181               node->ai_family = addr.sa.sa_family = AF_INET;
182               node->ai_addrlen = sizeof(sizeof(addr.sa4));
183               node->ai_addr = ares_malloc(sizeof(addr.sa4));
184               if (!node->ai_addr)
185                 {
186                   goto enomem;
187                 }
188               memcpy(node->ai_addr, &addr.sa4, sizeof(addr.sa4));
189             }
190         }
191       if ((hints->ai_family == AF_INET6) || (hints->ai_family == AF_UNSPEC))
192         {
193           addr.sa6.sin6_port = htons(port);
194           if (ares_inet_pton(AF_INET6, txtaddr, &addr.sa6.sin6_addr) > 0)
195             {
196               node = ares__append_addrinfo_node(&nodes);
197               if (!node)
198                 {
199                   goto enomem;
200                 }
201 
202               node->ai_family = addr.sa.sa_family = AF_INET6;
203               node->ai_addrlen = sizeof(sizeof(addr.sa6));
204               node->ai_addr = ares_malloc(sizeof(addr.sa6));
205               if (!node->ai_addr)
206                 {
207                   goto enomem;
208                 }
209               memcpy(node->ai_addr, &addr.sa6, sizeof(addr.sa6));
210             }
211         }
212       if (!node)
213         /* Ignore line if invalid address string for the requested family. */
214         continue;
215 
216       if (want_cname)
217         {
218           for (i = 0; i < alias_count; ++i)
219             {
220               cname = ares__append_addrinfo_cname(&cnames);
221               if (!cname)
222                 {
223                   goto enomem;
224                 }
225               cname->alias = ares_strdup(aliases[i]);
226               cname->name = ares_strdup(txthost);
227             }
228           /* No aliases, cname only. */
229           if(!alias_count)
230             {
231               cname = ares__append_addrinfo_cname(&cnames);
232               if (!cname)
233                 {
234                   goto enomem;
235                 }
236               cname->name = ares_strdup(txthost);
237             }
238         }
239     }
240 
241   /* Last read failed. */
242   if (status == ARES_ENOMEM)
243     {
244       goto enomem;
245     }
246 
247   /* Free line buffer. */
248   ares_free(line);
249 
250   ares__addrinfo_cat_cnames(&ai->cnames, cnames);
251   ares__addrinfo_cat_nodes(&ai->nodes, nodes);
252 
253   return node ? ARES_SUCCESS : ARES_ENOTFOUND;
254 
255 enomem:
256   ares_free(line);
257   ares__freeaddrinfo_cnames(cnames);
258   ares__freeaddrinfo_nodes(nodes);
259   return ARES_ENOMEM;
260 }
261