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
31static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
32
33static int initial_address_count = 1;
34static int subsequent_address_count = 2000;
35static int response_number = 0;
36
37static void
38response (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
149static char *
150expected_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
168static void
169test_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
209static int
210do_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

source code of glibc/resolv/tst-bug18665-tcp.c