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); }
/** handle new query command for bg worker */ static void handle_newq(struct libworker* w, uint8_t* buf, uint32_t len) { uint16_t qflags, qid; struct query_info qinfo; struct edns_data edns; struct ctx_query* q; if(w->is_bg_thread) { lock_basic_lock(&w->ctx->cfglock); q = context_lookup_new_query(w->ctx, buf, len); lock_basic_unlock(&w->ctx->cfglock); } else { q = context_deserialize_new_query(w->ctx, buf, len); } free(buf); if(!q) { log_err("failed to deserialize newq"); return; } if(!setup_qinfo_edns(w, q, &qinfo, &edns)) { add_bg_result(w, q, NULL, UB_SYNTAX, NULL); return; } qid = 0; qflags = BIT_RD; /* see if there is a fixed answer */ sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid); sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags); if(local_zones_answer(w->ctx->local_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL)) { regional_free_all(w->env->scratch); q->msg_security = sec_status_insecure; add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL); free(qinfo.qname); return; } if(w->ctx->env->auth_zones && auth_zones_answer(w->ctx->env->auth_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) { regional_free_all(w->env->scratch); q->msg_security = sec_status_insecure; add_bg_result(w, q, w->back->udp_buff, UB_NOERROR, NULL); free(qinfo.qname); return; } q->w = w; /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, w->back->udp_buff, qid, libworker_bg_done_cb, q)) { add_bg_result(w, q, NULL, UB_NOMEM, NULL); } free(qinfo.qname); }
int libworker_fg(struct ub_ctx* ctx, struct ctx_query* q) { struct libworker* w = libworker_setup(ctx, 0, NULL); uint16_t qflags, qid; struct query_info qinfo; struct edns_data edns; if(!w) return UB_INITFAIL; if(!setup_qinfo_edns(w, q, &qinfo, &edns)) { libworker_delete(w); return UB_SYNTAX; } qid = 0; qflags = BIT_RD; q->w = w; /* see if there is a fixed answer */ sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid); sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags); if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL)) { regional_free_all(w->env->scratch); libworker_fillup_fg(q, LDNS_RCODE_NOERROR, w->back->udp_buff, sec_status_insecure, NULL); libworker_delete(w); free(qinfo.qname); return UB_NOERROR; } if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) { regional_free_all(w->env->scratch); libworker_fillup_fg(q, LDNS_RCODE_NOERROR, w->back->udp_buff, sec_status_insecure, NULL); libworker_delete(w); free(qinfo.qname); return UB_NOERROR; } /* process new query */ if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, w->back->udp_buff, qid, libworker_fg_done_cb, q)) { free(qinfo.qname); return UB_NOMEM; } free(qinfo.qname); /* wait for reply */ comm_base_dispatch(w->base); libworker_delete(w); return UB_NOERROR; }
int libworker_attach_mesh(struct ub_ctx* ctx, struct ctx_query* q, int* async_id) { struct libworker* w = ctx->event_worker; uint16_t qflags, qid; struct query_info qinfo; struct edns_data edns; if(!w) return UB_INITFAIL; if(!setup_qinfo_edns(w, q, &qinfo, &edns)) return UB_SYNTAX; qid = 0; qflags = BIT_RD; q->w = w; /* see if there is a fixed answer */ sldns_buffer_write_u16_at(w->back->udp_buff, 0, qid); sldns_buffer_write_u16_at(w->back->udp_buff, 2, qflags); if(local_zones_answer(ctx->local_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch, NULL, NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL)) { regional_free_all(w->env->scratch); free(qinfo.qname); libworker_event_done_cb(q, LDNS_RCODE_NOERROR, w->back->udp_buff, sec_status_insecure, NULL); return UB_NOERROR; } if(ctx->env->auth_zones && auth_zones_answer(ctx->env->auth_zones, w->env, &qinfo, &edns, w->back->udp_buff, w->env->scratch)) { regional_free_all(w->env->scratch); free(qinfo.qname); libworker_event_done_cb(q, LDNS_RCODE_NOERROR, w->back->udp_buff, sec_status_insecure, NULL); return UB_NOERROR; } /* process new query */ if(async_id) *async_id = q->querynum; if(!mesh_new_callback(w->env->mesh, &qinfo, qflags, &edns, w->back->udp_buff, qid, libworker_event_done_cb, q)) { free(qinfo.qname); return UB_NOMEM; } free(qinfo.qname); return UB_NOERROR; }
/** compress domain names in rdata, return RETVAL_* */ static int compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen, struct regional* region, struct compress_tree_node** tree, const sldns_rr_descriptor* desc) { int labs, r, rdf = 0; size_t dname_len, len, pos = sldns_buffer_position(pkt); uint8_t count = desc->_dname_count; sldns_buffer_skip(pkt, 2); /* rdata len fill in later */ /* space for rdatalen checked for already */ rdata += 2; todolen -= 2; while(todolen > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: labs = dname_count_size_labels(rdata, &dname_len); if((r=compress_any_dname(rdata, pkt, labs, region, tree)) != RETVAL_OK) return r; rdata += dname_len; todolen -= dname_len; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = *rdata + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { /* copy over */ if(sldns_buffer_remaining(pkt) < len) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, len); todolen -= len; rdata += len; } rdf++; } /* copy remainder */ if(todolen > 0) { if(sldns_buffer_remaining(pkt) < todolen) return RETVAL_TRUNC; sldns_buffer_write(pkt, rdata, todolen); } /* set rdata len */ sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2); return RETVAL_OK; }
/** 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 ++; }
/** parse a query line to a packet into buffer */ static int qlist_parse_line(sldns_buffer* buf, char* p) { char nm[1024], cl[1024], tp[1024], fl[1024]; int r; int rec = 1, edns = 0; struct query_info qinfo; nm[0] = 0; cl[0] = 0; tp[0] = 0; fl[0] = 0; r = sscanf(p, " %1023s %1023s %1023s %1023s", nm, cl, tp, fl); if(r != 3 && r != 4) return 0; /*printf("nm='%s', cl='%s', tp='%s', fl='%s'\n", nm, cl, tp, fl);*/ if(strcmp(tp, "IN") == 0 || strcmp(tp, "CH") == 0) { qinfo.qtype = sldns_get_rr_type_by_name(cl); qinfo.qclass = sldns_get_rr_class_by_name(tp); } else { qinfo.qtype = sldns_get_rr_type_by_name(tp); qinfo.qclass = sldns_get_rr_class_by_name(cl); } if(fl[0] == '+') rec = 1; else if(fl[0] == '-') rec = 0; else if(fl[0] == 'E') edns = 1; if((fl[0] == '+' || fl[0] == '-') && fl[1] == 'E') edns = 1; qinfo.qname = sldns_str2wire_dname(nm, &qinfo.qname_len); if(!qinfo.qname) return 0; qinfo_query_encode(buf, &qinfo); sldns_buffer_write_u16_at(buf, 0, 0); /* zero ID */ if(rec) LDNS_RD_SET(sldns_buffer_begin(buf)); if(edns) { struct edns_data ed; memset(&ed, 0, sizeof(ed)); ed.edns_present = 1; ed.udp_size = EDNS_ADVERTISED_SIZE; /* Set DO bit in all EDNS datagrams ... */ ed.bits = EDNS_DO; attach_edns_record(buf, &ed); } free(qinfo.qname); return 1; }
void attach_edns_record(sldns_buffer* pkt, struct edns_data* edns) { size_t len; 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); sldns_buffer_write_u16(pkt, 0); /* rdatalen */ sldns_buffer_flip(pkt); }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* "prepend" any local alias records in the answer section if this * response is supposed to be authoritative. Currently it should * be a single CNAME record (sanity-checked in worker_handle_request()) * but it can be extended if and when we support more variations of * aliases. */ if(qinfo->local_alias && (flags & BIT_AA)) { struct reply_info arep; time_t timezero = 0; /* to use the 'authoritative' TTL */ memset(&arep, 0, sizeof(arep)); arep.flags = rep->flags; arep.an_numrrsets = 1; arep.rrset_count = 1; arep.rrsets = &qinfo->local_alias->rrset; if((r=insert_section(&arep, 1, &ancount, buffer, 0, timezero, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }
/** write a query over the TCP fd */ static void write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, const char* strname, const char* strtype, const char* strclass) { struct query_info qinfo; uint16_t len; /* qname */ qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); if(!qinfo.qname) { printf("cannot parse query name: '%s'\n", strname); exit(1); } /* qtype and qclass */ qinfo.qtype = sldns_get_rr_type_by_name(strtype); qinfo.qclass = sldns_get_rr_class_by_name(strclass); /* make query */ qinfo_query_encode(buf, &qinfo); sldns_buffer_write_u16_at(buf, 0, id); sldns_buffer_write_u16_at(buf, 2, BIT_RD); if(1) { /* add EDNS DO */ struct edns_data edns; memset(&edns, 0, sizeof(edns)); edns.edns_present = 1; edns.bits = EDNS_DO; edns.udp_size = 4096; attach_edns_record(buf, &edns); } /* send it */ if(!udp) { len = (uint16_t)sldns_buffer_limit(buf); len = htons(len); if(ssl) { if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) { log_crypto_err("cannot SSL_write"); exit(1); } } else { if(send(fd, (void*)&len, sizeof(len), 0) < (ssize_t)sizeof(len)){ #ifndef USE_WINSOCK perror("send() len failed"); #else printf("send len: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } } } if(ssl) { if(SSL_write(ssl, (void*)sldns_buffer_begin(buf), (int)sldns_buffer_limit(buf)) <= 0) { log_crypto_err("cannot SSL_write"); exit(1); } } else { if(send(fd, (void*)sldns_buffer_begin(buf), sldns_buffer_limit(buf), 0) < (ssize_t)sldns_buffer_limit(buf)) { #ifndef USE_WINSOCK perror("send() data failed"); #else printf("send data: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); } } free(qinfo.qname); }
int reply_info_encode(struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow, struct regional* region, uint16_t udpsize, int dnssec) { uint16_t ancount=0, nscount=0, arcount=0; struct compress_tree_node* tree = 0; int r; size_t rr_offset; sldns_buffer_clear(buffer); if(udpsize < sldns_buffer_limit(buffer)) sldns_buffer_set_limit(buffer, udpsize); if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE) return 0; sldns_buffer_write(buffer, &id, sizeof(uint16_t)); sldns_buffer_write_u16(buffer, flags); sldns_buffer_write_u16(buffer, rep->qdcount); /* set an, ns, ar counts to zero in case of small packets */ sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6); /* insert query section */ if(rep->qdcount) { if((r=insert_query(qinfo, &tree, buffer, region)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 4, 0); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } } /* roundrobin offset. using query id for random number. With ntohs * for different roundrobins for sequential id client senders. */ rr_offset = RRSET_ROUNDROBIN?ntohs(id):0; /* insert answer section */ if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer, 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 6, ancount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 6, ancount); /* if response is positive answer, auth/add sections are not required */ if( ! (MINIMAL_RESPONSES && positive_answer(rep, qinfo->qtype)) ) { /* insert auth section */ if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer, rep->an_numrrsets, timenow, region, &tree, LDNS_SECTION_AUTHORITY, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* create truncated message */ sldns_buffer_write_u16_at(buffer, 8, nscount); LDNS_TC_SET(sldns_buffer_begin(buffer)); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 8, nscount); /* insert add section */ if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer, rep->an_numrrsets + rep->ns_numrrsets, timenow, region, &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) { if(r == RETVAL_TRUNC) { /* no need to set TC bit, this is the additional */ sldns_buffer_write_u16_at(buffer, 10, arcount); sldns_buffer_flip(buffer); return 1; } return 0; } sldns_buffer_write_u16_at(buffer, 10, arcount); } sldns_buffer_flip(buffer); return 1; }