/** analyse pkt */ static void analyze(sldns_buffer* pkt) { uint16_t i, f, qd, an, ns, ar; int rrnum = 0; printf("packet length %d\n", (int)sldns_buffer_limit(pkt)); if(sldns_buffer_limit(pkt) < 12) return; i = sldns_buffer_read_u16(pkt); printf("id (hostorder): %d (0x%x)\n", (int)i, (unsigned)i); f = sldns_buffer_read_u16(pkt); printf("flags: 0x%x\n", (unsigned)f); qd = sldns_buffer_read_u16(pkt); printf("qdcount: %d\n", (int)qd); an = sldns_buffer_read_u16(pkt); printf("ancount: %d\n", (int)an); ns = sldns_buffer_read_u16(pkt); printf("nscount: %d\n", (int)ns); ar = sldns_buffer_read_u16(pkt); printf("arcount: %d\n", (int)ar); printf(";-- query section\n"); while(sldns_buffer_remaining(pkt) > 0) { if(rrnum == (int)qd) printf(";-- answer section\n"); if(rrnum == (int)qd+(int)an) printf(";-- authority section\n"); if(rrnum == (int)qd+(int)an+(int)ns) printf(";-- additional section\n"); printf("rr %d ", rrnum); analyze_rr(pkt, rrnum < (int)qd); rrnum++; } }
size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) { size_t i, hash_len; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, nm, nmlen); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); hash_len = nsec3_hash_algo_size_supported(algo); if(hash_len == 0) { log_err("nsec3 hash of unknown algo %d", algo); return 0; } if(hash_len > max) return 0; if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)res)) return 0; for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, res, hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); if(!secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)res)) return 0; } return hash_len; }
/** add entry to ringbuffer */ static void ring_add(struct ringbuf* r, sldns_buffer* pkt, struct timeval* now, struct timeval* delay, struct proxy* p) { /* time -- proxy* -- 16bitlen -- message */ uint16_t len = (uint16_t)sldns_buffer_limit(pkt); struct timeval when; size_t needed; uint8_t* where = NULL; log_assert(sldns_buffer_limit(pkt) <= 65535); needed = sizeof(when) + sizeof(p) + sizeof(len) + len; /* put item into ringbuffer */ if(r->low < r->high) { /* used part is in the middle */ if(r->size - r->high >= needed) { where = r->buf + r->high; r->high += needed; } else if(r->low > needed) { /* wrap around ringbuffer */ /* make sure r->low == r->high means empty */ /* so r->low == r->high cannot be used to signify * a completely full ringbuf */ if(r->size - r->high > sizeof(when)+sizeof(p)) { /* zero entry at end of buffer */ memset(r->buf+r->high, 0, sizeof(when)+sizeof(p)); } where = r->buf; r->high = needed; } else { /* drop message */ log_warn("warning: mem full, dropped message"); return; } } else { /* empty */ if(r->high == r->low) { where = r->buf; r->low = 0; r->high = needed; /* unused part is in the middle */ /* so ringbuffer has wrapped around */ } else if(r->low - r->high > needed) { where = r->buf + r->high; r->high += needed; } else { log_warn("warning: mem full, dropped message"); return; } } when = *now; dl_tv_add(&when, delay); /* copy it at where part */ log_assert(where != NULL); memmove(where, &when, sizeof(when)); memmove(where+sizeof(when), &p, sizeof(p)); memmove(where+sizeof(when)+sizeof(p), &len, sizeof(len)); memmove(where+sizeof(when)+sizeof(p)+sizeof(len), sldns_buffer_begin(pkt), len); }
/** send out waiting packets */ static void service_send(struct ringbuf* ring, struct timeval* now, sldns_buffer* pkt, struct sockaddr_storage* srv_addr, socklen_t srv_len) { struct proxy* p; struct timeval tv; ssize_t sent; while(!ring_empty(ring) && dl_tv_smaller(ring_peek_time(ring), now)) { /* this items needs to be sent out */ if(!ring_pop(ring, pkt, &tv, &p)) fatal_exit("ringbuf error: pop failed"); verbose(1, "send out query %d.%6.6d", (unsigned)tv.tv_sec, (unsigned)tv.tv_usec); log_addr(1, "from client", &p->addr, p->addr_len); /* send it */ sent = sendto(p->s, (void*)sldns_buffer_begin(pkt), sldns_buffer_limit(pkt), 0, (struct sockaddr*)srv_addr, srv_len); if(sent == -1) { #ifndef USE_WINSOCK log_err("sendto: %s", strerror(errno)); #else log_err("sendto: %s", wsa_strerror(WSAGetLastError())); #endif } else if(sent != (ssize_t)sldns_buffer_limit(pkt)) { log_err("sendto: partial send"); } p->lastuse = *now; p->numsent++; } }
size_t nsec3_get_hashed(sldns_buffer* buf, uint8_t* nm, size_t nmlen, int algo, size_t iter, uint8_t* salt, size_t saltlen, uint8_t* res, size_t max) { size_t i, hash_len; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, nm, nmlen); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); switch(algo) { #if defined(HAVE_EVP_SHA1) || defined(HAVE_NSS) case NSEC3_HASH_SHA1: #ifdef HAVE_SSL hash_len = SHA_DIGEST_LENGTH; #else hash_len = SHA1_LENGTH; #endif if(hash_len > max) return 0; # ifdef HAVE_SSL (void)SHA1((unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, res, hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); # ifdef HAVE_SSL (void)SHA1( (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf), (unsigned char*)res); # else (void)HASH_HashBuf(HASH_AlgSHA1, (unsigned char*)res, (unsigned char*)sldns_buffer_begin(buf), (unsigned long)sldns_buffer_limit(buf)); # endif } break; #endif /* HAVE_EVP_SHA1 or NSS */ default: log_err("nsec3 hash of unknown algo %d", algo); return 0; } return hash_len; }
struct waiting_tcp* pending_tcp_query(struct outside_network* outnet, sldns_buffer* packet, struct sockaddr_storage* addr, socklen_t addrlen, int timeout, comm_point_callback_t* callback, void* callback_arg, int ATTR_UNUSED(ssl_upstream)) { struct replay_runtime* runtime = (struct replay_runtime*)outnet->base; struct fake_pending* pend = (struct fake_pending*)calloc(1, sizeof(struct fake_pending)); log_assert(pend); pend->buffer = sldns_buffer_new(sldns_buffer_capacity(packet)); log_assert(pend->buffer); sldns_buffer_write(pend->buffer, sldns_buffer_begin(packet), sldns_buffer_limit(packet)); sldns_buffer_flip(pend->buffer); memcpy(&pend->addr, addr, addrlen); pend->addrlen = addrlen; pend->callback = callback; pend->cb_arg = callback_arg; pend->timeout = timeout; pend->transport = transport_tcp; pend->pkt = NULL; pend->zone = NULL; pend->runtime = runtime; pend->serviced = 0; pend->pkt_len = sldns_buffer_limit(packet); pend->pkt = memdup(sldns_buffer_begin(packet), pend->pkt_len); if(!pend->pkt) fatal_exit("out of memory"); log_pkt("pending tcp pkt: ", pend->pkt, pend->pkt_len); /* see if it matches the current moment */ if(runtime->now && runtime->now->evt_type == repevt_back_query && (runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, &pend->addr, pend->addrlen) == 0) && find_match(runtime->now->match, pend->pkt, pend->pkt_len, pend->transport)) { log_info("testbound: matched pending to event. " "advance time between events."); log_info("testbound: do STEP %d %s", runtime->now->time_step, repevt_string(runtime->now->evt_type)); advance_moment(runtime); /* still create the pending, because we need it to callback */ } log_info("testbound: created fake pending"); /* add to list */ pend->next = runtime->pending_list; runtime->pending_list = pend; return (struct waiting_tcp*)pend; }
/** 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; }
void attach_edns_record(sldns_buffer* pkt, struct edns_data* edns) { size_t len; size_t rdatapos; struct edns_option* opt; if(!edns || !edns->edns_present) return; /* inc additional count */ sldns_buffer_write_u16_at(pkt, 10, sldns_buffer_read_u16_at(pkt, 10) + 1); len = sldns_buffer_limit(pkt); sldns_buffer_clear(pkt); sldns_buffer_set_position(pkt, len); /* write EDNS record */ sldns_buffer_write_u8(pkt, 0); /* '.' label */ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */ sldns_buffer_write_u16(pkt, edns->udp_size); /* class */ sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */ sldns_buffer_write_u8(pkt, edns->edns_version); sldns_buffer_write_u16(pkt, edns->bits); rdatapos = sldns_buffer_position(pkt); sldns_buffer_write_u16(pkt, 0); /* rdatalen */ /* write rdata */ for(opt=edns->opt_list; opt; opt=opt->next) { sldns_buffer_write_u16(pkt, opt->opt_code); sldns_buffer_write_u16(pkt, opt->opt_len); if(opt->opt_len != 0) sldns_buffer_write(pkt, opt->opt_data, opt->opt_len); } if(edns->opt_list) sldns_buffer_write_u16_at(pkt, rdatapos, sldns_buffer_position(pkt)-rdatapos-2); sldns_buffer_flip(pkt); }
void pkt_dname_tolower(sldns_buffer* pkt, uint8_t* dname) { uint8_t lablen; int count = 0; if(dname >= sldns_buffer_end(pkt)) return; lablen = *dname++; while(lablen) { if(LABEL_IS_PTR(lablen)) { if((size_t)PTR_OFFSET(lablen, *dname) >= sldns_buffer_limit(pkt)) return; dname = sldns_buffer_at(pkt, PTR_OFFSET(lablen, *dname)); lablen = *dname++; if(count++ > MAX_COMPRESS_PTRS) return; continue; } if(dname+lablen >= sldns_buffer_end(pkt)) return; while(lablen--) { *dname = (uint8_t)tolower((int)*dname); dname++; } if(dname >= sldns_buffer_end(pkt)) return; lablen = *dname++; } }
/** performance test message encoding */ static void perf_encode(struct query_info* qi, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* out, time_t timenow, struct edns_data* edns) { static int num = 0; int ret; size_t max = 10000; size_t i; struct timeval start, end; double dt; struct regional* r2 = regional_create(); if(gettimeofday(&start, NULL) < 0) fatal_exit("gettimeofday: %s", strerror(errno)); /* encode a couple times */ for(i=0; i<max; i++) { ret = reply_info_encode(qi, rep, id, flags, out, timenow, r2, 65535, (int)(edns->bits & EDNS_DO) ); unit_assert(ret != 0); /* udp packets should fit */ attach_edns_record(out, edns); regional_free_all(r2); } if(gettimeofday(&end, NULL) < 0) fatal_exit("gettimeofday: %s", strerror(errno)); /* time in millisec */ dt = (double)(end.tv_sec - start.tv_sec)*1000. + ((double)end.tv_usec - (double)start.tv_usec)/1000.; printf("[%d] did %u in %g msec for %f encode/sec size %d\n", num++, (unsigned)max, dt, (double)max / (dt/1000.), (int)sldns_buffer_limit(out)); regional_destroy(r2); }
/** perf test a packet */ static void perftestpkt(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; time_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) checkformerr(pkt); unit_assert(ret != LDNS_RCODE_SERVFAIL); } else { perf_encode(&qi, rep, id, flags, out, timenow, &edns); } query_info_clear(&qi); reply_info_parsedelete(rep, alloc); regional_destroy(region); }
void libworker_event_done_cb(void* arg, int rcode, sldns_buffer* buf, enum sec_status s, char* why_bogus) { struct ctx_query* q = (struct ctx_query*)arg; ub_event_callback_type cb = (ub_event_callback_type)q->cb; void* cb_arg = q->cb_arg; int cancelled = q->cancelled; /* delete it now */ struct ub_ctx* ctx = q->w->ctx; lock_basic_lock(&ctx->cfglock); (void)rbtree_delete(&ctx->queries, q->node.key); ctx->num_async--; context_query_delete(q); lock_basic_unlock(&ctx->cfglock); if(!cancelled) { /* call callback */ int sec = 0; if(s == sec_status_bogus) sec = 1; else if(s == sec_status_secure) sec = 2; (*cb)(cb_arg, rcode, (void*)sldns_buffer_begin(buf), (int)sldns_buffer_limit(buf), sec, why_bogus); } }
/** * Create a DS digest for a DNSKEY entry. * * @param env: module environment. Uses scratch space. * @param dnskey_rrset: DNSKEY rrset. * @param dnskey_idx: index of RR in rrset. * @param ds_rrset: DS rrset * @param ds_idx: index of RR in DS rrset. * @param digest: digest is returned in here (must be correctly sized). * @return false on error. */ static int ds_create_dnskey_digest(struct module_env* env, struct ub_packed_rrset_key* dnskey_rrset, size_t dnskey_idx, struct ub_packed_rrset_key* ds_rrset, size_t ds_idx, uint8_t* digest) { sldns_buffer* b = env->scratch_buffer; uint8_t* dnskey_rdata; size_t dnskey_len; rrset_get_rdata(dnskey_rrset, dnskey_idx, &dnskey_rdata, &dnskey_len); /* create digest source material in buffer * digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); * DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. */ sldns_buffer_clear(b); sldns_buffer_write(b, dnskey_rrset->rk.dname, dnskey_rrset->rk.dname_len); query_dname_tolower(sldns_buffer_begin(b)); sldns_buffer_write(b, dnskey_rdata+2, dnskey_len-2); /* skip rdatalen*/ sldns_buffer_flip(b); return secalgo_ds_digest(ds_get_digest_algo(ds_rrset, ds_idx), (unsigned char*)sldns_buffer_begin(b), sldns_buffer_limit(b), (unsigned char*)digest); }
/** check expiry, return true if matches OK */ static int good_expiry_and_qinfo(struct module_qstate* qstate, struct sldns_buffer* buf) { uint64_t expiry; /* the expiry time is the last bytes of the buffer */ if(sldns_buffer_limit(buf) < sizeof(expiry)) return 0; sldns_buffer_read_at(buf, sldns_buffer_limit(buf)-sizeof(expiry), &expiry, sizeof(expiry)); expiry = be64toh(expiry); if((time_t)expiry < *qstate->env->now && !qstate->env->cfg->serve_expired) return 0; return 1; }
static void dt_fill_buffer(sldns_buffer *b, ProtobufCBinaryData *p, protobuf_c_boolean *has) { log_assert(b != NULL); p->len = sldns_buffer_limit(b); p->data = sldns_buffer_begin(b); *has = 1; }
/** setup query list in info */ static void qlist_add_line(struct perfinfo* info, char* line, int no) { if(!qlist_parse_line(info->buf, line)) { printf("error parsing query %d: %s\n", no, line); exit(1); } sldns_buffer_write_u16_at(info->buf, 0, (uint16_t)info->qlist_size); if(info->qlist_size + 1 > info->qlist_capacity) { qlist_grow_capacity(info); } info->qlist_len[info->qlist_size] = sldns_buffer_limit(info->buf); info->qlist_data[info->qlist_size] = memdup( sldns_buffer_begin(info->buf), sldns_buffer_limit(info->buf)); if(!info->qlist_data[info->qlist_size]) fatal_exit("out of memory"); info->qlist_size ++; }
void server_stats_insrcode(struct ub_server_stats* stats, sldns_buffer* buf) { if(stats->extended && sldns_buffer_limit(buf) != 0) { int r = (int)LDNS_RCODE_WIRE( sldns_buffer_begin(buf) ); stats->ans_rcode[r] ++; if(r == 0 && LDNS_ANCOUNT( sldns_buffer_begin(buf) ) == 0) stats->ans_rcode_nodata ++; } }
void sldns_buffer_copy(sldns_buffer* result, sldns_buffer* from) { size_t tocopy = sldns_buffer_limit(from); if(tocopy > sldns_buffer_capacity(result)) tocopy = sldns_buffer_capacity(result); sldns_buffer_clear(result); sldns_buffer_write(result, sldns_buffer_begin(from), tocopy); sldns_buffer_flip(result); }
int reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow, int cached, struct regional* region, uint16_t udpsize, struct edns_data* edns, int dnssec, int secure) { uint16_t flags; unsigned int attach_edns = 0; if(!cached || rep->authoritative) { /* original flags, copy RD and CD bits from query. */ flags = rep->flags | (qflags & (BIT_RD|BIT_CD)); } else { /* remove AA bit, copy RD and CD bits from query. */ flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD)); } if(secure && (dnssec || (qflags&BIT_AD))) flags |= BIT_AD; /* restore AA bit if we have a local alias and the response can be * authoritative. Also clear AD bit if set as the local data is the * primary answer. */ if(qinf->local_alias && (FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR || FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) { flags |= BIT_AA; flags &= ~BIT_AD; } log_assert(flags & BIT_QR); /* QR bit must be on in our replies */ if(udpsize < LDNS_HEADER_SIZE) return 0; if(sldns_buffer_capacity(pkt) < udpsize) udpsize = sldns_buffer_capacity(pkt); if(udpsize < LDNS_HEADER_SIZE + calc_edns_field_size(edns)) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { /* reserve space for edns record */ attach_edns = (unsigned int)calc_edns_field_size(edns); udpsize -= attach_edns; } if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region, udpsize, dnssec)) { log_err("reply encode: out of memory"); return 0; } if(attach_edns && sldns_buffer_capacity(pkt) >= sldns_buffer_limit(pkt)+attach_edns) attach_edns_record(pkt, edns); return 1; }
void comm_point_send_reply(struct comm_reply* repinfo) { struct replay_answer* ans = (struct replay_answer*)calloc(1, sizeof(struct replay_answer)); struct replay_runtime* runtime = (struct replay_runtime*)repinfo->c->ev; log_info("testbound: comm_point_send_reply fake"); /* dump it into the todo list */ log_assert(ans); memcpy(&ans->repinfo, repinfo, sizeof(struct comm_reply)); ans->next = NULL; if(runtime->answer_last) runtime->answer_last->next = ans; else runtime->answer_list = ans; runtime->answer_last = ans; /* try to parse packet */ ans->pkt = memdup(sldns_buffer_begin(ans->repinfo.c->buffer), sldns_buffer_limit(ans->repinfo.c->buffer)); ans->pkt_len = sldns_buffer_limit(ans->repinfo.c->buffer); if(!ans->pkt) fatal_exit("out of memory"); log_pkt("reply pkt: ", ans->pkt, ans->pkt_len); }
/** perform hash of name */ static int nsec3_calc_hash(struct regional* region, sldns_buffer* buf, struct nsec3_cached_hash* c) { int algo = nsec3_get_algo(c->nsec3, c->rr); size_t iter = nsec3_get_iter(c->nsec3, c->rr); uint8_t* salt; size_t saltlen, i; if(!nsec3_get_salt(c->nsec3, c->rr, &salt, &saltlen)) return -1; /* prepare buffer for first iteration */ sldns_buffer_clear(buf); sldns_buffer_write(buf, c->dname, c->dname_len); query_dname_tolower(sldns_buffer_begin(buf)); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); c->hash_len = nsec3_hash_algo_size_supported(algo); if(c->hash_len == 0) { log_err("nsec3 hash of unknown algo %d", algo); return -1; } c->hash = (uint8_t*)regional_alloc(region, c->hash_len); if(!c->hash) return 0; (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)c->hash); for(i=0; i<iter; i++) { sldns_buffer_clear(buf); sldns_buffer_write(buf, c->hash, c->hash_len); sldns_buffer_write(buf, salt, saltlen); sldns_buffer_flip(buf); (void)secalgo_nsec3_hash(algo, (unsigned char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), (unsigned char*)c->hash); } return 1; }
size_t pkt_dname_len(sldns_buffer* pkt) { size_t len = 0; int ptrcount = 0; uint8_t labellen; size_t endpos = 0; /* read dname and determine length */ /* check compression pointers, loops, out of bounds */ while(1) { /* read next label */ if(sldns_buffer_remaining(pkt) < 1) return 0; labellen = sldns_buffer_read_u8(pkt); if(LABEL_IS_PTR(labellen)) { /* compression ptr */ uint16_t ptr; if(sldns_buffer_remaining(pkt) < 1) return 0; ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt)); if(ptrcount++ > MAX_COMPRESS_PTRS) return 0; /* loop! */ if(sldns_buffer_limit(pkt) <= ptr) return 0; /* out of bounds! */ if(!endpos) endpos = sldns_buffer_position(pkt); sldns_buffer_set_position(pkt, ptr); } else { /* label contents */ if(labellen > 0x3f) return 0; /* label too long */ len += 1 + labellen; if(len > LDNS_MAX_DOMAINLEN) return 0; if(labellen == 0) { /* end of dname */ break; } if(sldns_buffer_remaining(pkt) < labellen) return 0; sldns_buffer_skip(pkt, (ssize_t)labellen); } } if(endpos) sldns_buffer_set_position(pkt, endpos); return len; }
/** convert data from return_msg into the data buffer */ static int prep_data(struct module_qstate* qstate, struct sldns_buffer* buf) { uint64_t timestamp, expiry; size_t oldlim; struct edns_data edns; memset(&edns, 0, sizeof(edns)); edns.edns_present = 1; edns.bits = EDNS_DO; edns.ext_rcode = 0; edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; if(!qstate->return_msg || !qstate->return_msg->rep) return 0; /* We don't store the reply if its TTL is 0 unless serve-expired is * enabled. Such a reply won't be reusable and simply be a waste for * the backend. It's also compatible with the default behavior of * dns_cache_store_msg(). */ if(qstate->return_msg->rep->ttl == 0 && !qstate->env->cfg->serve_expired) return 0; if(verbosity >= VERB_ALGO) log_dns_msg("cachedb encoding", &qstate->return_msg->qinfo, qstate->return_msg->rep); if(!reply_info_answer_encode(&qstate->return_msg->qinfo, qstate->return_msg->rep, 0, qstate->query_flags, buf, 0, 1, qstate->env->scratch, 65535, &edns, 1, 0)) return 0; /* TTLs in the return_msg are relative to time(0) so we have to * store that, we also store the smallest ttl in the packet+time(0) * as the packet expiry time */ /* qstate->return_msg->rep->ttl contains that relative shortest ttl */ timestamp = (uint64_t)*qstate->env->now; expiry = timestamp + (uint64_t)qstate->return_msg->rep->ttl; timestamp = htobe64(timestamp); expiry = htobe64(expiry); oldlim = sldns_buffer_limit(buf); if(oldlim + sizeof(timestamp)+sizeof(expiry) >= sldns_buffer_capacity(buf)) return 0; /* doesn't fit. */ sldns_buffer_set_limit(buf, oldlim + sizeof(timestamp)+sizeof(expiry)); sldns_buffer_write_at(buf, oldlim, ×tamp, sizeof(timestamp)); sldns_buffer_write_at(buf, oldlim+sizeof(timestamp), &expiry, sizeof(expiry)); return 1; }
/** fillup fg results */ static void libworker_fillup_fg(struct ctx_query* q, int rcode, sldns_buffer* buf, enum sec_status s, char* why_bogus) { if(why_bogus) q->res->why_bogus = strdup(why_bogus); if(rcode != 0) { q->res->rcode = rcode; q->msg_security = s; return; } q->res->rcode = LDNS_RCODE_SERVFAIL; q->msg_security = 0; q->msg = memdup(sldns_buffer_begin(buf), sldns_buffer_limit(buf)); q->msg_len = sldns_buffer_limit(buf); if(!q->msg) { return; /* the error is in the rcode */ } /* canonname and results */ q->msg_security = s; libworker_enter_result(q->res, buf, q->w->env->scratch, s); }
/** * Store the qstate.return_msg in extcache for key qstate.info */ static void cachedb_extcache_store(struct module_qstate* qstate, struct cachedb_env* ie) { char key[(CACHEDB_HASHSIZE/8)*2+1]; calc_hash(qstate, key, sizeof(key)); /* prepare data in scratch buffer */ if(!prep_data(qstate, qstate->env->scratch_buffer)) return; /* call backend */ (*ie->backend->store)(qstate->env, ie, key, sldns_buffer_begin(qstate->env->scratch_buffer), sldns_buffer_limit(qstate->env->scratch_buffer)); }
int nsec3_covers(uint8_t* zone, struct nsec3_cached_hash* hash, struct ub_packed_rrset_key* rrset, int rr, sldns_buffer* buf) { uint8_t* next, *owner; size_t nextlen; int len; if(!nsec3_get_nextowner(rrset, rr, &next, &nextlen)) return 0; /* malformed RR proves nothing */ /* check the owner name is a hashed value . apex * base32 encoded values must have equal length. * hash_value and next hash value must have equal length. */ if(nextlen != hash->hash_len || hash->hash_len==0||hash->b32_len==0|| (size_t)*rrset->rk.dname != hash->b32_len || query_dname_compare(rrset->rk.dname+1+ (size_t)*rrset->rk.dname, zone) != 0) return 0; /* bad lengths or owner name */ /* This is the "normal case: owner < next and owner < hash < next */ if(label_compare_lower(rrset->rk.dname+1, hash->b32, hash->b32_len) < 0 && memcmp(hash->hash, next, nextlen) < 0) return 1; /* convert owner name from text to binary */ sldns_buffer_clear(buf); owner = sldns_buffer_begin(buf); len = sldns_b32_pton_extended_hex((char*)rrset->rk.dname+1, hash->b32_len, owner, sldns_buffer_limit(buf)); if(len<1) return 0; /* bad owner name in some way */ if((size_t)len != hash->hash_len || (size_t)len != nextlen) return 0; /* wrong length */ /* this is the end of zone case: next <= owner && * (hash > owner || hash < next) * this also covers the only-apex case of next==owner. */ if(memcmp(next, owner, nextlen) <= 0 && ( memcmp(hash->hash, owner, nextlen) > 0 || memcmp(hash->hash, next, nextlen) < 0)) { return 1; } return 0; }
/** check if unbound formerr equals ldns formerr */ static void checkformerr(sldns_buffer* pkt) { int status = 0; char* s = sldns_wire2str_pkt(sldns_buffer_begin(pkt), sldns_buffer_limit(pkt)); if(!s) fatal_exit("out of memory"); if(strstr(s, "Error")) status = 1; if(strstr(s, "error")) status = 1; if(status == 0) { printf("Formerr, but ldns gives packet:\n"); printf("%s\n", s); free(s); exit(1); } free(s); unit_assert(status != 0); }
/** perform b32 encoding of hash */ static int nsec3_calc_b32(struct regional* region, sldns_buffer* buf, struct nsec3_cached_hash* c) { int r; sldns_buffer_clear(buf); r = sldns_b32_ntop_extended_hex(c->hash, c->hash_len, (char*)sldns_buffer_begin(buf), sldns_buffer_limit(buf)); if(r < 1) { log_err("b32_ntop_extended_hex: error in encoding: %d", r); return 0; } c->b32_len = (size_t)r; c->b32 = regional_alloc_init(region, sldns_buffer_begin(buf), c->b32_len); if(!c->b32) return 0; return 1; }
void error_encode(sldns_buffer* buf, int r, struct query_info* qinfo, uint16_t qid, uint16_t qflags, struct edns_data* edns) { uint16_t flags; sldns_buffer_clear(buf); sldns_buffer_write(buf, &qid, sizeof(uint16_t)); flags = (uint16_t)(BIT_QR | BIT_RA | r); /* QR and retcode*/ flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */ sldns_buffer_write_u16(buf, flags); if(qinfo) flags = 1; else flags = 0; sldns_buffer_write_u16(buf, flags); flags = 0; sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); sldns_buffer_write(buf, &flags, sizeof(uint16_t)); if(qinfo) { const uint8_t* qname = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname : qinfo->qname; size_t qname_len = qinfo->local_alias ? qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len; if(sldns_buffer_current(buf) == qname) sldns_buffer_skip(buf, (ssize_t)qname_len); else sldns_buffer_write(buf, qname, qname_len); sldns_buffer_write_u16(buf, qinfo->qtype); sldns_buffer_write_u16(buf, qinfo->qclass); } sldns_buffer_flip(buf); if(edns) { struct edns_data es = *edns; es.edns_version = EDNS_ADVERTISED_VERSION; es.udp_size = EDNS_ADVERTISED_SIZE; es.ext_rcode = 0; es.bits &= EDNS_DO; if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) > edns->udp_size) return; attach_edns_record(buf, &es); } }
int query_info_parse(struct query_info* m, sldns_buffer* query) { uint8_t* q = sldns_buffer_begin(query); /* minimum size: header + \0 + qtype + qclass */ if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) return 0; if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || LDNS_QDCOUNT(q) != 1 || sldns_buffer_position(query) != 0) return 0; sldns_buffer_skip(query, LDNS_HEADER_SIZE); m->qname = sldns_buffer_current(query); if((m->qname_len = query_dname_len(query)) == 0) return 0; /* parse error */ if(sldns_buffer_remaining(query) < 4) return 0; /* need qtype, qclass */ m->qtype = sldns_buffer_read_u16(query); m->qclass = sldns_buffer_read_u16(query); return 1; }