mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 20:40:05 +00:00
439 lines
14 KiB
C
439 lines
14 KiB
C
/* Test ns_name-related functions.
|
|
Copyright (C) 2017-2024 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
/* This test program processes the tst-ns_name.data file. */
|
|
|
|
#include <ctype.h>
|
|
#include <resolv.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <support/check.h>
|
|
#include <support/support.h>
|
|
#include <support/xstdio.h>
|
|
|
|
/* A byte buffer and its length. */
|
|
struct buffer
|
|
{
|
|
unsigned char *data;
|
|
size_t length;
|
|
};
|
|
|
|
/* Convert a base64-encoded string to its binary representation. */
|
|
static bool
|
|
base64_to_buffer (const char *base64, struct buffer *result)
|
|
{
|
|
/* "-" denotes an empty input. */
|
|
if (strcmp (base64, "-") == 0)
|
|
{
|
|
result->data = xmalloc (1);
|
|
result->length = 0;
|
|
return true;
|
|
}
|
|
|
|
size_t size = strlen (base64);
|
|
unsigned char *data = xmalloc (size);
|
|
int ret = b64_pton (base64, data, size);
|
|
if (ret < 0 || ret > size)
|
|
return false;
|
|
result->data = xrealloc (data, ret);
|
|
result->length = ret;
|
|
return true;
|
|
}
|
|
|
|
/* A test case for ns_name_unpack and ns_name_ntop. */
|
|
struct test_case
|
|
{
|
|
char *path;
|
|
size_t lineno;
|
|
struct buffer input;
|
|
size_t input_offset;
|
|
int unpack_result;
|
|
struct buffer unpack_output;
|
|
int ntop_result;
|
|
char *ntop_text;
|
|
};
|
|
|
|
/* Deallocate the buffers associated with the test case. */
|
|
static void
|
|
free_test_case (struct test_case *t)
|
|
{
|
|
free (t->path);
|
|
free (t->input.data);
|
|
free (t->unpack_output.data);
|
|
free (t->ntop_text);
|
|
}
|
|
|
|
/* Extract the test case information from a test file line. */
|
|
static bool
|
|
parse_test_case (const char *path, size_t lineno, const char *line,
|
|
struct test_case *result)
|
|
{
|
|
memset (result, 0, sizeof (*result));
|
|
result->path = xstrdup (path);
|
|
result->lineno = lineno;
|
|
result->ntop_result = -1;
|
|
char *input = NULL;
|
|
char *unpack_output = NULL;
|
|
int ret = sscanf (line, "%ms %zu %d %ms %d %ms",
|
|
&input, &result->input_offset,
|
|
&result->unpack_result, &unpack_output,
|
|
&result->ntop_result, &result->ntop_text);
|
|
if (ret < 3)
|
|
{
|
|
printf ("%s:%zu: error: missing input fields\n", path, lineno);
|
|
free (input);
|
|
return false;
|
|
}
|
|
if (!base64_to_buffer (input, &result->input))
|
|
{
|
|
printf ("%s:%zu: error: malformed base64 input data\n", path, lineno);
|
|
free (input);
|
|
free (unpack_output);
|
|
free (result->ntop_text);
|
|
return false;
|
|
}
|
|
free (input);
|
|
|
|
if (unpack_output == NULL)
|
|
result->unpack_output = (struct buffer) { NULL, 0 };
|
|
else if (!base64_to_buffer (unpack_output, &result->unpack_output))
|
|
{
|
|
printf ("%s:%zu: error: malformed base64 unpack data\n", path, lineno);
|
|
free (result->input.data);
|
|
free (unpack_output);
|
|
free (result->ntop_text);
|
|
return false;
|
|
}
|
|
free (unpack_output);
|
|
|
|
/* At this point, all allocated buffers have been transferred to
|
|
*result. */
|
|
|
|
if (result->input_offset > result->input.length)
|
|
{
|
|
printf ("%s:%zu: error: input offset %zu exceeds buffer size %zu\n",
|
|
path, lineno, result->input_offset, result->input.length);
|
|
free_test_case (result);
|
|
return false;
|
|
}
|
|
if (result->unpack_result < -1)
|
|
{
|
|
printf ("%s:%zu: error: invalid unpack result %d\n",
|
|
path, lineno, result->unpack_result);
|
|
free_test_case (result);
|
|
return false;
|
|
}
|
|
if (result->ntop_result < -1)
|
|
{
|
|
printf ("%s:%zu: error: invalid ntop result %d\n",
|
|
path, lineno, result->ntop_result);
|
|
free_test_case (result);
|
|
return false;
|
|
}
|
|
|
|
bool fields_consistent;
|
|
switch (ret)
|
|
{
|
|
case 3:
|
|
fields_consistent = result->unpack_result == -1;
|
|
break;
|
|
case 5:
|
|
fields_consistent = result->unpack_result != -1
|
|
&& result->ntop_result == -1;
|
|
break;
|
|
case 6:
|
|
fields_consistent = result->unpack_result != -1
|
|
&& result->ntop_result != -1;
|
|
break;
|
|
default:
|
|
fields_consistent = false;
|
|
}
|
|
if (!fields_consistent)
|
|
{
|
|
printf ("%s:%zu: error: wrong number of fields: %d\n",
|
|
path, lineno, ret);
|
|
free_test_case (result);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Format the buffer as a hexadecimal string and write it to standard
|
|
output. */
|
|
static void
|
|
print_hex (const char *label, struct buffer buffer)
|
|
{
|
|
printf (" %s ", label);
|
|
unsigned char *p = buffer.data;
|
|
unsigned char *end = p + buffer.length;
|
|
while (p < end)
|
|
{
|
|
printf ("%02X", *p & 0xFF);
|
|
++p;
|
|
}
|
|
putchar ('\n');
|
|
}
|
|
|
|
/* Run the test case specified in *T. */
|
|
static void
|
|
run_test_case (struct test_case *t)
|
|
{
|
|
/* Test ns_name_unpack. */
|
|
unsigned char *unpacked = xmalloc (NS_MAXCDNAME);
|
|
int consumed = ns_name_unpack
|
|
(t->input.data, t->input.data + t->input.length,
|
|
t->input.data + t->input_offset,
|
|
unpacked, NS_MAXCDNAME);
|
|
if (consumed != t->unpack_result)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong result from ns_name_unpack\n"
|
|
" expected: %d\n"
|
|
" actual: %d\n",
|
|
t->path, t->lineno, t->unpack_result, consumed);
|
|
return;
|
|
}
|
|
if (consumed != -1)
|
|
{
|
|
if (memcmp (unpacked, t->unpack_output.data,
|
|
t->unpack_output.length) != 0)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong data from ns_name_unpack\n",
|
|
t->path, t->lineno);
|
|
print_hex ("expected:", t->unpack_output);
|
|
print_hex ("actual: ",
|
|
(struct buffer) { unpacked, t->unpack_output.length });
|
|
return;
|
|
}
|
|
|
|
/* Test ns_name_ntop. */
|
|
char *text = xmalloc (NS_MAXDNAME);
|
|
int ret = ns_name_ntop (unpacked, text, NS_MAXDNAME);
|
|
if (ret != t->ntop_result)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong result from ns_name_top\n"
|
|
" expected: %d\n"
|
|
" actual: %d\n",
|
|
t->path, t->lineno, t->ntop_result, ret);
|
|
return;
|
|
}
|
|
if (ret != -1)
|
|
{
|
|
if (strcmp (text, t->ntop_text) != 0)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong data from ns_name_ntop\n",
|
|
t->path, t->lineno);
|
|
printf (" expected: \"%s\"\n", t->ntop_text);
|
|
printf (" actual: \"%s\"\n", text);
|
|
return;
|
|
}
|
|
|
|
/* Test ns_name_pton. Unpacking does not check the
|
|
NS_MAXCDNAME limit, but packing does, so we need to
|
|
adjust the expected result. */
|
|
int expected;
|
|
if (t->unpack_output.length > NS_MAXCDNAME)
|
|
expected = -1;
|
|
else if (strcmp (text, ".") == 0)
|
|
/* The root domain is fully qualified. */
|
|
expected = 1;
|
|
else
|
|
/* The domain name is never fully qualified. */
|
|
expected = 0;
|
|
unsigned char *repacked = xmalloc (NS_MAXCDNAME);
|
|
ret = ns_name_pton (text, repacked, NS_MAXCDNAME);
|
|
if (ret != expected)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong result from ns_name_pton\n"
|
|
" expected: %d\n"
|
|
" actual: %d\n",
|
|
t->path, t->lineno, expected, ret);
|
|
return;
|
|
}
|
|
if (ret >= 0
|
|
&& memcmp (repacked, unpacked, t->unpack_output.length) != 0)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong data from ns_name_pton\n",
|
|
t->path, t->lineno);
|
|
print_hex ("expected:", t->unpack_output);
|
|
print_hex ("actual: ",
|
|
(struct buffer) { repacked, t->unpack_output.length });
|
|
return;
|
|
}
|
|
|
|
/* Test ns_name_compress, no compression case. */
|
|
if (t->unpack_output.length > NS_MAXCDNAME)
|
|
expected = -1;
|
|
else
|
|
expected = t->unpack_output.length;
|
|
memset (repacked, '$', NS_MAXCDNAME);
|
|
{
|
|
enum { ptr_count = 5 };
|
|
const unsigned char *dnptrs[ptr_count] = { repacked, };
|
|
ret = ns_name_compress (text, repacked, NS_MAXCDNAME,
|
|
dnptrs, dnptrs + ptr_count);
|
|
if (ret != expected)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong result from ns_name_compress\n"
|
|
" expected: %d\n"
|
|
" actual: %d\n",
|
|
t->path, t->lineno, expected, ret);
|
|
return;
|
|
}
|
|
if (ret < 0)
|
|
{
|
|
TEST_VERIFY (dnptrs[0] == repacked);
|
|
TEST_VERIFY (dnptrs[1] == NULL);
|
|
}
|
|
else
|
|
{
|
|
if (memcmp (repacked, unpacked, t->unpack_output.length) != 0)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong data from ns_name_compress\n",
|
|
t->path, t->lineno);
|
|
print_hex ("expected:", t->unpack_output);
|
|
print_hex ("actual: ", (struct buffer) { repacked, ret });
|
|
return;
|
|
}
|
|
TEST_VERIFY (dnptrs[0] == repacked);
|
|
if (unpacked[0] == '\0')
|
|
/* The root domain is not a compression target. */
|
|
TEST_VERIFY (dnptrs[1] == NULL);
|
|
else
|
|
{
|
|
TEST_VERIFY (dnptrs[1] == repacked);
|
|
TEST_VERIFY (dnptrs[2] == NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Test ns_name_compress, full compression case. Skip this
|
|
test for invalid names and the root domain. */
|
|
if (expected >= 0 && unpacked[0] != '\0')
|
|
{
|
|
/* The destination buffer needs additional room for the
|
|
offset, the initial name, and the compression
|
|
reference. */
|
|
enum { name_offset = 259 };
|
|
size_t target_offset = name_offset + t->unpack_output.length;
|
|
size_t repacked_size = target_offset + 2;
|
|
repacked = xrealloc (repacked, repacked_size);
|
|
memset (repacked, '@', repacked_size);
|
|
memcpy (repacked + name_offset,
|
|
t->unpack_output.data, t->unpack_output.length);
|
|
enum { ptr_count = 5 };
|
|
const unsigned char *dnptrs[ptr_count]
|
|
= { repacked, repacked + name_offset, };
|
|
ret = ns_name_compress
|
|
(text, repacked + target_offset, NS_MAXCDNAME,
|
|
dnptrs, dnptrs + ptr_count);
|
|
if (ret != 2)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong result from ns_name_compress"
|
|
" (2)\n"
|
|
" expected: 2\n"
|
|
" actual: %d\n",
|
|
t->path, t->lineno, ret);
|
|
return;
|
|
}
|
|
if (memcmp (repacked + target_offset, "\xc1\x03", 2) != 0)
|
|
{
|
|
support_record_failure ();
|
|
printf ("%s:%zu: error: wrong data from ns_name_compress"
|
|
" (2)\n"
|
|
" expected: C103\n",
|
|
t->path, t->lineno);
|
|
print_hex ("actual: ",
|
|
(struct buffer) { repacked + target_offset, ret });
|
|
return;
|
|
}
|
|
TEST_VERIFY (dnptrs[0] == repacked);
|
|
TEST_VERIFY (dnptrs[1] == repacked + name_offset);
|
|
TEST_VERIFY (dnptrs[2] == NULL);
|
|
}
|
|
|
|
free (repacked);
|
|
}
|
|
free (text);
|
|
}
|
|
free (unpacked);
|
|
}
|
|
|
|
/* Open the file at PATH, parse the test cases contained in it, and
|
|
run them. */
|
|
static void
|
|
run_test_file (const char *path)
|
|
{
|
|
FILE *fp = xfopen (path, "re");
|
|
char *line = NULL;
|
|
size_t line_allocated = 0;
|
|
size_t lineno = 0;
|
|
|
|
while (true)
|
|
{
|
|
ssize_t ret = getline (&line, &line_allocated, fp);
|
|
if (ret < 0)
|
|
{
|
|
if (ferror (fp))
|
|
{
|
|
printf ("%s: error reading file: %m\n", path);
|
|
exit (1);
|
|
}
|
|
TEST_VERIFY (feof (fp));
|
|
break;
|
|
}
|
|
|
|
++lineno;
|
|
char *p = line;
|
|
while (isspace (*p))
|
|
++p;
|
|
if (*p == '\0' || *p == '#')
|
|
continue;
|
|
|
|
struct test_case test_case;
|
|
if (!parse_test_case (path, lineno, line, &test_case))
|
|
{
|
|
support_record_failure ();
|
|
continue;
|
|
}
|
|
run_test_case (&test_case);
|
|
free_test_case (&test_case);
|
|
}
|
|
free (line);
|
|
xfclose (fp);
|
|
}
|
|
|
|
static int
|
|
do_test (void)
|
|
{
|
|
run_test_file ("tst-ns_name.data");
|
|
return 0;
|
|
}
|
|
|
|
#include <support/test-driver.c>
|