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