static off_t grok_additional_for_opt_rr(const u_char *buf, int len, off_t offset, dns_message * m) { int x; unsigned short sometype; unsigned short someclass; unsigned short us; char somename[MAX_QNAME_SZ]; x = rfc1035NameUnpack(buf, len, &offset, somename, MAX_QNAME_SZ); if (0 != x) return 0; if (offset + 10 > len) return 0; sometype = nptohs(buf + offset); someclass = nptohs(buf + offset + 2); if (sometype == T_OPT) { m->edns.found = 1; m->edns.bufsiz = someclass; memcpy(&m->edns.version, buf + offset + 5, 1); us = nptohs(buf + offset + 6); m->edns.DO = (us >> 15) & 0x01; /* RFC 3225 */ }
/* * 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; }
static off_t grok_question(const u_char *buf, int len, off_t offset, char *qname, unsigned short *qtype, unsigned short *qclass) { char *t; int x; x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ); if (0 != x) return 0; if ('\0' == *qname) strcpy(qname, "."); /* XXX remove special characters from QNAME */ while ((t = strchr(qname, '\n'))) *t = ' '; while ((t = strchr(qname, '\r'))) *t = ' '; for (t = qname; *t; t++) *t = tolower(*t); if (offset + 4 > len) return 0; *qtype = nptohs(buf + offset); *qclass = nptohs(buf + offset + 2); offset += 4; return offset; }
static int rfc1035NameUnpack(const u_char *buf, size_t sz, off_t * off, char *name, int ns) { off_t no = 0; unsigned char c; size_t len; static int loop_detect = 0; if (loop_detect > 2) return 4; /* compression loop */ if (ns <= 0) return 4; /* probably compression loop */ do { if ((*off) >= sz) break; c = *(buf + (*off)); if (c > 191) { /* blasted compression */ int rc; unsigned short s; off_t ptr; s = nptohs(buf + (*off)); (*off) += sizeof(s); /* Sanity check */ if ((*off) >= sz) return 1; /* message too short */ ptr = s & 0x3FFF; /* Make sure the pointer is inside this message */ if (ptr >= sz) return 2; /* bad compression ptr */ if (ptr < DNS_MSG_HDR_SZ) return 2; /* bad compression ptr */ loop_detect++; rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); loop_detect--; return rc; } else if (c > RFC1035_MAXLABELSZ) { /* * "(The 10 and 01 combinations are reserved for future use.)" */ return 3; /* reserved label/compression flags */ break; } else { (*off)++; len = (size_t) c; if (len == 0) break; if (len > (ns - 1)) len = ns - 1; if ((*off) + len > sz) return 4; /* message is too short */ if (no + len + 1 > ns) return 5; /* qname would overflow name buffer */ memcpy(name + no, buf + (*off), len); (*off) += len; no += len; *(name + (no++)) = '.'; } } while (c > 0); if (no > 0) *(name + no - 1) = '\0'; /* make sure we didn't allow someone to overflow the name buffer */ assert(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; }
/* * 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; }