1 | /* Test __libc_res_nsend buffer mismanagement, basic TCP coverage. |
2 | Copyright (C) 2016-2022 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 <errno.h> |
20 | #include <netdb.h> |
21 | #include <resolv.h> |
22 | #include <stdio.h> |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <support/check.h> |
26 | #include <support/check_nss.h> |
27 | #include <support/resolv_test.h> |
28 | #include <support/xthread.h> |
29 | #include <support/xmemstream.h> |
30 | |
31 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; |
32 | |
33 | static int initial_address_count = 1; |
34 | static int subsequent_address_count = 2000; |
35 | static int response_number = 0; |
36 | |
37 | static void |
38 | response (const struct resolv_response_context *ctx, |
39 | struct resolv_response_builder *b, |
40 | const char *qname, uint16_t qclass, uint16_t qtype) |
41 | { |
42 | TEST_VERIFY_EXIT (qname != NULL); |
43 | |
44 | /* If not using TCP, just force its use. */ |
45 | if (!ctx->tcp) |
46 | { |
47 | struct resolv_response_flags flags = {.tc = true}; |
48 | resolv_response_init (b, flags); |
49 | resolv_response_add_question (b, name: qname, class: qclass, type: qtype); |
50 | return; |
51 | } |
52 | |
53 | struct resolv_response_flags flags = {}; |
54 | resolv_response_init (b, flags); |
55 | resolv_response_add_question (b, name: qname, class: qclass, type: qtype); |
56 | |
57 | resolv_response_section (b, ns_s_an); |
58 | |
59 | /* The number of addresses (in the additional section) for the name |
60 | server record (in the authoritative section). */ |
61 | int address_count; |
62 | xpthread_mutex_lock (mutex: &lock); |
63 | ++response_number; |
64 | if (response_number == 1) |
65 | address_count = initial_address_count; |
66 | else if (response_number == 2) |
67 | { |
68 | address_count = 0; |
69 | resolv_response_drop (b); |
70 | resolv_response_close (b); |
71 | } |
72 | else |
73 | address_count = subsequent_address_count; |
74 | xpthread_mutex_unlock (mutex: &lock); |
75 | |
76 | /* Only add the address record to the answer section if we requested |
77 | any name server addresses. */ |
78 | if (address_count > 0) |
79 | { |
80 | resolv_response_open_record (b, name: qname, class: qclass, type: qtype, ttl: 0); |
81 | switch (qtype) |
82 | { |
83 | case T_A: |
84 | { |
85 | char ipv4[4] = {10, response_number >> 8, response_number, 0}; |
86 | ipv4[3] = 2 * ctx->tcp + 4 * ctx->server_index; |
87 | resolv_response_add_data (b, &ipv4, sizeof (ipv4)); |
88 | } |
89 | break; |
90 | case T_AAAA: |
91 | { |
92 | char ipv6[16] |
93 | = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, |
94 | response_number >> 8, response_number, 0, 0}; |
95 | ipv6[15] = 2 * ctx->tcp + 4 * ctx->server_index; |
96 | resolv_response_add_data (b, &ipv6, sizeof (ipv6)); |
97 | } |
98 | break; |
99 | default: |
100 | support_record_failure (); |
101 | printf (format: "error: unexpected QTYPE: %s/%u/%u\n" , |
102 | qname, qclass, qtype); |
103 | } |
104 | resolv_response_close_record (b); |
105 | |
106 | /* Add the name server record. */ |
107 | resolv_response_section (b, ns_s_ns); |
108 | resolv_response_open_record (b, name: "example" , C_IN, T_NS, ttl: 0); |
109 | resolv_response_add_name (b, name: "ns.example" ); |
110 | resolv_response_close_record (b); |
111 | |
112 | /* Increase the response size with name server addresses. These |
113 | addresses are not copied out of nss_dns, and thus do not |
114 | trigger getaddrinfo retries with a larger buffer, making |
115 | testing more predictable. */ |
116 | resolv_response_section (b, ns_s_ar); |
117 | for (int i = 1; i <= address_count; ++i) |
118 | { |
119 | resolv_response_open_record (b, name: "ns.example" , class: qclass, type: qtype, ttl: 0); |
120 | switch (qtype) |
121 | { |
122 | case T_A: |
123 | { |
124 | char ipv4[4] = {response_number, i >> 8, i, 0}; |
125 | ipv4[3] = 2 * ctx->tcp + 4 * ctx->server_index; |
126 | resolv_response_add_data (b, &ipv4, sizeof (ipv4)); |
127 | } |
128 | break; |
129 | case T_AAAA: |
130 | { |
131 | char ipv6[16] |
132 | = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, |
133 | response_number >> 8, response_number, |
134 | i >> 8, i, 0, 0}; |
135 | ipv6[15] = 2 * ctx->tcp + 4 * ctx->server_index; |
136 | resolv_response_add_data (b, &ipv6, sizeof (ipv6)); |
137 | } |
138 | break; |
139 | default: |
140 | support_record_failure (); |
141 | printf (format: "error: unexpected QTYPE: %s/%u/%u\n" , |
142 | qname, qclass, qtype); |
143 | } |
144 | resolv_response_close_record (b); |
145 | } |
146 | } |
147 | } |
148 | |
149 | static char * |
150 | expected_result (unsigned port, unsigned response_number) |
151 | { |
152 | struct xmemstream mem; |
153 | xopen_memstream (stream: &mem); |
154 | /* We fail the second TCP query to the first server by closing the |
155 | connection immediately, without returning any data. This should |
156 | cause failover to the second server. */ |
157 | int server_index = 1; |
158 | fprintf (stream: mem.out, format: "address: STREAM/TCP 10.%u.%u.%u %u\n" , |
159 | (response_number >> 8) & 0xff, response_number & 0xff, |
160 | 2 + 4 * server_index, port); |
161 | fprintf (stream: mem.out, format: "address: STREAM/TCP 2001:db8::%x:%x %u\n" , |
162 | (response_number + 1) & 0xffff, |
163 | 2 + 4 * server_index, port); |
164 | xfclose_memstream (stream: &mem); |
165 | return mem.buffer; |
166 | } |
167 | |
168 | static void |
169 | test_different_sizes (void) |
170 | { |
171 | struct addrinfo hints = |
172 | { |
173 | .ai_family = AF_UNSPEC, |
174 | .ai_socktype = SOCK_STREAM, |
175 | .ai_protocol = IPPROTO_TCP, |
176 | }; |
177 | struct addrinfo *ai; |
178 | char *expected; |
179 | int ret; |
180 | |
181 | /* This magic number produces a response size close to 2048 |
182 | bytes. */ |
183 | initial_address_count = 124; |
184 | response_number = 0; |
185 | |
186 | ret = getaddrinfo (name: "www.example" , service: "80" , req: &hints, pai: &ai); |
187 | expected = expected_result (port: 80, response_number: 3); |
188 | check_addrinfo (query_description: "www.example:80" , ai, ret, expected); |
189 | if (ret == 0) |
190 | freeaddrinfo (ai: ai); |
191 | free (ptr: expected); |
192 | |
193 | response_number = 0; |
194 | ret = getaddrinfo (name: "www123.example" , service: "80" , req: &hints, pai: &ai); |
195 | if (ret == 0) |
196 | freeaddrinfo (ai: ai); |
197 | |
198 | response_number = 0; |
199 | ret = getaddrinfo (name: "www1234.example" , service: "80" , req: &hints, pai: &ai); |
200 | if (ret == 0) |
201 | freeaddrinfo (ai: ai); |
202 | |
203 | response_number = 0; |
204 | ret = getaddrinfo (name: "www12345.example" , service: "80" , req: &hints, pai: &ai); |
205 | if (ret == 0) |
206 | freeaddrinfo (ai: ai); |
207 | } |
208 | |
209 | static int |
210 | do_test (void) |
211 | { |
212 | struct resolv_test *obj = resolv_test_start |
213 | ((struct resolv_redirect_config) |
214 | { |
215 | .response_callback = response |
216 | }); |
217 | |
218 | test_different_sizes (); |
219 | |
220 | _res.options |= RES_SNGLKUP; |
221 | test_different_sizes (); |
222 | |
223 | _res.options |= RES_SNGLKUPREOP; |
224 | test_different_sizes (); |
225 | |
226 | resolv_test_end (obj); |
227 | return 0; |
228 | } |
229 | |
230 | #include <support/test-driver.c> |
231 | |