1 /* Test parallel queries with transaction ID collisions.
2 Copyright (C) 2020-2024 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 <arpa/nameser.h>
20 #include <array_length.h>
21 #include <resolv-internal.h>
22 #include <resolv_context.h>
26 #include <support/check.h>
27 #include <support/check_nss.h>
28 #include <support/resolv_test.h>
29 #include <support/support.h>
30 #include <support/test-driver.h>
32 /* Result of parsing a DNS question name.
34 A question name has the form reorder-N-M-rcode-C.example.net, where
35 N and M are either 0 and 1, corresponding to the reorder member,
36 and C is a number that will be stored in the rcode field.
38 Also see parse_qname below. */
41 /* The DNS response code requested from the first server. The
42 second server always responds with RCODE zero. */
45 /* Indicates whether to perform reordering in the responses from the
50 /* Fills *PARSED based on QNAME. */
52 parse_qname (struct parsed_qname
*parsed
, const char *qname
)
58 if (sscanf (qname
, "reorder-%d-%d.rcode-%d.%ms",
59 &reorder0
, &reorder1
, &rcode
, &suffix
) == 4)
62 TEST_COMPARE (reorder0
, 1);
64 TEST_COMPARE (reorder1
, 1);
65 TEST_VERIFY (rcode
>= 0 && rcode
<= 15);
66 TEST_COMPARE_STRING (suffix
, "example.net");
69 parsed
->rcode
= rcode
;
70 parsed
->reorder
[0] = reorder0
;
71 parsed
->reorder
[1] = reorder1
;
74 FAIL_EXIT1 ("unexpected query: %s", qname
);
77 /* Used to construct a response. The first server responds with an
78 error, the second server succeeds. */
80 build_response (const struct resolv_response_context
*ctx
,
81 struct resolv_response_builder
*b
,
82 const char *qname
, uint16_t qclass
, uint16_t qtype
)
84 struct parsed_qname parsed
;
85 parse_qname (&parsed
, qname
);
87 switch (ctx
->server_index
)
91 struct resolv_response_flags flags
= { 0 };
92 if (parsed
.rcode
== 0)
93 /* Simulate a delegation in case a NODATA (RCODE zero)
94 response is requested. */
95 flags
.clear_ra
= true;
97 flags
.rcode
= parsed
.rcode
;
99 resolv_response_init (b
, flags
);
100 resolv_response_add_question (b
, qname
, qclass
, qtype
);
106 struct resolv_response_flags flags
= { 0, };
107 resolv_response_init (b
, flags
);
108 resolv_response_add_question (b
, qname
, qclass
, qtype
);
110 resolv_response_section (b
, ns_s_an
);
111 resolv_response_open_record (b
, qname
, qclass
, qtype
, 0);
114 char ipv4
[4] = { 192, 0, 2, 1 };
115 resolv_response_add_data (b
, &ipv4
, sizeof (ipv4
));
120 = { 0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
121 resolv_response_add_data (b
, &ipv6
, sizeof (ipv6
));
123 resolv_response_close_record (b
);
129 /* Used to reorder responses. */
130 struct resolv_response_context
*previous_query
;
132 /* Used to keep track of the queries received. */
133 static int previous_server_index
= -1;
134 static uint16_t previous_qtype
;
136 /* For each server, buffer the first query and then send both answers
137 to the second query, reordered if requested. */
139 response (const struct resolv_response_context
*ctx
,
140 struct resolv_response_builder
*b
,
141 const char *qname
, uint16_t qclass
, uint16_t qtype
)
143 TEST_VERIFY (qtype
== T_A
|| qtype
== T_AAAA
);
144 if (ctx
->server_index
!= 0)
145 TEST_COMPARE (ctx
->server_index
, 1);
147 struct parsed_qname parsed
;
148 parse_qname (&parsed
, qname
);
150 if (previous_query
== NULL
)
152 /* No buffered query. Record this query and do not send a
154 TEST_COMPARE (previous_qtype
, 0);
155 previous_query
= resolv_response_context_duplicate (ctx
);
156 previous_qtype
= qtype
;
157 resolv_response_drop (b
);
158 previous_server_index
= ctx
->server_index
;
161 printf ("info: buffering first query for: %s\n", qname
);
165 TEST_VERIFY (previous_query
!= 0);
166 TEST_COMPARE (ctx
->server_index
, previous_server_index
);
167 TEST_VERIFY (previous_qtype
!= qtype
); /* Not a duplicate. */
169 /* If reordering, send a response for this query explicitly, and
170 then skip the implicit send. */
171 if (parsed
.reorder
[ctx
->server_index
])
174 printf ("info: sending reordered second response for: %s\n",
176 build_response (ctx
, b
, qname
, qclass
, qtype
);
177 resolv_response_send_udp (ctx
, b
);
178 resolv_response_drop (b
);
181 /* Build a response for the previous query and send it, thus
182 reordering the two responses. */
185 printf ("info: sending first response for: %s\n", qname
);
186 struct resolv_response_builder
*btmp
187 = resolv_response_builder_allocate (previous_query
->query_buffer
,
188 previous_query
->query_length
);
189 build_response (ctx
, btmp
, qname
, qclass
, previous_qtype
);
190 resolv_response_send_udp (ctx
, btmp
);
191 resolv_response_builder_free (btmp
);
194 /* If not reordering, send the reply as usual. */
195 if (!parsed
.reorder
[ctx
->server_index
])
198 printf ("info: sending non-reordered second response for: %s\n",
200 build_response (ctx
, b
, qname
, qclass
, qtype
);
203 /* Unbuffer the response and prepare for the next query. */
204 resolv_response_context_free (previous_query
);
205 previous_query
= NULL
;
207 previous_server_index
= -1;
211 /* Runs a query for QNAME and checks for the expected reply. See
212 struct parsed_qname for the expected format for QNAME. */
214 test_qname (const char *qname
, int rcode
)
216 struct resolv_context
*ctx
= __resolv_context_get ();
217 TEST_VERIFY_EXIT (ctx
!= NULL
);
219 unsigned char q1
[512];
220 int q1len
= res_mkquery (QUERY
, qname
, C_IN
, T_A
, NULL
, 0, NULL
,
222 TEST_VERIFY_EXIT (q1len
> 12);
224 unsigned char q2
[512];
225 int q2len
= res_mkquery (QUERY
, qname
, C_IN
, T_AAAA
, NULL
, 0, NULL
,
227 TEST_VERIFY_EXIT (q2len
> 12);
229 /* Produce a transaction ID collision. */
232 unsigned char ans1
[512];
233 unsigned char *ans1p
= ans1
;
234 unsigned char *ans2p
= NULL
;
237 int ans2p_malloced
= 0;
239 /* Perform a parallel A/AAAA query. */
240 int resplen1
= __res_context_send (ctx
, q1
, q1len
, q2
, q2len
,
241 ans1
, sizeof (ans1
), &ans1p
,
243 &resplen2
, &ans2p_malloced
);
245 TEST_VERIFY (resplen1
> 12);
246 TEST_VERIFY (resplen2
> 12);
247 if (resplen1
<= 12 || resplen2
<= 12)
250 if (rcode
== 1 || rcode
== 3)
252 /* Format Error and Name Error responses does not trigger
253 switching to the next server. */
254 TEST_COMPARE (ans1p
[3] & 0x0f, rcode
);
255 TEST_COMPARE (ans2p
[3] & 0x0f, rcode
);
259 /* The response should be successful. */
260 TEST_COMPARE (ans1p
[3] & 0x0f, 0);
261 TEST_COMPARE (ans2p
[3] & 0x0f, 0);
263 /* Due to bug 19691, the answer may not be in the slot matching the
264 query. Assume that the AAAA response is the longer one. */
265 unsigned char *a_answer
;
267 unsigned char *aaaa_answer
;
268 int aaaa_answer_length
;
269 if (resplen2
> resplen1
)
272 a_answer_length
= resplen1
;
274 aaaa_answer_length
= resplen2
;
279 a_answer_length
= resplen2
;
281 aaaa_answer_length
= resplen1
;
285 char *expected
= xasprintf ("name: %s\n"
286 "address: 192.0.2.1\n",
288 check_dns_packet (qname
, a_answer
, a_answer_length
, expected
);
292 char *expected
= xasprintf ("name: %s\n"
293 "address: 2001:db8::1\n",
295 check_dns_packet (qname
, aaaa_answer
, aaaa_answer_length
, expected
);
302 __resolv_context_put (ctx
);
308 struct resolv_test
*aux
= resolv_test_start
309 ((struct resolv_redirect_config
)
311 .response_callback
= response
,
313 /* The response callback use global state (the previous_*
314 variables), and query processing must therefore be
316 .single_thread_udp
= true,
319 for (int rcode
= 0; rcode
<= 5; ++rcode
)
320 for (int do_reorder_0
= 0; do_reorder_0
< 2; ++do_reorder_0
)
321 for (int do_reorder_1
= 0; do_reorder_1
< 2; ++do_reorder_1
)
323 char *qname
= xasprintf ("reorder-%d-%d.rcode-%d.example.net",
324 do_reorder_0
, do_reorder_1
, rcode
);
325 test_qname (qname
, rcode
);
329 resolv_test_end (aux
);
334 #include <support/test-driver.c>