GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: getaddrinfo_async.c,v 1.54 2017/02/27 10:44:46 jca Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2012 Eric Faurot <eric@openbsd.org> |
||
4 |
* |
||
5 |
* Permission to use, copy, modify, and distribute this software for any |
||
6 |
* purpose with or without fee is hereby granted, provided that the above |
||
7 |
* copyright notice and this permission notice appear in all copies. |
||
8 |
* |
||
9 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||
10 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
11 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||
12 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
13 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
14 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
15 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
16 |
*/ |
||
17 |
|||
18 |
#include <sys/types.h> |
||
19 |
#include <sys/socket.h> |
||
20 |
#include <sys/uio.h> |
||
21 |
#include <netinet/in.h> |
||
22 |
#include <arpa/nameser.h> |
||
23 |
#include <net/if.h> |
||
24 |
#include <netdb.h> |
||
25 |
|||
26 |
#include <asr.h> |
||
27 |
#include <errno.h> |
||
28 |
#include <ifaddrs.h> |
||
29 |
#include <resolv.h> |
||
30 |
#include <stdlib.h> |
||
31 |
#include <string.h> |
||
32 |
#include <unistd.h> |
||
33 |
#include <limits.h> |
||
34 |
|||
35 |
#include "asr_private.h" |
||
36 |
|||
37 |
struct match { |
||
38 |
int family; |
||
39 |
int socktype; |
||
40 |
int protocol; |
||
41 |
}; |
||
42 |
|||
43 |
static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); |
||
44 |
static int get_port(const char *, const char *, int); |
||
45 |
static int iter_family(struct asr_query *, int); |
||
46 |
static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); |
||
47 |
static int addrinfo_from_file(struct asr_query *, int, FILE *); |
||
48 |
static int addrinfo_from_pkt(struct asr_query *, char *, size_t); |
||
49 |
static int addrconfig_setup(struct asr_query *); |
||
50 |
|||
51 |
static const struct match matches[] = { |
||
52 |
{ PF_INET, SOCK_DGRAM, IPPROTO_UDP }, |
||
53 |
{ PF_INET, SOCK_STREAM, IPPROTO_TCP }, |
||
54 |
{ PF_INET, SOCK_RAW, 0 }, |
||
55 |
{ PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, |
||
56 |
{ PF_INET6, SOCK_STREAM, IPPROTO_TCP }, |
||
57 |
{ PF_INET6, SOCK_RAW, 0 }, |
||
58 |
{ -1, 0, 0, }, |
||
59 |
}; |
||
60 |
|||
61 |
#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) |
||
62 |
#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) |
||
63 |
/* Do not match SOCK_RAW unless explicitely specified */ |
||
64 |
#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ |
||
65 |
matches[(b)].socktype != SOCK_RAW)) |
||
66 |
|||
67 |
enum { |
||
68 |
DOM_INIT, |
||
69 |
DOM_DOMAIN, |
||
70 |
DOM_DONE |
||
71 |
}; |
||
72 |
|||
73 |
struct asr_query * |
||
74 |
getaddrinfo_async(const char *hostname, const char *servname, |
||
75 |
const struct addrinfo *hints, void *asr) |
||
76 |
{ |
||
77 |
struct asr_ctx *ac; |
||
78 |
struct asr_query *as; |
||
79 |
|||
80 |
✓✗✓✓ |
102 |
if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) |
81 |
12 |
ac = _asr_use_resolver(asr); |
|
82 |
else |
||
83 |
22 |
ac = _asr_no_resolver(); |
|
84 |
✓✗ | 34 |
if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL) |
85 |
goto abort; /* errno set */ |
||
86 |
34 |
as->as_run = getaddrinfo_async_run; |
|
87 |
|||
88 |
✓✗ | 34 |
if (hostname) { |
89 |
✓✗ | 34 |
if ((as->as.ai.hostname = strdup(hostname)) == NULL) |
90 |
goto abort; /* errno set */ |
||
91 |
} |
||
92 |
✓✗✓✗ |
68 |
if (servname && (as->as.ai.servname = strdup(servname)) == NULL) |
93 |
goto abort; /* errno set */ |
||
94 |
✓✗ | 34 |
if (hints) |
95 |
34 |
memmove(&as->as.ai.hints, hints, sizeof *hints); |
|
96 |
else { |
||
97 |
memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); |
||
98 |
as->as.ai.hints.ai_family = PF_UNSPEC; |
||
99 |
as->as.ai.hints.ai_flags = AI_ADDRCONFIG; |
||
100 |
} |
||
101 |
|||
102 |
34 |
_asr_ctx_unref(ac); |
|
103 |
34 |
return (as); |
|
104 |
abort: |
||
105 |
if (as) |
||
106 |
_asr_async_free(as); |
||
107 |
_asr_ctx_unref(ac); |
||
108 |
return (NULL); |
||
109 |
34 |
} |
|
110 |
DEF_WEAK(getaddrinfo_async); |
||
111 |
|||
112 |
static int |
||
113 |
getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) |
||
114 |
{ |
||
115 |
68 |
char fqdn[MAXDNAME]; |
|
116 |
const char *str; |
||
117 |
struct addrinfo *ai; |
||
118 |
int i, family, r; |
||
119 |
FILE *f; |
||
120 |
34 |
union { |
|
121 |
struct sockaddr sa; |
||
122 |
struct sockaddr_in sain; |
||
123 |
struct sockaddr_in6 sain6; |
||
124 |
} sa; |
||
125 |
|||
126 |
next: |
||
127 |
✓✗✗✗ ✗✗✗✓ ✗ |
68 |
switch (as->as_state) { |
128 |
|||
129 |
case ASR_STATE_INIT: |
||
130 |
|||
131 |
/* |
||
132 |
* First, make sure the parameters are valid. |
||
133 |
*/ |
||
134 |
|||
135 |
34 |
as->as_count = 0; |
|
136 |
|||
137 |
✗✓✗✗ |
34 |
if (as->as.ai.hostname == NULL && |
138 |
as->as.ai.servname == NULL) { |
||
139 |
ar->ar_gai_errno = EAI_NONAME; |
||
140 |
async_set_state(as, ASR_STATE_HALT); |
||
141 |
break; |
||
142 |
} |
||
143 |
|||
144 |
✓✗✗✓ |
68 |
if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { |
145 |
ar->ar_gai_errno = EAI_NODATA; |
||
146 |
async_set_state(as, ASR_STATE_HALT); |
||
147 |
break; |
||
148 |
} |
||
149 |
|||
150 |
34 |
ai = &as->as.ai.hints; |
|
151 |
|||
152 |
✓✗✗✓ |
68 |
if (ai->ai_addrlen || |
153 |
✓✗ | 34 |
ai->ai_canonname || |
154 |
✓✗ | 34 |
ai->ai_addr || |
155 |
34 |
ai->ai_next) { |
|
156 |
ar->ar_gai_errno = EAI_BADHINTS; |
||
157 |
async_set_state(as, ASR_STATE_HALT); |
||
158 |
break; |
||
159 |
} |
||
160 |
|||
161 |
✓✗✗✗ |
34 |
if (ai->ai_flags & ~AI_MASK || |
162 |
✗✓ | 34 |
(ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { |
163 |
ar->ar_gai_errno = EAI_BADFLAGS; |
||
164 |
async_set_state(as, ASR_STATE_HALT); |
||
165 |
break; |
||
166 |
} |
||
167 |
|||
168 |
✓✗✗✓ |
68 |
if (ai->ai_family != PF_UNSPEC && |
169 |
✓✗ | 34 |
ai->ai_family != PF_INET && |
170 |
34 |
ai->ai_family != PF_INET6) { |
|
171 |
ar->ar_gai_errno = EAI_FAMILY; |
||
172 |
async_set_state(as, ASR_STATE_HALT); |
||
173 |
break; |
||
174 |
} |
||
175 |
|||
176 |
✓✗✗✗ |
34 |
if (ai->ai_socktype && |
177 |
✗✓ | 34 |
ai->ai_socktype != SOCK_DGRAM && |
178 |
ai->ai_socktype != SOCK_STREAM && |
||
179 |
ai->ai_socktype != SOCK_RAW) { |
||
180 |
ar->ar_gai_errno = EAI_SOCKTYPE; |
||
181 |
async_set_state(as, ASR_STATE_HALT); |
||
182 |
break; |
||
183 |
} |
||
184 |
|||
185 |
✗✓✗✗ |
34 |
if (ai->ai_socktype == SOCK_RAW && |
186 |
get_port(as->as.ai.servname, NULL, 1) != 0) { |
||
187 |
ar->ar_gai_errno = EAI_SERVICE; |
||
188 |
async_set_state(as, ASR_STATE_HALT); |
||
189 |
break; |
||
190 |
} |
||
191 |
|||
192 |
/* Restrict result set to configured address families */ |
||
193 |
✗✓ | 34 |
if (ai->ai_flags & AI_ADDRCONFIG) { |
194 |
if (addrconfig_setup(as) == -1) { |
||
195 |
ar->ar_errno = errno; |
||
196 |
ar->ar_gai_errno = EAI_SYSTEM; |
||
197 |
async_set_state(as, ASR_STATE_HALT); |
||
198 |
break; |
||
199 |
} |
||
200 |
} |
||
201 |
|||
202 |
/* Make sure there is at least a valid combination */ |
||
203 |
✓✗ | 272 |
for (i = 0; matches[i].family != -1; i++) |
204 |
✓✓✗✓ ✗✗ |
238 |
if (MATCH_FAMILY(ai->ai_family, i) && |
205 |
✗✓✗✗ ✗✗ |
34 |
MATCH_SOCKTYPE(ai->ai_socktype, i) && |
206 |
✓✗✗✓ |
68 |
MATCH_PROTO(ai->ai_protocol, i)) |
207 |
break; |
||
208 |
✗✓ | 34 |
if (matches[i].family == -1) { |
209 |
ar->ar_gai_errno = EAI_BADHINTS; |
||
210 |
async_set_state(as, ASR_STATE_HALT); |
||
211 |
break; |
||
212 |
} |
||
213 |
|||
214 |
✗✓✗✗ |
34 |
if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) |
215 |
68 |
as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", |
|
216 |
34 |
as->as.ai.hints.ai_flags & AI_NUMERICSERV); |
|
217 |
✗✓✗✗ |
34 |
if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) |
218 |
68 |
as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", |
|
219 |
34 |
as->as.ai.hints.ai_flags & AI_NUMERICSERV); |
|
220 |
✓✗✓✗ ✗✗ |
68 |
if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || |
221 |
✗✓✗✗ |
34 |
(as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || |
222 |
✗✓✗✗ |
34 |
(ai->ai_protocol && (as->as.ai.port_udp == -1 || |
223 |
as->as.ai.port_tcp == -1))) { |
||
224 |
ar->ar_gai_errno = EAI_SERVICE; |
||
225 |
async_set_state(as, ASR_STATE_HALT); |
||
226 |
break; |
||
227 |
} |
||
228 |
|||
229 |
34 |
ar->ar_gai_errno = 0; |
|
230 |
|||
231 |
/* If hostname is NULL, use local address */ |
||
232 |
✗✓ | 68 |
if (as->as.ai.hostname == NULL) { |
233 |
✗✗ | 34 |
for (family = iter_family(as, 1); |
234 |
family != -1; |
||
235 |
family = iter_family(as, 0)) { |
||
236 |
/* |
||
237 |
* We could use statically built sockaddrs for |
||
238 |
* those, rather than parsing over and over. |
||
239 |
*/ |
||
240 |
if (family == PF_INET) |
||
241 |
str = (ai->ai_flags & AI_PASSIVE) ? \ |
||
242 |
"0.0.0.0" : "127.0.0.1"; |
||
243 |
else /* PF_INET6 */ |
||
244 |
str = (ai->ai_flags & AI_PASSIVE) ? \ |
||
245 |
"::" : "::1"; |
||
246 |
/* This can't fail */ |
||
247 |
_asr_sockaddr_from_str(&sa.sa, family, str); |
||
248 |
if ((r = addrinfo_add(as, &sa.sa, NULL))) { |
||
249 |
ar->ar_gai_errno = r; |
||
250 |
break; |
||
251 |
} |
||
252 |
} |
||
253 |
if (ar->ar_gai_errno == 0 && as->as_count == 0) { |
||
254 |
ar->ar_gai_errno = EAI_NODATA; |
||
255 |
} |
||
256 |
async_set_state(as, ASR_STATE_HALT); |
||
257 |
break; |
||
258 |
} |
||
259 |
|||
260 |
/* Try numeric addresses first */ |
||
261 |
✓✗ | 34 |
for (family = iter_family(as, 1); |
262 |
34 |
family != -1; |
|
263 |
family = iter_family(as, 0)) { |
||
264 |
|||
265 |
✓✗ | 102 |
if (_asr_sockaddr_from_str(&sa.sa, family, |
266 |
68 |
as->as.ai.hostname) == -1) |
|
267 |
continue; |
||
268 |
|||
269 |
✗✓ | 34 |
if ((r = addrinfo_add(as, &sa.sa, NULL))) |
270 |
ar->ar_gai_errno = r; |
||
271 |
break; |
||
272 |
} |
||
273 |
✓✗✓✗ |
68 |
if (ar->ar_gai_errno || as->as_count) { |
274 |
34 |
async_set_state(as, ASR_STATE_HALT); |
|
275 |
34 |
break; |
|
276 |
} |
||
277 |
|||
278 |
if (ai->ai_flags & AI_NUMERICHOST) { |
||
279 |
ar->ar_gai_errno = EAI_NONAME; |
||
280 |
async_set_state(as, ASR_STATE_HALT); |
||
281 |
break; |
||
282 |
} |
||
283 |
|||
284 |
async_set_state(as, ASR_STATE_NEXT_DB); |
||
285 |
break; |
||
286 |
|||
287 |
case ASR_STATE_NEXT_DB: |
||
288 |
if (_asr_iter_db(as) == -1) { |
||
289 |
async_set_state(as, ASR_STATE_NOT_FOUND); |
||
290 |
break; |
||
291 |
} |
||
292 |
as->as_family_idx = 0; |
||
293 |
async_set_state(as, ASR_STATE_SAME_DB); |
||
294 |
break; |
||
295 |
|||
296 |
case ASR_STATE_NEXT_FAMILY: |
||
297 |
as->as_family_idx += 1; |
||
298 |
if (as->as.ai.hints.ai_family != AF_UNSPEC || |
||
299 |
AS_FAMILY(as) == -1) { |
||
300 |
/* The family was specified, or we have tried all |
||
301 |
* families with this DB. |
||
302 |
*/ |
||
303 |
if (as->as_count) { |
||
304 |
ar->ar_gai_errno = 0; |
||
305 |
async_set_state(as, ASR_STATE_HALT); |
||
306 |
} else |
||
307 |
async_set_state(as, ASR_STATE_NEXT_DOMAIN); |
||
308 |
break; |
||
309 |
} |
||
310 |
async_set_state(as, ASR_STATE_SAME_DB); |
||
311 |
break; |
||
312 |
|||
313 |
case ASR_STATE_NEXT_DOMAIN: |
||
314 |
/* domain search is only for dns */ |
||
315 |
if (AS_DB(as) != ASR_DB_DNS) { |
||
316 |
async_set_state(as, ASR_STATE_NEXT_DB); |
||
317 |
break; |
||
318 |
} |
||
319 |
as->as_family_idx = 0; |
||
320 |
|||
321 |
free(as->as.ai.fqdn); |
||
322 |
as->as.ai.fqdn = NULL; |
||
323 |
r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); |
||
324 |
if (r == -1) { |
||
325 |
async_set_state(as, ASR_STATE_NEXT_DB); |
||
326 |
break; |
||
327 |
} |
||
328 |
if (r == 0) { |
||
329 |
ar->ar_gai_errno = EAI_FAIL; |
||
330 |
async_set_state(as, ASR_STATE_HALT); |
||
331 |
break; |
||
332 |
} |
||
333 |
as->as.ai.fqdn = strdup(fqdn); |
||
334 |
if (as->as.ai.fqdn == NULL) { |
||
335 |
ar->ar_gai_errno = EAI_MEMORY; |
||
336 |
async_set_state(as, ASR_STATE_HALT); |
||
337 |
break; |
||
338 |
} |
||
339 |
|||
340 |
async_set_state(as, ASR_STATE_SAME_DB); |
||
341 |
break; |
||
342 |
|||
343 |
case ASR_STATE_SAME_DB: |
||
344 |
/* query the current DB again */ |
||
345 |
switch (AS_DB(as)) { |
||
346 |
case ASR_DB_DNS: |
||
347 |
if (as->as.ai.fqdn == NULL) { |
||
348 |
/* First try, initialize domain iteration */ |
||
349 |
as->as_dom_flags = 0; |
||
350 |
as->as_dom_step = DOM_INIT; |
||
351 |
async_set_state(as, ASR_STATE_NEXT_DOMAIN); |
||
352 |
break; |
||
353 |
} |
||
354 |
|||
355 |
family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? |
||
356 |
AS_FAMILY(as) : as->as.ai.hints.ai_family; |
||
357 |
|||
358 |
if (family == AF_INET && |
||
359 |
as->as_flags & ASYNC_NO_INET) { |
||
360 |
async_set_state(as, ASR_STATE_NEXT_FAMILY); |
||
361 |
break; |
||
362 |
} else if (family == AF_INET6 && |
||
363 |
as->as_flags & ASYNC_NO_INET6) { |
||
364 |
async_set_state(as, ASR_STATE_NEXT_FAMILY); |
||
365 |
break; |
||
366 |
} |
||
367 |
|||
368 |
as->as_subq = _res_query_async_ctx(as->as.ai.fqdn, |
||
369 |
C_IN, (family == AF_INET6) ? T_AAAA : T_A, |
||
370 |
as->as_ctx); |
||
371 |
|||
372 |
if (as->as_subq == NULL) { |
||
373 |
if (errno == ENOMEM) |
||
374 |
ar->ar_gai_errno = EAI_MEMORY; |
||
375 |
else |
||
376 |
ar->ar_gai_errno = EAI_FAIL; |
||
377 |
async_set_state(as, ASR_STATE_HALT); |
||
378 |
break; |
||
379 |
} |
||
380 |
async_set_state(as, ASR_STATE_SUBQUERY); |
||
381 |
break; |
||
382 |
|||
383 |
case ASR_DB_FILE: |
||
384 |
f = fopen(_PATH_HOSTS, "re"); |
||
385 |
if (f == NULL) { |
||
386 |
async_set_state(as, ASR_STATE_NEXT_DB); |
||
387 |
break; |
||
388 |
} |
||
389 |
family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? |
||
390 |
AS_FAMILY(as) : as->as.ai.hints.ai_family; |
||
391 |
|||
392 |
r = addrinfo_from_file(as, family, f); |
||
393 |
if (r == -1) { |
||
394 |
if (errno == ENOMEM) |
||
395 |
ar->ar_gai_errno = EAI_MEMORY; |
||
396 |
else |
||
397 |
ar->ar_gai_errno = EAI_FAIL; |
||
398 |
async_set_state(as, ASR_STATE_HALT); |
||
399 |
} else |
||
400 |
async_set_state(as, ASR_STATE_NEXT_FAMILY); |
||
401 |
fclose(f); |
||
402 |
break; |
||
403 |
|||
404 |
default: |
||
405 |
async_set_state(as, ASR_STATE_NEXT_DB); |
||
406 |
} |
||
407 |
break; |
||
408 |
|||
409 |
case ASR_STATE_SUBQUERY: |
||
410 |
if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) |
||
411 |
return (ASYNC_COND); |
||
412 |
|||
413 |
as->as_subq = NULL; |
||
414 |
|||
415 |
if (ar->ar_datalen == -1) { |
||
416 |
async_set_state(as, ASR_STATE_NEXT_FAMILY); |
||
417 |
break; |
||
418 |
} |
||
419 |
|||
420 |
r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); |
||
421 |
if (r == -1) { |
||
422 |
if (errno == ENOMEM) |
||
423 |
ar->ar_gai_errno = EAI_MEMORY; |
||
424 |
else |
||
425 |
ar->ar_gai_errno = EAI_FAIL; |
||
426 |
async_set_state(as, ASR_STATE_HALT); |
||
427 |
} else |
||
428 |
async_set_state(as, ASR_STATE_NEXT_FAMILY); |
||
429 |
free(ar->ar_data); |
||
430 |
break; |
||
431 |
|||
432 |
case ASR_STATE_NOT_FOUND: |
||
433 |
/* No result found. Maybe we can try again. */ |
||
434 |
if (as->as_flags & ASYNC_AGAIN) |
||
435 |
ar->ar_gai_errno = EAI_AGAIN; |
||
436 |
else |
||
437 |
ar->ar_gai_errno = EAI_NODATA; |
||
438 |
async_set_state(as, ASR_STATE_HALT); |
||
439 |
break; |
||
440 |
|||
441 |
case ASR_STATE_HALT: |
||
442 |
✓✗ | 34 |
if (ar->ar_gai_errno == 0) { |
443 |
34 |
ar->ar_count = as->as_count; |
|
444 |
34 |
ar->ar_addrinfo = as->as.ai.aifirst; |
|
445 |
as->as.ai.aifirst = NULL; |
||
446 |
34 |
} else { |
|
447 |
ar->ar_count = 0; |
||
448 |
ar->ar_addrinfo = NULL; |
||
449 |
} |
||
450 |
34 |
return (ASYNC_DONE); |
|
451 |
|||
452 |
default: |
||
453 |
ar->ar_errno = EOPNOTSUPP; |
||
454 |
ar->ar_gai_errno = EAI_SYSTEM; |
||
455 |
async_set_state(as, ASR_STATE_HALT); |
||
456 |
break; |
||
457 |
} |
||
458 |
goto next; |
||
459 |
34 |
} |
|
460 |
|||
461 |
/* |
||
462 |
* Retreive the port number for the service name "servname" and |
||
463 |
* the protocol "proto". |
||
464 |
*/ |
||
465 |
static int |
||
466 |
get_port(const char *servname, const char *proto, int numonly) |
||
467 |
{ |
||
468 |
136 |
struct servent se; |
|
469 |
68 |
struct servent_data sed; |
|
470 |
int port, r; |
||
471 |
68 |
const char *e; |
|
472 |
|||
473 |
✗✓ | 68 |
if (servname == NULL) |
474 |
return (0); |
||
475 |
|||
476 |
68 |
e = NULL; |
|
477 |
68 |
port = strtonum(servname, 0, USHRT_MAX, &e); |
|
478 |
✓✗ | 68 |
if (e == NULL) |
479 |
68 |
return (port); |
|
480 |
if (errno == ERANGE) |
||
481 |
return (-2); /* invalid */ |
||
482 |
if (numonly) |
||
483 |
return (-2); |
||
484 |
|||
485 |
memset(&sed, 0, sizeof(sed)); |
||
486 |
r = getservbyname_r(servname, proto, &se, &sed); |
||
487 |
port = ntohs(se.s_port); |
||
488 |
endservent_r(&sed); |
||
489 |
|||
490 |
if (r == -1) |
||
491 |
return (-1); /* not found */ |
||
492 |
|||
493 |
return (port); |
||
494 |
68 |
} |
|
495 |
|||
496 |
/* |
||
497 |
* Iterate over the address families that are to be queried. Use the |
||
498 |
* list on the async context, unless a specific family was given in hints. |
||
499 |
*/ |
||
500 |
static int |
||
501 |
iter_family(struct asr_query *as, int first) |
||
502 |
{ |
||
503 |
✓✗ | 68 |
if (first) { |
504 |
34 |
as->as_family_idx = 0; |
|
505 |
✓✗ | 34 |
if (as->as.ai.hints.ai_family != PF_UNSPEC) |
506 |
34 |
return as->as.ai.hints.ai_family; |
|
507 |
return AS_FAMILY(as); |
||
508 |
} |
||
509 |
|||
510 |
if (as->as.ai.hints.ai_family != PF_UNSPEC) |
||
511 |
return (-1); |
||
512 |
|||
513 |
as->as_family_idx++; |
||
514 |
|||
515 |
return AS_FAMILY(as); |
||
516 |
34 |
} |
|
517 |
|||
518 |
/* |
||
519 |
* Use the sockaddr at "sa" to extend the result list on the "as" context, |
||
520 |
* with the specified canonical name "cname". This function adds one |
||
521 |
* entry per protocol/socktype match. |
||
522 |
*/ |
||
523 |
static int |
||
524 |
addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) |
||
525 |
{ |
||
526 |
struct addrinfo *ai; |
||
527 |
int i, port, proto; |
||
528 |
|||
529 |
✓✓ | 510 |
for (i = 0; matches[i].family != -1; i++) { |
530 |
✓✓✗✗ |
204 |
if (matches[i].family != sa->sa_family || |
531 |
✓✓✗✓ ✗✗ |
170 |
!MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || |
532 |
✓✗✗✓ |
68 |
!MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) |
533 |
continue; |
||
534 |
|||
535 |
34 |
proto = as->as.ai.hints.ai_protocol; |
|
536 |
✓✗ | 34 |
if (!proto) |
537 |
34 |
proto = matches[i].protocol; |
|
538 |
|||
539 |
✗✓ | 34 |
if (proto == IPPROTO_TCP) |
540 |
port = as->as.ai.port_tcp; |
||
541 |
✓✗ | 34 |
else if (proto == IPPROTO_UDP) |
542 |
34 |
port = as->as.ai.port_udp; |
|
543 |
else |
||
544 |
port = 0; |
||
545 |
|||
546 |
/* servname specified, but not defined for this protocol */ |
||
547 |
✓✗ | 34 |
if (port == -1) |
548 |
continue; |
||
549 |
|||
550 |
34 |
ai = calloc(1, sizeof(*ai) + sa->sa_len); |
|
551 |
✗✓ | 34 |
if (ai == NULL) |
552 |
return (EAI_MEMORY); |
||
553 |
34 |
ai->ai_family = sa->sa_family; |
|
554 |
34 |
ai->ai_socktype = matches[i].socktype; |
|
555 |
34 |
ai->ai_protocol = proto; |
|
556 |
34 |
ai->ai_flags = as->as.ai.hints.ai_flags; |
|
557 |
34 |
ai->ai_addrlen = sa->sa_len; |
|
558 |
34 |
ai->ai_addr = (void *)(ai + 1); |
|
559 |
✗✓✗✗ |
34 |
if (cname && |
560 |
as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { |
||
561 |
if ((ai->ai_canonname = strdup(cname)) == NULL) { |
||
562 |
free(ai); |
||
563 |
return (EAI_MEMORY); |
||
564 |
} |
||
565 |
} |
||
566 |
34 |
memmove(ai->ai_addr, sa, sa->sa_len); |
|
567 |
✗✓ | 34 |
if (sa->sa_family == PF_INET) |
568 |
((struct sockaddr_in *)ai->ai_addr)->sin_port = |
||
569 |
htons(port); |
||
570 |
✓✗ | 34 |
else if (sa->sa_family == PF_INET6) |
571 |
34 |
((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = |
|
572 |
34 |
htons(port); |
|
573 |
|||
574 |
✓✗ | 34 |
if (as->as.ai.aifirst == NULL) |
575 |
34 |
as->as.ai.aifirst = ai; |
|
576 |
✗✓ | 34 |
if (as->as.ai.ailast) |
577 |
as->as.ai.ailast->ai_next = ai; |
||
578 |
34 |
as->as.ai.ailast = ai; |
|
579 |
34 |
as->as_count += 1; |
|
580 |
34 |
} |
|
581 |
|||
582 |
34 |
return (0); |
|
583 |
34 |
} |
|
584 |
|||
585 |
static int |
||
586 |
addrinfo_from_file(struct asr_query *as, int family, FILE *f) |
||
587 |
{ |
||
588 |
char *tokens[MAXTOKEN], *c, buf[BUFSIZ + 1]; |
||
589 |
int n, i; |
||
590 |
union { |
||
591 |
struct sockaddr sa; |
||
592 |
struct sockaddr_in sain; |
||
593 |
struct sockaddr_in6 sain6; |
||
594 |
} u; |
||
595 |
|||
596 |
for (;;) { |
||
597 |
n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); |
||
598 |
if (n == -1) |
||
599 |
break; /* ignore errors reading the file */ |
||
600 |
|||
601 |
for (i = 1; i < n; i++) { |
||
602 |
if (strcasecmp(as->as.ai.hostname, tokens[i])) |
||
603 |
continue; |
||
604 |
if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) |
||
605 |
continue; |
||
606 |
break; |
||
607 |
} |
||
608 |
if (i == n) |
||
609 |
continue; |
||
610 |
|||
611 |
if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) |
||
612 |
c = tokens[1]; |
||
613 |
else |
||
614 |
c = NULL; |
||
615 |
|||
616 |
if (addrinfo_add(as, &u.sa, c)) |
||
617 |
return (-1); /* errno set */ |
||
618 |
} |
||
619 |
return (0); |
||
620 |
} |
||
621 |
|||
622 |
static int |
||
623 |
addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) |
||
624 |
{ |
||
625 |
struct asr_unpack p; |
||
626 |
struct asr_dns_header h; |
||
627 |
struct asr_dns_query q; |
||
628 |
struct asr_dns_rr rr; |
||
629 |
int i; |
||
630 |
union { |
||
631 |
struct sockaddr sa; |
||
632 |
struct sockaddr_in sain; |
||
633 |
struct sockaddr_in6 sain6; |
||
634 |
} u; |
||
635 |
char buf[MAXDNAME], *c; |
||
636 |
|||
637 |
_asr_unpack_init(&p, pkt, pktlen); |
||
638 |
_asr_unpack_header(&p, &h); |
||
639 |
for (; h.qdcount; h.qdcount--) |
||
640 |
_asr_unpack_query(&p, &q); |
||
641 |
|||
642 |
for (i = 0; i < h.ancount; i++) { |
||
643 |
_asr_unpack_rr(&p, &rr); |
||
644 |
if (rr.rr_type != q.q_type || |
||
645 |
rr.rr_class != q.q_class) |
||
646 |
continue; |
||
647 |
|||
648 |
memset(&u, 0, sizeof u); |
||
649 |
if (rr.rr_type == T_A) { |
||
650 |
u.sain.sin_len = sizeof u.sain; |
||
651 |
u.sain.sin_family = AF_INET; |
||
652 |
u.sain.sin_addr = rr.rr.in_a.addr; |
||
653 |
u.sain.sin_port = 0; |
||
654 |
} else if (rr.rr_type == T_AAAA) { |
||
655 |
u.sain6.sin6_len = sizeof u.sain6; |
||
656 |
u.sain6.sin6_family = AF_INET6; |
||
657 |
u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; |
||
658 |
u.sain6.sin6_port = 0; |
||
659 |
} else |
||
660 |
continue; |
||
661 |
|||
662 |
if (as->as.ai.hints.ai_flags & AI_CANONNAME) { |
||
663 |
_asr_strdname(rr.rr_dname, buf, sizeof buf); |
||
664 |
buf[strlen(buf) - 1] = '\0'; |
||
665 |
c = res_hnok(buf) ? buf : NULL; |
||
666 |
} else if (as->as.ai.hints.ai_flags & AI_FQDN) |
||
667 |
c = as->as.ai.fqdn; |
||
668 |
else |
||
669 |
c = NULL; |
||
670 |
|||
671 |
if (addrinfo_add(as, &u.sa, c)) |
||
672 |
return (-1); /* errno set */ |
||
673 |
} |
||
674 |
return (0); |
||
675 |
} |
||
676 |
|||
677 |
static int |
||
678 |
addrconfig_setup(struct asr_query *as) |
||
679 |
{ |
||
680 |
struct ifaddrs *ifa, *ifa0; |
||
681 |
struct sockaddr_in *sinp; |
||
682 |
struct sockaddr_in6 *sin6p; |
||
683 |
|||
684 |
if (getifaddrs(&ifa0) == -1) |
||
685 |
return (-1); |
||
686 |
|||
687 |
as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6; |
||
688 |
|||
689 |
for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { |
||
690 |
if (ifa->ifa_addr == NULL) |
||
691 |
continue; |
||
692 |
|||
693 |
switch (ifa->ifa_addr->sa_family) { |
||
694 |
case PF_INET: |
||
695 |
sinp = (struct sockaddr_in *)ifa->ifa_addr; |
||
696 |
|||
697 |
if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) |
||
698 |
continue; |
||
699 |
|||
700 |
as->as_flags &= ~ASYNC_NO_INET; |
||
701 |
break; |
||
702 |
case PF_INET6: |
||
703 |
sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; |
||
704 |
|||
705 |
if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) |
||
706 |
continue; |
||
707 |
|||
708 |
if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) |
||
709 |
continue; |
||
710 |
|||
711 |
as->as_flags &= ~ASYNC_NO_INET6; |
||
712 |
break; |
||
713 |
} |
||
714 |
} |
||
715 |
|||
716 |
freeifaddrs(ifa0); |
||
717 |
|||
718 |
return (0); |
||
719 |
} |
Generated by: GCOVR (Version 3.3) |