1471 lines
32 KiB
C
1471 lines
32 KiB
C
/*
|
|
* Copyright (C) 2005,2006 Justin Karneges
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "jdns_p.h"
|
|
|
|
#include "jdns_packet.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
// misc
|
|
//----------------------------------------------------------------------------
|
|
void *jdns_alloc(int size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
void *jdns_realloc(void *p, int size)
|
|
{
|
|
return realloc(p, size);
|
|
}
|
|
|
|
void jdns_free(void *p)
|
|
{
|
|
free(p);
|
|
}
|
|
|
|
char *jdns_strdup(const char *s)
|
|
{
|
|
char *p;
|
|
int len;
|
|
|
|
len = strlen(s) + 1; // the zero
|
|
p = (char *)jdns_alloc(len);
|
|
memcpy(p, s, len);
|
|
return p;
|
|
}
|
|
|
|
unsigned char *jdns_copy_array(const unsigned char *src, int size)
|
|
{
|
|
unsigned char *out;
|
|
if(size <= 0)
|
|
return 0;
|
|
out = (unsigned char *)jdns_alloc(size);
|
|
memcpy(out, src, size);
|
|
return out;
|
|
}
|
|
|
|
int jdns_domain_cmp(const unsigned char *a, const unsigned char *b)
|
|
{
|
|
int n;
|
|
int len_a;
|
|
|
|
// case-insensitive compare
|
|
len_a = _ustrlen(a);
|
|
if(len_a != (int)_ustrlen(b))
|
|
return 0;
|
|
|
|
for(n = 0; n < len_a; ++n)
|
|
{
|
|
if(tolower(a[n]) != tolower(b[n]))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_object
|
|
//----------------------------------------------------------------------------
|
|
void *jdns_object_new(int size, void (*dtor)(void *), void *(*cctor)(const void *))
|
|
{
|
|
jdns_object_t *a = (jdns_object_t *)jdns_alloc(size);
|
|
memset(a, 0, size);
|
|
a->dtor = dtor;
|
|
a->cctor = cctor;
|
|
return a;
|
|
}
|
|
|
|
void *jdns_object_copy(const void *a)
|
|
{
|
|
return ((const jdns_object_t *)a)->cctor(a);
|
|
}
|
|
|
|
void jdns_object_delete(void *a)
|
|
{
|
|
((jdns_object_t *)a)->dtor(a);
|
|
}
|
|
|
|
void jdns_object_free(void *a)
|
|
{
|
|
jdns_free(a);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_list
|
|
//----------------------------------------------------------------------------
|
|
jdns_list_t *jdns_list_new()
|
|
{
|
|
jdns_list_t *a = JDNS_OBJECT_NEW(jdns_list);
|
|
a->count = 0;
|
|
a->item = 0;
|
|
a->valueList = 0;
|
|
a->autoDelete = 0;
|
|
return a;
|
|
}
|
|
|
|
jdns_list_t *jdns_list_copy(const jdns_list_t *a)
|
|
{
|
|
jdns_list_t *c = jdns_list_new();
|
|
|
|
// note: copying a list with autoDelete should not ever be done.
|
|
// heck, let's not even allow it. return an empty list.
|
|
if(a->autoDelete)
|
|
return c;
|
|
|
|
c->valueList = a->valueList;
|
|
|
|
// copy the items
|
|
if(a->item)
|
|
{
|
|
int n;
|
|
c->count = a->count;
|
|
c->item = (void **)jdns_alloc(sizeof(void *) * c->count);
|
|
if(a->valueList)
|
|
{
|
|
// deep copy
|
|
for(n = 0; n < c->count; ++n)
|
|
c->item[n] = jdns_object_copy(a->item[n]);
|
|
}
|
|
else
|
|
{
|
|
// just the pointer
|
|
for(n = 0; n < c->count; ++n)
|
|
c->item[n] = a->item[n];
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void jdns_list_delete(jdns_list_t *a)
|
|
{
|
|
if(!a)
|
|
return;
|
|
jdns_list_clear(a);
|
|
jdns_object_free(a);
|
|
}
|
|
|
|
void jdns_list_clear(jdns_list_t *a)
|
|
{
|
|
if(a->item)
|
|
{
|
|
// delete the items if necessary
|
|
if(a->valueList || a->autoDelete)
|
|
{
|
|
int n;
|
|
for(n = 0; n < a->count; ++n)
|
|
jdns_object_delete(a->item[n]);
|
|
}
|
|
jdns_free(a->item);
|
|
a->item = 0;
|
|
a->count = 0;
|
|
}
|
|
}
|
|
|
|
void jdns_list_insert(jdns_list_t *a, void *item, int pos)
|
|
{
|
|
// make memory
|
|
if(!a->item)
|
|
a->item = (void **)jdns_alloc(sizeof(void *));
|
|
else
|
|
a->item = (void **)jdns_realloc(a->item, sizeof(void *) * (a->count + 1));
|
|
|
|
// prepare position
|
|
if(pos != -1)
|
|
memmove(a->item + pos + 1, a->item + pos, (a->count - pos) * sizeof(void *));
|
|
else
|
|
pos = a->count;
|
|
|
|
// insert it
|
|
if(a->valueList)
|
|
a->item[pos] = jdns_object_copy(item);
|
|
else
|
|
a->item[pos] = item;
|
|
++a->count;
|
|
}
|
|
|
|
void jdns_list_insert_value(jdns_list_t *a, const void *item, int pos)
|
|
{
|
|
jdns_list_insert(a, (void *)item, pos);
|
|
}
|
|
|
|
void jdns_list_remove(jdns_list_t *a, void *item)
|
|
{
|
|
int n;
|
|
int pos = -1;
|
|
for(n = 0; n < a->count; ++n)
|
|
{
|
|
if(a->item[n] == item)
|
|
{
|
|
pos = n;
|
|
break;
|
|
}
|
|
}
|
|
if(pos == -1)
|
|
return;
|
|
|
|
jdns_list_remove_at(a, pos);
|
|
}
|
|
|
|
void jdns_list_remove_at(jdns_list_t *a, int pos)
|
|
{
|
|
if(pos < 0 || pos >= a->count)
|
|
return;
|
|
|
|
// delete the item if necessary
|
|
if(a->valueList || a->autoDelete)
|
|
jdns_object_delete(a->item[pos]);
|
|
|
|
// free the position
|
|
if(a->count > 1)
|
|
{
|
|
memmove(a->item + pos, a->item + pos + 1, (a->count - pos - 1) * sizeof(void *));
|
|
--a->count;
|
|
}
|
|
else
|
|
{
|
|
jdns_free(a->item);
|
|
a->item = 0;
|
|
a->count = 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_string
|
|
//----------------------------------------------------------------------------
|
|
jdns_string_t *jdns_string_new()
|
|
{
|
|
jdns_string_t *s = JDNS_OBJECT_NEW(jdns_string);
|
|
s->data = 0;
|
|
s->size = 0;
|
|
return s;
|
|
}
|
|
|
|
jdns_string_t *jdns_string_copy(const jdns_string_t *s)
|
|
{
|
|
jdns_string_t *c = jdns_string_new();
|
|
if(s->data)
|
|
jdns_string_set(c, s->data, s->size);
|
|
return c;
|
|
}
|
|
|
|
void jdns_string_delete(jdns_string_t *s)
|
|
{
|
|
if(!s)
|
|
return;
|
|
if(s->data)
|
|
jdns_free(s->data);
|
|
jdns_object_free(s);
|
|
}
|
|
|
|
void jdns_string_set(jdns_string_t *s, const unsigned char *str, int str_len)
|
|
{
|
|
if(s->data)
|
|
jdns_free(s->data);
|
|
s->data = (unsigned char *)jdns_alloc(str_len + 1);
|
|
memcpy(s->data, str, str_len);
|
|
s->data[str_len] = 0;
|
|
s->size = str_len;
|
|
}
|
|
|
|
void jdns_string_set_cstr(jdns_string_t *s, const char *str)
|
|
{
|
|
jdns_string_set(s, (const unsigned char *)str, strlen(str));
|
|
}
|
|
|
|
int jdns_string_indexOf(const jdns_string_t *s, unsigned char c, int pos)
|
|
{
|
|
int n;
|
|
for(n = pos; n < s->size; ++n)
|
|
{
|
|
if(s->data[n] == c)
|
|
return n;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
jdns_stringlist_t *jdns_string_split(const jdns_string_t *s, unsigned char sep)
|
|
{
|
|
int at, n, len;
|
|
jdns_string_t *str;
|
|
jdns_stringlist_t *out;
|
|
|
|
at = 0;
|
|
out = jdns_stringlist_new();
|
|
while(at < s->size)
|
|
{
|
|
n = jdns_string_indexOf(s, sep, at);
|
|
if(n == -1)
|
|
n = s->size;
|
|
len = n - at;
|
|
// FIXME: should we allow empty items?
|
|
//if(len == 0)
|
|
// break;
|
|
str = jdns_string_new();
|
|
jdns_string_set(str, s->data + at, len);
|
|
jdns_stringlist_append(out, str);
|
|
jdns_string_delete(str);
|
|
at = n + 1; // skip over separator
|
|
}
|
|
return out;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_stringlist
|
|
//----------------------------------------------------------------------------
|
|
jdns_stringlist_t *jdns_stringlist_new()
|
|
{
|
|
jdns_list_t *a = jdns_list_new();
|
|
a->valueList = 1;
|
|
return (jdns_stringlist_t *)a;
|
|
}
|
|
|
|
jdns_stringlist_t *jdns_stringlist_copy(const jdns_stringlist_t *a)
|
|
{
|
|
return (jdns_stringlist_t *)jdns_list_copy((const jdns_list_t *)a);
|
|
}
|
|
|
|
void jdns_stringlist_delete(jdns_stringlist_t *a)
|
|
{
|
|
jdns_list_delete((jdns_list_t *)a);
|
|
// note: no need to call jdns_object_free() here
|
|
}
|
|
|
|
void jdns_stringlist_append(jdns_stringlist_t *a, const jdns_string_t *str)
|
|
{
|
|
jdns_list_insert_value((jdns_list_t *)a, str, -1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_address
|
|
//----------------------------------------------------------------------------
|
|
jdns_address_t *jdns_address_new()
|
|
{
|
|
jdns_address_t *a = alloc_type(jdns_address_t);
|
|
a->isIpv6 = 0;
|
|
a->addr.v4 = 0;
|
|
a->c_str = jdns_strdup("");
|
|
return a;
|
|
}
|
|
|
|
jdns_address_t *jdns_address_copy(const jdns_address_t *a)
|
|
{
|
|
jdns_address_t *c = jdns_address_new();
|
|
if(a->isIpv6)
|
|
jdns_address_set_ipv6(c, a->addr.v6);
|
|
else
|
|
jdns_address_set_ipv4(c, a->addr.v4);
|
|
return c;
|
|
}
|
|
|
|
void jdns_address_delete(jdns_address_t *a)
|
|
{
|
|
if(!a)
|
|
return;
|
|
if(a->isIpv6)
|
|
jdns_free(a->addr.v6);
|
|
jdns_free(a->c_str);
|
|
jdns_free(a);
|
|
}
|
|
|
|
void jdns_address_set_ipv4(jdns_address_t *a, unsigned long int ipv4)
|
|
{
|
|
if(a->isIpv6)
|
|
jdns_free(a->addr.v6);
|
|
jdns_free(a->c_str);
|
|
a->isIpv6 = 0;
|
|
a->addr.v4 = ipv4;
|
|
a->c_str = (char *)jdns_alloc(16); // max size (3 * 4 + 3 + 1)
|
|
sprintf(a->c_str, "%d.%d.%d.%d",
|
|
(unsigned char)((ipv4 >> 24) & 0xff),
|
|
(unsigned char)((ipv4 >> 16) & 0xff),
|
|
(unsigned char)((ipv4 >> 8) & 0xff),
|
|
(unsigned char)((ipv4) & 0xff));
|
|
}
|
|
|
|
void jdns_address_set_ipv6(jdns_address_t *a, const unsigned char *ipv6)
|
|
{
|
|
int n;
|
|
unsigned char *p;
|
|
unsigned short word[8];
|
|
if(a->isIpv6)
|
|
jdns_free(a->addr.v6);
|
|
jdns_free(a->c_str);
|
|
a->isIpv6 = 1;
|
|
a->addr.v6 = (unsigned char *)jdns_alloc(16);
|
|
memcpy(a->addr.v6, ipv6, 16);
|
|
p = (unsigned char *)a->addr.v6;
|
|
a->c_str = (char *)jdns_alloc(40); // max size (8 * 4 + 7 + 1)
|
|
// each word in a 16-byte ipv6 address is network byte order
|
|
for(n = 0; n < 8; ++n)
|
|
word[n] = ((unsigned short)(p[n * 2]) << 8) + (unsigned short)(p[n * 2 + 1]);
|
|
sprintf(a->c_str, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", word[0], word[1], word[2], word[3], word[4], word[5], word[6], word[7]);
|
|
}
|
|
|
|
int jdns_address_set_cstr(jdns_address_t *a, const char *str)
|
|
{
|
|
int slen = strlen(str);
|
|
|
|
// ipv6
|
|
if(strchr(str, ':'))
|
|
{
|
|
jdns_string_t *in;
|
|
jdns_stringlist_t *list;
|
|
unsigned char ipv6[16];
|
|
int n, at, count, fill;
|
|
|
|
in = jdns_string_new();
|
|
jdns_string_set_cstr(in, str);
|
|
list = jdns_string_split(in, ':');
|
|
jdns_string_delete(in);
|
|
|
|
// a confusing outputting-backwards parser adapted from qt
|
|
|
|
count = list->count;
|
|
|
|
if(count < 3 || count > 8)
|
|
goto error;
|
|
|
|
at = 16;
|
|
fill = 9 - count;
|
|
for(n = count - 1; n >= 0; --n)
|
|
{
|
|
if(at <= 0)
|
|
goto error;
|
|
|
|
if(list->item[n]->size == 0)
|
|
{
|
|
if(n == count - 1)
|
|
{
|
|
if(list->item[n - 1]->size != 0)
|
|
goto error;
|
|
ipv6[--at] = 0;
|
|
ipv6[--at] = 0;
|
|
}
|
|
else if(n == 0)
|
|
{
|
|
if(list->item[n + 1]->size != 0)
|
|
goto error;
|
|
ipv6[--at] = 0;
|
|
ipv6[--at] = 0;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
for(i = 0; i < fill; ++i)
|
|
{
|
|
if(at <= 0)
|
|
goto error;
|
|
ipv6[--at] = 0;
|
|
ipv6[--at] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(jdns_string_indexOf(list->item[n], '.', 0) == -1)
|
|
{
|
|
int x;
|
|
x = strtol((const char *)list->item[n]->data, NULL, 16);
|
|
if(x < 0 || x > 0xffff)
|
|
goto error;
|
|
ipv6[--at] = x & 0xff;
|
|
ipv6[--at] = (x >> 8) & 0xff;
|
|
}
|
|
else
|
|
{
|
|
jdns_address_t *v4;
|
|
|
|
if(n != count - 1)
|
|
goto error;
|
|
|
|
v4 = jdns_address_new();
|
|
if(!jdns_address_set_cstr(v4, (char *)list->item[n]->data))
|
|
{
|
|
jdns_address_delete(v4);
|
|
goto error;
|
|
}
|
|
|
|
ipv6[--at] = (unsigned char)(v4->addr.v4 & 0xff);
|
|
ipv6[--at] = (unsigned char)((v4->addr.v4 >> 8) & 0xff);
|
|
ipv6[--at] = (unsigned char)((v4->addr.v4 >> 16) & 0xff);
|
|
ipv6[--at] = (unsigned char)((v4->addr.v4 >> 24) & 0xff);
|
|
jdns_address_delete(v4);
|
|
--fill;
|
|
}
|
|
}
|
|
}
|
|
jdns_stringlist_delete(list);
|
|
|
|
jdns_address_set_ipv6(a, ipv6);
|
|
return 1;
|
|
|
|
error:
|
|
jdns_stringlist_delete(list);
|
|
return 0;
|
|
}
|
|
else if(strchr(str, '.'))
|
|
{
|
|
unsigned char b[4];
|
|
int x;
|
|
unsigned long int ipv4;
|
|
int at;
|
|
char *part;
|
|
int len;
|
|
const char *p, *p2;
|
|
|
|
p = str;
|
|
at = 0;
|
|
while(1)
|
|
{
|
|
p2 = strchr(p, '.');
|
|
if(!p2)
|
|
p2 = str + slen;
|
|
len = p2 - p;
|
|
|
|
// convert the section into a byte
|
|
part = (char *)jdns_alloc(len + 1);
|
|
memcpy(part, p, len);
|
|
part[len] = 0;
|
|
x = strtol(part, NULL, 10);
|
|
jdns_free(part);
|
|
if(x < 0 || x > 0xff)
|
|
break;
|
|
b[at++] = (unsigned char)x;
|
|
|
|
// done?
|
|
if(p2 >= str + slen)
|
|
break;
|
|
|
|
// skip over the separator
|
|
p = p2 + 1;
|
|
}
|
|
if(at != 4)
|
|
return 0;
|
|
|
|
ipv4 = 0;
|
|
ipv4 += b[0];
|
|
ipv4 <<= 8;
|
|
ipv4 += b[1];
|
|
ipv4 <<= 8;
|
|
ipv4 += b[2];
|
|
ipv4 <<= 8;
|
|
ipv4 += b[3];
|
|
|
|
jdns_address_set_ipv4(a, ipv4);
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int jdns_address_cmp(const jdns_address_t *a, const jdns_address_t *b)
|
|
{
|
|
// same protocol?
|
|
if(a->isIpv6 != b->isIpv6)
|
|
return 0;
|
|
if(a->isIpv6)
|
|
{
|
|
int n;
|
|
for(n = 0; n < 16; ++n)
|
|
{
|
|
if(a->addr.v6[n] != b->addr.v6[n])
|
|
break;
|
|
}
|
|
if(n == 16)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if(a->addr.v4 == b->addr.v4)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// FF02::FB
|
|
unsigned char jdns_multicast_addr6_value_v6[] =
|
|
{
|
|
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb
|
|
};
|
|
|
|
jdns_address_t *jdns_address_multicast4_new()
|
|
{
|
|
jdns_address_t *a = jdns_address_new();
|
|
jdns_address_set_ipv4(a, 0xe00000fb);
|
|
return a;
|
|
}
|
|
|
|
jdns_address_t *jdns_address_multicast6_new()
|
|
{
|
|
jdns_address_t *a = jdns_address_new();
|
|
jdns_address_set_ipv6(a, jdns_multicast_addr6_value_v6);
|
|
return a;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_server
|
|
//----------------------------------------------------------------------------
|
|
jdns_server_t *jdns_server_new()
|
|
{
|
|
jdns_server_t *s = alloc_type(jdns_server_t);
|
|
s->name = 0;
|
|
s->port = 0;
|
|
s->priority = 0;
|
|
s->weight = 0;
|
|
return s;
|
|
}
|
|
|
|
jdns_server_t *jdns_server_copy(const jdns_server_t *s)
|
|
{
|
|
jdns_server_t *c = jdns_server_new();
|
|
if(s->name)
|
|
c->name = _ustrdup(s->name);
|
|
c->port = s->port;
|
|
c->priority = s->priority;
|
|
c->weight = s->weight;
|
|
return c;
|
|
}
|
|
|
|
void jdns_server_delete(jdns_server_t *s)
|
|
{
|
|
if(!s)
|
|
return;
|
|
if(s->name)
|
|
jdns_free(s->name);
|
|
jdns_object_free(s);
|
|
}
|
|
|
|
void jdns_server_set_name(jdns_server_t *s, const unsigned char *name)
|
|
{
|
|
if(s->name)
|
|
jdns_free(s->name);
|
|
s->name = _ustrdup(name);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_nameserver
|
|
//----------------------------------------------------------------------------
|
|
jdns_nameserver_t *jdns_nameserver_new()
|
|
{
|
|
jdns_nameserver_t *a = alloc_type(jdns_nameserver_t);
|
|
a->address = 0;
|
|
a->port = -1;
|
|
return a;
|
|
}
|
|
|
|
jdns_nameserver_t *jdns_nameserver_copy(const jdns_nameserver_t *a)
|
|
{
|
|
jdns_nameserver_t *c = jdns_nameserver_new();
|
|
if(a->address)
|
|
c->address = jdns_address_copy(a->address);
|
|
c->port = a->port;
|
|
return c;
|
|
}
|
|
|
|
void jdns_nameserver_delete(jdns_nameserver_t *a)
|
|
{
|
|
if(!a)
|
|
return;
|
|
jdns_address_delete(a->address);
|
|
jdns_free(a);
|
|
}
|
|
|
|
void jdns_nameserver_set(jdns_nameserver_t *a, const jdns_address_t *addr, int port)
|
|
{
|
|
if(a->address)
|
|
jdns_address_delete(a->address);
|
|
a->address = jdns_address_copy(addr);
|
|
a->port = port;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_nameserverlist
|
|
//----------------------------------------------------------------------------
|
|
jdns_nameserverlist_t *jdns_nameserverlist_new()
|
|
{
|
|
jdns_nameserverlist_t *a = alloc_type(jdns_nameserverlist_t);
|
|
a->count = 0;
|
|
a->item = 0;
|
|
return a;
|
|
}
|
|
|
|
jdns_nameserverlist_t *jdns_nameserverlist_copy(const jdns_nameserverlist_t *a)
|
|
{
|
|
int n;
|
|
jdns_nameserverlist_t *c = jdns_nameserverlist_new();
|
|
if(a->item)
|
|
{
|
|
c->item = (jdns_nameserver_t **)jdns_alloc(sizeof(jdns_nameserver_t *) * a->count);
|
|
c->count = a->count;
|
|
for(n = 0; n < c->count; ++n)
|
|
c->item[n] = jdns_nameserver_copy(a->item[n]);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void jdns_nameserverlist_delete(jdns_nameserverlist_t *a)
|
|
{
|
|
int n;
|
|
if(!a)
|
|
return;
|
|
if(a->item)
|
|
{
|
|
for(n = 0; n < a->count; ++n)
|
|
jdns_nameserver_delete(a->item[n]);
|
|
jdns_free(a->item);
|
|
}
|
|
jdns_free(a);
|
|
}
|
|
|
|
void jdns_nameserverlist_append(jdns_nameserverlist_t *a, const jdns_address_t *addr, int port)
|
|
{
|
|
if(!a->item)
|
|
a->item = (jdns_nameserver_t **)jdns_alloc(sizeof(jdns_nameserver_t *));
|
|
else
|
|
a->item = (jdns_nameserver_t **)jdns_realloc(a->item, sizeof(jdns_nameserver_t *) * (a->count + 1));
|
|
a->item[a->count] = jdns_nameserver_new();
|
|
jdns_nameserver_set(a->item[a->count], addr, port);
|
|
++a->count;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_dnshost
|
|
//----------------------------------------------------------------------------
|
|
jdns_dnshost_t *jdns_dnshost_new()
|
|
{
|
|
jdns_dnshost_t *a = alloc_type(jdns_dnshost_t);
|
|
a->name = 0;
|
|
a->address = 0;
|
|
return a;
|
|
}
|
|
|
|
jdns_dnshost_t *jdns_dnshost_copy(const jdns_dnshost_t *a)
|
|
{
|
|
jdns_dnshost_t *c = jdns_dnshost_new();
|
|
if(a->name)
|
|
c->name = jdns_string_copy(a->name);
|
|
if(a->address)
|
|
c->address = jdns_address_copy(a->address);
|
|
return c;
|
|
}
|
|
|
|
void jdns_dnshost_delete(jdns_dnshost_t *a)
|
|
{
|
|
if(!a)
|
|
return;
|
|
jdns_string_delete(a->name);
|
|
jdns_address_delete(a->address);
|
|
jdns_free(a);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_dnshostlist
|
|
//----------------------------------------------------------------------------
|
|
jdns_dnshostlist_t *jdns_dnshostlist_new()
|
|
{
|
|
jdns_dnshostlist_t *a = alloc_type(jdns_dnshostlist_t);
|
|
a->count = 0;
|
|
a->item = 0;
|
|
return a;
|
|
}
|
|
|
|
jdns_dnshostlist_t *jdns_dnshostlist_copy(const jdns_dnshostlist_t *a)
|
|
{
|
|
int n;
|
|
jdns_dnshostlist_t *c = jdns_dnshostlist_new();
|
|
if(a->item)
|
|
{
|
|
c->item = (jdns_dnshost_t **)jdns_alloc(sizeof(jdns_dnshost_t *) * a->count);
|
|
c->count = a->count;
|
|
for(n = 0; n < c->count; ++n)
|
|
c->item[n] = jdns_dnshost_copy(a->item[n]);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void jdns_dnshostlist_delete(jdns_dnshostlist_t *a)
|
|
{
|
|
int n;
|
|
if(!a)
|
|
return;
|
|
if(a->item)
|
|
{
|
|
for(n = 0; n < a->count; ++n)
|
|
jdns_dnshost_delete(a->item[n]);
|
|
jdns_free(a->item);
|
|
}
|
|
jdns_free(a);
|
|
}
|
|
|
|
void jdns_dnshostlist_append(jdns_dnshostlist_t *a, const jdns_dnshost_t *host)
|
|
{
|
|
if(!a->item)
|
|
a->item = (jdns_dnshost_t **)jdns_alloc(sizeof(jdns_dnshost_t *));
|
|
else
|
|
a->item = (jdns_dnshost_t **)jdns_realloc(a->item, sizeof(jdns_dnshost_t *) * (a->count + 1));
|
|
a->item[a->count] = jdns_dnshost_copy(host);
|
|
++a->count;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_dnsparams
|
|
//----------------------------------------------------------------------------
|
|
jdns_dnsparams_t *jdns_dnsparams_new()
|
|
{
|
|
jdns_dnsparams_t *a = alloc_type(jdns_dnsparams_t);
|
|
a->nameservers = jdns_nameserverlist_new();
|
|
a->domains = jdns_stringlist_new();
|
|
a->hosts = jdns_dnshostlist_new();
|
|
return a;
|
|
}
|
|
|
|
jdns_dnsparams_t *jdns_dnsparams_copy(jdns_dnsparams_t *a)
|
|
{
|
|
jdns_dnsparams_t *c = jdns_dnsparams_new();
|
|
c->nameservers = jdns_nameserverlist_copy(a->nameservers);
|
|
c->domains = jdns_stringlist_copy(a->domains);
|
|
c->hosts = jdns_dnshostlist_copy(a->hosts);
|
|
return c;
|
|
}
|
|
|
|
void jdns_dnsparams_delete(jdns_dnsparams_t *a)
|
|
{
|
|
if(!a)
|
|
return;
|
|
jdns_nameserverlist_delete(a->nameservers);
|
|
jdns_stringlist_delete(a->domains);
|
|
jdns_dnshostlist_delete(a->hosts);
|
|
jdns_free(a);
|
|
}
|
|
|
|
void jdns_dnsparams_append_nameserver(jdns_dnsparams_t *a, const jdns_address_t *addr, int port)
|
|
{
|
|
jdns_nameserverlist_append(a->nameservers, addr, port);
|
|
}
|
|
|
|
void jdns_dnsparams_append_domain(jdns_dnsparams_t *a, const jdns_string_t *domain)
|
|
{
|
|
jdns_stringlist_append(a->domains, domain);
|
|
}
|
|
|
|
void jdns_dnsparams_append_host(jdns_dnsparams_t *a, const jdns_string_t *name, const jdns_address_t *address)
|
|
{
|
|
jdns_dnshost_t *h = jdns_dnshost_new();
|
|
h->name = jdns_string_copy(name);
|
|
h->address = jdns_address_copy(address);
|
|
jdns_dnshostlist_append(a->hosts, h);
|
|
jdns_dnshost_delete(h);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_rr
|
|
//----------------------------------------------------------------------------
|
|
void _jdns_rr_data_reset(jdns_rr_t *r)
|
|
{
|
|
if(r->rdata)
|
|
{
|
|
jdns_free(r->rdata);
|
|
r->rdata = 0;
|
|
}
|
|
r->rdlength = 0;
|
|
|
|
if(r->haveKnown)
|
|
{
|
|
switch(r->type)
|
|
{
|
|
case JDNS_RTYPE_A:
|
|
case JDNS_RTYPE_AAAA:
|
|
jdns_address_delete(r->data.address);
|
|
break;
|
|
case JDNS_RTYPE_MX:
|
|
case JDNS_RTYPE_SRV:
|
|
jdns_server_delete(r->data.server);
|
|
break;
|
|
case JDNS_RTYPE_CNAME:
|
|
case JDNS_RTYPE_PTR:
|
|
case JDNS_RTYPE_NS:
|
|
jdns_free(r->data.name);
|
|
break;
|
|
case JDNS_RTYPE_TXT:
|
|
jdns_stringlist_delete(r->data.texts);
|
|
break;
|
|
case JDNS_RTYPE_HINFO:
|
|
jdns_string_delete(r->data.hinfo.cpu);
|
|
jdns_string_delete(r->data.hinfo.os);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
r->haveKnown = 0;
|
|
}
|
|
r->type = -1;
|
|
}
|
|
|
|
void _jdns_rr_data_copy(const jdns_rr_t *r, jdns_rr_t *c)
|
|
{
|
|
c->type = r->type;
|
|
c->rdlength = r->rdlength;
|
|
c->rdata = jdns_copy_array(r->rdata, r->rdlength);
|
|
|
|
if(r->haveKnown)
|
|
{
|
|
switch(r->type)
|
|
{
|
|
case JDNS_RTYPE_A:
|
|
case JDNS_RTYPE_AAAA:
|
|
c->data.address = jdns_address_copy(r->data.address);
|
|
break;
|
|
case JDNS_RTYPE_MX:
|
|
case JDNS_RTYPE_SRV:
|
|
c->data.server = jdns_server_copy(r->data.server);
|
|
break;
|
|
case JDNS_RTYPE_CNAME:
|
|
case JDNS_RTYPE_PTR:
|
|
case JDNS_RTYPE_NS:
|
|
c->data.name = _ustrdup(r->data.name);
|
|
break;
|
|
case JDNS_RTYPE_TXT:
|
|
c->data.texts = jdns_stringlist_copy(r->data.texts);
|
|
break;
|
|
case JDNS_RTYPE_HINFO:
|
|
c->data.hinfo.cpu = jdns_string_copy(r->data.hinfo.cpu);
|
|
c->data.hinfo.os = jdns_string_copy(r->data.hinfo.os);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
c->haveKnown = 1;
|
|
}
|
|
}
|
|
|
|
jdns_rr_t *jdns_rr_new()
|
|
{
|
|
jdns_rr_t *r = alloc_type(jdns_rr_t);
|
|
r->owner = 0;
|
|
r->ttl = 0;
|
|
r->type = -1;
|
|
r->qclass = 0;
|
|
r->rdata = 0;
|
|
r->rdlength = 0;
|
|
r->haveKnown = 0;
|
|
return r;
|
|
}
|
|
|
|
jdns_rr_t *jdns_rr_copy(const jdns_rr_t *r)
|
|
{
|
|
jdns_rr_t *c = jdns_rr_new();
|
|
if(r->owner)
|
|
c->owner = _ustrdup(r->owner);
|
|
c->ttl = r->ttl;
|
|
_jdns_rr_data_copy(r, c);
|
|
return c;
|
|
}
|
|
|
|
void jdns_rr_delete(jdns_rr_t *r)
|
|
{
|
|
if(!r)
|
|
return;
|
|
if(r->owner)
|
|
jdns_free(r->owner);
|
|
_jdns_rr_data_reset(r);
|
|
jdns_free(r);
|
|
}
|
|
|
|
void jdns_rr_set_owner(jdns_rr_t *r, const unsigned char *name)
|
|
{
|
|
if(r->owner)
|
|
jdns_free(r->owner);
|
|
r->owner = _ustrdup(name);
|
|
}
|
|
|
|
void jdns_rr_set_record(jdns_rr_t *r, int type, const unsigned char *rdata, int rdlength)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = type;
|
|
r->rdlength = rdlength;
|
|
r->rdata = jdns_copy_array(rdata, rdlength);
|
|
}
|
|
|
|
void jdns_rr_set_A(jdns_rr_t *r, const jdns_address_t *address)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_A;
|
|
r->haveKnown = 1;
|
|
r->data.address = jdns_address_copy(address);
|
|
}
|
|
|
|
void jdns_rr_set_AAAA(jdns_rr_t *r, const jdns_address_t *address)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_AAAA;
|
|
r->haveKnown = 1;
|
|
r->data.address = jdns_address_copy(address);
|
|
}
|
|
|
|
void jdns_rr_set_MX(jdns_rr_t *r, const unsigned char *name, int priority)
|
|
{
|
|
jdns_server_t *s = jdns_server_new();
|
|
jdns_server_set_name(s, name);
|
|
s->priority = priority;
|
|
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_MX;
|
|
r->haveKnown = 1;
|
|
r->data.server = s;
|
|
}
|
|
|
|
void jdns_rr_set_SRV(jdns_rr_t *r, const unsigned char *name, int port, int priority, int weight)
|
|
{
|
|
jdns_server_t *s = jdns_server_new();
|
|
jdns_server_set_name(s, name);
|
|
s->port = port;
|
|
s->priority = priority;
|
|
s->weight = weight;
|
|
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_SRV;
|
|
r->haveKnown = 1;
|
|
r->data.server = s;
|
|
}
|
|
|
|
void jdns_rr_set_CNAME(jdns_rr_t *r, const unsigned char *name)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_CNAME;
|
|
r->haveKnown = 1;
|
|
r->data.name = _ustrdup(name);
|
|
}
|
|
|
|
void jdns_rr_set_PTR(jdns_rr_t *r, const unsigned char *name)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_PTR;
|
|
r->haveKnown = 1;
|
|
r->data.name = _ustrdup(name);
|
|
}
|
|
|
|
void jdns_rr_set_TXT(jdns_rr_t *r, const jdns_stringlist_t *texts)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_TXT;
|
|
r->haveKnown = 1;
|
|
r->data.texts = jdns_stringlist_copy(texts);
|
|
}
|
|
|
|
void jdns_rr_set_HINFO(jdns_rr_t *r, const jdns_string_t *cpu, const jdns_string_t *os)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_HINFO;
|
|
r->haveKnown = 1;
|
|
r->data.hinfo.cpu = jdns_string_copy(cpu);
|
|
r->data.hinfo.os = jdns_string_copy(os);
|
|
}
|
|
|
|
void jdns_rr_set_NS(jdns_rr_t *r, const unsigned char *name)
|
|
{
|
|
_jdns_rr_data_reset(r);
|
|
r->type = JDNS_RTYPE_NS;
|
|
r->haveKnown = 1;
|
|
r->data.name = _ustrdup(name);
|
|
}
|
|
|
|
int jdns_rr_verify(const jdns_rr_t *r)
|
|
{
|
|
if(r->type == -1)
|
|
return 0;
|
|
|
|
if(!jdns_packet_name_isvalid(r->owner, _ustrlen(r->owner)))
|
|
return 0;
|
|
|
|
switch(r->type)
|
|
{
|
|
case JDNS_RTYPE_MX:
|
|
case JDNS_RTYPE_SRV:
|
|
{
|
|
// consider it valid if we don't have a known to check
|
|
if(!r->haveKnown)
|
|
return 1;
|
|
if(!jdns_packet_name_isvalid(r->data.server->name, _ustrlen(r->data.server->name)))
|
|
return 0;
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_CNAME:
|
|
case JDNS_RTYPE_PTR:
|
|
case JDNS_RTYPE_NS:
|
|
{
|
|
if(!r->haveKnown)
|
|
return 1;
|
|
if(!jdns_packet_name_isvalid(r->data.name, _ustrlen(r->data.name)))
|
|
return 0;
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_TXT:
|
|
{
|
|
int n;
|
|
if(!r->haveKnown)
|
|
return 1;
|
|
for(n = 0; n < r->data.texts->count; ++n)
|
|
{
|
|
if(r->data.texts->item[n]->size > 255)
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_HINFO:
|
|
{
|
|
if(!r->haveKnown)
|
|
return 1;
|
|
if(r->data.hinfo.cpu->size > 255)
|
|
return 0;
|
|
if(r->data.hinfo.os->size > 255)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static jdns_string_t *read_name_at_end(const jdns_packet_resource_t *pr, const jdns_packet_t *ref, int _at)
|
|
{
|
|
jdns_string_t *name;
|
|
int at;
|
|
at = _at;
|
|
if(!jdns_packet_resource_read_name(pr, ref, &at, &name))
|
|
return 0;
|
|
if(at != pr->rdlength)
|
|
{
|
|
jdns_string_delete(name);
|
|
return 0;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static jdns_string_t *read_text_string(const jdns_packet_resource_t *pr, int *_at)
|
|
{
|
|
jdns_string_t *out;
|
|
int at, len;
|
|
at = *_at;
|
|
if(at + 1 > pr->rdlength)
|
|
return 0;
|
|
len = pr->rdata[at++];
|
|
if(at + len > pr->rdlength)
|
|
return 0;
|
|
out = jdns_string_new();
|
|
jdns_string_set(out, pr->rdata + at, len);
|
|
at += len;
|
|
*_at = at;
|
|
return out;
|
|
}
|
|
|
|
// if the type is known, then it must be parsed properly
|
|
// if the type is unknown, then that's ok
|
|
// rdata is always copied, known or not
|
|
jdns_rr_t *jdns_rr_from_resource(const jdns_packet_resource_t *pr, const jdns_packet_t *ref)
|
|
{
|
|
jdns_rr_t *rr = 0;
|
|
|
|
if(pr->qtype == JDNS_RTYPE_ANY)
|
|
return 0;
|
|
|
|
switch(pr->qtype)
|
|
{
|
|
case JDNS_RTYPE_A:
|
|
{
|
|
jdns_address_t *addr;
|
|
unsigned long int ip;
|
|
if(pr->rdlength != 4)
|
|
break;
|
|
memcpy(&ip, pr->rdata, 4);
|
|
ip = ntohl(ip);
|
|
addr = jdns_address_new();
|
|
jdns_address_set_ipv4(addr, ip);
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_A(rr, addr);
|
|
jdns_address_delete(addr);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_AAAA:
|
|
{
|
|
jdns_address_t *addr;
|
|
if(pr->rdlength != 16)
|
|
break;
|
|
addr = jdns_address_new();
|
|
jdns_address_set_ipv6(addr, pr->rdata);
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_AAAA(rr, addr);
|
|
jdns_address_delete(addr);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_MX:
|
|
{
|
|
unsigned short priority;
|
|
jdns_string_t *name;
|
|
if(pr->rdlength < 2)
|
|
break;
|
|
memcpy(&priority, pr->rdata, 2);
|
|
priority = ntohs(priority);
|
|
name = read_name_at_end(pr, ref, 2);
|
|
if(!name)
|
|
break;
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_MX(rr, name->data, priority);
|
|
jdns_string_delete(name);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_SRV:
|
|
{
|
|
unsigned short priority, weight, port;
|
|
jdns_string_t *name;
|
|
if(pr->rdlength < 6)
|
|
break;
|
|
memcpy(&priority, pr->rdata, 2);
|
|
priority = ntohs(priority);
|
|
memcpy(&weight, pr->rdata + 2, 2);
|
|
weight = ntohs(weight);
|
|
memcpy(&port, pr->rdata + 4, 2);
|
|
port = ntohs(port);
|
|
name = read_name_at_end(pr, ref, 6);
|
|
if(!name)
|
|
break;
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_SRV(rr, name->data, port, priority, weight);
|
|
jdns_string_delete(name);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_CNAME:
|
|
{
|
|
jdns_string_t *name;
|
|
name = read_name_at_end(pr, ref, 0);
|
|
if(!name)
|
|
break;
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_CNAME(rr, name->data);
|
|
jdns_string_delete(name);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_PTR:
|
|
{
|
|
jdns_string_t *name;
|
|
name = read_name_at_end(pr, ref, 0);
|
|
if(!name)
|
|
break;
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_PTR(rr, name->data);
|
|
jdns_string_delete(name);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_TXT:
|
|
{
|
|
jdns_stringlist_t *texts;
|
|
jdns_string_t *str;
|
|
int at, error;
|
|
texts = jdns_stringlist_new();
|
|
at = 0;
|
|
error = 0;
|
|
while(at < pr->rdlength)
|
|
{
|
|
str = read_text_string(pr, &at);
|
|
if(!str)
|
|
{
|
|
error = 1;
|
|
break;
|
|
}
|
|
jdns_stringlist_append(texts, str);
|
|
jdns_string_delete(str);
|
|
}
|
|
if(error)
|
|
{
|
|
jdns_stringlist_delete(texts);
|
|
break;
|
|
}
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_TXT(rr, texts);
|
|
jdns_stringlist_delete(texts);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_HINFO:
|
|
{
|
|
jdns_string_t *cpu, *os;
|
|
int at;
|
|
at = 0;
|
|
cpu = read_text_string(pr, &at);
|
|
if(!cpu)
|
|
break;
|
|
os = read_text_string(pr, &at);
|
|
if(!os)
|
|
{
|
|
jdns_string_delete(cpu);
|
|
break;
|
|
}
|
|
if(at != pr->rdlength)
|
|
{
|
|
jdns_string_delete(cpu);
|
|
jdns_string_delete(os);
|
|
break;
|
|
}
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_HINFO(rr, cpu, os);
|
|
jdns_string_delete(cpu);
|
|
jdns_string_delete(os);
|
|
break;
|
|
}
|
|
case JDNS_RTYPE_NS:
|
|
{
|
|
jdns_string_t *name;
|
|
name = read_name_at_end(pr, ref, 0);
|
|
if(!name)
|
|
break;
|
|
rr = jdns_rr_new();
|
|
jdns_rr_set_NS(rr, name->data);
|
|
jdns_string_delete(name);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
rr = jdns_rr_new();
|
|
rr->type = pr->qtype;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(rr)
|
|
{
|
|
rr->qclass = pr->qclass;
|
|
rr->owner = _ustrdup(pr->qname->data);
|
|
rr->ttl = pr->ttl;
|
|
rr->rdlength = pr->rdlength;
|
|
rr->rdata = jdns_copy_array(pr->rdata, pr->rdlength);
|
|
}
|
|
|
|
return rr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// jdns_response
|
|
//----------------------------------------------------------------------------
|
|
#define ARRAY_DELETE(array, count, dtor) \
|
|
{ \
|
|
if(count > 0) \
|
|
{ \
|
|
int n; \
|
|
for(n = 0; n < count; ++n) \
|
|
dtor(array[n]); \
|
|
} \
|
|
jdns_free(array); \
|
|
array = 0; \
|
|
count = 0; \
|
|
}
|
|
|
|
#define ARRAY_COPY(type, array_src, count_src, array_dest, count_dest, cctor) \
|
|
{ \
|
|
if(count_src > 0) \
|
|
{ \
|
|
int n; \
|
|
count_dest = count_src; \
|
|
array_dest = (type **)jdns_alloc(sizeof(type *) * count_dest); \
|
|
for(n = 0; n < count_dest; ++n) \
|
|
array_dest[n] = cctor(array_src[n]); \
|
|
} \
|
|
}
|
|
|
|
#define ARRAY_APPEND(type, array, count, item) \
|
|
{ \
|
|
if(!array) \
|
|
array = (type **)jdns_alloc(sizeof(type *)); \
|
|
else \
|
|
array = (type **)jdns_realloc(array, sizeof(type *) * (count + 1)); \
|
|
array[count] = item; \
|
|
++count; \
|
|
}
|
|
|
|
jdns_response_t *jdns_response_new()
|
|
{
|
|
jdns_response_t *r = alloc_type(jdns_response_t);
|
|
r->answerCount = 0;
|
|
r->answerRecords = 0;
|
|
r->authorityCount = 0;
|
|
r->authorityRecords = 0;
|
|
r->additionalCount = 0;
|
|
r->additionalRecords = 0;
|
|
return r;
|
|
}
|
|
|
|
jdns_response_t *jdns_response_copy(const jdns_response_t *r)
|
|
{
|
|
jdns_response_t *c = jdns_response_new();
|
|
ARRAY_COPY(jdns_rr_t, r->answerRecords, r->answerCount, c->answerRecords, c->answerCount, jdns_rr_copy);
|
|
ARRAY_COPY(jdns_rr_t, r->authorityRecords, r->authorityCount, c->authorityRecords, c->authorityCount, jdns_rr_copy);
|
|
ARRAY_COPY(jdns_rr_t, r->additionalRecords, r->additionalCount, c->additionalRecords, c->additionalCount, jdns_rr_copy);
|
|
return c;
|
|
}
|
|
|
|
void jdns_response_delete(jdns_response_t *r)
|
|
{
|
|
if(!r)
|
|
return;
|
|
ARRAY_DELETE(r->answerRecords, r->answerCount, jdns_rr_delete);
|
|
ARRAY_DELETE(r->authorityRecords, r->authorityCount, jdns_rr_delete);
|
|
ARRAY_DELETE(r->additionalRecords, r->additionalCount, jdns_rr_delete);
|
|
jdns_free(r);
|
|
}
|
|
|
|
void jdns_response_append_answer(jdns_response_t *r, const jdns_rr_t *rr)
|
|
{
|
|
ARRAY_APPEND(jdns_rr_t, r->answerRecords, r->answerCount, jdns_rr_copy(rr));
|
|
}
|
|
|
|
void jdns_response_append_authority(jdns_response_t *r, const jdns_rr_t *rr)
|
|
{
|
|
ARRAY_APPEND(jdns_rr_t, r->authorityRecords, r->authorityCount, jdns_rr_copy(rr));
|
|
}
|
|
|
|
void jdns_response_append_additional(jdns_response_t *r, const jdns_rr_t *rr)
|
|
{
|
|
ARRAY_APPEND(jdns_rr_t, r->additionalRecords, r->additionalCount, jdns_rr_copy(rr));
|
|
}
|
|
|
|
void jdns_response_remove_extra(jdns_response_t *r)
|
|
{
|
|
ARRAY_DELETE(r->authorityRecords, r->authorityCount, jdns_rr_delete);
|
|
ARRAY_DELETE(r->additionalRecords, r->additionalCount, jdns_rr_delete);
|
|
}
|
|
|
|
void jdns_response_remove_answer(jdns_response_t *r, int pos)
|
|
{
|
|
jdns_rr_t *rr = r->answerRecords[pos];
|
|
jdns_rr_delete(rr);
|
|
|
|
// free the position
|
|
if(r->answerCount > 1)
|
|
{
|
|
memmove(r->answerRecords + pos, r->answerRecords + pos + 1, (r->answerCount - pos - 1) * sizeof(void *));
|
|
--r->answerCount;
|
|
}
|
|
else
|
|
{
|
|
jdns_free(r->answerRecords);
|
|
r->answerRecords = 0;
|
|
r->answerCount = 0;
|
|
}
|
|
}
|