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