1 /*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2  *  SPDX-License-Identifier: GPL-3.0-or-later
3  */
4 
5 /** @file
6  * Implementation of packet-caching.  Prototypes in ./impl.h
7  *
8  * The packet is stashed in entry_h::data as uint16_t length + full packet wire format.
9  */
10 
11 #include "lib/utils.h"
12 #include "lib/layer/iterate.h" /* kr_response_classify */
13 #include "lib/cache/impl.h"
14 
15 
16 /** Compute TTL for a packet.  Generally it's minimum TTL, with extra conditions. */
17 KR_EXPORT
packet_ttl(const knot_pkt_t * pkt,bool is_negative)18 uint32_t packet_ttl(const knot_pkt_t *pkt, bool is_negative)
19 {
20 	bool has_ttl = false;
21 	uint32_t ttl = UINT32_MAX;
22 	/* Find minimum entry TTL in the packet or SOA minimum TTL. */
23 	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
24 		const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
25 		for (unsigned k = 0; k < sec->count; ++k) {
26 			const knot_rrset_t *rr = knot_pkt_rr(sec, k);
27 			if (is_negative) {
28 				/* Use SOA minimum TTL for negative answers. */
29 				if (rr->type == KNOT_RRTYPE_SOA) {
30 					return MIN(rr->ttl, knot_soa_minimum(rr->rrs.rdata));
31 				} else {
32 					continue; /* Use SOA only for negative answers. */
33 				}
34 			}
35 			if (knot_rrtype_is_metatype(rr->type)) {
36 				continue; /* Skip metatypes. */
37 			}
38 			ttl = MIN(ttl, rr->ttl);
39 			has_ttl = true;
40 		}
41 	}
42 	/* If no valid TTL present, go with zero (will get clamped to minimum). */
43 	return has_ttl ? ttl : 0;
44 }
45 
46 
stash_pkt(const knot_pkt_t * pkt,const struct kr_query * qry,const struct kr_request * req,const bool needs_pkt)47 void stash_pkt(const knot_pkt_t *pkt, const struct kr_query *qry,
48 		const struct kr_request *req, const bool needs_pkt)
49 {
50 	/* In some cases, stash also the packet. */
51 	const bool is_negative = kr_response_classify(pkt)
52 				& (PKT_NODATA|PKT_NXDOMAIN);
53 	const struct kr_qflags * const qf = &qry->flags;
54 	const bool want_negative = qf->DNSSEC_INSECURE || !qf->DNSSEC_WANT;
55 	const bool want_pkt = qf->DNSSEC_BOGUS /*< useful for +cd answers */
56 				|| (is_negative && want_negative) || needs_pkt;
57 
58 	if (!want_pkt || !knot_wire_get_aa(pkt->wire)
59 	    || pkt->parsed != pkt->size /*< malformed packet; still can't detect KNOT_EFEWDATA */
60 	   ) {
61 		return;
62 	}
63 
64 	/* Compute rank.  If cd bit is set or we got answer via non-validated
65 	 * forwarding, make the rank bad; otherwise it depends on flags.
66 	 * TODO: probably make validator attempt validation even with +cd. */
67 	uint8_t rank = KR_RANK_AUTH;
68 	const bool risky_vldr = is_negative && qf->FORWARD && qf->CNAME;
69 		/* ^^ CNAME'ed NXDOMAIN answer in forwarding mode can contain
70 		 * unvalidated records; original commit: d6e22f476. */
71 	if (knot_wire_get_cd(req->qsource.packet->wire) || qf->STUB || risky_vldr) {
72 		kr_rank_set(&rank, KR_RANK_OMIT);
73 	} else {
74 		if (qf->DNSSEC_BOGUS) {
75 			kr_rank_set(&rank, KR_RANK_BOGUS);
76 		} else if (qf->DNSSEC_INSECURE) {
77 			kr_rank_set(&rank, KR_RANK_INSECURE);
78 		} else if (!qf->DNSSEC_WANT) {
79 			/* no TAs at all, leave _RANK_AUTH */
80 		} else if (needs_pkt) {
81 			/* All bad cases should be filtered above,
82 			 * at least the same way as pktcache in kresd 1.5.x. */
83 			kr_rank_set(&rank, KR_RANK_SECURE);
84 		} else kr_assert(false);
85 	}
86 
87 	const uint16_t pkt_type = knot_pkt_qtype(pkt);
88 	const knot_dname_t *owner = knot_pkt_qname(pkt); /* qname can't be compressed */
89 
90 	// LATER: nothing exists under NXDOMAIN.  Implement that (optionally)?
91 #if 0
92 	if (knot_wire_get_rcode(pkt->wire) == KNOT_RCODE_NXDOMAIN
93 	 /* && !qf->DNSSEC_INSECURE */ ) {
94 		pkt_type = KNOT_RRTYPE_NS;
95 	}
96 #endif
97 
98 	/* Construct the key under which the pkt will be stored. */
99 	struct key k_storage, *k = &k_storage;
100 	knot_db_val_t key;
101 	int ret = kr_dname_lf(k->buf, owner, false);
102 	if (ret) {
103 		/* A server might (incorrectly) reply with QDCOUNT=0. */
104 		kr_assert(owner == NULL);
105 		return;
106 	}
107 	key = key_exact_type_maypkt(k, pkt_type);
108 
109 	/* For now we stash the full packet byte-exactly as it came from upstream. */
110 	const uint16_t pkt_size = pkt->size;
111 	knot_db_val_t val_new_entry = {
112 		.data = NULL,
113 		.len = offsetof(struct entry_h, data) + sizeof(pkt_size) + pkt->size,
114 	};
115 	/* Prepare raw memory for the new entry and fill it. */
116 	struct kr_cache *cache = &req->ctx->cache;
117 	ret = entry_h_splice(&val_new_entry, rank, key, k->type, pkt_type,
118 				owner, qry, cache, qry->timestamp.tv_sec);
119 	if (ret || kr_fails_assert(val_new_entry.data)) return; /* some aren't really errors */
120 	struct entry_h *eh = val_new_entry.data;
121 	memset(eh, 0, offsetof(struct entry_h, data));
122 	eh->time = qry->timestamp.tv_sec;
123 	eh->ttl  = MAX(MIN(packet_ttl(pkt, is_negative), cache->ttl_max), cache->ttl_min);
124 	eh->rank = rank;
125 	eh->is_packet = true;
126 	eh->has_optout = qf->DNSSEC_OPTOUT;
127 	memcpy(eh->data, &pkt_size, sizeof(pkt_size));
128 	memcpy(eh->data + sizeof(pkt_size), pkt->wire, pkt_size);
129 
130 	WITH_VERBOSE(qry) {
131 		auto_free char *type_str = kr_rrtype_text(pkt_type),
132 			*owner_str = kr_dname_text(owner);
133 		VERBOSE_MSG(qry, "=> stashed packet: rank 0%.2o, TTL %d, "
134 				"%s %s (%d B)\n",
135 				eh->rank, eh->ttl,
136 				type_str, owner_str, (int)val_new_entry.len);
137 	}
138 }
139 
140 
answer_from_pkt(kr_layer_t * ctx,knot_pkt_t * pkt,uint16_t type,const struct entry_h * eh,const void * eh_bound,uint32_t new_ttl)141 int answer_from_pkt(kr_layer_t *ctx, knot_pkt_t *pkt, uint16_t type,
142 		const struct entry_h *eh, const void *eh_bound, uint32_t new_ttl)
143 {
144 	struct kr_request *req = ctx->req;
145 	struct kr_query *qry = req->current_query;
146 
147 	const uint16_t msgid = knot_wire_get_id(pkt->wire);
148 
149 	/* Ensure the wire buffer is large enough.  Strategy: fit and at least double. */
150 	uint16_t pkt_len;
151 	memcpy(&pkt_len, eh->data, sizeof(pkt_len));
152 	if (pkt_len > pkt->max_size) {
153 		pkt->max_size = MIN(KNOT_WIRE_MAX_PKTSIZE,
154 				    MAX(pkt->max_size * 2, pkt_len));
155 		mm_free(&ctx->req->pool, pkt->wire); /* no-op, but... */
156 		pkt->wire = mm_alloc(&ctx->req->pool, pkt->max_size);
157 		pkt->compr.wire = pkt->wire;
158 		/* TODO: ^^ nicer way how to replace knot_pkt_t::wire ? */
159 	}
160 	kr_require(pkt->max_size >= pkt_len);
161 
162 	/* Copy answer and reparse it, but keep the original message id. */
163 	knot_pkt_clear(pkt);
164 	memcpy(pkt->wire, eh->data + 2, pkt_len);
165 	pkt->size = pkt_len;
166 	int ret = knot_pkt_parse(pkt, 0);
167 	if (ret == KNOT_EFEWDATA || ret == KNOT_EMALF) {
168 		return kr_error(ENOENT);
169 		/* LATER(opt): try harder to avoid stashing such packets */
170 	}
171 	if (kr_fails_assert(ret == KNOT_EOK))
172 		return kr_error(ret);
173 	knot_wire_set_id(pkt->wire, msgid);
174 
175 	/* Add rank into the additional field. */
176 	for (size_t i = 0; i < pkt->rrset_count; ++i) {
177 		kr_assert(!pkt->rr[i].additional);
178 		uint8_t *rr_rank = mm_alloc(&pkt->mm, sizeof(*rr_rank));
179 		if (!rr_rank) {
180 			return kr_error(ENOMEM);
181 		}
182 		*rr_rank = eh->rank;
183 		pkt->rr[i].additional = rr_rank;
184 	}
185 
186 	/* Adjust TTL in each record. */
187 	const uint32_t drift = eh->ttl - new_ttl;
188 	for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
189 		const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
190 		for (unsigned k = 0; k < sec->count; ++k) {
191 			knot_rrset_t *rrs = // vv FIXME??
192 				/*const-cast*/(knot_rrset_t *)knot_pkt_rr(sec, k);
193 			/* We need to be careful: due to enforcing minimum TTL
194 			 * on packet, some records may be below that value.
195 			 * We keep those records at TTL 0. */
196 			if (rrs->ttl >= drift) {
197 				rrs->ttl -= drift;
198 			} else {
199 				rrs->ttl = 0;
200 			}
201 		}
202 	}
203 
204 	/* Finishing touches. TODO: perhaps factor out */
205 	struct kr_qflags * const qf = &qry->flags;
206 	qf->EXPIRING = is_expiring(eh->ttl, new_ttl);
207 	qf->CACHED = true;
208 	qf->NO_MINIMIZE = true;
209 	qf->DNSSEC_INSECURE = kr_rank_test(eh->rank, KR_RANK_INSECURE);
210 	qf->DNSSEC_BOGUS = kr_rank_test(eh->rank, KR_RANK_BOGUS);
211 	if (qf->DNSSEC_INSECURE || qf->DNSSEC_BOGUS) {
212 		qf->DNSSEC_WANT = false;
213 	}
214 	qf->DNSSEC_OPTOUT = eh->has_optout;
215 	VERBOSE_MSG(qry, "=> satisfied by exact packet: rank 0%.2o, new TTL %d\n",
216 			eh->rank, new_ttl);
217 	return kr_ok();
218 }
219 
220