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
response(const struct resolv_response_context * ctx,struct resolv_response_builder * b,const char * qname,uint16_t qclass,uint16_t qtype)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, qname, qclass, qtype);
50       return;
51     }
52 
53   struct resolv_response_flags flags = {};
54   resolv_response_init (b, flags);
55   resolv_response_add_question (b, qname, qclass, 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 (&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 (&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, qname, qclass, qtype, 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 ("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, "example", C_IN, T_NS, 0);
109       resolv_response_add_name (b, "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, "ns.example", qclass, qtype, 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 ("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 *
expected_result(unsigned port,unsigned response_number)150 expected_result (unsigned port, unsigned response_number)
151 {
152   struct xmemstream mem;
153   xopen_memstream (&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 (mem.out, "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 (mem.out, "address: STREAM/TCP 2001:db8::%x:%x %u\n",
162            (response_number + 1) & 0xffff,
163            2 + 4 * server_index, port);
164   xfclose_memstream (&mem);
165   return mem.buffer;
166 }
167 
168 static void
test_different_sizes(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 ("www.example", "80", &hints, &ai);
187   expected = expected_result (80, 3);
188   check_addrinfo ("www.example:80", ai, ret, expected);
189   if (ret == 0)
190     freeaddrinfo (ai);
191   free (expected);
192 
193   response_number = 0;
194   ret = getaddrinfo ("www123.example", "80", &hints, &ai);
195   if (ret == 0)
196     freeaddrinfo (ai);
197 
198   response_number = 0;
199   ret = getaddrinfo ("www1234.example", "80", &hints, &ai);
200   if (ret == 0)
201     freeaddrinfo (ai);
202 
203   response_number = 0;
204   ret = getaddrinfo ("www12345.example", "80", &hints, &ai);
205   if (ret == 0)
206     freeaddrinfo (ai);
207 }
208 
209 static int
do_test(void)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