#include "1035.h" #include #include #if defined(_MSC_VER) && _MSC_VER < 1900 __inline int msnds_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { int count = -1; if (size != 0) count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); if (count == -1) count = _vscprintf(format, ap); return count; } __inline int msnds_snprintf(char *outBuf, size_t size, const char *format, ...) { int count; va_list ap; va_start(ap, format); count = msnds_vsnprintf(outBuf, size, format, ap); va_end(ap); return count; } #else #define msnds_snprintf snprintf #endif uint16_t net2short(const unsigned char **bufp) { unsigned short int i; memcpy(&i, *bufp, sizeof(short int)); *bufp += 2; return ntohs(i); } uint32_t net2long(const unsigned char **bufp) { uint32_t l; memcpy(&l, *bufp, sizeof(uint32_t)); *bufp += 4; return ntohl(l); } void short2net(uint16_t i, unsigned char **bufp) { uint16_t x = htons(i); memcpy(*bufp, &x, sizeof(uint16_t)); *bufp += 2; } void long2net(uint32_t l, unsigned char **bufp) { uint32_t x = htonl(l); memcpy(*bufp, &x, sizeof(uint32_t)); *bufp += 4; } static unsigned short int _ldecomp(const unsigned char *ptr) { unsigned short int i; i = (unsigned short int)(0xc0 ^ ptr[0]); i = (unsigned short int)(i<<8); i = (unsigned short int)(i | ptr[1]); return i; } static bool _label(struct message *m, const unsigned char **bufp, const unsigned char *bufEnd, char **namep) { int x; const unsigned char *label; char *name; /* Set namep to the end of the block */ *namep = name = (char *)m->_packet + m->_len; if (*bufp >= bufEnd) return false; // forward buffer pointer until we find the first compressed label bool moveBufp = true; /* Loop storing label in the block */ do { if (moveBufp) { label = *bufp; } if (label >= bufEnd) { break; } /* Since every domain name ends with the null label of the root, a domain name is terminated by a length byte of zero. */ if (*label == 0) { if (moveBufp) { *bufp += 1; } break; } /* Skip past any compression pointers, kick out if end encountered (bad data prolly) */ /* If a label is compressed, it has following structure of 2 bytes: * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * | 1 1| OFFSET | * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * * The OFFSET field specifies an offset from * the start of the message (i.e., the first octet of the ID field in the * domain header). A zero offset specifies the first byte of the ID field, * etc. **/ if (*label & 0xc0) { if (label + 2 > bufEnd) return false; unsigned short int offset = _ldecomp(label); if (offset > m->_len) return false; if (m->_buf + offset >= bufEnd) return false; label = m->_buf + offset; // chek if label is again pointer, then abort. if (*label & 0xc0) return false; moveBufp = false; *bufp += 2; } /* Make sure we're not over the limits * See https://tools.ietf.org/html/rfc1035 * 2.3.4. Size limits * */ const unsigned char labelLen = (unsigned char)*label; if (labelLen > 63) // maximum label length is 63 octets return false; long nameLen = (name + labelLen) - *namep; if (nameLen > 255) // maximum names length is 255 octets return false; if (label + 1 + labelLen > bufEnd) { return false; } if ((unsigned char*)name + labelLen > m->_packet + MAX_PACKET_LEN) { return false; } /* Copy chars for this label */ memcpy(name, label + 1, labelLen); name[labelLen] = '.'; name += labelLen + 1; if (moveBufp) { *bufp += labelLen + 1; } else { label += labelLen +1; } } while (*bufp <= bufEnd); if ((unsigned char*)name >= m->_packet + MAX_PACKET_LEN) { return false; } /* Terminate name and check for cache or cache it */ *name = '\0'; for (x = 0; x < MAX_NUM_LABELS && m->_labels[x]; x++) { if (strcmp(*namep, m->_labels[x])) continue; *namep = m->_labels[x]; return true; } /* No cache, so cache it if room */ if (x < MAX_NUM_LABELS && m->_labels[x] == 0) { m->_labels[x] = *namep; } m->_len += (unsigned long)((name - *namep) + 1); return true; } /* Internal label matching */ static int _lmatch(struct message *m, const char *l1, const char *l2) { int len; /* Always ensure we get called w/o a pointer */ if (*l1 & 0xc0) return _lmatch(m, (char*)m->_buf + _ldecomp((const unsigned char*)l1), l2); if (*l2 & 0xc0) return _lmatch(m, l1, (char*)m->_buf + _ldecomp((const unsigned char*) l2)); /* Same already? */ if (l1 == l2) return 1; /* Compare all label characters */ if (*l1 != *l2) return 0; for (len = 1; len <= *l1; len++) { if (l1[len] != l2[len]) return 0; } /* Get new labels */ l1 += *l1 + 1; l2 += *l2 + 1; /* At the end, all matched */ if (*l1 == 0 && *l2 == 0) return 1; /* Try next labels */ return _lmatch(m, l1, l2); } /* Nasty, convert host into label using compression */ static int _host(struct message *m, unsigned char **bufp, char *name) { char label[256], *l; int len = 0, x = 1, y = 0, last = 0; if (name == 0) return 0; /* Make our label */ while (name[y]) { if (name[y] == '.') { if (!name[y + 1]) break; label[last] = (char)(x - (last + 1)); last = x; } else { label[x] = name[y]; } if (x++ == 255) return 0; y++; } label[last] = (char)(x - (last + 1)); if (x == 1) x--; /* Special case, bad names, but handle correctly */ len = x + 1; label[x] = 0; /* Always terminate w/ a 0 */ /* Double-loop checking each label against all m->_labels for match */ for (x = 0; label[x]; x += label[x] + 1) { for (y = 0; y < MAX_NUM_LABELS && m->_labels[y]; y++) { if (_lmatch(m, label + x, m->_labels[y])) { /* Matching label, set up pointer */ l = label + x; short2net((unsigned short)((unsigned char *)m->_labels[y] - m->_packet), (unsigned char **)&l); label[x] = (char)(label[x] | 0xc0); len = x + 2; break; } } if (label[x] & 0xc0) break; } /* Copy into buffer, point there now */ memcpy(*bufp, label, (size_t)len); l = (char *)*bufp; *bufp += len; /* For each new label, store it's location for future compression */ for (x = 0; l[x] && m->_label < MAX_NUM_LABELS; x += l[x] + 1) { if (l[x] & 0xc0) break; m->_labels[m->_label++] = l + x; } return len; } static bool _rrparse(struct message *m, struct resource *rr, int count, const unsigned char **bufp, const unsigned char* bufferEnd) { int i; const unsigned char *addr_bytes = NULL; if (count == 0) { return true; } if (*bufp >= m->_bufEnd) { return false; } for (i = 0; i < count; i++) { if (*bufp >= bufferEnd) { return false; } if (!_label(m, bufp, bufferEnd, &(rr[i].name))) { return false; } if (*bufp + 10 > bufferEnd) { return false; } rr[i].type = net2short(bufp); rr[i].clazz = net2short(bufp); rr[i].ttl = net2long(bufp); rr[i].rdlength = net2short(bufp); // fprintf(stderr, "Record type %d class 0x%2x ttl %lu len %d\n", rr[i].type, rr[i].clazz, rr[i].ttl, rr[i].rdlength); /* For the following records the rdata will be parsed later. So don't set it here: * NS, CNAME, PTR, DNAME, SOA, MX, AFSDB, RT, KX, RP, PX, SRV, NSEC * See 18.14 of https://tools.ietf.org/html/rfc6762#page-47 */ if (rr[i].type == QTYPE_NS || rr[i].type == QTYPE_CNAME || rr[i].type == QTYPE_PTR || rr[i].type == QTYPE_SRV) { rr[i].rdlength = 0; } else { /* If not going to overflow, make copy of source rdata */ if (*bufp + rr[i].rdlength > bufferEnd) { return false; } if (m->_len + rr[i].rdlength > MAX_PACKET_LEN) { return false; } rr[i].rdata = m->_packet + m->_len; m->_len += rr[i].rdlength; memcpy(rr[i].rdata, *bufp, rr[i].rdlength); } /* Parse commonly known ones */ switch (rr[i].type) { case QTYPE_A: if (m->_len + 16 > MAX_PACKET_LEN) { return false; } rr[i].known.a.name = (char *)m->_packet + m->_len; m->_len += 16; if (*bufp + 4 > bufferEnd) { return false; } msnds_snprintf(rr[i].known.a.name,16, "%d.%d.%d.%d", (*bufp)[0], (*bufp)[1], (*bufp)[2], (*bufp)[3]); addr_bytes = (const unsigned char *) *bufp; rr[i].known.a.ip.s_addr = (in_addr_t) ( ((in_addr_t) addr_bytes[0]) | (((in_addr_t) addr_bytes[1]) << 8) | (((in_addr_t) addr_bytes[2]) << 16) | (((in_addr_t) addr_bytes[3]) << 24)); *bufp += 4; break; case QTYPE_NS: if (!_label(m, bufp, bufferEnd, &(rr[i].known.ns.name))) { return false; } break; case QTYPE_CNAME: if (!_label(m, bufp, bufferEnd, &(rr[i].known.cname.name))) { return false; } break; case QTYPE_PTR: if (!_label(m, bufp, bufferEnd, &(rr[i].known.ptr.name))) { return false; } break; case QTYPE_SRV: if (*bufp + 6 > bufferEnd) { return false; } rr[i].known.srv.priority = net2short(bufp); rr[i].known.srv.weight = net2short(bufp); rr[i].known.srv.port = net2short(bufp); if (!_label(m, bufp, bufferEnd, &(rr[i].known.srv.name))) { return false; } break; case QTYPE_TXT: default: *bufp += rr[i].rdlength; } } return true; } /* Keep all our mem in one (aligned) block for easy freeing */ #define my(x,y, cast) \ while (m->_len & 7) \ m->_len++; \ \ if (m->_len + y > MAX_PACKET_LEN) { return false; } \ x = (cast)(void *)(m->_packet + m->_len); \ m->_len += y; bool message_parse(struct message *m, unsigned char *packet, size_t packetLen) { int i; const unsigned char *buf; m->_bufEnd = packet + packetLen; /* Message format: https://tools.ietf.org/html/rfc1035 +---------------------+ | Header | +---------------------+ | Question | the question for the name server +---------------------+ | Answer | RRs answering the question +---------------------+ | Authority | RRs pointing toward an authority +---------------------+ | Additional | RRs holding additional information +---------------------+ */ if (packet == 0 || m == 0) return false; /* See https://tools.ietf.org/html/rfc1035 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ /* The header always needs to be present. Size = 12 byte */ if (packetLen < 12) return false; /* Header stuff bit crap */ buf = m->_buf = packet; m->id = net2short(&buf); if (buf[0] & 0x80) m->header.qr = 1; m->header.opcode = (unsigned short)(((buf[0] & 0x78) >> 3) & 15); if (buf[0] & 0x04) m->header.aa = 1; if (buf[0] & 0x02) m->header.tc = 1; if (buf[0] & 0x01) m->header.rd = 1; if (buf[1] & 0x80) m->header.ra = 1; m->header.z = (unsigned short)(((buf[1] & 0x70) >> 4) & 7); m->header.rcode = (unsigned short)(buf[1] & 0x0F); buf += 2; m->qdcount = net2short(&buf); m->ancount = net2short(&buf); m->nscount = net2short(&buf); m->arcount = net2short(&buf); // check if the message has the correct size, i.e. the count matches the number of bytes /* Process questions */ my(m->qd, (sizeof(struct question) * m->qdcount), struct question *); for (i = 0; i < m->qdcount; i++) { if (!_label(m, &buf, m->_bufEnd, &(m->qd[i].name))) { return false; } if (buf + 4 > m->_bufEnd) { return false; } m->qd[i].type = net2short(&buf); m->qd[i].clazz = net2short(&buf); } if (buf > m->_bufEnd) { return false; } /* Process rrs */ my(m->an, (sizeof(struct resource) * m->ancount), struct resource *); my(m->ns, (sizeof(struct resource) * m->nscount), struct resource *); my(m->ar, (sizeof(struct resource) * m->arcount), struct resource *); if (!_rrparse(m, m->an, m->ancount, &buf, m->_bufEnd)) return false; if (!_rrparse(m, m->ns, m->nscount, &buf, m->_bufEnd)) return false; if (!_rrparse(m, m->ar, m->arcount, &buf, m->_bufEnd)) return false; return true; } void message_qd(struct message *m, char *name, unsigned short int type, unsigned short int clazz) { m->qdcount++; if (m->_buf == 0) { m->_buf = m->_packet + 12; m->_bufEnd = m->_packet + sizeof(m->_packet); } _host(m, &(m->_buf), name); short2net(type, &(m->_buf)); short2net(clazz, &(m->_buf)); } static void _rrappend(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl) { if (m->_buf == 0) { m->_buf = m->_packet + 12; m->_bufEnd = m->_packet + sizeof(m->_packet); } _host(m, &(m->_buf), name); short2net(type, &(m->_buf)); short2net(clazz, &(m->_buf)); long2net((uint32_t)ttl, &(m->_buf)); } void message_an(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl) { m->ancount++; _rrappend(m, name, type, clazz, ttl); } void message_ns(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl) { m->nscount++; _rrappend(m, name, type, clazz, ttl); } void message_ar(struct message *m, char *name, unsigned short int type, unsigned short int clazz, unsigned long ttl) { m->arcount++; _rrappend(m, name, type, clazz, ttl); } void message_rdata_long(struct message *m, struct in_addr l) { short2net(4, &(m->_buf)); long2net(l.s_addr, &(m->_buf)); } void message_rdata_name(struct message *m, char *name) { unsigned char *mybuf = m->_buf; m->_buf += 2; short2net((unsigned short)_host(m, &(m->_buf), name), &mybuf); } void message_rdata_srv(struct message *m, unsigned short int priority, unsigned short int weight, unsigned short int port, char *name) { unsigned char *mybuf = m->_buf; m->_buf += 2; short2net(priority, &(m->_buf)); short2net(weight, &(m->_buf)); short2net(port, &(m->_buf)); short2net((unsigned short)(_host(m, &(m->_buf), name) + 6), &mybuf); } void message_rdata_raw(struct message *m, unsigned char *rdata, unsigned short int rdlength) { if (((unsigned char *)m->_buf - m->_packet) + rdlength > 4096) rdlength = 0; short2net(rdlength, &(m->_buf)); memcpy(m->_buf, rdata, rdlength); m->_buf += rdlength; } unsigned char *message_packet(struct message *m) { unsigned char c, *buf = m->_buf, *bufEnd = m->_bufEnd; m->_buf = m->_packet; m->_bufEnd = m->_packet + sizeof(m->_packet); short2net(m->id, &(m->_buf)); if (m->header.qr) m->_buf[0] |= 0x80; if ((c = (unsigned char)m->header.opcode)) m->_buf[0] |= (unsigned char)(c << 3); if (m->header.aa) m->_buf[0] |= 0x04; if (m->header.tc) m->_buf[0] |= 0x02; if (m->header.rd) m->_buf[0] |= 0x01; if (m->header.ra) m->_buf[1] |= 0x80; if ((c = (unsigned char)m->header.z)) m->_buf[1] |= (unsigned char)(c << 4); if (m->header.rcode) m->_buf[1] = (unsigned char)(m->_buf[1] | m->header.rcode); m->_buf += 2; short2net(m->qdcount, &(m->_buf)); short2net(m->ancount, &(m->_buf)); short2net(m->nscount, &(m->_buf)); short2net(m->arcount, &(m->_buf)); m->_buf = buf; /* Restore, so packet_len works */ m->_bufEnd = bufEnd; return m->_packet; } int message_packet_len(struct message *m) { if (m->_buf == 0) return 12; return (int)((unsigned char *)m->_buf - m->_packet); }