1/* Test ns_name-related functions.
2 Copyright (C) 2017-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/* This test program processes the tst-ns_name.data file. */
20
21#include <ctype.h>
22#include <resolv.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <support/check.h>
28#include <support/support.h>
29#include <support/xstdio.h>
30
31/* A byte buffer and its length. */
32struct buffer
33{
34 unsigned char *data;
35 size_t length;
36};
37
38/* Convert a base64-encoded string to its binary representation. */
39static bool
40base64_to_buffer (const char *base64, struct buffer *result)
41{
42 /* "-" denotes an empty input. */
43 if (strcmp (s1: base64, s2: "-") == 0)
44 {
45 result->data = xmalloc (n: 1);
46 result->length = 0;
47 return true;
48 }
49
50 size_t size = strlen (s: base64);
51 unsigned char *data = xmalloc (n: size);
52 int ret = b64_pton (base64, data, size);
53 if (ret < 0 || ret > size)
54 return false;
55 result->data = xrealloc (o: data, n: ret);
56 result->length = ret;
57 return true;
58}
59
60/* A test case for ns_name_unpack and ns_name_ntop. */
61struct test_case
62{
63 char *path;
64 size_t lineno;
65 struct buffer input;
66 size_t input_offset;
67 int unpack_result;
68 struct buffer unpack_output;
69 int ntop_result;
70 char *ntop_text;
71};
72
73/* Deallocate the buffers associated with the test case. */
74static void
75free_test_case (struct test_case *t)
76{
77 free (ptr: t->path);
78 free (ptr: t->input.data);
79 free (ptr: t->unpack_output.data);
80 free (ptr: t->ntop_text);
81}
82
83/* Extract the test case information from a test file line. */
84static bool
85parse_test_case (const char *path, size_t lineno, const char *line,
86 struct test_case *result)
87{
88 memset (s: result, c: 0, n: sizeof (*result));
89 result->path = xstrdup (path);
90 result->lineno = lineno;
91 result->ntop_result = -1;
92 char *input = NULL;
93 char *unpack_output = NULL;
94 int ret = sscanf (s: line, format: "%ms %zu %d %ms %d %ms",
95 &input, &result->input_offset,
96 &result->unpack_result, &unpack_output,
97 &result->ntop_result, &result->ntop_text);
98 if (ret < 3)
99 {
100 printf (format: "%s:%zu: error: missing input fields\n", path, lineno);
101 free (ptr: input);
102 return false;
103 }
104 if (!base64_to_buffer (base64: input, result: &result->input))
105 {
106 printf (format: "%s:%zu: error: malformed base64 input data\n", path, lineno);
107 free (ptr: input);
108 free (ptr: unpack_output);
109 free (ptr: result->ntop_text);
110 return false;
111 }
112 free (ptr: input);
113
114 if (unpack_output == NULL)
115 result->unpack_output = (struct buffer) { NULL, 0 };
116 else if (!base64_to_buffer (base64: unpack_output, result: &result->unpack_output))
117 {
118 printf (format: "%s:%zu: error: malformed base64 unpack data\n", path, lineno);
119 free (ptr: result->input.data);
120 free (ptr: unpack_output);
121 free (ptr: result->ntop_text);
122 return false;
123 }
124 free (ptr: unpack_output);
125
126 /* At this point, all allocated buffers have been transferred to
127 *result. */
128
129 if (result->input_offset > result->input.length)
130 {
131 printf (format: "%s:%zu: error: input offset %zu exceeds buffer size %zu\n",
132 path, lineno, result->input_offset, result->input.length);
133 free_test_case (t: result);
134 return false;
135 }
136 if (result->unpack_result < -1)
137 {
138 printf (format: "%s:%zu: error: invalid unpack result %d\n",
139 path, lineno, result->unpack_result);
140 free_test_case (t: result);
141 return false;
142 }
143 if (result->ntop_result < -1)
144 {
145 printf (format: "%s:%zu: error: invalid ntop result %d\n",
146 path, lineno, result->ntop_result);
147 free_test_case (t: result);
148 return false;
149 }
150
151 bool fields_consistent;
152 switch (ret)
153 {
154 case 3:
155 fields_consistent = result->unpack_result == -1;
156 break;
157 case 5:
158 fields_consistent = result->unpack_result != -1
159 && result->ntop_result == -1;
160 break;
161 case 6:
162 fields_consistent = result->unpack_result != -1
163 && result->ntop_result != -1;
164 break;
165 default:
166 fields_consistent = false;
167 }
168 if (!fields_consistent)
169 {
170 printf (format: "%s:%zu: error: wrong number of fields: %d\n",
171 path, lineno, ret);
172 free_test_case (t: result);
173 return false;
174 }
175 return true;
176}
177
178/* Format the buffer as a hexadecimal string and write it to standard
179 output. */
180static void
181print_hex (const char *label, struct buffer buffer)
182{
183 printf (format: " %s ", label);
184 unsigned char *p = buffer.data;
185 unsigned char *end = p + buffer.length;
186 while (p < end)
187 {
188 printf (format: "%02X", *p & 0xFF);
189 ++p;
190 }
191 putchar (c: '\n');
192}
193
194/* Run the test case specified in *T. */
195static void
196run_test_case (struct test_case *t)
197{
198 /* Test ns_name_unpack. */
199 unsigned char *unpacked = xmalloc (NS_MAXCDNAME);
200 int consumed = ns_name_unpack
201 (t->input.data, t->input.data + t->input.length,
202 t->input.data + t->input_offset,
203 unpacked, NS_MAXCDNAME);
204 if (consumed != t->unpack_result)
205 {
206 support_record_failure ();
207 printf (format: "%s:%zu: error: wrong result from ns_name_unpack\n"
208 " expected: %d\n"
209 " actual: %d\n",
210 t->path, t->lineno, t->unpack_result, consumed);
211 return;
212 }
213 if (consumed != -1)
214 {
215 if (memcmp (s1: unpacked, s2: t->unpack_output.data,
216 n: t->unpack_output.length) != 0)
217 {
218 support_record_failure ();
219 printf (format: "%s:%zu: error: wrong data from ns_name_unpack\n",
220 t->path, t->lineno);
221 print_hex (label: "expected:", buffer: t->unpack_output);
222 print_hex (label: "actual: ",
223 buffer: (struct buffer) { unpacked, t->unpack_output.length });
224 return;
225 }
226
227 /* Test ns_name_ntop. */
228 char *text = xmalloc (NS_MAXDNAME);
229 int ret = ns_name_ntop (unpacked, text, NS_MAXDNAME);
230 if (ret != t->ntop_result)
231 {
232 support_record_failure ();
233 printf (format: "%s:%zu: error: wrong result from ns_name_top\n"
234 " expected: %d\n"
235 " actual: %d\n",
236 t->path, t->lineno, t->ntop_result, ret);
237 return;
238 }
239 if (ret != -1)
240 {
241 if (strcmp (s1: text, s2: t->ntop_text) != 0)
242 {
243 support_record_failure ();
244 printf (format: "%s:%zu: error: wrong data from ns_name_ntop\n",
245 t->path, t->lineno);
246 printf (format: " expected: \"%s\"\n", t->ntop_text);
247 printf (format: " actual: \"%s\"\n", text);
248 return;
249 }
250
251 /* Test ns_name_pton. Unpacking does not check the
252 NS_MAXCDNAME limit, but packing does, so we need to
253 adjust the expected result. */
254 int expected;
255 if (t->unpack_output.length > NS_MAXCDNAME)
256 expected = -1;
257 else if (strcmp (s1: text, s2: ".") == 0)
258 /* The root domain is fully qualified. */
259 expected = 1;
260 else
261 /* The domain name is never fully qualified. */
262 expected = 0;
263 unsigned char *repacked = xmalloc (NS_MAXCDNAME);
264 ret = ns_name_pton (text, repacked, NS_MAXCDNAME);
265 if (ret != expected)
266 {
267 support_record_failure ();
268 printf (format: "%s:%zu: error: wrong result from ns_name_pton\n"
269 " expected: %d\n"
270 " actual: %d\n",
271 t->path, t->lineno, expected, ret);
272 return;
273 }
274 if (ret >= 0
275 && memcmp (s1: repacked, s2: unpacked, n: t->unpack_output.length) != 0)
276 {
277 support_record_failure ();
278 printf (format: "%s:%zu: error: wrong data from ns_name_pton\n",
279 t->path, t->lineno);
280 print_hex (label: "expected:", buffer: t->unpack_output);
281 print_hex (label: "actual: ",
282 buffer: (struct buffer) { repacked, t->unpack_output.length });
283 return;
284 }
285
286 /* Test ns_name_compress, no compression case. */
287 if (t->unpack_output.length > NS_MAXCDNAME)
288 expected = -1;
289 else
290 expected = t->unpack_output.length;
291 memset (s: repacked, c: '$', NS_MAXCDNAME);
292 {
293 enum { ptr_count = 5 };
294 const unsigned char *dnptrs[ptr_count] = { repacked, };
295 ret = ns_name_compress (text, repacked, NS_MAXCDNAME,
296 dnptrs, dnptrs + ptr_count);
297 if (ret != expected)
298 {
299 support_record_failure ();
300 printf (format: "%s:%zu: error: wrong result from ns_name_compress\n"
301 " expected: %d\n"
302 " actual: %d\n",
303 t->path, t->lineno, expected, ret);
304 return;
305 }
306 if (ret < 0)
307 {
308 TEST_VERIFY (dnptrs[0] == repacked);
309 TEST_VERIFY (dnptrs[1] == NULL);
310 }
311 else
312 {
313 if (memcmp (s1: repacked, s2: unpacked, n: t->unpack_output.length) != 0)
314 {
315 support_record_failure ();
316 printf (format: "%s:%zu: error: wrong data from ns_name_compress\n",
317 t->path, t->lineno);
318 print_hex (label: "expected:", buffer: t->unpack_output);
319 print_hex (label: "actual: ", buffer: (struct buffer) { repacked, ret });
320 return;
321 }
322 TEST_VERIFY (dnptrs[0] == repacked);
323 if (unpacked[0] == '\0')
324 /* The root domain is not a compression target. */
325 TEST_VERIFY (dnptrs[1] == NULL);
326 else
327 {
328 TEST_VERIFY (dnptrs[1] == repacked);
329 TEST_VERIFY (dnptrs[2] == NULL);
330 }
331 }
332 }
333
334 /* Test ns_name_compress, full compression case. Skip this
335 test for invalid names and the root domain. */
336 if (expected >= 0 && unpacked[0] != '\0')
337 {
338 /* The destination buffer needs additional room for the
339 offset, the initial name, and the compression
340 reference. */
341 enum { name_offset = 259 };
342 size_t target_offset = name_offset + t->unpack_output.length;
343 size_t repacked_size = target_offset + 2;
344 repacked = xrealloc (o: repacked, n: repacked_size);
345 memset (s: repacked, c: '@', n: repacked_size);
346 memcpy (dest: repacked + name_offset,
347 src: t->unpack_output.data, n: t->unpack_output.length);
348 enum { ptr_count = 5 };
349 const unsigned char *dnptrs[ptr_count]
350 = { repacked, repacked + name_offset, };
351 ret = ns_name_compress
352 (text, repacked + target_offset, NS_MAXCDNAME,
353 dnptrs, dnptrs + ptr_count);
354 if (ret != 2)
355 {
356 support_record_failure ();
357 printf (format: "%s:%zu: error: wrong result from ns_name_compress"
358 " (2)\n"
359 " expected: 2\n"
360 " actual: %d\n",
361 t->path, t->lineno, ret);
362 return;
363 }
364 if (memcmp (s1: repacked + target_offset, s2: "\xc1\x03", n: 2) != 0)
365 {
366 support_record_failure ();
367 printf (format: "%s:%zu: error: wrong data from ns_name_compress"
368 " (2)\n"
369 " expected: C103\n",
370 t->path, t->lineno);
371 print_hex (label: "actual: ",
372 buffer: (struct buffer) { repacked + target_offset, ret });
373 return;
374 }
375 TEST_VERIFY (dnptrs[0] == repacked);
376 TEST_VERIFY (dnptrs[1] == repacked + name_offset);
377 TEST_VERIFY (dnptrs[2] == NULL);
378 }
379
380 free (ptr: repacked);
381 }
382 free (ptr: text);
383 }
384 free (ptr: unpacked);
385}
386
387/* Open the file at PATH, parse the test cases contained in it, and
388 run them. */
389static void
390run_test_file (const char *path)
391{
392 FILE *fp = xfopen (path, mode: "re");
393 char *line = NULL;
394 size_t line_allocated = 0;
395 size_t lineno = 0;
396
397 while (true)
398 {
399 ssize_t ret = getline (lineptr: &line, n: &line_allocated, stream: fp);
400 if (ret < 0)
401 {
402 if (ferror (stream: fp))
403 {
404 printf (format: "%s: error reading file: %m\n", path);
405 exit (status: 1);
406 }
407 TEST_VERIFY (feof (fp));
408 break;
409 }
410
411 ++lineno;
412 char *p = line;
413 while (isspace (*p))
414 ++p;
415 if (*p == '\0' || *p == '#')
416 continue;
417
418 struct test_case test_case;
419 if (!parse_test_case (path, lineno, line, result: &test_case))
420 {
421 support_record_failure ();
422 continue;
423 }
424 run_test_case (t: &test_case);
425 free_test_case (t: &test_case);
426 }
427 free (ptr: line);
428 xfclose (fp);
429}
430
431static int
432do_test (void)
433{
434 run_test_file (path: "tst-ns_name.data");
435 return 0;
436}
437
438#include <support/test-driver.c>
439

source code of glibc/resolv/tst-ns_name.c