/** convert and print rdata */ static void print_rd(int t, char* data, size_t len) { size_t i, pos = 0; uint8_t* rd = (uint8_t*)malloc(len+2); ldns_rr* rr = ldns_rr_new(); ldns_status status; if(!rd || !rr) { fprintf(stderr, "out of memory"); exit(1); } ldns_rr_set_type(rr, t); ldns_write_uint16(rd, len); memmove(rd+2, data, len); ldns_rr_set_owner(rr, NULL); status = ldns_wire2rdf(rr, rd, len+2, &pos); if(status != LDNS_STATUS_OK) { free(rd); ldns_rr_free(rr); printf("error_printing_data"); return; } for(i=0; i<ldns_rr_rd_count(rr); i++) { printf(" "); ldns_rdf_print(stdout, ldns_rr_rdf(rr, i)); } ldns_rr_free(rr); free(rd); }
/** synthesize a CNAME rrset */ static struct rrset_parse* synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, size_t aliaslen, struct regional* region, struct msg_parse* msg, struct rrset_parse* rrset, struct rrset_parse* prev, struct rrset_parse* nx, ldns_buffer* pkt) { struct rrset_parse* cn = (struct rrset_parse*)regional_alloc(region, sizeof(struct rrset_parse)); if(!cn) return NULL; memset(cn, 0, sizeof(*cn)); cn->rr_first = (struct rr_parse*)regional_alloc(region, sizeof(struct rr_parse)); if(!cn->rr_first) return NULL; cn->rr_last = cn->rr_first; /* CNAME from sname to alias */ cn->dname = (uint8_t*)regional_alloc(region, *snamelen); if(!cn->dname) return NULL; dname_pkt_copy(pkt, cn->dname, *sname); cn->dname_len = *snamelen; cn->type = LDNS_RR_TYPE_CNAME; cn->section = rrset->section; cn->rrset_class = rrset->rrset_class; cn->rr_count = 1; cn->size = sizeof(uint16_t) + aliaslen; cn->hash=pkt_hash_rrset(pkt, cn->dname, cn->type, cn->rrset_class, 0); /* allocate TTL + rdatalen + uncompressed dname */ memset(cn->rr_first, 0, sizeof(struct rr_parse)); cn->rr_first->outside_packet = 1; cn->rr_first->ttl_data = (uint8_t*)regional_alloc(region, sizeof(uint32_t)+sizeof(uint16_t)+aliaslen); if(!cn->rr_first->ttl_data) return NULL; ldns_write_uint32(cn->rr_first->ttl_data, 0); /* TTL = 0 */ ldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen); memmove(cn->rr_first->ttl_data+6, alias, aliaslen); cn->rr_first->size = sizeof(uint16_t)+aliaslen; /* link it in */ cn->rrset_all_next = nx; if(prev) prev->rrset_all_next = cn; else msg->rrset_first = cn; if(nx == NULL) msg->rrset_last = cn; msg->rrset_count ++; msg->an_rrsets++; /* it is not inserted in the msg hashtable. */ *sname = cn->rr_first->ttl_data + sizeof(uint32_t)+sizeof(uint16_t); *snamelen = aliaslen; return cn; }
ssize_t ldns_tcp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen) { uint8_t *sendbuf; ssize_t bytes; /* add length of packet */ sendbuf = LDNS_XMALLOC(uint8_t, ldns_buffer_position(qbin) + 2); if(!sendbuf) return 0; ldns_write_uint16(sendbuf, ldns_buffer_position(qbin)); memcpy(sendbuf + 2, ldns_buffer_begin(qbin), ldns_buffer_position(qbin)); bytes = sendto(sockfd, (void*)sendbuf, ldns_buffer_position(qbin) + 2, 0, (struct sockaddr *)to, tolen); LDNS_FREE(sendbuf); if (bytes == -1 || (size_t) bytes != ldns_buffer_position(qbin) + 2 ) { return 0; } return bytes; }
/* * 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; }
static ldns_status ldns_tsig_mac_new(ldns_rdf **tsig_mac, uint8_t *pkt_wire, size_t pkt_wire_size, const char *key_data, ldns_rdf *key_name_rdf, ldns_rdf *fudge_rdf, ldns_rdf *algorithm_rdf, ldns_rdf *time_signed_rdf, ldns_rdf *error_rdf, ldns_rdf *other_data_rdf, ldns_rdf *orig_mac_rdf, int tsig_timers_only) { ldns_status status; char *wireformat; int wiresize; unsigned char *mac_bytes = NULL; unsigned char *key_bytes = NULL; int key_size; const EVP_MD *digester; char *algorithm_name = NULL; unsigned int md_len = EVP_MAX_MD_SIZE; ldns_rdf *result = NULL; ldns_buffer *data_buffer = NULL; ldns_rdf *canonical_key_name_rdf = NULL; ldns_rdf *canonical_algorithm_rdf = NULL; if (key_name_rdf == NULL || algorithm_rdf == NULL) { return LDNS_STATUS_NULL; } canonical_key_name_rdf = ldns_rdf_clone(key_name_rdf); if (canonical_key_name_rdf == NULL) { return LDNS_STATUS_MEM_ERR; } canonical_algorithm_rdf = ldns_rdf_clone(algorithm_rdf); if (canonical_algorithm_rdf == NULL) { ldns_rdf_deep_free(canonical_key_name_rdf); return LDNS_STATUS_MEM_ERR; } /* * prepare the digestable information */ data_buffer = ldns_buffer_new(LDNS_MAX_PACKETLEN); if (!data_buffer) { status = LDNS_STATUS_MEM_ERR; goto clean; } /* if orig_mac is not NULL, add it too */ if (orig_mac_rdf) { (void) ldns_rdf2buffer_wire(data_buffer, orig_mac_rdf); } ldns_buffer_write(data_buffer, pkt_wire, pkt_wire_size); if (!tsig_timers_only) { ldns_dname2canonical(canonical_key_name_rdf); (void)ldns_rdf2buffer_wire(data_buffer, canonical_key_name_rdf); ldns_buffer_write_u16(data_buffer, LDNS_RR_CLASS_ANY); ldns_buffer_write_u32(data_buffer, 0); ldns_dname2canonical(canonical_algorithm_rdf); (void)ldns_rdf2buffer_wire(data_buffer, canonical_algorithm_rdf); } (void)ldns_rdf2buffer_wire(data_buffer, time_signed_rdf); (void)ldns_rdf2buffer_wire(data_buffer, fudge_rdf); if (!tsig_timers_only) { (void)ldns_rdf2buffer_wire(data_buffer, error_rdf); (void)ldns_rdf2buffer_wire(data_buffer, other_data_rdf); } wireformat = (char *) data_buffer->_data; wiresize = (int) ldns_buffer_position(data_buffer); algorithm_name = ldns_rdf2str(algorithm_rdf); if(!algorithm_name) { status = LDNS_STATUS_MEM_ERR; goto clean; } /* prepare the key */ key_bytes = LDNS_XMALLOC(unsigned char, ldns_b64_pton_calculate_size(strlen(key_data))); if(!key_bytes) { status = LDNS_STATUS_MEM_ERR; goto clean; } key_size = ldns_b64_pton(key_data, key_bytes, ldns_b64_pton_calculate_size(strlen(key_data))); if (key_size < 0) { status = LDNS_STATUS_INVALID_B64; goto clean; } /* hmac it */ /* 2 spare bytes for the length */ mac_bytes = LDNS_XMALLOC(unsigned char, md_len+2); if(!mac_bytes) { status = LDNS_STATUS_MEM_ERR; goto clean; } memset(mac_bytes, 0, md_len+2); digester = ldns_digest_function(algorithm_name); if (digester) { (void) HMAC(digester, key_bytes, key_size, (void *)wireformat, (size_t) wiresize, mac_bytes + 2, &md_len); ldns_write_uint16(mac_bytes, md_len); result = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_INT16_DATA, md_len + 2, mac_bytes); } else { status = LDNS_STATUS_CRYPTO_UNKNOWN_ALGO; goto clean; } *tsig_mac = result; status = LDNS_STATUS_OK; clean: LDNS_FREE(mac_bytes); LDNS_FREE(key_bytes); LDNS_FREE(algorithm_name); ldns_buffer_free(data_buffer); ldns_rdf_deep_free(canonical_algorithm_rdf); ldns_rdf_deep_free(canonical_key_name_rdf); return status; }
ldns_status ldns_pkt2buffer_wire(ldns_buffer *buffer, const ldns_pkt *packet) { ldns_rr_list *rr_list; uint16_t i; /* edns tmp vars */ ldns_rr *edns_rr; uint8_t edata[4]; (void) ldns_hdr2buffer_wire(buffer, packet); rr_list = ldns_pkt_question(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { (void) ldns_rr2buffer_wire(buffer, ldns_rr_list_rr(rr_list, i), LDNS_SECTION_QUESTION); } } rr_list = ldns_pkt_answer(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { (void) ldns_rr2buffer_wire(buffer, ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ANSWER); } } rr_list = ldns_pkt_authority(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { (void) ldns_rr2buffer_wire(buffer, ldns_rr_list_rr(rr_list, i), LDNS_SECTION_AUTHORITY); } } rr_list = ldns_pkt_additional(packet); if (rr_list) { for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) { (void) ldns_rr2buffer_wire(buffer, ldns_rr_list_rr(rr_list, i), LDNS_SECTION_ADDITIONAL); } } /* add EDNS to additional if it is needed */ if (ldns_pkt_edns(packet)) { edns_rr = ldns_rr_new(); ldns_rr_set_owner(edns_rr, ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, ".")); ldns_rr_set_type(edns_rr, LDNS_RR_TYPE_OPT); ldns_rr_set_class(edns_rr, ldns_pkt_edns_udp_size(packet)); edata[0] = ldns_pkt_edns_extended_rcode(packet); edata[1] = ldns_pkt_edns_version(packet); ldns_write_uint16(&edata[2], ldns_pkt_edns_z(packet)); ldns_rr_set_ttl(edns_rr, ldns_read_uint32(edata)); (void)ldns_rr2buffer_wire(buffer, edns_rr, LDNS_SECTION_ADDITIONAL); ldns_rr_free(edns_rr); } /* add TSIG to additional if it is there */ if (ldns_pkt_tsig(packet)) { (void) ldns_rr2buffer_wire(buffer, ldns_pkt_tsig(packet), LDNS_SECTION_ADDITIONAL); } return LDNS_STATUS_OK; }
/** synthesize DNAME+CNAME response from cached DNAME item */ static struct dns_msg* synth_dname_msg(struct ub_packed_rrset_key* rrset, struct regional* region, uint32_t now, struct query_info* q) { struct dns_msg* msg; struct ub_packed_rrset_key* ck; struct packed_rrset_data* newd, *d = (struct packed_rrset_data*) rrset->entry.data; uint8_t* newname, *dtarg = NULL; size_t newlen, dtarglen; if(now > d->ttl) return NULL; /* only allow validated (with DNSSEC) DNAMEs used from cache * for insecure DNAMEs, query again. */ if(d->security != sec_status_secure) return NULL; msg = gen_dns_msg(region, q, 2); /* DNAME + CNAME RRset */ if(!msg) return NULL; msg->rep->flags = BIT_QR; /* reply, no AA, no error */ msg->rep->authoritative = 0; /* reply stored in cache can't be authoritative */ msg->rep->qdcount = 1; msg->rep->ttl = d->ttl - now; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(msg->rep->ttl); msg->rep->security = sec_status_unchecked; msg->rep->an_numrrsets = 1; msg->rep->ns_numrrsets = 0; msg->rep->ar_numrrsets = 0; msg->rep->rrset_count = 1; msg->rep->rrsets[0] = packed_rrset_copy_region(rrset, region, now); if(!msg->rep->rrsets[0]) /* copy DNAME */ return NULL; /* synth CNAME rrset */ get_cname_target(rrset, &dtarg, &dtarglen); if(!dtarg) return NULL; newlen = q->qname_len + dtarglen - rrset->rk.dname_len; if(newlen > LDNS_MAX_DOMAINLEN) { msg->rep->flags |= LDNS_RCODE_YXDOMAIN; return msg; } newname = (uint8_t*)regional_alloc(region, newlen); if(!newname) return NULL; /* new name is concatenation of qname front (without DNAME owner) * and DNAME target name */ memcpy(newname, q->qname, q->qname_len-rrset->rk.dname_len); memmove(newname+(q->qname_len-rrset->rk.dname_len), dtarg, dtarglen); /* create rest of CNAME rrset */ ck = (struct ub_packed_rrset_key*)regional_alloc(region, sizeof(struct ub_packed_rrset_key)); if(!ck) return NULL; memset(&ck->entry, 0, sizeof(ck->entry)); msg->rep->rrsets[1] = ck; ck->entry.key = ck; ck->rk.type = htons(LDNS_RR_TYPE_CNAME); ck->rk.rrset_class = rrset->rk.rrset_class; ck->rk.flags = 0; ck->rk.dname = regional_alloc_init(region, q->qname, q->qname_len); if(!ck->rk.dname) return NULL; ck->rk.dname_len = q->qname_len; ck->entry.hash = rrset_key_hash(&ck->rk); newd = (struct packed_rrset_data*)regional_alloc_zero(region, sizeof(struct packed_rrset_data) + sizeof(size_t) + sizeof(uint8_t*) + sizeof(uint32_t) + sizeof(uint16_t) + newlen); if(!newd) return NULL; ck->entry.data = newd; newd->ttl = 0; /* 0 for synthesized CNAME TTL */ newd->count = 1; newd->rrsig_count = 0; newd->trust = rrset_trust_ans_noAA; newd->rr_len = (size_t*)((uint8_t*)newd + sizeof(struct packed_rrset_data)); newd->rr_len[0] = newlen + sizeof(uint16_t); packed_rrset_ptr_fixup(newd); newd->rr_ttl[0] = newd->ttl; msg->rep->ttl = newd->ttl; msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(newd->ttl); ldns_write_uint16(newd->rr_data[0], newlen); memmove(newd->rr_data[0] + sizeof(uint16_t), newname, newlen); msg->rep->an_numrrsets ++; msg->rep->rrset_count ++; return msg; }
/* * Parses data buffer to a query, finds the correct answer * and calls the given function for every packet to send. */ void handle_query(uint8_t* inbuf, ssize_t inlen, struct entry* entries, int* count, enum transport_type transport, void (*sendfunc)(uint8_t*, size_t, void*), void* userdata, FILE* verbose_out) { ldns_status status; ldns_pkt *query_pkt = NULL; ldns_pkt *answer_pkt = NULL; struct reply_packet *p; ldns_rr *query_rr = NULL; uint8_t *outbuf = NULL; size_t answer_size = 0; struct entry* entry = NULL; ldns_rdf *stop_command = ldns_dname_new_frm_str("server.stop."); status = ldns_wire2pkt(&query_pkt, inbuf, (size_t)inlen); if (status != LDNS_STATUS_OK) { verbose(1, "Got bad packet: %s\n", ldns_get_errorstr_by_id(status)); ldns_rdf_free(stop_command); return; } query_rr = ldns_rr_list_rr(ldns_pkt_question(query_pkt), 0); verbose(1, "query %d: id %d: %s %d bytes: ", ++(*count), (int)ldns_pkt_id(query_pkt), (transport==transport_tcp)?"TCP":"UDP", (int)inlen); if(verbose_out) ldns_rr_print(verbose_out, query_rr); if(verbose_out) ldns_pkt_print(verbose_out, query_pkt); if (ldns_rr_get_type(query_rr) == LDNS_RR_TYPE_TXT && ldns_rr_get_class(query_rr) == LDNS_RR_CLASS_CH && ldns_dname_compare(ldns_rr_owner(query_rr), stop_command) == 0) { exit(0); } /* fill up answer packet */ entry = find_match(entries, query_pkt, transport); if(!entry || !entry->reply_list) { verbose(1, "no answer packet for this query, no reply.\n"); ldns_pkt_free(query_pkt); ldns_rdf_free(stop_command); return; } for(p = entry->reply_list; p; p = p->next) { verbose(3, "Answer pkt:\n"); if (p->reply_from_hex) { /* try to parse the hex packet, if it can be * parsed, we can use adjust rules. if not, * send packet literally */ status = ldns_buffer2pkt_wire(&answer_pkt, p->reply_from_hex); if (status == LDNS_STATUS_OK) { adjust_packet(entry, answer_pkt, query_pkt); if(verbose_out) ldns_pkt_print(verbose_out, answer_pkt); status = ldns_pkt2wire(&outbuf, answer_pkt, &answer_size); verbose(2, "Answer packet size: %u bytes.\n", (unsigned int)answer_size); if (status != LDNS_STATUS_OK) { verbose(1, "Error creating answer: %s\n", ldns_get_errorstr_by_id(status)); ldns_pkt_free(query_pkt); ldns_rdf_free(stop_command); return; } ldns_pkt_free(answer_pkt); answer_pkt = NULL; } else { verbose(3, "Could not parse hex data (%s), sending hex data directly.\n", ldns_get_errorstr_by_id(status)); /* still try to adjust ID */ answer_size = ldns_buffer_capacity(p->reply_from_hex); outbuf = LDNS_XMALLOC(uint8_t, answer_size); memcpy(outbuf, ldns_buffer_export(p->reply_from_hex), answer_size); if(entry->copy_id) { ldns_write_uint16(outbuf, ldns_pkt_id(query_pkt)); } } } else { answer_pkt = ldns_pkt_clone(p->reply); adjust_packet(entry, answer_pkt, query_pkt); if(verbose_out) ldns_pkt_print(verbose_out, answer_pkt); status = ldns_pkt2wire(&outbuf, answer_pkt, &answer_size); verbose(1, "Answer packet size: %u bytes.\n", (unsigned int)answer_size); if (status != LDNS_STATUS_OK) { verbose(1, "Error creating answer: %s\n", ldns_get_errorstr_by_id(status)); ldns_pkt_free(query_pkt); ldns_rdf_free(stop_command); return; } ldns_pkt_free(answer_pkt); answer_pkt = NULL; } if(p->packet_sleep) { verbose(3, "sleeping for next packet %d secs\n", p->packet_sleep); #ifdef HAVE_SLEEP sleep(p->packet_sleep); #else Sleep(p->packet_sleep * 1000); #endif verbose(3, "wakeup for next packet " "(slept %d secs)\n", p->packet_sleep); } sendfunc(outbuf, answer_size, userdata); LDNS_FREE(outbuf); outbuf = NULL; answer_size = 0; } ldns_pkt_free(query_pkt); ldns_rdf_free(stop_command); }