blob: 488b30ae9007312012d8219c1c0b9aaf260db4a8 [file] [log] [blame]
/* $Id: dns.c 113 2007-02-06 10:17:49Z lennart $ */
/***
This file is part of nss-mdns.
nss-mdns is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
nss-mdns is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with nss-mdns; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include "dns.h"
#define SET_16_P(data, value) \
do { \
uint16_t __value = value; \
memcpy(data, &__value, sizeof(uint16_t)); \
} while(0)
#define SET_16(data, idx, value) \
SET_16_P(((uint8_t *)data) + idx * sizeof(uint16_t)/sizeof(uint8_t), value)
#define GET_16_P(data, value) \
do { \
uint8_t *__value = ((uint8_t *)&value); \
memcpy(__value, data, sizeof(uint16_t)); \
} while(0)
#define GET_16(data, idx, value) \
GET_16_P(((uint8_t *)data) + idx * sizeof(uint16_t)/sizeof(uint8_t), value)
#define GET_32_P(data, value) \
do { \
uint8_t *__value = ((uint8_t *)&value); \
memcpy(__value, data, sizeof(uint32_t)); \
} while(0)
#define GET_32(data, idx, value) \
GET_32_P(((uint8_t *)data) + idx * sizeof(uint32_t)/sizeof(uint8_t), value)
struct dns_packet* dns_packet_new(void) {
struct dns_packet *p;
if (!(p = malloc(sizeof(struct dns_packet))))
return NULL;
p->size = p->rindex = 2*6;
memset(p->data, 0, p->size);
return p;
}
void dns_packet_free(struct dns_packet *p) {
assert(p);
free(p);
}
void dns_packet_set_field(struct dns_packet *p, unsigned idx, uint16_t v) {
assert(p);
assert(idx < 2*6);
SET_16(p->data, idx, htons(v));
}
uint16_t dns_packet_get_field(struct dns_packet *p, unsigned idx) {
assert(p);
assert(idx < 2*6);
uint16_t r;
GET_16(p->data, idx, r);
return ntohs(r);
}
uint8_t* dns_packet_append_name(struct dns_packet *p, const char *name) {
uint8_t *d, *f = NULL;
assert(p);
for (;;) {
size_t n = strcspn(name, ".");
if (!n || n > 63)
return NULL;
d = dns_packet_extend(p, n+1);
if (!f)
f = d;
d[0] = n;
memcpy(d+1, name, n);
name += n;
/* no trailing dot */
if (!*name)
break;
name ++;
/* trailing dot */
if (!*name)
break;
}
d = dns_packet_extend(p, 1);
d[0] = 0;
return f;
}
uint8_t* dns_packet_append_uint16(struct dns_packet *p, uint16_t v) {
uint8_t *d;
assert(p);
d = dns_packet_extend(p, sizeof(uint16_t));
SET_16_P(d, htons(v));
return d;
}
uint8_t *dns_packet_extend(struct dns_packet *p, size_t l) {
uint8_t *d;
assert(p);
assert(p->size+l <= sizeof(p->data));
d = p->data + p->size;
p->size += l;
return d;
}
uint8_t *dns_packet_append_name_compressed(struct dns_packet *p, const char *name, uint8_t *prev) {
uint8_t *d;
signed long k;
assert(p);
if (!prev)
return dns_packet_append_name(p, name);
k = prev - p->data;
if (k < 0 || k >= 0x4000 || (size_t) k >= p->size)
return dns_packet_append_name(p, name);
d = dns_packet_extend(p, sizeof(uint16_t));
SET_16_P(d, htons((0xC000 | k)));
return prev;
}
int dns_packet_check_valid(struct dns_packet *p) {
uint16_t flags;
assert(p);
if (p->size < 12)
return -1;
flags = dns_packet_get_field(p, DNS_FIELD_FLAGS);
if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE)
return -1;
return 0;
}
int dns_packet_check_valid_response(struct dns_packet *p) {
uint16_t flags;
assert(p);
if (dns_packet_check_valid(p) < 0)
return -1;
flags = dns_packet_get_field(p, DNS_FIELD_FLAGS);
if (!(flags & DNS_FLAG_QR))
return -1;
return 0;
}
static ssize_t consume_labels(struct dns_packet *p, size_t idx, char *ret_name, size_t l) {
ssize_t ret = 0;
int compressed = 0;
int first_label = 1;
int j;
assert(p && ret_name && l);
for (j = 0; j < 63; j++) {
uint8_t n;
if (idx+1 > p->size)
return -1;
n = p->data[idx];
if (!n) {
idx++;
if (!compressed)
ret++;
if (l < 1)
return -1;
*ret_name = 0;
return ret;
} else if (n <= 63) {
/* Uncompressed label */
idx++;
if (!compressed)
ret++;
if (idx + n > p->size)
return -1;
if ((size_t) n + 1 > l)
return -1;
if (!first_label) {
*(ret_name++) = '.';
l--;
} else
first_label = 0;
memcpy(ret_name, p->data + idx, n);
idx += n;
ret_name += n;
l -= n;
if (!compressed)
ret += n;
} else if ((n & 0xC0) == 0xC0) {
size_t nptr;
/* Compressed label */
if (idx+2 > p->size)
return -1;
nptr = ((size_t) (p->data[idx] & ~0xC0)) << 8 | p->data[idx+1];
if (nptr >= idx || nptr < 12)
return -1;
idx = nptr;
if (!compressed)
ret += 2;
compressed = 1;
} else
return -1;
}
return -1;
}
int dns_packet_consume_name(struct dns_packet *p, char *ret_name, size_t l) {
ssize_t r;
if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
return -1;
p->rindex += r;
return 0;
}
int dns_packet_consume_uint16(struct dns_packet *p, uint16_t *ret_v) {
assert(p && ret_v);
uint16_t r;
if (p->rindex + sizeof(uint16_t) > p->size)
return -1;
GET_16_P(p->data + p->rindex, r);
*ret_v = ntohs(r);
p->rindex += sizeof(uint16_t);
return 0;
}
int dns_packet_consume_uint32(struct dns_packet *p, uint32_t *ret_v) {
assert(p && ret_v);
uint32_t r;
if (p->rindex + sizeof(uint32_t) > p->size)
return -1;
GET_32_P(p->data + p->rindex, r);
*ret_v = ntohl(r);
p->rindex += sizeof(uint32_t);
return 0;
}
int dns_packet_consume_bytes(struct dns_packet *p, void *ret_data, size_t l) {
assert(p && ret_data && l > 0);
if (p->rindex + l > p->size)
return -1;
memcpy(ret_data, p->data + p->rindex, l);
p->rindex += l;
return 0;
}
int dns_packet_consume_seek(struct dns_packet *p, size_t length) {
assert(p);
if (!length)
return 0;
if (p->rindex + length > p->size)
return -1;
p->rindex += length;
return 0;
}