/*! \brief Set EDNS section. */ static int prepare_edns(conf_t *conf, zone_t *zone, knot_pkt_t *pkt) { conf_val_t val = conf_zone_get(conf, C_REQUEST_EDNS_OPTION, zone->name); /* Check if an extra EDNS option is configured. */ size_t opt_len; const uint8_t *opt_data = conf_data(&val, &opt_len); if (opt_data == NULL) { return KNOT_EOK; } knot_rrset_t opt_rr; conf_val_t *max_payload = &conf->cache.srv_max_udp_payload; int ret = knot_edns_init(&opt_rr, conf_int(max_payload), 0, KNOT_EDNS_VERSION, &pkt->mm); if (ret != KNOT_EOK) { return ret; } ret = knot_edns_add_option(&opt_rr, wire_read_u64(opt_data), yp_bin_len(opt_data + sizeof(uint64_t)), yp_bin(opt_data + sizeof(uint64_t)), &pkt->mm); if (ret != KNOT_EOK) { knot_rrset_clear(&opt_rr, &pkt->mm); return ret; } knot_pkt_begin(pkt, KNOT_ADDITIONAL); ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &opt_rr, KNOT_PF_FREE); if (ret != KNOT_EOK) { knot_rrset_clear(&opt_rr, &pkt->mm); return ret; } return KNOT_EOK; }
int main(int argc, char *argv[]) { plan(25); /* Create memory pool context. */ int ret = 0; knot_mm_t mm; mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE); /* Create names and data. */ knot_dname_t* dnames[NAMECOUNT] = {0}; knot_rrset_t* rrsets[NAMECOUNT] = {0}; for (unsigned i = 0; i < NAMECOUNT; ++i) { dnames[i] = knot_dname_from_str_alloc(g_names[i]); } uint8_t *edns_str = (uint8_t *)"ab"; /* Create OPT RR. */ knot_rrset_t opt_rr; ret = knot_edns_init(&opt_rr, 1024, 0, 0, &mm); if (ret != KNOT_EOK) { skip_block(25, "Failed to initialize OPT RR."); return 0; } /* Add NSID */ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID, strlen((char *)edns_str), edns_str, &mm); if (ret != KNOT_EOK) { knot_rrset_clear(&opt_rr, &mm); skip_block(25, "Failed to add NSID to OPT RR."); return 0; } /* * Packet writer tests. */ /* Create packet. */ knot_pkt_t *out = knot_pkt_new(NULL, MM_DEFAULT_BLKSIZE, &mm); ok(out != NULL, "pkt: new"); /* Mark as response (not part of the test). */ knot_wire_set_qr(out->wire); /* Secure packet. */ const char *tsig_secret = "abcd"; knot_tsig_key_t tsig_key; tsig_key.algorithm = DNSSEC_TSIG_HMAC_MD5; tsig_key.name = dnames[0]; tsig_key.secret.data = (uint8_t *)strdup(tsig_secret); tsig_key.secret.size = strlen(tsig_secret); ret = knot_pkt_reserve(out, knot_tsig_wire_maxsize(&tsig_key)); ok(ret == KNOT_EOK, "pkt: set TSIG key"); /* Write question. */ ret = knot_pkt_put_question(out, dnames[0], KNOT_CLASS_IN, KNOT_RRTYPE_A); ok(ret == KNOT_EOK, "pkt: put question"); /* Add OPT to packet (empty NSID). */ ret = knot_pkt_reserve(out, knot_edns_wire_size(&opt_rr)); ok(ret == KNOT_EOK, "pkt: reserve OPT RR"); /* Begin ANSWER section. */ ret = knot_pkt_begin(out, KNOT_ANSWER); ok(ret == KNOT_EOK, "pkt: begin ANSWER"); /* Write ANSWER section. */ rrsets[0] = knot_rrset_new(dnames[0], KNOT_RRTYPE_A, KNOT_CLASS_IN, NULL); knot_dname_free(&dnames[0], NULL); knot_rrset_add_rdata(rrsets[0], RDVAL(0), RDLEN(0), TTL, NULL); ret = knot_pkt_put(out, KNOT_COMPR_HINT_QNAME, rrsets[0], 0); ok(ret == KNOT_EOK, "pkt: write ANSWER"); /* Begin AUTHORITY. */ ret = knot_pkt_begin(out, KNOT_AUTHORITY); ok(ret == KNOT_EOK, "pkt: begin AUTHORITY"); /* Write rest to AUTHORITY. */ ret = KNOT_EOK; for (unsigned i = 1; i < NAMECOUNT; ++i) { rrsets[i] = knot_rrset_new(dnames[i], KNOT_RRTYPE_NS, KNOT_CLASS_IN, NULL); knot_dname_free(&dnames[i], NULL); knot_rrset_add_rdata(rrsets[i], RDVAL(i), RDLEN(i), TTL, NULL); ret |= knot_pkt_put(out, KNOT_COMPR_HINT_NONE, rrsets[i], 0); } ok(ret == KNOT_EOK, "pkt: write AUTHORITY(%u)", NAMECOUNT - 1); /* Begin ADDITIONALS */ ret = knot_pkt_begin(out, KNOT_ADDITIONAL); ok(ret == KNOT_EOK, "pkt: begin ADDITIONALS"); /* Encode OPT RR. */ ret = knot_pkt_put(out, KNOT_COMPR_HINT_NONE, &opt_rr, 0); ok(ret == KNOT_EOK, "pkt: write OPT RR"); /* * Packet reader tests. */ /* Create new packet from query packet. */ knot_pkt_t *in = knot_pkt_new(out->wire, out->size, &out->mm); ok(in != NULL, "pkt: create packet for parsing"); /* Read packet header. */ ret = knot_pkt_parse_question(in); ok(ret == KNOT_EOK, "pkt: read header"); /* Read packet payload. */ ret = knot_pkt_parse_payload(in, 0); ok(ret == KNOT_EOK, "pkt: read payload"); /* Compare parsed packet to written packet. */ packet_match(in, out); /* * Copied packet tests. */ knot_pkt_t *copy = knot_pkt_new(NULL, in->max_size, &in->mm); ret = knot_pkt_copy(copy, in); ok(ret == KNOT_EOK, "pkt: create packet copy"); /* Compare copied packet to original. */ packet_match(in, copy); /* Free packets. */ knot_pkt_free(©); knot_pkt_free(&out); knot_pkt_free(&in); ok(in == NULL && out == NULL && copy == NULL, "pkt: free"); /* Free extra data. */ for (unsigned i = 0; i < NAMECOUNT; ++i) { knot_rrset_free(&rrsets[i], NULL); } free(tsig_key.secret.data); mp_delete((struct mempool *)mm.ctx); return 0; }
int knot_edns_new_from_rr(knot_opt_rr_t *opt_rr, const knot_rrset_t *rrset) { if (opt_rr == NULL || rrset == NULL || rrset->type != KNOT_RRTYPE_OPT || rrset->rrs.rr_count == 0) { return KNOT_EINVAL; } dbg_edns_verb("Parsing payload.\n"); opt_rr->payload = rrset->rclass; /* RFC6891, 6.2.5 Value < 512B should be treated as 512. */ if (opt_rr->payload < EDNS_MIN_UDP_PAYLOAD) { opt_rr->payload = EDNS_MIN_UDP_PAYLOAD; } // TTL has switched bytes uint32_t ttl; dbg_edns_detail("TTL: %u\n", knot_rrset_rr_ttl(rrset, 0)); knot_wire_write_u32((uint8_t *)&ttl, knot_rrset_rr_ttl(rrset, 0)); // first byte of TTL is extended RCODE dbg_edns_detail("TTL: %u\n", ttl); memcpy(&opt_rr->ext_rcode, &ttl, 1); dbg_edns_detail("Parsed extended RCODE: %u.\n", opt_rr->ext_rcode); // second is the version memcpy(&opt_rr->version, (const uint8_t *)(&ttl) + 1, 1); dbg_edns_detail("Parsed version: %u.\n", opt_rr->version); // third and fourth are flags opt_rr->flags = knot_wire_read_u16((const uint8_t *)(&ttl) + 2); dbg_edns_detail("Parsed flags: %u.\n", opt_rr->flags); // size of the header, options are counted elsewhere opt_rr->size = 11; int rc = 0; dbg_edns_verb("Parsing options.\n"); uint16_t size = knot_rrset_rr_size(rrset, 0); if (size > 0) { uint8_t *raw = knot_rrset_rr_rdata(rrset, 0); size_t pos = 0; while (pos < size) { // ensure there is enough data to parse the OPTION CODE // and OPTION LENGTH if (size - pos + 2 < 4) { dbg_edns("Not enough data to parse.\n"); return KNOT_EMALF; } uint16_t opt_code = knot_wire_read_u16(raw + pos); uint16_t opt_size = knot_wire_read_u16(raw + pos + 2); // there should be enough data for parsing the OPTION // data if (size - pos < opt_size) { dbg_edns("Not enough data to parse options: " "size - pos=%zu, opt_size=%d\n", size - pos, opt_size); return KNOT_EMALF; } rc = knot_edns_add_option(opt_rr, opt_code, opt_size, raw + pos + 4); if (rc != KNOT_EOK) { dbg_edns("Could not add option.\n"); return rc; } pos += 4 + opt_size; } } dbg_edns_verb("EDNS created.\n"); return KNOT_EOK; }
static bool test_setters(knot_rrset_t *opt_rr, int *done) { assert(opt_rr != NULL); assert(done != NULL); /* Header-related setters. */ knot_edns_set_payload(opt_rr, E_MAX_PLD2); knot_edns_set_ext_rcode(opt_rr, E_RCODE2); knot_edns_set_version(opt_rr, E_VERSION2); knot_edns_set_do(opt_rr); bool success = true; bool check = check_header(opt_rr, E_MAX_PLD2, E_VERSION2, DO_FLAG, E_RCODE2, "OPT RR setters", done); success &= check; /* OPTION(RDATA)-related setters. */ /* Proper option. */ int ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID, E_NSID_LEN, (uint8_t *)E_NSID_STR, NULL); ok(ret == KNOT_EOK, "OPT RR setters: add option with data (ret = %s)", knot_strerror(ret)); (*done)++; /* Wrong argument: no OPT RR. */ ret = knot_edns_add_option(NULL, E_OPT3_CODE, E_OPT3_FAKE_LEN, (uint8_t *)E_OPT3_FAKE_DATA, NULL); ok(ret == KNOT_EINVAL, "OPT RR setters: add option (rr == NULL) " "(ret = %s)", knot_strerror(ret)); (*done)++; /* Wrong argument: option length != 0 && data == NULL. */ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_FAKE_LEN, NULL, NULL); ok(ret == KNOT_EINVAL, "OPT RR setters: add option (data == NULL, " "len != 0) (ret = %s)", knot_strerror(ret)); (*done)++; /* Empty OPTION (length 0, data != NULL). */ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_LEN, (uint8_t *)E_OPT3_FAKE_DATA, NULL); ok(ret == KNOT_EOK, "OPT RR setters: add empty option 1 (ret = %s)", knot_strerror(ret)); (*done)++; /* Empty OPTION (length 0, data == NULL). */ ret = knot_edns_add_option(opt_rr, E_OPT4_CODE, E_OPT4_LEN, (uint8_t *)E_OPT4_DATA, NULL); ok(ret == KNOT_EOK, "OPT RR setters: add empty option 2 (ret = %s)", knot_strerror(ret)); (*done)++; knot_rdata_t *rdata = knot_rdataset_at(&opt_rr->rrs, 0); if (rdata == NULL) { skip_block(2, "No RDATA in OPT RR."); return false; } /* Check proper option */ check = check_option(rdata, KNOT_EDNS_OPTION_NSID, E_NSID_LEN, (uint8_t *)E_NSID_STR, "OPT RR setters (proper option)", done); success &= check; /* Check empty option 1 */ check = check_option(rdata, E_OPT3_CODE, E_OPT3_LEN, (uint8_t *)E_OPT3_DATA, "OPT RR setters (empty option 1)", done); success &= check; /* Check empty option 2 */ check = check_option(rdata, E_OPT4_CODE, E_OPT4_LEN, (uint8_t *)E_OPT4_DATA, "OPT RR setters (empty option 2)", done); success &= check; return success; }