ldns_status ldns_rr2buffer_wire(ldns_buffer *buffer, const ldns_rr *rr, int section) { uint16_t i; uint16_t rdl_pos = 0; if (ldns_rr_owner(rr)) { (void) ldns_dname2buffer_wire(buffer, ldns_rr_owner(rr)); } if (ldns_buffer_reserve(buffer, 4)) { (void) ldns_buffer_write_u16(buffer, ldns_rr_get_type(rr)); (void) ldns_buffer_write_u16(buffer, ldns_rr_get_class(rr)); } if (section != LDNS_SECTION_QUESTION) { if (ldns_buffer_reserve(buffer, 6)) { ldns_buffer_write_u32(buffer, ldns_rr_ttl(rr)); /* remember pos for later */ rdl_pos = ldns_buffer_position(buffer); ldns_buffer_write_u16(buffer, 0); } for (i = 0; i < ldns_rr_rd_count(rr); i++) { (void) ldns_rdf2buffer_wire(buffer, ldns_rr_rdf(rr, i)); } if (rdl_pos != 0) { ldns_buffer_write_u16_at(buffer, rdl_pos, ldns_buffer_position(buffer) - rdl_pos - 2); } } return ldns_buffer_status(buffer); }
/** compress domain names in rdata, return RETVAL_* */ static int compress_rdata(ldns_buffer* pkt, uint8_t* rdata, size_t todolen, struct regional* region, struct compress_tree_node** tree, const ldns_rr_descriptor* desc) { int labs, r, rdf = 0; size_t dname_len, len, pos = ldns_buffer_position(pkt); uint8_t count = desc->_dname_count; ldns_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(ldns_buffer_remaining(pkt) < len) return RETVAL_TRUNC; ldns_buffer_write(pkt, rdata, len); todolen -= len; rdata += len; } rdf++; } /* copy remainder */ if(todolen > 0) { if(ldns_buffer_remaining(pkt) < todolen) return RETVAL_TRUNC; ldns_buffer_write(pkt, rdata, todolen); } /* set rdata len */ ldns_buffer_write_u16_at(pkt, pos, ldns_buffer_position(pkt)-pos-2); return RETVAL_OK; }
/** analyze domain name in packet, possibly compressed */ static void analyze_dname(ldns_buffer* pkt) { size_t oldpos = ldns_buffer_position(pkt); size_t len; printf("[pos %d] dname: ", (int)oldpos); dname_print(stdout, pkt, ldns_buffer_current(pkt)); len = pkt_dname_len(pkt); printf(" len=%d", (int)len); if(ldns_buffer_position(pkt)-oldpos != len) printf(" comprlen=%d\n", (int)(ldns_buffer_position(pkt)-oldpos)); else printf("\n"); }
/** store msg section in wireformat buffer, return RETVAL_* */ static int insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs, ldns_buffer* pkt, size_t rrsets_before, uint32_t timenow, struct regional* region, struct compress_tree_node** tree, ldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) { int r; size_t i, setstart; *num_rrs = 0; if(s != LDNS_SECTION_ADDITIONAL) { if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY) dnssec = 1; /* include all types in ANY answer */ for(i=0; i<num_rrsets; i++) { setstart = ldns_buffer_position(pkt); if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], pkt, num_rrs, timenow, region, 1, 1, tree, s, qtype, dnssec, rr_offset)) != RETVAL_OK) { /* Bad, but if due to size must set TC bit */ /* trim off the rrset neatly. */ ldns_buffer_set_position(pkt, setstart); return r; } } } else { for(i=0; i<num_rrsets; i++) { setstart = ldns_buffer_position(pkt); if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], pkt, num_rrs, timenow, region, 1, 0, tree, s, qtype, dnssec, rr_offset)) != RETVAL_OK) { ldns_buffer_set_position(pkt, setstart); return r; } } if(dnssec) for(i=0; i<num_rrsets; i++) { setstart = ldns_buffer_position(pkt); if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i], pkt, num_rrs, timenow, region, 0, 1, tree, s, qtype, dnssec, rr_offset)) != RETVAL_OK) { ldns_buffer_set_position(pkt, setstart); return r; } } } return RETVAL_OK; }
ldns_rdf * ldns_sign_public_evp(ldns_buffer *to_sign, EVP_PKEY *key, const EVP_MD *digest_type) { unsigned int siglen; ldns_rdf *sigdata_rdf; ldns_buffer *b64sig; EVP_MD_CTX ctx; const EVP_MD *md_type; int r; siglen = 0; b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN); if (!b64sig) { return NULL; } /* initializes a signing context */ md_type = digest_type; if(!md_type) { /* unknown message difest */ ldns_buffer_free(b64sig); return NULL; } EVP_MD_CTX_init(&ctx); r = EVP_SignInit(&ctx, md_type); if(r == 1) { r = EVP_SignUpdate(&ctx, (unsigned char*) ldns_buffer_begin(to_sign), ldns_buffer_position(to_sign)); } else { ldns_buffer_free(b64sig); return NULL; } if(r == 1) { r = EVP_SignFinal(&ctx, (unsigned char*) ldns_buffer_begin(b64sig), &siglen, key); } else { ldns_buffer_free(b64sig); return NULL; } if(r != 1) { ldns_buffer_free(b64sig); return NULL; } /* unfortunately, OpenSSL output is differenct from DNS DSA format */ if (EVP_PKEY_type(key->type) == EVP_PKEY_DSA) { sigdata_rdf = ldns_convert_dsa_rrsig_asn12rdf(b64sig, siglen); } else { /* ok output for other types is the same */ sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen, ldns_buffer_begin(b64sig)); } ldns_buffer_free(b64sig); EVP_MD_CTX_cleanup(&ctx); return sigdata_rdf; }
ldns_rdf * ldns_sign_public_rsamd5(ldns_buffer *to_sign, RSA *key) { unsigned char *md5_hash; unsigned int siglen; ldns_rdf *sigdata_rdf; ldns_buffer *b64sig; b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN); if (!b64sig) { return NULL; } md5_hash = MD5((unsigned char*)ldns_buffer_begin(to_sign), ldns_buffer_position(to_sign), NULL); if (!md5_hash) { ldns_buffer_free(b64sig); return NULL; } RSA_sign(NID_md5, md5_hash, MD5_DIGEST_LENGTH, (unsigned char*)ldns_buffer_begin(b64sig), &siglen, key); sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen, ldns_buffer_begin(b64sig)); ldns_buffer_free(b64sig); return sigdata_rdf; }
ldns_rdf * ldns_sign_public_rsasha1(ldns_buffer *to_sign, RSA *key) { unsigned char *sha1_hash; unsigned int siglen; ldns_rdf *sigdata_rdf; ldns_buffer *b64sig; int result; siglen = 0; b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN); if (!b64sig) { return NULL; } sha1_hash = SHA1((unsigned char*)ldns_buffer_begin(to_sign), ldns_buffer_position(to_sign), NULL); if (!sha1_hash) { ldns_buffer_free(b64sig); return NULL; } result = RSA_sign(NID_sha1, sha1_hash, SHA_DIGEST_LENGTH, (unsigned char*)ldns_buffer_begin(b64sig), &siglen, key); if (result != 1) { ldns_buffer_free(b64sig); return NULL; } sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, siglen, ldns_buffer_begin(b64sig)); ldns_buffer_free(b64sig); /* can't free this buffer ?? */ return sigdata_rdf; }
/** * Sign data with DSA * * \param[in] to_sign The ldns_buffer containing raw data that is * to be signed * \param[in] key The DSA key structure to sign with * \return ldns_rdf for the RRSIG ldns_rr */ ldns_rdf * ldns_sign_public_dsa(ldns_buffer *to_sign, DSA *key) { unsigned char *sha1_hash; ldns_rdf *sigdata_rdf; ldns_buffer *b64sig; DSA_SIG *sig; uint8_t *data; size_t pad; b64sig = ldns_buffer_new(LDNS_MAX_PACKETLEN); if (!b64sig) { return NULL; } sha1_hash = SHA1((unsigned char*)ldns_buffer_begin(to_sign), ldns_buffer_position(to_sign), NULL); if (!sha1_hash) { ldns_buffer_free(b64sig); return NULL; } sig = DSA_do_sign(sha1_hash, SHA_DIGEST_LENGTH, key); if(!sig) { ldns_buffer_free(b64sig); return NULL; } data = LDNS_XMALLOC(uint8_t, 1 + 2 * SHA_DIGEST_LENGTH); if(!data) { ldns_buffer_free(b64sig); DSA_SIG_free(sig); return NULL; } data[0] = 1; pad = 20 - (size_t) BN_num_bytes(sig->r); if (pad > 0) { memset(data + 1, 0, pad); } BN_bn2bin(sig->r, (unsigned char *) (data + 1) + pad); pad = 20 - (size_t) BN_num_bytes(sig->s); if (pad > 0) { memset(data + 1 + SHA_DIGEST_LENGTH, 0, pad); } BN_bn2bin(sig->s, (unsigned char *) (data + 1 + SHA_DIGEST_LENGTH + pad)); sigdata_rdf = ldns_rdf_new_frm_data(LDNS_RDF_TYPE_B64, 1 + 2 * SHA_DIGEST_LENGTH, data); ldns_buffer_free(b64sig); LDNS_FREE(data); DSA_SIG_free(sig); return sigdata_rdf; }
/* don't wait for an answer */ ssize_t ldns_udp_send_query(ldns_buffer *qbin, int sockfd, const struct sockaddr_storage *to, socklen_t tolen) { ssize_t bytes; bytes = sendto(sockfd, (void*)ldns_buffer_begin(qbin), ldns_buffer_position(qbin), 0, (struct sockaddr *)to, tolen); if (bytes == -1 || (size_t)bytes != ldns_buffer_position(qbin)) { return 0; } if ((size_t) bytes != ldns_buffer_position(qbin)) { return 0; } return bytes; }
void ldns_bskipc(ldns_buffer *buffer, char c) { while (c == (char) ldns_buffer_read_u8_at(buffer, ldns_buffer_position(buffer))) { if (ldns_buffer_available_at(buffer, buffer->_position + sizeof(char), sizeof(char))) { buffer->_position += sizeof(char); } else { return; } } }
static int l_buf_info(lua_State *L) { ldns_buffer *b = (ldns_buffer *)lua_touserdata(L, 1); if (!b) { return 0; } printf("capacity %d; position %d; limit %d\n", ldns_buffer_capacity(b), ldns_buffer_position(b), ldns_buffer_limit(b)); return 0; }
/** analyze rdata in packet */ static void analyze_rdata(ldns_buffer*pkt, const ldns_rr_descriptor* desc, uint16_t rdlen) { int rdf = 0; int count = (int)desc->_dname_count; size_t len, oldpos; while(rdlen > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: oldpos = ldns_buffer_position(pkt); analyze_dname(pkt); rdlen -= ldns_buffer_position(pkt)-oldpos; count --; len = 0; break; case LDNS_RDF_TYPE_STR: len = ldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); } if(len) { printf(" wf[%d]", (int)len); ldns_buffer_skip(pkt, (ssize_t)len); rdlen -= len; } rdf++; } if(rdlen) { size_t i; printf(" remain[%d]\n", (int)rdlen); for(i=0; i<rdlen; i++) printf(" %2.2X", (unsigned)ldns_buffer_current(pkt)[i]); printf("\n"); } else printf("\n"); ldns_buffer_skip(pkt, (ssize_t)rdlen); }
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; }
size_t pkt_dname_len(ldns_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(ldns_buffer_remaining(pkt) < 1) return 0; labellen = ldns_buffer_read_u8(pkt); if(LABEL_IS_PTR(labellen)) { /* compression ptr */ uint16_t ptr; if(ldns_buffer_remaining(pkt) < 1) return 0; ptr = PTR_OFFSET(labellen, ldns_buffer_read_u8(pkt)); if(ptrcount++ > MAX_COMPRESS_PTRS) return 0; /* loop! */ if(ldns_buffer_limit(pkt) <= ptr) return 0; /* out of bounds! */ if(!endpos) endpos = ldns_buffer_position(pkt); ldns_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(ldns_buffer_remaining(pkt) < labellen) return 0; ldns_buffer_skip(pkt, (ssize_t)labellen); } } if(endpos) ldns_buffer_set_position(pkt, endpos); return len; }
/** compress any domain name to the packet, return RETVAL_* */ static int compress_any_dname(uint8_t* dname, ldns_buffer* pkt, int labs, struct regional* region, struct compress_tree_node** tree) { struct compress_tree_node* p; struct compress_tree_node** insertpt = NULL; size_t pos = ldns_buffer_position(pkt); if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) { if(!write_compressed_dname(pkt, dname, labs, p)) return RETVAL_TRUNC; } else { if(!dname_buffer_write(pkt, dname)) return RETVAL_TRUNC; } if(!compress_tree_store(dname, labs, pos, region, p, insertpt)) return RETVAL_OUTMEM; return RETVAL_OK; }
/** store query section in wireformat buffer, return RETVAL */ static int insert_query(struct query_info* qinfo, struct compress_tree_node** tree, ldns_buffer* buffer, struct regional* region) { if(ldns_buffer_remaining(buffer) < qinfo->qname_len+sizeof(uint16_t)*2) return RETVAL_TRUNC; /* buffer too small */ /* the query is the first name inserted into the tree */ if(!compress_tree_store(qinfo->qname, dname_count_labels(qinfo->qname), ldns_buffer_position(buffer), region, NULL, tree)) return RETVAL_OUTMEM; if(ldns_buffer_current(buffer) == qinfo->qname) ldns_buffer_skip(buffer, (ssize_t)qinfo->qname_len); else ldns_buffer_write(buffer, qinfo->qname, qinfo->qname_len); ldns_buffer_write_u16(buffer, qinfo->qtype); ldns_buffer_write_u16(buffer, qinfo->qclass); return RETVAL_OK; }
int query_info_parse(struct query_info* m, ldns_buffer* query) { uint8_t* q = ldns_buffer_begin(query); /* minimum size: header + \0 + qtype + qclass */ if(ldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) return 0; if(LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY || LDNS_QDCOUNT(q) != 1 || ldns_buffer_position(query) != 0) return 0; ldns_buffer_skip(query, LDNS_HEADER_SIZE); m->qname = ldns_buffer_current(query); if((m->qname_len = query_dname_len(query)) == 0) return 0; /* parse error */ if(ldns_buffer_remaining(query) < 4) return 0; /* need qtype, qclass */ m->qtype = ldns_buffer_read_u16(query); m->qclass = ldns_buffer_read_u16(query); return 1; }
/** get additional name from rrset RR, return false if no name present */ static int get_additional_name(struct rrset_parse* rrset, struct rr_parse* rr, uint8_t** nm, size_t* nmlen, ldns_buffer* pkt) { size_t offset = 0; size_t len, oldpos; switch(rrset->type) { case LDNS_RR_TYPE_MB: case LDNS_RR_TYPE_MD: case LDNS_RR_TYPE_MF: case LDNS_RR_TYPE_NS: offset = 0; break; case LDNS_RR_TYPE_MX: case LDNS_RR_TYPE_KX: offset = 2; break; case LDNS_RR_TYPE_SRV: offset = 6; break; case LDNS_RR_TYPE_NAPTR: /* TODO: NAPTR not supported, glue stripped off */ return 0; default: return 0; } len = ldns_read_uint16(rr->ttl_data+sizeof(uint32_t)); if(len < offset+1) return 0; /* rdata field too small */ *nm = rr->ttl_data+sizeof(uint32_t)+sizeof(uint16_t)+offset; oldpos = ldns_buffer_position(pkt); ldns_buffer_set_position(pkt, (size_t)(*nm - ldns_buffer_begin(pkt))); *nmlen = pkt_dname_len(pkt); ldns_buffer_set_position(pkt, oldpos); if(*nmlen == 0) return 0; return 1; }
/** * read contents of trusted-keys{ ... ; clauses and insert keys into storage. * @param anchors: where to store keys * @param buf: buffer to use * @param line: line number in file * @param in: file to read from. * @return 0 on error. */ static int process_bind_contents(struct val_anchors* anchors, ldns_buffer* buf, int* line, FILE* in) { /* loop over contents, collate strings before ; */ /* contents is (numbered): 0 1 2 3 4 5 6 7 8 */ /* name. 257 3 5 base64 base64 */ /* quoted value: 0 "111" 0 0 0 0 0 0 0 */ /* comments value: 1 "000" 1 1 1 "0 0 0 0" 1 */ int contnum = 0; int quoted = 0; int comments = 1; int rdlen; char* str = 0; ldns_buffer_clear(buf); while((rdlen=readkeyword_bindfile(in, buf, line, comments))) { if(rdlen == 1 && ldns_buffer_position(buf) == 1 && isspace((int)*ldns_buffer_begin(buf))) { /* starting whitespace is removed */ ldns_buffer_clear(buf); continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '"') { /* remove " from the string */ if(contnum == 0) { quoted = 1; comments = 0; } ldns_buffer_skip(buf, -1); if(contnum > 0 && quoted) { if(ldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } ldns_buffer_write(buf, " DNSKEY ", 8); quoted = 0; comments = 1; } else if(contnum > 0) comments = !comments; continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == ';') { if(contnum < 5) { ldns_buffer_write_u8(buf, 0); log_err("line %d, bad key", *line); return 0; } ldns_buffer_skip(buf, -1); ldns_buffer_write_u8(buf, 0); str = strdup((char*)ldns_buffer_begin(buf)); if(!str) { log_err("line %d, allocation failure", *line); return 0; } if(!anchor_store_str(anchors, buf, str)) { log_err("line %d, bad key", *line); free(str); return 0; } free(str); ldns_buffer_clear(buf); contnum = 0; quoted = 0; comments = 1; continue; } else if(rdlen == 1 && ldns_buffer_current(buf)[-1] == '}') { if(contnum > 0) { ldns_buffer_write_u8(buf, 0); log_err("line %d, bad key before }", *line); return 0; } return 1; } else if(rdlen == 1 && isspace((int)ldns_buffer_current(buf)[-1])) { /* leave whitespace here */ } else { /* not space or whatnot, so actual content */ contnum ++; if(contnum == 1 && !quoted) { if(ldns_buffer_remaining(buf) < 8+1) { log_err("line %d, too long", *line); return 0; } ldns_buffer_write(buf, " DNSKEY ", 8); } } } log_err("line %d, EOF before }", *line); return 0; }
/** * Read a keyword skipping bind comments; spaces, specials, restkeywords. * The file is split into the following tokens: * * special characters, on their own, rdlen=1, { } doublequote ; * * whitespace becomes a single ' ' or tab. Newlines become spaces. * * other words ('keywords') * * comments are skipped if desired * / / C++ style comment to end of line * # to end of line * / * C style comment * / * @param in: file to read from. * @param buf: buffer, what is read is stored after current buffer position. * Space is left in the buffer to write a terminating 0. * @param line: line number is increased per line, for error reports. * @param comments: if 0, comments are not possible and become text. * if 1, comments are skipped entirely. * In BIND files, this is when reading quoted strings, for example * " base 64 text with / / in there " * @return the number of character written to the buffer. * 0 on end of file. */ static int readkeyword_bindfile(FILE* in, ldns_buffer* buf, int* line, int comments) { int c; int numdone = 0; while((c = getc(in)) != EOF ) { if(comments && c == '#') { /* # blabla */ skip_to_eol(in); (*line)++; continue; } else if(comments && c=='/' && numdone>0 && /* /_/ bla*/ ldns_buffer_read_u8_at(buf, ldns_buffer_position(buf)-1) == '/') { ldns_buffer_skip(buf, -1); numdone--; skip_to_eol(in); (*line)++; continue; } else if(comments && c=='*' && numdone>0 && /* /_* bla *_/ */ ldns_buffer_read_u8_at(buf, ldns_buffer_position(buf)-1) == '/') { ldns_buffer_skip(buf, -1); numdone--; /* skip to end of comment */ while(c != EOF && (c=getc(in)) != EOF ) { if(c == '*') { if((c=getc(in)) == '/') break; } if(c == '\n') (*line)++; } continue; } /* not a comment, complete the keyword */ if(numdone > 0) { /* check same type */ if(isspace(c)) { ungetc(c, in); return numdone; } if(is_bind_special(c)) { ungetc(c, in); return numdone; } } if(c == '\n') { c = ' '; (*line)++; } /* space for 1 char + 0 string terminator */ if(ldns_buffer_remaining(buf) < 2) { fatal_exit("trusted-keys, %d, string too long", *line); } ldns_buffer_write_u8(buf, (uint8_t)c); numdone++; if(isspace(c)) { /* collate whitespace into ' ' */ while((c = getc(in)) != EOF ) { if(c == '\n') (*line)++; if(!isspace(c)) { ungetc(c, in); break; } } return numdone; } if(is_bind_special(c)) return numdone; } return numdone; }
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; }
static CborError cbor_ldns_rr_list(CborEncoder *encoder, ldns_rr_list *list, size_t count, int *should_flush) { CborError cbor_err = CborNoError; size_t n; ldns_buffer *dname; char *dname_str; if (!encoder) { return CborErrorInternalError; } if (!list) { return CborErrorInternalError; } if (!count) { return CborErrorInternalError; } if (!should_flush) { return CborErrorInternalError; } for (n = 0; cbor_err == CborNoError && n < count; n++) { CborEncoder cbor_rr; uint8_t *rdata_bytes; ldns_buffer *rdata; ldns_rr *rr = ldns_rr_list_rr(list, n); size_t rd_count; if (!rr) { return CborErrorInternalError; } rd_count = ldns_rr_rd_count(rr); if (!(dname = ldns_buffer_new(512))) { return CborErrorOutOfMemory; } if (ldns_rdf2buffer_str_dname(dname, ldns_rr_owner(rr)) != LDNS_STATUS_OK) { ldns_buffer_free(dname); return CborErrorInternalError; } ldns_buffer_write_u8(dname, 0); if (!(dname_str = ldns_buffer_export(dname))) { ldns_buffer_free(dname); return CborErrorOutOfMemory; } if (cbor_err == CborNoError) cbor_err = append_cbor_map(encoder, &cbor_rr, CborIndefiniteLength, should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "NAME", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, dname_str, should_flush); free(dname_str); ldns_buffer_free(dname); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "CLASS", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor_rr, ldns_rr_get_class(rr), should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "TYPE", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor_rr, ldns_rr_get_type(rr), should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "TTL", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor_rr, ldns_rr_ttl(rr), should_flush); if (rd_count == 1) { if (!(rdata = ldns_buffer_new(64*1024))) { return CborErrorOutOfMemory; } if (ldns_rdf2buffer_wire(rdata, ldns_rr_rdf(rr, 0)) != LDNS_STATUS_OK) { ldns_buffer_free(rdata); return CborErrorInternalError; } if (!(rdata_bytes = ldns_buffer_export(rdata))) { ldns_buffer_free(rdata); return CborErrorOutOfMemory; } if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "RDLENGTH", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&cbor_rr, ldns_buffer_position(rdata), should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "RDATA", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_bytes(&cbor_rr, rdata_bytes, ldns_buffer_position(rdata), should_flush); free(rdata_bytes); ldns_buffer_free(rdata); } else if (rd_count > 1) { size_t n2; CborEncoder rr_set; if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&cbor_rr, "rrSet", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_array(&cbor_rr, &rr_set, CborIndefiniteLength, should_flush); for (n2 = 0; n2 < rd_count; n2++) { if (!(rdata = ldns_buffer_new(64*1024))) { return CborErrorOutOfMemory; } if (ldns_rdf2buffer_wire(rdata, ldns_rr_rdf(rr, n2)) != LDNS_STATUS_OK) { ldns_buffer_free(rdata); return CborErrorInternalError; } if (!(rdata_bytes = ldns_buffer_export(rdata))) { ldns_buffer_free(rdata); return CborErrorOutOfMemory; } if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&rr_set, "RDLENGTH", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_uint(&rr_set, ldns_buffer_position(rdata), should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_text_stringz(&rr_set, "RDATA", should_flush); if (cbor_err == CborNoError) cbor_err = append_cbor_bytes(&rr_set, rdata_bytes, ldns_buffer_position(rdata), should_flush); free(rdata_bytes); ldns_buffer_free(rdata); } if (cbor_err == CborNoError) cbor_err = close_cbor_container(&cbor_rr, &rr_set, should_flush); } if (cbor_err == CborNoError) cbor_err = close_cbor_container(encoder, &cbor_rr, should_flush); } return cbor_err; }
ldns_status ldns_rr2buffer_wire_canonical(ldns_buffer *buffer, const ldns_rr *rr, int section) { uint16_t i; uint16_t rdl_pos = 0; bool pre_rfc3597 = false; switch (ldns_rr_get_type(rr)) { case LDNS_RR_TYPE_NS: case LDNS_RR_TYPE_MD: case LDNS_RR_TYPE_MF: case LDNS_RR_TYPE_CNAME: case LDNS_RR_TYPE_SOA: case LDNS_RR_TYPE_MB: case LDNS_RR_TYPE_MG: case LDNS_RR_TYPE_MR: case LDNS_RR_TYPE_PTR: case LDNS_RR_TYPE_HINFO: case LDNS_RR_TYPE_MINFO: case LDNS_RR_TYPE_MX: case LDNS_RR_TYPE_RP: case LDNS_RR_TYPE_AFSDB: case LDNS_RR_TYPE_RT: case LDNS_RR_TYPE_SIG: case LDNS_RR_TYPE_PX: case LDNS_RR_TYPE_NXT: case LDNS_RR_TYPE_NAPTR: case LDNS_RR_TYPE_KX: case LDNS_RR_TYPE_SRV: case LDNS_RR_TYPE_DNAME: case LDNS_RR_TYPE_A6: pre_rfc3597 = true; break; default: break; } if (ldns_rr_owner(rr)) { (void) ldns_rdf2buffer_wire_canonical(buffer, ldns_rr_owner(rr)); } if (ldns_buffer_reserve(buffer, 4)) { (void) ldns_buffer_write_u16(buffer, ldns_rr_get_type(rr)); (void) ldns_buffer_write_u16(buffer, ldns_rr_get_class(rr)); } if (section != LDNS_SECTION_QUESTION) { if (ldns_buffer_reserve(buffer, 6)) { ldns_buffer_write_u32(buffer, ldns_rr_ttl(rr)); /* remember pos for later */ rdl_pos = ldns_buffer_position(buffer); ldns_buffer_write_u16(buffer, 0); } for (i = 0; i < ldns_rr_rd_count(rr); i++) { if (pre_rfc3597) { (void) ldns_rdf2buffer_wire_canonical(buffer, ldns_rr_rdf(rr, i)); } else { (void) ldns_rdf2buffer_wire(buffer, ldns_rr_rdf(rr, i)); } } if (rdl_pos != 0) { ldns_buffer_write_u16_at(buffer, rdl_pos, ldns_buffer_position(buffer) - rdl_pos - 2); } } return ldns_buffer_status(buffer); }
/** do the rdata copy */ static int rdata_copy(ldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, struct rr_parse* rr, uint32_t* rr_ttl, uint16_t type) { uint16_t pkt_len; const ldns_rr_descriptor* desc; *rr_ttl = ldns_read_uint32(rr->ttl_data); /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */ if(*rr_ttl & 0x80000000U) *rr_ttl = 0; if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; if(*rr_ttl < data->ttl) data->ttl = *rr_ttl; if(rr->outside_packet) { /* uncompressed already, only needs copy */ memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size); return 1; } ldns_buffer_set_position(pkt, (size_t) (rr->ttl_data - ldns_buffer_begin(pkt) + sizeof(uint32_t))); /* insert decompressed size into rdata len stored in memory */ /* -2 because rdatalen bytes are not included. */ pkt_len = htons(rr->size - 2); memmove(to, &pkt_len, sizeof(uint16_t)); to += 2; /* read packet rdata len */ pkt_len = ldns_buffer_read_u16(pkt); if(ldns_buffer_remaining(pkt) < pkt_len) return 0; desc = ldns_rr_descript(type); if(pkt_len > 0 && desc && desc->_dname_count > 0) { int count = (int)desc->_dname_count; int rdf = 0; size_t len; size_t oldpos; /* decompress dnames. */ while(pkt_len > 0 && count) { switch(desc->_wireformat[rdf]) { case LDNS_RDF_TYPE_DNAME: oldpos = ldns_buffer_position(pkt); dname_pkt_copy(pkt, to, ldns_buffer_current(pkt)); to += pkt_dname_len(pkt); pkt_len -= ldns_buffer_position(pkt)-oldpos; count--; len = 0; break; case LDNS_RDF_TYPE_STR: len = ldns_buffer_current(pkt)[0] + 1; break; default: len = get_rdf_size(desc->_wireformat[rdf]); break; } if(len) { memmove(to, ldns_buffer_current(pkt), len); to += len; ldns_buffer_skip(pkt, (ssize_t)len); log_assert(len <= pkt_len); pkt_len -= len; } rdf++; } } /* copy remaining rdata */ if(pkt_len > 0) memmove(to, ldns_buffer_current(pkt), pkt_len); return 1; }
/** store rrset in buffer in wireformat, return RETVAL_* */ static int packed_rrset_encode(struct ub_packed_rrset_key* key, ldns_buffer* pkt, uint16_t* num_rrs, uint32_t timenow, struct regional* region, int do_data, int do_sig, struct compress_tree_node** tree, ldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset) { size_t i, j, owner_pos; int r, owner_labs; uint16_t owner_ptr = 0; struct packed_rrset_data* data = (struct packed_rrset_data*) key->entry.data; /* does this RR type belong in the answer? */ if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec)) return RETVAL_OK; owner_labs = dname_count_labels(key->rk.dname); owner_pos = ldns_buffer_position(pkt); if(do_data) { const ldns_rr_descriptor* c = type_rdata_compressable(key); for(i=0; i<data->count; i++) { /* rrset roundrobin */ j = (i + rr_offset) % data->count; if((r=compress_owner(key, pkt, region, tree, owner_pos, &owner_ptr, owner_labs)) != RETVAL_OK) return r; ldns_buffer_write(pkt, &key->rk.type, 2); ldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[j] < timenow) ldns_buffer_write_u32(pkt, 0); else ldns_buffer_write_u32(pkt, data->rr_ttl[j]-timenow); if(c) { if((r=compress_rdata(pkt, data->rr_data[j], data->rr_len[j], region, tree, c)) != RETVAL_OK) return r; } else { if(ldns_buffer_remaining(pkt) < data->rr_len[j]) return RETVAL_TRUNC; ldns_buffer_write(pkt, data->rr_data[j], data->rr_len[j]); } } } /* insert rrsigs */ if(do_sig && dnssec) { size_t total = data->count+data->rrsig_count; for(i=data->count; i<total; i++) { if(owner_ptr && owner_labs != 1) { if(ldns_buffer_remaining(pkt) < 2+4+4+data->rr_len[i]) return RETVAL_TRUNC; ldns_buffer_write(pkt, &owner_ptr, 2); } else { if((r=compress_any_dname(key->rk.dname, pkt, owner_labs, region, tree)) != RETVAL_OK) return r; if(ldns_buffer_remaining(pkt) < 4+4+data->rr_len[i]) return RETVAL_TRUNC; } ldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG); ldns_buffer_write(pkt, &key->rk.rrset_class, 2); if(data->rr_ttl[i] < timenow) ldns_buffer_write_u32(pkt, 0); else ldns_buffer_write_u32(pkt, data->rr_ttl[i]-timenow); /* rrsig rdata cannot be compressed, perform 100+ byte * memcopy. */ ldns_buffer_write(pkt, data->rr_data[i], data->rr_len[i]); } } /* change rrnum only after we are sure it fits */ if(do_data) *num_rrs += data->count; if(do_sig && dnssec) *num_rrs += data->rrsig_count; return RETVAL_OK; }
/** convert hex buffer to binary buffer */ static ldns_buffer * data_buffer2wire(ldns_buffer *data_buffer) { ldns_buffer *wire_buffer = NULL; int c; /* stat hack * 0 = normal * 1 = comment (skip to end of line) * 2 = unprintable character found, read binary data directly */ size_t data_buf_pos = 0; int state = 0; uint8_t *hexbuf; int hexbufpos = 0; size_t wirelen; uint8_t *data_wire = (uint8_t *) ldns_buffer_export(data_buffer); uint8_t *wire = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN); hexbuf = LDNS_XMALLOC(uint8_t, LDNS_MAX_PACKETLEN); for (data_buf_pos = 0; data_buf_pos < ldns_buffer_position(data_buffer); data_buf_pos++) { c = (int) data_wire[data_buf_pos]; if (state < 2 && !isascii(c)) { /*verbose("non ascii character found in file: (%d) switching to raw mode\n", c);*/ state = 2; } switch (state) { case 0: if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) { hexbuf[hexbufpos] = (uint8_t) c; hexbufpos++; } else if (c == ';') { state = 1; } else if (c == ' ' || c == '\t' || c == '\n') { /* skip whitespace */ } break; case 1: if (c == '\n' || c == EOF) { state = 0; } break; case 2: hexbuf[hexbufpos] = (uint8_t) c; hexbufpos++; break; default: error("unknown state while reading"); LDNS_FREE(hexbuf); return 0; break; } } if (hexbufpos >= LDNS_MAX_PACKETLEN) { /*verbose("packet size reached\n");*/ } /* lenient mode: length must be multiple of 2 */ if (hexbufpos % 2 != 0) { hexbuf[hexbufpos] = (uint8_t) '0'; hexbufpos++; } if (state < 2) { wirelen = hexstr2bin((char *) hexbuf, hexbufpos, wire, 0, LDNS_MAX_PACKETLEN); wire_buffer = ldns_buffer_new(wirelen); ldns_buffer_new_frm_data(wire_buffer, wire, wirelen); } else { error("Incomplete hex data, not at byte boundary\n"); } LDNS_FREE(wire); LDNS_FREE(hexbuf); return wire_buffer; }