/[packages]/updates/5/glibc/current/SOURCES/212-resolv-Reduce-EDNS-payload-size-to-1200-bytes-BZ-213_CVE-2017-12132.patch
ViewVC logotype

Contents of /updates/5/glibc/current/SOURCES/212-resolv-Reduce-EDNS-payload-size-to-1200-bytes-BZ-213_CVE-2017-12132.patch

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1184057 - (show annotations) (download)
Fri Dec 22 19:56:02 2017 UTC (6 years, 3 months ago) by tmb
File size: 25164 byte(s)
fix CVE-2017-1213[23], CVE-2017-1567[01], CVE-2017-15804
1 From e14a27723cc3a154d67f3f26e719d08c0ba9ad25 Mon Sep 17 00:00:00 2001
2 From: Florian Weimer <fweimer@redhat.com>
3 Date: Thu, 13 Apr 2017 13:09:38 +0200
4 Subject: [PATCH] resolv: Reduce EDNS payload size to 1200 bytes [BZ #21361]
5
6 This hardens the stub resolver against fragmentation-based attacks.
7 ---
8 [ Rebased for 2.22 /tmb ]
9 Signed-off-by: Thomas Backlund <tmb@mageia.org>
10
11 ---
12 include/resolv.h | 3
13 resolv/Makefile | 4
14 resolv/res_mkquery.c | 28 ++
15 resolv/res_query.c | 23 +-
16 resolv/resolv-internal.h | 43 ++++
17 resolv/tst-resolv-edns.c | 501 +++++++++++++++++++++++++++++++++++++++++++++++
18 6 files changed, 591 insertions(+), 11 deletions(-)
19
20 #diff --git a/ChangeLog b/ChangeLog
21 #index 2cdf82cc7e..1cd7a7b48a 100644
22 #--- a/ChangeLog
23 #+++ b/ChangeLog
24 #@@ -1,3 +1,24 @@
25 #+2017-04-13 Florian Weimer <fweimer@redhat.com>
26 ##+
27 #+ [BZ #21361]
28 #+ Limit EDNS buffer size to 1200 bytes.
29 #+ * include/resolv.h (__res_nopt): Remove declaration.
30 #+ * resolv/Makefile (tests): tst-resolv-edns.
31 #+ (tst-resolv-edns): Link with -lresolv, -lpthread.
32 #+ * resolv/res_mkquery.c (__res_ntop): Limit EDNS buffer size to the
33 #+ interval [512, 1200].
34 #+ * resolv/res_query.c (__libc_res_nquery): Use 1200 buffer size if
35 #+ we can resize the buffer.
36 #+ * resolv/resolv-internal.h (RESOLV_EDNS_BUFFER_SIZE): Define.
37 #+ (__res_nopt): Declare.
38 #+ * resolv/tst-resolv-edns.c: New file.
39 #+ * resolv/resolv_test.h (struct resolv_edns_info): Define.
40 #+ (struct resolv_response_context): Add edns member.
41 #+ * resolv/resolv_test.c (struct query_info): Add edns member.
42 #+ (parse_query): Extract EDNS information from the query.
43 #+ (server_thread_udp_process_one): Propagate EDNS data.
44 #+ (server_thread_tcp_client): Likewise.
45 #+
46 # 2017-04-13 Florian Weimer <fweimer@redhat.com>
47 #
48 # [BZ #21359]
49 #diff --git a/NEWS b/NEWS
50 #index 28bb00887a..99288b5f22 100644
51 #--- a/NEWS
52 #+++ b/NEWS
53 #@@ -46,7 +46,8 @@ Version 2.26
54 #
55 # Security related changes:
56 #
57 #- [Add security related changes here]
58 #+* The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes,
59 #+ to avoid fragmentation-based spoofing attacks.
60 #
61 # The following bugs are resolved with this release:
62 #
63 diff -Nurp glibc-2.22.old/include/resolv.h glibc-2.22/include/resolv.h
64 --- glibc-2.22.old/include/resolv.h 2015-08-05 09:42:21.000000000 +0300
65 +++ glibc-2.22/include/resolv.h 2017-12-16 01:10:19.368966932 +0200
66 @@ -38,8 +38,6 @@ extern void res_send_setrhook (res_send_
67 extern int res_ourserver_p (const res_state __statp,
68 const struct sockaddr_in6 *__inp);
69 extern void __res_iclose (res_state statp, bool free_addr);
70 -extern int __res_nopt(res_state statp, int n0, u_char *buf, int buflen,
71 - int anslen);
72 libc_hidden_proto (__res_ninit)
73 libc_hidden_proto (__res_maybe_init)
74 libc_hidden_proto (__res_nclose)
75 @@ -88,7 +86,6 @@ libresolv_hidden_proto (__res_nameinquer
76 libresolv_hidden_proto (__res_queriesmatch)
77 libresolv_hidden_proto (__res_nsend)
78 libresolv_hidden_proto (__b64_ntop)
79 -libresolv_hidden_proto (__res_nopt)
80 libresolv_hidden_proto (__dn_count_labels)
81 libresolv_hidden_proto (__p_secstodate)
82
83 diff -Nurp glibc-2.20.old/resolv/Makefile glibc-2.20/resolv/Makefile
84 --- glibc-2.20.old/resolv/Makefile
85 +++ glibc-2.20/resolv/Makefile
86 @@ -39,6 +39,7 @@ extra-libs := libresolv libnss_dns
87 ifeq ($(have-thread-library),yes)
88 extra-libs += libanl
89 routines += gai_sigqueue
90 +tests += tst-resolv-edns
91
92 # This test sends millions of packets and is rather slow.
93 xtests += tst-resolv-qtypes
94 @@ -113,3 +114,4 @@ $(objpfx)mtrace-tst-leaks2.out: $(objpfx
95 $(evaluate-test)
96
97 $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
98 +$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library)
99 diff -Nurp glibc-2.22.old/resolv/res_mkquery.c glibc-2.22/resolv/res_mkquery.c
100 --- glibc-2.22.old/resolv/res_mkquery.c 2017-12-16 00:58:28.803564502 +0200
101 +++ glibc-2.22/resolv/res_mkquery.c 2017-12-16 01:01:59.249647472 +0200
102 @@ -74,7 +74,7 @@ static const char rcsid[] = "$BINDId: re
103 #include <netinet/in.h>
104 #include <arpa/nameser.h>
105 #include <netdb.h>
106 -#include <resolv.h>
107 +#include <resolv/resolv-internal.h>
108 #include <stdio.h>
109 #include <string.h>
110 #include <sys/time.h>
111 @@ -250,7 +250,30 @@ __res_nopt(res_state statp,
112 *cp++ = 0; /* "." */
113
114 NS_PUT16(T_OPT, cp); /* TYPE */
115 - NS_PUT16(MIN(anslen, 0xffff), cp); /* CLASS = UDP payload size */
116 +
117 + /* Lowering the advertised buffer size based on the actual
118 + answer buffer size is desirable because the server will
119 + minimize the reply to fit into the UDP packet (and A
120 + non-minimal response might not fit the buffer).
121 +
122 + The RESOLV_EDNS_BUFFER_SIZE limit could still result in TCP
123 + fallback and a non-minimal response which has to be
124 + hard-truncated in the stub resolver, but this is price to
125 + pay for avoiding fragmentation. (This issue does not
126 + affect the nss_dns functions because they use the stub
127 + resolver in such a way that it allocates a properly sized
128 + response buffer.) */
129 + {
130 + uint16_t buffer_size;
131 + if (anslen < 512)
132 + buffer_size = 512;
133 + else if (anslen > RESOLV_EDNS_BUFFER_SIZE)
134 + buffer_size = RESOLV_EDNS_BUFFER_SIZE;
135 + else
136 + buffer_size = anslen;
137 + NS_PUT16 (buffer_size, cp);
138 + }
139 +
140 *cp++ = NOERROR; /* extended RCODE */
141 *cp++ = 0; /* EDNS version */
142
143 @@ -268,4 +291,3 @@ __res_nopt(res_state statp,
144
145 return cp - buf;
146 }
147 -libresolv_hidden_def (__res_nopt)
148 diff -Nurp glibc-2.22.old/resolv/resolv-internal.h glibc-2.22/resolv/resolv-internal.h
149 --- glibc-2.22.old/resolv/resolv-internal.h 1970-01-01 02:00:00.000000000 +0200
150 +++ glibc-2.22/resolv/resolv-internal.h 2017-12-16 01:17:47.557518586 +0200
151 @@ -0,0 +1,43 @@
152 +/* libresolv interfaces for internal use across glibc.
153 + Copyright (C) 2016-2017 Free Software Foundation, Inc.
154 + This file is part of the GNU C Library.
155 +
156 + The GNU C Library is free software; you can redistribute it and/or
157 + modify it under the terms of the GNU Lesser General Public
158 + License as published by the Free Software Foundation; either
159 + version 2.1 of the License, or (at your option) any later version.
160 +
161 + The GNU C Library is distributed in the hope that it will be useful,
162 + but WITHOUT ANY WARRANTY; without even the implied warranty of
163 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
164 + Lesser General Public License for more details.
165 +
166 + You should have received a copy of the GNU Lesser General Public
167 + License along with the GNU C Library; if not, see
168 + <http://www.gnu.org/licenses/>. */
169 +
170 +#ifndef _RESOLV_INTERNAL_H
171 +#define _RESOLV_INTERNAL_H 1
172 +
173 +#include <resolv.h>
174 +#include <stdbool.h>
175 +
176 +enum
177 + {
178 + /* The advertized EDNS buffer size. The value 1200 is derived
179 + from the IPv6 minimum MTU (1280 bytes) minus some arbitrary
180 + space for tunneling overhead. If the DNS server does not react
181 + to ICMP Fragmentation Needed But DF Set messages, this should
182 + avoid all UDP fragments on current networks. Avoiding UDP
183 + fragments is desirable because it prevents fragmentation-based
184 + spoofing attacks because the randomness in a DNS packet is
185 + concentrated in the first fragment (with the headers) and does
186 + not protect subsequent fragments. */
187 + RESOLV_EDNS_BUFFER_SIZE = 1200,
188 + };
189 +
190 +/* Add an OPT record to a DNS query. */
191 +int __res_nopt (res_state, int n0, unsigned char *buf, int buflen,
192 + int anslen) attribute_hidden;
193 +
194 +#endif /* _RESOLV_INTERNAL_H */
195 diff -Nurp glibc-2.22.old/resolv/res_query.c glibc-2.22/resolv/res_query.c
196 --- glibc-2.22.old/resolv/res_query.c 2017-12-16 00:58:28.803564502 +0200
197 +++ glibc-2.22/resolv/res_query.c 2017-12-16 01:01:59.249647472 +0200
198 @@ -82,6 +82,7 @@ static const char rcsid[] = "$BINDId: re
199 #include <stdio.h>
200 #include <stdlib.h>
201 #include <string.h>
202 +#include <resolv/resolv-internal.h>
203
204 /* Options. Leave them on. */
205 /* #undef DEBUG */
206 @@ -151,7 +152,10 @@ __libc_res_nquery(res_state statp,
207 if ((oflags & RES_F_EDNS0ERR) == 0
208 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
209 {
210 - n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
211 + /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
212 + buffer can be reallocated. */
213 + n = __res_nopt (statp, n, query1, bufsize,
214 + RESOLV_EDNS_BUFFER_SIZE);
215 if (n < 0)
216 goto unspec_nomem;
217 }
218 @@ -172,8 +176,10 @@ __libc_res_nquery(res_state statp,
219 if (n > 0
220 && (oflags & RES_F_EDNS0ERR) == 0
221 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
222 - n = __res_nopt(statp, n, query2, bufsize - nused - n,
223 - anslen / 2);
224 + /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
225 + buffer can be reallocated. */
226 + n = __res_nopt (statp, n, query2, bufsize,
227 + RESOLV_EDNS_BUFFER_SIZE);
228 nquery2 = n;
229 }
230
231 @@ -187,7 +193,16 @@ __libc_res_nquery(res_state statp,
232 if (n > 0
233 && (oflags & RES_F_EDNS0ERR) == 0
234 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
235 - n = __res_nopt(statp, n, query1, bufsize, anslen);
236 + {
237 + /* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer
238 + can be reallocated. */
239 + size_t advertise;
240 + if (answerp == NULL)
241 + advertise = anslen;
242 + else
243 + advertise = RESOLV_EDNS_BUFFER_SIZE;
244 + n = __res_nopt (statp, n, query1, bufsize, advertise);
245 + }
246
247 nquery1 = n;
248 }
249 diff -Nurp glibc-2.22.old/resolv/tst-resolv-edns.c glibc-2.22/resolv/tst-resolv-edns.c
250 --- glibc-2.22.old/resolv/tst-resolv-edns.c 1970-01-01 02:00:00.000000000 +0200
251 +++ glibc-2.22/resolv/tst-resolv-edns.c 2017-12-16 01:02:04.695727230 +0200
252 @@ -0,0 +1,501 @@
253 +/* Test EDNS handling in the stub resolver.
254 + Copyright (C) 2016-2017 Free Software Foundation, Inc.
255 + This file is part of the GNU C Library.
256 +
257 + The GNU C Library is free software; you can redistribute it and/or
258 + modify it under the terms of the GNU Lesser General Public
259 + License as published by the Free Software Foundation; either
260 + version 2.1 of the License, or (at your option) any later version.
261 +
262 + The GNU C Library is distributed in the hope that it will be useful,
263 + but WITHOUT ANY WARRANTY; without even the implied warranty of
264 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
265 + Lesser General Public License for more details.
266 +
267 + You should have received a copy of the GNU Lesser General Public
268 + License along with the GNU C Library; if not, see
269 + <http://www.gnu.org/licenses/>. */
270 +
271 +#include <errno.h>
272 +#include <netdb.h>
273 +#include <stdio.h>
274 +#include <stdlib.h>
275 +#include <string.h>
276 +#include <support/check.h>
277 +#include <support/resolv_test.h>
278 +#include <support/support.h>
279 +#include <support/test-driver.h>
280 +#include <support/xthread.h>
281 +
282 +/* Data produced by a test query. */
283 +struct response_data
284 +{
285 + char *qname;
286 + uint16_t qtype;
287 + struct resolv_edns_info edns;
288 +};
289 +
290 +/* Global array used by put_response and get_response to record
291 + response data. The test DNS server returns the index of the array
292 + element which contains the actual response data. This enables the
293 + test case to return arbitrary amounts of data with the limited
294 + number of bits which fit into an IP addres.
295 +
296 + The volatile specifier is needed because the test case accesses
297 + these variables from a callback function called from a function
298 + which is marked as __THROW (i.e., a leaf function which actually is
299 + not). */
300 +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
301 +static struct response_data ** volatile response_data_array;
302 +volatile static size_t response_data_count;
303 +
304 +/* Extract information from the query, store it in a struct
305 + response_data object, and return its index in the
306 + response_data_array. */
307 +static unsigned int
308 +put_response (const struct resolv_response_context *ctx,
309 + const char *qname, uint16_t qtype)
310 +{
311 + xpthread_mutex_lock (&mutex);
312 + ++response_data_count;
313 + /* We only can represent 2**24 indexes in 10.0.0.0/8. */
314 + TEST_VERIFY (response_data_count < (1 << 24));
315 + response_data_array = xrealloc
316 + (response_data_array, sizeof (*response_data_array) * response_data_count);
317 + unsigned int index = response_data_count - 1;
318 + struct response_data *data = xmalloc (sizeof (*data));
319 + *data = (struct response_data)
320 + {
321 + .qname = xstrdup (qname),
322 + .qtype = qtype,
323 + .edns = ctx->edns,
324 + };
325 + response_data_array[index] = data;
326 + xpthread_mutex_unlock (&mutex);
327 + return index;
328 +}
329 +
330 +/* Verify the index into the response_data array and return the data
331 + at it. */
332 +static struct response_data *
333 +get_response (unsigned int index)
334 +{
335 + xpthread_mutex_lock (&mutex);
336 + TEST_VERIFY_EXIT (index < response_data_count);
337 + struct response_data *result = response_data_array[index];
338 + xpthread_mutex_unlock (&mutex);
339 + return result;
340 +}
341 +
342 +/* Deallocate all response data. */
343 +static void
344 +free_response_data (void)
345 +{
346 + xpthread_mutex_lock (&mutex);
347 + size_t count = response_data_count;
348 + struct response_data **array = response_data_array;
349 + for (unsigned int i = 0; i < count; ++i)
350 + {
351 + struct response_data *data = array[i];
352 + free (data->qname);
353 + free (data);
354 + }
355 + free (array);
356 + response_data_array = NULL;
357 + response_data_count = 0;
358 + xpthread_mutex_unlock (&mutex);
359 +}
360 +
361 +#define EDNS_PROBE_EXAMPLE "edns-probe.example"
362 +
363 +static void
364 +response (const struct resolv_response_context *ctx,
365 + struct resolv_response_builder *b,
366 + const char *qname, uint16_t qclass, uint16_t qtype)
367 +{
368 + TEST_VERIFY_EXIT (qname != NULL);
369 +
370 + /* The "tcp." prefix can be used to request TCP fallback. */
371 + const char *qname_compare = qname;
372 + bool force_tcp;
373 + if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
374 + {
375 + force_tcp = true;
376 + qname_compare += strlen ("tcp.");
377 + }
378 + else
379 + force_tcp = false;
380 +
381 + enum {edns_probe} requested_qname;
382 + if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0)
383 + requested_qname = edns_probe;
384 + else
385 + {
386 + support_record_failure ();
387 + printf ("error: unexpected QNAME: %s\n", qname);
388 + return;
389 + }
390 + TEST_VERIFY_EXIT (qclass == C_IN);
391 + struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
392 + resolv_response_init (b, flags);
393 + resolv_response_add_question (b, qname, qclass, qtype);
394 + if (flags.tc)
395 + return;
396 +
397 + if (test_verbose)
398 + printf ("info: edns=%d payload_size=%d\n",
399 + ctx->edns.active, ctx->edns.payload_size);
400 +
401 + /* Encode the response_data object in multiple address records.
402 + Each record carries two bytes of payload data, and an index. */
403 + resolv_response_section (b, ns_s_an);
404 + switch (requested_qname)
405 + {
406 + case edns_probe:
407 + {
408 + unsigned int index = put_response (ctx, qname, qtype);
409 + switch (qtype)
410 + {
411 + case T_A:
412 + {
413 + uint32_t addr = htonl (0x0a000000 | index);
414 + resolv_response_open_record (b, qname, qclass, qtype, 0);
415 + resolv_response_add_data (b, &addr, sizeof (addr));
416 + resolv_response_close_record (b);
417 + }
418 + break;
419 + case T_AAAA:
420 + {
421 + char addr[16]
422 + = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
423 + index >> 16, index >> 8, index};
424 + resolv_response_open_record (b, qname, qclass, qtype, 0);
425 + resolv_response_add_data (b, &addr, sizeof (addr));
426 + resolv_response_close_record (b);
427 + }
428 + }
429 + }
430 + break;
431 + }
432 +}
433 +
434 +/* Update *DATA with data from ADDRESS of SIZE. Set the corresponding
435 + flag in SHADOW for each byte written. */
436 +static struct response_data *
437 +decode_address (const void *address, size_t size)
438 +{
439 + switch (size)
440 + {
441 + case 4:
442 + TEST_VERIFY (memcmp (address, "\x0a", 1) == 0);
443 + break;
444 + case 16:
445 + TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0);
446 + break;
447 + default:
448 + FAIL_EXIT1 ("unexpected address size %zu", size);
449 + }
450 + const unsigned char *addr = address;
451 + unsigned int index = addr[size - 3] * 256 * 256
452 + + addr[size - 2] * 256
453 + + addr[size - 1];
454 + return get_response (index);
455 +}
456 +
457 +static struct response_data *
458 +decode_hostent (struct hostent *e)
459 +{
460 + TEST_VERIFY_EXIT (e != NULL);
461 + TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL);
462 + TEST_VERIFY (e->h_addr_list[1] == NULL);
463 + return decode_address (e->h_addr_list[0], e->h_length);
464 +}
465 +
466 +static struct response_data *
467 +decode_addrinfo (struct addrinfo *ai, int family)
468 +{
469 + struct response_data *data = NULL;
470 + while (ai != NULL)
471 + {
472 + if (ai->ai_family == family)
473 + {
474 + struct response_data *new_data;
475 + switch (family)
476 + {
477 + case AF_INET:
478 + {
479 + struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr;
480 + new_data = decode_address (&pin->sin_addr.s_addr, 4);
481 + }
482 + break;
483 + case AF_INET6:
484 + {
485 + struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr;
486 + new_data = decode_address (&pin->sin6_addr.s6_addr, 16);
487 + }
488 + break;
489 + default:
490 + FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
491 + }
492 + if (data == NULL)
493 + data = new_data;
494 + else
495 + /* Check pointer equality because this should be the same
496 + response (same index). */
497 + TEST_VERIFY (data == new_data);
498 + }
499 + ai = ai->ai_next;
500 + }
501 + TEST_VERIFY_EXIT (data != NULL);
502 + return data;
503 +}
504 +
505 +/* Updated by the main test loop in accordance with what is set in
506 + _res.options. */
507 +static bool use_edns;
508 +static bool use_dnssec;
509 +
510 +/* Verify the decoded response data against the flags above. */
511 +static void
512 +verify_response_data_payload (struct response_data *data,
513 + size_t expected_payload)
514 +{
515 + bool edns = use_edns || use_dnssec;
516 + TEST_VERIFY (data->edns.active == edns);
517 + if (!edns)
518 + expected_payload = 0;
519 + if (data->edns.payload_size != expected_payload)
520 + {
521 + support_record_failure ();
522 + printf ("error: unexpected payload size %d (edns=%d)\n",
523 + (int) data->edns.payload_size, edns);
524 + }
525 + uint16_t expected_flags = 0;
526 + if (use_dnssec)
527 + expected_flags |= 0x8000; /* DO flag. */
528 + if (data->edns.flags != expected_flags)
529 + {
530 + support_record_failure ();
531 + printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
532 + (int) data->edns.flags, edns);
533 + }
534 +}
535 +
536 +/* Same as verify_response_data_payload, but use the default
537 + payload. */
538 +static void
539 +verify_response_data (struct response_data *data)
540 +{
541 + verify_response_data_payload (data, 1200);
542 +}
543 +
544 +static void
545 +check_hostent (struct hostent *e)
546 +{
547 + TEST_VERIFY_EXIT (e != NULL);
548 + verify_response_data (decode_hostent (e));
549 +}
550 +
551 +static void
552 +do_ai (int family)
553 +{
554 + struct addrinfo hints = { .ai_family = family };
555 + struct addrinfo *ai;
556 + int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai);
557 + TEST_VERIFY_EXIT (ret == 0);
558 + switch (family)
559 + {
560 + case AF_INET:
561 + case AF_INET6:
562 + verify_response_data (decode_addrinfo (ai, family));
563 + break;
564 + case AF_UNSPEC:
565 + verify_response_data (decode_addrinfo (ai, AF_INET));
566 + verify_response_data (decode_addrinfo (ai, AF_INET6));
567 + break;
568 + default:
569 + FAIL_EXIT1 ("invalid address family %d", family);
570 + }
571 + freeaddrinfo (ai);
572 +}
573 +
574 +enum res_op
575 +{
576 + res_op_search,
577 + res_op_query,
578 + res_op_querydomain,
579 + res_op_nsearch,
580 + res_op_nquery,
581 + res_op_nquerydomain,
582 +
583 + res_op_last = res_op_nquerydomain,
584 +};
585 +
586 +static const char *
587 +res_op_string (enum res_op op)
588 +{
589 + switch (op)
590 + {
591 + case res_op_search:
592 + return "res_search";
593 + case res_op_query:
594 + return "res_query";
595 + case res_op_querydomain:
596 + return "res_querydomain";
597 + case res_op_nsearch:
598 + return "res_nsearch";
599 + case res_op_nquery:
600 + return "res_nquery";
601 + case res_op_nquerydomain:
602 + return "res_nquerydomain";
603 + }
604 + FAIL_EXIT1 ("invalid res_op value %d", (int) op);
605 +}
606 +
607 +/* Call libresolv function OP to look up PROBE_NAME, with an answer
608 + buffer of SIZE bytes. Check that the advertised UDP buffer size is
609 + in fact EXPECTED_BUFFER_SIZE. */
610 +static void
611 +do_res_search (const char *probe_name, enum res_op op, size_t size,
612 + size_t expected_buffer_size)
613 +{
614 + if (test_verbose)
615 + printf ("info: testing %s with buffer size %zu\n",
616 + res_op_string (op), size);
617 + unsigned char *buffer = xmalloc (size);
618 + int ret = -1;
619 + switch (op)
620 + {
621 + case res_op_search:
622 + ret = res_search (probe_name, C_IN, T_A, buffer, size);
623 + break;
624 + case res_op_query:
625 + ret = res_query (probe_name, C_IN, T_A, buffer, size);
626 + break;
627 + case res_op_nsearch:
628 + ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size);
629 + break;
630 + case res_op_nquery:
631 + ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size);
632 + break;
633 + case res_op_querydomain:
634 + case res_op_nquerydomain:
635 + {
636 + char *example_stripped = xstrdup (probe_name);
637 + char *dot_example = strstr (example_stripped, ".example");
638 + if (dot_example != NULL && strcmp (dot_example, ".example") == 0)
639 + {
640 + /* Truncate the domain name. */
641 + *dot_example = '\0';
642 + if (op == res_op_querydomain)
643 + ret = res_querydomain
644 + (example_stripped, "example", C_IN, T_A, buffer, size);
645 + else
646 + ret = res_nquerydomain
647 + (&_res, example_stripped, "example", C_IN, T_A, buffer, size);
648 + }
649 + else
650 + FAIL_EXIT1 ("invalid probe name: %s", probe_name);
651 + free (example_stripped);
652 + }
653 + break;
654 + }
655 + TEST_VERIFY_EXIT (ret > 12);
656 + unsigned char *end = buffer + ret;
657 +
658 + HEADER *hd = (HEADER *) buffer;
659 + TEST_VERIFY (ntohs (hd->qdcount) == 1);
660 + TEST_VERIFY (ntohs (hd->ancount) == 1);
661 + /* Skip over the header. */
662 + unsigned char *p = buffer + sizeof (*hd);
663 + /* Skip over the question. */
664 + ret = dn_skipname (p, end);
665 + TEST_VERIFY_EXIT (ret > 0);
666 + p += ret;
667 + TEST_VERIFY_EXIT (end - p >= 4);
668 + p += 4;
669 + /* Skip over the RNAME and the RR header, but stop at the RDATA
670 + length. */
671 + ret = dn_skipname (p, end);
672 + TEST_VERIFY_EXIT (ret > 0);
673 + p += ret;
674 + TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4);
675 + p += 2 + 2 + 4;
676 + /* The IP address should be 4 bytes long. */
677 + TEST_VERIFY_EXIT (p[0] == 0);
678 + TEST_VERIFY_EXIT (p[1] == 4);
679 + /* Extract the address information. */
680 + p += 2;
681 + struct response_data *data = decode_address (p, 4);
682 +
683 + verify_response_data_payload (data, expected_buffer_size);
684 +
685 + free (buffer);
686 +}
687 +
688 +static void
689 +run_test (const char *probe_name)
690 +{
691 + if (test_verbose)
692 + printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
693 + use_edns, use_dnssec);
694 + check_hostent (gethostbyname (probe_name));
695 + check_hostent (gethostbyname2 (probe_name, AF_INET));
696 + check_hostent (gethostbyname2 (probe_name, AF_INET6));
697 + do_ai (AF_UNSPEC);
698 + do_ai (AF_INET);
699 + do_ai (AF_INET6);
700 +
701 + for (int op = 0; op <= res_op_last; ++op)
702 + {
703 + do_res_search (probe_name, op, 301, 512);
704 + do_res_search (probe_name, op, 511, 512);
705 + do_res_search (probe_name, op, 512, 512);
706 + do_res_search (probe_name, op, 513, 513);
707 + do_res_search (probe_name, op, 657, 657);
708 + do_res_search (probe_name, op, 1199, 1199);
709 + do_res_search (probe_name, op, 1200, 1200);
710 + do_res_search (probe_name, op, 1201, 1200);
711 + do_res_search (probe_name, op, 65535, 1200);
712 + }
713 +}
714 +
715 +static int
716 +do_test (void)
717 +{
718 + for (int do_edns = 0; do_edns < 2; ++do_edns)
719 + for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
720 + for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
721 + {
722 + struct resolv_test *aux = resolv_test_start
723 + ((struct resolv_redirect_config)
724 + {
725 + .response_callback = response,
726 + });
727 +
728 + use_edns = do_edns;
729 + if (do_edns)
730 + _res.options |= RES_USE_EDNS0;
731 + use_dnssec = do_dnssec;
732 + if (do_dnssec)
733 + _res.options |= RES_USE_DNSSEC;
734 +
735 + char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
736 + if (do_tcp)
737 + {
738 + char *n = xasprintf ("tcp.%s", probe_name);
739 + free (probe_name);
740 + probe_name = n;
741 + }
742 +
743 + run_test (probe_name);
744 +
745 + free (probe_name);
746 + resolv_test_end (aux);
747 + }
748 +
749 + free_response_data ();
750 + return 0;
751 +}
752 +
753 +#include <support/test-driver.c>

  ViewVC Help
Powered by ViewVC 1.1.30