This repository has been archived on 2025-12-24. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
yachat/iris-legacy/iris/irisnet/jdns/jdns_packet.c
2025-12-25 01:38:25 +05:00

989 lines
22 KiB
C

/*
* Copyright (C) 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_packet.h"
#include "jdns_p.h"
// jer's endian functions
static unsigned short int net2short(const unsigned char **bufp)
{
unsigned short int i;
i = **bufp;
i <<= 8;
i |= *(*bufp + 1);
*bufp += 2;
return i;
}
static unsigned long int net2long(const unsigned char **bufp)
{
unsigned long int l;
l = **bufp;
l <<= 8;
l |= *(*bufp + 1);
l <<= 8;
l |= *(*bufp + 2);
l <<= 8;
l |= *(*bufp + 3);
*bufp += 4;
return l;
}
static void short2net(unsigned short int i, unsigned char **bufp)
{
*(*bufp + 1) = (unsigned char)i;
i >>= 8;
**bufp = (unsigned char)i;
*bufp += 2;
}
static void long2net(unsigned long int l, unsigned char **bufp)
{
*(*bufp + 3) = (unsigned char)l;
l >>= 8;
*(*bufp + 2) = (unsigned char)l;
l >>= 8;
*(*bufp + 1) = (unsigned char)l;
l >>= 8;
**bufp = (unsigned char)l;
*bufp += 4;
}
// label stuff
typedef struct jdns_packet_label
{
JDNS_OBJECT
int offset;
jdns_string_t *value;
} jdns_packet_label_t;
static void jdns_packet_label_delete(jdns_packet_label_t *a);
static jdns_packet_label_t *jdns_packet_label_copy(const jdns_packet_label_t *a);
static jdns_packet_label_t *jdns_packet_label_new()
{
jdns_packet_label_t *a = JDNS_OBJECT_NEW(jdns_packet_label);
a->offset = 0;
a->value = 0;
return a;
}
jdns_packet_label_t *jdns_packet_label_copy(const jdns_packet_label_t *a)
{
jdns_packet_label_t *c = jdns_packet_label_new();
c->offset = a->offset;
if(a->value)
c->value = jdns_string_copy(a->value);
return c;
}
void jdns_packet_label_delete(jdns_packet_label_t *a)
{
if(!a)
return;
jdns_string_delete(a->value);
jdns_object_free(a);
}
// gets an offset for decompression. does range and hop count checking also
static int getoffset(const unsigned char *str, int refsize, int *hopsleft)
{
unsigned short int x;
if(*hopsleft <= 0)
return -1;
--(*hopsleft);
x = str[0] & 0x3f;
x <<= 8;
x |= str[1];
// stay in bounds
if(x >= refsize)
return -1;
return x;
}
static int readlabel(const unsigned char *in, int insize, const unsigned char *ref, int refsize, int *_at, jdns_string_t **name)
{
int at;
unsigned char out[255];
int out_size;
const unsigned char *label, *last;
int hopped_yet;
int hopsleft;
int label_size;
at = *_at;
// stay in range
if(at < 0 || at >= insize)
return 0;
out_size = 0;
label = in + at;
hopped_yet = 0;
last = in + insize;
while(1)
{
// need a byte
if(label + 1 > last)
goto error;
// we make this a while loop instead of an 'if', in case
// there's a pointer to a pointer. as a precaution,
// we will hop no more than 8 times
hopsleft = 8;
while(*label & 0xc0)
{
int offset;
// need the next byte, too
if(label + 2 > last)
goto error;
offset = getoffset(label, refsize, &hopsleft);
if(offset == -1)
goto error;
label = ref + offset;
if(!hopped_yet)
{
at += 2;
hopped_yet = 1;
last = ref + refsize;
}
// need a byte
if(label + 1 > last)
goto error;
}
label_size = *label & 0x3f;
// null label? then we're done
if(label_size == 0)
{
if(!hopped_yet)
++at;
break;
}
// enough source bytes? (length byte + length)
if(label + label_size + 1 > last)
goto error;
// enough dest bytes? (length + dot)
if(out_size + label_size + 1 > 255)
goto error;
memcpy(out + out_size, label + 1, label_size);
out_size += label_size;
out[out_size] = '.';
++out_size;
if(!hopped_yet)
at += label_size + 1;
label += label_size + 1;
}
*_at = at;
*name = jdns_string_new();
jdns_string_set(*name, out, out_size);
return 1;
error:
return 0;
}
// this function compares labels in label format:
// [length] [value ...] [length] [value ...] [0]
static int matchlabel(const unsigned char *a, int asize, const unsigned char *b, int bsize, const unsigned char *ref, int refsize, int ahopsleft, int bhopsleft)
{
int n, alen, blen, offset;
// same pointer?
if(a == b)
return 1;
if(asize < 1 || bsize < 1)
return 0;
// always ensure we get called without a pointer
if(*a & 0xc0)
{
if(asize < 2)
return 0;
offset = getoffset(a, refsize, &ahopsleft);
if(offset == -1)
return 0;
return matchlabel(ref + offset, refsize - offset, b, bsize, ref, refsize, ahopsleft, bhopsleft);
}
if(*b & 0xc0)
{
if(bsize < 2)
return 0;
offset = getoffset(b, refsize, &bhopsleft);
if(offset == -1)
return 0;
return matchlabel(a, asize, ref + offset, refsize - offset, ref, refsize, ahopsleft, bhopsleft);
}
alen = *a & 0x3f;
blen = *b & 0x3f;
// must be same length
if(alen != blen)
return 0;
// done?
if(alen == 0)
return 1;
// length byte + length + first byte of next label
if(asize < alen + 2)
return 0;
if(bsize < blen + 2)
return 0;
// compare the value
for(n = 1; n < alen + 1; ++n)
{
if(a[n] != b[n])
return 0;
}
// try next labels
n = alen + 1;
return matchlabel(a + n, asize - n, b + n, bsize - n, ref, refsize, ahopsleft, bhopsleft);
}
int jdns_packet_name_isvalid(const unsigned char *name, int size)
{
int n, at, len;
// at least one byte, no larger than 254 (one byte is gained when
// converting to a label, which has a 255 byte max)
if(size < 1 || size > 254)
return 0;
// last byte must be a dot
if(name[size - 1] != '.')
return 0;
// first byte can't be a dot if there are characters after
if(size > 1 && name[0] == '.')
return 0;
// each sublabel must be between 1 and 63 in length
at = 0;
while(1)
{
// search for dot or end
for(n = at; n < size; ++n)
{
if(name[n] == '.')
break;
}
// length of last one is always zero
if(n >= size)
break;
len = n - at;
if(len < 1 || len > 63)
return 0;
at = n + 1; // skip over the dot
}
return 1;
}
// this function assumes label is pointing to a 255 byte buffer
static int name_to_label(const jdns_string_t *name, unsigned char *label)
{
int n, i, at, len;
if(!jdns_packet_name_isvalid(name->data, name->size))
return -1;
if(name->size == 1)
{
label[0] = 0;
return 1;
}
at = 0;
i = 0;
while(1)
{
// search for dot or end
for(n = at; n < name->size; ++n)
{
if(name->data[n] == '.')
break;
}
len = n - at;
if(i + (len + 1) > 255) // length byte + length
return 0;
label[i++] = len;
memcpy(label + i, name->data + at, len);
i += len;
if(n >= name->size) // end?
break;
at = n + 1; // skip over the dot
}
return i;
}
// lookup list is made of jdns_packet_labels
static int writelabel(const jdns_string_t *name, int at, int left, unsigned char **bufp, jdns_list_t *lookup)
{
unsigned char label[255];
int n, i, len;
unsigned char *l;
unsigned char *ref;
int refsize;
len = name_to_label(name, label);
if(len == -1)
return 0;
ref = *bufp - at;
refsize = at + left;
for(n = 0; label[n]; n += label[n] + 1)
{
for(i = 0; i < lookup->count; ++i)
{
jdns_packet_label_t *pl = (jdns_packet_label_t *)lookup->item[i];
if(matchlabel(label + n, len - n, pl->value->data, pl->value->size, ref, refsize, 8, 8))
{
// set up a pointer right here, overwriting
// the length byte and the first content
// byte of this section within 'label'.
// this is safe, because the length value
// will always be greater than zero,
// ensuring we have two bytes available to
// use.
l = label + n;
short2net((unsigned short int)pl->offset, &l);
label[n] |= 0xc0;
len = n + 2; // cut things short
break;
}
}
if(label[n] & 0xc0) // double loop, so break again
break;
}
if(left < len)
return 0;
// copy into buffer, point there now
memcpy(*bufp, label, len);
l = *bufp;
*bufp += len;
// for each new label, store its location for future compression
for(n = 0; l[n]; n += l[n] + 1)
{
jdns_string_t *str;
jdns_packet_label_t *pl;
if(l[n] & 0xc0)
break;
pl = jdns_packet_label_new();
str = jdns_string_new();
jdns_string_set(str, l + n, len - n);
pl->offset = l + n - ref;
pl->value = str;
jdns_list_insert(lookup, pl, -1);
}
return 1;
}
//----------------------------------------------------------------------------
// jdns_packet_write
//----------------------------------------------------------------------------
#define JDNS_PACKET_WRITE_RAW 0
#define JDNS_PACKET_WRITE_NAME 1
struct jdns_packet_write
{
JDNS_OBJECT
int type;
jdns_string_t *value;
};
void jdns_packet_write_delete(jdns_packet_write_t *a);
jdns_packet_write_t *jdns_packet_write_copy(const jdns_packet_write_t *a);
jdns_packet_write_t *jdns_packet_write_new()
{
jdns_packet_write_t *a = JDNS_OBJECT_NEW(jdns_packet_write);
a->type = 0;
a->value = 0;
return a;
}
jdns_packet_write_t *jdns_packet_write_copy(const jdns_packet_write_t *a)
{
jdns_packet_write_t *c = jdns_packet_write_new();
c->type = a->type;
if(a->value)
c->value = jdns_string_copy(a->value);
return c;
}
void jdns_packet_write_delete(jdns_packet_write_t *a)
{
if(!a)
return;
jdns_string_delete(a->value);
jdns_object_free(a);
}
//----------------------------------------------------------------------------
// jdns_packet_question
//----------------------------------------------------------------------------
jdns_packet_question_t *jdns_packet_question_new()
{
jdns_packet_question_t *a = JDNS_OBJECT_NEW(jdns_packet_question);
a->qname = 0;
a->qtype = 0;
a->qclass = 0;
return a;
}
jdns_packet_question_t *jdns_packet_question_copy(const jdns_packet_question_t *a)
{
jdns_packet_question_t *c = jdns_packet_question_new();
if(a->qname)
c->qname = jdns_string_copy(a->qname);
c->qtype = a->qtype;
c->qclass = a->qclass;
return c;
}
void jdns_packet_question_delete(jdns_packet_question_t *a)
{
if(!a)
return;
jdns_string_delete(a->qname);
jdns_object_free(a);
}
//----------------------------------------------------------------------------
// jdns_packet_resource
//----------------------------------------------------------------------------
jdns_packet_resource_t *jdns_packet_resource_new()
{
jdns_packet_resource_t *a = JDNS_OBJECT_NEW(jdns_packet_resource);
a->qname = 0;
a->qtype = 0;
a->qclass = 0;
a->ttl = 0;
a->rdlength = 0;
a->rdata = 0;
a->writelog = jdns_list_new();
a->writelog->valueList = 1;
return a;
}
jdns_packet_resource_t *jdns_packet_resource_copy(const jdns_packet_resource_t *a)
{
jdns_packet_resource_t *c = jdns_packet_resource_new();
if(a->qname)
c->qname = jdns_string_copy(a->qname);
c->qtype = a->qtype;
c->qclass = a->qclass;
c->ttl = a->ttl;
c->rdlength = a->rdlength;
c->rdata = jdns_copy_array(a->rdata, a->rdlength);
jdns_list_delete(c->writelog);
c->writelog = jdns_list_copy(a->writelog);
return c;
}
void jdns_packet_resource_delete(jdns_packet_resource_t *a)
{
if(!a)
return;
jdns_string_delete(a->qname);
if(a->rdata)
jdns_free(a->rdata);
jdns_list_delete(a->writelog);
jdns_object_free(a);
}
void jdns_packet_resource_add_bytes(jdns_packet_resource_t *a, const unsigned char *data, int size)
{
jdns_packet_write_t *write = jdns_packet_write_new();
write->type = JDNS_PACKET_WRITE_RAW;
write->value = jdns_string_new();
jdns_string_set(write->value, data, size);
jdns_list_insert_value(a->writelog, write, -1);
jdns_packet_write_delete(write);
}
void jdns_packet_resource_add_name(jdns_packet_resource_t *a, const jdns_string_t *name)
{
jdns_packet_write_t *write = jdns_packet_write_new();
write->type = JDNS_PACKET_WRITE_NAME;
write->value = jdns_string_copy(name);
jdns_list_insert_value(a->writelog, write, -1);
jdns_packet_write_delete(write);
}
int jdns_packet_resource_read_name(const jdns_packet_resource_t *a, const jdns_packet_t *p, int *at, jdns_string_t **name)
{
return readlabel(a->rdata, a->rdlength, p->raw_data, p->raw_size, at, name);
}
//----------------------------------------------------------------------------
// jdns_packet
//----------------------------------------------------------------------------
// note: both process_qsection and process_rrsection modify the 'dest' list,
// even if later items cause an error. this turns out to be convenient
// for handling truncated dns packets
static int process_qsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp)
{
int n;
int offset, at;
jdns_string_t *name = 0;
const unsigned char *buf;
buf = *bufp;
for(n = 0; n < count; ++n)
{
jdns_packet_question_t *q;
offset = buf - data;
at = 0;
if(!readlabel(data + offset, size - offset, data, size, &at, &name))
goto error;
offset += at;
// need 4 more bytes
if(size - offset < 4)
goto error;
buf = data + offset;
q = jdns_packet_question_new();
q->qname = name;
name = 0;
q->qtype = net2short(&buf);
q->qclass = net2short(&buf);
jdns_list_insert_value(dest, q, -1);
jdns_packet_question_delete(q);
}
*bufp = buf;
return 1;
error:
jdns_string_delete(name);
return 0;
}
static int process_rrsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp)
{
int n;
int offset, at;
jdns_string_t *name = 0;
const unsigned char *buf;
buf = *bufp;
for(n = 0; n < count; ++n)
{
jdns_packet_resource_t *r;
offset = buf - data;
at = 0;
if(!readlabel(data + offset, size - offset, data, size, &at, &name))
goto error;
offset += at;
// need 10 more bytes
if(offset + 10 > size)
goto error;
buf = data + offset;
r = jdns_packet_resource_new();
r->qname = name;
name = 0;
r->qtype = net2short(&buf);
r->qclass = net2short(&buf);
r->ttl = net2long(&buf);
r->rdlength = net2short(&buf);
offset = buf - data;
// make sure we have enough for the rdata
if(size - offset < r->rdlength)
{
jdns_packet_resource_delete(r);
goto error;
}
r->rdata = jdns_copy_array(buf, r->rdlength);
buf += r->rdlength;
jdns_list_insert_value(dest, r, -1);
jdns_packet_resource_delete(r);
}
*bufp = buf;
return 1;
error:
jdns_string_delete(name);
return 0;
}
static int append_qsection(const jdns_list_t *src, int at, int left, unsigned char **bufp, jdns_list_t *lookup)
{
unsigned char *buf, *start, *last;
int n;
buf = *bufp;
start = buf - at;
last = buf + left;
for(n = 0; n < src->count; ++n)
{
jdns_packet_question_t *q = (jdns_packet_question_t *)src->item[n];
if(!writelabel(q->qname, buf - start, last - buf, &buf, lookup))
goto error;
if(buf + 4 > last)
goto error;
short2net(q->qtype, &buf);
short2net(q->qclass, &buf);
}
*bufp = buf;
return 1;
error:
return 0;
}
static int append_rrsection(const jdns_list_t *src, int at, int left, unsigned char **bufp, jdns_list_t *lookup)
{
unsigned char *buf, *start, *last, *rdlengthp;
int n, i, rdlength;
buf = *bufp;
start = buf - at;
last = buf + left;
for(n = 0; n < src->count; ++n)
{
jdns_packet_resource_t *r = (jdns_packet_resource_t *)src->item[n];
if(!writelabel(r->qname, buf - start, last - buf, &buf, lookup))
goto error;
if(buf + 10 > last)
goto error;
short2net(r->qtype, &buf);
short2net(r->qclass, &buf);
long2net(r->ttl, &buf);
// skip over rdlength
rdlengthp = buf;
buf += 2;
// play write log
rdlength = 0;
for(i = 0; i < r->writelog->count; ++i)
{
jdns_packet_write_t *write = (jdns_packet_write_t *)r->writelog->item[i];
if(write->type == JDNS_PACKET_WRITE_RAW)
{
if(buf + write->value->size > last)
goto error;
memcpy(buf, write->value->data, write->value->size);
buf += write->value->size;
}
else // JDNS_PACKET_WRITE_NAME
{
if(!writelabel(write->value, buf - start, last - buf, &buf, lookup))
goto error;
}
}
i = buf - rdlengthp; // should be rdata size + 2
short2net((unsigned short int)(i - 2), &rdlengthp);
}
*bufp = buf;
return 1;
error:
return 0;
}
jdns_packet_t *jdns_packet_new()
{
jdns_packet_t *a = JDNS_OBJECT_NEW(jdns_packet);
a->id = 0;
a->opts.qr = 0;
a->opts.opcode = 0;
a->opts.aa = 0;
a->opts.tc = 0;
a->opts.rd = 0;
a->opts.ra = 0;
a->opts.z = 0;
a->opts.rcode = 0;
a->questions = jdns_list_new();
a->answerRecords = jdns_list_new();
a->authorityRecords = jdns_list_new();
a->additionalRecords = jdns_list_new();
a->questions->valueList = 1;
a->answerRecords->valueList = 1;
a->authorityRecords->valueList = 1;
a->additionalRecords->valueList = 1;
a->fully_parsed = 0;
a->raw_size = 0;
a->raw_data = 0;
return a;
}
jdns_packet_t *jdns_packet_copy(const jdns_packet_t *a)
{
jdns_packet_t *c = jdns_packet_new();
c->id = a->id;
c->opts.qr = a->opts.qr;
c->opts.opcode = a->opts.opcode;
c->opts.aa = a->opts.aa;
c->opts.tc = a->opts.tc;
c->opts.rd = a->opts.rd;
c->opts.ra = a->opts.ra;
c->opts.z = a->opts.z;
c->opts.rcode = a->opts.rcode;
jdns_list_delete(c->questions);
jdns_list_delete(c->answerRecords);
jdns_list_delete(c->authorityRecords);
jdns_list_delete(c->additionalRecords);
c->questions = jdns_list_copy(a->questions);
c->answerRecords = jdns_list_copy(a->answerRecords);
c->authorityRecords = jdns_list_copy(a->authorityRecords);
c->additionalRecords = jdns_list_copy(a->additionalRecords);
c->fully_parsed = a->fully_parsed;
c->raw_size = a->raw_size;
c->raw_data = jdns_copy_array(a->raw_data, a->raw_size);
return c;
}
void jdns_packet_delete(jdns_packet_t *a)
{
if(!a)
return;
jdns_list_delete(a->questions);
jdns_list_delete(a->answerRecords);
jdns_list_delete(a->authorityRecords);
jdns_list_delete(a->additionalRecords);
if(a->raw_data)
jdns_free(a->raw_data);
jdns_object_free(a);
}
int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size)
{
jdns_packet_t *tmp = 0;
const unsigned char *buf;
// need at least some data
if(!data || size == 0)
return 0;
// header (id + options + item counts) is 12 bytes
if(size < 12)
goto error;
tmp = jdns_packet_new();
buf = data;
// id
tmp->id = net2short(&buf);
// options
if(buf[0] & 0x80) // qr is bit 7
tmp->opts.qr = 1;
tmp->opts.opcode = (buf[0] & 0x78) >> 3; // opcode is bits 6,5,4,3
if(buf[0] & 0x04) // aa is bit 2
tmp->opts.aa = 1;
if(buf[0] & 0x02) // tc is bit 1
tmp->opts.tc = 1;
if(buf[0] & 0x01) // rd is bit 0
tmp->opts.rd = 1;
if(buf[1] & 0x80) // ra is bit 7 (second byte)
tmp->opts.ra = 1;
tmp->opts.z = (buf[1] & 0x70) >> 4; // z is bits 6,5,4
tmp->opts.rcode = buf[1] & 0x0f; // rcode is bits 3,2,1,0
buf += 2;
// item counts
tmp->qdcount = net2short(&buf);
tmp->ancount = net2short(&buf);
tmp->nscount = net2short(&buf);
tmp->arcount = net2short(&buf);
// if these fail, we don't count them as errors, since the packet
// might have been truncated
if(!process_qsection(tmp->questions, tmp->qdcount, data, size, &buf))
goto skip;
if(!process_rrsection(tmp->answerRecords, tmp->ancount, data, size, &buf))
goto skip;
if(!process_rrsection(tmp->authorityRecords, tmp->nscount, data, size, &buf))
goto skip;
if(!process_rrsection(tmp->additionalRecords, tmp->arcount, data, size, &buf))
goto skip;
tmp->fully_parsed = 1;
skip:
// keep the raw data for reference during rdata parsing
tmp->raw_size = size;
tmp->raw_data = jdns_copy_array(data, size);
*a = tmp;
return 1;
error:
jdns_packet_delete(tmp);
return 0;
}
int jdns_packet_export(jdns_packet_t *a, int maxsize)
{
unsigned char *block = 0;
unsigned char *buf, *last;
unsigned char c;
int size;
jdns_list_t *lookup = 0; // to hold jdns_packet_label_t
// clear out any existing raw data before we begin
if(a->raw_data)
{
jdns_free(a->raw_data);
a->raw_data = 0;
a->raw_size = 0;
}
// preallocate
size = maxsize;
block = (unsigned char *)jdns_alloc(size);
memset(block, 0, size);
buf = block;
last = block + size;
if(size < 12)
goto error;
short2net(a->id, &buf);
if(a->opts.qr)
buf[0] |= 0x80;
c = (unsigned char)a->opts.opcode;
buf[0] |= c << 3;
if(a->opts.aa)
buf[0] |= 0x04;
if(a->opts.tc)
buf[0] |= 0x02;
if(a->opts.rd)
buf[0] |= 0x01;
if(a->opts.ra)
buf[1] |= 0x80;
c = (unsigned char)a->opts.z;
buf[1] |= c << 4;
c = (unsigned char)a->opts.rcode;
buf[1] |= c;
buf += 2;
short2net((unsigned short int)a->questions->count, &buf);
short2net((unsigned short int)a->answerRecords->count, &buf);
short2net((unsigned short int)a->authorityRecords->count, &buf);
short2net((unsigned short int)a->additionalRecords->count, &buf);
// append sections
lookup = jdns_list_new();
lookup->autoDelete = 1;
if(!append_qsection(a->questions, buf - block, last - buf, &buf, lookup))
goto error;
if(!append_rrsection(a->answerRecords, buf - block, last - buf, &buf, lookup))
goto error;
if(!append_rrsection(a->authorityRecords, buf - block, last - buf, &buf, lookup))
goto error;
if(!append_rrsection(a->additionalRecords, buf - block, last - buf, &buf, lookup))
goto error;
// done with all sections
jdns_list_delete(lookup);
// condense
size = buf - block;
block = (unsigned char *)jdns_realloc(block, size);
// finalize
a->qdcount = a->questions->count;
a->ancount = a->answerRecords->count;
a->nscount = a->authorityRecords->count;
a->arcount = a->additionalRecords->count;
a->raw_data = block;
a->raw_size = size;
return 1;
error:
jdns_list_delete(lookup);
if(block)
jdns_free(block);
return 0;
}