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