ixfrcreate.c revision 1.1.1.1
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 */ 20static 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 */ 29static 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 */ 38static 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 */ 51static 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 */ 67static 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 */ 92static 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 */ 111static 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 */ 125static 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 */ 137static 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. */ 161static 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 */ 194static 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 */ 207struct 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 */ 244void 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 */ 254static 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 */ 262static 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 */ 270static 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 */ 289static 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 */ 323static 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 */ 340static 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 */ 370static 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 */ 386static 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 */ 398static 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 */ 472static 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 */ 513static 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 */ 530static 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 size_t atmarktype = 0; 537 qsort(marktypes, marktypes_used, sizeof(marktypes[0]), &sort_uint16); 538 for(s=domain->rrsets; s; s=s->next) { 539 uint16_t tp; 540 if(s->zone != zone) 541 continue; 542 tp = rrset_rrtype(s); 543 if(atmarktype < marktypes_used && marktypes[atmarktype]==tp) { 544 /* the item is in the marked list, skip it */ 545 atmarktype++; 546 continue; 547 } 548 if(!process_add_rrset(store, domain, s)) 549 return 0; 550 } 551 return 1; 552} 553 554/* check the difference between the domain and RRs from spool */ 555static int process_diff_domain(FILE* spool, struct ixfr_create* ixfrcr, 556 struct ixfr_store* store, struct zone* zone, struct domain* domain) 557{ 558 /* Read the RR types from spool. Mark off the ones seen, 559 * later, the notseen ones from the new zone are added RRsets. 560 * For the ones not in the new zone, they are deleted RRsets. 561 * If they exist in old and new, check for RR differences. */ 562 uint32_t spool_type_count, i; 563 uint16_t marktypes[65536]; 564 size_t marktypes_used = 0; 565 if(!read_spool_u32(spool, &spool_type_count)) { 566 log_msg(LOG_ERR, "error reading file %s: %s", 567 ixfrcr->file_name, strerror(errno)); 568 return 0; 569 } 570 if(spool_type_count > sizeof(marktypes)) { 571 log_msg(LOG_ERR, "error reading file %s: spool type count " 572 "too large", ixfrcr->file_name); 573 return 0; 574 } 575 for(i=0; i<spool_type_count; i++) { 576 uint16_t tp, kl, rrcount; 577 struct rrset* rrset; 578 if(!read_spool_u16(spool, &tp) || 579 !read_spool_u16(spool, &kl) || 580 !read_spool_u16(spool, &rrcount)) { 581 log_msg(LOG_ERR, "error reading file %s: %s", 582 ixfrcr->file_name, strerror(errno)); 583 return 0; 584 } 585 /* The rrcount is within limits of sizeof(marktypes), because 586 * the uint16_t < 65536 */ 587 rrset = domain_find_rrset(domain, zone, tp); 588 if(!rrset) { 589 /* rrset in spool but not in new zone, deleted RRset */ 590 if(!process_spool_delrrset(spool, ixfrcr, store, 591 (void*)dname_name(domain_dname(domain)), 592 domain_dname(domain)->name_size, tp, kl, 593 rrcount)) 594 return 0; 595 } else { 596 /* add to the marked types, this one is present in 597 * spool */ 598 marktypes[marktypes_used++] = tp; 599 /* rrset in old and in new zone, diff the RRset */ 600 if(!process_diff_rrset(spool, ixfrcr, store, domain, 601 tp, kl, rrcount, rrset)) 602 return 0; 603 } 604 } 605 /* process markoff to see if new zone has RRsets not in spool, 606 * those are added RRsets. */ 607 if(!process_marktypes(store, zone, domain, marktypes, marktypes_used)) 608 return 0; 609 return 1; 610} 611 612/* add the RRs for the domain in new zone */ 613static int process_domain_add_RRs(struct ixfr_store* store, struct zone* zone, 614 struct domain* domain) 615{ 616 rrset_type* s; 617 for(s=domain->rrsets; s; s=s->next) { 618 if(s->zone != zone) 619 continue; 620 if(!process_add_rrset(store, domain, s)) 621 return 0; 622 } 623 return 1; 624} 625 626/* del the RRs for the domain from the spool */ 627static int process_domain_del_RRs(struct ixfr_create* ixfrcr, 628 struct ixfr_store* store, FILE* spool, uint8_t* dname, 629 size_t dname_len) 630{ 631 uint32_t spool_type_count, i; 632 if(!read_spool_u32(spool, &spool_type_count)) { 633 log_msg(LOG_ERR, "error reading file %s: %s", 634 ixfrcr->file_name, strerror(errno)); 635 return 0; 636 } 637 if(spool_type_count > 65536) { 638 log_msg(LOG_ERR, "error reading file %s: del RR spool type " 639 "count too large", ixfrcr->file_name); 640 return 0; 641 } 642 for(i=0; i<spool_type_count; i++) { 643 uint16_t tp, kl, rrcount; 644 if(!read_spool_u16(spool, &tp) || 645 !read_spool_u16(spool, &kl) || 646 !read_spool_u16(spool, &rrcount)) { 647 log_msg(LOG_ERR, "error reading file %s: %s", 648 ixfrcr->file_name, strerror(errno)); 649 return 0; 650 } 651 /* The rrcount is within reasonable limits, because 652 * the uint16_t < 65536 */ 653 if(!process_spool_delrrset(spool, ixfrcr, store, dname, 654 dname_len, tp, kl, rrcount)) 655 return 0; 656 } 657 return 1; 658} 659 660/* init the spool dname iterator */ 661static void spool_dname_iter_init(struct spool_dname_iterator* iter, 662 FILE* spool, char* file_name) 663{ 664 memset(iter, 0, sizeof(*iter)); 665 iter->spool = spool; 666 iter->file_name = file_name; 667} 668 669/* read the dname element into the buffer for the spool dname iterator */ 670static int spool_dname_iter_read(struct spool_dname_iterator* iter) 671{ 672 if(!read_spool_dname(iter->spool, iter->dname, sizeof(iter->dname), 673 &iter->dname_len)) { 674 log_msg(LOG_ERR, "error reading file %s: %s", 675 iter->file_name, strerror(errno)); 676 return 0; 677 } 678 return 1; 679} 680 681/* get the next name to operate on, that is not processed yet, 0 on failure 682 * returns okay on endoffile, check with eof for that. 683 * when done with an element, set iter->is_processed on the element. */ 684static int spool_dname_iter_next(struct spool_dname_iterator* iter) 685{ 686 if(iter->eof) 687 return 1; 688 if(!iter->read_first) { 689 /* read the first one */ 690 if(!spool_dname_iter_read(iter)) 691 return 0; 692 if(iter->dname_len == 0) 693 iter->eof = 1; 694 iter->read_first = 1; 695 iter->is_processed = 0; 696 } 697 if(!iter->is_processed) { 698 /* the current one needs processing */ 699 return 1; 700 } 701 /* read the next one */ 702 if(!spool_dname_iter_read(iter)) 703 return 0; 704 if(iter->dname_len == 0) 705 iter->eof = 1; 706 iter->is_processed = 0; 707 return 1; 708} 709 710/* check if the ixfr is too large */ 711static int ixfr_create_too_large(struct ixfr_create* ixfrcr, 712 struct ixfr_store* store) 713{ 714 if(store->cancelled) 715 return 1; 716 if(ixfrcr->max_size != 0 && 717 ixfr_data_size(store->data) > ixfrcr->max_size) { 718 if(ixfrcr->errorcmdline) { 719 log_msg(LOG_ERR, "the ixfr for %s exceeds size %u, it is not created", 720 wiredname2str(ixfrcr->zone_name), 721 (unsigned)ixfrcr->max_size); 722 } else { 723 VERBOSITY(2, (LOG_INFO, "the ixfr for %s exceeds size %u, it is not created", 724 wiredname2str(ixfrcr->zone_name), 725 (unsigned)ixfrcr->max_size)); 726 } 727 ixfr_store_cancel(store); 728 return 1; 729 } 730 return 0; 731} 732 733/* process the spool input before the domain */ 734static int process_spool_before_domain(FILE* spool, struct ixfr_create* ixfrcr, 735 struct ixfr_store* store, struct domain* domain, 736 struct spool_dname_iterator* iter, struct region* tmp_region) 737{ 738 const dname_type* dname; 739 if(ixfr_create_too_large(ixfrcr, store)) 740 return 0; 741 /* read the domains and rrsets before the domain and those are from 742 * the old zone. If the domain is equal, return to have that processed 743 * if we bypass, that means the domain does not exist, do that */ 744 while(!iter->eof) { 745 if(!spool_dname_iter_next(iter)) 746 return 0; 747 if(iter->eof) 748 break; 749 /* see if we are at, before or after the domain */ 750 dname = dname_make(tmp_region, iter->dname, 1); 751 if(!dname) { 752 log_msg(LOG_ERR, "error in dname in %s", 753 iter->file_name); 754 return 0; 755 } 756 if(dname_compare(dname, domain_dname(domain)) < 0) { 757 /* the dname is smaller than the one from the zone. 758 * it must be deleted, process it */ 759 if(!process_domain_del_RRs(ixfrcr, store, spool, 760 iter->dname, iter->dname_len)) 761 return 0; 762 iter->is_processed = 1; 763 } else { 764 /* we are at or after the domain we are looking for, 765 * done here */ 766 return 1; 767 } 768 if(ixfr_create_too_large(ixfrcr, store)) 769 return 0; 770 } 771 /* no more domains on spool, done here */ 772 return 1; 773} 774 775/* process the spool input for the domain */ 776static int process_spool_for_domain(FILE* spool, struct ixfr_create* ixfrcr, 777 struct ixfr_store* store, struct zone* zone, struct domain* domain, 778 struct spool_dname_iterator* iter, struct region* tmp_region) 779{ 780 /* process all the spool that is not the domain, that is before the 781 * domain in the new zone */ 782 if(!process_spool_before_domain(spool, ixfrcr, store, domain, iter, 783 tmp_region)) 784 return 0; 785 786 if(ixfr_create_too_large(ixfrcr, store)) 787 return 0; 788 /* are we at the correct domain now? */ 789 if(iter->eof || iter->dname_len != domain_dname(domain)->name_size || 790 memcmp(iter->dname, dname_name(domain_dname(domain)), 791 iter->dname_len) != 0) { 792 /* the domain from the new zone is not present in the old zone, 793 * the content is in the added RRs set */ 794 if(!process_domain_add_RRs(store, zone, domain)) 795 return 0; 796 return 1; 797 } 798 799 /* process the domain */ 800 /* the domain exists both in the old and new zone, 801 * check for RR differences */ 802 if(!process_diff_domain(spool, ixfrcr, store, zone, domain)) 803 return 0; 804 iter->is_processed = 1; 805 806 return 1; 807} 808 809/* process remaining spool items */ 810static int process_spool_remaining(FILE* spool, struct ixfr_create* ixfrcr, 811 struct ixfr_store* store, struct spool_dname_iterator* iter) 812{ 813 /* the remaining domain names in the spool file, that is after 814 * the last domain in the new zone. */ 815 if(ixfr_create_too_large(ixfrcr, store)) 816 return 0; 817 while(!iter->eof) { 818 if(!spool_dname_iter_next(iter)) 819 return 0; 820 if(iter->eof) 821 break; 822 /* the domain only exists in the spool, the old zone, 823 * and not in the new zone. That would be domains 824 * after the new zone domains, or there are no new 825 * zone domains */ 826 if(!process_domain_del_RRs(ixfrcr, store, spool, iter->dname, 827 iter->dname_len)) 828 return 0; 829 iter->is_processed = 1; 830 if(ixfr_create_too_large(ixfrcr, store)) 831 return 0; 832 } 833 return 1; 834} 835 836/* walk through the zone and find the differences */ 837static int ixfr_create_walk_zone(FILE* spool, struct ixfr_create* ixfrcr, 838 struct ixfr_store* store, struct zone* zone) 839{ 840 struct domain* domain; 841 struct spool_dname_iterator iter; 842 struct region* tmp_region; 843 spool_dname_iter_init(&iter, spool, ixfrcr->file_name); 844 tmp_region = region_create(xalloc, free); 845 for(domain = zone->apex; domain && domain_is_subdomain(domain, 846 zone->apex); domain = domain_next(domain)) { 847 uint32_t count = domain_count_rrsets(domain, zone); 848 if(count == 0) 849 continue; 850 851 /* the domain is a domain in the new zone */ 852 if(!process_spool_for_domain(spool, ixfrcr, store, zone, 853 domain, &iter, tmp_region)) { 854 region_destroy(tmp_region); 855 return 0; 856 } 857 region_free_all(tmp_region); 858 if(ixfr_create_too_large(ixfrcr, store)) 859 return 0; 860 } 861 if(!process_spool_remaining(spool, ixfrcr, store, &iter)) { 862 region_destroy(tmp_region); 863 return 0; 864 } 865 region_destroy(tmp_region); 866 return 1; 867} 868 869/* see if the ixfr has already been created by reading the file header 870 * of the to-be-created file, if that file already exists */ 871static int ixfr_create_already_done_serial(struct zone* zone, 872 const char* zfile, int checknew, uint32_t old_serial, 873 uint32_t new_serial) 874{ 875 uint32_t file_oldserial = 0, file_newserial = 0; 876 size_t data_size = 0; 877 if(!ixfr_read_file_header(zone->opts->name, zfile, 1, &file_oldserial, 878 &file_newserial, &data_size, 0)) { 879 /* could not read, so it was not done */ 880 return 0; 881 } 882 if(file_oldserial == old_serial && 883 (!checknew || file_newserial == new_serial)) { 884 log_msg(LOG_INFO, "IXFR already exists in file %s.ixfr, nothing to do", 885 zfile); 886 return 1; 887 } 888 return 0; 889} 890 891/* See the data size of the ixfr by reading the file header of the ixfr file */ 892static int ixfr_read_header_data_size(const char* zname, 893 const char* zfile, int file_num, size_t* data_size) 894{ 895 uint32_t file_oldserial = 0, file_newserial = 0; 896 if(!ixfr_read_file_header(zname, zfile, file_num, &file_oldserial, 897 &file_newserial, data_size, 0)) { 898 /* could not read */ 899 return 0; 900 } 901 return 1; 902} 903 904/* see if the ixfr has already been created by reading the file header 905 * of the to-be-created file, if that file already exists */ 906static int ixfr_create_already_done(struct ixfr_create* ixfrcr, 907 struct zone* zone, const char* zfile, int checknew) 908{ 909 return ixfr_create_already_done_serial(zone, zfile, checknew, 910 ixfrcr->old_serial, ixfrcr->new_serial); 911} 912 913/* store the new soa record for the ixfr */ 914static int ixfr_create_store_newsoa(struct ixfr_store* store, 915 struct zone* zone) 916{ 917 if(!zone || !zone->soa_rrset) { 918 log_msg(LOG_ERR, "error no SOA rrset"); 919 return 0; 920 } 921 if(zone->soa_rrset->rr_count == 0) { 922 log_msg(LOG_ERR, "error empty SOA rrset"); 923 return 0; 924 } 925 if(!ixfr_store_add_newsoa_rdatas(store, domain_dname(zone->apex), 926 zone->soa_rrset->rrs[0].type, zone->soa_rrset->rrs[0].klass, 927 zone->soa_rrset->rrs[0].ttl, zone->soa_rrset->rrs[0].rdatas, 928 zone->soa_rrset->rrs[0].rdata_count)) { 929 log_msg(LOG_ERR, "out of memory"); 930 return 0; 931 } 932 return 1; 933} 934 935/* initialise ixfr_create perform, open spool, read header, get serial */ 936static int ixfr_perform_init(struct ixfr_create* ixfrcr, struct zone* zone, 937 struct ixfr_store* store_mem, struct ixfr_store** store, FILE** spool) 938{ 939 *spool = fopen(ixfrcr->file_name, "r"); 940 if(!*spool) { 941 log_msg(LOG_ERR, "could not open %s for reading: %s", 942 ixfrcr->file_name, strerror(errno)); 943 return 0; 944 } 945 if(!read_spool_header(*spool, ixfrcr)) { 946 fclose(*spool); 947 return 0; 948 } 949 ixfrcr->new_serial = zone_get_current_serial(zone); 950 *store = ixfr_store_start(zone, store_mem, ixfrcr->old_serial, 951 ixfrcr->new_serial); 952 if(!ixfr_create_store_newsoa(*store, zone)) { 953 fclose(*spool); 954 ixfr_store_free(*store); 955 return 0; 956 } 957 return 1; 958} 959 960/* rename the other ixfr files */ 961static int ixfr_create_rename_and_delete_files(const char* zname, 962 const char* zoptsname, const char* zfile, uint32_t ixfr_number, 963 size_t ixfr_size, size_t cur_data_size) 964{ 965 size_t size_in_use = cur_data_size; 966 int dest_nr_files = (int)ixfr_number, maxsizehit = 0; 967 int num = 1; 968 while(ixfr_file_exists(zfile, num)) { 969 size_t fsize = 0; 970 if(!maxsizehit) { 971 if(!ixfr_read_header_data_size(zoptsname, zfile, num, 972 &fsize) || size_in_use + fsize > ixfr_size) { 973 /* no more than this because of storage size */ 974 dest_nr_files = num; 975 maxsizehit = 1; 976 } 977 size_in_use += fsize; 978 } 979 num++; 980 } 981 num--; 982 /* num is now the number of ixfr files that exist */ 983 while(num > 0) { 984 if(num+1 > dest_nr_files) { 985 (void)ixfr_unlink_it(zname, zfile, num, 0); 986 } else { 987 if(!ixfr_rename_it(zname, zfile, num, 0, num+1, 0)) 988 return 0; 989 } 990 num--; 991 } 992 return 1; 993} 994 995/* finish up ixfr create processing */ 996static void ixfr_create_finishup(struct ixfr_create* ixfrcr, 997 struct ixfr_store* store, struct zone* zone, int append_mem, 998 struct nsd* nsd, const char* zfile, uint32_t ixfr_number) 999{ 1000 char log_buf[1024], nowstr[128]; 1001 /* create the log message */ 1002 time_t now = time(NULL); 1003 if(store->cancelled || ixfr_create_too_large(ixfrcr, store)) { 1004 /* remove unneeded files. 1005 * since this ixfr cannot be created the others are useless. */ 1006 ixfr_delete_superfluous_files(zone, zfile, 0); 1007 return; 1008 } 1009 snprintf(nowstr, sizeof(nowstr), "%s", ctime(&now)); 1010 if(strchr(nowstr, '\n')) 1011 *strchr(nowstr, '\n') = 0; 1012 snprintf(log_buf, sizeof(log_buf), 1013 "IXFR created by NSD %s for %s %u to %u of %u bytes at time %s", 1014 PACKAGE_VERSION, wiredname2str(ixfrcr->zone_name), 1015 (unsigned)ixfrcr->old_serial, (unsigned)ixfrcr->new_serial, 1016 (unsigned)ixfr_data_size(store->data), nowstr); 1017 store->data->log_str = strdup(log_buf); 1018 if(!store->data->log_str) { 1019 log_msg(LOG_ERR, "out of memory"); 1020 ixfr_store_free(store); 1021 return; 1022 } 1023 if(!ixfr_create_rename_and_delete_files( 1024 wiredname2str(ixfrcr->zone_name), zone->opts->name, zfile, 1025 ixfr_number, ixfrcr->max_size, ixfr_data_size(store->data))) { 1026 log_msg(LOG_ERR, "could not rename other ixfr files"); 1027 ixfr_store_free(store); 1028 return; 1029 } 1030 if(!ixfr_write_file(zone, store->data, zfile, 1)) { 1031 log_msg(LOG_ERR, "could not write to file"); 1032 ixfr_store_free(store); 1033 return; 1034 } 1035 if(append_mem) { 1036 ixfr_store_finish(store, nsd, log_buf); 1037 } 1038} 1039 1040void ixfr_readup_exist(struct zone* zone, struct nsd* nsd, 1041 const char* zfile) 1042{ 1043 /* the .ixfr file already exists with the correct serial numbers 1044 * on the disk. Read up the ixfr files from the drive and put them 1045 * in memory. To match the zone that has just been read. 1046 * We can skip ixfr creation, and read up the files from the drive. 1047 * If the files on the drive are consistent, we end up with exactly 1048 * those ixfrs and that zone in memory. 1049 * Presumably, the user has used nsd-checkzone to create an IXFR 1050 * file and has put a new zone file, so we read up the data that 1051 * we should have now. 1052 * This also takes into account the config on number and size. */ 1053 ixfr_read_from_file(nsd, zone, zfile); 1054} 1055 1056int ixfr_create_perform(struct ixfr_create* ixfrcr, struct zone* zone, 1057 int append_mem, struct nsd* nsd, const char* zfile, 1058 uint32_t ixfr_number) 1059{ 1060 struct ixfr_store store_mem, *store; 1061 FILE* spool; 1062 if(!ixfr_perform_init(ixfrcr, zone, &store_mem, &store, &spool)) { 1063 (void)unlink(ixfrcr->file_name); 1064 return 0; 1065 } 1066 if(ixfrcr->new_serial == ixfrcr->old_serial || 1067 compare_serial(ixfrcr->new_serial, ixfrcr->old_serial)<0) { 1068 log_msg(LOG_ERR, "zone %s ixfr could not be created because the serial is the same or moves backwards, from %u to %u", 1069 wiredname2str(ixfrcr->zone_name), 1070 (unsigned)ixfrcr->old_serial, 1071 (unsigned)ixfrcr->new_serial); 1072 ixfr_store_cancel(store); 1073 fclose(spool); 1074 ixfr_store_free(store); 1075 (void)unlink(ixfrcr->file_name); 1076 ixfr_delete_superfluous_files(zone, zfile, 0); 1077 if(append_mem) 1078 ixfr_store_delixfrs(zone); 1079 return 0; 1080 } 1081 if(ixfr_create_already_done(ixfrcr, zone, zfile, 1)) { 1082 ixfr_store_cancel(store); 1083 fclose(spool); 1084 ixfr_store_free(store); 1085 (void)unlink(ixfrcr->file_name); 1086 if(append_mem) { 1087 ixfr_readup_exist(zone, nsd, zfile); 1088 } 1089 return 0; 1090 } 1091 1092 if(!ixfr_create_walk_zone(spool, ixfrcr, store, zone)) { 1093 fclose(spool); 1094 ixfr_store_free(store); 1095 (void)unlink(ixfrcr->file_name); 1096 ixfr_delete_superfluous_files(zone, zfile, 0); 1097 return 0; 1098 } 1099 if(store->data && !store->data->oldsoa) { 1100 log_msg(LOG_ERR, "error spool file did not contain a SOA record"); 1101 fclose(spool); 1102 ixfr_store_free(store); 1103 (void)unlink(ixfrcr->file_name); 1104 return 0; 1105 } 1106 if(!store->cancelled) 1107 ixfr_store_finish_data(store); 1108 fclose(spool); 1109 (void)unlink(ixfrcr->file_name); 1110 1111 ixfr_create_finishup(ixfrcr, store, zone, append_mem, nsd, zfile, 1112 ixfr_number); 1113 return 1; 1114} 1115 1116void ixfr_create_cancel(struct ixfr_create* ixfrcr) 1117{ 1118 if(!ixfrcr) 1119 return; 1120 (void)unlink(ixfrcr->file_name); 1121 ixfr_create_free(ixfrcr); 1122} 1123 1124int ixfr_create_from_difference(struct zone* zone, const char* zfile, 1125 int* ixfr_create_already_done_flag) 1126{ 1127 uint32_t old_serial; 1128 *ixfr_create_already_done_flag = 0; 1129 /* only if the zone is ixfr enabled */ 1130 if(!zone_is_ixfr_enabled(zone)) 1131 return 0; 1132 /* only if ixfr create is enabled */ 1133 if(!zone->opts->pattern->create_ixfr) 1134 return 0; 1135 /* only if there is a zone in memory to compare with */ 1136 if(!zone->soa_rrset || !zone->apex) 1137 return 0; 1138 1139 old_serial = zone_get_current_serial(zone); 1140 if(ixfr_create_already_done_serial(zone, zfile, 0, old_serial, 0)) { 1141 *ixfr_create_already_done_flag = 1; 1142 return 0; 1143 } 1144 1145 return 1; 1146} 1147