/** * Fill CH class answer into buffer. Keeps query. * @param pkt: buffer * @param str: string to put into text record (<255). * @param edns: edns reply information. * @param worker: worker with scratch region. */ static void chaos_replystr(sldns_buffer* pkt, const char* str, struct edns_data* edns, struct worker* worker) { size_t len = strlen(str); unsigned int rd = LDNS_RD_WIRE(sldns_buffer_begin(pkt)); unsigned int cd = LDNS_CD_WIRE(sldns_buffer_begin(pkt)); if(len>255) len=255; /* cap size of TXT record */ sldns_buffer_clear(pkt); sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip id */ sldns_buffer_write_u16(pkt, (uint16_t)(BIT_QR|BIT_RA)); if(rd) LDNS_RD_SET(sldns_buffer_begin(pkt)); if(cd) LDNS_CD_SET(sldns_buffer_begin(pkt)); sldns_buffer_write_u16(pkt, 1); /* qdcount */ sldns_buffer_write_u16(pkt, 1); /* ancount */ sldns_buffer_write_u16(pkt, 0); /* nscount */ sldns_buffer_write_u16(pkt, 0); /* arcount */ (void)query_dname_len(pkt); /* skip qname */ sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qtype */ sldns_buffer_skip(pkt, (ssize_t)sizeof(uint16_t)); /* skip qclass */ sldns_buffer_write_u16(pkt, 0xc00c); /* compr ptr to query */ sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_TXT); sldns_buffer_write_u16(pkt, LDNS_RR_CLASS_CH); sldns_buffer_write_u32(pkt, 0); /* TTL */ sldns_buffer_write_u16(pkt, sizeof(uint8_t) + len); sldns_buffer_write_u8(pkt, len); sldns_buffer_write(pkt, str, len); sldns_buffer_flip(pkt); edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) edns->opt_list = NULL; attach_edns_record(pkt, edns); }
/** encode answer consisting of 1 rrset */ static int local_encode(struct query_info* qinfo, struct edns_data* edns, sldns_buffer* buf, struct regional* temp, struct ub_packed_rrset_key* rrset, int ansec, int rcode) { struct reply_info rep; uint16_t udpsize; /* make answer with time=0 for fixed TTL values */ memset(&rep, 0, sizeof(rep)); rep.flags = (uint16_t)((BIT_QR | BIT_AA | BIT_RA) | rcode); rep.qdcount = 1; if(ansec) rep.an_numrrsets = 1; else rep.ns_numrrsets = 1; rep.rrset_count = 1; rep.rrsets = &rrset; udpsize = edns->udp_size; edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, temp) || !reply_info_answer_encode(qinfo, &rep, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), buf, 0, 0, temp, udpsize, edns, (int)(edns->bits&EDNS_DO), 0)) error_encode(buf, (LDNS_RCODE_SERVFAIL|BIT_AA), qinfo, *(uint16_t*)sldns_buffer_begin(buf), sldns_buffer_read_u16_at(buf, 2), edns); return 1; }
/** answer query from the cache */ static int answer_from_cache(struct worker* worker, struct query_info* qinfo, struct reply_info* rep, uint16_t id, uint16_t flags, struct comm_reply* repinfo, struct edns_data* edns) { time_t timenow = *worker->env.now; uint16_t udpsize = edns->udp_size; int secure; int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd) && worker->env.need_to_validate; /* see if it is possible */ if(rep->ttl < timenow) { /* the rrsets may have been updated in the meantime. * we will refetch the message format from the * authoritative server */ return 0; } if(!rrset_array_lock(rep->ref, rep->rrset_count, timenow)) return 0; /* locked and ids and ttls are OK. */ /* check CNAME chain (if any) */ if(rep->an_numrrsets > 0 && (rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_CNAME) || rep->rrsets[0]->rk.type == htons(LDNS_RR_TYPE_DNAME))) { if(!reply_check_cname_chain(qinfo, rep)) { /* cname chain invalid, redo iterator steps */ verbose(VERB_ALGO, "Cache reply: cname chain broken"); bail_out: rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); return 0; } } /* check security status of the cached answer */ if( rep->security == sec_status_bogus && must_validate) { /* BAD cached */ edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); if(worker->stats.extended) { worker->stats.ans_bogus ++; worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL] ++; } return 1; } else if( rep->security == sec_status_unchecked && must_validate) { verbose(VERB_ALGO, "Cache reply: unchecked entry needs " "validation"); goto bail_out; /* need to validate cache entry first */ } else if(rep->security == sec_status_secure) { if(reply_all_rrsets_secure(rep)) secure = 1; else { if(must_validate) { verbose(VERB_ALGO, "Cache reply: secure entry" " changed status"); goto bail_out; /* rrset changed, re-verify */ } secure = 0; } } else secure = 0; edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; if(!reply_info_answer_encode(qinfo, rep, id, flags, repinfo->c->buffer, timenow, 1, worker->scratchpad, udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, qinfo, id, flags, edns); } /* cannot send the reply right now, because blocking network syscall * is bad while holding locks. */ rrset_array_unlock_touch(worker->env.rrset_cache, worker->scratchpad, rep->ref, rep->rrset_count); if(worker->stats.extended) { if(secure) worker->stats.ans_secure++; server_stats_insrcode(&worker->stats, repinfo->c->buffer); } /* go and return this buffer to the client */ return 1; }
/** answer nonrecursive query from the cache */ static int answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, uint16_t id, uint16_t flags, struct comm_reply* repinfo, struct edns_data* edns) { /* for a nonrecursive query return either: * o an error (servfail; we try to avoid this) * o a delegation (closest we have; this routine tries that) * o the answer (checked by answer_from_cache) * * So, grab a delegation from the rrset cache. * Then check if it needs validation, if so, this routine fails, * so that iterator can prime and validator can verify rrsets. */ uint16_t udpsize = edns->udp_size; int secure = 0; time_t timenow = *worker->env.now; int must_validate = (!(flags&BIT_CD) || worker->env.cfg->ignore_cd) && worker->env.need_to_validate; struct dns_msg *msg = NULL; struct delegpt *dp; dp = dns_cache_find_delegation(&worker->env, qinfo->qname, qinfo->qname_len, qinfo->qtype, qinfo->qclass, worker->scratchpad, &msg, timenow); if(!dp) { /* no delegation, need to reprime */ return 0; } if(must_validate) { switch(check_delegation_secure(msg->rep)) { case sec_status_unchecked: /* some rrsets have not been verified yet, go and * let validator do that */ return 0; case sec_status_bogus: /* some rrsets are bogus, reply servfail */ edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); if(worker->stats.extended) { worker->stats.ans_bogus++; worker->stats.ans_rcode[LDNS_RCODE_SERVFAIL]++; } return 1; case sec_status_secure: /* all rrsets are secure */ /* remove non-secure rrsets from the add. section*/ if(worker->env.cfg->val_clean_additional) deleg_remove_nonsecure_additional(msg->rep); secure = 1; break; case sec_status_indeterminate: case sec_status_insecure: default: /* not secure */ secure = 0; break; } } /* return this delegation from the cache */ edns->edns_version = EDNS_ADVERTISED_VERSION; edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; if(!edns_opt_inplace_reply(edns, worker->scratchpad)) return 0; msg->rep->flags |= BIT_QR|BIT_RA; if(!reply_info_answer_encode(&msg->qinfo, msg->rep, id, flags, repinfo->c->buffer, 0, 1, worker->scratchpad, udpsize, edns, (int)(edns->bits & EDNS_DO), secure)) { error_encode(repinfo->c->buffer, LDNS_RCODE_SERVFAIL, &msg->qinfo, id, flags, edns); } if(worker->stats.extended) { if(secure) worker->stats.ans_secure++; server_stats_insrcode(&worker->stats, repinfo->c->buffer); } return 1; }