Add NEWS entry for CVE-2020-6096 (bug 25620)
[glibc.git] / support / resolv_test.c
blob53b7fc41ab894455547b7e0074753fe25edad4e6
1 /* DNS test framework and libresolv redirection.
2 Copyright (C) 2016-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <support/resolv_test.h>
21 #include <arpa/inet.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <nss.h>
25 #include <resolv.h>
26 #include <search.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <support/check.h>
30 #include <support/namespace.h>
31 #include <support/support.h>
32 #include <support/test-driver.h>
33 #include <support/xsocket.h>
34 #include <support/xthread.h>
35 #include <support/xunistd.h>
36 #include <sys/uio.h>
37 #include <unistd.h>
39 /* Response builder. */
41 enum
43 max_response_length = 65536
46 /* Used for locating domain names containing for the purpose of
47 forming compression references. */
48 struct compressed_name
50 uint16_t offset;
51 unsigned char length;
52 unsigned char name[]; /* Without terminating NUL. */
55 static struct compressed_name *
56 allocate_compressed_name (const unsigned char *encoded, unsigned int offset)
58 /* Compute the length of the domain name. */
59 size_t length;
61 const unsigned char *p;
62 for (p = encoded; *p != '\0';)
64 /* No compression references are allowed. */
65 TEST_VERIFY (*p <= 63);
66 /* Skip over the label. */
67 p += 1 + *p;
69 length = p - encoded;
70 ++length; /* For the terminating NUL byte. */
72 TEST_VERIFY_EXIT (length <= 255);
74 struct compressed_name *result
75 = xmalloc (offsetof (struct compressed_name, name) + length);
76 result->offset = offset;
77 result->length = length;
78 memcpy (result->name, encoded, length);
79 return result;
82 /* Convert CH to lower case. Only change letters in the ASCII
83 range. */
84 static inline unsigned char
85 ascii_tolower (unsigned char ch)
87 if ('A' <= ch && ch <= 'Z')
88 return ch - 'A' + 'a';
89 else
90 return ch;
93 /* Compare both names, for use with tsearch. The order is arbitrary,
94 but the comparison is case-insenstive. */
95 static int
96 compare_compressed_name (const void *left, const void *right)
98 const struct compressed_name *crleft = left;
99 const struct compressed_name *crright = right;
101 if (crleft->length != crright->length)
102 /* The operands are converted to int before the subtraction. */
103 return crleft->length - crright->length;
105 const unsigned char *nameleft = crleft->name;
106 const unsigned char *nameright = crright->name;
108 while (true)
110 int lenleft = *nameleft++;
111 int lenright = *nameright++;
113 /* Labels must not e compression references. */
114 TEST_VERIFY (lenleft <= 63);
115 TEST_VERIFY (lenright <= 63);
117 if (lenleft != lenright)
118 return left - right;
119 if (lenleft == 0)
120 /* End of name reached without spotting a difference. */
121 return 0;
122 /* Compare the label in a case-insenstive manner. */
123 const unsigned char *endnameleft = nameleft + lenleft;
124 while (nameleft < endnameleft)
126 int l = *nameleft++;
127 int r = *nameright++;
128 if (l != r)
130 l = ascii_tolower (l);
131 r = ascii_tolower (r);
132 if (l != r)
133 return l - r;
139 struct resolv_response_builder
141 const unsigned char *query_buffer;
142 size_t query_length;
144 size_t offset; /* Bytes written so far in buffer. */
145 ns_sect section; /* Current section in the DNS packet. */
146 unsigned int truncate_bytes; /* Bytes to remove at end of response. */
147 bool drop; /* Discard generated response. */
148 bool close; /* Close TCP client connection. */
150 /* Offset of the two-byte RDATA length field in the currently
151 written RDATA sub-structure. 0 if no RDATA is being written. */
152 size_t current_rdata_offset;
154 /* tsearch tree for locating targets for label compression. */
155 void *compression_offsets;
157 /* Must be last. Not zeroed for performance reasons. */
158 unsigned char buffer[max_response_length];
161 /* Response builder. */
163 void
164 resolv_response_init (struct resolv_response_builder *b,
165 struct resolv_response_flags flags)
167 if (b->offset > 0)
168 FAIL_EXIT1 ("response_init: called at offset %zu", b->offset);
169 if (b->query_length < 12)
170 FAIL_EXIT1 ("response_init called for a query of size %zu",
171 b->query_length);
172 if (flags.rcode > 15)
173 FAIL_EXIT1 ("response_init: invalid RCODE %u", flags.rcode);
175 /* Copy the transaction ID. */
176 b->buffer[0] = b->query_buffer[0];
177 b->buffer[1] = b->query_buffer[1];
179 /* Initialize the flags. */
180 b->buffer[2] = 0x80; /* Mark as response. */
181 b->buffer[2] |= b->query_buffer[2] & 0x01; /* Copy the RD bit. */
182 if (flags.tc)
183 b->buffer[2] |= 0x02;
184 b->buffer[3] = 0x80 | flags.rcode; /* Always set RA. */
185 if (flags.ad)
186 b->buffer[3] |= 0x20;
188 /* Fill in the initial section count values. */
189 b->buffer[4] = flags.qdcount >> 8;
190 b->buffer[5] = flags.qdcount;
191 b->buffer[6] = flags.ancount >> 8;
192 b->buffer[7] = flags.ancount;
193 b->buffer[8] = flags.nscount >> 8;
194 b->buffer[9] = flags.nscount;
195 b->buffer[10] = flags.adcount >> 8;
196 b->buffer[11] = flags.adcount;
198 b->offset = 12;
201 void
202 resolv_response_section (struct resolv_response_builder *b, ns_sect section)
204 if (b->offset == 0)
205 FAIL_EXIT1 ("resolv_response_section: response_init not called before");
206 if (section < b->section)
207 FAIL_EXIT1 ("resolv_response_section: cannot go back to previous section");
208 b->section = section;
211 /* Add a single byte to B. */
212 static inline void
213 response_add_byte (struct resolv_response_builder *b, unsigned char ch)
215 if (b->offset == max_response_length)
216 FAIL_EXIT1 ("DNS response exceeds 64 KiB limit");
217 b->buffer[b->offset] = ch;
218 ++b->offset;
221 /* Add a 16-bit word VAL to B, in big-endian format. */
222 static void
223 response_add_16 (struct resolv_response_builder *b, uint16_t val)
225 response_add_byte (b, val >> 8);
226 response_add_byte (b, val);
229 /* Increment the pers-section record counter in the packet header. */
230 static void
231 response_count_increment (struct resolv_response_builder *b)
233 unsigned int offset = b->section;
234 offset = 4 + 2 * offset;
235 ++b->buffer[offset + 1];
236 if (b->buffer[offset + 1] == 0)
238 /* Carry. */
239 ++b->buffer[offset];
240 if (b->buffer[offset] == 0)
241 /* Overflow. */
242 FAIL_EXIT1 ("too many records in section");
246 void
247 resolv_response_add_question (struct resolv_response_builder *b,
248 const char *name, uint16_t class, uint16_t type)
250 if (b->offset == 0)
251 FAIL_EXIT1 ("resolv_response_add_question: "
252 "resolv_response_init not called");
253 if (b->section != ns_s_qd)
254 FAIL_EXIT1 ("resolv_response_add_question: "
255 "must be called in the question section");
257 resolv_response_add_name (b, name);
258 response_add_16 (b, type);
259 response_add_16 (b, class);
261 response_count_increment (b);
264 void
265 resolv_response_add_name (struct resolv_response_builder *b,
266 const char *const origname)
268 unsigned char encoded_name[NS_MAXDNAME];
269 if (ns_name_pton (origname, encoded_name, sizeof (encoded_name)) < 0)
270 FAIL_EXIT1 ("ns_name_pton (\"%s\"): %m", origname);
272 /* Copy the encoded name into the output buffer, apply compression
273 where possible. */
274 for (const unsigned char *name = encoded_name; ;)
276 if (*name == '\0')
278 /* We have reached the end of the name. Add the terminating
279 NUL byte. */
280 response_add_byte (b, '\0');
281 break;
284 /* Set to the compression target if compression is possible. */
285 struct compressed_name *crname_target;
287 /* Compression references can only reach the beginning of the
288 packet. */
289 enum { compression_limit = 1 << 12 };
292 /* The trailing part of the name to be looked up in the tree
293 with the compression targets. */
294 struct compressed_name *crname
295 = allocate_compressed_name (name, b->offset);
297 if (b->offset < compression_limit)
299 /* Add the name to the tree, for future compression
300 references. */
301 void **ptr = tsearch (crname, &b->compression_offsets,
302 compare_compressed_name);
303 if (ptr == NULL)
304 FAIL_EXIT1 ("tsearch out of memory");
305 crname_target = *ptr;
307 if (crname_target != crname)
308 /* The new name was not actually added to the tree.
309 Deallocate it. */
310 free (crname);
311 else
312 /* Signal that the tree did not yet contain the name,
313 but keep the allocation because it is now part of the
314 tree. */
315 crname_target = NULL;
317 else
319 /* This name cannot be reached by a compression reference.
320 No need to add it to the tree for future reference. */
321 void **ptr = tfind (crname, &b->compression_offsets,
322 compare_compressed_name);
323 if (ptr != NULL)
324 crname_target = *ptr;
325 else
326 crname_target = NULL;
327 TEST_VERIFY (crname_target != crname);
328 /* Not added to the tree. */
329 free (crname);
333 if (crname_target != NULL)
335 /* The name is known. Reference the previous location. */
336 unsigned int old_offset = crname_target->offset;
337 TEST_VERIFY_EXIT (old_offset < compression_limit);
338 response_add_byte (b, 0xC0 | (old_offset >> 8));
339 response_add_byte (b, old_offset);
340 break;
342 else
344 /* The name is new. Add this label. */
345 unsigned int len = 1 + *name;
346 resolv_response_add_data (b, name, len);
347 name += len;
352 void
353 resolv_response_open_record (struct resolv_response_builder *b,
354 const char *name,
355 uint16_t class, uint16_t type, uint32_t ttl)
357 if (b->section == ns_s_qd)
358 FAIL_EXIT1 ("resolv_response_open_record called in question section");
359 if (b->current_rdata_offset != 0)
360 FAIL_EXIT1 ("resolv_response_open_record called with open record");
362 resolv_response_add_name (b, name);
363 response_add_16 (b, type);
364 response_add_16 (b, class);
365 response_add_16 (b, ttl >> 16);
366 response_add_16 (b, ttl);
368 b->current_rdata_offset = b->offset;
369 /* Add room for the RDATA length. */
370 response_add_16 (b, 0);
374 void
375 resolv_response_close_record (struct resolv_response_builder *b)
377 size_t rdata_offset = b->current_rdata_offset;
378 if (rdata_offset == 0)
379 FAIL_EXIT1 ("response_close_record called without open record");
380 size_t rdata_length = b->offset - rdata_offset - 2;
381 if (rdata_length > 65535)
382 FAIL_EXIT1 ("RDATA length %zu exceeds limit", rdata_length);
383 b->buffer[rdata_offset] = rdata_length >> 8;
384 b->buffer[rdata_offset + 1] = rdata_length;
385 response_count_increment (b);
386 b->current_rdata_offset = 0;
389 void
390 resolv_response_add_data (struct resolv_response_builder *b,
391 const void *data, size_t length)
393 size_t remaining = max_response_length - b->offset;
394 if (remaining < length)
395 FAIL_EXIT1 ("resolv_response_add_data: not enough room for %zu bytes",
396 length);
397 memcpy (b->buffer + b->offset, data, length);
398 b->offset += length;
401 void
402 resolv_response_drop (struct resolv_response_builder *b)
404 b->drop = true;
407 void
408 resolv_response_close (struct resolv_response_builder *b)
410 b->close = true;
413 void
414 resolv_response_truncate_data (struct resolv_response_builder *b, size_t count)
416 if (count > 65535)
417 FAIL_EXIT1 ("resolv_response_truncate_data: argument too large: %zu",
418 count);
419 b->truncate_bytes = count;
423 size_t
424 resolv_response_length (const struct resolv_response_builder *b)
426 return b->offset;
429 unsigned char *
430 resolv_response_buffer (const struct resolv_response_builder *b)
432 unsigned char *result = xmalloc (b->offset);
433 memcpy (result, b->buffer, b->offset);
434 return result;
437 static struct resolv_response_builder *
438 response_builder_allocate
439 (const unsigned char *query_buffer, size_t query_length)
441 struct resolv_response_builder *b = xmalloc (sizeof (*b));
442 memset (b, 0, offsetof (struct resolv_response_builder, buffer));
443 b->query_buffer = query_buffer;
444 b->query_length = query_length;
445 return b;
448 static void
449 response_builder_free (struct resolv_response_builder *b)
451 tdestroy (b->compression_offsets, free);
452 free (b);
455 /* DNS query processing. */
457 /* Data extracted from the question section of a DNS packet. */
458 struct query_info
460 char qname[MAXDNAME];
461 uint16_t qclass;
462 uint16_t qtype;
463 struct resolv_edns_info edns;
466 /* Update *INFO from the specified DNS packet. */
467 static void
468 parse_query (struct query_info *info,
469 const unsigned char *buffer, size_t length)
471 HEADER hd;
472 _Static_assert (sizeof (hd) == 12, "DNS header size");
473 if (length < sizeof (hd))
474 FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
475 memcpy (&hd, buffer, sizeof (hd));
477 if (ntohs (hd.qdcount) != 1)
478 FAIL_EXIT1 ("malformed DNS query: wrong question count: %d",
479 (int) ntohs (hd.qdcount));
480 if (ntohs (hd.ancount) != 0)
481 FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d",
482 (int) ntohs (hd.ancount));
483 if (ntohs (hd.nscount) != 0)
484 FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d",
485 (int) ntohs (hd.nscount));
486 if (ntohs (hd.arcount) > 1)
487 FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d",
488 (int) ntohs (hd.arcount));
490 int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd),
491 info->qname, sizeof (info->qname));
492 if (ret < 0)
493 FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME");
495 /* Obtain QTYPE and QCLASS. */
496 size_t remaining = length - (12 + ret);
497 struct
499 uint16_t qtype;
500 uint16_t qclass;
501 } qtype_qclass;
502 if (remaining < sizeof (qtype_qclass))
503 FAIL_EXIT1 ("malformed DNS query: "
504 "query lacks QCLASS/QTYPE, QNAME: %s", info->qname);
505 memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass));
506 info->qclass = ntohs (qtype_qclass.qclass);
507 info->qtype = ntohs (qtype_qclass.qtype);
509 memset (&info->edns, 0, sizeof (info->edns));
510 if (ntohs (hd.arcount) > 0)
512 /* Parse EDNS record. */
513 struct __attribute__ ((packed, aligned (1)))
515 uint8_t root;
516 uint16_t rtype;
517 uint16_t payload;
518 uint8_t edns_extended_rcode;
519 uint8_t edns_version;
520 uint16_t flags;
521 uint16_t rdatalen;
522 } rr;
523 _Static_assert (sizeof (rr) == 11, "EDNS record size");
525 if (remaining < 4 + sizeof (rr))
526 FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record");
527 memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr));
528 if (rr.root != 0)
529 FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root);
530 if (rr.rtype != htons (41))
531 FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n",
532 ntohs (rr.rtype));
533 info->edns.active = true;
534 info->edns.extended_rcode = rr.edns_extended_rcode;
535 info->edns.version = rr.edns_version;
536 info->edns.flags = ntohs (rr.flags);
537 info->edns.payload_size = ntohs (rr.payload);
542 /* Main testing framework. */
544 /* Per-server information. One struct is allocated for each test
545 server. */
546 struct resolv_test_server
548 /* Local address of the server. UDP and TCP use the same port. */
549 struct sockaddr_in address;
551 /* File descriptor of the UDP server, or -1 if this server is
552 disabled. */
553 int socket_udp;
555 /* File descriptor of the TCP server, or -1 if this server is
556 disabled. */
557 int socket_tcp;
559 /* Counter of the number of responses processed so far. */
560 size_t response_number;
562 /* Thread handles for the server threads (if not disabled in the
563 configuration). */
564 pthread_t thread_udp;
565 pthread_t thread_tcp;
568 /* Main struct for keeping track of libresolv redirection and
569 testing. */
570 struct resolv_test
572 /* After initialization, any access to the struct must be performed
573 while this lock is acquired. */
574 pthread_mutex_t lock;
576 /* Data for each test server. */
577 struct resolv_test_server servers[resolv_max_test_servers];
579 /* Used if config.single_thread_udp is true. */
580 pthread_t thread_udp_single;
582 struct resolv_redirect_config config;
583 bool termination_requested;
586 /* Function implementing a server thread. */
587 typedef void (*thread_callback) (struct resolv_test *, int server_index);
589 /* Storage for thread-specific data, for passing to the
590 thread_callback function. */
591 struct thread_closure
593 struct resolv_test *obj; /* Current test object. */
594 thread_callback callback; /* Function to call. */
595 int server_index; /* Index of the implemented server. */
598 /* Wrap response_callback as a function which can be passed to
599 pthread_create. */
600 static void *
601 thread_callback_wrapper (void *arg)
603 struct thread_closure *closure = arg;
604 closure->callback (closure->obj, closure->server_index);
605 free (closure);
606 return NULL;
609 /* Start a server thread for the specified SERVER_INDEX, implemented
610 by CALLBACK. */
611 static pthread_t
612 start_server_thread (struct resolv_test *obj, int server_index,
613 thread_callback callback)
615 struct thread_closure *closure = xmalloc (sizeof (*closure));
616 *closure = (struct thread_closure)
618 .obj = obj,
619 .callback = callback,
620 .server_index = server_index,
622 return xpthread_create (NULL, thread_callback_wrapper, closure);
625 /* Process one UDP query. Return false if a termination requested has
626 been detected. */
627 static bool
628 server_thread_udp_process_one (struct resolv_test *obj, int server_index)
630 unsigned char query[512];
631 struct sockaddr_storage peer;
632 socklen_t peerlen = sizeof (peer);
633 size_t length = xrecvfrom (obj->servers[server_index].socket_udp,
634 query, sizeof (query), 0,
635 (struct sockaddr *) &peer, &peerlen);
636 /* Check for termination. */
638 bool termination_requested;
639 xpthread_mutex_lock (&obj->lock);
640 termination_requested = obj->termination_requested;
641 xpthread_mutex_unlock (&obj->lock);
642 if (termination_requested)
643 return false;
647 struct query_info qinfo;
648 parse_query (&qinfo, query, length);
649 if (test_verbose > 0)
651 if (test_verbose > 1)
652 printf ("info: UDP server %d: incoming query:"
653 " %zd bytes, %s/%u/%u, tnxid=0x%02x%02x\n",
654 server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype,
655 query[0], query[1]);
656 else
657 printf ("info: UDP server %d: incoming query:"
658 " %zd bytes, %s/%u/%u\n",
659 server_index, length, qinfo.qname, qinfo.qclass, qinfo.qtype);
662 struct resolv_response_context ctx =
664 .query_buffer = query,
665 .query_length = length,
666 .server_index = server_index,
667 .tcp = false,
668 .edns = qinfo.edns,
670 struct resolv_response_builder *b = response_builder_allocate (query, length);
671 obj->config.response_callback
672 (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype);
674 if (b->drop)
676 if (test_verbose)
677 printf ("info: UDP server %d: dropping response to %s/%u/%u\n",
678 server_index, qinfo.qname, qinfo.qclass, qinfo.qtype);
680 else
682 if (test_verbose)
684 if (b->offset >= 12)
685 printf ("info: UDP server %d: sending response:"
686 " %zu bytes, RCODE %d (for %s/%u/%u)\n",
687 server_index, b->offset, b->buffer[3] & 0x0f,
688 qinfo.qname, qinfo.qclass, qinfo.qtype);
689 else
690 printf ("info: UDP server %d: sending response: %zu bytes"
691 " (for %s/%u/%u)\n",
692 server_index, b->offset,
693 qinfo.qname, qinfo.qclass, qinfo.qtype);
694 if (b->truncate_bytes > 0)
695 printf ("info: truncated by %u bytes\n", b->truncate_bytes);
697 size_t to_send = b->offset;
698 if (to_send < b->truncate_bytes)
699 to_send = 0;
700 else
701 to_send -= b->truncate_bytes;
703 /* Ignore most errors here because the other end may have closed
704 the socket. */
705 if (sendto (obj->servers[server_index].socket_udp,
706 b->buffer, to_send, 0,
707 (struct sockaddr *) &peer, peerlen) < 0)
708 TEST_VERIFY_EXIT (errno != EBADF);
710 response_builder_free (b);
711 return true;
714 /* UDP thread_callback function. Variant for one thread per
715 server. */
716 static void
717 server_thread_udp (struct resolv_test *obj, int server_index)
719 while (server_thread_udp_process_one (obj, server_index))
723 /* Single-threaded UDP processing function, for the single_thread_udp
724 case. */
725 static void *
726 server_thread_udp_single (void *closure)
728 struct resolv_test *obj = closure;
730 struct pollfd fds[resolv_max_test_servers];
731 for (int server_index = 0; server_index < resolv_max_test_servers;
732 ++server_index)
733 if (obj->config.servers[server_index].disable_udp)
734 fds[server_index] = (struct pollfd) {.fd = -1};
735 else
737 fds[server_index] = (struct pollfd)
739 .fd = obj->servers[server_index].socket_udp,
740 .events = POLLIN
743 /* Make the socket non-blocking. */
744 int flags = fcntl (obj->servers[server_index].socket_udp, F_GETFL, 0);
745 if (flags < 0)
746 FAIL_EXIT1 ("fcntl (F_GETFL): %m");
747 flags |= O_NONBLOCK;
748 if (fcntl (obj->servers[server_index].socket_udp, F_SETFL, flags) < 0)
749 FAIL_EXIT1 ("fcntl (F_SETFL): %m");
752 while (true)
754 xpoll (fds, resolv_max_test_servers, -1);
755 for (int server_index = 0; server_index < resolv_max_test_servers;
756 ++server_index)
757 if (fds[server_index].revents != 0)
759 if (!server_thread_udp_process_one (obj, server_index))
760 goto out;
761 fds[server_index].revents = 0;
765 out:
766 return NULL;
769 /* Start the single UDP handler thread (for the single_thread_udp
770 case). */
771 static void
772 start_server_thread_udp_single (struct resolv_test *obj)
774 obj->thread_udp_single
775 = xpthread_create (NULL, server_thread_udp_single, obj);
778 /* Data describing a TCP client connect. */
779 struct tcp_thread_closure
781 struct resolv_test *obj;
782 int server_index;
783 int client_socket;
786 /* Read a complete DNS query packet. If EOF_OK, an immediate
787 end-of-file condition is acceptable. */
788 static bool
789 read_fully (int fd, void *buf, size_t len, bool eof_ok)
791 const void *const end = buf + len;
792 while (buf < end)
794 ssize_t ret = read (fd, buf, end - buf);
795 if (ret == 0)
797 if (!eof_ok)
799 support_record_failure ();
800 printf ("error: unexpected EOF on TCP connection\n");
802 return false;
804 else if (ret < 0)
806 if (!eof_ok || errno != ECONNRESET)
808 support_record_failure ();
809 printf ("error: TCP read: %m\n");
811 return false;
813 buf += ret;
814 eof_ok = false;
816 return true;
819 /* Write an array of iovecs. Terminate the process on failure. */
820 static void
821 writev_fully (int fd, struct iovec *buffers, size_t count)
823 while (count > 0)
825 /* Skip zero-length write requests. */
826 if (buffers->iov_len == 0)
828 ++buffers;
829 --count;
830 continue;
832 /* Try to rewrite the remaing buffers. */
833 ssize_t ret = writev (fd, buffers, count);
834 if (ret < 0)
835 FAIL_EXIT1 ("writev: %m");
836 if (ret == 0)
837 FAIL_EXIT1 ("writev: invalid return value zero");
838 /* Find the buffers that were successfully written. */
839 while (ret > 0)
841 if (count == 0)
842 FAIL_EXIT1 ("internal writev consistency failure");
843 /* Current buffer was partially written. */
844 if (buffers->iov_len > (size_t) ret)
846 buffers->iov_base += ret;
847 buffers->iov_len -= ret;
848 ret = 0;
850 else
852 ret -= buffers->iov_len;
853 buffers->iov_len = 0;
854 ++buffers;
855 --count;
861 /* Thread callback for handling a single established TCP connection to
862 a client. */
863 static void *
864 server_thread_tcp_client (void *arg)
866 struct tcp_thread_closure *closure = arg;
868 while (true)
870 /* Read packet length. */
871 uint16_t query_length;
872 if (!read_fully (closure->client_socket,
873 &query_length, sizeof (query_length), true))
874 break;
875 query_length = ntohs (query_length);
877 /* Read the packet. */
878 unsigned char *query_buffer = xmalloc (query_length);
879 read_fully (closure->client_socket, query_buffer, query_length, false);
881 struct query_info qinfo;
882 parse_query (&qinfo, query_buffer, query_length);
883 if (test_verbose > 0)
885 if (test_verbose > 1)
886 printf ("info: UDP server %d: incoming query:"
887 " %d bytes, %s/%u/%u, tnxid=0x%02x%02x\n",
888 closure->server_index, query_length,
889 qinfo.qname, qinfo.qclass, qinfo.qtype,
890 query_buffer[0], query_buffer[1]);
891 else
892 printf ("info: TCP server %d: incoming query:"
893 " %u bytes, %s/%u/%u\n",
894 closure->server_index, query_length,
895 qinfo.qname, qinfo.qclass, qinfo.qtype);
898 struct resolv_response_context ctx =
900 .query_buffer = query_buffer,
901 .query_length = query_length,
902 .server_index = closure->server_index,
903 .tcp = true,
904 .edns = qinfo.edns,
906 struct resolv_response_builder *b = response_builder_allocate
907 (query_buffer, query_length);
908 closure->obj->config.response_callback
909 (&ctx, b, qinfo.qname, qinfo.qclass, qinfo.qtype);
911 if (b->drop)
913 if (test_verbose)
914 printf ("info: TCP server %d: dropping response to %s/%u/%u\n",
915 closure->server_index,
916 qinfo.qname, qinfo.qclass, qinfo.qtype);
918 else
920 if (test_verbose)
921 printf ("info: TCP server %d: sending response: %zu bytes"
922 " (for %s/%u/%u)\n",
923 closure->server_index, b->offset,
924 qinfo.qname, qinfo.qclass, qinfo.qtype);
925 uint16_t length = htons (b->offset);
926 size_t to_send = b->offset;
927 if (to_send < b->truncate_bytes)
928 to_send = 0;
929 else
930 to_send -= b->truncate_bytes;
931 struct iovec buffers[2] =
933 {&length, sizeof (length)},
934 {b->buffer, to_send}
936 writev_fully (closure->client_socket, buffers, 2);
938 bool close_flag = b->close;
939 response_builder_free (b);
940 free (query_buffer);
941 if (close_flag)
942 break;
945 xclose (closure->client_socket);
946 free (closure);
947 return NULL;
950 /* thread_callback for the TCP case. Accept connections and create a
951 new thread for each client. */
952 static void
953 server_thread_tcp (struct resolv_test *obj, int server_index)
955 while (true)
957 /* Get the client conenction. */
958 int client_socket = xaccept
959 (obj->servers[server_index].socket_tcp, NULL, NULL);
961 /* Check for termination. */
962 xpthread_mutex_lock (&obj->lock);
963 if (obj->termination_requested)
965 xpthread_mutex_unlock (&obj->lock);
966 xclose (client_socket);
967 break;
969 xpthread_mutex_unlock (&obj->lock);
971 /* Spawn a new thread for handling this connection. */
972 struct tcp_thread_closure *closure = xmalloc (sizeof (*closure));
973 *closure = (struct tcp_thread_closure)
975 .obj = obj,
976 .server_index = server_index,
977 .client_socket = client_socket,
980 pthread_t thr
981 = xpthread_create (NULL, server_thread_tcp_client, closure);
982 /* TODO: We should keep track of this thread so that we can
983 block in resolv_test_end until it has exited. */
984 xpthread_detach (thr);
988 /* Create UDP and TCP server sockets. */
989 static void
990 make_server_sockets (struct resolv_test_server *server)
992 while (true)
994 server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
995 server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
997 /* Pick the address for the UDP socket. */
998 server->address = (struct sockaddr_in)
1000 .sin_family = AF_INET,
1001 .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK)}
1003 xbind (server->socket_udp,
1004 (struct sockaddr *)&server->address, sizeof (server->address));
1006 /* Retrieve the address. */
1007 socklen_t addrlen = sizeof (server->address);
1008 xgetsockname (server->socket_udp,
1009 (struct sockaddr *)&server->address, &addrlen);
1011 /* Bind the TCP socket to the same address. */
1013 int on = 1;
1014 xsetsockopt (server->socket_tcp, SOL_SOCKET, SO_REUSEADDR,
1015 &on, sizeof (on));
1017 if (bind (server->socket_tcp,
1018 (struct sockaddr *)&server->address,
1019 sizeof (server->address)) != 0)
1021 /* Port collision. The UDP bind succeeded, but the TCP BIND
1022 failed. We assume here that the kernel will pick the
1023 next local UDP address randomly. */
1024 if (errno == EADDRINUSE)
1026 xclose (server->socket_udp);
1027 xclose (server->socket_tcp);
1028 continue;
1030 FAIL_EXIT1 ("TCP bind: %m");
1032 xlisten (server->socket_tcp, 5);
1033 break;
1037 /* Like make_server_sockets, but the caller supplies the address to
1038 use. */
1039 static void
1040 make_server_sockets_for_address (struct resolv_test_server *server,
1041 const struct sockaddr *addr)
1043 server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1044 server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1046 if (addr->sa_family == AF_INET)
1047 server->address = *(const struct sockaddr_in *) addr;
1048 else
1049 /* We cannot store the server address in the socket. This should
1050 not matter if disable_redirect is used. */
1051 server->address = (struct sockaddr_in) { .sin_family = 0, };
1053 xbind (server->socket_udp,
1054 (struct sockaddr *)&server->address, sizeof (server->address));
1055 xbind (server->socket_tcp,
1056 (struct sockaddr *)&server->address, sizeof (server->address));
1057 xlisten (server->socket_tcp, 5);
1060 /* One-time initialization of NSS. */
1061 static void
1062 resolv_redirect_once (void)
1064 /* Only use nss_dns. */
1065 __nss_configure_lookup ("hosts", "dns");
1066 __nss_configure_lookup ("networks", "dns");
1067 /* Enter a network namespace for isolation and firewall state
1068 cleanup. The tests will still work if these steps fail, but they
1069 may be less reliable. */
1070 support_become_root ();
1071 support_enter_network_namespace ();
1073 pthread_once_t resolv_redirect_once_var = PTHREAD_ONCE_INIT;
1075 void
1076 resolv_test_init (void)
1078 /* Perform one-time initialization of NSS. */
1079 xpthread_once (&resolv_redirect_once_var, resolv_redirect_once);
1082 /* Copy the search path from CONFIG.search to the _res object. */
1083 static void
1084 set_search_path (struct resolv_redirect_config config)
1086 memset (_res.defdname, 0, sizeof (_res.defdname));
1087 memset (_res.dnsrch, 0, sizeof (_res.dnsrch));
1089 char *current = _res.defdname;
1090 char *end = current + sizeof (_res.defdname);
1092 for (unsigned int i = 0;
1093 i < sizeof (config.search) / sizeof (config.search[0]); ++i)
1095 if (config.search[i] == NULL)
1096 continue;
1098 size_t length = strlen (config.search[i]) + 1;
1099 size_t remaining = end - current;
1100 TEST_VERIFY_EXIT (length <= remaining);
1101 memcpy (current, config.search[i], length);
1102 _res.dnsrch[i] = current;
1103 current += length;
1107 struct resolv_test *
1108 resolv_test_start (struct resolv_redirect_config config)
1110 /* Apply configuration defaults. */
1111 if (config.nscount == 0)
1112 config.nscount = resolv_max_test_servers;
1114 struct resolv_test *obj = xmalloc (sizeof (*obj));
1115 *obj = (struct resolv_test) {
1116 .config = config,
1117 .lock = PTHREAD_MUTEX_INITIALIZER,
1120 if (!config.disable_redirect)
1121 resolv_test_init ();
1123 /* Create all the servers, to reserve the necessary ports. */
1124 for (int server_index = 0; server_index < config.nscount; ++server_index)
1125 if (config.disable_redirect && config.server_address_overrides != NULL)
1126 make_server_sockets_for_address
1127 (obj->servers + server_index,
1128 config.server_address_overrides[server_index]);
1129 else
1130 make_server_sockets (obj->servers + server_index);
1132 /* Start server threads. Disable the server ports, as
1133 requested. */
1134 for (int server_index = 0; server_index < config.nscount; ++server_index)
1136 struct resolv_test_server *server = obj->servers + server_index;
1137 if (config.servers[server_index].disable_udp)
1139 xclose (server->socket_udp);
1140 server->socket_udp = -1;
1142 else if (!config.single_thread_udp)
1143 server->thread_udp = start_server_thread (obj, server_index,
1144 server_thread_udp);
1145 if (config.servers[server_index].disable_tcp)
1147 xclose (server->socket_tcp);
1148 server->socket_tcp = -1;
1150 else
1151 server->thread_tcp = start_server_thread (obj, server_index,
1152 server_thread_tcp);
1154 if (config.single_thread_udp)
1155 start_server_thread_udp_single (obj);
1157 if (config.disable_redirect)
1158 return obj;
1160 int timeout = 1;
1162 /* Initialize libresolv. */
1163 TEST_VERIFY_EXIT (res_init () == 0);
1165 /* Disable IPv6 name server addresses. The code below only
1166 overrides the IPv4 addresses. */
1167 __res_iclose (&_res, true);
1168 _res._u._ext.nscount = 0;
1170 /* Redirect queries to the server socket. */
1171 if (test_verbose)
1173 printf ("info: old timeout value: %d\n", _res.retrans);
1174 printf ("info: old retry attempt value: %d\n", _res.retry);
1175 printf ("info: old _res.options: 0x%lx\n", _res.options);
1176 printf ("info: old _res.nscount value: %d\n", _res.nscount);
1177 printf ("info: old _res.ndots value: %d\n", _res.ndots);
1179 _res.retrans = timeout;
1180 _res.retry = 4;
1181 _res.nscount = config.nscount;
1182 _res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
1183 _res.ndots = 1;
1184 if (test_verbose)
1186 printf ("info: new timeout value: %d\n", _res.retrans);
1187 printf ("info: new retry attempt value: %d\n", _res.retry);
1188 printf ("info: new _res.options: 0x%lx\n", _res.options);
1189 printf ("info: new _res.nscount value: %d\n", _res.nscount);
1190 printf ("info: new _res.ndots value: %d\n", _res.ndots);
1192 for (int server_index = 0; server_index < config.nscount; ++server_index)
1194 TEST_VERIFY_EXIT (obj->servers[server_index].address.sin_port != 0);
1195 _res.nsaddr_list[server_index] = obj->servers[server_index].address;
1196 if (test_verbose)
1198 char buf[256];
1199 TEST_VERIFY_EXIT
1200 (inet_ntop (AF_INET, &obj->servers[server_index].address.sin_addr,
1201 buf, sizeof (buf)) != NULL);
1202 printf ("info: server %d: %s/%u\n",
1203 server_index, buf,
1204 htons (obj->servers[server_index].address.sin_port));
1208 set_search_path (config);
1210 return obj;
1213 void
1214 resolv_test_end (struct resolv_test *obj)
1216 res_close ();
1218 xpthread_mutex_lock (&obj->lock);
1219 obj->termination_requested = true;
1220 xpthread_mutex_unlock (&obj->lock);
1222 /* Send trigger packets to unblock the server threads. */
1223 for (int server_index = 0; server_index < obj->config.nscount;
1224 ++server_index)
1226 if (!obj->config.servers[server_index].disable_udp)
1228 int sock = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1229 xsendto (sock, "", 1, 0,
1230 (struct sockaddr *) &obj->servers[server_index].address,
1231 sizeof (obj->servers[server_index].address));
1232 xclose (sock);
1234 if (!obj->config.servers[server_index].disable_tcp)
1236 int sock = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1237 xconnect (sock,
1238 (struct sockaddr *) &obj->servers[server_index].address,
1239 sizeof (obj->servers[server_index].address));
1240 xclose (sock);
1244 if (obj->config.single_thread_udp)
1245 xpthread_join (obj->thread_udp_single);
1247 /* Wait for the server threads to terminate. */
1248 for (int server_index = 0; server_index < obj->config.nscount;
1249 ++server_index)
1251 if (!obj->config.servers[server_index].disable_udp)
1253 if (!obj->config.single_thread_udp)
1254 xpthread_join (obj->servers[server_index].thread_udp);
1255 xclose (obj->servers[server_index].socket_udp);
1257 if (!obj->config.servers[server_index].disable_tcp)
1259 xpthread_join (obj->servers[server_index].thread_tcp);
1260 xclose (obj->servers[server_index].socket_tcp);
1264 free (obj);