1 /*
2 * ixfrcreate.c -- generating IXFR differences from zone files.
3 *
4 * Copyright (c) 2021, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9
10 #include "config.h"
11 #include <stdio.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include "ixfrcreate.h"
15 #include "namedb.h"
16 #include "ixfr.h"
17 #include "options.h"
18
19 /* spool a uint16_t to file */
spool_u16(FILE * out,uint16_t val)20 static int spool_u16(FILE* out, uint16_t val)
21 {
22 if(!fwrite(&val, sizeof(val), 1, out)) {
23 return 0;
24 }
25 return 1;
26 }
27
28 /* spool a uint32_t to file */
spool_u32(FILE * out,uint32_t val)29 static int spool_u32(FILE* out, uint32_t val)
30 {
31 if(!fwrite(&val, sizeof(val), 1, out)) {
32 return 0;
33 }
34 return 1;
35 }
36
37 /* spool dname to file */
spool_dname(FILE * out,dname_type * dname)38 static int spool_dname(FILE* out, dname_type* dname)
39 {
40 uint16_t namelen = dname->name_size;
41 if(!fwrite(&namelen, sizeof(namelen), 1, out)) {
42 return 0;
43 }
44 if(!fwrite(dname_name(dname), namelen, 1, out)) {
45 return 0;
46 }
47 return 1;
48 }
49
50 /* calculate the rdatalen of an RR */
rr_rdatalen_uncompressed(rr_type * rr)51 static size_t rr_rdatalen_uncompressed(rr_type* rr)
52 {
53 int i;
54 size_t rdlen_uncompressed = 0;
55 for(i=0; i<rr->rdata_count; i++) {
56 if(rdata_atom_is_domain(rr->type, i)) {
57 rdlen_uncompressed += domain_dname(rr->rdatas[i].domain)
58 ->name_size;
59 } else {
60 rdlen_uncompressed += rr->rdatas[i].data[0];
61 }
62 }
63 return rdlen_uncompressed;
64 }
65
66 /* spool the data for one rr into the file */
spool_rr_data(FILE * out,rr_type * rr)67 static int spool_rr_data(FILE* out, rr_type* rr)
68 {
69 int i;
70 uint16_t rdlen;
71 if(!spool_u32(out, rr->ttl))
72 return 0;
73 rdlen = rr_rdatalen_uncompressed(rr);
74 if(!spool_u16(out, rdlen))
75 return 0;
76 for(i=0; i<rr->rdata_count; i++) {
77 if(rdata_atom_is_domain(rr->type, i)) {
78 if(!fwrite(dname_name(domain_dname(
79 rr->rdatas[i].domain)), domain_dname(
80 rr->rdatas[i].domain)->name_size, 1, out))
81 return 0;
82 } else {
83 if(!fwrite(&rr->rdatas[i].data[1],
84 rr->rdatas[i].data[0], 1, out))
85 return 0;
86 }
87 }
88 return 1;
89 }
90
91 /* spool one rrset to file */
spool_rrset(FILE * out,rrset_type * rrset)92 static int spool_rrset(FILE* out, rrset_type* rrset)
93 {
94 int i;
95 if(rrset->rr_count == 0)
96 return 1;
97 if(!spool_u16(out, rrset->rrs[0].type))
98 return 0;
99 if(!spool_u16(out, rrset->rrs[0].klass))
100 return 0;
101 if(!spool_u16(out, rrset->rr_count))
102 return 0;
103 for(i=0; i<rrset->rr_count; i++) {
104 if(!spool_rr_data(out, &rrset->rrs[i]))
105 return 0;
106 }
107 return 1;
108 }
109
110 /* spool rrsets to file */
spool_rrsets(FILE * out,rrset_type * rrsets,struct zone * zone)111 static int spool_rrsets(FILE* out, rrset_type* rrsets, struct zone* zone)
112 {
113 rrset_type* s;
114 for(s=rrsets; s; s=s->next) {
115 if(s->zone != zone)
116 continue;
117 if(!spool_rrset(out, s)) {
118 return 0;
119 }
120 }
121 return 1;
122 }
123
124 /* count number of rrsets for a domain */
domain_count_rrsets(domain_type * domain,zone_type * zone)125 static size_t domain_count_rrsets(domain_type* domain, zone_type* zone)
126 {
127 rrset_type* s;
128 size_t count = 0;
129 for(s=domain->rrsets; s; s=s->next) {
130 if(s->zone == zone)
131 count++;
132 }
133 return count;
134 }
135
136 /* spool the domain names to file, each one in turn. end with enddelimiter */
spool_domains(FILE * out,struct zone * zone)137 static int spool_domains(FILE* out, struct zone* zone)
138 {
139 domain_type* domain;
140 for(domain = zone->apex; domain && domain_is_subdomain(domain,
141 zone->apex); domain = domain_next(domain)) {
142 uint32_t count = domain_count_rrsets(domain, zone);
143 if(count == 0)
144 continue;
145 /* write the name */
146 if(!spool_dname(out, domain_dname(domain)))
147 return 0;
148 if(!spool_u32(out, count))
149 return 0;
150 /* write the rrsets */
151 if(!spool_rrsets(out, domain->rrsets, zone))
152 return 0;
153 }
154 /* the end delimiter is a 0 length. domain names are not zero length */
155 if(!spool_u16(out, 0))
156 return 0;
157 return 1;
158 }
159
160 /* spool the namedb zone to the file. print error on failure. */
spool_zone_to_file(struct zone * zone,char * file_name,uint32_t serial)161 static int spool_zone_to_file(struct zone* zone, char* file_name,
162 uint32_t serial)
163 {
164 FILE* out;
165 out = fopen(file_name, "w");
166 if(!out) {
167 log_msg(LOG_ERR, "could not open %s for writing: %s",
168 file_name, strerror(errno));
169 return 0;
170 }
171 if(!spool_dname(out, domain_dname(zone->apex))) {
172 log_msg(LOG_ERR, "could not write %s: %s",
173 file_name, strerror(errno));
174 fclose(out);
175 return 0;
176 }
177 if(!spool_u32(out, serial)) {
178 log_msg(LOG_ERR, "could not write %s: %s",
179 file_name, strerror(errno));
180 fclose(out);
181 return 0;
182 }
183 if(!spool_domains(out, zone)) {
184 log_msg(LOG_ERR, "could not write %s: %s",
185 file_name, strerror(errno));
186 fclose(out);
187 return 0;
188 }
189 fclose(out);
190 return 1;
191 }
192
193 /* create ixfr spool file name */
create_ixfr_spool_name(struct ixfr_create * ixfrcr,const char * zfile)194 static int create_ixfr_spool_name(struct ixfr_create* ixfrcr,
195 const char* zfile)
196 {
197 char buf[1024];
198 snprintf(buf, sizeof(buf), "%s.spoolzone.%u", zfile,
199 (unsigned)getpid());
200 ixfrcr->file_name = strdup(buf);
201 if(!ixfrcr->file_name)
202 return 0;
203 return 1;
204 }
205
206 /* start ixfr creation */
ixfr_create_start(struct zone * zone,const char * zfile,uint64_t ixfr_size,int errorcmdline)207 struct ixfr_create* ixfr_create_start(struct zone* zone, const char* zfile,
208 uint64_t ixfr_size, int errorcmdline)
209 {
210 struct ixfr_create* ixfrcr = (struct ixfr_create*)calloc(1,
211 sizeof(*ixfrcr));
212 if(!ixfrcr) {
213 log_msg(LOG_ERR, "malloc failure");
214 return NULL;
215 }
216 ixfrcr->zone_name_len = domain_dname(zone->apex)->name_size;
217 ixfrcr->zone_name = (uint8_t*)malloc(ixfrcr->zone_name_len);
218 if(!ixfrcr->zone_name) {
219 free(ixfrcr);
220 log_msg(LOG_ERR, "malloc failure");
221 return NULL;
222 }
223 memmove(ixfrcr->zone_name, dname_name(domain_dname(zone->apex)),
224 ixfrcr->zone_name_len);
225
226 if(!create_ixfr_spool_name(ixfrcr, zfile)) {
227 ixfr_create_free(ixfrcr);
228 log_msg(LOG_ERR, "malloc failure");
229 return NULL;
230 }
231 ixfrcr->old_serial = zone_get_current_serial(zone);
232 if(!spool_zone_to_file(zone, ixfrcr->file_name, ixfrcr->old_serial)) {
233 ixfr_create_free(ixfrcr);
234 return NULL;
235 }
236 if(zone->opts && zone->opts->pattern)
237 ixfrcr->max_size = (size_t)zone->opts->pattern->ixfr_size;
238 else ixfrcr->max_size = (size_t)ixfr_size;
239 ixfrcr->errorcmdline = errorcmdline;
240 return ixfrcr;
241 }
242
243 /* free ixfr create */
ixfr_create_free(struct ixfr_create * ixfrcr)244 void ixfr_create_free(struct ixfr_create* ixfrcr)
245 {
246 if(!ixfrcr)
247 return;
248 free(ixfrcr->file_name);
249 free(ixfrcr->zone_name);
250 free(ixfrcr);
251 }
252
253 /* read uint16_t from spool */
read_spool_u16(FILE * spool,uint16_t * val)254 static int read_spool_u16(FILE* spool, uint16_t* val)
255 {
256 if(fread(val, sizeof(*val), 1, spool) < 1)
257 return 0;
258 return 1;
259 }
260
261 /* read uint32_t from spool */
read_spool_u32(FILE * spool,uint32_t * val)262 static int read_spool_u32(FILE* spool, uint32_t* val)
263 {
264 if(fread(val, sizeof(*val), 1, spool) < 1)
265 return 0;
266 return 1;
267 }
268
269 /* read dname from spool */
read_spool_dname(FILE * spool,uint8_t * buf,size_t buflen,size_t * dname_len)270 static int read_spool_dname(FILE* spool, uint8_t* buf, size_t buflen,
271 size_t* dname_len)
272 {
273 uint16_t len;
274 if(fread(&len, sizeof(len), 1, spool) < 1)
275 return 0;
276 if(len > buflen) {
277 log_msg(LOG_ERR, "dname too long");
278 return 0;
279 }
280 if(len > 0) {
281 if(fread(buf, len, 1, spool) < 1)
282 return 0;
283 }
284 *dname_len = len;
285 return 1;
286 }
287
288 /* read and check the spool file header */
read_spool_header(FILE * spool,struct ixfr_create * ixfrcr)289 static int read_spool_header(FILE* spool, struct ixfr_create* ixfrcr)
290 {
291 uint8_t dname[MAXDOMAINLEN+1];
292 size_t dname_len;
293 uint32_t serial;
294 /* read apex */
295 if(!read_spool_dname(spool, dname, sizeof(dname), &dname_len)) {
296 log_msg(LOG_ERR, "error reading file %s: %s",
297 ixfrcr->file_name, strerror(errno));
298 return 0;
299 }
300 /* read serial */
301 if(!read_spool_u32(spool, &serial)) {
302 log_msg(LOG_ERR, "error reading file %s: %s",
303 ixfrcr->file_name, strerror(errno));
304 return 0;
305 }
306
307 /* check */
308 if(ixfrcr->zone_name_len != dname_len ||
309 memcmp(ixfrcr->zone_name, dname, ixfrcr->zone_name_len) != 0) {
310 log_msg(LOG_ERR, "error file %s does not contain the correct zone apex",
311 ixfrcr->file_name);
312 return 0;
313 }
314 if(ixfrcr->old_serial != serial) {
315 log_msg(LOG_ERR, "error file %s does not contain the correct zone serial",
316 ixfrcr->file_name);
317 return 0;
318 }
319 return 1;
320 }
321
322 /* store the old soa record when we encounter it on the spool */
process_store_oldsoa(struct ixfr_store * store,uint8_t * dname,size_t dname_len,uint16_t tp,uint16_t kl,uint32_t ttl,uint8_t * buf,uint16_t rdlen)323 static int process_store_oldsoa(struct ixfr_store* store, uint8_t* dname,
324 size_t dname_len, uint16_t tp, uint16_t kl, uint32_t ttl, uint8_t* buf,
325 uint16_t rdlen)
326 {
327 if(store->data->oldsoa) {
328 log_msg(LOG_ERR, "error spool contains multiple SOA records");
329 return 0;
330 }
331 if(!ixfr_store_oldsoa_uncompressed(store, dname, dname_len, tp, kl,
332 ttl, buf, rdlen)) {
333 log_msg(LOG_ERR, "out of memory");
334 return 0;
335 }
336 return 1;
337 }
338
339 /* see if rdata matches, true if equal */
rdata_match(struct rr * rr,uint8_t * rdata,uint16_t rdlen)340 static int rdata_match(struct rr* rr, uint8_t* rdata, uint16_t rdlen)
341 {
342 size_t rdpos = 0;
343 int i;
344 for(i=0; i<rr->rdata_count; i++) {
345 if(rdata_atom_is_domain(rr->type, i)) {
346 if(rdpos + domain_dname(rr->rdatas[i].domain)->name_size
347 > rdlen)
348 return 0;
349 if(memcmp(rdata+rdpos,
350 dname_name(domain_dname(rr->rdatas[i].domain)),
351 domain_dname(rr->rdatas[i].domain)->name_size)
352 != 0)
353 return 0;
354 rdpos += domain_dname(rr->rdatas[i].domain)->name_size;
355 } else {
356 if(rdpos + rr->rdatas[i].data[0] > rdlen)
357 return 0;
358 if(memcmp(rdata+rdpos, &rr->rdatas[i].data[1],
359 rr->rdatas[i].data[0]) != 0)
360 return 0;
361 rdpos += rr->rdatas[i].data[0];
362 }
363 }
364 if(rdpos != rdlen)
365 return 0;
366 return 1;
367 }
368
369 /* find an rdata in an rrset, true if found and sets index found */
rrset_find_rdata(struct rrset * rrset,uint32_t ttl,uint8_t * rdata,uint16_t rdlen,uint16_t * index)370 static int rrset_find_rdata(struct rrset* rrset, uint32_t ttl, uint8_t* rdata,
371 uint16_t rdlen, uint16_t* index)
372 {
373 int i;
374 for(i=0; i<rrset->rr_count; i++) {
375 if(rrset->rrs[i].ttl != ttl)
376 continue;
377 if(rdata_match(&rrset->rrs[i], rdata, rdlen)) {
378 *index = i;
379 return 1;
380 }
381 }
382 return 0;
383 }
384
385 /* sort comparison for uint16 elements */
sort_uint16(const void * x,const void * y)386 static int sort_uint16(const void* x, const void* y)
387 {
388 const uint16_t* ax = (const uint16_t*)x;
389 const uint16_t* ay = (const uint16_t*)y;
390 if(*ax < *ay)
391 return -1;
392 if(*ax > *ay)
393 return 1;
394 return 0;
395 }
396
397 /* spool read an rrset, it is a deleted RRset */
process_diff_rrset(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct domain * domain,uint16_t tp,uint16_t kl,uint16_t rrcount,struct rrset * rrset)398 static int process_diff_rrset(FILE* spool, struct ixfr_create* ixfrcr,
399 struct ixfr_store* store, struct domain* domain,
400 uint16_t tp, uint16_t kl, uint16_t rrcount, struct rrset* rrset)
401 {
402 /* read RRs from file and see if they are added, deleted or in both */
403 uint8_t buf[MAX_RDLENGTH];
404 uint16_t marked[65536];
405 size_t marked_num = 0, atmarked;
406 int i;
407 for(i=0; i<rrcount; i++) {
408 uint16_t rdlen, index;
409 uint32_t ttl;
410 if(!read_spool_u32(spool, &ttl) ||
411 !read_spool_u16(spool, &rdlen)) {
412 log_msg(LOG_ERR, "error reading file %s: %s",
413 ixfrcr->file_name, strerror(errno));
414 return 0;
415 }
416 /* because rdlen is uint16_t always smaller than sizeof(buf)*/
417 #pragma GCC diagnostic push
418 #pragma GCC diagnostic ignored "-Wtype-limits"
419 assert(rdlen <= sizeof(buf));
420 #pragma GCC diagnostic pop
421 if(fread(buf, rdlen, 1, spool) < 1) {
422 log_msg(LOG_ERR, "error reading file %s: %s",
423 ixfrcr->file_name, strerror(errno));
424 return 0;
425 }
426 if(tp == TYPE_SOA) {
427 if(!process_store_oldsoa(store,
428 (void*)dname_name(domain_dname(domain)),
429 domain_dname(domain)->name_size, tp, kl, ttl,
430 buf, rdlen))
431 return 0;
432 }
433 /* see if the rr is in the RRset */
434 if(rrset_find_rdata(rrset, ttl, buf, rdlen, &index)) {
435 /* it is in both, mark it */
436 marked[marked_num++] = index;
437 } else {
438 /* not in new rrset, but only on spool, it is
439 * a deleted RR */
440 if(!ixfr_store_delrr_uncompressed(store,
441 (void*)dname_name(domain_dname(domain)),
442 domain_dname(domain)->name_size,
443 tp, kl, ttl, buf, rdlen)) {
444 log_msg(LOG_ERR, "out of memory");
445 return 0;
446 }
447 }
448 }
449 /* now that we are done, see if RRs in the rrset are not marked,
450 * and thus are new rrs that are added */
451 qsort(marked, marked_num, sizeof(marked[0]), &sort_uint16);
452 atmarked = 0;
453 for(i=0; i<rrset->rr_count; i++) {
454 if(atmarked < marked_num && marked[atmarked] == i) {
455 /* the item is in the marked list, skip it */
456 atmarked++;
457 continue;
458 }
459 /* not in the marked list, the RR is added */
460 if(!ixfr_store_addrr_rdatas(store, domain_dname(domain),
461 rrset->rrs[i].type, rrset->rrs[i].klass,
462 rrset->rrs[i].ttl, rrset->rrs[i].rdatas,
463 rrset->rrs[i].rdata_count)) {
464 log_msg(LOG_ERR, "out of memory");
465 return 0;
466 }
467 }
468 return 1;
469 }
470
471 /* spool read an rrset, it is a deleted RRset */
process_spool_delrrset(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,uint8_t * dname,size_t dname_len,uint16_t tp,uint16_t kl,uint16_t rrcount)472 static int process_spool_delrrset(FILE* spool, struct ixfr_create* ixfrcr,
473 struct ixfr_store* store, uint8_t* dname, size_t dname_len,
474 uint16_t tp, uint16_t kl, uint16_t rrcount)
475 {
476 /* read the RRs from file and add to del list. */
477 uint8_t buf[MAX_RDLENGTH];
478 int i;
479 for(i=0; i<rrcount; i++) {
480 uint16_t rdlen;
481 uint32_t ttl;
482 if(!read_spool_u32(spool, &ttl) ||
483 !read_spool_u16(spool, &rdlen)) {
484 log_msg(LOG_ERR, "error reading file %s: %s",
485 ixfrcr->file_name, strerror(errno));
486 return 0;
487 }
488 /* because rdlen is uint16_t always smaller than sizeof(buf)*/
489 #pragma GCC diagnostic push
490 #pragma GCC diagnostic ignored "-Wtype-limits"
491 assert(rdlen <= sizeof(buf));
492 #pragma GCC diagnostic pop
493 if(fread(buf, rdlen, 1, spool) < 1) {
494 log_msg(LOG_ERR, "error reading file %s: %s",
495 ixfrcr->file_name, strerror(errno));
496 return 0;
497 }
498 if(tp == TYPE_SOA) {
499 if(!process_store_oldsoa(store, dname, dname_len,
500 tp, kl, ttl, buf, rdlen))
501 return 0;
502 }
503 if(!ixfr_store_delrr_uncompressed(store, dname, dname_len, tp,
504 kl, ttl, buf, rdlen)) {
505 log_msg(LOG_ERR, "out of memory");
506 return 0;
507 }
508 }
509 return 1;
510 }
511
512 /* add the rrset to the added list */
process_add_rrset(struct ixfr_store * ixfr_store,struct domain * domain,struct rrset * rrset)513 static int process_add_rrset(struct ixfr_store* ixfr_store,
514 struct domain* domain, struct rrset* rrset)
515 {
516 int i;
517 for(i=0; i<rrset->rr_count; i++) {
518 if(!ixfr_store_addrr_rdatas(ixfr_store, domain_dname(domain),
519 rrset->rrs[i].type, rrset->rrs[i].klass,
520 rrset->rrs[i].ttl, rrset->rrs[i].rdatas,
521 rrset->rrs[i].rdata_count)) {
522 log_msg(LOG_ERR, "out of memory");
523 return 0;
524 }
525 }
526 return 1;
527 }
528
529 /* add the RR types that are not in the marktypes list from the new zone */
process_marktypes(struct ixfr_store * store,struct zone * zone,struct domain * domain,uint16_t * marktypes,size_t marktypes_used)530 static int process_marktypes(struct ixfr_store* store, struct zone* zone,
531 struct domain* domain, uint16_t* marktypes, size_t marktypes_used)
532 {
533 /* walk through the rrsets in the zone, if it is not in the
534 * marktypes list, then it is new and an added RRset */
535 rrset_type* s;
536 qsort(marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16);
537 for(s=domain->rrsets; s; s=s->next) {
538 uint16_t tp;
539 if(s->zone != zone)
540 continue;
541 tp = rrset_rrtype(s);
542 if(bsearch(&tp, marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16)) {
543 /* the item is in the marked list, skip it */
544 continue;
545 }
546 if(!process_add_rrset(store, domain, s))
547 return 0;
548 }
549 return 1;
550 }
551
552 /* check the difference between the domain and RRs from spool */
process_diff_domain(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct zone * zone,struct domain * domain)553 static int process_diff_domain(FILE* spool, struct ixfr_create* ixfrcr,
554 struct ixfr_store* store, struct zone* zone, struct domain* domain)
555 {
556 /* Read the RR types from spool. Mark off the ones seen,
557 * later, the notseen ones from the new zone are added RRsets.
558 * For the ones not in the new zone, they are deleted RRsets.
559 * If they exist in old and new, check for RR differences. */
560 uint32_t spool_type_count, i;
561 uint16_t marktypes[65536];
562 size_t marktypes_used = 0;
563 if(!read_spool_u32(spool, &spool_type_count)) {
564 log_msg(LOG_ERR, "error reading file %s: %s",
565 ixfrcr->file_name, strerror(errno));
566 return 0;
567 }
568 if(spool_type_count > sizeof(marktypes)) {
569 log_msg(LOG_ERR, "error reading file %s: spool type count "
570 "too large", ixfrcr->file_name);
571 return 0;
572 }
573 for(i=0; i<spool_type_count; i++) {
574 uint16_t tp, kl, rrcount;
575 struct rrset* rrset;
576 if(!read_spool_u16(spool, &tp) ||
577 !read_spool_u16(spool, &kl) ||
578 !read_spool_u16(spool, &rrcount)) {
579 log_msg(LOG_ERR, "error reading file %s: %s",
580 ixfrcr->file_name, strerror(errno));
581 return 0;
582 }
583 /* The rrcount is within limits of sizeof(marktypes), because
584 * the uint16_t < 65536 */
585 rrset = domain_find_rrset(domain, zone, tp);
586 if(!rrset) {
587 /* rrset in spool but not in new zone, deleted RRset */
588 if(!process_spool_delrrset(spool, ixfrcr, store,
589 (void*)dname_name(domain_dname(domain)),
590 domain_dname(domain)->name_size, tp, kl,
591 rrcount))
592 return 0;
593 } else {
594 /* add to the marked types, this one is present in
595 * spool */
596 marktypes[marktypes_used++] = tp;
597 /* rrset in old and in new zone, diff the RRset */
598 if(!process_diff_rrset(spool, ixfrcr, store, domain,
599 tp, kl, rrcount, rrset))
600 return 0;
601 }
602 }
603 /* process markoff to see if new zone has RRsets not in spool,
604 * those are added RRsets. */
605 if(!process_marktypes(store, zone, domain, marktypes, marktypes_used))
606 return 0;
607 return 1;
608 }
609
610 /* add the RRs for the domain in new zone */
process_domain_add_RRs(struct ixfr_store * store,struct zone * zone,struct domain * domain)611 static int process_domain_add_RRs(struct ixfr_store* store, struct zone* zone,
612 struct domain* domain)
613 {
614 rrset_type* s;
615 for(s=domain->rrsets; s; s=s->next) {
616 if(s->zone != zone)
617 continue;
618 if(!process_add_rrset(store, domain, s))
619 return 0;
620 }
621 return 1;
622 }
623
624 /* del the RRs for the domain from the spool */
process_domain_del_RRs(struct ixfr_create * ixfrcr,struct ixfr_store * store,FILE * spool,uint8_t * dname,size_t dname_len)625 static int process_domain_del_RRs(struct ixfr_create* ixfrcr,
626 struct ixfr_store* store, FILE* spool, uint8_t* dname,
627 size_t dname_len)
628 {
629 uint32_t spool_type_count, i;
630 if(!read_spool_u32(spool, &spool_type_count)) {
631 log_msg(LOG_ERR, "error reading file %s: %s",
632 ixfrcr->file_name, strerror(errno));
633 return 0;
634 }
635 if(spool_type_count > 65536) {
636 log_msg(LOG_ERR, "error reading file %s: del RR spool type "
637 "count too large", ixfrcr->file_name);
638 return 0;
639 }
640 for(i=0; i<spool_type_count; i++) {
641 uint16_t tp, kl, rrcount;
642 if(!read_spool_u16(spool, &tp) ||
643 !read_spool_u16(spool, &kl) ||
644 !read_spool_u16(spool, &rrcount)) {
645 log_msg(LOG_ERR, "error reading file %s: %s",
646 ixfrcr->file_name, strerror(errno));
647 return 0;
648 }
649 /* The rrcount is within reasonable limits, because
650 * the uint16_t < 65536 */
651 if(!process_spool_delrrset(spool, ixfrcr, store, dname,
652 dname_len, tp, kl, rrcount))
653 return 0;
654 }
655 return 1;
656 }
657
658 /* init the spool dname iterator */
spool_dname_iter_init(struct spool_dname_iterator * iter,FILE * spool,char * file_name)659 static void spool_dname_iter_init(struct spool_dname_iterator* iter,
660 FILE* spool, char* file_name)
661 {
662 memset(iter, 0, sizeof(*iter));
663 iter->spool = spool;
664 iter->file_name = file_name;
665 }
666
667 /* read the dname element into the buffer for the spool dname iterator */
spool_dname_iter_read(struct spool_dname_iterator * iter)668 static int spool_dname_iter_read(struct spool_dname_iterator* iter)
669 {
670 if(!read_spool_dname(iter->spool, iter->dname, sizeof(iter->dname),
671 &iter->dname_len)) {
672 log_msg(LOG_ERR, "error reading file %s: %s",
673 iter->file_name, strerror(errno));
674 return 0;
675 }
676 return 1;
677 }
678
679 /* get the next name to operate on, that is not processed yet, 0 on failure
680 * returns okay on endoffile, check with eof for that.
681 * when done with an element, set iter->is_processed on the element. */
spool_dname_iter_next(struct spool_dname_iterator * iter)682 static int spool_dname_iter_next(struct spool_dname_iterator* iter)
683 {
684 if(iter->eof)
685 return 1;
686 if(!iter->read_first) {
687 /* read the first one */
688 if(!spool_dname_iter_read(iter))
689 return 0;
690 if(iter->dname_len == 0)
691 iter->eof = 1;
692 iter->read_first = 1;
693 iter->is_processed = 0;
694 }
695 if(!iter->is_processed) {
696 /* the current one needs processing */
697 return 1;
698 }
699 /* read the next one */
700 if(!spool_dname_iter_read(iter))
701 return 0;
702 if(iter->dname_len == 0)
703 iter->eof = 1;
704 iter->is_processed = 0;
705 return 1;
706 }
707
708 /* check if the ixfr is too large */
ixfr_create_too_large(struct ixfr_create * ixfrcr,struct ixfr_store * store)709 static int ixfr_create_too_large(struct ixfr_create* ixfrcr,
710 struct ixfr_store* store)
711 {
712 if(store->cancelled)
713 return 1;
714 if(ixfrcr->max_size != 0 &&
715 ixfr_data_size(store->data) > ixfrcr->max_size) {
716 if(ixfrcr->errorcmdline) {
717 log_msg(LOG_ERR, "the ixfr for %s exceeds size %u, it is not created",
718 wiredname2str(ixfrcr->zone_name),
719 (unsigned)ixfrcr->max_size);
720 } else {
721 VERBOSITY(2, (LOG_INFO, "the ixfr for %s exceeds size %u, it is not created",
722 wiredname2str(ixfrcr->zone_name),
723 (unsigned)ixfrcr->max_size));
724 }
725 ixfr_store_cancel(store);
726 return 1;
727 }
728 return 0;
729 }
730
731 /* process the spool input before the domain */
process_spool_before_domain(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct domain * domain,struct spool_dname_iterator * iter,struct region * tmp_region)732 static int process_spool_before_domain(FILE* spool, struct ixfr_create* ixfrcr,
733 struct ixfr_store* store, struct domain* domain,
734 struct spool_dname_iterator* iter, struct region* tmp_region)
735 {
736 const dname_type* dname;
737 if(ixfr_create_too_large(ixfrcr, store))
738 return 0;
739 /* read the domains and rrsets before the domain and those are from
740 * the old zone. If the domain is equal, return to have that processed
741 * if we bypass, that means the domain does not exist, do that */
742 while(!iter->eof) {
743 if(!spool_dname_iter_next(iter))
744 return 0;
745 if(iter->eof)
746 break;
747 /* see if we are at, before or after the domain */
748 dname = dname_make(tmp_region, iter->dname, 1);
749 if(!dname) {
750 log_msg(LOG_ERR, "error in dname in %s",
751 iter->file_name);
752 return 0;
753 }
754 if(dname_compare(dname, domain_dname(domain)) < 0) {
755 /* the dname is smaller than the one from the zone.
756 * it must be deleted, process it */
757 if(!process_domain_del_RRs(ixfrcr, store, spool,
758 iter->dname, iter->dname_len))
759 return 0;
760 iter->is_processed = 1;
761 } else {
762 /* we are at or after the domain we are looking for,
763 * done here */
764 return 1;
765 }
766 if(ixfr_create_too_large(ixfrcr, store))
767 return 0;
768 }
769 /* no more domains on spool, done here */
770 return 1;
771 }
772
773 /* process the spool input for the domain */
process_spool_for_domain(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct zone * zone,struct domain * domain,struct spool_dname_iterator * iter,struct region * tmp_region)774 static int process_spool_for_domain(FILE* spool, struct ixfr_create* ixfrcr,
775 struct ixfr_store* store, struct zone* zone, struct domain* domain,
776 struct spool_dname_iterator* iter, struct region* tmp_region)
777 {
778 /* process all the spool that is not the domain, that is before the
779 * domain in the new zone */
780 if(!process_spool_before_domain(spool, ixfrcr, store, domain, iter,
781 tmp_region))
782 return 0;
783
784 if(ixfr_create_too_large(ixfrcr, store))
785 return 0;
786 /* are we at the correct domain now? */
787 if(iter->eof || iter->dname_len != domain_dname(domain)->name_size ||
788 memcmp(iter->dname, dname_name(domain_dname(domain)),
789 iter->dname_len) != 0) {
790 /* the domain from the new zone is not present in the old zone,
791 * the content is in the added RRs set */
792 if(!process_domain_add_RRs(store, zone, domain))
793 return 0;
794 return 1;
795 }
796
797 /* process the domain */
798 /* the domain exists both in the old and new zone,
799 * check for RR differences */
800 if(!process_diff_domain(spool, ixfrcr, store, zone, domain))
801 return 0;
802 iter->is_processed = 1;
803
804 return 1;
805 }
806
807 /* process remaining spool items */
process_spool_remaining(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct spool_dname_iterator * iter)808 static int process_spool_remaining(FILE* spool, struct ixfr_create* ixfrcr,
809 struct ixfr_store* store, struct spool_dname_iterator* iter)
810 {
811 /* the remaining domain names in the spool file, that is after
812 * the last domain in the new zone. */
813 if(ixfr_create_too_large(ixfrcr, store))
814 return 0;
815 while(!iter->eof) {
816 if(!spool_dname_iter_next(iter))
817 return 0;
818 if(iter->eof)
819 break;
820 /* the domain only exists in the spool, the old zone,
821 * and not in the new zone. That would be domains
822 * after the new zone domains, or there are no new
823 * zone domains */
824 if(!process_domain_del_RRs(ixfrcr, store, spool, iter->dname,
825 iter->dname_len))
826 return 0;
827 iter->is_processed = 1;
828 if(ixfr_create_too_large(ixfrcr, store))
829 return 0;
830 }
831 return 1;
832 }
833
834 /* walk through the zone and find the differences */
ixfr_create_walk_zone(FILE * spool,struct ixfr_create * ixfrcr,struct ixfr_store * store,struct zone * zone)835 static int ixfr_create_walk_zone(FILE* spool, struct ixfr_create* ixfrcr,
836 struct ixfr_store* store, struct zone* zone)
837 {
838 struct domain* domain;
839 struct spool_dname_iterator iter;
840 struct region* tmp_region;
841 spool_dname_iter_init(&iter, spool, ixfrcr->file_name);
842 tmp_region = region_create(xalloc, free);
843 for(domain = zone->apex; domain && domain_is_subdomain(domain,
844 zone->apex); domain = domain_next(domain)) {
845 uint32_t count = domain_count_rrsets(domain, zone);
846 if(count == 0)
847 continue;
848
849 /* the domain is a domain in the new zone */
850 if(!process_spool_for_domain(spool, ixfrcr, store, zone,
851 domain, &iter, tmp_region)) {
852 region_destroy(tmp_region);
853 return 0;
854 }
855 region_free_all(tmp_region);
856 if(ixfr_create_too_large(ixfrcr, store))
857 return 0;
858 }
859 if(!process_spool_remaining(spool, ixfrcr, store, &iter)) {
860 region_destroy(tmp_region);
861 return 0;
862 }
863 region_destroy(tmp_region);
864 return 1;
865 }
866
867 /* see if the ixfr has already been created by reading the file header
868 * of the to-be-created file, if that file already exists */
ixfr_create_already_done_serial(struct zone * zone,const char * zfile,int checknew,uint32_t old_serial,uint32_t new_serial)869 static int ixfr_create_already_done_serial(struct zone* zone,
870 const char* zfile, int checknew, uint32_t old_serial,
871 uint32_t new_serial)
872 {
873 uint32_t file_oldserial = 0, file_newserial = 0;
874 size_t data_size = 0;
875 if(!ixfr_read_file_header(zone->opts->name, zfile, 1, &file_oldserial,
876 &file_newserial, &data_size, 0)) {
877 /* could not read, so it was not done */
878 return 0;
879 }
880 if(file_oldserial == old_serial &&
881 (!checknew || file_newserial == new_serial)) {
882 log_msg(LOG_INFO, "IXFR already exists in file %s.ixfr, nothing to do",
883 zfile);
884 return 1;
885 }
886 return 0;
887 }
888
889 /* See the data size of the ixfr by reading the file header of the ixfr file */
ixfr_read_header_data_size(const char * zname,const char * zfile,int file_num,size_t * data_size)890 static int ixfr_read_header_data_size(const char* zname,
891 const char* zfile, int file_num, size_t* data_size)
892 {
893 uint32_t file_oldserial = 0, file_newserial = 0;
894 if(!ixfr_read_file_header(zname, zfile, file_num, &file_oldserial,
895 &file_newserial, data_size, 0)) {
896 /* could not read */
897 return 0;
898 }
899 return 1;
900 }
901
902 /* see if the ixfr has already been created by reading the file header
903 * of the to-be-created file, if that file already exists */
ixfr_create_already_done(struct ixfr_create * ixfrcr,struct zone * zone,const char * zfile,int checknew)904 static int ixfr_create_already_done(struct ixfr_create* ixfrcr,
905 struct zone* zone, const char* zfile, int checknew)
906 {
907 return ixfr_create_already_done_serial(zone, zfile, checknew,
908 ixfrcr->old_serial, ixfrcr->new_serial);
909 }
910
911 /* store the new soa record for the ixfr */
ixfr_create_store_newsoa(struct ixfr_store * store,struct zone * zone)912 static int ixfr_create_store_newsoa(struct ixfr_store* store,
913 struct zone* zone)
914 {
915 if(!zone || !zone->soa_rrset) {
916 log_msg(LOG_ERR, "error no SOA rrset");
917 return 0;
918 }
919 if(zone->soa_rrset->rr_count == 0) {
920 log_msg(LOG_ERR, "error empty SOA rrset");
921 return 0;
922 }
923 if(!ixfr_store_add_newsoa_rdatas(store, domain_dname(zone->apex),
924 zone->soa_rrset->rrs[0].type, zone->soa_rrset->rrs[0].klass,
925 zone->soa_rrset->rrs[0].ttl, zone->soa_rrset->rrs[0].rdatas,
926 zone->soa_rrset->rrs[0].rdata_count)) {
927 log_msg(LOG_ERR, "out of memory");
928 return 0;
929 }
930 return 1;
931 }
932
933 /* initialise ixfr_create perform, open spool, read header, get serial */
ixfr_perform_init(struct ixfr_create * ixfrcr,struct zone * zone,struct ixfr_store * store_mem,struct ixfr_store ** store,FILE ** spool)934 static int ixfr_perform_init(struct ixfr_create* ixfrcr, struct zone* zone,
935 struct ixfr_store* store_mem, struct ixfr_store** store, FILE** spool)
936 {
937 *spool = fopen(ixfrcr->file_name, "r");
938 if(!*spool) {
939 log_msg(LOG_ERR, "could not open %s for reading: %s",
940 ixfrcr->file_name, strerror(errno));
941 return 0;
942 }
943 if(!read_spool_header(*spool, ixfrcr)) {
944 fclose(*spool);
945 return 0;
946 }
947 ixfrcr->new_serial = zone_get_current_serial(zone);
948 *store = ixfr_store_start(zone, store_mem);
949 if(!ixfr_create_store_newsoa(*store, zone)) {
950 fclose(*spool);
951 ixfr_store_free(*store);
952 return 0;
953 }
954 return 1;
955 }
956
957 /* rename the other ixfr files */
ixfr_create_rename_and_delete_files(const char * zname,const char * zoptsname,const char * zfile,uint32_t ixfr_number,size_t ixfr_size,size_t cur_data_size)958 static int ixfr_create_rename_and_delete_files(const char* zname,
959 const char* zoptsname, const char* zfile, uint32_t ixfr_number,
960 size_t ixfr_size, size_t cur_data_size)
961 {
962 size_t size_in_use = cur_data_size;
963 int dest_nr_files = (int)ixfr_number, maxsizehit = 0;
964 int num = 1;
965 while(ixfr_file_exists(zfile, num)) {
966 size_t fsize = 0;
967 if(!maxsizehit) {
968 if(!ixfr_read_header_data_size(zoptsname, zfile, num,
969 &fsize) || size_in_use + fsize > ixfr_size) {
970 /* no more than this because of storage size */
971 dest_nr_files = num;
972 maxsizehit = 1;
973 }
974 size_in_use += fsize;
975 }
976 num++;
977 }
978 num--;
979 /* num is now the number of ixfr files that exist */
980 while(num > 0) {
981 if(num+1 > dest_nr_files) {
982 (void)ixfr_unlink_it(zname, zfile, num, 0);
983 } else {
984 if(!ixfr_rename_it(zname, zfile, num, 0, num+1, 0))
985 return 0;
986 }
987 num--;
988 }
989 return 1;
990 }
991
992 /* finish up ixfr create processing */
ixfr_create_finishup(struct ixfr_create * ixfrcr,struct ixfr_store * store,struct zone * zone,int append_mem,struct nsd * nsd,const char * zfile,uint32_t ixfr_number)993 static void ixfr_create_finishup(struct ixfr_create* ixfrcr,
994 struct ixfr_store* store, struct zone* zone, int append_mem,
995 struct nsd* nsd, const char* zfile, uint32_t ixfr_number)
996 {
997 char log_buf[1024], nowstr[128];
998 /* create the log message */
999 time_t now = time(NULL);
1000 if(store->cancelled || ixfr_create_too_large(ixfrcr, store)) {
1001 /* remove unneeded files.
1002 * since this ixfr cannot be created the others are useless. */
1003 ixfr_delete_superfluous_files(zone, zfile, 0);
1004 return;
1005 }
1006 snprintf(nowstr, sizeof(nowstr), "%s", ctime(&now));
1007 if(strchr(nowstr, '\n'))
1008 *strchr(nowstr, '\n') = 0;
1009 snprintf(log_buf, sizeof(log_buf),
1010 "IXFR created by NSD %s for %s %u to %u of %u bytes at time %s",
1011 PACKAGE_VERSION, wiredname2str(ixfrcr->zone_name),
1012 (unsigned)ixfrcr->old_serial, (unsigned)ixfrcr->new_serial,
1013 (unsigned)ixfr_data_size(store->data), nowstr);
1014 store->data->log_str = strdup(log_buf);
1015 if(!store->data->log_str) {
1016 log_msg(LOG_ERR, "out of memory");
1017 ixfr_store_free(store);
1018 return;
1019 }
1020 if(!ixfr_create_rename_and_delete_files(
1021 wiredname2str(ixfrcr->zone_name), zone->opts->name, zfile,
1022 ixfr_number, ixfrcr->max_size, ixfr_data_size(store->data))) {
1023 log_msg(LOG_ERR, "could not rename other ixfr files");
1024 ixfr_store_free(store);
1025 return;
1026 }
1027 if(!ixfr_write_file(zone, store->data, zfile, 1)) {
1028 log_msg(LOG_ERR, "could not write to file");
1029 ixfr_store_free(store);
1030 return;
1031 }
1032 if(append_mem) {
1033 ixfr_store_finish(store, nsd, log_buf);
1034 }
1035 }
1036
ixfr_readup_exist(struct zone * zone,struct nsd * nsd,const char * zfile)1037 void ixfr_readup_exist(struct zone* zone, struct nsd* nsd,
1038 const char* zfile)
1039 {
1040 /* the .ixfr file already exists with the correct serial numbers
1041 * on the disk. Read up the ixfr files from the drive and put them
1042 * in memory. To match the zone that has just been read.
1043 * We can skip ixfr creation, and read up the files from the drive.
1044 * If the files on the drive are consistent, we end up with exactly
1045 * those ixfrs and that zone in memory.
1046 * Presumably, the user has used nsd-checkzone to create an IXFR
1047 * file and has put a new zone file, so we read up the data that
1048 * we should have now.
1049 * This also takes into account the config on number and size. */
1050 ixfr_read_from_file(nsd, zone, zfile);
1051 }
1052
ixfr_create_perform(struct ixfr_create * ixfrcr,struct zone * zone,int append_mem,struct nsd * nsd,const char * zfile,uint32_t ixfr_number)1053 int ixfr_create_perform(struct ixfr_create* ixfrcr, struct zone* zone,
1054 int append_mem, struct nsd* nsd, const char* zfile,
1055 uint32_t ixfr_number)
1056 {
1057 struct ixfr_store store_mem, *store;
1058 FILE* spool;
1059 if(!ixfr_perform_init(ixfrcr, zone, &store_mem, &store, &spool)) {
1060 (void)unlink(ixfrcr->file_name);
1061 return 0;
1062 }
1063 if(ixfrcr->new_serial == ixfrcr->old_serial ||
1064 compare_serial(ixfrcr->new_serial, ixfrcr->old_serial)<0) {
1065 log_msg(LOG_ERR, "zone %s ixfr could not be created because the serial is the same or moves backwards, from %u to %u",
1066 wiredname2str(ixfrcr->zone_name),
1067 (unsigned)ixfrcr->old_serial,
1068 (unsigned)ixfrcr->new_serial);
1069 ixfr_store_cancel(store);
1070 fclose(spool);
1071 ixfr_store_free(store);
1072 (void)unlink(ixfrcr->file_name);
1073 ixfr_delete_superfluous_files(zone, zfile, 0);
1074 if(append_mem)
1075 ixfr_store_delixfrs(zone);
1076 return 0;
1077 }
1078 if(ixfr_create_already_done(ixfrcr, zone, zfile, 1)) {
1079 ixfr_store_cancel(store);
1080 fclose(spool);
1081 ixfr_store_free(store);
1082 (void)unlink(ixfrcr->file_name);
1083 if(append_mem) {
1084 ixfr_readup_exist(zone, nsd, zfile);
1085 }
1086 return 0;
1087 }
1088
1089 if(!ixfr_create_walk_zone(spool, ixfrcr, store, zone)) {
1090 fclose(spool);
1091 ixfr_store_free(store);
1092 (void)unlink(ixfrcr->file_name);
1093 ixfr_delete_superfluous_files(zone, zfile, 0);
1094 return 0;
1095 }
1096 if(store->data && !store->data->oldsoa) {
1097 log_msg(LOG_ERR, "error spool file did not contain a SOA record");
1098 fclose(spool);
1099 ixfr_store_free(store);
1100 (void)unlink(ixfrcr->file_name);
1101 return 0;
1102 }
1103 if(!store->cancelled)
1104 ixfr_store_finish_data(store);
1105 fclose(spool);
1106 (void)unlink(ixfrcr->file_name);
1107
1108 ixfr_create_finishup(ixfrcr, store, zone, append_mem, nsd, zfile,
1109 ixfr_number);
1110 return 1;
1111 }
1112
ixfr_create_cancel(struct ixfr_create * ixfrcr)1113 void ixfr_create_cancel(struct ixfr_create* ixfrcr)
1114 {
1115 if(!ixfrcr)
1116 return;
1117 (void)unlink(ixfrcr->file_name);
1118 ixfr_create_free(ixfrcr);
1119 }
1120
ixfr_create_from_difference(struct zone * zone,const char * zfile,int * ixfr_create_already_done_flag)1121 int ixfr_create_from_difference(struct zone* zone, const char* zfile,
1122 int* ixfr_create_already_done_flag)
1123 {
1124 uint32_t old_serial;
1125 *ixfr_create_already_done_flag = 0;
1126 /* only if the zone is ixfr enabled */
1127 if(!zone_is_ixfr_enabled(zone))
1128 return 0;
1129 /* only if ixfr create is enabled */
1130 if(!zone->opts->pattern->create_ixfr)
1131 return 0;
1132 /* only if there is a zone in memory to compare with */
1133 if(!zone->soa_rrset || !zone->apex)
1134 return 0;
1135
1136 old_serial = zone_get_current_serial(zone);
1137 if(ixfr_create_already_done_serial(zone, zfile, 0, old_serial, 0)) {
1138 *ixfr_create_already_done_flag = 1;
1139 return 0;
1140 }
1141
1142 return 1;
1143 }
1144