static bool pkt_has_type(const knot_pkt_t *pkt, uint16_t type) { if (!pkt) { return false; } if (section_has_type(knot_pkt_section(pkt, KNOT_ANSWER), type)) { return true; } if (section_has_type(knot_pkt_section(pkt, KNOT_AUTHORITY), type)) { return true; } return section_has_type(knot_pkt_section(pkt, KNOT_ADDITIONAL), type); }
int kr_nsec_existence_denial(const knot_pkt_t *pkt, knot_section_t section_id, const knot_dname_t *sname, uint16_t stype) { const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); if (!sec || !sname) { return kr_error(EINVAL); } int flags = 0; for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rrset = knot_pkt_rr(sec, i); if (rrset->type != KNOT_RRTYPE_NSEC) { continue; } /* NSEC proves that name exists, but has no data (RFC4035 4.9, 1) */ if (knot_dname_is_equal(rrset->owner, sname)) { no_data_response_check_rrtype(&flags, rrset, stype); } else { /* NSEC proves that name doesn't exist (RFC4035, 4.9, 2) */ name_error_response_check_rr(&flags, rrset, sname); } no_data_wildcard_existence_check(&flags, rrset, sec); } return kr_nsec_existence_denied(flags) ? kr_ok() : kr_error(ENOENT); }
static int update_delegation(struct kr_request *req, struct kr_query *qry, knot_pkt_t *answer, bool has_nsec3) { struct kr_zonecut *cut = &qry->zone_cut; /* RFC4035 3.1.4. authoritative must send either DS or proof of non-existence. * If it contains neither, the referral is bogus (or an attempted downgrade attack). */ unsigned section = KNOT_ANSWER; if (!knot_wire_get_aa(answer->wire)) { /* Referral */ section = KNOT_AUTHORITY; } else if (knot_pkt_qtype(answer) == KNOT_RRTYPE_DS) { /* Subrequest */ section = KNOT_ANSWER; } else { /* N/A */ return kr_ok(); } int ret = 0; const knot_dname_t *proved_name = knot_pkt_qname(answer); /* Aggregate DS records (if using multiple keys) */ knot_rrset_t *new_ds = update_ds(cut, knot_pkt_section(answer, section)); if (!new_ds) { /* No DS provided, check for proof of non-existence. */ if (!has_nsec3) { if (!knot_wire_get_aa(answer->wire)) { /* Referral, check if it is referral to unsigned, rfc4035 5.2 */ ret = kr_nsec_ref_to_unsigned(answer); } else { /* No-data answer */ ret = kr_nsec_existence_denial(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS); } } else { if (!knot_wire_get_aa(answer->wire)) { /* Referral, check if it is referral to unsigned, rfc5155 8.9 */ ret = kr_nsec3_ref_to_unsigned(answer); } else { /* No-data answer, QTYPE is DS, rfc5155 8.6 */ ret = kr_nsec3_no_data(answer, KNOT_AUTHORITY, proved_name, KNOT_RRTYPE_DS); } if (ret == kr_error(DNSSEC_NOT_FOUND)) { /* Not bogus, going insecure due to optout */ ret = 0; } } if (ret != 0) { DEBUG_MSG(qry, "<= bogus proof of DS non-existence\n"); qry->flags |= QUERY_DNSSEC_BOGUS; } else { DEBUG_MSG(qry, "<= DS doesn't exist, going insecure\n"); qry->flags &= ~QUERY_DNSSEC_WANT; qry->flags |= QUERY_DNSSEC_INSECURE; } return ret; } /* Extend trust anchor */ DEBUG_MSG(qry, "<= DS: OK\n"); cut->trust_anchor = new_ds; return ret; }
void print_data_xfr(const knot_pkt_t *packet, const style_t *style) { if (packet == NULL || style == NULL) { DBG_NULL; return; } const knot_pktsection_t *answers = knot_pkt_section(packet, KNOT_ANSWER); switch (style->format) { case FORMAT_DIG: print_section_dig(knot_pkt_rr(answers, 0), answers->count, style); break; case FORMAT_HOST: print_section_host(knot_pkt_rr(answers, 0), answers->count, style); break; case FORMAT_FULL: print_section_full(knot_pkt_rr(answers, 0), answers->count, style, true); // Print TSIG record. if (style->show_tsig && knot_pkt_has_tsig(packet)) { print_section_full(packet->tsig_rr, 1, style, false); } break; default: break; } }
static int validate_section(kr_rrset_validation_ctx_t *vctx, knot_mm_t *pool) { if (!vctx) { return kr_error(EINVAL); } const knot_pktsection_t *sec = knot_pkt_section(vctx->pkt, vctx->section_id); if (!sec) { return kr_ok(); } int ret = kr_ok(); map_t stash = map_make(); stash.malloc = (map_alloc_f) mm_alloc; stash.free = (map_free_f) mm_free; stash.baton = pool; /* Determine RR types contained in the section. */ for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(sec, i); if (rr->type == KNOT_RRTYPE_RRSIG) { continue; } if ((rr->type == KNOT_RRTYPE_NS) && (vctx->section_id == KNOT_AUTHORITY)) { continue; } /* Only validate answers from current cut, records above the cut are stripped. */ if (!knot_dname_in(vctx->zone_name, rr->owner)) { continue; } ret = kr_rrmap_add(&stash, rr, 0, pool); if (ret != 0) { goto fail; } } /* Can't use qry->zone_cut.name directly, as this name can * change when updating cut information before validation. */ vctx->zone_name = vctx->keys ? vctx->keys->owner : NULL; ret = map_walk(&stash, &validate_rrset, vctx); if (ret != 0) { return ret; } ret = vctx->result; fail: return ret; }
static const knot_dname_t *signature_authority(knot_pkt_t *pkt) { for (knot_section_t i = KNOT_ANSWER; i <= KNOT_AUTHORITY; ++i) { const knot_pktsection_t *sec = knot_pkt_section(pkt, i); for (unsigned k = 0; k < sec->count; ++k) { const knot_rrset_t *rr = knot_pkt_rr(sec, k); if (rr->type == KNOT_RRTYPE_RRSIG) { return knot_rrsig_signer_name(&rr->rrs, 0); } } } return NULL; }
int notify_process_query(knot_pkt_t *pkt, struct query_data *qdata) { if (pkt == NULL || qdata == NULL) { return KNOT_STATE_FAIL; } /* Validate notification query. */ int state = notify_check_query(qdata); if (state == KNOT_STATE_FAIL) { switch (qdata->rcode) { case KNOT_RCODE_NOTAUTH: /* Not authoritative or ACL check failed. */ NOTIFY_QLOG(LOG_NOTICE, "unauthorized request"); break; case KNOT_RCODE_FORMERR: /* Silently ignore bad queries. */ default: break; } return state; } /* Reserve space for TSIG. */ knot_pkt_reserve(pkt, knot_tsig_wire_maxsize(&qdata->sign.tsig_key)); /* SOA RR in answer may be included, recover serial. */ const knot_pktsection_t *answer = knot_pkt_section(qdata->query, KNOT_ANSWER); if (answer->count > 0) { const knot_rrset_t *soa = knot_pkt_rr(answer, 0); if (soa->type == KNOT_RRTYPE_SOA) { uint32_t serial = knot_soa_serial(&soa->rrs); NOTIFY_QLOG(LOG_INFO, "received serial %u", serial); } else { /* Complain, but accept N/A record. */ NOTIFY_QLOG(LOG_NOTICE, "received, bad record in answer section"); } } else { NOTIFY_QLOG(LOG_INFO, "received, doesn't have SOA"); } /* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */ zone_t *zone = (zone_t *)qdata->zone; zone_set_preferred_master(zone, qdata->param->remote); zone_events_schedule(zone, ZONE_EVENT_REFRESH, ZONE_EVENT_NOW); int ret = zone_events_write_persistent(zone); if (ret != KNOT_EOK) { return KNOT_STATE_FAIL; } return KNOT_STATE_DONE; }
_public_ int knot_pkt_parse_payload(knot_pkt_t *pkt, unsigned flags) { if (pkt == NULL) { return KNOT_EINVAL; } assert(pkt->wire != NULL); assert(pkt->size > 0); /* Reserve memory in advance to avoid resizing. */ size_t rr_count = knot_wire_get_ancount(pkt->wire) + knot_wire_get_nscount(pkt->wire) + knot_wire_get_arcount(pkt->wire); int ret = pkt_rr_array_alloc(pkt, rr_count); if (ret != KNOT_EOK) { return ret; } for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) { ret = knot_pkt_begin(pkt, i); if (ret != KNOT_EOK) { return ret; } ret = knot_pkt_parse_section(pkt, flags); if (ret != KNOT_EOK) { return ret; } } /* TSIG must be last record of AR if present. */ const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL); if (pkt->tsig_rr != NULL) { const knot_rrset_t *last_rr = knot_pkt_rr(ar, ar->count - 1); if (ar->count > 0 && pkt->tsig_rr->rrs.data != last_rr->rrs.data) { return KNOT_EMALF; } } /* Check for trailing garbage. */ if (pkt->parsed < pkt->size) { return KNOT_EMALF; } return KNOT_EOK; }
int kr_nsec_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t section_id, const knot_dname_t *sname) { const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); if (!sec || !sname) { return kr_error(EINVAL); } for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rrset = knot_pkt_rr(sec, i); if (rrset->type != KNOT_RRTYPE_NSEC) { continue; } if (nsec_nonamematch(rrset, sname) == 0) { return kr_ok(); } } return kr_error(ENOENT); }
int kr_nsec_name_error_response_check(const knot_pkt_t *pkt, knot_section_t section_id, const knot_dname_t *sname) { const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); if (!sec || !sname) { return kr_error(EINVAL); } int flags = 0; for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rrset = knot_pkt_rr(sec, i); if (rrset->type != KNOT_RRTYPE_NSEC) { continue; } int ret = name_error_response_check_rr(&flags, rrset, sname); if (ret != 0) { return ret; } } return kr_nsec_existence_denied(flags) ? kr_ok() : kr_error(ENOENT); }
int kr_nsec_no_data_response_check(const knot_pkt_t *pkt, knot_section_t section_id, const knot_dname_t *sname, uint16_t stype) { const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); if (!sec || !sname) { return kr_error(EINVAL); } int flags = 0; for (unsigned i = 0; i < sec->count; ++i) { const knot_rrset_t *rrset = knot_pkt_rr(sec, i); if (rrset->type != KNOT_RRTYPE_NSEC) { continue; } if (knot_dname_is_equal(rrset->owner, sname)) { int ret = no_data_response_check_rrtype(&flags, rrset, stype); if (ret != 0) { return ret; } } } return (flags & FLG_NOEXIST_RRTYPE) ? kr_ok() : kr_error(ENOENT); }
static int rosedb_log_message(char *stream, size_t *maxlen, knot_pkt_t *pkt, const char *threat_code, struct query_data *qdata) { char dname_buf[KNOT_DNAME_MAXLEN] = {'\0'}; struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); time_t now = time(NULL); struct tm tm; gmtime_r(&now, &tm); /* Field 1 Timestamp (UTC). */ STREAM_WRITE(stream, maxlen, strftime, "%Y-%m-%d %H:%M:%S\t", &tm); /* Field 2/3 Remote, local address. */ const struct sockaddr *remote = (const struct sockaddr *)qdata->param->remote; memcpy(&addr, remote, sockaddr_len(remote)); int client_port = sockaddr_port(&addr); sockaddr_port_set(&addr, 0); STREAM_WRITE(stream, maxlen, sockaddr_tostr, &addr); STREAM_WRITE(stream, maxlen, snprintf, "\t"); getsockname(qdata->param->socket, (struct sockaddr *)&addr, &addr_len); int server_port = sockaddr_port(&addr); sockaddr_port_set(&addr, 0); STREAM_WRITE(stream, maxlen, sockaddr_tostr, &addr); STREAM_WRITE(stream, maxlen, snprintf, "\t"); /* Field 4/5 Local, remote port. */ STREAM_WRITE(stream, maxlen, snprintf, "%d\t%d\t", client_port, server_port); /* Field 6 Threat ID. */ STREAM_WRITE(stream, maxlen, snprintf, "%s\t", threat_code); /* Field 7 - 13 NULL */ STREAM_WRITE(stream, maxlen, snprintf, "\t\t\t\t\t\t\t"); /* Field 14 QNAME */ knot_dname_to_str(dname_buf, knot_pkt_qname(qdata->query), sizeof(dname_buf)); STREAM_WRITE(stream, maxlen, snprintf, "%s\t", dname_buf); /* Field 15 Resolution (0 = local, 1 = lookup)*/ STREAM_WRITE(stream, maxlen, snprintf, "0\t"); /* Field 16 RDATA. * - Return randomly RDATA in the answer section (probabilistic rotation). * - Empty if no answer. */ const knot_pktsection_t *ans = knot_pkt_section(pkt, KNOT_ANSWER); if (ans->count > 0) { const knot_rrset_t *rr = &ans->rr[knot_random_uint16_t() % ans->count]; int ret = knot_rrset_txt_dump_data(rr, 0, stream, *maxlen, &KNOT_DUMP_STYLE_DEFAULT); if (ret < 0) { return ret; } stream_skip(&stream, maxlen, ret); } STREAM_WRITE(stream, maxlen, snprintf, "\t"); /* Field 17 Connection type. */ STREAM_WRITE(stream, maxlen, snprintf, "%s\t", net_is_connected(qdata->param->socket) ? "TCP" : "UDP"); /* Field 18 Query type. */ char type_str[16] = { '\0' }; knot_rrtype_to_string(knot_pkt_qtype(qdata->query), type_str, sizeof(type_str)); STREAM_WRITE(stream, maxlen, snprintf, "%s\t", type_str); /* Field 19 First authority. */ const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY); if (ns->count > 0 && ns->rr[0].type == KNOT_RRTYPE_NS) { const knot_dname_t *label = knot_ns_name(&ns->rr[0].rrs, 0); memset(dname_buf, 0, sizeof(dname_buf)); memcpy(dname_buf, label + 1, *label); STREAM_WRITE(stream, maxlen, snprintf, "%s", dname_buf); } return KNOT_EOK; }
static int validate(knot_layer_t *ctx, knot_pkt_t *pkt) { int ret = 0; struct kr_request *req = ctx->data; struct kr_query *qry = req->current_query; /* Ignore faulty or unprocessed responses. */ if (ctx->state & (KNOT_STATE_FAIL|KNOT_STATE_CONSUME)) { return ctx->state; } /* Pass-through if user doesn't want secure answer or stub. */ /* @todo: Validating stub resolver mode. */ if (!(qry->flags & QUERY_DNSSEC_WANT) || (qry->flags & QUERY_STUB)) { return ctx->state; } /* Answer for RRSIG may not set DO=1, but all records MUST still validate. */ bool use_signatures = (knot_pkt_qtype(pkt) != KNOT_RRTYPE_RRSIG); if (!(qry->flags & QUERY_CACHED) && !knot_pkt_has_dnssec(pkt) && !use_signatures) { DEBUG_MSG(qry, "<= got insecure response\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } /* Track difference between current TA and signer name. * This indicates that the NS is auth for both parent-child, and we must update DS/DNSKEY to validate it. */ const bool track_pc_change = (!(qry->flags & QUERY_CACHED) && (qry->flags & QUERY_DNSSEC_WANT)); const knot_dname_t *ta_name = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL; const knot_dname_t *signer = signature_authority(pkt); if (track_pc_change && ta_name && (!signer || !knot_dname_is_equal(ta_name, signer))) { if (ctx->state == KNOT_STATE_YIELD) { /* Already yielded for revalidation. */ return KNOT_STATE_FAIL; } DEBUG_MSG(qry, ">< cut changed, needs revalidation\n"); if (!signer) { /* Not a DNSSEC-signed response, ask parent for DS to prove transition to INSECURE. */ } else if (knot_dname_is_sub(signer, qry->zone_cut.name)) { /* Key signer is below current cut, advance and refetch keys. */ qry->zone_cut.name = knot_dname_copy(signer, &req->pool); } else if (!knot_dname_is_equal(signer, qry->zone_cut.name)) { /* Key signer is above the current cut, so we can't validate it. This happens when a server is authoritative for both grandparent, parent and child zone. Ascend to parent cut, and refetch authority for signer. */ if (qry->zone_cut.parent) { memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut)); } else { qry->flags |= QUERY_AWAIT_CUT; } qry->zone_cut.name = knot_dname_copy(signer, &req->pool); } /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */ return KNOT_STATE_YIELD; } /* Check if this is a DNSKEY answer, check trust chain and store. */ uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire); uint16_t qtype = knot_pkt_qtype(pkt); bool has_nsec3 = pkt_has_type(pkt, KNOT_RRTYPE_NSEC3); if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) { ret = validate_keyset(qry, pkt, has_nsec3); if (ret != 0) { DEBUG_MSG(qry, "<= bad keys, broken trust chain\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* Validate non-existence proof if not positive answer. */ if (!(qry->flags & QUERY_CACHED) && pkt_rcode == KNOT_RCODE_NXDOMAIN) { /* @todo If knot_pkt_qname(pkt) is used instead of qry->sname then the tests crash. */ if (!has_nsec3) { ret = kr_nsec_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname); } else { ret = kr_nsec3_name_error_response_check(pkt, KNOT_AUTHORITY, qry->sname); } if (ret != 0) { DEBUG_MSG(qry, "<= bad NXDOMAIN proof\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* @todo WTH, this needs API that just tries to find a proof and the caller * doesn't have to worry about NSEC/NSEC3 * @todo rework this */ if (!(qry->flags & QUERY_CACHED)) { const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER); if (pkt_rcode == KNOT_RCODE_NOERROR && an->count == 0 && knot_wire_get_aa(pkt->wire)) { /* @todo * ? quick mechanism to determine which check to preform first * ? merge the functionality together to share code/resources */ if (!has_nsec3) { ret = kr_nsec_existence_denial(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt)); } else { ret = kr_nsec3_no_data(pkt, KNOT_AUTHORITY, knot_pkt_qname(pkt), knot_pkt_qtype(pkt)); } if (ret != 0) { if (has_nsec3 && (ret == kr_error(DNSSEC_NOT_FOUND))) { DEBUG_MSG(qry, "<= can't prove NODATA due to optout, going insecure\n"); qry->flags &= ~QUERY_DNSSEC_WANT; qry->flags |= QUERY_DNSSEC_INSECURE; } else { DEBUG_MSG(qry, "<= bad NODATA proof\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } } } /* Validate all records, fail as bogus if it doesn't match. * Do not revalidate data from cache, as it's already trusted. */ if (!(qry->flags & QUERY_CACHED)) { ret = validate_records(qry, pkt, req->rplan.pool, has_nsec3); if (ret != 0) { DEBUG_MSG(qry, "<= couldn't validate RRSIGs\n"); qry->flags |= QUERY_DNSSEC_BOGUS; return KNOT_STATE_FAIL; } } /* Check if wildcard expansion detected for final query. * If yes, copy authority. */ if ((qry->parent == NULL) && (qry->flags & QUERY_DNSSEC_WEXPAND)) { const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY); for (unsigned i = 0; i < auth->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(auth, i); kr_rrarray_add(&req->authority, rr, &req->answer->mm); } } /* Check and update current delegation point security status. */ ret = update_delegation(req, qry, pkt, has_nsec3); if (ret != 0) { return KNOT_STATE_FAIL; } /* Update parent query zone cut */ if (qry->parent) { if (update_parent_keys(qry, qtype) != 0) { return KNOT_STATE_FAIL; } } DEBUG_MSG(qry, "<= answer valid, OK\n"); return KNOT_STATE_DONE; }
int kr_rrset_validate_with_key(const knot_pkt_t *pkt, knot_section_t section_id, const knot_rrset_t *covered, const knot_rrset_t *keys, size_t key_pos, const struct dseckey *key, const knot_dname_t *zone_name, uint32_t timestamp, bool has_nsec3) { struct dseckey *created_key = NULL; if (key == NULL) { const knot_rdata_t *krr = knot_rdataset_at(&keys->rrs, key_pos); int ret = kr_dnssec_key_from_rdata(&created_key, keys->owner, knot_rdata_data(krr), knot_rdata_rdlen(krr)); if (ret != 0) { return ret; } key = created_key; } uint16_t keytag = dnssec_key_get_keytag((dnssec_key_t *)key); int covered_labels = knot_dname_labels(covered->owner, NULL); if (knot_dname_is_wildcard(covered->owner)) { /* The asterisk does not count, RFC4034 3.1.3, paragraph 3. */ --covered_labels; } const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id); for (unsigned i = 0; i < sec->count; ++i) { /* Consider every RRSIG that matches owner and covers the class/type. */ const knot_rrset_t *rrsig = knot_pkt_rr(sec, i); if (rrsig->type != KNOT_RRTYPE_RRSIG) { continue; } if ((covered->rclass != rrsig->rclass) || !knot_dname_is_equal(covered->owner, rrsig->owner)) { continue; } for (uint16_t j = 0; j < rrsig->rrs.rr_count; ++j) { int val_flgs = 0; int trim_labels = 0; if (knot_rrsig_type_covered(&rrsig->rrs, j) != covered->type) { continue; } if (validate_rrsig_rr(&val_flgs, covered_labels, rrsig, j, keys, key_pos, keytag, zone_name, timestamp) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { trim_labels = wildcard_radix_len_diff(covered->owner, rrsig, j); if (trim_labels < 0) { break; } } if (kr_check_signature(rrsig, j, (dnssec_key_t *) key, covered, trim_labels) != 0) { continue; } if (val_flgs & FLG_WILDCARD_EXPANSION) { int ret = 0; if (!has_nsec3) { ret = kr_nsec_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner); } else { ret = kr_nsec3_wildcard_answer_response_check(pkt, KNOT_AUTHORITY, covered->owner, trim_labels - 1); } if (ret != 0) { continue; } } /* Validated with current key, OK */ kr_dnssec_key_free(&created_key); return kr_ok(); } } /* No applicable key found, cannot be validated. */ kr_dnssec_key_free(&created_key); return kr_error(ENOENT); }
static int validate_records(struct kr_query *qry, knot_pkt_t *answer, knot_mm_t *pool, bool has_nsec3) { if (!qry->zone_cut.key) { DEBUG_MSG(qry, "<= no DNSKEY, can't validate\n"); return kr_error(EBADMSG); } kr_rrset_validation_ctx_t vctx = { .pkt = answer, .section_id = KNOT_ANSWER, .keys = qry->zone_cut.key, .zone_name = qry->zone_cut.name, .timestamp = qry->timestamp.tv_sec, .has_nsec3 = has_nsec3, .flags = 0, .result = 0 }; int ret = validate_section(&vctx, pool); if (ret != 0) { return ret; } uint32_t an_flags = vctx.flags; vctx.section_id = KNOT_AUTHORITY; /* zone_name can be changed by validate_section(), restore it */ vctx.zone_name = qry->zone_cut.name; vctx.flags = 0; vctx.result = 0; ret = validate_section(&vctx, pool); if (ret != 0) { return ret; } /* Records were validated. * If there is wildcard expansion in answer, flag the query. */ if (an_flags & KR_DNSSEC_VFLG_WEXPAND) { qry->flags |= QUERY_DNSSEC_WEXPAND; } return ret; } static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_nsec3) { /* Merge DNSKEY records from answer that are below/at current cut. */ bool updated_key = false; const knot_pktsection_t *an = knot_pkt_section(answer, KNOT_ANSWER); for (unsigned i = 0; i < an->count; ++i) { const knot_rrset_t *rr = knot_pkt_rr(an, i); if ((rr->type != KNOT_RRTYPE_DNSKEY) || !knot_dname_in(qry->zone_cut.name, rr->owner)) { continue; } /* Merge with zone cut (or replace ancestor key). */ if (!qry->zone_cut.key || !knot_dname_is_equal(qry->zone_cut.key->owner, rr->owner)) { qry->zone_cut.key = knot_rrset_copy(rr, qry->zone_cut.pool); if (!qry->zone_cut.key) { return kr_error(ENOMEM); } updated_key = true; } else { int ret = knot_rdataset_merge(&qry->zone_cut.key->rrs, &rr->rrs, qry->zone_cut.pool); if (ret != 0) { knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool); return ret; } updated_key = true; } } /* Check if there's a key for current TA. */ if (updated_key && !(qry->flags & QUERY_CACHED)) { kr_rrset_validation_ctx_t vctx = { .pkt = answer, .section_id = KNOT_ANSWER, .keys = qry->zone_cut.key, .zone_name = qry->zone_cut.name, .timestamp = qry->timestamp.tv_sec, .has_nsec3 = has_nsec3, .flags = 0, .result = 0 }; int ret = kr_dnskeys_trusted(&vctx, qry->zone_cut.trust_anchor); if (ret != 0) { knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool); return ret; } if (vctx.flags & KR_DNSSEC_VFLG_WEXPAND) { qry->flags |= QUERY_DNSSEC_WEXPAND; } } return kr_ok(); }
static int axfr_answer_packet(knot_pkt_t *pkt, struct xfr_proc *proc) { assert(pkt != NULL); assert(proc != NULL); /* Update counters. */ proc->npkts += 1; proc->nbytes += pkt->size; /* Init zone creator. */ zcreator_t zc = {.z = proc->contents, .master = false, .ret = KNOT_EOK }; const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); for (uint16_t i = 0; i < answer->count; ++i) { const knot_rrset_t *rr = &answer->rr[i]; if (rr->type == KNOT_RRTYPE_SOA && node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) { return KNOT_NS_PROC_DONE; } else { int ret = zcreator_step(&zc, rr); if (ret != KNOT_EOK) { return KNOT_NS_PROC_FAIL; } } } return KNOT_NS_PROC_MORE; } int axfr_answer_process(knot_pkt_t *pkt, struct answer_data *adata) { if (pkt == NULL || adata == NULL) { return KNOT_NS_PROC_FAIL; } /* Check RCODE. */ uint8_t rcode = knot_wire_get_rcode(pkt->wire); if (rcode != KNOT_RCODE_NOERROR) { lookup_table_t *lut = lookup_by_id(knot_rcode_names, rcode); if (lut != NULL) { AXFRIN_LOG(LOG_ERR, "server responded with %s", lut->name); } return KNOT_NS_PROC_FAIL; } /* Initialize processing with first packet. */ if (adata->ext == NULL) { NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0); AXFRIN_LOG(LOG_INFO, "starting"); int ret = axfr_answer_init(adata); if (ret != KNOT_EOK) { AXFRIN_LOG(LOG_ERR, "failed (%s)", knot_strerror(ret)); return KNOT_NS_PROC_FAIL; } } else { NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 100); } /* Process answer packet. */ int ret = axfr_answer_packet(pkt, (struct xfr_proc *)adata->ext); if (ret == KNOT_NS_PROC_DONE) { NS_NEED_TSIG_SIGNED(&adata->param->tsig_ctx, 0); /* This was the last packet, finalize zone and publish it. */ int fret = axfr_answer_finalize(adata); if (fret != KNOT_EOK) { ret = KNOT_NS_PROC_FAIL; } } return ret; }
void print_packet(const knot_pkt_t *packet, const net_t *net, const size_t size, const float elapsed, const time_t exec_time, const bool incoming, const style_t *style) { if (packet == NULL || style == NULL) { DBG_NULL; return; } const knot_pktsection_t *answers = knot_pkt_section(packet, KNOT_ANSWER); const knot_pktsection_t *authority = knot_pkt_section(packet, KNOT_AUTHORITY); const knot_pktsection_t *additional = knot_pkt_section(packet, KNOT_ADDITIONAL); uint16_t qdcount = knot_wire_get_qdcount(packet->wire); uint16_t ancount = knot_wire_get_ancount(packet->wire); uint16_t nscount = knot_wire_get_nscount(packet->wire); uint16_t arcount = knot_wire_get_arcount(packet->wire); // Get Extended RCODE from the packet. uint16_t rcode = knot_pkt_get_ext_rcode(packet); // Disable additionals printing if there are no other records. // OPT record may be placed anywhere within additionals! if (knot_pkt_has_edns(packet) && arcount == 1) { arcount = 0; } // Print packet information header. if (style->show_header) { print_header(packet, style, rcode); } // Print EDNS section. if (style->show_edns && knot_pkt_has_edns(packet)) { printf("\n;; EDNS PSEUDOSECTION:\n;; "); print_section_opt(packet->opt_rr, knot_wire_get_rcode(packet->wire)); } // Print DNS sections. switch (style->format) { case FORMAT_DIG: if (ancount > 0) { print_section_dig(knot_pkt_rr(answers, 0), ancount, style); } break; case FORMAT_HOST: if (ancount > 0) { print_section_host(knot_pkt_rr(answers, 0), ancount, style); } else { print_error_host(rcode, packet, style); } break; case FORMAT_NSUPDATE: if (style->show_question && qdcount > 0) { printf("\n;; ZONE SECTION:\n;; "); print_section_question(knot_pkt_qname(packet), knot_pkt_qclass(packet), knot_pkt_qtype(packet), style); } if (style->show_answer && ancount > 0) { printf("\n;; PREREQUISITE SECTION:\n"); print_section_full(knot_pkt_rr(answers, 0), ancount, style, true); } if (style->show_authority && nscount > 0) { printf("\n;; UPDATE SECTION:\n"); print_section_full(knot_pkt_rr(authority, 0), nscount, style, true); } if (style->show_additional && arcount > 0) { printf("\n;; ADDITIONAL DATA:\n"); print_section_full(knot_pkt_rr(additional, 0), arcount, style, true); } break; case FORMAT_FULL: if (style->show_question && qdcount > 0) { printf("\n;; QUESTION SECTION:\n;; "); print_section_question(knot_pkt_qname(packet), knot_pkt_qclass(packet), knot_pkt_qtype(packet), style); } if (style->show_answer && ancount > 0) { printf("\n;; ANSWER SECTION:\n"); print_section_full(knot_pkt_rr(answers, 0), ancount, style, true); } if (style->show_authority && nscount > 0) { printf("\n;; AUTHORITY SECTION:\n"); print_section_full(knot_pkt_rr(authority, 0), nscount, style, true); } if (style->show_additional && arcount > 0) { printf("\n;; ADDITIONAL SECTION:\n"); print_section_full(knot_pkt_rr(additional, 0), arcount, style, true); } break; default: break; } // Print TSIG section. if (style->show_tsig && knot_pkt_has_tsig(packet)) { printf("\n;; TSIG PSEUDOSECTION:\n"); print_section_full(packet->tsig_rr, 1, style, false); } // Print packet statistics. if (style->show_footer) { printf("\n"); print_footer(size, 0, 0, net, elapsed, exec_time, incoming); } }