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. |
4 | |
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. |
9 | |
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. |
14 | |
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/>. */ |
18 | |
19 | #include <support/resolv_test.h> |
20 | |
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> |
38 | |
39 | /* Response builder. */ |
40 | |
41 | enum |
42 | { |
43 | max_response_length = 65536 |
44 | }; |
45 | |
46 | /* Used for locating domain names containing for the purpose of |
47 | forming compression references. */ |
48 | struct compressed_name |
49 | { |
50 | uint16_t offset; |
51 | unsigned char length; |
52 | unsigned char name[]; /* Without terminating NUL. */ |
53 | }; |
54 | |
55 | static struct compressed_name * |
56 | allocate_compressed_name (const unsigned char *encoded, unsigned int offset) |
57 | { |
58 | /* Compute the length of the domain name. */ |
59 | size_t length; |
60 | { |
61 | const unsigned char *p; |
62 | for (p = encoded; *p != '\0';) |
63 | { |
64 | /* No compression references are allowed. */ |
65 | TEST_VERIFY (*p <= 63); |
66 | /* Skip over the label. */ |
67 | p += 1 + *p; |
68 | } |
69 | length = p - encoded; |
70 | ++length; /* For the terminating NUL byte. */ |
71 | } |
72 | TEST_VERIFY_EXIT (length <= 255); |
73 | |
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; |
80 | } |
81 | |
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) |
86 | { |
87 | if ('A' <= ch && ch <= 'Z') |
88 | return ch - 'A' + 'a'; |
89 | else |
90 | return ch; |
91 | } |
92 | |
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) |
97 | { |
98 | const struct compressed_name *crleft = left; |
99 | const struct compressed_name *crright = right; |
100 | |
101 | if (crleft->length != crright->length) |
102 | /* The operands are converted to int before the subtraction. */ |
103 | return crleft->length - crright->length; |
104 | |
105 | const unsigned char *nameleft = crleft->name; |
106 | const unsigned char *nameright = crright->name; |
107 | |
108 | while (true) |
109 | { |
110 | int lenleft = *nameleft++; |
111 | int lenright = *nameright++; |
112 | |
113 | /* Labels must not e compression references. */ |
114 | TEST_VERIFY (lenleft <= 63); |
115 | TEST_VERIFY (lenright <= 63); |
116 | |
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) |
125 | { |
126 | int l = *nameleft++; |
127 | int r = *nameright++; |
128 | if (l != r) |
129 | { |
130 | l = ascii_tolower (l); |
131 | r = ascii_tolower (r); |
132 | if (l != r) |
133 | return l - r; |
134 | } |
135 | } |
136 | } |
137 | } |
138 | |
139 | struct resolv_response_builder |
140 | { |
141 | const unsigned char *query_buffer; |
142 | size_t query_length; |
143 | |
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. */ |
149 | |
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; |
153 | |
154 | /* tsearch tree for locating targets for label compression. */ |
155 | void *compression_offsets; |
156 | |
157 | /* Must be last. Not zeroed for performance reasons. */ |
158 | unsigned char buffer[max_response_length]; |
159 | }; |
160 | |
161 | /* Response builder. */ |
162 | |
163 | void |
164 | resolv_response_init (struct resolv_response_builder *b, |
165 | struct resolv_response_flags flags) |
166 | { |
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); |
174 | |
175 | /* Copy the transaction ID. */ |
176 | b->buffer[0] = b->query_buffer[0]; |
177 | b->buffer[1] = b->query_buffer[1]; |
178 | |
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; |
187 | |
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; |
197 | |
198 | b->offset = 12; |
199 | } |
200 | |
201 | void |
202 | resolv_response_section (struct resolv_response_builder *b, ns_sect section) |
203 | { |
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; |
209 | } |
210 | |
211 | /* Add a single byte to B. */ |
212 | static inline void |
213 | response_add_byte (struct resolv_response_builder *b, unsigned char ch) |
214 | { |
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; |
219 | } |
220 | |
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) |
224 | { |
225 | response_add_byte (b, val >> 8); |
226 | response_add_byte (b, val); |
227 | } |
228 | |
229 | /* Increment the pers-section record counter in the packet header. */ |
230 | static void |
231 | response_count_increment (struct resolv_response_builder *b) |
232 | { |
233 | unsigned int offset = b->section; |
234 | offset = 4 + 2 * offset; |
235 | ++b->buffer[offset + 1]; |
236 | if (b->buffer[offset + 1] == 0) |
237 | { |
238 | /* Carry. */ |
239 | ++b->buffer[offset]; |
240 | if (b->buffer[offset] == 0) |
241 | /* Overflow. */ |
242 | FAIL_EXIT1 ("too many records in section" ); |
243 | } |
244 | } |
245 | |
246 | void |
247 | resolv_response_add_question (struct resolv_response_builder *b, |
248 | const char *name, uint16_t class, uint16_t type) |
249 | { |
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" ); |
256 | |
257 | resolv_response_add_name (b, name); |
258 | response_add_16 (b, type); |
259 | response_add_16 (b, class); |
260 | |
261 | response_count_increment (b); |
262 | } |
263 | |
264 | void |
265 | resolv_response_add_name (struct resolv_response_builder *b, |
266 | const char *const origname) |
267 | { |
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); |
271 | |
272 | /* Copy the encoded name into the output buffer, apply compression |
273 | where possible. */ |
274 | for (const unsigned char *name = encoded_name; ;) |
275 | { |
276 | if (*name == '\0') |
277 | { |
278 | /* We have reached the end of the name. Add the terminating |
279 | NUL byte. */ |
280 | response_add_byte (b, '\0'); |
281 | break; |
282 | } |
283 | |
284 | /* Set to the compression target if compression is possible. */ |
285 | struct compressed_name *crname_target; |
286 | |
287 | /* Compression references can only reach the beginning of the |
288 | packet. */ |
289 | enum { compression_limit = 1 << 12 }; |
290 | |
291 | { |
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); |
296 | |
297 | if (b->offset < compression_limit) |
298 | { |
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; |
306 | |
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; |
316 | } |
317 | else |
318 | { |
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); |
330 | } |
331 | } |
332 | |
333 | if (crname_target != NULL) |
334 | { |
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; |
341 | } |
342 | else |
343 | { |
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; |
348 | } |
349 | } |
350 | } |
351 | |
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) |
356 | { |
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" ); |
361 | |
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); |
367 | |
368 | b->current_rdata_offset = b->offset; |
369 | /* Add room for the RDATA length. */ |
370 | response_add_16 (b, 0); |
371 | } |
372 | |
373 | |
374 | void |
375 | resolv_response_close_record (struct resolv_response_builder *b) |
376 | { |
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; |
387 | } |
388 | |
389 | void |
390 | resolv_response_add_data (struct resolv_response_builder *b, |
391 | const void *data, size_t length) |
392 | { |
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; |
399 | } |
400 | |
401 | void |
402 | resolv_response_drop (struct resolv_response_builder *b) |
403 | { |
404 | b->drop = true; |
405 | } |
406 | |
407 | void |
408 | resolv_response_close (struct resolv_response_builder *b) |
409 | { |
410 | b->close = true; |
411 | } |
412 | |
413 | void |
414 | resolv_response_truncate_data (struct resolv_response_builder *b, size_t count) |
415 | { |
416 | if (count > 65535) |
417 | FAIL_EXIT1 ("resolv_response_truncate_data: argument too large: %zu" , |
418 | count); |
419 | b->truncate_bytes = count; |
420 | } |
421 | |
422 | |
423 | size_t |
424 | resolv_response_length (const struct resolv_response_builder *b) |
425 | { |
426 | return b->offset; |
427 | } |
428 | |
429 | unsigned char * |
430 | resolv_response_buffer (const struct resolv_response_builder *b) |
431 | { |
432 | unsigned char *result = xmalloc (b->offset); |
433 | memcpy (result, b->buffer, b->offset); |
434 | return result; |
435 | } |
436 | |
437 | static struct resolv_response_builder * |
438 | response_builder_allocate |
439 | (const unsigned char *query_buffer, size_t query_length) |
440 | { |
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; |
446 | } |
447 | |
448 | static void |
449 | response_builder_free (struct resolv_response_builder *b) |
450 | { |
451 | tdestroy (b->compression_offsets, free); |
452 | free (b); |
453 | } |
454 | |
455 | /* DNS query processing. */ |
456 | |
457 | /* Data extracted from the question section of a DNS packet. */ |
458 | struct query_info |
459 | { |
460 | char qname[MAXDNAME]; |
461 | uint16_t qclass; |
462 | uint16_t qtype; |
463 | struct resolv_edns_info edns; |
464 | }; |
465 | |
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) |
470 | { |
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)); |
476 | |
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)); |
489 | |
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" ); |
494 | |
495 | /* Obtain QTYPE and QCLASS. */ |
496 | size_t remaining = length - (12 + ret); |
497 | struct |
498 | { |
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); |
508 | |
509 | memset (&info->edns, 0, sizeof (info->edns)); |
510 | if (ntohs (hd.arcount) > 0) |
511 | { |
512 | /* Parse EDNS record. */ |
513 | struct __attribute__ ((packed, aligned (1))) |
514 | { |
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" ); |
524 | |
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); |
538 | } |
539 | } |
540 | |
541 | |
542 | /* Main testing framework. */ |
543 | |
544 | /* Per-server information. One struct is allocated for each test |
545 | server. */ |
546 | struct resolv_test_server |
547 | { |
548 | /* Local address of the server. UDP and TCP use the same port. */ |
549 | struct sockaddr_in address; |
550 | |
551 | /* File descriptor of the UDP server, or -1 if this server is |
552 | disabled. */ |
553 | int socket_udp; |
554 | |
555 | /* File descriptor of the TCP server, or -1 if this server is |
556 | disabled. */ |
557 | int socket_tcp; |
558 | |
559 | /* Counter of the number of responses processed so far. */ |
560 | size_t response_number; |
561 | |
562 | /* Thread handles for the server threads (if not disabled in the |
563 | configuration). */ |
564 | pthread_t thread_udp; |
565 | pthread_t thread_tcp; |
566 | }; |
567 | |
568 | /* Main struct for keeping track of libresolv redirection and |
569 | testing. */ |
570 | struct resolv_test |
571 | { |
572 | /* After initialization, any access to the struct must be performed |
573 | while this lock is acquired. */ |
574 | pthread_mutex_t lock; |
575 | |
576 | /* Data for each test server. */ |
577 | struct resolv_test_server servers[resolv_max_test_servers]; |
578 | |
579 | /* Used if config.single_thread_udp is true. */ |
580 | pthread_t thread_udp_single; |
581 | |
582 | struct resolv_redirect_config config; |
583 | bool termination_requested; |
584 | }; |
585 | |
586 | /* Function implementing a server thread. */ |
587 | typedef void (*thread_callback) (struct resolv_test *, int server_index); |
588 | |
589 | /* Storage for thread-specific data, for passing to the |
590 | thread_callback function. */ |
591 | struct thread_closure |
592 | { |
593 | struct resolv_test *obj; /* Current test object. */ |
594 | thread_callback callback; /* Function to call. */ |
595 | int server_index; /* Index of the implemented server. */ |
596 | }; |
597 | |
598 | /* Wrap response_callback as a function which can be passed to |
599 | pthread_create. */ |
600 | static void * |
601 | thread_callback_wrapper (void *arg) |
602 | { |
603 | struct thread_closure *closure = arg; |
604 | closure->callback (closure->obj, closure->server_index); |
605 | free (closure); |
606 | return NULL; |
607 | } |
608 | |
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) |
614 | { |
615 | struct thread_closure *closure = xmalloc (sizeof (*closure)); |
616 | *closure = (struct thread_closure) |
617 | { |
618 | .obj = obj, |
619 | .callback = callback, |
620 | .server_index = server_index, |
621 | }; |
622 | return xpthread_create (NULL, thread_callback_wrapper, closure); |
623 | } |
624 | |
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) |
629 | { |
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. */ |
637 | { |
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; |
644 | } |
645 | |
646 | |
647 | struct query_info qinfo; |
648 | parse_query (&qinfo, query, length); |
649 | if (test_verbose > 0) |
650 | { |
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); |
660 | } |
661 | |
662 | struct resolv_response_context ctx = |
663 | { |
664 | .query_buffer = query, |
665 | .query_length = length, |
666 | .server_index = server_index, |
667 | .tcp = false, |
668 | .edns = qinfo.edns, |
669 | }; |
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); |
673 | |
674 | if (b->drop) |
675 | { |
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); |
679 | } |
680 | else |
681 | { |
682 | if (test_verbose) |
683 | { |
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); |
696 | } |
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; |
702 | |
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); |
709 | } |
710 | response_builder_free (b); |
711 | return true; |
712 | } |
713 | |
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) |
718 | { |
719 | while (server_thread_udp_process_one (obj, server_index)) |
720 | ; |
721 | } |
722 | |
723 | /* Single-threaded UDP processing function, for the single_thread_udp |
724 | case. */ |
725 | static void * |
726 | server_thread_udp_single (void *closure) |
727 | { |
728 | struct resolv_test *obj = closure; |
729 | |
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 |
736 | { |
737 | fds[server_index] = (struct pollfd) |
738 | { |
739 | .fd = obj->servers[server_index].socket_udp, |
740 | .events = POLLIN |
741 | }; |
742 | |
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" ); |
750 | } |
751 | |
752 | while (true) |
753 | { |
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) |
758 | { |
759 | if (!server_thread_udp_process_one (obj, server_index)) |
760 | goto out; |
761 | fds[server_index].revents = 0; |
762 | } |
763 | } |
764 | |
765 | out: |
766 | return NULL; |
767 | } |
768 | |
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) |
773 | { |
774 | obj->thread_udp_single |
775 | = xpthread_create (NULL, server_thread_udp_single, obj); |
776 | } |
777 | |
778 | /* Data describing a TCP client connect. */ |
779 | struct tcp_thread_closure |
780 | { |
781 | struct resolv_test *obj; |
782 | int server_index; |
783 | int client_socket; |
784 | }; |
785 | |
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) |
790 | { |
791 | const void *const end = buf + len; |
792 | while (buf < end) |
793 | { |
794 | ssize_t ret = read (fd, buf, end - buf); |
795 | if (ret == 0) |
796 | { |
797 | if (!eof_ok) |
798 | { |
799 | support_record_failure (); |
800 | printf ("error: unexpected EOF on TCP connection\n" ); |
801 | } |
802 | return false; |
803 | } |
804 | else if (ret < 0) |
805 | { |
806 | if (!eof_ok || errno != ECONNRESET) |
807 | { |
808 | support_record_failure (); |
809 | printf ("error: TCP read: %m\n" ); |
810 | } |
811 | return false; |
812 | } |
813 | buf += ret; |
814 | eof_ok = false; |
815 | } |
816 | return true; |
817 | } |
818 | |
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) |
822 | { |
823 | while (count > 0) |
824 | { |
825 | /* Skip zero-length write requests. */ |
826 | if (buffers->iov_len == 0) |
827 | { |
828 | ++buffers; |
829 | --count; |
830 | continue; |
831 | } |
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) |
840 | { |
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) |
845 | { |
846 | buffers->iov_base += ret; |
847 | buffers->iov_len -= ret; |
848 | ret = 0; |
849 | } |
850 | else |
851 | { |
852 | ret -= buffers->iov_len; |
853 | buffers->iov_len = 0; |
854 | ++buffers; |
855 | --count; |
856 | } |
857 | } |
858 | } |
859 | } |
860 | |
861 | /* Thread callback for handling a single established TCP connection to |
862 | a client. */ |
863 | static void * |
864 | server_thread_tcp_client (void *arg) |
865 | { |
866 | struct tcp_thread_closure *closure = arg; |
867 | |
868 | while (true) |
869 | { |
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); |
876 | |
877 | /* Read the packet. */ |
878 | unsigned char *query_buffer = xmalloc (query_length); |
879 | read_fully (closure->client_socket, query_buffer, query_length, false); |
880 | |
881 | struct query_info qinfo; |
882 | parse_query (&qinfo, query_buffer, query_length); |
883 | if (test_verbose > 0) |
884 | { |
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); |
896 | } |
897 | |
898 | struct resolv_response_context ctx = |
899 | { |
900 | .query_buffer = query_buffer, |
901 | .query_length = query_length, |
902 | .server_index = closure->server_index, |
903 | .tcp = true, |
904 | .edns = qinfo.edns, |
905 | }; |
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); |
910 | |
911 | if (b->drop) |
912 | { |
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); |
917 | } |
918 | else |
919 | { |
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] = |
932 | { |
933 | {&length, sizeof (length)}, |
934 | {b->buffer, to_send} |
935 | }; |
936 | writev_fully (closure->client_socket, buffers, 2); |
937 | } |
938 | bool close_flag = b->close; |
939 | response_builder_free (b); |
940 | free (query_buffer); |
941 | if (close_flag) |
942 | break; |
943 | } |
944 | |
945 | xclose (closure->client_socket); |
946 | free (closure); |
947 | return NULL; |
948 | } |
949 | |
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) |
954 | { |
955 | while (true) |
956 | { |
957 | /* Get the client conenction. */ |
958 | int client_socket = xaccept |
959 | (obj->servers[server_index].socket_tcp, NULL, NULL); |
960 | |
961 | /* Check for termination. */ |
962 | xpthread_mutex_lock (&obj->lock); |
963 | if (obj->termination_requested) |
964 | { |
965 | xpthread_mutex_unlock (&obj->lock); |
966 | xclose (client_socket); |
967 | break; |
968 | } |
969 | xpthread_mutex_unlock (&obj->lock); |
970 | |
971 | /* Spawn a new thread for handling this connection. */ |
972 | struct tcp_thread_closure *closure = xmalloc (sizeof (*closure)); |
973 | *closure = (struct tcp_thread_closure) |
974 | { |
975 | .obj = obj, |
976 | .server_index = server_index, |
977 | .client_socket = client_socket, |
978 | }; |
979 | |
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); |
985 | } |
986 | } |
987 | |
988 | /* Create UDP and TCP server sockets. */ |
989 | static void |
990 | make_server_sockets (struct resolv_test_server *server) |
991 | { |
992 | while (true) |
993 | { |
994 | server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
995 | server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
996 | |
997 | /* Pick the address for the UDP socket. */ |
998 | server->address = (struct sockaddr_in) |
999 | { |
1000 | .sin_family = AF_INET, |
1001 | .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK)} |
1002 | }; |
1003 | xbind (server->socket_udp, |
1004 | (struct sockaddr *)&server->address, sizeof (server->address)); |
1005 | |
1006 | /* Retrieve the address. */ |
1007 | socklen_t addrlen = sizeof (server->address); |
1008 | xgetsockname (server->socket_udp, |
1009 | (struct sockaddr *)&server->address, &addrlen); |
1010 | |
1011 | /* Bind the TCP socket to the same address. */ |
1012 | { |
1013 | int on = 1; |
1014 | xsetsockopt (server->socket_tcp, SOL_SOCKET, SO_REUSEADDR, |
1015 | &on, sizeof (on)); |
1016 | } |
1017 | if (bind (server->socket_tcp, |
1018 | (struct sockaddr *)&server->address, |
1019 | sizeof (server->address)) != 0) |
1020 | { |
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) |
1025 | { |
1026 | xclose (server->socket_udp); |
1027 | xclose (server->socket_tcp); |
1028 | continue; |
1029 | } |
1030 | FAIL_EXIT1 ("TCP bind: %m" ); |
1031 | } |
1032 | xlisten (server->socket_tcp, 5); |
1033 | break; |
1034 | } |
1035 | } |
1036 | |
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) |
1042 | { |
1043 | server->socket_udp = xsocket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); |
1044 | server->socket_tcp = xsocket (AF_INET, SOCK_STREAM, IPPROTO_TCP); |
1045 | |
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, }; |
1052 | |
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); |
1058 | } |
1059 | |
1060 | /* One-time initialization of NSS. */ |
1061 | static void |
1062 | resolv_redirect_once (void) |
1063 | { |
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 (); |
1072 | } |
1073 | pthread_once_t resolv_redirect_once_var = PTHREAD_ONCE_INIT; |
1074 | |
1075 | void |
1076 | resolv_test_init (void) |
1077 | { |
1078 | /* Perform one-time initialization of NSS. */ |
1079 | xpthread_once (&resolv_redirect_once_var, resolv_redirect_once); |
1080 | } |
1081 | |
1082 | /* Copy the search path from CONFIG.search to the _res object. */ |
1083 | static void |
1084 | set_search_path (struct resolv_redirect_config config) |
1085 | { |
1086 | memset (_res.defdname, 0, sizeof (_res.defdname)); |
1087 | memset (_res.dnsrch, 0, sizeof (_res.dnsrch)); |
1088 | |
1089 | char *current = _res.defdname; |
1090 | char *end = current + sizeof (_res.defdname); |
1091 | |
1092 | for (unsigned int i = 0; |
1093 | i < sizeof (config.search) / sizeof (config.search[0]); ++i) |
1094 | { |
1095 | if (config.search[i] == NULL) |
1096 | continue; |
1097 | |
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; |
1104 | } |
1105 | } |
1106 | |
1107 | struct resolv_test * |
1108 | resolv_test_start (struct resolv_redirect_config config) |
1109 | { |
1110 | /* Apply configuration defaults. */ |
1111 | if (config.nscount == 0) |
1112 | config.nscount = resolv_max_test_servers; |
1113 | |
1114 | struct resolv_test *obj = xmalloc (sizeof (*obj)); |
1115 | *obj = (struct resolv_test) { |
1116 | .config = config, |
1117 | .lock = PTHREAD_MUTEX_INITIALIZER, |
1118 | }; |
1119 | |
1120 | if (!config.disable_redirect) |
1121 | resolv_test_init (); |
1122 | |
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); |
1131 | |
1132 | /* Start server threads. Disable the server ports, as |
1133 | requested. */ |
1134 | for (int server_index = 0; server_index < config.nscount; ++server_index) |
1135 | { |
1136 | struct resolv_test_server *server = obj->servers + server_index; |
1137 | if (config.servers[server_index].disable_udp) |
1138 | { |
1139 | xclose (server->socket_udp); |
1140 | server->socket_udp = -1; |
1141 | } |
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) |
1146 | { |
1147 | xclose (server->socket_tcp); |
1148 | server->socket_tcp = -1; |
1149 | } |
1150 | else |
1151 | server->thread_tcp = start_server_thread (obj, server_index, |
1152 | server_thread_tcp); |
1153 | } |
1154 | if (config.single_thread_udp) |
1155 | start_server_thread_udp_single (obj); |
1156 | |
1157 | if (config.disable_redirect) |
1158 | return obj; |
1159 | |
1160 | int timeout = 1; |
1161 | |
1162 | /* Initialize libresolv. */ |
1163 | TEST_VERIFY_EXIT (res_init () == 0); |
1164 | |
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; |
1169 | |
1170 | /* Redirect queries to the server socket. */ |
1171 | if (test_verbose) |
1172 | { |
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); |
1178 | } |
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) |
1185 | { |
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); |
1191 | } |
1192 | for (int server_index = 0; server_index < config.nscount; ++server_index) |
1193 | { |
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) |
1197 | { |
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)); |
1205 | } |
1206 | } |
1207 | |
1208 | set_search_path (config); |
1209 | |
1210 | return obj; |
1211 | } |
1212 | |
1213 | void |
1214 | resolv_test_end (struct resolv_test *obj) |
1215 | { |
1216 | res_close (); |
1217 | |
1218 | xpthread_mutex_lock (&obj->lock); |
1219 | obj->termination_requested = true; |
1220 | xpthread_mutex_unlock (&obj->lock); |
1221 | |
1222 | /* Send trigger packets to unblock the server threads. */ |
1223 | for (int server_index = 0; server_index < obj->config.nscount; |
1224 | ++server_index) |
1225 | { |
1226 | if (!obj->config.servers[server_index].disable_udp) |
1227 | { |
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); |
1233 | } |
1234 | if (!obj->config.servers[server_index].disable_tcp) |
1235 | { |
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); |
1241 | } |
1242 | } |
1243 | |
1244 | if (obj->config.single_thread_udp) |
1245 | xpthread_join (obj->thread_udp_single); |
1246 | |
1247 | /* Wait for the server threads to terminate. */ |
1248 | for (int server_index = 0; server_index < obj->config.nscount; |
1249 | ++server_index) |
1250 | { |
1251 | if (!obj->config.servers[server_index].disable_udp) |
1252 | { |
1253 | if (!obj->config.single_thread_udp) |
1254 | xpthread_join (obj->servers[server_index].thread_udp); |
1255 | xclose (obj->servers[server_index].socket_udp); |
1256 | } |
1257 | if (!obj->config.servers[server_index].disable_tcp) |
1258 | { |
1259 | xpthread_join (obj->servers[server_index].thread_tcp); |
1260 | xclose (obj->servers[server_index].socket_tcp); |
1261 | } |
1262 | } |
1263 | |
1264 | free (obj); |
1265 | } |
1266 | |