990 lines
22 KiB
C
990 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;
|
||
|
|
}
|