1 1.1 christos /* 2 1.1 christos * xfrd-catalog-zones.c -- catalog zone implementation for NSD 3 1.1 christos * 4 1.1 christos * Copyright (c) 2024, NLnet Labs. All rights reserved. 5 1.1 christos * 6 1.1 christos * See LICENSE for the license. 7 1.1 christos */ 8 1.1 christos #include "config.h" 9 1.1 christos #include "difffile.h" 10 1.1 christos #include "nsd.h" 11 1.1 christos #include "packet.h" 12 1.1 christos #include "rdata.h" 13 1.1 christos #include "xfrd-catalog-zones.h" 14 1.1 christos #include "xfrd-notify.h" 15 1.1 christos 16 1.1 christos 17 1.1 christos /****************** ****************** 18 1.1 christos ****************** catalog consumer zone processing ****************** 19 1.1 christos ****************** ******************/ 20 1.1 christos 21 1.1 christos /** process a catalog consumer zone, load if needed */ 22 1.1 christos static void xfrd_process_catalog_consumer_zone( 23 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone); 24 1.1 christos 25 1.1 christos /** make the catalog consumer zone invalid for given reason */ 26 1.1 christos static void vmake_catalog_consumer_invalid( 27 1.1 christos struct xfrd_catalog_consumer_zone *consumer_zone, 28 1.1 christos const char *format, va_list args); 29 1.1 christos 30 1.1 christos /** return (static) dname with label prepended to dname */ 31 1.1 christos static dname_type* label_plus_dname(const char* label,const dname_type* dname); 32 1.1 christos 33 1.1 christos /** delete the catalog member zone */ 34 1.1 christos static void catalog_del_consumer_member_zone( 35 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone, 36 1.1 christos struct catalog_member_zone* consumer_member_zone); 37 1.1 christos 38 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 39 1.1 christos /* return a single catalog consumer zone from xfrd struct */ 40 1.1 christos static inline struct xfrd_catalog_consumer_zone* 41 1.1 christos xfrd_one_catalog_consumer_zone() 42 1.1 christos { 43 1.1 christos return xfrd 44 1.1 christos && xfrd->catalog_consumer_zones 45 1.1 christos && xfrd->catalog_consumer_zones->count == 1 46 1.1 christos ? (struct xfrd_catalog_consumer_zone*) 47 1.1 christos rbtree_first(xfrd->catalog_consumer_zones) : NULL; 48 1.1 christos } 49 1.1 christos #endif 50 1.1 christos 51 1.1 christos /** return the catalog-member-pattern or NULL on error if not present */ 52 1.1 christos static inline struct pattern_options* 53 1.1 christos catalog_member_pattern(struct xfrd_catalog_consumer_zone* consumer_zone) 54 1.1 christos { 55 1.1 christos if (!consumer_zone->options->pattern 56 1.1 christos || !consumer_zone->options->pattern->catalog_member_pattern) 57 1.1 christos return NULL; 58 1.1 christos return pattern_options_find(xfrd->nsd->options, 59 1.1 christos consumer_zone->options->pattern->catalog_member_pattern); 60 1.1 christos } 61 1.1 christos 62 1.1 christos /** see if we have more zonestatistics entries and it has to be incremented */ 63 1.1 christos static inline void 64 1.1 christos zonestat_inc_ifneeded() 65 1.1 christos { 66 1.1 christos #ifdef USE_ZONE_STATS 67 1.1 christos if(xfrd->nsd->options->zonestatnames->count != xfrd->zonestat_safe) 68 1.1 christos task_new_zonestat_inc(xfrd->nsd->task[xfrd->nsd->mytask], 69 1.1 christos xfrd->last_task, 70 1.1 christos xfrd->nsd->options->zonestatnames->count); 71 1.1 christos #endif /* USE_ZONE_STATS */ 72 1.1 christos } 73 1.1 christos 74 1.1 christos 75 1.1 christos /****************** ****************** 76 1.1 christos ****************** catalog producer zone processing ****************** 77 1.1 christos ****************** ******************/ 78 1.1 christos 79 1.1 christos /** process catalog producer zone producer_zone */ 80 1.1 christos static void xfrd_process_catalog_producer_zone( 81 1.1 christos struct xfrd_catalog_producer_zone* producer_zone); 82 1.1 christos 83 1.1 christos /** rbnode must be struct catalog_member_zone*; compares (key->member_id) */ 84 1.1 christos static int member_id_compare(const void *left, const void *right); 85 1.1 christos 86 1.1 christos /** return xfrd_catalog_producer_zone* pointed to by cmz' catalog-producer-zone 87 1.1 christos * pattern option. struct is created if necessary. returns NULL on failure. */ 88 1.1 christos static struct xfrd_catalog_producer_zone* xfrd_get_catalog_producer_zone( 89 1.1 christos struct catalog_member_zone* cmz); 90 1.1 christos 91 1.1 christos /** helper struct for generating XFR files, for conveying the catalog producer 92 1.1 christos * zone content to the server process. 93 1.1 christos */ 94 1.1 christos struct xfrd_xfr_writer { 95 1.1 christos struct xfrd_catalog_producer_zone* producer_zone; 96 1.1 christos char packet_space[16384]; 97 1.1 christos buffer_type packet; 98 1.1 christos uint32_t seq_nr; /* number of messages already handled */ 99 1.1 christos uint32_t old_serial, new_serial; /* host byte order */ 100 1.1 christos uint64_t xfrfilenumber; /* identifier for file to store xfr into */ 101 1.1 christos }; 102 1.1 christos 103 1.1 christos /** initialize xfrd_xfr_writer struct xw */ 104 1.1 christos static void xfr_writer_init(struct xfrd_xfr_writer* xw, 105 1.1 christos struct xfrd_catalog_producer_zone* producer_zone); 106 1.1 christos 107 1.1 christos /** write packet from xfrd_xfr_writer struct xw to xfr file */ 108 1.1 christos static void xfr_writer_write_packet(struct xfrd_xfr_writer* xw); 109 1.1 christos 110 1.1 christos /** commit xfr file (send to server process), with provided log message */ 111 1.1 christos static void xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, 112 1.1 christos ...); 113 1.1 christos 114 1.1 christos /** try writing SOA RR with serial to packet buffer. returns 0 on failure */ 115 1.1 christos static int try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, 116 1.1 christos uint32_t serial); 117 1.1 christos 118 1.1 christos /** try writing RR to packet buffer. returns 0 on failure */ 119 1.1 christos static int try_buffer_write_RR(buffer_type* packet, const dname_type* owner, 120 1.1 christos uint16_t rr_type, uint16_t rdata_len, const void* rdata); 121 1.1 christos 122 1.1 christos /** try writing PTR RR to packet buffer. returns 0 on failure */ 123 1.1 christos static inline int try_buffer_write_PTR(buffer_type* packet, 124 1.1 christos const dname_type* owner, const dname_type* name); 125 1.1 christos 126 1.1 christos /** try writing TXT RR to packet buffer. returns 0 on failure */ 127 1.1 christos static int try_buffer_write_TXT(buffer_type* packet, const dname_type* name, 128 1.1 christos const char *txt); 129 1.1 christos 130 1.1 christos /** add SOA RR with serial serial to xfrd_xfr_writer xw */ 131 1.1 christos static inline void xfr_writer_add_SOA(struct xfrd_xfr_writer* xw, 132 1.1 christos const dname_type* owner, uint32_t serial) 133 1.1 christos { 134 1.1 christos if(try_buffer_write_SOA(&xw->packet, owner, serial)) 135 1.1 christos return; 136 1.1 christos xfr_writer_write_packet(xw); 137 1.1 christos assert(buffer_position(&xw->packet) == 12); 138 1.1 christos try_buffer_write_SOA(&xw->packet, owner, serial); 139 1.1 christos } 140 1.1 christos 141 1.1 christos /** add RR to xfrd_xfr_writer xw */ 142 1.1 christos static inline void xfr_writer_add_RR(struct xfrd_xfr_writer* xw, 143 1.1 christos const dname_type* owner, 144 1.1 christos uint16_t rr_type, uint16_t rdata_len, const void* rdata) 145 1.1 christos { 146 1.1 christos if(try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata)) 147 1.1 christos return; 148 1.1 christos xfr_writer_write_packet(xw); 149 1.1 christos assert(buffer_position(&xw->packet) == 12); 150 1.1 christos try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata); 151 1.1 christos } 152 1.1 christos 153 1.1 christos /** add PTR RR to xfrd_xfr_writer xw */ 154 1.1 christos static inline void xfr_writer_add_PTR(struct xfrd_xfr_writer* xw, 155 1.1 christos const dname_type* owner, const dname_type* name) 156 1.1 christos { 157 1.1 christos if(try_buffer_write_PTR(&xw->packet, owner, name)) 158 1.1 christos return; 159 1.1 christos xfr_writer_write_packet(xw); 160 1.1 christos assert(buffer_position(&xw->packet) == 12); 161 1.1 christos try_buffer_write_PTR(&xw->packet, owner, name); 162 1.1 christos } 163 1.1 christos 164 1.1 christos /** add TXT RR to xfrd_xfr_writer xw */ 165 1.1 christos static inline void xfr_writer_add_TXT(struct xfrd_xfr_writer* xw, 166 1.1 christos const dname_type* owner, const char* txt) 167 1.1 christos { 168 1.1 christos if(try_buffer_write_TXT(&xw->packet, owner, txt)) 169 1.1 christos return; 170 1.1 christos xfr_writer_write_packet(xw); 171 1.1 christos assert(buffer_position(&xw->packet) == 12); 172 1.1 christos try_buffer_write_TXT(&xw->packet, owner, txt); 173 1.1 christos } 174 1.1 christos 175 1.1 christos 176 1.1 christos /****************** ****************** 177 1.1 christos ****************** catalog consumer zone processing ****************** 178 1.1 christos ****************** ******************/ 179 1.1 christos 180 1.1 christos void 181 1.1 christos xfrd_init_catalog_consumer_zone(xfrd_state_type* xfrd, 182 1.1 christos struct zone_options* zone) 183 1.1 christos { 184 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone; 185 1.1 christos 186 1.1 christos if ((consumer_zone = (struct xfrd_catalog_consumer_zone*)rbtree_search( 187 1.1 christos xfrd->catalog_consumer_zones, zone->node.key))) { 188 1.1 christos log_msg(LOG_ERR, "cannot initialize new catalog consumer zone:" 189 1.1 christos " '%s: it already exists in xfrd's catalog " 190 1.1 christos " consumer zones index", zone->name); 191 1.1 christos /* Maybe we need to reprocess it? */ 192 1.1 christos make_catalog_consumer_valid(consumer_zone); 193 1.1 christos return; 194 1.1 christos } 195 1.1 christos consumer_zone = (struct xfrd_catalog_consumer_zone*) 196 1.1 christos region_alloc(xfrd->region, 197 1.1 christos sizeof(struct xfrd_catalog_consumer_zone)); 198 1.1 christos memset(consumer_zone, 0, sizeof(struct xfrd_catalog_consumer_zone)); 199 1.1 christos consumer_zone->node.key = zone->node.key; 200 1.1 christos consumer_zone->options = zone; 201 1.1 christos consumer_zone->member_ids.region = xfrd->region; 202 1.1 christos consumer_zone->member_ids.root = RBTREE_NULL; 203 1.1 christos consumer_zone->member_ids.count = 0; 204 1.1 christos consumer_zone->member_ids.cmp = member_id_compare; 205 1.1 christos consumer_zone->mtime.tv_sec = 0; 206 1.1 christos consumer_zone->mtime.tv_nsec = 0; 207 1.1 christos 208 1.1 christos consumer_zone->invalid = NULL; 209 1.1 christos rbtree_insert(xfrd->catalog_consumer_zones, 210 1.1 christos (rbnode_type*)consumer_zone); 211 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 212 1.1 christos if ((int)xfrd->catalog_consumer_zones->count > 1) { 213 1.1 christos log_msg(LOG_ERR, "catalog consumer processing disabled: " 214 1.1 christos "only one single catalog consumer zone allowed"); 215 1.1 christos } 216 1.1 christos #endif 217 1.1 christos if(zone->pattern && zone->pattern->store_ixfr) { 218 1.1 christos /* Don't process ixfrs from xfrd */ 219 1.1 christos zone->pattern->store_ixfr = 0; 220 1.1 christos } 221 1.1 christos } 222 1.1 christos 223 1.1 christos void 224 1.1 christos xfrd_deinit_catalog_consumer_zone(xfrd_state_type* xfrd, 225 1.1 christos const dname_type* dname) 226 1.1 christos { 227 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone; 228 1.1 christos zone_type* zone; 229 1.1 christos 230 1.1 christos if (!(consumer_zone =(struct xfrd_catalog_consumer_zone*)rbtree_delete( 231 1.1 christos xfrd->catalog_consumer_zones, dname))) { 232 1.1 christos log_msg(LOG_ERR, "cannot de-initialize catalog consumer zone:" 233 1.1 christos " '%s: it did not exist in xfrd's catalog " 234 1.1 christos " consumer zones index", 235 1.1 christos dname_to_string(dname, NULL)); 236 1.1 christos return; 237 1.1 christos } 238 1.1 christos if (consumer_zone->member_ids.count) 239 1.1 christos log_msg(LOG_WARNING, "de-initialize catalog consumer zone:" 240 1.1 christos " '%s: will cause all member zones to be " 241 1.1 christos " deleted", consumer_zone->options->name); 242 1.1 christos 243 1.1 christos while (consumer_zone->member_ids.count) { 244 1.1 christos struct catalog_member_zone* cmz = (struct catalog_member_zone*) 245 1.1 christos rbtree_first(&consumer_zone->member_ids)->key; 246 1.1 christos 247 1.1 christos log_msg(LOG_INFO, "deleting member zone '%s' on " 248 1.1 christos "de-initializing catalog consumer zone '%s'", 249 1.1 christos cmz->options.name, consumer_zone->options->name); 250 1.1 christos catalog_del_consumer_member_zone(consumer_zone, cmz); 251 1.1 christos } 252 1.1 christos if ((zone = namedb_find_zone(xfrd->nsd->db, dname))) { 253 1.1 christos namedb_zone_delete(xfrd->nsd->db, zone); 254 1.1 christos } 255 1.1 christos region_recycle(xfrd->region, consumer_zone, sizeof(*consumer_zone)); 256 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 257 1.1 christos if((consumer_zone = xfrd_one_catalog_consumer_zone()) 258 1.1 christos && consumer_zone->options && consumer_zone->options->node.key) { 259 1.1 christos xfrd_zone_type* zone = (xfrd_zone_type*)rbtree_search( 260 1.1 christos xfrd->zones, 261 1.1 christos (const dname_type*)consumer_zone->options->node.key); 262 1.1 christos 263 1.1 christos if(zone) { 264 1.1 christos zone->soa_disk_acquired = 0; 265 1.1 christos zone->soa_nsd_acquired = 0; 266 1.1 christos xfrd_handle_notify_and_start_xfr(zone, NULL); 267 1.1 christos } 268 1.1 christos } 269 1.1 christos #endif 270 1.1 christos } 271 1.1 christos 272 1.1 christos /** make the catalog consumer zone invalid for given reason */ 273 1.1 christos static void 274 1.1 christos vmake_catalog_consumer_invalid( 275 1.1 christos struct xfrd_catalog_consumer_zone *consumer_zone, 276 1.1 christos const char *format, va_list args) 277 1.1 christos { 278 1.1 christos char message[MAXSYSLOGMSGLEN]; 279 1.1 christos if (!consumer_zone || consumer_zone->invalid) return; 280 1.1 christos vsnprintf(message, sizeof(message), format, args); 281 1.1 christos log_msg(LOG_ERR, "invalid catalog consumer zone '%s': %s", 282 1.1 christos consumer_zone->options->name, message); 283 1.1 christos consumer_zone->invalid = region_strdup(xfrd->region, message); 284 1.1 christos } 285 1.1 christos 286 1.1 christos void 287 1.1 christos make_catalog_consumer_invalid(struct xfrd_catalog_consumer_zone *consumer_zone, 288 1.1 christos const char *format, ...) 289 1.1 christos { 290 1.1 christos va_list args; 291 1.1 christos if (!consumer_zone || consumer_zone->invalid) return; 292 1.1 christos va_start(args, format); 293 1.1 christos vmake_catalog_consumer_invalid(consumer_zone, format, args); 294 1.1 christos va_end(args); 295 1.1 christos } 296 1.1 christos 297 1.1 christos void 298 1.1 christos make_catalog_consumer_valid(struct xfrd_catalog_consumer_zone *consumer_zone) 299 1.1 christos { 300 1.1 christos if (consumer_zone->invalid) { 301 1.1 christos region_recycle(xfrd->region, consumer_zone->invalid, 302 1.1 christos strlen(consumer_zone->invalid) + 1); 303 1.1 christos consumer_zone->invalid = NULL; 304 1.1 christos } 305 1.1 christos } 306 1.1 christos 307 1.1 christos static dname_type* 308 1.1 christos label_plus_dname(const char* label, const dname_type* dname) 309 1.1 christos { 310 1.1 christos static struct { 311 1.1 christos dname_type dname; 312 1.1 christos uint8_t bytes[MAXDOMAINLEN + 128 /* max number of labels */]; 313 1.1 christos } ATTR_PACKED name; 314 1.1 christos size_t i, ll; 315 1.1 christos 316 1.1 christos if (!label || !dname || dname->label_count > 127) 317 1.1 christos return NULL; 318 1.1 christos ll = strlen(label); 319 1.1 christos if ((int)dname->name_size + ll + 1 > MAXDOMAINLEN) 320 1.1 christos return NULL; 321 1.1 christos 322 1.1 christos /* In reversed order and first copy with memmove, so we can nest. 323 1.1 christos * i.e. label_plus_dname(label1, label_plus_dname(label2, dname)) 324 1.1 christos */ 325 1.1 christos memmove(name.bytes + dname->label_count 326 1.1 christos + 1 /* label_count increases by one */ 327 1.1 christos + 1 /* label type/length byte for label */ + ll, 328 1.1 christos ((char*)dname) + sizeof(dname_type) + dname->label_count, 329 1.1 christos dname->name_size); 330 1.1 christos memcpy(name.bytes + dname->label_count 331 1.1 christos + 1 /* label_count increases by one */ 332 1.1 christos + 1 /* label type/length byte for label */, label, ll); 333 1.1 christos name.bytes[dname->label_count + 1] = ll; /* label type/length byte */ 334 1.1 christos name.bytes[dname->label_count] = 0; /* first label follows last 335 1.1 christos * label_offsets element */ 336 1.1 christos for (i = 0; i < dname->label_count; i++) 337 1.1 christos name.bytes[i] = ((uint8_t*)(void*)dname)[sizeof(dname_type)+i] 338 1.1 christos + 1 /* label type/length byte for label */ + ll; 339 1.1 christos name.dname.label_count = dname->label_count + 1 /* label_count incr. */; 340 1.1 christos name.dname.name_size = dname->name_size + ll 341 1.1 christos + 1 /* label length */; 342 1.1 christos return &name.dname; 343 1.1 christos } 344 1.1 christos 345 1.1 christos static void 346 1.1 christos catalog_del_consumer_member_zone( 347 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone, 348 1.1 christos struct catalog_member_zone* consumer_member_zone) 349 1.1 christos { 350 1.1 christos const dname_type* dname = consumer_member_zone->options.node.key; 351 1.1 christos 352 1.1 christos /* create deletion task */ 353 1.1 christos task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], 354 1.1 christos xfrd->last_task, dname); 355 1.1 christos xfrd_set_reload_now(xfrd); 356 1.1 christos /* delete it in xfrd */ 357 1.1 christos if(zone_is_slave(&consumer_member_zone->options)) { 358 1.1 christos xfrd_del_slave_zone(xfrd, dname); 359 1.1 christos } 360 1.1 christos xfrd_del_notify(xfrd, dname); 361 1.1 christos #ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 362 1.1 christos /* delete it in xfrd's catalog consumers list */ 363 1.1 christos if(zone_is_catalog_consumer(&consumer_member_zone->options)) { 364 1.1 christos xfrd_deinit_catalog_consumer_zone(xfrd, dname); 365 1.1 christos } 366 1.1 christos #endif 367 1.1 christos if(consumer_member_zone->member_id) { 368 1.1 christos rbtree_delete(&consumer_zone->member_ids,consumer_member_zone); 369 1.1 christos consumer_member_zone->node = *RBTREE_NULL; 370 1.1 christos region_recycle( xfrd->nsd->options->region, 371 1.1 christos (void*)consumer_member_zone->member_id, 372 1.1 christos dname_total_size(consumer_member_zone->member_id)); 373 1.1 christos consumer_member_zone->member_id = NULL; 374 1.1 christos } 375 1.1 christos zone_options_delete(xfrd->nsd->options,&consumer_member_zone->options); 376 1.1 christos } 377 1.1 christos 378 1.1 christos void xfrd_check_catalog_consumer_zonefiles(const dname_type* name) 379 1.1 christos { 380 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone; 381 1.1 christos 382 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 383 1.1 christos consumer_zone = xfrd_one_catalog_consumer_zone(); 384 1.1 christos if (!consumer_zone) 385 1.1 christos return; 386 1.1 christos if (name && dname_compare(name, consumer_zone->node.key) != 0) 387 1.1 christos return; 388 1.1 christos name = consumer_zone->node.key; 389 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Mark %s " 390 1.1 christos "for checking", consumer_zone->options->name)); 391 1.1 christos make_catalog_consumer_valid(consumer_zone); 392 1.1 christos namedb_read_zonefile(xfrd->nsd, namedb_find_or_create_zone( 393 1.1 christos xfrd->nsd->db, name, consumer_zone->options), NULL, NULL); 394 1.1 christos #else 395 1.1 christos if (!name) { 396 1.1 christos RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, 397 1.1 christos xfrd->catalog_consumer_zones) { 398 1.1 christos make_catalog_consumer_valid(consumer_zone); 399 1.1 christos namedb_read_zonefile(xfrd->nsd, 400 1.1 christos namedb_find_or_create_zone(xfrd->nsd->db, 401 1.1 christos consumer_zone->options->node.key, 402 1.1 christos consumer_zone->options), 403 1.1 christos NULL, NULL); 404 1.1 christos } 405 1.1 christos } else if ((consumer_zone = (struct xfrd_catalog_consumer_zone*) 406 1.1 christos rbtree_search(xfrd->catalog_consumer_zones, name))) { 407 1.1 christos make_catalog_consumer_valid(consumer_zone); 408 1.1 christos namedb_read_zonefile(xfrd->nsd, 409 1.1 christos namedb_find_or_create_zone( 410 1.1 christos xfrd->nsd->db, name, consumer_zone->options), 411 1.1 christos NULL, NULL); 412 1.1 christos } 413 1.1 christos #endif 414 1.1 christos } 415 1.1 christos 416 1.1 christos const char *invalid_catalog_consumer_zone(struct zone_options* zone) 417 1.1 christos { 418 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone; 419 1.1 christos const char *msg; 420 1.1 christos 421 1.1 christos if (!zone || !zone_is_catalog_consumer(zone)) 422 1.1 christos msg = NULL; 423 1.1 christos 424 1.1 christos else if (!xfrd) 425 1.1 christos msg = "asked for catalog information outside of xfrd process"; 426 1.1 christos 427 1.1 christos else if (!xfrd->catalog_consumer_zones) 428 1.1 christos msg = "zone not found: " 429 1.1 christos "xfrd's catalog consumer zones index is empty"; 430 1.1 christos 431 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 432 1.1 christos else if (xfrd->catalog_consumer_zones->count > 1) 433 1.1 christos return "not processing: more than one catalog consumer zone " 434 1.1 christos "configured and only a single one allowed"; 435 1.1 christos #endif 436 1.1 christos else if (!(consumer_zone = (struct xfrd_catalog_consumer_zone*) 437 1.1 christos rbtree_search(xfrd->catalog_consumer_zones, zone->node.key))) 438 1.1 christos msg = "zone not found in xfrd's catalog consumer zones index"; 439 1.1 christos else 440 1.1 christos return consumer_zone->invalid; 441 1.1 christos 442 1.1 christos if (msg) 443 1.1 christos log_msg(LOG_ERR, "catalog consumer zone '%s': %s", 444 1.1 christos zone->name, msg); 445 1.1 christos 446 1.1 christos return msg; 447 1.1 christos } 448 1.1 christos 449 1.1 christos void xfrd_process_catalog_consumer_zones() 450 1.1 christos { 451 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone; 452 1.1 christos 453 1.1 christos #ifndef MULTIPLE_CATALOG_CONSUMER_ZONES 454 1.1 christos if((consumer_zone = xfrd_one_catalog_consumer_zone())) 455 1.1 christos xfrd_process_catalog_consumer_zone(consumer_zone); 456 1.1 christos #else 457 1.1 christos RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, 458 1.1 christos xfrd->catalog_consumer_zones) { 459 1.1 christos xfrd_process_catalog_consumer_zone(consumer_zone); 460 1.1 christos } 461 1.1 christos #endif 462 1.1 christos } 463 1.1 christos 464 1.1 christos static inline struct catalog_member_zone* cursor_cmz(rbnode_type* node) 465 1.1 christos { return node != RBTREE_NULL ? (struct catalog_member_zone*)node->key : NULL; } 466 1.1 christos static inline const dname_type* cursor_member_id(rbnode_type* node) 467 1.1 christos { return cursor_cmz(node) ? cursor_cmz(node)->member_id : NULL; } 468 1.1 christos 469 1.1 christos #if !defined(NDEBUG) && 1 /* Only disable for seriously slow debugging */ 470 1.1 christos static void debug_log_consumer_members( 471 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone) 472 1.1 christos { 473 1.1 christos rbnode_type* cursor; 474 1.1 christos size_t i; 475 1.1 christos 476 1.1 christos for ( cursor = rbtree_first(&consumer_zone->member_ids), i = 0 477 1.1 christos ; cursor != RBTREE_NULL; i++, cursor = rbtree_next(cursor)) { 478 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Catalog member %.2zu: %s = %s", 479 1.1 christos i, dname_to_string(cursor_member_id(cursor), NULL), 480 1.1 christos cursor_cmz(cursor)->options.name)); 481 1.1 christos } 482 1.1 christos } 483 1.1 christos #else 484 1.1 christos # define debug_log_consumer_members(x) /* nothing */ 485 1.1 christos #endif 486 1.1 christos 487 1.1 christos static void 488 1.1 christos xfrd_process_catalog_consumer_zone( 489 1.1 christos struct xfrd_catalog_consumer_zone* consumer_zone) 490 1.1 christos { 491 1.1 christos zone_type* zone; 492 1.1 christos const dname_type* dname; 493 1.1 christos domain_type *match, *closest_encloser, *member_id, *group; 494 1.1 christos rrset_type *rrset; 495 1.1 christos size_t i; 496 1.1 christos uint8_t version_2_found; 497 1.1 christos /* Currect catalog member zone */ 498 1.1 christos rbnode_type* cursor; 499 1.1 christos struct pattern_options *default_pattern = NULL; 500 1.1 christos /* A transfer of a catalog zone can contain deletion and adding of 501 1.1 christos * the same member zone. In such cases it can occur that the member 502 1.1 christos * is tried to be added before it is deleted. For these exceptional 503 1.1 christos * cases, we will rewalk the zone after the first pass, to retry 504 1.1 christos * adding those zones. 505 1.1 christos * 506 1.1 christos * Initial pass is mode "try_to_add". 507 1.1 christos * If a zone cannot be added, mode is set to "retry_to_add" 508 1.1 christos * If after the first pass the mode is "retry_to_add", 509 1.1 christos * mode will be set to "just_add", and a second pass is done. 510 1.1 christos */ 511 1.1 christos enum { try_to_add, retry_to_add, just_add } mode; 512 1.1 christos 513 1.1 christos assert(consumer_zone); 514 1.1 christos if (!xfrd->nsd->db) { 515 1.1 christos xfrd->nsd->db = namedb_open(xfrd->nsd->options); 516 1.1 christos } 517 1.1 christos dname = (const dname_type*)consumer_zone->node.key; 518 1.1 christos if (dname->name_size > 247) { 519 1.1 christos make_catalog_consumer_invalid(consumer_zone, "name too long"); 520 1.1 christos return; 521 1.1 christos } 522 1.1 christos if (dname->label_count > 126) { 523 1.1 christos make_catalog_consumer_invalid(consumer_zone,"too many labels"); 524 1.1 christos return; 525 1.1 christos } 526 1.1 christos zone = namedb_find_zone(xfrd->nsd->db, dname); 527 1.1 christos if (!zone) { 528 1.1 christos zone = namedb_zone_create(xfrd->nsd->db, dname, 529 1.1 christos consumer_zone->options); 530 1.1 christos namedb_read_zonefile(xfrd->nsd, zone, NULL, NULL); 531 1.1 christos } 532 1.1 christos if (timespec_compare(&consumer_zone->mtime, &zone->mtime) == 0) { 533 1.1 christos /* Not processing unchanged catalog consumer zone */ 534 1.1 christos return; 535 1.1 christos } 536 1.1 christos consumer_zone->mtime = zone->mtime; 537 1.1 christos /* start processing */ 538 1.1 christos /* Lookup version.<consumer_zone> TXT and check that it is version 2 */ 539 1.1 christos if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("version", dname), 540 1.1 christos &match, &closest_encloser) 541 1.1 christos || !(rrset = domain_find_rrset(match, zone, TYPE_TXT))) { 542 1.1 christos make_catalog_consumer_invalid(consumer_zone, 543 1.1 christos "'version.%s TXT RRset not found", 544 1.1 christos consumer_zone->options->name); 545 1.1 christos return; 546 1.1 christos } 547 1.1 christos version_2_found = 0; 548 1.1 christos for (i = 0; i < rrset->rr_count; i++) { 549 1.1 christos if(rrset->rrs[i]->rdlength == 2 && 550 1.1 christos rrset->rrs[i]->rdata[0] == 1 /* TXT string length */ && 551 1.1 christos rrset->rrs[i]->rdata[1] == '2') { 552 1.1 christos version_2_found = 1; 553 1.1 christos break; 554 1.1 christos } 555 1.1 christos } 556 1.1 christos if (!version_2_found) { 557 1.1 christos make_catalog_consumer_invalid(consumer_zone, 558 1.1 christos "'version.%s' TXT RR with value \"2\" not found", 559 1.1 christos consumer_zone->options->name); 560 1.1 christos return; 561 1.1 christos } 562 1.1 christos /* Walk over all names under zones.<consumer_zone> */ 563 1.1 christos if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("zones", dname), 564 1.1 christos &match, &closest_encloser)) { 565 1.1 christos /* zones.<consumer_zone> does not exist, so the catalog has no 566 1.1 christos * members. This is just fine. But there may be members that need 567 1.1 christos * to be deleted. 568 1.1 christos */ 569 1.1 christos cursor = rbtree_first(&consumer_zone->member_ids); 570 1.1 christos mode = just_add; 571 1.1 christos goto delete_members; 572 1.1 christos } 573 1.1 christos mode = consumer_zone->member_ids.count ? try_to_add : just_add; 574 1.1 christos retry_adding: 575 1.1 christos cursor = rbtree_first(&consumer_zone->member_ids); 576 1.1 christos for ( member_id = domain_next(match) 577 1.1 christos ; member_id && domain_is_subdomain(member_id, match) 578 1.1 christos ; member_id = domain_next(member_id)) { 579 1.1 christos domain_type *member_domain; 580 1.1 christos char member_domain_str[5 * MAXDOMAINLEN]; 581 1.1 christos struct zone_options* zopt; 582 1.1 christos int valid_group_values; 583 1.1 christos struct pattern_options *pattern = NULL; 584 1.1 christos struct catalog_member_zone* to_add; 585 1.1 christos 586 1.1 christos if (domain_dname(member_id)->label_count > dname->label_count+2 587 1.1 christos || !(rrset = domain_find_rrset(member_id, zone, TYPE_PTR))) 588 1.1 christos continue; 589 1.1 christos 590 1.1 christos /* RFC9432 Section 4.1. Member Zones: 591 1.1 christos * 592 1.1 christos * `` This PTR record MUST be the only record in the PTR RRset 593 1.1 christos * with the same name. The presence of more than one record 594 1.1 christos * in the RRset indicates a broken catalog zone that MUST 595 1.1 christos * NOT be processed (see Section 5.1). 596 1.1 christos */ 597 1.1 christos if (rrset->rr_count != 1) { 598 1.1 christos make_catalog_consumer_invalid(consumer_zone, 599 1.1 christos "only a single PTR RR expected on '%s'", 600 1.1 christos domain_to_string(member_id)); 601 1.1 christos return; 602 1.1 christos } 603 1.1 christos /* A PTR rr always has 1 rdata element which is a dname */ 604 1.1 christos member_domain = rdata_domain_ref(rrset->rrs[0]); 605 1.1 christos if(!member_domain) 606 1.1 christos continue; 607 1.1 christos domain_to_string_buf(member_domain, member_domain_str); 608 1.1 christos /* remove trailing dot */ 609 1.1 christos member_domain_str[strlen(member_domain_str) - 1] = 0; 610 1.1 christos 611 1.1 christos valid_group_values = 0; 612 1.1 christos /* Lookup group.<member_id> TXT for matching patterns */ 613 1.1 christos if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("group", 614 1.1 christos domain_dname(member_id)), 615 1.1 christos &group, &closest_encloser) 616 1.1 christos || !(rrset = domain_find_rrset(group, zone, TYPE_TXT))) { 617 1.1 christos ; /* pass */ 618 1.1 christos 619 1.1 christos } else for (i = 0; i < rrset->rr_count; i++) { 620 1.1 christos /* Max single TXT rdata field length + '\x00' == 256 */ 621 1.1 christos char group_value[256]; 622 1.1 christos 623 1.1 christos /* Looking for a single TXT rdata field */ 624 1.1 christos if(rrset->rrs[i]->rdlength < 1 625 1.1 christos || rrset->rrs[i]->rdlength != 626 1.1 christos ((uint16_t)rrset->rrs[i]->rdata[0])+1 627 1.1 christos 628 1.1 christos /* rdata field should be at least 1 char */ 629 1.1 christos || rrset->rrs[i]->rdata[0] < 1) 630 1.1 christos continue; 631 1.1 christos 632 1.1 christos memcpy( group_value 633 1.1 christos , rrset->rrs[i]->rdata+1 634 1.1 christos , rrset->rrs[i]->rdata[0] 635 1.1 christos ); 636 1.1 christos group_value[ 637 1.1 christos rrset->rrs[i]->rdata[0] 638 1.1 christos ] = 0; 639 1.1 christos if ((pattern = pattern_options_find( 640 1.1 christos xfrd->nsd->options, group_value))) 641 1.1 christos valid_group_values += 1; 642 1.1 christos } 643 1.1 christos if (valid_group_values > 1) { 644 1.1 christos log_msg(LOG_ERR, "member zone '%s': only a single " 645 1.1 christos "group property that matches a pattern is " 646 1.1 christos "allowed." 647 1.1 christos "The pattern from \"catalog-member-pattern\" " 648 1.1 christos "will be used instead.", 649 1.1 christos domain_to_string(member_id)); 650 1.1 christos valid_group_values = 0; 651 1.1 christos 652 1.1 christos } else if (valid_group_values == 1 && pattern 653 1.1 christos && pattern->catalog_producer_zone) { 654 1.1 christos log_msg(LOG_ERR, "member zone '%s': group property " 655 1.1 christos "'%s' matches a catalog producer member zone " 656 1.1 christos "pattern. In NSD, catalog member zones can be " 657 1.1 christos "either a member of a catalog consumer zone or" 658 1.1 christos " a catalog producer zone, but not both.", 659 1.1 christos domain_to_string(member_id), pattern->pname); 660 1.1 christos valid_group_values = 0; 661 1.1 christos } 662 1.1 christos if (valid_group_values == 1) { 663 1.1 christos /* pass: pattern is already set */ 664 1.1 christos assert(pattern); 665 1.1 christos 666 1.1 christos } else if (default_pattern) 667 1.1 christos pattern = default_pattern; /* pass */ 668 1.1 christos 669 1.1 christos else if (!(pattern = default_pattern = 670 1.1 christos catalog_member_pattern(consumer_zone))) { 671 1.1 christos make_catalog_consumer_invalid(consumer_zone, 672 1.1 christos "missing 'group.%s' TXT RR and no default " 673 1.1 christos "pattern from \"catalog-member-pattern\"", 674 1.1 christos domain_to_string(member_id)); 675 1.1 christos return; 676 1.1 christos } 677 1.1 christos if (cursor == RBTREE_NULL) 678 1.1 christos ; /* End of the current member zones list. 679 1.1 christos * From here onwards, zones will only be added. 680 1.1 christos */ 681 1.1 christos else { 682 1.1 christos int cmp = 0; 683 1.1 christos #ifndef NDEBUG 684 1.1 christos char member_id_str[5 * MAXDOMAINLEN]; 685 1.1 christos domain_to_string_buf(member_id, member_id_str); 686 1.1 christos #endif 687 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Comparing %s with %s", 688 1.1 christos member_id_str, 689 1.1 christos dname_to_string(cursor_member_id(cursor), 690 1.1 christos NULL))); 691 1.1 christos 692 1.1 christos while (cursor != RBTREE_NULL && 693 1.1 christos (cmp = dname_compare( 694 1.1 christos domain_dname(member_id), 695 1.1 christos cursor_member_id(cursor))) > 0) { 696 1.1 christos /* member_id is ahead of the current catalog 697 1.1 christos * member zone pointed to by cursor. 698 1.1 christos * The member zone must be deleted. 699 1.1 christos */ 700 1.1 christos struct catalog_member_zone* to_delete = 701 1.1 christos cursor_cmz(cursor); 702 1.1 christos #ifndef NDEBUG 703 1.1 christos const char *member_id_to_delete_str = 704 1.1 christos dname_to_string(to_delete->member_id, NULL); 705 1.1 christos #endif 706 1.1 christos cursor = rbtree_next(cursor); 707 1.1 christos 708 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, 709 1.1 christos "%s > %s: delete %s", 710 1.1 christos member_id_str, 711 1.1 christos member_id_to_delete_str, 712 1.1 christos member_id_to_delete_str)); 713 1.1 christos catalog_del_consumer_member_zone( 714 1.1 christos consumer_zone, to_delete); 715 1.1 christos if(cursor != RBTREE_NULL) { 716 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, 717 1.1 christos "Comparing %s with %s", 718 1.1 christos member_id_str, 719 1.1 christos dname_to_string( 720 1.1 christos cursor_member_id(cursor), 721 1.1 christos NULL))); 722 1.1 christos } 723 1.1 christos } 724 1.1 christos if (cursor != RBTREE_NULL && cmp == 0) { 725 1.1 christos /* member_id is also in an current catalog 726 1.1 christos * member zone, and cursor is pointing 727 1.1 christos * to it. So, move along ... 728 1.1 christos */ 729 1.1 christos /* ... but first check if the pattern needs 730 1.1 christos * a change 731 1.1 christos */ 732 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s == %s: " 733 1.1 christos "Compare pattern %s with %s", 734 1.1 christos member_id_str, member_id_str, 735 1.1 christos cursor_cmz(cursor)->options.pattern->pname, 736 1.1 christos pattern->pname)); 737 1.1 christos 738 1.1 christos if (cursor_cmz(cursor)->options.pattern == 739 1.1 christos pattern) 740 1.1 christos ; /* pass: Pattern remains the same */ 741 1.1 christos else { 742 1.1 christos /* Changing patterns is basically 743 1.1 christos * deleting and adding the zone again 744 1.1 christos */ 745 1.1 christos zopt = &cursor_cmz(cursor)->options; 746 1.1 christos dname = (dname_type *)zopt->node.key; 747 1.1 christos task_new_del_zone( 748 1.1 christos xfrd->nsd->task[xfrd->nsd->mytask], 749 1.1 christos xfrd->last_task, 750 1.1 christos dname); 751 1.1 christos xfrd_set_reload_now(xfrd); 752 1.1 christos if(zone_is_slave(zopt)) { 753 1.1 christos xfrd_del_slave_zone( xfrd 754 1.1 christos , dname); 755 1.1 christos } 756 1.1 christos xfrd_del_notify(xfrd, dname); 757 1.1 christos #ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 758 1.1 christos if(zone_is_catalog_consumer(zopt)) { 759 1.1 christos xfrd_deinit_catalog_consumer_zone( 760 1.1 christos xfrd, dname); 761 1.1 christos } 762 1.1 christos #endif 763 1.1 christos /* It is a catalog consumer member, 764 1.1 christos * so no need to check if it was a 765 1.1 christos * catalog producer member zone to 766 1.1 christos * delete and add 767 1.1 christos */ 768 1.1 christos zopt->pattern = pattern; 769 1.1 christos task_new_add_zone( 770 1.1 christos xfrd->nsd->task[xfrd->nsd->mytask], 771 1.1 christos xfrd->last_task, zopt->name, 772 1.1 christos pattern->pname, 773 1.1 christos getzonestatid( xfrd->nsd->options 774 1.1 christos , zopt)); 775 1.1 christos zonestat_inc_ifneeded(); 776 1.1 christos xfrd_set_reload_now(xfrd); 777 1.1 christos #ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 778 1.1 christos if(zone_is_catalog_consumer(zopt)) { 779 1.1 christos xfrd_init_catalog_consumer_zone( 780 1.1 christos xfrd, zopt); 781 1.1 christos } 782 1.1 christos #endif 783 1.1 christos init_notify_send(xfrd->notify_zones, 784 1.1 christos xfrd->region, zopt); 785 1.1 christos if(zone_is_slave(zopt)) { 786 1.1 christos xfrd_init_slave_zone( 787 1.1 christos xfrd, zopt); 788 1.1 christos } 789 1.1 christos } 790 1.1 christos cursor = rbtree_next(cursor); 791 1.1 christos continue; 792 1.1 christos } 793 1.1 christos /* member_id is not in the current catalog member zone 794 1.1 christos * list, so it must be added 795 1.1 christos */ 796 1.1 christos assert(cursor == RBTREE_NULL || cmp < 0); 797 1.1 christos } 798 1.1 christos /* See if the zone already exists */ 799 1.1 christos zopt = zone_options_find(xfrd->nsd->options, 800 1.1 christos domain_dname(member_domain)); 801 1.1 christos if (zopt) { 802 1.1 christos /* Produce warning if zopt is from other catalog. 803 1.1 christos * Give debug message if zopt is not from this catalog. 804 1.1 christos */ 805 1.1 christos switch(mode) { 806 1.1 christos case try_to_add: 807 1.1 christos mode = retry_to_add; 808 1.1 christos break; 809 1.1 christos case just_add: 810 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Cannot add " 811 1.1 christos "catalog member zone %s (from %s): " 812 1.1 christos "zone already exists", 813 1.1 christos member_domain_str, 814 1.1 christos domain_to_string(member_id))); 815 1.1 christos break; 816 1.1 christos default: 817 1.1 christos break; 818 1.1 christos } 819 1.1 christos continue; 820 1.1 christos } 821 1.1 christos /* Add member zone if not already there */ 822 1.1 christos log_msg(LOG_INFO, "Adding '%s' PTR '%s'", 823 1.1 christos domain_to_string(member_id), 824 1.1 christos member_domain_str); 825 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Adding %s PTR %s", 826 1.1 christos domain_to_string(member_id), member_domain_str)); 827 1.1 christos to_add= catalog_member_zone_create(xfrd->nsd->options->region); 828 1.1 christos to_add->options.name = region_strdup( 829 1.1 christos xfrd->nsd->options->region, member_domain_str); 830 1.1 christos to_add->options.pattern = pattern; 831 1.1 christos if (!nsd_options_insert_zone(xfrd->nsd->options, 832 1.1 christos &to_add->options)) { 833 1.1 christos log_msg(LOG_ERR, "bad domain name '%s' pattern %s", 834 1.1 christos member_domain_str, 835 1.1 christos ( pattern->pname ? pattern->pname: "<NULL>")); 836 1.1 christos zone_options_delete(xfrd->nsd->options, 837 1.1 christos &to_add->options); 838 1.1 christos continue; 839 1.1 christos } 840 1.1 christos to_add->member_id = dname_copy( xfrd->nsd->options->region 841 1.1 christos , domain_dname(member_id)); 842 1.1 christos /* Insert into the members_id list */ 843 1.1 christos to_add->node.key = to_add; 844 1.1 christos if(!rbtree_insert( &consumer_zone->member_ids, &to_add->node)){ 845 1.1 christos log_msg(LOG_ERR, "Error adding '%s' PTR '%s' to " 846 1.1 christos "consumer_zone->member_ids", 847 1.1 christos domain_to_string(member_id), 848 1.1 christos member_domain_str); 849 1.1 christos break; 850 1.1 christos } else 851 1.1 christos cursor = rbtree_next(&to_add->node); 852 1.1 christos /* make addzone task and schedule reload */ 853 1.1 christos task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], 854 1.1 christos xfrd->last_task, member_domain_str, 855 1.1 christos pattern->pname, 856 1.1 christos getzonestatid(xfrd->nsd->options, &to_add->options)); 857 1.1 christos zonestat_inc_ifneeded(); 858 1.1 christos xfrd_set_reload_now(xfrd); 859 1.1 christos #ifdef MULTIPLE_CATALOG_CONSUMER_ZONES 860 1.1 christos /* add to xfrd - catalog consumer zones */ 861 1.1 christos if(zone_is_catalog_consumer(&to_add->options)) { 862 1.1 christos xfrd_init_catalog_consumer_zone(xfrd,&to_add->options); 863 1.1 christos } 864 1.1 christos #endif 865 1.1 christos /* add to xfrd - notify (for master and slaves) */ 866 1.1 christos init_notify_send(xfrd->notify_zones, xfrd->region, 867 1.1 christos &to_add->options); 868 1.1 christos /* add to xfrd - slave */ 869 1.1 christos if(zone_is_slave(&to_add->options)) { 870 1.1 christos xfrd_init_slave_zone(xfrd, &to_add->options); 871 1.1 christos } 872 1.1 christos DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Added catalog " 873 1.1 christos "member zone %s (from %s)", 874 1.1 christos member_domain_str, domain_to_string(member_id))); 875 1.1 christos } 876 1.1 christos delete_members: 877 1.1 christos while (cursor != RBTREE_NULL) { 878 1.1 christos /* Any current catalog member zones remaining, don't have an 879 1.1 christos * member_id in the catalog anymore, so should be deleted too. 880 1.1 christos */ 881 1.1 christos struct catalog_member_zone* to_delete = cursor_cmz(cursor); 882 1.1 christos 883 1.1 christos cursor = rbtree_next(cursor); 884 1.1 christos catalog_del_consumer_member_zone(consumer_zone, to_delete); 885 1.1 christos } 886 1.1 christos if(mode == retry_to_add) { 887 1.1 christos mode = just_add; 888 1.1 christos goto retry_adding; 889 1.1 christos } 890 1.1 christos debug_log_consumer_members(consumer_zone); 891 1.1 christos make_catalog_consumer_valid(consumer_zone); 892 1.1 christos } 893 1.1 christos 894 1.1 christos 895 1.1 christos /****************** ****************** 896 1.1 christos ****************** catalog producer zone processing ****************** 897 1.1 christos ****************** ******************/ 898 1.1 christos 899 1.1 christos static int member_id_compare(const void *left, const void *right) 900 1.1 christos { 901 1.1 christos return dname_compare( ((struct catalog_member_zone*)left )->member_id 902 1.1 christos , ((struct catalog_member_zone*)right)->member_id); 903 1.1 christos } 904 1.1 christos 905 1.1 christos static struct xfrd_catalog_producer_zone* 906 1.1 christos xfrd_get_catalog_producer_zone(struct catalog_member_zone* cmz) 907 1.1 christos { 908 1.1 christos struct zone_options *producer_zopt; 909 1.1 christos struct xfrd_catalog_producer_zone* producer_zone; 910 1.1 christos const dname_type* producer_name; 911 1.1 christos const char* producer_name_str; 912 1.1 christos 913 1.1 christos assert(xfrd); 914 1.1 christos if(!cmz || !cmz->options.pattern->catalog_producer_zone) 915 1.1 christos return NULL; 916 1.1 christos 917 1.1 christos /* TODO: Store as dname in pattern->catalog_producer_zone */ 918 1.1 christos producer_name = dname_parse(xfrd->nsd->options->region, 919 1.1 christos cmz->options.pattern->catalog_producer_zone); 920 1.1 christos producer_zopt = zone_options_find(xfrd->nsd->options, producer_name); 921 1.1 christos producer_name_str = dname_to_string(producer_name, NULL); 922 1.1 christos region_recycle( xfrd->nsd->options->region, (void *)producer_name 923 1.1 christos , dname_total_size(producer_name)); 924 1.1 christos if(!producer_zopt) { 925 1.1 christos log_msg(LOG_ERR, "catalog producer zone '%s' not found for " 926 1.1 christos "zone '%s'", producer_name_str, cmz->options.name); 927 1.1 christos return NULL; 928 1.1 christos } 929 1.1 christos if(!zone_is_catalog_producer(producer_zopt)) { 930 1.1 christos log_msg(LOG_ERR, "cannot add catalog producer member " 931 1.1 christos "zone '%s' to non producer zone '%s'", 932 1.1 christos cmz->options.name, producer_zopt->name); 933 1.1 christos return NULL; 934 1.1 christos } 935 1.1 christos producer_name = (dname_type*)producer_zopt->node.key; 936 1.1 christos producer_zone = (struct xfrd_catalog_producer_zone*) 937 1.1 christos rbtree_search(xfrd->catalog_producer_zones, producer_name); 938 1.1 christos if (!producer_zone) { 939 1.1 christos /* Create a new one */ 940 1.1 christos DEBUG(DEBUG_XFRD, 1, (LOG_INFO,"creating catalog producer zone" 941 1.1 christos " '%s'", producer_zopt->name)); 942 1.1 christos producer_zone = (struct xfrd_catalog_producer_zone*) 943 1.1 christos region_alloc(xfrd->region, sizeof(*producer_zone)); 944 1.1 christos memset(producer_zone , 0, sizeof(*producer_zone)); 945 1.1 christos producer_zone->node.key = producer_zopt->node.key; 946 1.1 christos producer_zone->options = producer_zopt; 947 1.1 christos producer_zone->member_ids.region = xfrd->region; 948 1.1 christos producer_zone->member_ids.root = RBTREE_NULL; 949 1.1 christos producer_zone->member_ids.count = 0; 950 1.1 christos producer_zone->member_ids.cmp = member_id_compare; 951 1.1 christos producer_zone->serial = 0; 952 1.1 christos producer_zone->to_delete = NULL; 953 1.1 christos producer_zone->to_add = NULL; 954 1.1 christos producer_zone->latest_pxfr = NULL; 955 1.1 christos producer_zone->axfr = 1; 956 1.1 christos rbtree_insert(xfrd->catalog_producer_zones, 957 1.1 christos (rbnode_type*)producer_zone); 958 1.1 christos } 959 1.1 christos return producer_zone; 960 1.1 christos } 961 1.1 christos 962 1.1 christos void 963 1.1 christos xfrd_add_catalog_producer_member(struct catalog_member_zone* cmz) 964 1.1 christos { 965 1.1 christos struct xfrd_catalog_producer_zone* producer_zone; 966 1.1 christos const dname_type* producer_name; 967 1.1 christos struct xfrd_producer_member* to_add; 968 1.1 christos 969 1.1 christos assert(xfrd); 970 1.1 christos if (!(producer_zone = xfrd_get_catalog_producer_zone(cmz))) { 971 1.1 christos return; 972 1.1 christos } 973 1.1 christos producer_name = producer_zone->node.key; 974 1.1 christos while(!cmz->member_id) { 975 1.1 christos /* Make new member_id with this catalog producer */ 976 1.1 christos char id_label[sizeof(uint32_t)*2+1]; 977 1.1 christos uint32_t new_id = (uint32_t)random_generate(0x7fffffff); 978 1.1 christos 979 1.1 christos hex_ntop((void*)&new_id, sizeof(uint32_t), id_label, sizeof(id_label)); 980 1.1 christos id_label[sizeof(uint32_t)*2] = 0; 981 1.1 christos cmz->member_id = label_plus_dname(id_label, 982 1.1 christos label_plus_dname("zones", producer_name)); 983 1.1 christos DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "does member_id %s exist?", 984 1.1 christos dname_to_string(cmz->member_id, NULL))); 985 1.1 christos if (!rbtree_search(&producer_zone->member_ids, cmz)) { 986 1.1 christos cmz->member_id = dname_copy(xfrd->nsd->options->region, 987 1.1 christos cmz->member_id); 988 1.1 christos break; 989 1.1 christos } 990 1.1 christos cmz->member_id = NULL; 991 1.1 christos } 992 1.1 christos cmz->node.key = cmz; 993 1.1 christos rbtree_insert(&producer_zone->member_ids, &cmz->node); 994 1.1 christos 995 1.1 christos /* Put data to be added to the producer zone to the to_add stack */ 996 1.1 christos to_add = (struct xfrd_producer_member*)region_alloc(xfrd->region, 997 1.1 christos sizeof(struct xfrd_producer_member)); 998 1.1 christos to_add->member_id = cmz->member_id; 999 1.1 christos to_add->member_zone_name = (dname_type*)cmz->options.node.key; 1000 1.1 christos to_add->group_name = cmz->options.pattern->pname; 1001 1.1 christos to_add->next = producer_zone->to_add; 1002 1.1 christos producer_zone->to_add = to_add; 1003 1.1 christos } 1004 1.1 christos 1005 1.1 christos int 1006 1.1 christos xfrd_del_catalog_producer_member(struct xfrd_state* xfrd, 1007 1.1 christos const dname_type* member_zone_name) 1008 1.1 christos { 1009 1.1 christos struct xfrd_producer_member* to_delete; 1010 1.1 christos struct catalog_member_zone* cmz; 1011 1.1 christos struct xfrd_catalog_producer_zone* producer_zone; 1012 1.1 christos 1013 1.1 christos if(!(cmz = as_catalog_member_zone(zone_options_find(xfrd->nsd->options, 1014 1.1 christos member_zone_name))) 1015 1.1 christos || !(producer_zone = xfrd_get_catalog_producer_zone(cmz)) 1016 1.1 christos || !rbtree_delete(&producer_zone->member_ids, cmz)) 1017 1.1 christos return 0; 1018 1.1 christos to_delete = (struct xfrd_producer_member*)region_alloc(xfrd->region, 1019 1.1 christos sizeof(struct xfrd_producer_member)); 1020 1.1 christos to_delete->member_id = cmz->member_id; cmz->member_id = NULL; 1021 1.1 christos cmz->node = *RBTREE_NULL; 1022 1.1 christos to_delete->member_zone_name = member_zone_name; 1023 1.1 christos to_delete->group_name = cmz->options.pattern->pname; 1024 1.1 christos to_delete->next = producer_zone->to_delete; 1025 1.1 christos producer_zone->to_delete = to_delete; 1026 1.1 christos return 1; 1027 1.1 christos } 1028 1.1 christos 1029 1.1 christos static int 1030 1.1 christos try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, 1031 1.1 christos uint32_t serial) 1032 1.1 christos { 1033 1.1 christos size_t mark = buffer_position(packet); 1034 1.1 christos 1035 1.1 christos if(try_buffer_write(packet, dname_name(owner), owner->name_size) 1036 1.1 christos && try_buffer_write_u16(packet, TYPE_SOA) 1037 1.1 christos && try_buffer_write_u16(packet, CLASS_IN) 1038 1.1 christos && try_buffer_write_u32(packet, 0) /* TTL*/ 1039 1.1 christos && try_buffer_write_u16(packet, 9 + 9 + 5 * sizeof(uint32_t)) 1040 1.1 christos && try_buffer_write(packet, "\007invalid\000", 9) /* primary */ 1041 1.1 christos && try_buffer_write(packet, "\007invalid\000", 9) /* mailbox */ 1042 1.1 christos && try_buffer_write_u32(packet, serial) /* serial */ 1043 1.1 christos && try_buffer_write_u32(packet, 3600) /* refresh*/ 1044 1.1 christos && try_buffer_write_u32(packet, 600) /* retry */ 1045 1.1 christos && try_buffer_write_u32(packet, 2147483646) /* expire */ 1046 1.1 christos && try_buffer_write_u32(packet, 0) /* minimum */) { 1047 1.1 christos ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1048 1.1 christos return 1; 1049 1.1 christos } 1050 1.1 christos buffer_set_position(packet, mark); 1051 1.1 christos return 0; 1052 1.1 christos } 1053 1.1 christos 1054 1.1 christos static int 1055 1.1 christos try_buffer_write_RR(buffer_type* packet, const dname_type* owner, 1056 1.1 christos uint16_t rr_type, uint16_t rdata_len, const void* rdata) 1057 1.1 christos { 1058 1.1 christos size_t mark = buffer_position(packet); 1059 1.1 christos 1060 1.1 christos if(try_buffer_write(packet, dname_name(owner), owner->name_size) 1061 1.1 christos && try_buffer_write_u16(packet, rr_type) 1062 1.1 christos && try_buffer_write_u16(packet, CLASS_IN) 1063 1.1 christos && try_buffer_write_u32(packet, 0) /* TTL*/ 1064 1.1 christos && try_buffer_write_u16(packet, rdata_len) 1065 1.1 christos && try_buffer_write(packet, rdata, rdata_len)) { 1066 1.1 christos ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1067 1.1 christos return 1; 1068 1.1 christos } 1069 1.1 christos buffer_set_position(packet, mark); 1070 1.1 christos return 0; 1071 1.1 christos } 1072 1.1 christos 1073 1.1 christos static inline int 1074 1.1 christos try_buffer_write_PTR(buffer_type* packet, const dname_type* owner, 1075 1.1 christos const dname_type* name) 1076 1.1 christos { 1077 1.1 christos return try_buffer_write_RR(packet, owner, TYPE_PTR, 1078 1.1 christos name->name_size, dname_name(name)); 1079 1.1 christos } 1080 1.1 christos 1081 1.1 christos static int 1082 1.1 christos try_buffer_write_TXT(buffer_type* packet, const dname_type* name, 1083 1.1 christos const char *txt) 1084 1.1 christos { 1085 1.1 christos size_t mark = buffer_position(packet); 1086 1.1 christos size_t len = strlen(txt); 1087 1.1 christos 1088 1.1 christos if(len > 255) { 1089 1.1 christos log_msg(LOG_ERR, "cannot make '%s 0 IN TXT \"%s\"': rdata " 1090 1.1 christos "field too long", dname_to_string(name, NULL), txt); 1091 1.1 christos return 1; 1092 1.1 christos } 1093 1.1 christos if(try_buffer_write(packet, dname_name(name), name->name_size) 1094 1.1 christos && try_buffer_write_u16(packet, TYPE_TXT) 1095 1.1 christos && try_buffer_write_u16(packet, CLASS_IN) 1096 1.1 christos && try_buffer_write_u32(packet, 0) /* TTL*/ 1097 1.1 christos && try_buffer_write_u16(packet, len + 1) 1098 1.1 christos && try_buffer_write_u8(packet, len) 1099 1.1 christos && try_buffer_write_string(packet, txt)) { 1100 1.1 christos ANCOUNT_SET(packet, ANCOUNT(packet) + 1); 1101 1.1 christos return 1; 1102 1.1 christos } 1103 1.1 christos buffer_set_position(packet, mark); 1104 1.1 christos return 0; 1105 1.1 christos } 1106 1.1 christos 1107 1.1 christos static void 1108 1.1 christos xfr_writer_init(struct xfrd_xfr_writer* xw, 1109 1.1 christos struct xfrd_catalog_producer_zone* producer_zone) 1110 1.1 christos { 1111 1.1 christos xw->producer_zone = producer_zone; 1112 1.1 christos memset(&xw->packet, 0, sizeof(xw->packet)); 1113 1.1 christos buffer_create_from(&xw->packet, xw->packet_space, sizeof(xw->packet_space)); 1114 1.1 christos buffer_write(&xw->packet, "\000\000\000\000\000\000" 1115 1.1 christos "\000\000\000\000\000\000", 12); /* header */ 1116 1.1 christos xw->seq_nr = 0; 1117 1.1 christos xw->old_serial = xw->producer_zone->serial; 1118 1.1 christos xw->new_serial = (uint32_t)xfrd_time(); 1119 1.1 christos if(xw->new_serial <= xw->old_serial) 1120 1.1 christos xw->new_serial = xw->old_serial + 1; 1121 1.1 christos if(producer_zone->axfr) { 1122 1.1 christos xw->old_serial = 0; 1123 1.1 christos producer_zone->axfr = 0; 1124 1.1 christos } 1125 1.1 christos xw->xfrfilenumber = xfrd->xfrfilenumber++; 1126 1.1 christos } 1127 1.1 christos 1128 1.1 christos static void 1129 1.1 christos xfr_writer_write_packet(struct xfrd_xfr_writer* xw) 1130 1.1 christos { 1131 1.1 christos const dname_type* producer_name = 1132 1.1 christos (const dname_type*)xw->producer_zone->options->node.key; 1133 1.1 christos 1134 1.1 christos /* We want some content at least, so not just a header 1135 1.1 christos * This can occur when final SOA was already written. 1136 1.1 christos */ 1137 1.1 christos if(buffer_position(&xw->packet) == 12) 1138 1.1 christos return; 1139 1.1 christos buffer_flip(&xw->packet); 1140 1.1 christos diff_write_packet( dname_to_string(producer_name, NULL) 1141 1.1 christos , xw->producer_zone->options->pattern->pname 1142 1.1 christos , xw->old_serial, xw->new_serial, xw->seq_nr 1143 1.1 christos , buffer_begin(&xw->packet), buffer_limit(&xw->packet) 1144 1.1 christos , xfrd->nsd, xw->xfrfilenumber); 1145 1.1 christos xw->seq_nr += 1; 1146 1.1 christos buffer_clear(&xw->packet); 1147 1.1 christos buffer_write(&xw->packet, "\000\000\000\000\000\000" 1148 1.1 christos "\000\000\000\000\000\000", 12); /* header */ 1149 1.1 christos } 1150 1.1 christos 1151 1.1 christos 1152 1.1 christos static void 1153 1.1 christos xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, ...) 1154 1.1 christos { 1155 1.1 christos va_list args; 1156 1.1 christos char msg[1024]; 1157 1.1 christos const dname_type* producer_name = 1158 1.1 christos (const dname_type*)xw->producer_zone->options->node.key; 1159 1.1 christos 1160 1.1 christos va_start(args, fmt); 1161 1.1 christos if (vsnprintf(msg, sizeof(msg), fmt, args) >= (int)sizeof(msg)) { 1162 1.1 christos log_msg(LOG_WARNING, "truncated diff commit message: '%s'", 1163 1.1 christos msg); 1164 1.1 christos } 1165 1.1 christos xfr_writer_write_packet(xw); /* Write remaining data */ 1166 1.1 christos diff_write_commit( dname_to_string(producer_name, NULL) 1167 1.1 christos , xw->old_serial, xw->new_serial 1168 1.1 christos , xw->seq_nr /* Number of packets */ 1169 1.1 christos , 1, msg, xfrd->nsd, xw->xfrfilenumber); 1170 1.1 christos task_new_apply_xfr( xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task 1171 1.1 christos , producer_name 1172 1.1 christos , xw->old_serial, xw->new_serial, xw->xfrfilenumber); 1173 1.1 christos xfrd_set_reload_now(xfrd); 1174 1.1 christos } 1175 1.1 christos 1176 1.1 christos static void 1177 1.1 christos xfrd_process_catalog_producer_zone( 1178 1.1 christos struct xfrd_catalog_producer_zone* producer_zone) 1179 1.1 christos { 1180 1.1 christos struct xfrd_xfr_writer xw; 1181 1.1 christos dname_type* producer_name; 1182 1.1 christos struct xfrd_producer_xfr* pxfr; 1183 1.1 christos 1184 1.1 christos if(!producer_zone->to_add && !producer_zone->to_delete) 1185 1.1 christos return; /* No changes */ 1186 1.1 christos 1187 1.1 christos producer_name = (dname_type*)producer_zone->node.key; 1188 1.1 christos xfr_writer_init(&xw, producer_zone); 1189 1.1 christos xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1190 1.1 christos 1191 1.1 christos if(xw.old_serial == 0) { 1192 1.1 christos /* initial deployment */ 1193 1.1 christos assert(producer_zone->to_add && !producer_zone->to_delete); 1194 1.1 christos 1195 1.1 christos xfr_writer_add_RR (&xw, producer_name 1196 1.1 christos , TYPE_NS, 9, "\007invalid\000"); 1197 1.1 christos xfr_writer_add_TXT(&xw, label_plus_dname("version" 1198 1.1 christos , producer_name), "2"); 1199 1.1 christos goto add_member_zones; 1200 1.1 christos } 1201 1.1 christos /* IXFR */ 1202 1.1 christos xfr_writer_add_SOA(&xw, producer_name, xw.old_serial); 1203 1.1 christos while(producer_zone->to_delete) { 1204 1.1 christos struct xfrd_producer_member* to_delete = 1205 1.1 christos producer_zone->to_delete; 1206 1.1 christos 1207 1.1 christos /* Pop to_delete from stack */ 1208 1.1 christos producer_zone->to_delete = to_delete->next; 1209 1.1 christos to_delete->next = NULL; 1210 1.1 christos 1211 1.1 christos /* Write <member_id> PTR <member_name> */ 1212 1.1 christos xfr_writer_add_PTR(&xw, to_delete->member_id 1213 1.1 christos , to_delete->member_zone_name); 1214 1.1 christos 1215 1.1 christos /* Write group.<member_id> TXT <pattern> */ 1216 1.1 christos xfr_writer_add_TXT( &xw 1217 1.1 christos , label_plus_dname("group" 1218 1.1 christos , to_delete->member_id) 1219 1.1 christos , to_delete->group_name); 1220 1.1 christos 1221 1.1 christos region_recycle( xfrd->nsd->options->region 1222 1.1 christos , (void *)to_delete->member_id 1223 1.1 christos , dname_total_size(to_delete->member_id)); 1224 1.1 christos region_recycle( xfrd->region /* allocated in perform_delzone */ 1225 1.1 christos , (void *)to_delete->member_zone_name 1226 1.1 christos , dname_total_size(to_delete->member_zone_name)); 1227 1.1 christos /* Don't recycle to_delete->group_name it's pattern->pname */ 1228 1.1 christos region_recycle( xfrd->region, to_delete, sizeof(*to_delete)); 1229 1.1 christos } 1230 1.1 christos xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1231 1.1 christos 1232 1.1 christos add_member_zones: 1233 1.1 christos while(producer_zone->to_add) { 1234 1.1 christos struct xfrd_producer_member* to_add = producer_zone->to_add; 1235 1.1 christos 1236 1.1 christos /* Pop to_add from stack */ 1237 1.1 christos producer_zone->to_add = to_add->next; 1238 1.1 christos to_add->next = NULL; 1239 1.1 christos 1240 1.1 christos /* Write <member_id> PTR <member_name> */ 1241 1.1 christos xfr_writer_add_PTR(&xw, to_add->member_id, 1242 1.1 christos to_add->member_zone_name); 1243 1.1 christos 1244 1.1 christos /* Write group.<member_id> TXT <pattern> */ 1245 1.1 christos xfr_writer_add_TXT( &xw 1246 1.1 christos , label_plus_dname("group" 1247 1.1 christos , to_add->member_id) 1248 1.1 christos , to_add->group_name); 1249 1.1 christos 1250 1.1 christos /* Don't recycle any of the struct attributes as they come 1251 1.1 christos * from zone_option's that are in use 1252 1.1 christos */ 1253 1.1 christos region_recycle(xfrd->region, to_add, sizeof(*to_add)); 1254 1.1 christos } 1255 1.1 christos xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); 1256 1.1 christos xfr_writer_commit(&xw, "xfr for catalog producer zone " 1257 1.1 christos "'%s' with %d members from %u to %u", 1258 1.1 christos dname_to_string(producer_name, NULL), 1259 1.1 christos producer_zone->member_ids.count, 1260 1.1 christos xw.old_serial, xw.new_serial); 1261 1.1 christos producer_zone->serial = xw.new_serial; 1262 1.1 christos 1263 1.1 christos /* Hook up an xfrd_producer_xfr, to delete the xfr file when applied */ 1264 1.1 christos pxfr = (struct xfrd_producer_xfr*)region_alloc(xfrd->region, 1265 1.1 christos sizeof(struct xfrd_producer_xfr)); 1266 1.1 christos pxfr->serial = xw.new_serial; 1267 1.1 christos pxfr->xfrfilenumber = xw.xfrfilenumber; 1268 1.1 christos if((pxfr->next = producer_zone->latest_pxfr)) 1269 1.1 christos pxfr->next->prev_next_ptr = &pxfr->next; 1270 1.1 christos pxfr->prev_next_ptr = &producer_zone->latest_pxfr; 1271 1.1 christos producer_zone->latest_pxfr = pxfr; 1272 1.1 christos } 1273 1.1 christos 1274 1.1 christos void xfrd_process_catalog_producer_zones() 1275 1.1 christos { 1276 1.1 christos struct xfrd_catalog_producer_zone* producer_zone; 1277 1.1 christos 1278 1.1 christos RBTREE_FOR(producer_zone, struct xfrd_catalog_producer_zone*, 1279 1.1 christos xfrd->catalog_producer_zones) { 1280 1.1 christos xfrd_process_catalog_producer_zone(producer_zone); 1281 1.1 christos } 1282 1.1 christos } 1283 1.1 christos 1284