#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <mach/boolean.h>
#include "DNSNameList.h"
#ifdef TEST_DNSNAMELIST
#include "util.h"
#endif TEST_DNSNAMELIST
#define DNS_PTR_PATTERN_BYTE_MASK (uint8_t)0xc0
#define DNS_PTR_PATTERN_MASK (uint16_t)0xc000
#define DNS_PTR_VALUE_MASK (uint16_t)(~DNS_PTR_PATTERN_MASK)
#define DNS_LABEL_LENGTH_MAX 63
#define DNS_BUF_INITIAL_SIZE 128
typedef struct {
uint8_t db_buf_s[DNS_BUF_INITIAL_SIZE];
bool db_buf_user_supplied;
uint8_t * db_buf;
int db_buf_used;
int db_buf_size;
} DNSBuf, * DNSBufRef;
#define DNS_NAME_OFFSETS_N_START 8
typedef struct {
uint32_t dno_list_s[DNS_NAME_OFFSETS_N_START];
uint32_t * dno_list;
int dno_count;
int dno_size;
} DNSNameOffsets, * DNSNameOffsetsRef;
#define DNS_NAME_OFFSETS_LIST_N_START 8
typedef struct {
DNSNameOffsetsRef * dnl_list;
int dnl_count;
int dnl_size;
} DNSNameOffsetsList, * DNSNameOffsetsListRef;
typedef struct {
DNSNameOffsetsRef dn_offsets;
uint8_t * dn_buf;
int dn_buf_size;
} DNSName, * DNSNameRef;
typedef struct {
DNSBuf dnb_buf;
DNSNameOffsetsList dnb_names;
} DNSNamesBuf, * DNSNamesBufRef;
static void
DNSBufInit(DNSBufRef db, uint8_t * buf, int buf_size)
{
bzero(db, sizeof(*db));
if (buf != NULL) {
db->db_buf_user_supplied = TRUE;
db->db_buf = buf;
db->db_buf_size = buf_size;
}
else {
db->db_buf = db->db_buf_s;
db->db_buf_size = sizeof(db->db_buf_s);
}
return;
}
static void
DNSBufFreeElements(DNSBufRef db)
{
if (db->db_buf_user_supplied == FALSE) {
if (db->db_buf != NULL && db->db_buf != db->db_buf_s) {
free(db->db_buf);
}
}
bzero(db, sizeof(*db));
return;
}
static __inline__ void
DNSBufSetUsed(DNSBufRef db, int used)
{
if (db->db_buf_used < 0 || db->db_buf_used > db->db_buf_size) {
fprintf(stderr, "trying to set used to %d\n", used);
return;
}
db->db_buf_used = used;
return;
}
static __inline__ int
DNSBufUsed(DNSBufRef db)
{
return (db->db_buf_used);
}
static __inline__ uint8_t *
DNSBufBuffer(DNSBufRef db)
{
return (db->db_buf);
}
static bool
DNSBufAddData(DNSBufRef db, const void * data, int data_len)
{
#ifdef DEBUG
printf("Adding: ");
print_data((void *)data, data_len);
#endif DEBUG
if (data_len > (db->db_buf_size - db->db_buf_used)) {
if (db->db_buf_user_supplied) {
fprintf(stderr,
"user-supplied buffer failed to add data with"
" length %d (> %d)\n",
data_len, (db->db_buf_size - db->db_buf_used));
return (FALSE);
}
#ifdef DEBUG
printf("Buffer growing from %d to %d\n", db->db_buf_size,
db->db_buf_size + ((data_len > DNS_BUF_INITIAL_SIZE)
? data_len : DNS_BUF_INITIAL_SIZE));
#endif DEBUG
db->db_buf_size += (data_len > DNS_BUF_INITIAL_SIZE)
? data_len : DNS_BUF_INITIAL_SIZE;
if (db->db_buf == db->db_buf_s) {
db->db_buf = malloc(db->db_buf_size);
memcpy(db->db_buf, db->db_buf_s, db->db_buf_used);
}
else {
db->db_buf = reallocf(db->db_buf, db->db_buf_size);
}
}
memcpy(db->db_buf + db->db_buf_used, data, data_len);
db->db_buf_used += data_len;
return (TRUE);
}
static void
DNSNameOffsetsInit(DNSNameOffsetsRef list)
{
bzero(list, sizeof(*list));
list->dno_size = DNS_NAME_OFFSETS_N_START;
list->dno_list = list->dno_list_s;
return;
}
static DNSNameOffsetsRef
DNSNameOffsetsCreate(void)
{
DNSNameOffsetsRef list;
list = malloc(sizeof(*list));
if (list != NULL) {
DNSNameOffsetsInit(list);
}
return (list);
}
static bool
DNSNameOffsetsContainsOffset(DNSNameOffsetsRef list, uint32_t off)
{
int i;
for (i = 0; i < list->dno_count; i++) {
if (list->dno_list[i] == off) {
return (TRUE);
}
}
return (FALSE);
}
static void
DNSNameOffsetsFreeElements(DNSNameOffsetsRef list)
{
if (list->dno_list != NULL && list->dno_list != list->dno_list_s) {
free(list->dno_list);
}
DNSNameOffsetsInit(list);
return;
}
static void
DNSNameOffsetsFree(DNSNameOffsetsRef * list_p)
{
DNSNameOffsetsRef list;
if (list_p == NULL) {
return;
}
list = *list_p;
if (list == NULL) {
return;
}
if (list->dno_list != NULL && list->dno_list != list->dno_list_s) {
free(list->dno_list);
}
free(list);
*list_p = NULL;
return;
}
static void
DNSNameOffsetsAdd(DNSNameOffsetsRef list, uint32_t this_offset)
{
if (list->dno_size == list->dno_count) {
#ifdef DEBUG
printf("Growing from %d to %d\n", list->dno_size, list->dno_size * 2);
#endif DEBUG
list->dno_size *= 2;
if (list->dno_list == list->dno_list_s) {
list->dno_list = malloc(list->dno_size * sizeof(*list->dno_list));
bcopy(list->dno_list_s, list->dno_list,
list->dno_count * sizeof(*list->dno_list));
}
else {
list->dno_list = reallocf(list->dno_list,
list->dno_size * sizeof(*list->dno_list));
}
}
list->dno_list[list->dno_count++] = this_offset;
return;
}
static void
DNSNameOffsetsSet(DNSNameOffsetsRef list, int i, uint32_t this_offset)
{
if (i > list->dno_count) {
fprintf(stderr, "attempt to set offset 0x%x at index %d\n",
this_offset, i);
}
list->dno_list[i] = this_offset;
return;
}
static __inline__ uint32_t
DNSNameOffsetsElement(const DNSNameOffsetsRef list, int i)
{
return (list->dno_list[i]);
}
static __inline__ uint32_t
DNSNameOffsetsCount(const DNSNameOffsetsRef list)
{
return (list->dno_count);
}
static void
DNSNameOffsetsListInit(DNSNameOffsetsListRef nlist)
{
bzero(nlist, sizeof(*nlist));
return;
}
static void
DNSNameOffsetsListFreeElements(DNSNameOffsetsListRef nlist)
{
if (nlist->dnl_list != NULL) {
int i;
for (i = 0; i < nlist->dnl_count; i++) {
DNSNameOffsetsFree(nlist->dnl_list + i);
}
free(nlist->dnl_list);
}
DNSNameOffsetsListInit(nlist);
return;
}
static void
DNSNameOffsetsListAdd(DNSNameOffsetsListRef nlist, DNSNameOffsetsRef this_list)
{
if (nlist->dnl_size == nlist->dnl_count) {
if (nlist->dnl_size == 0) {
#ifdef DEBUG
printf("Namelist growing from 0 to %d\n",
DNS_NAME_OFFSETS_LIST_N_START);
#endif DEBUG
nlist->dnl_size = DNS_NAME_OFFSETS_LIST_N_START;
nlist->dnl_list
= malloc(nlist->dnl_size * sizeof(*nlist->dnl_list));
}
else {
#ifdef DEBUG
printf("Namelist growing from %d to %d\n", nlist->dnl_size,
nlist->dnl_size * 2);
#endif DEBUG
nlist->dnl_size *= 2;
nlist->dnl_list
= reallocf(nlist->dnl_list,
nlist->dnl_size * sizeof(*nlist->dnl_list));
}
}
nlist->dnl_list[nlist->dnl_count++] = this_list;
return;
}
static __inline__ DNSNameOffsetsRef
DNSNameOffsetsListElement(DNSNameOffsetsListRef nlist, int i)
{
return (nlist->dnl_list[i]);
}
static __inline__ int
DNSNameOffsetsListCount(DNSNameOffsetsListRef nlist)
{
return (nlist->dnl_count);
}
static void
DNSNameFree(DNSNameRef * name_p)
{
DNSNameRef name;
if (name_p == NULL) {
return;
}
name = *name_p;
if (name == NULL) {
return;
}
DNSNameOffsetsFree(&name->dn_offsets);
free(name);
*name_p = NULL;
}
static DNSNameRef
DNSNameCreate(const char * dns_name)
{
int digits = 0;
int i;
int name_len;
DNSNameRef new_name;
int size = 0;
uint32_t start_offset;
name_len = strlen(dns_name);
new_name = (DNSNameRef)malloc(sizeof(*new_name) + name_len + 2);
new_name->dn_offsets = DNSNameOffsetsCreate();
new_name->dn_buf = (uint8_t *)(new_name + 1);
start_offset = 0;
for (i = 0; i <= name_len; i++) {
if (i == name_len || dns_name[i] == '.') {
if (digits == 0) {
fprintf(stderr, "label length is 0\n");
goto failed;
}
if (digits > DNS_LABEL_LENGTH_MAX) {
fprintf(stderr, "label length %d > %d\n", digits,
DNS_LABEL_LENGTH_MAX);
goto failed;
}
DNSNameOffsetsAdd(new_name->dn_offsets, start_offset);
new_name->dn_buf[start_offset] = digits;
size += digits + 1;
digits = 0;
start_offset = i + 1;
}
else {
new_name->dn_buf[1 + i] = dns_name[i];
digits++;
}
}
new_name->dn_buf_size = size + 1;
new_name->dn_buf[size] = '\0';
return (new_name);
failed:
DNSNameFree(&new_name);
return (NULL);
}
static int
DNSNameMatch(DNSNameRef name1, DNSNameRef name2)
{
int count1 = DNSNameOffsetsCount(name1->dn_offsets);
int count2 = DNSNameOffsetsCount(name2->dn_offsets);
int i1;
int i2;
int matched = 0;
for (i1 = (count1 - 1), i2 = (count2 - 1);
i1 >= 0 && i2 >= 0;
i1--, i2--) {
uint32_t buf_off1 = DNSNameOffsetsElement(name1->dn_offsets, i1);
uint32_t buf_off2 = DNSNameOffsetsElement(name2->dn_offsets, i2);
uint8_t nlen1 = *(uint8_t *)(name1->dn_buf + buf_off1);
uint8_t nlen2 = *(uint8_t *)(name2->dn_buf + buf_off2);
if (nlen1 != nlen2) {
break;
}
#ifdef DEBUG
printf("Comparing %.*s to %.*s\n",
nlen1, name1->dn_buf + buf_off1 + 1,
nlen1, name2->dn_buf + buf_off2 + 1);
#endif DEBUG
if (memcmp(name1->dn_buf + buf_off1 + 1, name2->dn_buf + buf_off2 + 1,
nlen1)) {
break;
}
matched++;
}
return (matched);
}
static void
DNSNamesBufFreeElements(DNSNamesBufRef nb)
{
DNSBufFreeElements(&nb->dnb_buf);
DNSNameOffsetsListFreeElements(&nb->dnb_names);
return;
}
static void
DNSNamesBufInit(DNSNamesBufRef nb, void * buf, int buf_size)
{
DNSBufInit(&nb->dnb_buf, buf, buf_size);
DNSNameOffsetsListInit(&nb->dnb_names);
return;
}
static __inline__ int
DNSNamesBufUsed(DNSNamesBufRef nb)
{
return (DNSBufUsed(&nb->dnb_buf));
}
static __inline__ void
DNSNamesBufSetUsed(DNSNamesBufRef nb, int used)
{
return (DNSBufSetUsed(&nb->dnb_buf, used));
}
static __inline__ uint8_t *
DNSNamesBufBuffer(DNSNamesBufRef nb)
{
return (DNSBufBuffer(&nb->dnb_buf));
}
static bool
DNSNamesBufAddData(DNSNamesBufRef nb, const void * data, int data_len)
{
return (DNSBufAddData(&nb->dnb_buf, data, data_len));
}
static bool
DNSNamesBufAddName(DNSNamesBufRef nb, const char * dns_name)
{
int best_matched = 0;
DNSNameOffsetsRef best_name = NULL;
int best_start_offset = 0;
uint32_t buf_offset;
int count;
int i;
DNSNameRef new_name = NULL;
bool ret = FALSE;
int start_count;
buf_offset = DNSNamesBufUsed(nb);
new_name = DNSNameCreate(dns_name);
if (new_name == NULL) {
goto failed;
}
for (i = 0; i < DNSNameOffsetsListCount(&nb->dnb_names); i++) {
int matched;
DNSName this_name;
DNSNameOffsetsRef this_offset;
this_offset = DNSNameOffsetsListElement(&nb->dnb_names, i);
this_name.dn_buf = DNSNamesBufBuffer(nb);
this_name.dn_offsets = this_offset;
matched = DNSNameMatch(&this_name, new_name);
if (matched > best_matched) {
best_matched = matched;
best_name = this_offset;
}
}
if (best_name == NULL) {
if (DNSNamesBufAddData(nb, new_name->dn_buf,
new_name->dn_buf_size) == FALSE) {
goto failed;
}
}
count = DNSNameOffsetsCount(new_name->dn_offsets);
start_count = count - best_matched;
if (best_name != NULL) {
best_start_offset = DNSNameOffsetsCount(best_name) - best_matched;
}
for (i = 0; i < count; i++) {
if (i >= start_count) {
uint32_t best_offset;
best_offset = DNSNameOffsetsElement(best_name, best_start_offset++);
if (i == start_count) {
uint16_t ptr;
ptr = htons(DNS_PTR_PATTERN_MASK | best_offset);
if (DNSNamesBufAddData(nb, &ptr, sizeof(ptr)) == FALSE) {
goto failed;
}
}
DNSNameOffsetsSet(new_name->dn_offsets, i, best_offset);
}
else {
uint32_t name_offset;
name_offset = DNSNameOffsetsElement(new_name->dn_offsets, i);
if (best_name != NULL) {
if (DNSNamesBufAddData(nb, new_name->dn_buf + name_offset,
new_name->dn_buf[name_offset] + 1)
== FALSE) {
goto failed;
}
}
DNSNameOffsetsSet(new_name->dn_offsets, i,
name_offset + buf_offset);
}
}
DNSNameOffsetsListAdd(&nb->dnb_names, new_name->dn_offsets);
new_name->dn_offsets = NULL;
ret = TRUE;
failed:
if (ret == FALSE) {
DNSNamesBufSetUsed(nb, buf_offset);
}
DNSNameFree(&new_name);
return (ret);
}
uint8_t *
DNSNameListBufferCreate(const char * names[], int names_count,
uint8_t * buffer, int * buffer_size)
{
int bufsize = *buffer_size;
int i;
DNSNamesBuf nb;
int used = 0;
if (names_count == 0) {
goto failed;
}
if (buffer == NULL) {
bufsize = 0;
}
else if (bufsize == 0) {
goto failed;
}
DNSNamesBufInit(&nb, buffer, bufsize);
for (i = 0; i < names_count; i++) {
if (DNSNamesBufAddName(&nb, names[i]) == FALSE) {
fprintf(stderr, "failed to add %s\n", names[i]);
if (buffer != NULL) {
used = DNSNamesBufUsed(&nb);
buffer = NULL;
}
goto failed;
}
}
used = DNSNamesBufUsed(&nb);
if (buffer == NULL) {
buffer = malloc(used);
memcpy(buffer, DNSNamesBufBuffer(&nb), used);
}
failed:
*buffer_size = used;
DNSNamesBufFreeElements(&nb);
return (buffer);
}
const char * *
DNSNameListCreate(const uint8_t * buffer, int buffer_size,
int * names_count)
{
DNSNameOffsets buffer_offsets;
bool first;
uint32_t last_completed_name = 0;
uint32_t left;
char * * list = NULL;
int list_count = 0;
DNSBuf name_buf;
uint32_t read_head;
uint32_t scan;
if (buffer_size == 0) {
return (NULL);
}
DNSBufInit(&name_buf, NULL, 0);
DNSNameOffsetsInit(&buffer_offsets);
left = buffer_size;
first = TRUE;
for (read_head = scan = 0; read_head < buffer_size; ) {
uint16_t ptr;
if ((buffer[read_head] & DNS_PTR_PATTERN_BYTE_MASK)
== DNS_PTR_PATTERN_BYTE_MASK) {
if (read_head >= scan) {
if (left < 2) {
fprintf(stderr, "truncated pointer value\n");
goto failed;
}
scan += 2;
left -= 2;
}
ptr = (uint16_t)(ntohs(*((uint16_t *)(buffer + read_head)))
& DNS_PTR_VALUE_MASK);
if (ptr >= read_head) {
fprintf(stderr,
"pointer points at or ahead of current position\n");
goto failed;
}
if (last_completed_name == 0
|| ptr >= last_completed_name) {
fprintf(stderr, "attempt to create infinite loop\n");
goto failed;
}
if (DNSNameOffsetsContainsOffset(&buffer_offsets, ptr) == FALSE) {
fprintf(stderr, "attempt to point off into the weeds\n");
goto failed;
}
read_head = ptr;
}
else {
uint8_t len = buffer[read_head];
if (read_head >= scan) {
if (len > DNS_LABEL_LENGTH_MAX) {
fprintf(stderr, "label length %d > %d\n", len,
DNS_LABEL_LENGTH_MAX);
goto failed;
}
if (left < (len + 1)) {
fprintf(stderr, "label truncated %d < %d\n",
left, len + 1);
goto failed;
}
scan += len + 1;
left -= len + 1;
}
if (len == 0) {
char ch = '\0';
(void)DNSBufAddData(&name_buf, &ch, 1);
last_completed_name = scan;
read_head = scan;
list_count++;
first = TRUE;
}
else {
char dot = '.';
if (DNSNameOffsetsContainsOffset(&buffer_offsets, read_head)
== FALSE) {
DNSNameOffsetsAdd(&buffer_offsets, read_head);
}
if (first) {
first = FALSE;
}
else {
(void)DNSBufAddData(&name_buf, &dot, 1);
}
DNSBufAddData(&name_buf, buffer + read_head + 1, len);
read_head += len + 1;
}
}
}
if (list_count == 0) {
if (DNSBufUsed(&name_buf) == 0) {
fprintf(stderr, "empty list\n");
}
else {
fprintf(stderr, "name missing end label\n");
}
goto failed;
}
list = (char * *)
malloc(DNSBufUsed(&name_buf) + sizeof(*list) * list_count);
{
int i;
char * name_start;
name_start = (char *)(list) + sizeof(*list) * list_count;
memcpy(name_start, DNSBufBuffer(&name_buf),
DNSBufUsed(&name_buf));
for (i = 0; i < list_count; i++) {
list[i] = name_start;
name_start += strlen(name_start) + 1;
}
}
failed:
if (list != NULL) {
*names_count = list_count;
}
else {
*names_count = 0;
}
DNSBufFreeElements(&name_buf);
DNSNameOffsetsFreeElements(&buffer_offsets);
return ((const char * *)list);
}
#ifdef TEST_DNSNAMELIST
const uint8_t bad_buf1[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
3, 'g', 'o', 'o', 0xc0, 0xb };
const uint8_t bad_buf2[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0xc0, 0x00 };
const uint8_t bad_buf3[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0xc0, 0x0a };
const uint8_t bad_buf4[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
3, 'g', 'o', 'o', 0xc0, 0x01 };
const uint8_t bad_buf5[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
3, 'g', 'o', 'o', 0xc0 };
const uint8_t bad_buf6[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
16, 'g', 'o', 'o' };
const uint8_t bad_buf7[] = {
5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm'
};
const uint8_t good_buf1[] = {
4, 'e', 'u', 'r', 'o', 5, 'a', 'p', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
9, 'm', 'a', 'r', 'k', 'e', 't', 'i', 'n', 'g', 0xc0, 0x0b,
11, 'e', 'n', 'g', 'i', 'n', 'e', 'e', 'r', 'i', 'n', 'g', 0xc0, 0x05,
};
const uint8_t good_buf2[] = {
1, 'z', 0,
1, 'y', 0xc0, 0x00,
1, 'x', 0xc0, 0x03,
1, 'w', 1, 'x', 0,
1, 'w', 0xc0, 0x07,
1, 'w', 1, 'x', 1, 'y', 1, 'z', 0xc0, 0x0b,
};
struct test {
const char * name;
const uint8_t * buf;
int buf_size;
bool expect_success;
};
static const struct test all_tests[] = {
{ "infinite loop", bad_buf1, sizeof(bad_buf1), FALSE },
{ "infinite loop", bad_buf2, sizeof(bad_buf2), FALSE },
{ "forward pointer", bad_buf3, sizeof(bad_buf3), FALSE },
{ "pointer to non-existent location", bad_buf4, sizeof(bad_buf4), FALSE },
{ "truncated pointer", bad_buf5, sizeof(bad_buf5), FALSE },
{ "truncated label", bad_buf6, sizeof(bad_buf6), FALSE },
{ "no end label", bad_buf7, sizeof(bad_buf7), FALSE },
{ "apple.com", good_buf1, sizeof(good_buf1), TRUE },
{ "w.x", good_buf2, sizeof(good_buf2), TRUE },
{ NULL, NULL, 0}
};
#include <ctype.h>
static void
DNSNameListBufferDumpCharArray(const uint8_t * buf, int buf_size)
{
int i;
const uint8_t * scan;
printf("const uint8_t buf[] = {\n\t");
for (i = 0, scan = buf; i < buf_size; scan++, i++) {
uint8_t val = *scan;
if ((val & DNS_PTR_PATTERN_BYTE_MASK) == DNS_PTR_PATTERN_BYTE_MASK) {
if (i == (buf_size - 1)) {
printf("truncated pointer!\n");
return;
}
printf("0x%02x, 0x%02x,\n\t", scan[0], scan[1]);
scan++;
i++;
}
else {
if (val == 0) {
printf("0, \n\t");
}
else if (isprint(val)) {
printf("'%c', ", val);
}
else {
printf("%d, ", val);
}
}
}
printf("\n};\n");
fflush(stdout);
return;
}
int
main(int argc, const char * * argv)
{
uint8_t * buf;
int buf_size;
int i;
const char * * list;
int list_count;
const struct test * test;
uint8_t tmp[16];
if (argc >= 2) {
buf = DNSNameListBufferCreate(argv + 1, argc - 1, NULL, &buf_size);
if (buf != NULL) {
print_data(buf, buf_size);
list = DNSNameListCreate(buf, buf_size, &list_count);
if (list != NULL) {
printf("Domains: ");
for (i = 0; i < list_count; i++) {
if (strcasecmp(list[i], argv[i + 1]) != 0) {
fprintf(stderr, "ERROR! %s != %s\n",
list[i], argv[i + 1]);
break;
}
printf("%s%s", (i > 0) ? " " : "", list[i]);
}
printf("\n");
free(list);
}
DNSNameListBufferDumpCharArray(buf, buf_size);
free(buf);
}
buf_size = sizeof(tmp);
buf = DNSNameListBufferCreate(argv + 1, argc - 1, tmp, &buf_size);
if (buf_size == 0) {
printf("FAILED to convert\n");
}
else {
if (buf == NULL) {
printf("Buffer not large enough, but got %d bytes\n",
buf_size);
}
print_data(tmp, buf_size);
list = DNSNameListCreate(tmp, buf_size, &list_count);
if (list != NULL) {
printf("Domains: ");
for (i = 0; i < list_count; i++) {
printf("%s%s", (i > 0) ? " " : "", list[i]);
}
printf("\n");
free(list);
}
}
}
for (i = 0, test = all_tests; test->name != NULL; i++, test++) {
list = DNSNameListCreate(test->buf, test->buf_size,
&list_count);
printf("Test %d '%s' ", i + 1, test->name);
if (test->expect_success == (list != NULL)) {
printf("PASSED\n");
}
else {
printf("FAILED\n");
}
}
exit(0);
}
#endif TEST_DNSNAMELIST