mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-12-04 21:09:19 +08:00
839 lines
21 KiB
C++
839 lines
21 KiB
C++
|
|
||
|
#include "stdafx.h"
|
||
|
#include <stdio.h> /* for snprintf */
|
||
|
|
||
|
#ifdef ACL_BCB_COMPILER
|
||
|
#pragma hdrstop
|
||
|
#endif
|
||
|
|
||
|
#include "rfc1035.h"
|
||
|
|
||
|
#define RFC1035_MAXLABELSZ 63
|
||
|
#define rfc1035_unpack_error 15
|
||
|
|
||
|
int rfc1035_errno;
|
||
|
const char *rfc1035_error_message;
|
||
|
|
||
|
#if 0
|
||
|
#define RFC1035_UNPACK_DEBUG acl_msg_error("unpack error at %s:%d", __FILE__,__LINE__)
|
||
|
#else
|
||
|
#define RFC1035_UNPACK_DEBUG (void)0
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* rfc1035HeaderPack()
|
||
|
*
|
||
|
* Packs a rfc1035_header structure into a buffer.
|
||
|
* Returns number of octets packed (should always be 12)
|
||
|
*/
|
||
|
static int rfc1035HeaderPack(char *buf, size_t sz, rfc1035_message * hdr)
|
||
|
{
|
||
|
const char *myname = "rfc1035HeaderPack";
|
||
|
int off = 0;
|
||
|
unsigned short s;
|
||
|
unsigned short t;
|
||
|
|
||
|
if (sz < 12)
|
||
|
acl_msg_fatal("%s: sz(%d) < 12", myname, sz);
|
||
|
|
||
|
s = htons(hdr->id);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
t = 0;
|
||
|
t |= hdr->qr << 15;
|
||
|
t |= (hdr->opcode << 11);
|
||
|
t |= (hdr->aa << 10);
|
||
|
t |= (hdr->tc << 9);
|
||
|
t |= (hdr->rd << 8);
|
||
|
t |= (hdr->ra << 7);
|
||
|
t |= hdr->rcode;
|
||
|
s = htons(t);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
s = htons(hdr->qdcount);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
s = htons(hdr->ancount);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
s = htons(hdr->nscount);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
s = htons(hdr->arcount);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
if (off != 12)
|
||
|
acl_msg_fatal("%s: off(%d) != 12", myname, off);
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035LabelPack()
|
||
|
*
|
||
|
* Packs a label into a buffer. The format of
|
||
|
* a label is one octet specifying the number of character
|
||
|
* bytes to follow. Labels must be smaller than 64 octets.
|
||
|
* Returns number of octets packed.
|
||
|
*/
|
||
|
static int rfc1035LabelPack(char *buf, size_t sz, const char *label)
|
||
|
{
|
||
|
const char *myname = "rfc1035LabelPack";
|
||
|
int off = 0;
|
||
|
size_t len = label ? strlen(label) : 0;
|
||
|
|
||
|
if (label) {
|
||
|
if (strchr(label, '.') != NULL)
|
||
|
acl_msg_fatal("%s: '.' exist in label(%s)", myname, label);
|
||
|
}
|
||
|
|
||
|
if (len > RFC1035_MAXLABELSZ)
|
||
|
len = RFC1035_MAXLABELSZ;
|
||
|
if (sz < len + 1)
|
||
|
acl_msg_fatal("%s: sz(%d) < len(%d) + 1", myname, sz, len);
|
||
|
*(buf + off) = (char) len;
|
||
|
off++;
|
||
|
memcpy(buf + off, label, len);
|
||
|
off += (int) len;
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035NamePack()
|
||
|
*
|
||
|
* Packs a name into a buffer. Names are packed as a
|
||
|
* sequence of labels, terminated with NULL label.
|
||
|
* Note message compression is not supported here.
|
||
|
* Returns number of octets packed.
|
||
|
*/
|
||
|
static int rfc1035NamePack(char *buf, size_t sz, const char *name)
|
||
|
{
|
||
|
const char *myname = "rfc1035NamePack";
|
||
|
int off = 0;
|
||
|
char *copy, *ptr;
|
||
|
char *t;
|
||
|
|
||
|
copy = acl_mystrdup(name);
|
||
|
/*
|
||
|
* NOTE: use of strtok here makes names like foo....com valid.
|
||
|
*/
|
||
|
ptr = copy;
|
||
|
for (t = acl_mystrtok(&ptr, "."); t; t = acl_mystrtok(&ptr, "."))
|
||
|
off += rfc1035LabelPack(buf + off, sz - off, t);
|
||
|
acl_myfree(copy);
|
||
|
off += rfc1035LabelPack(buf + off, sz - off, NULL);
|
||
|
if (off > (int) sz)
|
||
|
acl_msg_fatal("%s: off(%d) > sz(%d)", myname, off, sz);
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035QuestionPack()
|
||
|
*
|
||
|
* Packs a QUESTION section of a message.
|
||
|
* Returns number of octets packed.
|
||
|
*/
|
||
|
static int rfc1035QuestionPack(char *buf, size_t sz, const char *name,
|
||
|
unsigned short type, unsigned short tclass)
|
||
|
{
|
||
|
const char *myname = "rfc1035QuestionPack";
|
||
|
int off = 0;
|
||
|
unsigned short s;
|
||
|
|
||
|
off += rfc1035NamePack(buf + off, sz - off, name);
|
||
|
s = htons(type);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
s = htons(tclass);
|
||
|
memcpy(buf + off, &s, sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
if (off > (int) sz)
|
||
|
acl_msg_fatal("%s: off(%d) > sz(%d)", myname, off, sz);
|
||
|
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035HeaderUnpack()
|
||
|
*
|
||
|
* 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, int *off,
|
||
|
rfc1035_message * h)
|
||
|
{
|
||
|
const char *myname = "rfc1035HeaderUnpack";
|
||
|
unsigned short s;
|
||
|
unsigned short t;
|
||
|
|
||
|
if (*off != 0)
|
||
|
acl_msg_fatal("%s: *off(%d) != 0", myname, *off);
|
||
|
|
||
|
/*
|
||
|
* The header is 12 octets. This is a bogus message if the size
|
||
|
* is less than that.
|
||
|
*/
|
||
|
if (sz < 12)
|
||
|
return 1;
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
h->id = ntohs(s);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
t = ntohs(s);
|
||
|
h->qr = (t >> 15) & 0x01;
|
||
|
h->opcode = (t >> 11) & 0x0F;
|
||
|
h->aa = (t >> 10) & 0x01;
|
||
|
h->tc = (t >> 9) & 0x01;
|
||
|
h->rd = (t >> 8) & 0x01;
|
||
|
h->ra = (t >> 7) & 0x01;
|
||
|
/*
|
||
|
* We might want to check that the reserved 'Z' bits (6-4) are
|
||
|
* all zero as per RFC 1035. If not the message should be
|
||
|
* rejected.
|
||
|
*/
|
||
|
h->rcode = t & 0x0F;
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
h->qdcount = ntohs(s);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
h->ancount = ntohs(s);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
h->nscount = ntohs(s);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
h->arcount = ntohs(s);
|
||
|
|
||
|
if (*off != 12)
|
||
|
acl_msg_fatal("%s: *off(%d) != 12", myname, *off);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035NameUnpack()
|
||
|
*
|
||
|
* Unpacks a Name in a message buffer into a char*.
|
||
|
* Note 'buf' points to the beginning of the whole message,
|
||
|
* 'off' points to the spot where the Name begins, and 'sz'
|
||
|
* is the size of the whole message. 'name' must be allocated
|
||
|
* by the caller.
|
||
|
*
|
||
|
* Supports the RFC1035 message compression through recursion.
|
||
|
*
|
||
|
* Updates the new buffer offset.
|
||
|
*
|
||
|
* Returns 0 (success) or 1 (error)
|
||
|
*/
|
||
|
static int rfc1035NameUnpack(const char *buf, size_t sz, int *off,
|
||
|
unsigned short *rdlength, char *name, size_t ns, int rdepth)
|
||
|
{
|
||
|
const char *myname = "rfc1035NameUnpack";
|
||
|
int no = 0;
|
||
|
unsigned char c;
|
||
|
size_t len;
|
||
|
|
||
|
if (ns <= 0)
|
||
|
acl_msg_fatal("%s: ns(%d) <= 0", myname, ns);
|
||
|
do {
|
||
|
if (*off >= (int) sz)
|
||
|
acl_msg_fatal("%s: *off(%d) >= sz(%d)", myname, *off, sz);
|
||
|
c = *(buf + (*off));
|
||
|
if (c > 191) {
|
||
|
/* blasted compression */
|
||
|
unsigned short s;
|
||
|
int ptr;
|
||
|
if (rdepth > 64) /* infinite pointer loop */
|
||
|
return 1;
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
s = ntohs(s);
|
||
|
(*off) += sizeof(s);
|
||
|
/* Sanity check */
|
||
|
if ((*off) >= (int) sz)
|
||
|
return 1;
|
||
|
ptr = s & 0x3FFF;
|
||
|
/* Make sure the pointer is inside this message */
|
||
|
if (ptr >= (int) sz)
|
||
|
return 1;
|
||
|
return rfc1035NameUnpack(buf, sz, &ptr, rdlength, name + no,
|
||
|
ns - no, rdepth + 1);
|
||
|
} else if (c > RFC1035_MAXLABELSZ) {
|
||
|
/*
|
||
|
* "(The 10 and 01 combinations are reserved for future use.)"
|
||
|
*/
|
||
|
return 1;
|
||
|
} else {
|
||
|
(*off)++;
|
||
|
len = (size_t) c;
|
||
|
if (len == 0)
|
||
|
break;
|
||
|
if (len > (ns - no - 1)) /* label won't fit */
|
||
|
return 1;
|
||
|
if ((*off) + len >= sz) /* message is too short */
|
||
|
return 1;
|
||
|
memcpy(name + no, buf + (*off), len);
|
||
|
(*off) += (int) len;
|
||
|
no += (int) len;
|
||
|
*(name + (no++)) = '.';
|
||
|
if (rdlength)
|
||
|
*rdlength += (unsigned short) len + 1;
|
||
|
}
|
||
|
} while (c > 0 && no < (int) ns);
|
||
|
if (no)
|
||
|
*(name + no - 1) = '\0';
|
||
|
else
|
||
|
*name = '\0';
|
||
|
/* make sure we didn't allow someone to overflow the name buffer */
|
||
|
if (no > (int) ns)
|
||
|
acl_msg_fatal("%s: no(%d) > ns(%d)", myname, no, ns);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035RRUnpack()
|
||
|
*
|
||
|
* Unpacks a RFC1035 Resource Record into 'RR' from a message buffer.
|
||
|
* The caller must free RR->rdata!
|
||
|
*
|
||
|
* Updates the new message buffer offset.
|
||
|
*
|
||
|
* Returns 0 (success) or 1 (error)
|
||
|
*/
|
||
|
static int rfc1035RRUnpack(const char *buf, size_t sz, int *off, rfc1035_rr * RR)
|
||
|
{
|
||
|
const char *myname = "rfc1035RRUnpack";
|
||
|
unsigned short s;
|
||
|
unsigned int i;
|
||
|
unsigned short rdlength;
|
||
|
int rdata_off;
|
||
|
|
||
|
if (rfc1035NameUnpack(buf, sz, off, NULL, RR->name, RFC1035_MAXHOSTNAMESZ, 0)) {
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
memset(RR, '\0', sizeof(*RR));
|
||
|
return 1;
|
||
|
}
|
||
|
/*
|
||
|
* Make sure the remaining message has enough octets for the
|
||
|
* rest of the RR fields.
|
||
|
*/
|
||
|
if ((*off) + 10 > (int) sz) {
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
memset(RR, '\0', sizeof(*RR));
|
||
|
return 1;
|
||
|
}
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
RR->type = ntohs(s);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
RR->tclass = ntohs(s);
|
||
|
memcpy(&i, buf + (*off), sizeof(i));
|
||
|
(*off) += sizeof(i);
|
||
|
RR->ttl = ntohl(i);
|
||
|
memcpy(&s, buf + (*off), sizeof(s));
|
||
|
(*off) += sizeof(s);
|
||
|
rdlength = ntohs(s);
|
||
|
if ((*off) + rdlength > (int) sz) {
|
||
|
/*
|
||
|
* We got a truncated packet. 'dnscache' truncates UDP
|
||
|
* replies at 512 octets, as per RFC 1035.
|
||
|
*/
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
memset(RR, '\0', sizeof(*RR));
|
||
|
return 1;
|
||
|
}
|
||
|
RR->rdlength = rdlength;
|
||
|
switch (RR->type) {
|
||
|
case RFC1035_TYPE_PTR:
|
||
|
RR->rdata = (char*) acl_mymalloc(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))
|
||
|
return 1;
|
||
|
if (rdata_off > ((*off) + rdlength)) {
|
||
|
/*
|
||
|
* This probably doesn't happen for valid packets, but
|
||
|
* I want to make sure that NameUnpack doesn't go beyond
|
||
|
* the RDATA area.
|
||
|
*/
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
acl_myfree(RR->rdata);
|
||
|
memset(RR, '\0', sizeof(*RR));
|
||
|
return 1;
|
||
|
}
|
||
|
break;
|
||
|
case RFC1035_TYPE_A:
|
||
|
default:
|
||
|
RR->rdata = (char*) acl_mymalloc(rdlength);
|
||
|
memcpy(RR->rdata, buf + (*off), rdlength);
|
||
|
break;
|
||
|
}
|
||
|
(*off) += rdlength;
|
||
|
if (*off > (int) sz)
|
||
|
acl_msg_fatal("%s: *off(%d) > sz(%d)", myname, *off, sz);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char *rfc1035Strerror(int errnum)
|
||
|
{
|
||
|
struct __ERRMSG{
|
||
|
int errnum;
|
||
|
const char *msg;
|
||
|
};
|
||
|
static const struct __ERRMSG errmsg[] = {
|
||
|
{ 0, "No error condition" },
|
||
|
{ 1, "Format Error: The name server was unable to "
|
||
|
"interpret the query." },
|
||
|
{ 2, "Server Failure: The name server was "
|
||
|
"unable to process this query." },
|
||
|
{ 3, "Name Error: The domain name does not exist." },
|
||
|
{ 4, "Not Implemented: The name server does "
|
||
|
"not support the requested kind of query." },
|
||
|
{ 5, "Refused: The name server refuses to "
|
||
|
"perform the specified operation." },
|
||
|
{ rfc1035_unpack_error, "The DNS reply message is corrupt or could "
|
||
|
"not be safely parsed." },
|
||
|
{ -1, NULL },
|
||
|
};
|
||
|
const char *unknown = "Unknown Error";
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; errmsg[i].msg != NULL; i++) {
|
||
|
if (errmsg[i].errnum == -errnum)
|
||
|
return (errmsg[i].msg);
|
||
|
}
|
||
|
|
||
|
return (unknown);
|
||
|
}
|
||
|
|
||
|
static void rfc1035SetErrno(int n)
|
||
|
{
|
||
|
switch (rfc1035_errno = n) {
|
||
|
case 0:
|
||
|
rfc1035_error_message = "No error condition";
|
||
|
break;
|
||
|
case 1:
|
||
|
rfc1035_error_message = "Format Error: The name server was "
|
||
|
"unable to interpret the query.";
|
||
|
break;
|
||
|
case 2:
|
||
|
rfc1035_error_message = "Server Failure: The name server was "
|
||
|
"unable to process this query.";
|
||
|
break;
|
||
|
case 3:
|
||
|
rfc1035_error_message = "Name Error: The domain name does "
|
||
|
"not exist.";
|
||
|
break;
|
||
|
case 4:
|
||
|
rfc1035_error_message = "Not Implemented: The name server does "
|
||
|
"not support the requested kind of query.";
|
||
|
break;
|
||
|
case 5:
|
||
|
rfc1035_error_message = "Refused: The name server refuses to "
|
||
|
"perform the specified operation.";
|
||
|
break;
|
||
|
case rfc1035_unpack_error:
|
||
|
rfc1035_error_message = "The DNS reply message is corrupt or could "
|
||
|
"not be safely parsed.";
|
||
|
break;
|
||
|
default:
|
||
|
rfc1035_error_message = "Unknown Error";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void rfc1035RRDestroy(rfc1035_rr * rr, int n)
|
||
|
{
|
||
|
const char *myname = "rfc1035RRDestroy";
|
||
|
|
||
|
if (rr == NULL)
|
||
|
return;
|
||
|
if (n <= 0)
|
||
|
acl_msg_fatal("%s: n(%d) <= 0", myname, n);
|
||
|
while (n--) {
|
||
|
if (rr[n].rdata)
|
||
|
acl_myfree(rr[n].rdata);
|
||
|
}
|
||
|
acl_myfree(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, int *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;
|
||
|
}
|
||
|
if (*off + 4 > (int) sz) {
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
memset(query, '\0', sizeof(*query));
|
||
|
return 1;
|
||
|
}
|
||
|
memcpy(&s, buf + *off, 2);
|
||
|
*off += 2;
|
||
|
query->qtype = ntohs(s);
|
||
|
memcpy(&s, buf + *off, 2);
|
||
|
*off += 2;
|
||
|
query->qclass = ntohs(s);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void rfc1035MessageDestroy(rfc1035_message * msg)
|
||
|
{
|
||
|
if (!msg)
|
||
|
return;
|
||
|
if (msg->query)
|
||
|
acl_myfree(msg->query);
|
||
|
if (msg->answer)
|
||
|
rfc1035RRDestroy(msg->answer, msg->ancount);
|
||
|
acl_myfree(msg);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035QueryCompare()
|
||
|
*
|
||
|
* Compares two rfc1035_query entries
|
||
|
*
|
||
|
* Returns 0 (equal) or !=0 (different)
|
||
|
*/
|
||
|
int rfc1035QueryCompare(const rfc1035_query * a, const rfc1035_query * b)
|
||
|
{
|
||
|
size_t la, lb;
|
||
|
|
||
|
if (a->qtype != b->qtype)
|
||
|
return 1;
|
||
|
if (a->qclass != b->qclass)
|
||
|
return 1;
|
||
|
la = strlen(a->name);
|
||
|
lb = strlen(b->name);
|
||
|
if (la != lb) {
|
||
|
/* Trim root label(s) */
|
||
|
while (la > 0 && a->name[la - 1] == '.')
|
||
|
la--;
|
||
|
while (lb > 0 && b->name[lb - 1] == '.')
|
||
|
lb--;
|
||
|
}
|
||
|
if (la != lb)
|
||
|
return 1;
|
||
|
|
||
|
return strncasecmp(a->name, b->name, la);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035MessageUnpack()
|
||
|
*
|
||
|
* Takes the contents of a DNS reply and fills in an array
|
||
|
* of resource record structures. The records array is allocated
|
||
|
* here, and should be freed by calling rfc1035RRDestroy().
|
||
|
*
|
||
|
* Returns number of records unpacked, zero if DNS reply indicates
|
||
|
* zero answers, or an error number < 0.
|
||
|
*/
|
||
|
|
||
|
int rfc1035MessageUnpack(const char *buf, size_t sz, rfc1035_message ** answer)
|
||
|
{
|
||
|
int off = 0;
|
||
|
int i;
|
||
|
int nr = 0;
|
||
|
rfc1035_message *msg;
|
||
|
rfc1035_rr *recs;
|
||
|
rfc1035_query *querys;
|
||
|
|
||
|
msg = (rfc1035_message*) acl_mycalloc(1, sizeof(*msg));
|
||
|
if (rfc1035HeaderUnpack(buf + off, sz - off, &off, msg)) {
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
rfc1035SetErrno(rfc1035_unpack_error);
|
||
|
acl_myfree(msg);
|
||
|
return -rfc1035_unpack_error;
|
||
|
}
|
||
|
rfc1035_errno = 0;
|
||
|
rfc1035_error_message = NULL;
|
||
|
i = (int) msg->qdcount;
|
||
|
if (i != 1) {
|
||
|
/* This can not be an answer to our queries.. */
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
rfc1035SetErrno(rfc1035_unpack_error);
|
||
|
acl_myfree(msg);
|
||
|
return -rfc1035_unpack_error;
|
||
|
}
|
||
|
querys = msg->query = (rfc1035_query*) acl_mycalloc((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;
|
||
|
}
|
||
|
}
|
||
|
*answer = msg;
|
||
|
if (msg->rcode) {
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
rfc1035SetErrno((int) msg->rcode);
|
||
|
return -((int) msg->rcode);
|
||
|
}
|
||
|
if (msg->ancount == 0)
|
||
|
return 0;
|
||
|
recs = msg->answer = (rfc1035_rr*) acl_mycalloc((int) msg->ancount, sizeof(*recs));
|
||
|
for (i = 0; i < (int) msg->ancount; i++) {
|
||
|
if (off >= (int) sz) { /* corrupt packet */
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
break;
|
||
|
}
|
||
|
if (rfc1035RRUnpack(buf, sz, &off, &recs[i])) { /* corrupt RR */
|
||
|
RFC1035_UNPACK_DEBUG;
|
||
|
break;
|
||
|
}
|
||
|
nr++;
|
||
|
}
|
||
|
if (nr == 0) {
|
||
|
/*
|
||
|
* we expected to unpack some answers (ancount != 0), but
|
||
|
* didn't actually get any.
|
||
|
*/
|
||
|
rfc1035MessageDestroy(msg);
|
||
|
*answer = NULL;
|
||
|
rfc1035SetErrno(rfc1035_unpack_error);
|
||
|
return -rfc1035_unpack_error;
|
||
|
}
|
||
|
|
||
|
if (msg->nscount > 0) {
|
||
|
//rfc1035NSUnpack(buf, sz, &off);
|
||
|
}
|
||
|
|
||
|
if (msg->arcount > 0) {
|
||
|
//rfc1035ARUnpack(buf, sz, &off);
|
||
|
}
|
||
|
return nr;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035RRPack()
|
||
|
*
|
||
|
* Unpacks a RFC1035 Resource Record into 'RR' from a message buffer.
|
||
|
* The caller must free RR->rdata!
|
||
|
*
|
||
|
* Updates the new message buffer offset.
|
||
|
*
|
||
|
* Returns > 0 (success) or 0 (error)
|
||
|
*/
|
||
|
static int rfc1035RRPack(const rfc1035_rr *RR, char *buf, size_t sz)
|
||
|
{
|
||
|
const char *myname = "rfc1035RRPack";
|
||
|
unsigned short s;
|
||
|
unsigned int i;
|
||
|
int off = 0, off_saved;
|
||
|
|
||
|
off = rfc1035NamePack(buf + off, sz, RR->name);
|
||
|
s = htons(RR->type);
|
||
|
memcpy(buf + off, &s,sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
|
||
|
s = htons(RR->tclass);
|
||
|
memcpy(buf + off, &s ,sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
|
||
|
i = htonl(RR->ttl);
|
||
|
memcpy(buf + off, &i ,sizeof(i));
|
||
|
off += sizeof(i);
|
||
|
|
||
|
switch (RR->type) {
|
||
|
case RFC1035_TYPE_PTR:
|
||
|
case RFC1035_TYPE_NS:
|
||
|
if (strlen(RR->rdata) > RFC1035_MAXHOSTNAMESZ)
|
||
|
return (0);
|
||
|
off_saved = off;
|
||
|
off += sizeof(s);
|
||
|
off += rfc1035NamePack(buf + off, sz, RR->rdata);
|
||
|
s = off - off_saved - (unsigned short) sizeof(s);
|
||
|
s = htons(s);
|
||
|
memcpy(buf + off_saved, &s ,sizeof(s));
|
||
|
break;
|
||
|
default:
|
||
|
s = htons(RR->rdlength);
|
||
|
memcpy(buf + off, &s ,sizeof(s));
|
||
|
off += sizeof(s);
|
||
|
memcpy(buf + off, RR->rdata, RR->rdlength);
|
||
|
off += RR->rdlength;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ((unsigned) off > sz)
|
||
|
acl_msg_fatal("%s: off(%d) > sz(%d)", myname, off, sz);
|
||
|
|
||
|
return (off);
|
||
|
}
|
||
|
|
||
|
ssize_t rfc1035BuildAReply(const char *hostname, const ACL_ARGV *ip_argv,
|
||
|
const char *dnsname, const char *dns_ip,
|
||
|
unsigned short qid, char *buf, size_t sz)
|
||
|
{
|
||
|
rfc1035_message h;
|
||
|
rfc1035_rr rr;
|
||
|
size_t offset = 0;
|
||
|
unsigned int nip;
|
||
|
int i;
|
||
|
|
||
|
memset(&h, '\0', sizeof(h));
|
||
|
h.id = qid;
|
||
|
h.qr = 1;
|
||
|
h.opcode = 0; /* QUERY */
|
||
|
h.aa = 0;
|
||
|
h.tc = 0;
|
||
|
h.rd = 1;
|
||
|
h.ra = 0;
|
||
|
h.rcode = 0;
|
||
|
h.qdcount = 1;
|
||
|
h.ancount = ip_argv->argc;
|
||
|
h.nscount = (dnsname && *dnsname) ? 1 : 0;
|
||
|
h.arcount = (h.nscount && dns_ip && *dns_ip) ? 1 : 0;
|
||
|
offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
|
||
|
offset += rfc1035QuestionPack(buf + offset, sz - offset, hostname,
|
||
|
RFC1035_TYPE_A, RFC1035_CLASS_IN);
|
||
|
|
||
|
for (i = 0; i < ip_argv->argc; i++) {
|
||
|
memset(&rr, 0, sizeof(rr));
|
||
|
snprintf(rr.name, sizeof(rr.name), "%s", hostname);
|
||
|
rr.type = RFC1035_TYPE_A;
|
||
|
rr.tclass = RFC1035_CLASS_IN;
|
||
|
rr.ttl = 5;
|
||
|
rr.rdlength = 4;
|
||
|
nip = inet_addr(ip_argv->argv[i]);
|
||
|
rr.rdata = (char*) acl_mycalloc(1, rr.rdlength);
|
||
|
memcpy(rr.rdata, &nip, rr.rdlength);
|
||
|
offset += rfc1035RRPack(&rr, buf + offset, sz - offset);
|
||
|
acl_myfree(rr.rdata);
|
||
|
}
|
||
|
|
||
|
if (h.nscount) {
|
||
|
memset(&rr, 0, sizeof(rr));
|
||
|
snprintf(rr.name, sizeof(rr.name), "%s", dnsname);
|
||
|
rr.type = RFC1035_TYPE_NS;
|
||
|
rr.tclass = RFC1035_CLASS_IN;
|
||
|
rr.ttl = 5;
|
||
|
rr.rdlength = (unsigned short) strlen(dnsname);
|
||
|
rr.rdata = acl_mystrdup(dnsname);
|
||
|
offset += rfc1035RRPack(&rr, buf + offset, sz - offset);
|
||
|
acl_myfree(rr.rdata);
|
||
|
}
|
||
|
|
||
|
if (h.arcount) {
|
||
|
memset(&rr, 0, sizeof(rr));
|
||
|
snprintf(rr.name, sizeof(rr.name), "%s", dnsname);
|
||
|
rr.type = RFC1035_TYPE_NS;
|
||
|
rr.tclass = RFC1035_CLASS_IN;
|
||
|
rr.ttl = 5;
|
||
|
rr.rdlength = (unsigned short) strlen(dns_ip);
|
||
|
rr.rdata = acl_mystrdup(dns_ip);
|
||
|
offset += rfc1035RRPack(&rr, buf + offset, sz - offset);
|
||
|
acl_myfree(rr.rdata);
|
||
|
}
|
||
|
|
||
|
return ((int) offset);
|
||
|
}
|
||
|
/*
|
||
|
* rfc1035BuildAQuery()
|
||
|
*
|
||
|
* Builds a message buffer with a QUESTION to lookup A records
|
||
|
* for a hostname. Caller must allocate 'buf' which should
|
||
|
* probably be at least 512 octets. The 'szp' initially
|
||
|
* specifies the size of the buffer, on return it contains
|
||
|
* the size of the message (i.e. how much to write).
|
||
|
* Returns the size of the query
|
||
|
*/
|
||
|
ssize_t rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz,
|
||
|
unsigned short qid, rfc1035_query * query)
|
||
|
{
|
||
|
const char *myname = "rfc1035BuildAQuery";
|
||
|
rfc1035_message h;
|
||
|
size_t offset = 0;
|
||
|
|
||
|
memset(&h, '\0', sizeof(h));
|
||
|
h.id = qid;
|
||
|
h.qr = 0;
|
||
|
h.rd = 1;
|
||
|
h.opcode = 0; /* QUERY */
|
||
|
h.qdcount = (unsigned int) 1;
|
||
|
offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
|
||
|
offset += rfc1035QuestionPack(buf + offset, sz - offset, hostname,
|
||
|
RFC1035_TYPE_A, RFC1035_CLASS_IN);
|
||
|
if (query) {
|
||
|
query->qtype = RFC1035_TYPE_A;
|
||
|
query->qclass = RFC1035_CLASS_IN;
|
||
|
ACL_SAFE_STRNCPY(query->name, hostname, sizeof(query->name));
|
||
|
}
|
||
|
|
||
|
if (offset > sz)
|
||
|
acl_msg_fatal("%s: offset(%d) > sz(%d)", myname, offset, sz);
|
||
|
return (ssize_t) offset;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfc1035BuildPTRQuery()
|
||
|
*
|
||
|
* Builds a message buffer with a QUESTION to lookup PTR records
|
||
|
* for an address. Caller must allocate 'buf' which should
|
||
|
* probably be at least 512 octets. The 'szp' initially
|
||
|
* specifies the size of the buffer, on return it contains
|
||
|
* the size of the message (i.e. how much to write).
|
||
|
* Returns the size of the query
|
||
|
*/
|
||
|
ssize_t rfc1035BuildPTRQuery(const struct in_addr addr, char *buf,
|
||
|
size_t sz, unsigned short qid, rfc1035_query * query)
|
||
|
{
|
||
|
const char *myname = "rfc1035BuildPTRQuery";
|
||
|
rfc1035_message h;
|
||
|
size_t offset = 0;
|
||
|
static char rev[32];
|
||
|
unsigned int i;
|
||
|
|
||
|
memset(&h, '\0', sizeof(h));
|
||
|
i = (unsigned int) ntohl(addr.s_addr);
|
||
|
snprintf(rev, 32, "%u.%u.%u.%u.in-addr.arpa.",
|
||
|
i & 255, (i >> 8) & 255, (i >> 16) & 255, (i >> 24) & 255);
|
||
|
h.id = qid;
|
||
|
h.qr = 0;
|
||
|
h.rd = 1;
|
||
|
h.opcode = 0; /* QUERY */
|
||
|
h.qdcount = (unsigned int) 1;
|
||
|
offset += rfc1035HeaderPack(buf + offset, sz - offset, &h);
|
||
|
offset += rfc1035QuestionPack(buf + offset, sz - offset, rev,
|
||
|
RFC1035_TYPE_PTR, RFC1035_CLASS_IN);
|
||
|
if (query) {
|
||
|
query->qtype = RFC1035_TYPE_PTR;
|
||
|
query->qclass = RFC1035_CLASS_IN;
|
||
|
ACL_SAFE_STRNCPY(query->name, rev, sizeof(query->name));
|
||
|
}
|
||
|
if (offset > sz)
|
||
|
acl_msg_fatal("%s: offset(%d) > sz(%d)", myname, offset, sz);
|
||
|
return (ssize_t) offset;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* We're going to retry a former query, but we
|
||
|
* just need a new ID for it. Lucky for us ID
|
||
|
* is the first field in the message buffer.
|
||
|
*/
|
||
|
void rfc1035SetQueryID(char *buf, unsigned short qid)
|
||
|
{
|
||
|
unsigned short s = htons(qid);
|
||
|
memcpy(buf, &s, sizeof(s));
|
||
|
}
|