/*
- * $Id: rfc1035.c,v 1.41 2005/05/09 02:32:09 hno Exp $
+ * $Id: rfc1035.c,v 1.42 2005/05/10 08:23:07 hno Exp $
*
* Low level DNS protocol routines
* AUTHOR: Duane Wessels
*/
#include "config.h"
+#include "util.h"
#if HAVE_STDIO_H
#include <stdio.h>
#endif
-typedef struct _rfc1035_header rfc1035_header;
int rfc1035_errno;
const char *rfc1035_error_message;
-struct _rfc1035_header {
- unsigned short id;
- unsigned int qr:1;
- unsigned int opcode:4;
- unsigned int aa:1;
- unsigned int tc:1;
- unsigned int rd:1;
- unsigned int ra:1;
- unsigned int rcode:4;
- unsigned short qdcount;
- unsigned short ancount;
- unsigned short nscount;
- unsigned short arcount;
-};
/*
* rfc1035HeaderPack()
* Returns number of octets packed (should always be 12)
*/
static off_t
-rfc1035HeaderPack(char *buf, size_t sz, rfc1035_header * hdr)
+rfc1035HeaderPack(char *buf, size_t sz, rfc1035_message * hdr)
{
off_t off = 0;
unsigned short s;
rfc1035NamePack(char *buf, size_t sz, const char *name)
{
off_t off = 0;
- char *copy = strdup(name);
+ char *copy = xstrdup(name);
char *t;
/*
* NOTE: use of strtok here makes names like foo....com valid.
*/
for (t = strtok(copy, "."); t; t = strtok(NULL, "."))
off += rfc1035LabelPack(buf + off, sz - off, t);
- free(copy);
+ xfree(copy);
off += rfc1035LabelPack(buf + off, sz - off, NULL);
assert(off <= sz);
return off;
/*
* rfc1035HeaderUnpack()
*
- * Unpacks a RFC1035 message header buffer into a rfc1035_header
- * structure.
+ * Unpacks a RFC1035 message header buffer into the header fields
+ * of the rfc1035_message structure.
*
* Updates the buffer offset, which is the same as number of
* octects unpacked since the header starts at offset 0.
* Returns 0 (success) or 1 (error)
*/
static int
-rfc1035HeaderUnpack(const char *buf, size_t sz, off_t * off, rfc1035_header * h)
+rfc1035HeaderUnpack(const char *buf, size_t sz, off_t * off, rfc1035_message * h)
{
unsigned short s;
unsigned short t;
RR->rdlength = rdlength;
switch (RR->type) {
case RFC1035_TYPE_PTR:
- RR->rdata = malloc(RFC1035_MAXHOSTNAMESZ);
+ RR->rdata = xmalloc(RFC1035_MAXHOSTNAMESZ);
rdata_off = *off;
RR->rdlength = 0; /* Filled in by rfc1035NameUnpack */
if (rfc1035NameUnpack(buf, sz, &rdata_off, &RR->rdlength, RR->rdata, RFC1035_MAXHOSTNAMESZ, 0))
* the RDATA area.
*/
RFC1035_UNPACK_DEBUG;
- free(RR->rdata);
+ xfree(RR->rdata);
memset(RR, '\0', sizeof(*RR));
return 1;
}
break;
case RFC1035_TYPE_A:
default:
- RR->rdata = malloc(rdlength);
+ RR->rdata = xmalloc(rdlength);
memcpy(RR->rdata, buf + (*off), rdlength);
break;
}
}
}
-void
+static void
rfc1035RRDestroy(rfc1035_rr * rr, int n)
{
if (rr == NULL)
assert(n > 0);
while (n--) {
if (rr[n].rdata)
- free(rr[n].rdata);
+ xfree(rr[n].rdata);
+ }
+ xfree(rr);
+}
+
+/*
+ * rfc1035QueryUnpack()
+ *
+ * Unpacks a RFC1035 Query Record into 'query' from a message buffer.
+ *
+ * Updates the new message buffer offset.
+ *
+ * Returns 0 (success) or 1 (error)
+ */
+static int
+rfc1035QueryUnpack(const char *buf, size_t sz, off_t * off, rfc1035_query * query)
+{
+ unsigned short s;
+ if (rfc1035NameUnpack(buf, sz, off, NULL, query->name, RFC1035_MAXHOSTNAMESZ, 0)) {
+ RFC1035_UNPACK_DEBUG;
+ memset(query, '\0', sizeof(*query));
+ return 1;
}
- free(rr);
+ if (*off + 4 > sz) {
+ RFC1035_UNPACK_DEBUG;
+ memset(query, '\0', sizeof(*query));
+ return 1;
+ }
+ memcpy(&s, buf + *off, 2);
+ *off += 2;
+ buf+=2;
+ query->qtype = ntohs(s);
+ memcpy(&s, buf + *off, 2);
+ *off += 2;
+ buf+=2;
+ query->qtype = ntohs(s);
+ return 0;
+}
+
+void
+rfc1035MessageDestroy(rfc1035_message *msg)
+{
+ if (!msg)
+ return;
+ if (msg->query)
+ xfree(msg->query);
+ if (msg->answer)
+ rfc1035RRDestroy(msg->answer, msg->ancount);
+ xfree(msg);
}
/*
- * rfc1035AnswersUnpack()
+ * rfc1035MessageUnpack()
*
* Takes the contents of a DNS reply and fills in an array
* of resource record structures. The records array is allocated
*/
int
-rfc1035AnswersUnpack(const char *buf,
+rfc1035MessageUnpack(const char *buf,
size_t sz,
- rfc1035_rr ** records,
- unsigned short *id)
+ rfc1035_message ** answer)
{
off_t off = 0;
- int l;
int i;
int nr = 0;
- rfc1035_header hdr;
+ rfc1035_message *msg;
rfc1035_rr *recs;
- memset(&hdr, '\0', sizeof(hdr));
- if (rfc1035HeaderUnpack(buf + off, sz - off, &off, &hdr)) {
+ rfc1035_query *querys;
+ msg = xcalloc(1, sizeof(*msg));
+ if (rfc1035HeaderUnpack(buf + off, sz - off, &off, msg)) {
RFC1035_UNPACK_DEBUG;
rfc1035SetErrno(rfc1035_unpack_error);
+ xfree(msg);
return -rfc1035_unpack_error;
}
- *id = hdr.id;
rfc1035_errno = 0;
rfc1035_error_message = NULL;
- if (hdr.rcode) {
+ if (msg->rcode) {
RFC1035_UNPACK_DEBUG;
- rfc1035SetErrno((int) hdr.rcode);
+ rfc1035SetErrno((int) msg->rcode);
+ xfree(msg);
return -rfc1035_errno;
}
- i = (int) hdr.qdcount;
- /* skip question */
- while (i--) {
- do {
- l = (int) (unsigned char) *(buf + off);
- off++;
- if (l > 191) { /* compression */
- off++;
- break;
- } else if (l > RFC1035_MAXLABELSZ) {
- /* illegal combination of compression bits */
- RFC1035_UNPACK_DEBUG;
- rfc1035SetErrno(rfc1035_unpack_error);
- return -rfc1035_unpack_error;
- } else {
- off += l;
- }
- } while (l > 0); /* a zero-length label terminates */
- off += 4; /* qtype, qclass */
- if (off > sz) {
+ i = (int) msg->qdcount;
+ if (i != 1) {
+ /* This can not be an answer to our queries.. */
+ RFC1035_UNPACK_DEBUG;
+ rfc1035SetErrno(rfc1035_unpack_error);
+ xfree(msg);
+ return -rfc1035_unpack_error;
+ }
+ querys = msg->query = xcalloc((int)msg->qdcount, sizeof(*querys));
+ for (i = 0; i < (int)msg->qdcount; i++) {
+ if (rfc1035QueryUnpack(buf, sz, &off, &querys[i])) {
RFC1035_UNPACK_DEBUG;
rfc1035SetErrno(rfc1035_unpack_error);
+ rfc1035MessageDestroy(msg);
return -rfc1035_unpack_error;
}
}
- if (hdr.ancount == 0)
+ *answer = msg;
+ if (msg->ancount == 0)
return 0;
- recs = calloc((int)hdr.ancount, sizeof(*recs));
- for (i = 0; i < (int)hdr.ancount; i++) {
+ recs = msg->answer = xcalloc((int)msg->ancount, sizeof(*recs));
+ for (i = 0; i < (int)msg->ancount; i++) {
if (off >= sz) { /* corrupt packet */
RFC1035_UNPACK_DEBUG;
break;
* we expected to unpack some answers (ancount != 0), but
* didn't actually get any.
*/
- free(recs);
+ rfc1035MessageDestroy(msg);
+ *answer = NULL;
rfc1035SetErrno(rfc1035_unpack_error);
return -rfc1035_unpack_error;
}
- *records = recs;
return nr;
}
ssize_t
rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid)
{
- static rfc1035_header h;
+ static rfc1035_message h;
size_t offset = 0;
memset(&h, '\0', sizeof(h));
h.id = qid;
ssize_t
rfc1035BuildPTRQuery(const struct IN_ADDR addr, char *buf, size_t sz, unsigned short qid)
{
- static rfc1035_header h;
+ static rfc1035_message h;
size_t offset = 0;
static char rev[32];
unsigned int i;
/*
- * $Id: dns_internal.cc,v 1.72 2005/05/09 16:33:55 hno Exp $
+ * $Id: dns_internal.cc,v 1.73 2005/05/10 08:23:07 hno Exp $
*
* DEBUG: section 78 DNS lookups; interacts with lib/rfc1035.c
* AUTHOR: Duane Wessels
idnsGrokReply(const char *buf, size_t sz)
{
int n;
- rfc1035_rr *answers = NULL;
- unsigned short rid;
+ rfc1035_message *message = NULL;
idns_query *q;
- n = rfc1035AnswersUnpack(buf,
+ n = rfc1035MessageUnpack(buf,
sz,
- &answers,
- &rid);
- debug(78, 3) ("idnsGrokReply: ID %#hx, %d answers\n", rid, n);
+ &message);
- if (n == -15 /* rfc1035_unpack_error */ ) {
+ if (message == NULL) {
debug(78, 1) ("idnsGrokReply: Malformed DNS response\n");
return;
}
- q = idnsFindQuery(rid);
+ debug(78, 3) ("idnsGrokReply: ID %#hx, %d answers\n", message->id, n);
+
+ q = idnsFindQuery(message->id);
+
+ /* FIXME: We should also verify the query to match ours */
if (q == NULL) {
debug(78, 3) ("idnsGrokReply: Late response\n");
- rfc1035RRDestroy(answers, n);
+ rfc1035MessageDestroy(message);
return;
}
q->error = NULL;
if (n < 0) {
- debug(78, 3) ("idnsGrokReply: error %d\n", rfc1035_errno);
+ debug(78, 3) ("idnsGrokReply: error %s (%d)\n", rfc1035_error_message, rfc1035_errno);
q->error = rfc1035_error_message;
q->rcode = -n;
* unable to process this query due to a problem with
* the name server."
*/
- assert(NULL == answers);
+ rfc1035MessageDestroy(message);
q->start_t = current_time;
q->id = idnsQueryID();
rfc1035SetQueryID(q->buf, q->id);
}
}
- idnsCallback(q, answers, n, q->error);
- rfc1035RRDestroy(answers, n);
+ idnsCallback(q, message->answer, n, q->error);
+ rfc1035MessageDestroy(message);
memFree(q, MEM_IDNS_QUERY);
}