Пример #1
0
_public_
uint16_t knot_pkt_type(const knot_pkt_t *pkt)
{
	if (pkt == NULL) {
		return 0;
	}

	bool is_query = (knot_wire_get_qr(pkt->wire) == 0);
	uint16_t ret = KNOT_QUERY_INVALID;
	uint8_t opcode = knot_wire_get_opcode(pkt->wire);
	uint8_t query_type = knot_pkt_qtype(pkt);

	switch (opcode) {
	case KNOT_OPCODE_QUERY:
		switch (query_type) {
		case 0 /* RESERVED */: /* INVALID */ break;
		case KNOT_RRTYPE_AXFR: ret = KNOT_QUERY_AXFR; break;
		case KNOT_RRTYPE_IXFR: ret = KNOT_QUERY_IXFR; break;
		default:               ret = KNOT_QUERY_NORMAL; break;
		}
		break;
	case KNOT_OPCODE_NOTIFY: ret = KNOT_QUERY_NOTIFY; break;
	case KNOT_OPCODE_UPDATE: ret = KNOT_QUERY_UPDATE; break;
	default: break;
	}

	if (!is_query) {
		ret = ret|KNOT_RESPONSE;
	}

	return ret;
}
Пример #2
0
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;
}
Пример #3
0
void print_header_xfr(const knot_pkt_t *packet, const style_t  *style)
{
    if (style == NULL) {
        DBG_NULL;
        return;
    }

    char xfr[16] = "AXFR";

    switch (knot_pkt_qtype(packet)) {
    case KNOT_RRTYPE_AXFR:
        break;
    case KNOT_RRTYPE_IXFR:
        xfr[0] = 'I';
        break;
    default:
        return;
    }

    if (style->show_header) {
        char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
        if (style->style.ascii_to_idn != NULL) {
            style->style.ascii_to_idn(&owner);
        }
        if (owner != NULL) {
            printf(";; %s for %s\n", xfr, owner);
            free(owner);
        }
    }
}
Пример #4
0
static void print_error_host(const uint16_t   code,
                             const knot_pkt_t *packet,
                             const style_t    *style)
{
    const char *rcode_str = "Unknown";
    char type[32] = "Unknown";
    char *owner;

    lookup_table_t *rcode;

    owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
    if (style->style.ascii_to_idn != NULL) {
        style->style.ascii_to_idn(&owner);
    }

    rcode = lookup_by_id(knot_rcode_names, code);
    if (rcode != NULL) {
        rcode_str = rcode->name;
    }
    knot_rrtype_to_string(knot_pkt_qtype(packet), type, sizeof(type));

    if (code == KNOT_RCODE_NOERROR) {
        printf("Host %s has no %s record\n", owner, type);
    } else {
        printf("Host %s type %s error: %s\n", owner, type, rcode_str);
    }

    free(owner);
}
Пример #5
0
/*! \brief Check if query fits the template requirements. */
static int template_match(int state, synth_template_t *tpl, knot_pkt_t *pkt, struct query_data *qdata)
{
	/* Parse address from query name. */
	char addr_str[SOCKADDR_STRLEN] = { '\0' };
	int ret = addr_parse(qdata, tpl, addr_str);
	if (ret != KNOT_EOK) {
		return state; /* Can't identify addr in QNAME, not applicable. */
	}

	/* Match against template netblock. */
	struct sockaddr_storage query_addr = { '\0' };
	int provided_af = tpl->subnet.ss.ss_family;
	ret = sockaddr_set(&query_addr, provided_af, addr_str, 0);
	if (ret == KNOT_EOK) {
		ret = netblock_match(&tpl->subnet, &query_addr);
	}
	if (ret != 0) {
		return state; /* Out of our netblock, not applicable. */
	}

	/* Check if the request is for an available query type. */
	uint16_t qtype = knot_pkt_qtype(qdata->query);
	switch (tpl->type) {
	case SYNTH_FORWARD:
		if (!query_satisfied_by_family(qtype, provided_af)) {
			qdata->rcode = KNOT_RCODE_NOERROR;
			return NODATA;
		}
		break;
	case SYNTH_REVERSE:
		if (qtype != KNOT_RRTYPE_PTR && qtype != KNOT_RRTYPE_ANY) {
			qdata->rcode = KNOT_RCODE_NOERROR;
			return NODATA;
		}
		break;
	default:
		break;
	}

	/* Synthetise record from template. */
	knot_rrset_t *rr = synth_rr(addr_str, tpl, pkt, qdata);
	if (rr == NULL) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		return ERROR;
	}

	/* Insert synthetic response into packet. */
	if (knot_pkt_put(pkt, 0, rr, KNOT_PF_FREE) != KNOT_EOK) {
		return ERROR;
	}

	/* Authoritative response. */
	knot_wire_set_aa(pkt->wire);

	return HIT;
}
Пример #6
0
static int rosedb_synth(knot_pkt_t *pkt, const knot_dname_t *key, struct iter *it,
                        struct query_data *qdata)
{
	struct entry entry;
	int ret = KNOT_EOK;
	uint16_t qtype = knot_pkt_qtype(qdata->query);

	/* Answer section. */
	while (ret == KNOT_EOK) {
		if (cache_iter_val(it, &entry) == 0) {
			ret = rosedb_synth_rr(pkt, &entry, qtype);
		}
		if (cache_iter_next(it) != 0) {
			break;
		}
	}

	/* Authority section. */
	knot_pkt_begin(pkt, KNOT_AUTHORITY);
	
	/* Not found (zone cut if records exist). */
	ret = cache_iter_begin(it, key);
	while (ret == KNOT_EOK) {
		if (cache_iter_val(it, &entry) == 0) {
			ret = rosedb_synth_rr(pkt, &entry, KNOT_RRTYPE_NS);
			ret = rosedb_synth_rr(pkt, &entry, KNOT_RRTYPE_SOA);
		}
		if (cache_iter_next(it) != 0) {
			break;
		}
	}

	/* Our response is authoritative. */
	if (knot_wire_get_nscount(pkt->wire) > 0) {
		knot_wire_set_aa(pkt->wire);
		if (knot_wire_get_ancount(pkt->wire) == 0) {
			qdata->rcode = KNOT_RCODE_NXDOMAIN;
		}
	}

	/* Send message to syslog. */
	struct sockaddr_storage syslog_addr;
	if (sockaddr_set(&syslog_addr, AF_INET, entry.syslog_ip, DEFAULT_PORT) == KNOT_EOK) {
		int sock = net_unbound_socket(AF_INET, &syslog_addr);
		if (sock > 0) {
			rosedb_send_log(sock, (struct sockaddr *)&syslog_addr, pkt,
			                entry.threat_code, qdata);
			close(sock);
		}
	}

	return ret;
}
Пример #7
0
static uint8_t rrl_clsid(rrl_req_t *p)
{
    /* Check error code */
    int ret = CLS_NULL;
    switch (knot_wire_get_rcode(p->w)) {
    case KNOT_RCODE_NOERROR:
        ret = CLS_NORMAL;
        break;
    case KNOT_RCODE_NXDOMAIN:
        return CLS_NXDOMAIN;
        break;
    default:
        return CLS_ERROR;
        break;
    }

    /* Check if answered from a qname */
    if (ret == CLS_NORMAL && p->flags & RRL_WILDCARD) {
        return CLS_WILDCARD;
    }

    /* Check query type for spec. classes. */
    if (p->query) {
        switch(knot_pkt_qtype(p->query)) {
        case KNOT_RRTYPE_ANY:      /* ANY spec. class */
            return CLS_ANY;
            break;
        case KNOT_RRTYPE_DNSKEY:
        case KNOT_RRTYPE_RRSIG:
        case KNOT_RRTYPE_DS:      /* DNSSEC-related RR class. */
            return CLS_DNSSEC;
            break;
        default:
            break;
        }
    }

    /* Check packet size for threshold. */
    if (p->len >= RRL_PSIZE_LARGE) {
        return CLS_LARGE;
    }

    /* Check ancount */
    if (knot_wire_get_ancount(p->w) == 0) {
        return CLS_EMPTY;
    }

    return ret;
}
Пример #8
0
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;
}
Пример #9
0
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;
}
Пример #10
0
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);
    }
}