static int process_rrsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp) { int n; int offset, at; jdns_string_t *name = 0; const unsigned char *buf; buf = *bufp; for(n = 0; n < count; ++n) { jdns_packet_resource_t *r; offset = buf - data; at = 0; if(!readlabel(data + offset, size - offset, data, size, &at, &name)) goto error; offset += at; // need 10 more bytes if(offset + 10 > size) goto error; buf = data + offset; r = jdns_packet_resource_new(); r->qname = name; name = 0; r->qtype = net2short(&buf); r->qclass = net2short(&buf); r->ttl = net2long(&buf); r->rdlength = net2short(&buf); offset = buf - data; // make sure we have enough for the rdata if(size - offset < r->rdlength) { jdns_packet_resource_delete(r); goto error; } r->rdata = jdns_copy_array(buf, r->rdlength); buf += r->rdlength; jdns_list_insert_value(dest, r, -1); jdns_packet_resource_delete(r); } *bufp = buf; return 1; error: jdns_string_delete(name); return 0; }
static int process_qsection(jdns_list_t *dest, int count, const unsigned char *data, int size, const unsigned char **bufp) { int n; int offset, at; jdns_string_t *name = 0; const unsigned char *buf; buf = *bufp; for(n = 0; n < count; ++n) { jdns_packet_question_t *q; offset = buf - data; at = 0; if(!readlabel(data + offset, size - offset, data, size, &at, &name)) goto error; offset += at; // need 4 more bytes if(size - offset < 4) goto error; buf = data + offset; q = jdns_packet_question_new(); q->qname = name; name = 0; q->qtype = net2short(&buf); q->qclass = net2short(&buf); jdns_list_insert_value(dest, q, -1); jdns_packet_question_delete(q); } *bufp = buf; return 1; error: jdns_string_delete(name); return 0; }
void message_parse(struct message *m, unsigned char *packet) { unsigned char *buf; int i; if(packet == 0 || m == 0) return; // keep all our mem in one (aligned) block for easy freeing #define my(x,y) while(m->_len&7) m->_len++; x = (void*)(m->_packet + m->_len); m->_len += y; // header stuff bit crap m->_buf = buf = packet; m->id = net2short(&buf); if(buf[0] & 0x80) m->header.qr = 1; m->header.opcode = (buf[0] & 0x78) >> 3; 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 = (buf[1] & 0x70) >> 4; m->header.rcode = buf[1] & 0x0F; buf += 2; m->qdcount = net2short(&buf); if(m->_len + (sizeof(struct question) * m->qdcount) > MAX_PACKET_LEN - 8) { m->qdcount = 0; return; } m->ancount = net2short(&buf); if(m->_len + (sizeof(struct resource) * m->ancount) > MAX_PACKET_LEN - 8) { m->ancount = 0; return; } m->nscount = net2short(&buf); if(m->_len + (sizeof(struct resource) * m->nscount) > MAX_PACKET_LEN - 8) { m->nscount = 0; return; } m->arcount = net2short(&buf); if(m->_len + (sizeof(struct resource) * m->arcount) > MAX_PACKET_LEN - 8) { m->arcount = 0; return; } // process questions my(m->qd, sizeof(struct question) * m->qdcount); for(i=0; i < m->qdcount; i++) { _label(m, &buf, &(m->qd[i].name)); m->qd[i].type = net2short(&buf); m->qd[i].rr_class = net2short(&buf); } // process rrs my(m->an, sizeof(struct resource) * m->ancount); my(m->ns, sizeof(struct resource) * m->nscount); my(m->ar, sizeof(struct resource) * m->arcount); if(! _rrparse(m,m->an,m->ancount,&buf)) m->ancount = 0; // some error in parsing, set those counts to 0 if(! _rrparse(m,m->ns,m->nscount,&buf)) m->nscount = 0; if(! _rrparse(m,m->ar,m->arcount,&buf)) m->arcount = 0; }
/** Return 1 on success, 0 on failure. */ int _rrparse(struct message *m, struct resource *rr, int count, unsigned char **bufp) { int i; for(i=0; i < count; i++) { _label(m, bufp, &(rr[i].name)); rr[i].type = net2short(bufp); rr[i].rr_class = net2short(bufp); rr[i].ttl = net2long(bufp); rr[i].rdlength = net2short(bufp); // if not going to overflow, make copy of source rdata if(rr[i].rdlength + (*bufp - m->_buf) > MAX_PACKET_LEN || m->_len + rr[i].rdlength > MAX_PACKET_LEN) return 0; 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 1: if(m->_len + 16 > MAX_PACKET_LEN) return 0; rr[i].known.a.name = m->_packet + m->_len; m->_len += 16; sprintf(rr[i].known.a.name,"%d.%d.%d.%d",(*bufp)[0],(*bufp)[1],(*bufp)[2],(*bufp)[3]); rr[i].known.a.ip = net2long(bufp); break; case 2: _label(m, bufp, &(rr[i].known.ns.name)); break; case 5: _label(m, bufp, &(rr[i].known.cname.name)); break; case 12: _label(m, bufp, &(rr[i].known.ptr.name)); break; case 33: rr[i].known.srv.priority = net2short(bufp); rr[i].known.srv.weight = net2short(bufp); rr[i].known.srv.port = net2short(bufp); _label(m, bufp, &(rr[i].known.srv.name)); break; default: *bufp += rr[i].rdlength; } } return 1; }
int jdns_packet_import(jdns_packet_t **a, const unsigned char *data, int size) { jdns_packet_t *tmp = 0; const unsigned char *buf; // need at least some data if(!data || size == 0) return 0; // header (id + options + item counts) is 12 bytes if(size < 12) goto error; tmp = jdns_packet_new(); buf = data; // id tmp->id = net2short(&buf); // options if(buf[0] & 0x80) // qr is bit 7 tmp->opts.qr = 1; tmp->opts.opcode = (buf[0] & 0x78) >> 3; // opcode is bits 6,5,4,3 if(buf[0] & 0x04) // aa is bit 2 tmp->opts.aa = 1; if(buf[0] & 0x02) // tc is bit 1 tmp->opts.tc = 1; if(buf[0] & 0x01) // rd is bit 0 tmp->opts.rd = 1; if(buf[1] & 0x80) // ra is bit 7 (second byte) tmp->opts.ra = 1; tmp->opts.z = (buf[1] & 0x70) >> 4; // z is bits 6,5,4 tmp->opts.rcode = buf[1] & 0x0f; // rcode is bits 3,2,1,0 buf += 2; // item counts tmp->qdcount = net2short(&buf); tmp->ancount = net2short(&buf); tmp->nscount = net2short(&buf); tmp->arcount = net2short(&buf); // if these fail, we don't count them as errors, since the packet // might have been truncated if(!process_qsection(tmp->questions, tmp->qdcount, data, size, &buf)) goto skip; if(!process_rrsection(tmp->answerRecords, tmp->ancount, data, size, &buf)) goto skip; if(!process_rrsection(tmp->authorityRecords, tmp->nscount, data, size, &buf)) goto skip; if(!process_rrsection(tmp->additionalRecords, tmp->arcount, data, size, &buf)) goto skip; tmp->fully_parsed = 1; skip: // keep the raw data for reference during rdata parsing tmp->raw_size = size; tmp->raw_data = jdns_copy_array(data, size); *a = tmp; return 1; error: jdns_packet_delete(tmp); return 0; }