/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 | } |