1 | /* Test the behavior of the trust-ad option. |
2 | Copyright (C) 2019-2024 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 <resolv.h> |
20 | #include <stdlib.h> |
21 | #include <string.h> |
22 | #include <support/check.h> |
23 | #include <support/check_nss.h> |
24 | #include <support/resolv_test.h> |
25 | #include <support/support.h> |
26 | |
27 | /* This controls properties of the response. volatile because |
28 | __res_send is incorrectly declared as __THROW. */ |
29 | static volatile unsigned char response_number; |
30 | static volatile bool response_ad_bit; |
31 | static volatile bool query_ad_bit; |
32 | |
33 | static void |
34 | response (const struct resolv_response_context *ctx, |
35 | struct resolv_response_builder *b, |
36 | const char *qname, uint16_t qclass, uint16_t qtype) |
37 | { |
38 | TEST_COMPARE (qclass, C_IN); |
39 | TEST_COMPARE (qtype, T_A); |
40 | TEST_COMPARE_STRING (qname, "www.example" ); |
41 | |
42 | HEADER ; |
43 | memcpy (dest: &header, src: ctx->query_buffer, n: sizeof (header)); |
44 | TEST_COMPARE (header.ad, query_ad_bit); |
45 | |
46 | struct resolv_response_flags flags = { .ad = response_ad_bit, }; |
47 | resolv_response_init (b, flags); |
48 | resolv_response_add_question (b, name: qname, class: qclass, type: qtype); |
49 | resolv_response_section (b, ns_s_an); |
50 | resolv_response_open_record (b, name: qname, class: qclass, T_A, ttl: 0x12345678); |
51 | char addr[4] = { 192, 0, 2, response_number }; |
52 | resolv_response_add_data (b, addr, sizeof (addr)); |
53 | resolv_response_close_record (b); |
54 | } |
55 | |
56 | static void |
57 | check_answer (const unsigned char *buffer, size_t buffer_length, |
58 | bool expected_ad) |
59 | { |
60 | HEADER ; |
61 | TEST_VERIFY (buffer_length > sizeof (header)); |
62 | memcpy (dest: &header, src: buffer, n: sizeof (header)); |
63 | TEST_COMPARE (0, header.aa); |
64 | TEST_COMPARE (expected_ad, header.ad); |
65 | TEST_COMPARE (0, header.opcode); |
66 | TEST_COMPARE (1, header.qr); |
67 | TEST_COMPARE (0, header.rcode); |
68 | TEST_COMPARE (1, header.rd); |
69 | TEST_COMPARE (0, header.tc); |
70 | TEST_COMPARE (1, ntohs (header.qdcount)); |
71 | TEST_COMPARE (1, ntohs (header.ancount)); |
72 | TEST_COMPARE (0, ntohs (header.nscount)); |
73 | TEST_COMPARE (0, ntohs (header.arcount)); |
74 | |
75 | char *description = xasprintf (format: "response=%d ad=%d" , |
76 | response_number, expected_ad); |
77 | char *expected = xasprintf (format: "name: www.example\n" |
78 | "address: 192.0.2.%d\n" , response_number); |
79 | check_dns_packet (query_description: description, buffer, buffer_length, expected); |
80 | free (ptr: expected); |
81 | free (ptr: description); |
82 | } |
83 | |
84 | static int |
85 | do_test (void) |
86 | { |
87 | struct resolv_test *aux = resolv_test_start |
88 | ((struct resolv_redirect_config) |
89 | { |
90 | .response_callback = response, |
91 | }); |
92 | |
93 | /* By default, the resolver is not trusted, and the AD bit is |
94 | cleared. */ |
95 | |
96 | static const unsigned char hand_crafted_query[] = |
97 | { |
98 | 10, 11, /* Transaction ID. */ |
99 | 1, 0x20, /* Query with RD, AD flags. */ |
100 | 0, 1, /* One question. */ |
101 | 0, 0, 0, 0, 0, 0, /* The other sections are empty. */ |
102 | 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0, |
103 | 0, T_A, /* A query. */ |
104 | 0, 1, /* Class IN. */ |
105 | }; |
106 | |
107 | ++response_number; |
108 | response_ad_bit = false; |
109 | |
110 | unsigned char buffer[512]; |
111 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
112 | query_ad_bit = true; |
113 | int ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), |
114 | buffer, sizeof (buffer)); |
115 | TEST_VERIFY (ret > 0); |
116 | check_answer (buffer, buffer_length: ret, false); |
117 | |
118 | ++response_number; |
119 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
120 | query_ad_bit = false; |
121 | ret = res_query ("www.example" , C_IN, T_A, buffer, sizeof (buffer)); |
122 | TEST_VERIFY (ret > 0); |
123 | check_answer (buffer, buffer_length: ret, false); |
124 | response_ad_bit = true; |
125 | |
126 | response_ad_bit = true; |
127 | |
128 | ++response_number; |
129 | query_ad_bit = true; |
130 | ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), |
131 | buffer, sizeof (buffer)); |
132 | TEST_VERIFY (ret > 0); |
133 | check_answer (buffer, buffer_length: ret, false); |
134 | |
135 | ++response_number; |
136 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
137 | query_ad_bit = false; |
138 | ret = res_query ("www.example" , C_IN, T_A, buffer, sizeof (buffer)); |
139 | TEST_VERIFY (ret > 0); |
140 | check_answer (buffer, buffer_length: ret, false); |
141 | |
142 | /* No AD bit set in generated queries. */ |
143 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
144 | ret = res_mkquery (QUERY, "www.example" , C_IN, T_A, |
145 | (const unsigned char *) "" , 0, NULL, |
146 | buffer, sizeof (buffer)); |
147 | HEADER ; |
148 | memcpy (dest: &header, src: buffer, n: sizeof (header)); |
149 | TEST_VERIFY (!header.ad); |
150 | |
151 | /* With RES_TRUSTAD, the AD bit is passed through if it set in the |
152 | response. It is also included in queries. */ |
153 | |
154 | _res.options |= RES_TRUSTAD; |
155 | query_ad_bit = true; |
156 | |
157 | response_ad_bit = false; |
158 | |
159 | ++response_number; |
160 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
161 | ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), |
162 | buffer, sizeof (buffer)); |
163 | TEST_VERIFY (ret > 0); |
164 | check_answer (buffer, buffer_length: ret, false); |
165 | |
166 | ++response_number; |
167 | memset (s: buffer, c: 255, n: sizeof (buffer)); |
168 | ret = res_query ("www.example" , C_IN, T_A, buffer, sizeof (buffer)); |
169 | TEST_VERIFY (ret > 0); |
170 | check_answer (buffer, buffer_length: ret, false); |
171 | |
172 | response_ad_bit = true; |
173 | |
174 | ++response_number; |
175 | memset (s: buffer, c: 0, n: sizeof (buffer)); |
176 | ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), |
177 | buffer, sizeof (buffer)); |
178 | TEST_VERIFY (ret > 0); |
179 | check_answer (buffer, buffer_length: ret, true); |
180 | |
181 | ++response_number; |
182 | memset (s: buffer, c: 0, n: sizeof (buffer)); |
183 | ret = res_query ("www.example" , C_IN, T_A, buffer, sizeof (buffer)); |
184 | TEST_VERIFY (ret > 0); |
185 | check_answer (buffer, buffer_length: ret, true); |
186 | |
187 | /* AD bit set in generated queries. */ |
188 | memset (s: buffer, c: 0, n: sizeof (buffer)); |
189 | ret = res_mkquery (QUERY, "www.example" , C_IN, T_A, |
190 | (const unsigned char *) "" , 0, NULL, |
191 | buffer, sizeof (buffer)); |
192 | memcpy (dest: &header, src: buffer, n: sizeof (header)); |
193 | TEST_VERIFY (header.ad); |
194 | |
195 | resolv_test_end (aux); |
196 | |
197 | return 0; |
198 | } |
199 | |
200 | #include <support/test-driver.c> |
201 | |