1 /*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 
19 #include "knot/journal/serialization.h"
20 #include "knot/zone/zone-tree.h"
21 #include "libknot/libknot.h"
22 
23 #define SERIALIZE_RRSET_INIT (-1)
24 #define SERIALIZE_RRSET_DONE ((1L<<16)+1)
25 
26 typedef enum {
27 	PHASE_ZONE_SOA,
28 	PHASE_ZONE_NODES,
29 	PHASE_ZONE_NSEC3,
30 	PHASE_SOA_1,
31 	PHASE_REM,
32 	PHASE_SOA_2,
33 	PHASE_ADD,
34 	PHASE_END,
35 } serialize_phase_t;
36 
37 #define RRSET_BUF_MAXSIZE 256
38 
39 struct serialize_ctx {
40 	const zone_contents_t *z;
41 	zone_tree_it_t zit;
42 	zone_node_t *n;
43 	uint16_t node_pos;
44 
45 	const changeset_t *ch;
46 	changeset_iter_t it;
47 	serialize_phase_t changeset_phase;
48 	long rrset_phase;
49 	knot_rrset_t rrset_buf[RRSET_BUF_MAXSIZE];
50 	size_t rrset_buf_size;
51 };
52 
serialize_init(const changeset_t * ch)53 serialize_ctx_t *serialize_init(const changeset_t *ch)
54 {
55 	serialize_ctx_t *ctx = calloc(1, sizeof(*ctx));
56 	if (ctx == NULL) {
57 		return NULL;
58 	}
59 
60 	ctx->ch = ch;
61 	ctx->changeset_phase = ch->soa_from != NULL ? PHASE_SOA_1 : PHASE_SOA_2;
62 	ctx->rrset_phase = SERIALIZE_RRSET_INIT;
63 	ctx->rrset_buf_size = 0;
64 
65 	return ctx;
66 }
67 
serialize_zone_init(const zone_contents_t * z)68 serialize_ctx_t *serialize_zone_init(const zone_contents_t *z)
69 {
70 	serialize_ctx_t *ctx = calloc(1, sizeof(*ctx));
71 	if (ctx == NULL) {
72 		return NULL;
73 	}
74 
75 	ctx->z = z;
76 	ctx->changeset_phase = PHASE_ZONE_SOA;
77 	ctx->rrset_phase = SERIALIZE_RRSET_INIT;
78 	ctx->rrset_buf_size = 0;
79 
80 	return ctx;
81 }
82 
get_next_rrset(serialize_ctx_t * ctx)83 static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx)
84 {
85 	knot_rrset_t res;
86 	knot_rrset_init_empty(&res);
87 	switch (ctx->changeset_phase) {
88 	case PHASE_ZONE_SOA:
89 		zone_tree_it_begin(ctx->z->nodes, &ctx->zit);
90 		ctx->changeset_phase = PHASE_ZONE_NODES;
91 		return node_rrset(ctx->z->apex, KNOT_RRTYPE_SOA);
92 	case PHASE_ZONE_NODES:
93 	case PHASE_ZONE_NSEC3:
94 		while (ctx->n == NULL || ctx->node_pos >= ctx->n->rrset_count) {
95 			if (zone_tree_it_finished(&ctx->zit)) {
96 				zone_tree_it_free(&ctx->zit);
97 				if (ctx->changeset_phase == PHASE_ZONE_NSEC3 || zone_tree_is_empty(ctx->z->nsec3_nodes)) {
98 					ctx->changeset_phase = PHASE_END;
99 					return res;
100 				} else {
101 					zone_tree_it_begin(ctx->z->nsec3_nodes, &ctx->zit);
102 					ctx->changeset_phase = PHASE_ZONE_NSEC3;
103 				}
104 			}
105 			ctx->n = zone_tree_it_val(&ctx->zit);
106 			zone_tree_it_next(&ctx->zit);
107 			ctx->node_pos = 0;
108 		}
109 		res = node_rrset_at(ctx->n, ctx->node_pos++);
110 		if (ctx->n == ctx->z->apex && res.type == KNOT_RRTYPE_SOA) {
111 			return get_next_rrset(ctx);
112 		}
113 		return res;
114 	case PHASE_SOA_1:
115 		changeset_iter_rem(&ctx->it, ctx->ch);
116 		ctx->changeset_phase = PHASE_REM;
117 		return *ctx->ch->soa_from;
118 	case PHASE_REM:
119 		res = changeset_iter_next(&ctx->it);
120 		if (knot_rrset_empty(&res)) {
121 			changeset_iter_clear(&ctx->it);
122 			changeset_iter_add(&ctx->it, ctx->ch);
123 			ctx->changeset_phase = PHASE_ADD;
124 			return *ctx->ch->soa_to;
125 		}
126 		return res;
127 	case PHASE_SOA_2:
128 		if (ctx->it.node != NULL) {
129 			changeset_iter_clear(&ctx->it);
130 		}
131 		changeset_iter_add(&ctx->it, ctx->ch);
132 		ctx->changeset_phase = PHASE_ADD;
133 		return *ctx->ch->soa_to;
134 	case PHASE_ADD:
135 		res = changeset_iter_next(&ctx->it);
136 		if (knot_rrset_empty(&res)) {
137 			changeset_iter_clear(&ctx->it);
138 			ctx->changeset_phase = PHASE_END;
139 		}
140 		return res;
141 	default:
142 		return res;
143 	}
144 }
145 
serialize_prepare(serialize_ctx_t * ctx,size_t thresh_size,size_t max_size,size_t * realsize)146 void serialize_prepare(serialize_ctx_t *ctx, size_t thresh_size,
147                        size_t max_size, size_t *realsize)
148 {
149 	*realsize = 0;
150 
151 	// check if we are in middle of a rrset
152 	if (ctx->rrset_buf_size > 0) {
153 		ctx->rrset_buf[0] = ctx->rrset_buf[ctx->rrset_buf_size - 1];
154 		ctx->rrset_buf_size = 1;
155 	} else {
156 		ctx->rrset_buf[0] = get_next_rrset(ctx);
157 		if (ctx->changeset_phase == PHASE_END) {
158 			ctx->rrset_buf_size = 0;
159 			return;
160 		}
161 		ctx->rrset_buf_size = 1;
162 	}
163 
164 	size_t candidate = 0;
165 	long tmp_phase = ctx->rrset_phase;
166 	while (1) {
167 		if (tmp_phase >= ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs.count) {
168 			if (ctx->rrset_buf_size >= RRSET_BUF_MAXSIZE) {
169 				return;
170 			}
171 			ctx->rrset_buf[ctx->rrset_buf_size++] = get_next_rrset(ctx);
172 			if (ctx->changeset_phase == PHASE_END) {
173 				ctx->rrset_buf_size--;
174 				return;
175 			}
176 			tmp_phase = SERIALIZE_RRSET_INIT;
177 		}
178 		if (tmp_phase == SERIALIZE_RRSET_INIT) {
179 			candidate += 3 * sizeof(uint16_t) +
180 			             knot_dname_size(ctx->rrset_buf[ctx->rrset_buf_size - 1].owner);
181 		} else {
182 			candidate += sizeof(uint32_t) + sizeof(uint16_t) +
183 			             knot_rdataset_at(&ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs, tmp_phase)->len;
184 		}
185 		if (candidate > max_size) {
186 			return;
187 		}
188 		*realsize = candidate;
189 		if (candidate >= thresh_size) {
190 			return;
191 		}
192 		tmp_phase++;
193 	}
194 }
195 
serialize_chunk(serialize_ctx_t * ctx,uint8_t * dst_chunk,size_t chunk_size)196 void serialize_chunk(serialize_ctx_t *ctx, uint8_t *dst_chunk, size_t chunk_size)
197 {
198 	wire_ctx_t wire = wire_ctx_init(dst_chunk, chunk_size);
199 
200 	for (size_t i = 0; ; ) {
201 		if (ctx->rrset_phase >= ctx->rrset_buf[i].rrs.count) {
202 			if (++i >= ctx->rrset_buf_size) {
203 				break;
204 			}
205 			ctx->rrset_phase = SERIALIZE_RRSET_INIT;
206 		}
207 		if (ctx->rrset_phase == SERIALIZE_RRSET_INIT) {
208 			int size = knot_dname_to_wire(wire.position, ctx->rrset_buf[i].owner,
209 			                              wire_ctx_available(&wire));
210 			if (size < 0 || wire_ctx_available(&wire) < size + 3 * sizeof(uint16_t)) {
211 				break;
212 			}
213 			wire_ctx_skip(&wire, size);
214 			wire_ctx_write_u16(&wire, ctx->rrset_buf[i].type);
215 			wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rclass);
216 			wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rrs.count);
217 		} else {
218 			const knot_rdata_t *rr = knot_rdataset_at(&ctx->rrset_buf[i].rrs,
219 			                                          ctx->rrset_phase);
220 			assert(rr);
221 			uint16_t rdlen = rr->len;
222 			if (wire_ctx_available(&wire) < sizeof(uint32_t) + sizeof(uint16_t) + rdlen) {
223 				break;
224 			}
225 			// Compatibility, but one TTL per rrset would be enough.
226 			wire_ctx_write_u32(&wire, ctx->rrset_buf[i].ttl);
227 			wire_ctx_write_u16(&wire, rdlen);
228 			wire_ctx_write(&wire, rr->data, rdlen);
229 		}
230 		ctx->rrset_phase++;
231 	}
232 	assert(wire.error == KNOT_EOK);
233 }
234 
serialize_unfinished(serialize_ctx_t * ctx)235 bool serialize_unfinished(serialize_ctx_t *ctx)
236 {
237 	return ctx->changeset_phase < PHASE_END;
238 }
239 
serialize_deinit(serialize_ctx_t * ctx)240 void serialize_deinit(serialize_ctx_t *ctx)
241 {
242 	if (ctx->it.node != NULL) {
243 		changeset_iter_clear(&ctx->it);
244 	}
245 	if (ctx->zit.tree != NULL) {
246 		zone_tree_it_free(&ctx->zit);
247 	}
248 	free(ctx);
249 }
250 
rrset_binary_size(const knot_rrset_t * rrset)251 static uint64_t rrset_binary_size(const knot_rrset_t *rrset)
252 {
253 	if (rrset == NULL || rrset->rrs.count == 0) {
254 		return 0;
255 	}
256 
257 	// Owner size + type + class + RR count.
258 	uint64_t size = knot_dname_size(rrset->owner) + 3 * sizeof(uint16_t);
259 
260 	// RRs.
261 	knot_rdata_t *rr = rrset->rrs.rdata;
262 	for (uint16_t i = 0; i < rrset->rrs.count; i++) {
263 		// TTL + RR size + RR.
264 		size += sizeof(uint32_t) + sizeof(uint16_t) + rr->len;
265 		rr = knot_rdataset_next(rr);
266 	}
267 
268 	return size;
269 }
270 
changeset_serialized_size(const changeset_t * ch)271 size_t changeset_serialized_size(const changeset_t *ch)
272 {
273 	if (ch == NULL) {
274 		return 0;
275 	}
276 
277 	size_t soa_from_size = rrset_binary_size(ch->soa_from);
278 	size_t soa_to_size = rrset_binary_size(ch->soa_to);
279 
280 	changeset_iter_t it;
281 	if (ch->remove == NULL) {
282 		changeset_iter_add(&it, ch);
283 	} else {
284 		changeset_iter_all(&it, ch);
285 	}
286 
287 	size_t change_size = 0;
288 	knot_rrset_t rrset = changeset_iter_next(&it);
289 	while (!knot_rrset_empty(&rrset)) {
290 		change_size += rrset_binary_size(&rrset);
291 		rrset = changeset_iter_next(&it);
292 	}
293 
294 	changeset_iter_clear(&it);
295 
296 	return soa_from_size + soa_to_size + change_size;
297 }
298 
serialize_rrset(wire_ctx_t * wire,const knot_rrset_t * rrset)299 int serialize_rrset(wire_ctx_t *wire, const knot_rrset_t *rrset)
300 {
301 	assert(wire != NULL && rrset != NULL);
302 
303 	// write owner, type, class, rrcnt
304 	int size = knot_dname_to_wire(wire->position, rrset->owner,
305 				      wire_ctx_available(wire));
306 	if (size < 0 || wire_ctx_available(wire) < size + 3 * sizeof(uint16_t)) {
307 			assert(0);
308 	}
309 	wire_ctx_skip(wire, size);
310 	wire_ctx_write_u16(wire, rrset->type);
311 	wire_ctx_write_u16(wire, rrset->rclass);
312 	wire_ctx_write_u16(wire, rrset->rrs.count);
313 
314 	for (size_t phase = 0; phase < rrset->rrs.count; phase++) {
315 		const knot_rdata_t *rr = knot_rdataset_at(&rrset->rrs, phase);
316 		assert(rr);
317 		uint16_t rdlen = rr->len;
318 		if (wire_ctx_available(wire) < sizeof(uint32_t) + sizeof(uint16_t) + rdlen) {
319 			assert(0);
320 		}
321 		wire_ctx_write_u32(wire, rrset->ttl);
322 		wire_ctx_write_u16(wire, rdlen);
323 		wire_ctx_write(wire, rr->data, rdlen);
324 		assert(wire->error == KNOT_EOK);
325 	}
326 
327 	return KNOT_EOK;
328 }
329 
deserialize_rrset(wire_ctx_t * wire,knot_rrset_t * rrset)330 int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset)
331 {
332 	assert(wire != NULL && rrset != NULL);
333 
334 	// Read owner, rtype, rclass and RR count.
335 	int size = knot_dname_size(wire->position);
336 	if (size < 0) {
337 		assert(0);
338 	}
339 	knot_dname_t *owner = knot_dname_copy(wire->position, NULL);
340 	if (owner == NULL || wire_ctx_available(wire) < size + 3 * sizeof(uint16_t)) {
341 		knot_dname_free(owner, NULL);
342 		return KNOT_EMALF;
343 	}
344 	wire_ctx_skip(wire, size);
345 	uint16_t type = wire_ctx_read_u16(wire);
346 	uint16_t rclass = wire_ctx_read_u16(wire);
347 	uint16_t rrcount = wire_ctx_read_u16(wire);
348 	if (wire->error != KNOT_EOK) {
349 		knot_dname_free(owner, NULL);
350 		return wire->error;
351 	}
352 	if (rrset->owner != NULL) {
353 		if (knot_dname_cmp(owner, rrset->owner) != 0) {
354 			knot_dname_free(owner, NULL);
355 			return KNOT_ESEMCHECK;
356 		}
357 		knot_rrset_clear(rrset, NULL);
358 	}
359 	knot_rrset_init(rrset, owner, type, rclass, 0);
360 
361 	for (size_t phase = 0; phase < rrcount && wire_ctx_available(wire) > 0; phase++) {
362 		uint32_t ttl = wire_ctx_read_u32(wire);
363 		uint32_t rdata_size = wire_ctx_read_u16(wire);
364 		if (phase == 0) {
365 			rrset->ttl = ttl;
366 		}
367 		if (wire->error != KNOT_EOK ||
368 		    wire_ctx_available(wire) < rdata_size ||
369 		    knot_rrset_add_rdata(rrset, wire->position, rdata_size,
370 					 NULL) != KNOT_EOK) {
371 			knot_rrset_clear(rrset, NULL);
372 			return KNOT_EMALF;
373 		}
374 		wire_ctx_skip(wire, rdata_size);
375 		assert(wire->error == KNOT_EOK);
376 	}
377 
378 	return KNOT_EOK;
379 }
380 
rrset_serialized_size(const knot_rrset_t * rrset)381 size_t rrset_serialized_size(const knot_rrset_t *rrset)
382 {
383 	if (rrset == NULL) {
384 		return 0;
385 	}
386 
387 	// Owner size + type + class + RR count.
388 	size_t size = knot_dname_size(rrset->owner) + 3 * sizeof(uint16_t);
389 
390 	for (uint16_t i = 0; i < rrset->rrs.count; i++) {
391 		const knot_rdata_t *rr = knot_rdataset_at(&rrset->rrs, i);
392 		assert(rr);
393 		// TTL + RR size + RR.
394 		size += sizeof(uint32_t) + sizeof(uint16_t) + rr->len;
395 	}
396 
397 	return size;
398 }
399