/** check request sanity. * @param pkt: the wire packet to examine for sanity. * @param worker: parameters for checking. * @return error code, 0 OK, or -1 discard. */ static int worker_check_request(sldns_buffer* pkt, struct worker* worker) { if(sldns_buffer_limit(pkt) < LDNS_HEADER_SIZE) { verbose(VERB_QUERY, "request too short, discarded"); return -1; } if(sldns_buffer_limit(pkt) > NORMAL_UDP_SIZE && worker->daemon->cfg->harden_large_queries) { verbose(VERB_QUERY, "request too large, discarded"); return -1; } if(LDNS_QR_WIRE(sldns_buffer_begin(pkt))) { verbose(VERB_QUERY, "request has QR bit on, discarded"); return -1; } if(LDNS_TC_WIRE(sldns_buffer_begin(pkt))) { LDNS_TC_CLR(sldns_buffer_begin(pkt)); verbose(VERB_QUERY, "request bad, has TC bit on"); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } if(LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt)) != LDNS_PACKET_QUERY) { verbose(VERB_QUERY, "request unknown opcode %d", LDNS_OPCODE_WIRE(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_NOTIMPL); } if(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) != 1) { verbose(VERB_QUERY, "request wrong nr qd=%d", LDNS_QDCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0) { verbose(VERB_QUERY, "request wrong nr an=%d", LDNS_ANCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } if(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) { verbose(VERB_QUERY, "request wrong nr ns=%d", LDNS_NSCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) { verbose(VERB_QUERY, "request wrong nr ar=%d", LDNS_ARCOUNT(sldns_buffer_begin(pkt))); return worker_err_ratelimit(worker, LDNS_RCODE_FORMERR); } return 0; }
int parse_edns_from_pkt(sldns_buffer* pkt, struct edns_data* edns) { log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); log_assert(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) == 0); log_assert(LDNS_NSCOUNT(sldns_buffer_begin(pkt)) == 0); /* check edns section is present */ if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) { return LDNS_RCODE_FORMERR; } if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) { memset(edns, 0, sizeof(*edns)); edns->udp_size = 512; return 0; } /* domain name must be the root of length 1. */ if(pkt_dname_len(pkt) != 1) return LDNS_RCODE_FORMERR; if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */ return LDNS_RCODE_FORMERR; if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT) return LDNS_RCODE_FORMERR; edns->edns_present = 1; edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */ edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ edns->edns_version = sldns_buffer_read_u8(pkt); edns->bits = sldns_buffer_read_u16(pkt); /* ignore rdata and rrsigs */ return 0; }
void server_stats_insrcode(struct server_stats* stats, ldns_buffer* buf) { if(stats->extended && ldns_buffer_limit(buf) != 0) { int r = (int)LDNS_RCODE_WIRE( ldns_buffer_begin(buf) ); stats->ans_rcode[r] ++; if(r == 0 && LDNS_ANCOUNT( ldns_buffer_begin(buf) ) == 0) stats->ans_rcode_nodata ++; } }
/** * Send reply to mesh reply entry * @param m: mesh state to send it for. * @param rcode: if not 0, error code. * @param rep: reply to send (or NULL if rcode is set). * @param r: reply entry * @param prev: previous reply, already has its answer encoded in buffer. */ static void mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, struct mesh_reply* r, struct mesh_reply* prev) { struct timeval end_time; struct timeval duration; int secure; /* examine security status */ if(m->s.env->need_to_validate && (!(r->qflags&BIT_CD) || m->s.env->cfg->ignore_cd) && rep && rep->security <= sec_status_bogus) { rcode = LDNS_RCODE_SERVFAIL; if(m->s.env->cfg->stat_extended) m->s.env->mesh->ans_bogus++; } if(rep && rep->security == sec_status_secure) secure = 1; else secure = 0; if(!rep && rcode == LDNS_RCODE_NOERROR) rcode = LDNS_RCODE_SERVFAIL; /* send the reply */ if(prev && prev->qflags == r->qflags && prev->edns.edns_present == r->edns.edns_present && prev->edns.bits == r->edns.bits && prev->edns.udp_size == r->edns.udp_size) { /* if the previous reply is identical to this one, fix ID */ if(prev->query_reply.c->buffer != r->query_reply.c->buffer) ldns_buffer_copy(r->query_reply.c->buffer, prev->query_reply.c->buffer); ldns_buffer_write_at(r->query_reply.c->buffer, 0, &r->qid, sizeof(uint16_t)); ldns_buffer_write_at(r->query_reply.c->buffer, 12, r->qname, m->s.qinfo.qname_len); comm_point_send_reply(&r->query_reply); } else if(rcode) { m->s.qinfo.qname = r->qname; error_encode(r->query_reply.c->buffer, rcode, &m->s.qinfo, r->qid, r->qflags, &r->edns); comm_point_send_reply(&r->query_reply); } else { size_t udp_size = r->edns.udp_size; r->edns.edns_version = EDNS_ADVERTISED_VERSION; r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; m->s.qinfo.qname = r->qname; if(!reply_info_answer_encode(&m->s.qinfo, rep, r->qid, r->qflags, r->query_reply.c->buffer, 0, 1, m->s.env->scratch, udp_size, &r->edns, (int)(r->edns.bits & EDNS_DO), secure)) { error_encode(r->query_reply.c->buffer, LDNS_RCODE_SERVFAIL, &m->s.qinfo, r->qid, r->qflags, &r->edns); } comm_point_send_reply(&r->query_reply); } /* account */ m->s.env->mesh->num_reply_addrs--; end_time = *m->s.env->now_tv; timeval_subtract(&duration, &end_time, &r->start_time); verbose(VERB_ALGO, "query took %d.%6.6d sec", (int)duration.tv_sec, (int)duration.tv_usec); m->s.env->mesh->replies_sent++; timeval_add(&m->s.env->mesh->replies_sum_wait, &duration); timehist_insert(m->s.env->mesh->histogram, &duration); if(m->s.env->cfg->stat_extended) { uint16_t rc = FLAGS_GET_RCODE(ldns_buffer_read_u16_at(r-> query_reply.c->buffer, 2)); if(secure) m->s.env->mesh->ans_secure++; m->s.env->mesh->ans_rcode[ rc ] ++; if(rc == 0 && LDNS_ANCOUNT(ldns_buffer_begin(r-> query_reply.c->buffer)) == 0) m->s.env->mesh->ans_nodata++; } }
/* * Makes an exact copy of the wire, but with the tsig rr removed */ static uint8_t * ldns_tsig_prepare_pkt_wire(uint8_t *wire, size_t wire_len, size_t *result_len) { uint8_t *wire2 = NULL; uint16_t qd_count; uint16_t an_count; uint16_t ns_count; uint16_t ar_count; ldns_rr *rr; size_t pos; uint16_t i; ldns_status status; if(wire_len < LDNS_HEADER_SIZE) { return NULL; } /* fake parse the wire */ qd_count = LDNS_QDCOUNT(wire); an_count = LDNS_ANCOUNT(wire); ns_count = LDNS_NSCOUNT(wire); ar_count = LDNS_ARCOUNT(wire); if (ar_count > 0) { ar_count--; } else { return NULL; } pos = LDNS_HEADER_SIZE; for (i = 0; i < qd_count; i++) { status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_QUESTION); if (status != LDNS_STATUS_OK) { return NULL; } ldns_rr_free(rr); } for (i = 0; i < an_count; i++) { status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_ANSWER); if (status != LDNS_STATUS_OK) { return NULL; } ldns_rr_free(rr); } for (i = 0; i < ns_count; i++) { status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_AUTHORITY); if (status != LDNS_STATUS_OK) { return NULL; } ldns_rr_free(rr); } for (i = 0; i < ar_count; i++) { status = ldns_wire2rr(&rr, wire, wire_len, &pos, LDNS_SECTION_ADDITIONAL); if (status != LDNS_STATUS_OK) { return NULL; } ldns_rr_free(rr); } *result_len = pos; wire2 = LDNS_XMALLOC(uint8_t, *result_len); if(!wire2) { return NULL; } memcpy(wire2, wire, *result_len); ldns_write_uint16(wire2 + LDNS_ARCOUNT_OFF, ar_count); return wire2; }
/** test a packet */ static void testpkt(sldns_buffer* pkt, struct alloc_cache* alloc, sldns_buffer* out, const char* hex) { struct query_info qi; struct reply_info* rep = 0; int ret; uint16_t id; uint16_t flags; uint32_t timenow = 0; struct regional* region = regional_create(); struct edns_data edns; hex_to_buf(pkt, hex); memmove(&id, sldns_buffer_begin(pkt), sizeof(id)); if(sldns_buffer_limit(pkt) < 2) flags = 0; else memmove(&flags, sldns_buffer_at(pkt, 2), sizeof(flags)); flags = ntohs(flags); ret = reply_info_parse(pkt, alloc, &qi, &rep, region, &edns); if(ret != 0) { char rbuf[16]; sldns_wire2str_rcode_buf(ret, rbuf, sizeof(rbuf)); if(vbmp) printf("parse code %d: %s\n", ret, rbuf); if(ret == LDNS_RCODE_FORMERR) { unit_assert(!check_formerr_gone); checkformerr(pkt); } unit_assert(ret != LDNS_RCODE_SERVFAIL); } else if(!check_formerr_gone) { const size_t lim = 512; ret = reply_info_encode(&qi, rep, id, flags, out, timenow, region, 65535, (int)(edns.bits & EDNS_DO) ); unit_assert(ret != 0); /* udp packets should fit */ attach_edns_record(out, &edns); if(vbmp) printf("inlen %u outlen %u\n", (unsigned)sldns_buffer_limit(pkt), (unsigned)sldns_buffer_limit(out)); if(!check_nosameness) test_buffers(pkt, out); if(check_rrsigs) check_the_rrsigs(&qi, rep); if(sldns_buffer_limit(out) > lim) { ret = reply_info_encode(&qi, rep, id, flags, out, timenow, region, lim - calc_edns_field_size(&edns), (int)(edns.bits & EDNS_DO)); unit_assert(ret != 0); /* should fit, but with TC */ attach_edns_record(out, &edns); if( LDNS_QDCOUNT(sldns_buffer_begin(out)) != LDNS_QDCOUNT(sldns_buffer_begin(pkt)) || LDNS_ANCOUNT(sldns_buffer_begin(out)) != LDNS_ANCOUNT(sldns_buffer_begin(pkt)) || LDNS_NSCOUNT(sldns_buffer_begin(out)) != LDNS_NSCOUNT(sldns_buffer_begin(pkt))) unit_assert( LDNS_TC_WIRE(sldns_buffer_begin(out))); /* must set TC bit if shortened */ unit_assert(sldns_buffer_limit(out) <= lim); } } query_info_clear(&qi); reply_info_parsedelete(rep, alloc); regional_destroy(region); }