diff --git a/Makefile.in b/Makefile.in index 1ef3b948d..a9cc03867 100644 --- a/Makefile.in +++ b/Makefile.in @@ -77,7 +77,7 @@ TARGETS=nsd nsd-checkconf nsd-checkzone nsd-control nsd.conf.sample nsd-control- MANUALS=nsd.8 nsd-checkconf.8 nsd-checkzone.8 nsd-control.8 nsd.conf.5 COMMON_OBJ=answer.o axfr.o ixfr.o ixfrcreate.o buffer.o configlexer.o configparser.o dname.o dns.o edns.o iterated_hash.o lookup3.o namedb.o nsec3.o options.o packet.o query.o rbtree.o radtree.o rdata.o region-allocator.o rrl.o siphash.o tsig.o tsig-openssl.o udb.o util.o bitset.o popen3.o proxy_protocol.o -XFRD_OBJ=xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o $(DNSTAP_OBJ) +XFRD_OBJ=xfrd-catalog-zones.o xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o $(DNSTAP_OBJ) NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o nsd.o server.o dbaccess.o dbcreate.o zlexer.o zonec.o zparser.o verify.o ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-checkzone.o nsd-control.o nsd-mem.o xfr-inspect.o NSD_CHECKCONF_OBJ=$(COMMON_OBJ) nsd-checkconf.o @@ -431,15 +431,15 @@ configparser.o: configparser.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/opti $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h configparser.h dbaccess.o: $(srcdir)/dbaccess.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/options.h $(srcdir)/rdata.h \ - $(srcdir)/udb.h $(srcdir)/zonec.h $(srcdir)/nsec3.h $(srcdir)/difffile.h $(srcdir)/nsd.h $(srcdir)/edns.h \ - $(srcdir)/bitset.h $(srcdir)/ixfr.h $(srcdir)/query.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/ixfrcreate.h + $(srcdir)/udb.h $(srcdir)/zonec.h $(srcdir)/nsec3.h $(srcdir)/difffile.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/ixfr.h $(srcdir)/query.h \ + $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/ixfrcreate.h dbcreate.o: $(srcdir)/dbcreate.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/udb.h \ - $(srcdir)/options.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/ixfr.h $(srcdir)/query.h $(srcdir)/packet.h $(srcdir)/tsig.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/udb.h $(srcdir)/options.h $(srcdir)/nsd.h \ + $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/ixfr.h $(srcdir)/query.h $(srcdir)/packet.h $(srcdir)/tsig.h difffile.o: $(srcdir)/difffile.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/difffile.h $(srcdir)/rbtree.h \ $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \ - $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/xfrd-disk.h $(srcdir)/packet.h $(srcdir)/rdata.h \ - $(srcdir)/nsec3.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/tsig.h $(srcdir)/ixfr.h $(srcdir)/zonec.h + $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/xfrd-disk.h $(srcdir)/packet.h $(srcdir)/rdata.h $(srcdir)/nsec3.h $(srcdir)/nsd.h $(srcdir)/edns.h \ + $(srcdir)/bitset.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/tsig.h $(srcdir)/ixfr.h $(srcdir)/zonec.h $(srcdir)/xfrd-catalog-zones.h $(srcdir)/xfrd.h dname.o: $(srcdir)/dname.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/dns.h $(srcdir)/dname.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h \ $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/tsig.h @@ -468,8 +468,8 @@ netio.o: $(srcdir)/netio.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/netio.h $(srcdir)/util.h nsd.o: $(srcdir)/nsd.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/tsig.h $(srcdir)/dname.h \ - $(srcdir)/remote.h $(srcdir)/xfrd-disk.h $(srcdir)/dnstap/dnstap_collector.h $(srcdir)/util/proxy_protocol.h \ - config.h $(srcdir)/compat/cpuset.h + $(srcdir)/remote.h $(srcdir)/xfrd-disk.h $(srcdir)/ipc.h $(srcdir)/netio.h $(srcdir)/util/proxy_protocol.h config.h \ + $(srcdir)/compat/cpuset.h nsd-checkconf.o: $(srcdir)/nsd-checkconf.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h $(srcdir)/rrl.h $(srcdir)/query.h \ $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h @@ -482,15 +482,15 @@ nsd-control.o: $(srcdir)/nsd-control.c config.h $(srcdir)/compat/cpuset.h $(srcd $(srcdir)/dns.h $(srcdir)/radtree.h nsd-mem.o: $(srcdir)/nsd-mem.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/tsig.h $(srcdir)/dname.h $(srcdir)/options.h $(srcdir)/rbtree.h \ - $(srcdir)/namedb.h $(srcdir)/radtree.h + $(srcdir)/namedb.h $(srcdir)/radtree.h $(srcdir)/difffile.h $(srcdir)/udb.h nsec3.o: $(srcdir)/nsec3.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsec3.h $(srcdir)/iterated_hash.h \ $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \ $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/answer.h $(srcdir)/packet.h $(srcdir)/query.h $(srcdir)/tsig.h \ - $(srcdir)/udb.h $(srcdir)/options.h + $(srcdir)/options.h options.o: $(srcdir)/options.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/options.h \ $(srcdir)/region-allocator.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h \ $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/ixfr.h $(srcdir)/difffile.h \ - $(srcdir)/udb.h $(srcdir)/rrl.h configparser.h + $(srcdir)/udb.h $(srcdir)/rrl.h $(srcdir)/xfrd.h configparser.h packet.o: $(srcdir)/packet.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/packet.h $(srcdir)/dns.h $(srcdir)/namedb.h \ $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/query.h \ $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/tsig.h $(srcdir)/rdata.h @@ -507,8 +507,8 @@ region-allocator.o: $(srcdir)/region-allocator.c config.h $(srcdir)/compat/cpuse $(srcdir)/region-allocator.h $(srcdir)/util.h remote.o: $(srcdir)/remote.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/remote.h $(srcdir)/util.h $(srcdir)/xfrd.h \ $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/dns.h $(srcdir)/radtree.h \ - $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-notify.h $(srcdir)/xfrd-tcp.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h \ - $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/netio.h + $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-catalog-zones.h $(srcdir)/xfrd-notify.h $(srcdir)/xfrd-tcp.h $(srcdir)/nsd.h \ + $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/netio.h rrl.o: $(srcdir)/rrl.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/rrl.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/nsd.h $(srcdir)/edns.h \ $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/lookup3.h $(srcdir)/options.h @@ -516,16 +516,14 @@ server.o: $(srcdir)/server.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/axfr.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/query.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/packet.h $(srcdir)/tsig.h $(srcdir)/netio.h $(srcdir)/xfrd.h $(srcdir)/options.h $(srcdir)/xfrd-tcp.h \ $(srcdir)/xfrd-disk.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/nsec3.h $(srcdir)/ipc.h $(srcdir)/remote.h $(srcdir)/lookup3.h $(srcdir)/rrl.h \ - $(srcdir)/ixfr.h $(srcdir)/dnstap/dnstap_collector.h $(srcdir)/verify.h $(srcdir)/util/proxy_protocol.h config.h \ - $(srcdir)/compat/cpuset.h + $(srcdir)/ixfr.h $(srcdir)/verify.h $(srcdir)/util/proxy_protocol.h config.h $(srcdir)/compat/cpuset.h siphash.o: $(srcdir)/siphash.c tsig.o: $(srcdir)/tsig.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/tsig.h $(srcdir)/buffer.h \ $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/tsig-openssl.h $(srcdir)/dns.h $(srcdir)/packet.h $(srcdir)/namedb.h \ $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/query.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h tsig-openssl.o: $(srcdir)/tsig-openssl.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/tsig-openssl.h \ $(srcdir)/region-allocator.h $(srcdir)/tsig.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dname.h -udb.o: $(srcdir)/udb.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/udb.h $(srcdir)/lookup3.h $(srcdir)/util.h \ - $(srcdir)/radtree.h +udb.o: $(srcdir)/udb.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/udb.h $(srcdir)/lookup3.h $(srcdir)/util.h util.o: $(srcdir)/util.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/util.h $(srcdir)/region-allocator.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h $(srcdir)/zonec.h $(srcdir)/nsd.h $(srcdir)/edns.h \ $(srcdir)/bitset.h @@ -534,9 +532,13 @@ verify.o: $(srcdir)/verify.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/region $(srcdir)/options.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/verify.h $(srcdir)/popen3.h xfrd.o: $(srcdir)/xfrd.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h \ $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h \ - $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd-notify.h $(srcdir)/netio.h $(srcdir)/nsd.h \ - $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/rdata.h $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h $(srcdir)/rrl.h \ - $(srcdir)/query.h $(srcdir)/dnstap/dnstap_collector.h + $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd-notify.h \ + $(srcdir)/xfrd-catalog-zones.h $(srcdir)/netio.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h $(srcdir)/rdata.h \ + $(srcdir)/difffile.h $(srcdir)/udb.h $(srcdir)/ipc.h $(srcdir)/remote.h $(srcdir)/rrl.h $(srcdir)/query.h +xfrd-catalog-zones.o: $(srcdir)/xfrd-catalog-zones.c config.h $(srcdir)/compat/cpuset.h \ + $(srcdir)/difffile.h $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h \ + $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h $(srcdir)/packet.h \ + $(srcdir)/xfrd-catalog-zones.h $(srcdir)/xfrd.h $(srcdir)/tsig.h $(srcdir)/xfrd-notify.h xfrd-disk.o: $(srcdir)/xfrd-disk.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/xfrd-disk.h $(srcdir)/xfrd.h \ $(srcdir)/rbtree.h $(srcdir)/region-allocator.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h \ $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h @@ -546,16 +548,17 @@ xfrd-notify.o: $(srcdir)/xfrd-notify.c config.h $(srcdir)/compat/cpuset.h $(srcd xfrd-tcp.o: $(srcdir)/xfrd-tcp.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/bitset.h $(srcdir)/xfrd-tcp.h $(srcdir)/xfrd.h $(srcdir)/rbtree.h \ $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/options.h $(srcdir)/tsig.h $(srcdir)/packet.h $(srcdir)/xfrd-disk.h -xfr-inspect.o: $(srcdir)/xfr-inspect.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/udb.h \ - $(srcdir)/dns.h $(srcdir)/util.h $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/packet.h $(srcdir)/namedb.h \ - $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h $(srcdir)/difffile.h $(srcdir)/options.h +xfr-inspect.o: $(srcdir)/xfr-inspect.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/util.h $(srcdir)/buffer.h \ + $(srcdir)/region-allocator.h $(srcdir)/packet.h $(srcdir)/dns.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \ + $(srcdir)/rdata.h $(srcdir)/difffile.h $(srcdir)/options.h $(srcdir)/udb.h zlexer.o: zlexer.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h zparser.h zonec.o: $(srcdir)/zonec.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/zonec.h $(srcdir)/namedb.h $(srcdir)/dname.h \ $(srcdir)/buffer.h $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/rdata.h \ zparser.h $(srcdir)/options.h $(srcdir)/nsec3.h zparser.o: zparser.c config.h $(srcdir)/compat/cpuset.h $(srcdir)/dname.h $(srcdir)/buffer.h \ - $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h + $(srcdir)/region-allocator.h $(srcdir)/util.h $(srcdir)/namedb.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h $(srcdir)/zonec.h \ + zparser.h b64_ntop.o: $(srcdir)/compat/b64_ntop.c config.h $(srcdir)/compat/cpuset.h b64_pton.o: $(srcdir)/compat/b64_pton.c config.h $(srcdir)/compat/cpuset.h basename.o: $(srcdir)/compat/basename.c @@ -597,8 +600,7 @@ cutest_iter.o: $(srcdir)/tpkg/cutest/cutest_iter.c config.h $(srcdir)/compat/cpu cutest_namedb.o: $(srcdir)/tpkg/cutest/cutest_namedb.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ $(srcdir)/rbtree.h $(srcdir)/namedb.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/nsec3.h $(srcdir)/udb.h \ - $(srcdir)/udb.h $(srcdir)/difffile.h $(srcdir)/namedb.h $(srcdir)/options.h $(srcdir)/zonec.h $(srcdir)/nsd.h \ - $(srcdir)/edns.h $(srcdir)/bitset.h + $(srcdir)/difffile.h $(srcdir)/namedb.h $(srcdir)/options.h $(srcdir)/udb.h $(srcdir)/zonec.h $(srcdir)/nsd.h $(srcdir)/edns.h $(srcdir)/bitset.h cutest_options.o: $(srcdir)/tpkg/cutest/cutest_options.c config.h $(srcdir)/compat/cpuset.h \ $(srcdir)/tpkg/cutest/cutest.h $(srcdir)/region-allocator.h $(srcdir)/options.h $(srcdir)/region-allocator.h \ $(srcdir)/rbtree.h $(srcdir)/util.h $(srcdir)/dname.h $(srcdir)/buffer.h $(srcdir)/util.h $(srcdir)/nsd.h $(srcdir)/dns.h $(srcdir)/edns.h $(srcdir)/bitset.h diff --git a/buffer.h b/buffer.h index 9cf532147..1967173fd 100644 --- a/buffer.h +++ b/buffer.h @@ -260,6 +260,24 @@ buffer_write(buffer_type *buffer, const void *data, size_t count) buffer->_position += count; } +static inline int +try_buffer_write_at(buffer_type *buffer, size_t at, const void *data, size_t count) +{ + if(!buffer_available_at(buffer, at, count)) + return 0; + memcpy(buffer->_data + at, data, count); + return 1; +} + +static inline int +try_buffer_write(buffer_type *buffer, const void *data, size_t count) +{ + if(!try_buffer_write_at(buffer, buffer->_position, data, count)) + return 0; + buffer->_position += count; + return 1; +} + static inline void buffer_write_string_at(buffer_type *buffer, size_t at, const char *str) { @@ -272,6 +290,18 @@ buffer_write_string(buffer_type *buffer, const char *str) buffer_write(buffer, str, strlen(str)); } +static inline int +try_buffer_write_string_at(buffer_type *buffer, size_t at, const char *str) +{ + return try_buffer_write_at(buffer, at, str, strlen(str)); +} + +static inline int +try_buffer_write_string(buffer_type *buffer, const char *str) +{ + return try_buffer_write(buffer, str, strlen(str)); +} + static inline void buffer_write_u8_at(buffer_type *buffer, size_t at, uint8_t data) { @@ -328,6 +358,78 @@ buffer_write_u64(buffer_type *buffer, uint64_t data) buffer->_position += sizeof(data); } +static inline int +try_buffer_write_u8_at(buffer_type *buffer, size_t at, uint8_t data) +{ + if(!buffer_available_at(buffer, at, sizeof(data))) + return 0; + buffer->_data[at] = data; + return 1; +} + +static inline int +try_buffer_write_u8(buffer_type *buffer, uint8_t data) +{ + if(!try_buffer_write_u8_at(buffer, buffer->_position, data)) + return 0; + buffer->_position += sizeof(data); + return 1; +} + +static inline int +try_buffer_write_u16_at(buffer_type *buffer, size_t at, uint16_t data) +{ + if(!buffer_available_at(buffer, at, sizeof(data))) + return 0; + write_uint16(buffer->_data + at, data); + return 1; +} + +static inline int +try_buffer_write_u16(buffer_type *buffer, uint16_t data) +{ + if(!try_buffer_write_u16_at(buffer, buffer->_position, data)) + return 0; + buffer->_position += sizeof(data); + return 1; +} + +static inline int +try_buffer_write_u32_at(buffer_type *buffer, size_t at, uint32_t data) +{ + if(!buffer_available_at(buffer, at, sizeof(data))) + return 0; + write_uint32(buffer->_data + at, data); + return 1; +} + +static inline int +try_buffer_write_u32(buffer_type *buffer, uint32_t data) +{ + if(!try_buffer_write_u32_at(buffer, buffer->_position, data)) + return 0; + buffer->_position += sizeof(data); + return 1; +} + +static inline int +try_buffer_write_u64_at(buffer_type *buffer, size_t at, uint64_t data) +{ + if(!buffer_available_at(buffer, at, sizeof(data))) + return 0; + write_uint64(buffer->_data + at, data); + return 1; +} + +static inline int +try_buffer_write_u64(buffer_type *buffer, uint64_t data) +{ + if(!try_buffer_write_u64_at(buffer, buffer->_position, data)) + return 0; + buffer->_position += sizeof(data); + return 1; +} + static inline void buffer_read_at(buffer_type *buffer, size_t at, void *data, size_t count) { diff --git a/configlexer.lex b/configlexer.lex index 1525e7971..03fa21bc3 100644 --- a/configlexer.lex +++ b/configlexer.lex @@ -319,6 +319,9 @@ verifier{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERIFIER; } verifier-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERIFIER_COUNT; } verifier-feed-zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERIFIER_FEED_ZONE; } verifier-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_VERIFIER_TIMEOUT; } +catalog{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CATALOG; } +catalog-member-pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CATALOG_MEMBER_PATTERN; } +catalog-producer-zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CATALOG_PRODUCER_ZONE; } {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} servers={UNQUOTEDLETTER}* { diff --git a/configparser.y b/configparser.y index fe63a5e36..8d8e5cecf 100644 --- a/configparser.y +++ b/configparser.y @@ -33,6 +33,7 @@ extern config_parser_state_type *cfg_parser; static void append_acl(struct acl_options **list, struct acl_options *acl); static void add_to_last_acl(struct acl_options **list, char *ac); static int parse_boolean(const char *str, int *bln); +static int parse_catalog_role(const char *str, int *role); static int parse_expire_expr(const char *str, long long *num, uint8_t *expr); static int parse_number(const char *str, long long *num); static int parse_range(const char *str, long long *low, long long *high); @@ -53,6 +54,7 @@ struct component { struct cpu_option *cpu; char **strv; struct component *comp; + int role; } %token STRING @@ -63,6 +65,7 @@ struct component { %type cpus %type command %type arguments +%type catalog_role /* server */ %token VAR_SERVER @@ -202,6 +205,9 @@ struct component { %token VAR_IXFR_SIZE %token VAR_IXFR_NUMBER %token VAR_CREATE_IXFR +%token VAR_CATALOG +%token VAR_CATALOG_MEMBER_PATTERN +%token VAR_CATALOG_PRODUCER_ZONE /* zone */ %token VAR_ZONE @@ -907,6 +913,8 @@ pattern_or_zone_option: | VAR_REQUEST_XFR STRING STRING { acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3); + if(cfg_parser->pattern->catalog_role == CATALOG_ROLE_PRODUCER) + yyerror("catalog producer zones cannot be secondary zones"); if(acl->blocked) yyerror("blocked address used for request-xfr"); if(acl->rangetype != acl_range_single) @@ -1035,7 +1043,32 @@ pattern_or_zone_option: | VAR_VERIFIER_FEED_ZONE boolean { cfg_parser->pattern->verifier_feed_zone = $2; } | VAR_VERIFIER_TIMEOUT number - { cfg_parser->pattern->verifier_timeout = $2; } ; + { cfg_parser->pattern->verifier_timeout = $2; } + | VAR_CATALOG catalog_role + { + if($2 == CATALOG_ROLE_PRODUCER && cfg_parser->pattern->request_xfr) + yyerror("catalog producer zones cannot be secondary zones"); + cfg_parser->pattern->catalog_role = $2; + cfg_parser->pattern->catalog_role_is_default = 0; + } + | VAR_CATALOG_MEMBER_PATTERN STRING + { + cfg_parser->pattern->catalog_member_pattern = region_strdup(cfg_parser->opt->region, $2); + } + | VAR_CATALOG_PRODUCER_ZONE STRING + { + dname_type *dname; + + if(cfg_parser->zone) { + yyerror("catalog-producer-zone option is for patterns only and cannot " + "be used in a zone clause"); + } else if(!(dname = (dname_type *)dname_parse(cfg_parser->opt->region, $2))) { + yyerror("bad catalog producer name %s", $2); + } else { + region_recycle(cfg_parser->opt->region, dname, dname_total_size(dname)); + cfg_parser->pattern->catalog_producer_zone = region_strdup(cfg_parser->opt->region, $2); + } + }; verify: VAR_VERIFY verify_block ; @@ -1146,6 +1179,15 @@ tlsauth_option: { char *tls_auth_name = region_strdup(cfg_parser->opt->region, $1); add_to_last_acl(&cfg_parser->pattern->request_xfr, tls_auth_name);} ; +catalog_role: + STRING + { + if(!parse_catalog_role($1, &$$)) { + yyerror("expected consumer or producer"); + YYABORT; /* trigger a parser error */ + } + } ; + %% static void @@ -1264,3 +1306,18 @@ parse_range(const char *str, long long *low, long long *high) return 0; } + +static int +parse_catalog_role(const char *str, int *role) +{ + if(strcasecmp(str, "consumer") == 0) { + *role = CATALOG_ROLE_CONSUMER; + } else if(strcmp(str, "producer") == 0) { + *role = CATALOG_ROLE_PRODUCER; + } else { + return 0; + } + return 1; +} + + diff --git a/configure.ac b/configure.ac index 340592d5b..19a850276 100644 --- a/configure.ac +++ b/configure.ac @@ -971,6 +971,16 @@ case "$enable_checking" in ;; esac +AC_ARG_ENABLE(log-role, AS_HELP_STRING([--enable-log-role],[Shows the role of processes in the logfile (enable this only for debugging purposes)])) +case "$enable_log_role" in + yes) + AC_DEFINE_UNQUOTED([USE_LOG_PROCESS_ROLE], [], [Define this to show the role of processes in the logfile for debugging purposes.]) + ;; + no|*) + ;; +esac + + AC_ARG_ENABLE(memclean, AS_HELP_STRING([--enable-memclean],[Cleanup memory (at exit) for eg. valgrind, memcheck])) if test "$enable_memclean" = "yes"; then AC_DEFINE_UNQUOTED([MEMCLEAN], [1], [Define this to cleanup memory at exit (eg. for valgrind, etc.)]) fi diff --git a/difffile.c b/difffile.c index ef8ba94e3..c4d18ad96 100644 --- a/difffile.c +++ b/difffile.c @@ -24,6 +24,7 @@ #include "rrl.h" #include "ixfr.h" #include "zonec.h" +#include "xfrd-catalog-zones.h" static int write_64(FILE *out, uint64_t val) @@ -919,7 +920,7 @@ find_or_create_zone(namedb_type* db, const dname_type* zone_name, * by xfrd, who wrote the AXFR or IXFR to disk, so we only * need to add it to our config. * This process does not need linesize and offset zonelist */ - zopt = zone_list_zone_insert(opt, zstr, patname, 0, 0); + zopt = zone_list_zone_insert(opt, zstr, patname); if(!zopt) return 0; } @@ -1273,7 +1274,7 @@ check_for_bad_serial(namedb_type* db, const char* zone_str, uint32_t old_serial) return 0; } -static int +int apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in, struct nsd_options* ATTR_UNUSED(opt), udb_base* taskudb, udb_ptr* last_task, uint32_t xfrfilenr) @@ -1348,7 +1349,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in, if(check_for_bad_serial(nsd->db, zone_buf, old_serial)) { DEBUG(DEBUG_XFRD,1, (LOG_ERR, "skipping diff file commit with bad serial")); - return 1; + return -2; /* Success in "main" process, failure in "xfrd" */ } if(!zone->is_skipped) @@ -1372,7 +1373,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in, diff_update_commit( zone_buf, DIFF_CORRUPT, nsd, xfrfilenr); /* the udb is still dirty, it is bad */ - exit(1); + return -1; /* Fatal! */ } else if(ret == 2) { break; } @@ -1407,7 +1408,7 @@ apply_ixfr_for_zone(nsd_type* nsd, zone_type* zone, FILE* in, /* add/del failures in IXFR, get an AXFR */ diff_update_commit( zone_buf, DIFF_INCONSISTENT, nsd, xfrfilenr); - exit(1); + return -1; /* Fatal! */ } if(ixfr_store) ixfr_store_finish(ixfr_store, nsd, log_buf); @@ -1598,6 +1599,7 @@ void task_new_check_zonefiles(udb_base* udb, udb_ptr* last, const dname_type* zone) { udb_ptr e; + xfrd_check_catalog_consumer_zonefiles(zone); DEBUG(DEBUG_IPC,1, (LOG_INFO, "add task checkzonefiles")); if(!task_create_new_elem(udb, last, &e, sizeof(struct task_list_d) + (zone?dname_total_size(zone):0), zone)) { @@ -2111,13 +2113,23 @@ task_process_apply_xfr(struct nsd* nsd, udb_base* udb, udb_ptr *last_task, return; } /* read and apply zone transfer */ - if(!apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb, - last_task, TASKLIST(task)->yesno)) { + switch(apply_ixfr_for_zone(nsd, zone, df, nsd->options, udb, last_task, + TASKLIST(task)->yesno)) { + case 1: /* Success */ + break; + + case 0: /* Failure */ /* soainfo_gone will be communicated from server_reload, unless preceding updates have been applied */ zone->is_skipped = 1; - } + break; + + case -1:/* Fatal */ + exit(1); + break; + default:break; + } fclose(df); } diff --git a/difffile.h b/difffile.h index ba777c8a5..4a88d4b1d 100644 --- a/difffile.h +++ b/difffile.h @@ -66,6 +66,11 @@ int add_RR(namedb_type* db, const dname_type* dname, buffer_type* packet, size_t rdatalen, zone_type *zone, int* softfail); +/* apply the xfr file identified by xfrfilenr to zone */ +int apply_ixfr_for_zone(struct nsd* nsd, zone_type* zone, FILE* in, + struct nsd_options* opt, udb_base* taskudb, udb_ptr* last_task, + uint32_t xfrfilenr); + enum soainfo_hint { soainfo_ok, soainfo_gone, diff --git a/dname.c b/dname.c index 487d2535a..eced1714e 100644 --- a/dname.c +++ b/dname.c @@ -392,6 +392,12 @@ const char * dname_to_string(const dname_type *dname, const dname_type *origin) { static char buf[MAXDOMAINLEN * 5]; + return dname_to_string_buf(dname, origin, buf); +} + +const char * +dname_to_string_buf(const dname_type *dname, const dname_type *origin, char buf[MAXDOMAINLEN * 5]) +{ size_t i; size_t labels_to_convert = dname->label_count - 1; int absolute = 1; @@ -399,7 +405,7 @@ dname_to_string(const dname_type *dname, const dname_type *origin) const uint8_t *src; if (dname->label_count == 1) { - strlcpy(buf, ".", sizeof(buf)); + strlcpy(buf, ".", MAXDOMAINLEN * 5); return buf; } diff --git a/dname.h b/dname.h index 0d5dc4ac8..e37cb2373 100644 --- a/dname.h +++ b/dname.h @@ -15,6 +15,7 @@ #include "buffer.h" #include "region-allocator.h" +#include "dns.h" /* for MAXDOMAINLEN */ #if defined(NAMEDB_UPPERCASE) || defined(USE_NAMEDB_UPPERCASE) #define DNAME_NORMALIZE toupper @@ -346,6 +347,19 @@ label_next(const uint8_t *label) const char *dname_to_string(const dname_type *dname, const dname_type *origin); +/* + * Convert DNAME to its string representation. The result if written + * to the provided buffer buf, which must be at least 5 times + * MAXDOMAINNAMELEN. + * + * If ORIGIN is provided and DNAME is a subdomain of ORIGIN the dname + * will be represented relative to ORIGIN. + * + * Pre: dname != NULL + */ +const char *dname_to_string_buf(const dname_type *dname, + const dname_type *origin, + char buf[MAXDOMAINLEN * 5]); /* * Create a dname containing the single label specified by STR diff --git a/doc/ChangeLog b/doc/ChangeLog index 261627eeb..8f24ab57f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,12 @@ +15 February 2024: Willem + - Merge #304: Support for Catalog zones version "2" as specified in + RFC 9432. Both the consumer as well as the producer role are + implemented, but only a single catalog consumer zone is allowed. + The "coo" property, only relevant with multiple catalog consumer, + is therefore not supported. The "group" property is supported. + Have a look at the nsd.conf man page for details on how to + configure and use catalog zones. + 12 February 2024: Willem - Allow SOA apex queries to otherwise with allow-query protected zones for clients matching a provide-xfr rule, because clients that are diff --git a/doc/RELNOTES b/doc/RELNOTES index 739579aad..dc3281b43 100644 --- a/doc/RELNOTES +++ b/doc/RELNOTES @@ -7,6 +7,14 @@ FEATURES: protected zones for clients matching a provide-xfr rule, because clients that are allowed to transfer the zone need to be able to query SOA at the apex preceding the actual transfer. + - Merge #304: Support for Catalog zones version "2" as specified in + RFC 9432. Both the consumer as well as the producer role are + implemented, but only a single catalog consumer zone is allowed. + The "coo" property, only relevant with multiple catalog consumer, + is therefore not supported. The "group" property is supported. + Have a look at the nsd.conf man page for details on how to + configure and use catalog zones. + BUG FIXES: - Fix to sync the tests script file common.sh. - Update test script file common.sh. diff --git a/namedb.h b/namedb.h index 65cb1aed2..032d0eb39 100644 --- a/namedb.h +++ b/namedb.h @@ -318,6 +318,10 @@ static inline int domain_is_subdomain(domain_type* d1, domain_type* d2) /* easy printout, to static buffer of dname_to_string, fqdn. */ static inline const char* domain_to_string(domain_type* domain) { return dname_to_string(domain_dname(domain), NULL); } +/* easy printout, to given buffer of dname_to_string, fqdn. */ +static inline const char* domain_to_string_buf(domain_type* domain, char *buf) +{ return dname_to_string_buf(domain_dname(domain), NULL, buf); } + /* * The type covered by the signature in the specified RRSIG RR. @@ -391,6 +395,11 @@ void namedb_read_zonefile(struct nsd* nsd, struct zone* zone, struct udb_base* taskudb, struct udb_ptr* last_task); zone_type* namedb_zone_create(namedb_type* db, const dname_type* dname, struct zone_options* zopt); +static inline zone_type* +namedb_find_or_create_zone(namedb_type *db, const dname_type *dname, + struct zone_options* zopt) +{ zone_type* zone = namedb_find_zone(db, dname); + return zone ? zone : namedb_zone_create(db, dname, zopt); } void namedb_zone_delete(namedb_type* db, zone_type* zone); void namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt); void namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options); diff --git a/nsd-checkconf.c b/nsd-checkconf.c index 9f931ac4c..e0fcaaa0e 100644 --- a/nsd-checkconf.c +++ b/nsd-checkconf.c @@ -585,6 +585,20 @@ static void print_zone_content_elems(pattern_options_type* pat) if(pat->verifier_timeout != VERIFIER_TIMEOUT_INHERIT) { printf("\tverifier-timeout: %d\n", pat->verifier_timeout); } + + if(!pat->catalog_role_is_default) + switch(pat->catalog_role) { + case CATALOG_ROLE_CONSUMER: printf("\tcatalog: consumer\n"); + break; + case CATALOG_ROLE_PRODUCER: printf("\tcatalog: producer\n"); + break; + default : break; + } + + if(pat->catalog_member_pattern) + print_string_var("catalog-member-pattern:", pat->catalog_member_pattern); + if(pat->catalog_producer_zone) + print_string_var("catalog-producer-zone:", pat->catalog_producer_zone); } void @@ -806,8 +820,8 @@ additional_checks(nsd_options_type* opt, const char* filename) "is received?\n", filename, zone->name); errors ++; } - if(!zone_is_slave(zone) && (!zone->pattern->zonefile || - zone->pattern->zonefile[0] == 0)) { + if(!zone_is_slave(zone) && !zone_is_catalog_producer(zone) + && (!zone->pattern->zonefile || zone->pattern->zonefile[0] == 0)) { fprintf(stderr, "%s: zone %s is a primary zone but has " "no zonefile. Where can the data come from?\n", filename, zone->name); @@ -963,7 +977,7 @@ main(int argc, char* argv[]) /* read config file */ options = nsd_options_create(region_create(xalloc, free)); tsig_init(options->region); - if (!parse_options_file(options, configfile, NULL, NULL) || + if (!parse_options_file(options, configfile, NULL, NULL, NULL) || !additional_checks(options, configfile)) { exit(2); } diff --git a/nsd-control.c b/nsd-control.c index 543998230..3ead1fb6a 100644 --- a/nsd-control.c +++ b/nsd-control.c @@ -523,7 +523,7 @@ go(const char* cfgfile, char* svr, int argc, char* argv[]) exit(1); } tsig_init(opt->region); - if(!parse_options_file(opt, cfgfile, NULL, NULL)) { + if(!parse_options_file(opt, cfgfile, NULL, NULL, NULL)) { fprintf(stderr, "could not read config file\n"); exit(1); } diff --git a/nsd-mem.c b/nsd-mem.c index 9f7b6992a..cc8455218 100644 --- a/nsd-mem.c +++ b/nsd-mem.c @@ -274,7 +274,7 @@ main(int argc, char *argv[]) DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, DEFAULT_INITIAL_CLEANUP_SIZE, 1)); tsig_init(nsd.options->region); - if(!parse_options_file(nsd.options, configfile, NULL, NULL)) { + if(!parse_options_file(nsd.options, configfile, NULL, NULL, NULL)) { error("could not read config: %s\n", configfile); } if(!parse_zone_list_file(nsd.options)) { diff --git a/nsd.c b/nsd.c index 2d2be9ec4..3ff231f53 100644 --- a/nsd.c +++ b/nsd.c @@ -1134,7 +1134,7 @@ main(int argc, char *argv[]) pp_init(&write_uint16, &write_uint32); /* Read options */ - if(!parse_options_file(nsd.options, configfile, NULL, NULL)) { + if(!parse_options_file(nsd.options, configfile, NULL, NULL, NULL)) { error("could not read config: %s\n", configfile); } if(!parse_zone_list_file(nsd.options)) { diff --git a/nsd.conf.5.in b/nsd.conf.5.in index 54d0a80ea..e1a3b1929 100644 --- a/nsd.conf.5.in +++ b/nsd.conf.5.in @@ -734,8 +734,10 @@ The zone options such as .BR verify\-zone , .BR verifier , .BR verifier\-feed\-zone , +.BR verifier\-timeout , +.BR catalog , and -.B verifier\-timeout +.B catalog\-member\-pattern can be given. They are applied to the patterns and zones that include this pattern. .SS "Zone Options" @@ -987,10 +989,57 @@ Feed updated zone to verifier over standard input. Default is verifier\-feed\-zone configured in .B verify:\fR. .TP -.B verifier\-timeout: +.B verifier\-timeout:\fR Number of seconds before verifier is forcefully terminated. Specify 0 (zero) to not use a specific timeout. Default is verifier\-timeout from .B verify:\fR. +.TP +.B catalog:\fR +If set to \fIconsumer\fR, catalog zone processing is enabled for the zone. +Only a single zone may be configured as a catalog consumer zone. When more than +one catalog consumer zone is configured, none of them will be processed. +Member zones of the catalog will use the pattern specified by the group +property, or if a group property is missing or invalid, the pattern specified +by the \fBcatalog\-member\-pattern\fR option is used. Group properties are valid +if there is only a single value matching the name of a for member zones valid +pattern. +.IP +A zone with the option set to \fIproducer\fR, can be used to produce a +catalog zone. Member zones for catalog producer zones can be added with +"\fInsd\-control addzone \fR", where has a +\fBcatalog\-producer\-zone\fR option pointing to a catalog producer zone. +Members will get a group property with the pattern name as value. +Catalog producer zones must be primary zones and may not have a +\fBrequest\-xfr\fR option. Catalog producer zones will \fInot\fR read content +from zone files, but will reconstruct the zone on startup from the member zone +entries in @zonelistfile@, specified with the \fBzonelistfile\fR option. +.IP +The status of both catalog consumer and producer zones can be verified with +\fInsd\-control zonestatus\fR. It will show the number of member zones and, if +the catalog zone is invalid, the reason for it to be invalid is shown. +\fInsd\-control zonestatus\fR will also show the entry of a catalog member zone +in the catalog (consumer or producer) zone as \fBcatalog-member-id:\fR. +.IP +A catalog zone can either be catalog consumer zone or a catalog producer zone +but not both. Likewise, catalog member zones can be either a member of catalog +consumer zone or a catalog producer zone but not both. +.IP +Catalog zones contain a list of zones that are served. Use \fBallow\-query: +0.0.0.0/0 BLOCKED\fR and \fBallow\-query: ::0/0 BLOCKED\fR in a catalog zone +zone or pattern clause to prevent revealing the catalog. Also consider using +transfers over TLS to further protect the catalog against eavesdroppers. +.TP +.B catalog\-member\-pattern:\fR +If this option is provided for a catalog consumer zone, members of that catalog +that have a missing or an invalid group property will be added using pattern +. +.TP +.B catalog\-producer\-zone:\fR +This option can only be used in a pattern. Adding a zone using +"\fInsd\-control addzone \fR with a containing this +option, will cause a catalog member entry to be created in the catalog producer +zone . must exist and must be a valid catalog +producer zone. .SS "Key Declarations" The .B key: diff --git a/nsd.conf.sample.in b/nsd.conf.sample.in index 5f97e8171..ddf4a8c07 100644 --- a/nsd.conf.sample.in +++ b/nsd.conf.sample.in @@ -488,6 +488,24 @@ remote-control: # Default is verifier-timeout in verify. # verifier-timeout: 0 + # Turn this zone into a catalog consumer zone. + # The catalog-member-pattern option is the default pattern that + # will be used for members without or with invalid group property. + # catalog: consumer + # catalog-member-pattern: "example-pattern" + + # Turn this zone into a catalog producer zone. + # Member zones can be added using nsd-control addzone + # where is a pattern containing a catalog-producer-zone + # option pointing to this zone. + # catalog: producer + + # Use this pattern to add catalog producer members. "catalog1.invalid" + # needs to be a valid catalog producer zone; i.e. a primary zone + # without a request-xfr option and with and catalog option set to + # producer. + # catalog-producer-member: "catalog1.invalid" + # Fixed zone entries. Here you can config zones that cannot be deleted. # Zones that are dynamically added and deleted are put in the zonelist file. # diff --git a/options.c b/options.c index 5d1194132..1cfeeb948 100644 --- a/options.c +++ b/options.c @@ -21,6 +21,7 @@ #include "difffile.h" #include "rrl.h" #include "bitset.h" +#include "xfrd.h" #include "configparser.h" config_parser_state_type* cfg_parser = 0; @@ -202,7 +203,8 @@ warn_if_directory(const char* filetype, FILE* f, const char* fname) int parse_options_file(struct nsd_options* opt, const char* file, - void (*err)(void*,const char*), void* err_arg) + void (*err)(void*,const char*), void* err_arg, + struct nsd_options* old_opts) { FILE *in = 0; struct pattern_options* pat; @@ -246,6 +248,10 @@ parse_options_file(struct nsd_options* opt, const char* file, RBTREE_FOR(pat, struct pattern_options*, opt->patterns) { + struct pattern_options* old_pat = + old_opts ? pattern_options_find(old_opts, pat->pname) + : NULL; + /* lookup keys for acls */ for(acl=pat->allow_notify; acl; acl=acl->next) { @@ -300,6 +306,43 @@ parse_options_file(struct nsd_options* opt, const char* file, c_error("key %s in pattern %s could not be found", acl->key_name, pat->pname); } + /* lookup zones for catalog-producer-zone options */ + if(pat->catalog_producer_zone) { + struct zone_options* zopt; + const dname_type *dname = dname_parse(opt->region, + pat->catalog_producer_zone); + if(dname == NULL) { + ; /* pass; already erred during parsing */ + + } else if (!(zopt = zone_options_find(opt, dname))) { + c_error("catalog producer zone %s in pattern " + "%s could not be found", + pat->catalog_producer_zone, + pat->pname); + + } else if (!zone_is_catalog_producer(zopt)) { + c_error("catalog-producer-zone %s in pattern " + "%s is not configered as a " + "catalog: producer", + pat->catalog_producer_zone, + pat->pname); + } + } + if( !old_opts /* Okay to add a cat producer member zone pat */ + || (!old_pat) /* But not to add, change or del an existing */ + || ( old_pat && !old_pat->catalog_producer_zone + && !pat->catalog_producer_zone) + || ( old_pat && old_pat->catalog_producer_zone + && pat->catalog_producer_zone + && strcmp( old_pat->catalog_producer_zone + , pat->catalog_producer_zone) == 0)){ + ; /* No existing catalog producer member zone added + * or changed. Everyting is fine: pass */ + } else { + c_error("catalog-producer-zone in pattern %s cannot " + "be removed or changed on a running NSD", + pat->pname); + } } if(cfg_parser->errors > 0) @@ -372,18 +415,26 @@ zone_list_free_insert(struct nsd_options* opt, int linesize, off_t off) opt->zonefree_number++; } -struct zone_options* -zone_list_zone_insert(struct nsd_options* opt, const char* nm, - const char* patnm, int linesize, off_t off) +static struct zone_options* +zone_list_member_zone_insert(struct nsd_options* opt, const char* nm, + const char* patnm, int linesize, off_t off, const char* mem_idnm, + new_member_id_type new_member_id) { struct pattern_options* pat = pattern_options_find(opt, patnm); + struct catalog_member_zone* cmz = NULL; struct zone_options* zone; + char member_id_str[MAXDOMAINLEN * 5 + 3] = "ERROR!"; + DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "zone_list_zone_insert(\"%s\", \"%s\"" + ", %d, \"%s\")", nm, patnm, linesize, + (mem_idnm ? mem_idnm : ""))); if(!pat) { log_msg(LOG_ERR, "pattern does not exist for zone %s " "pattern %s", nm, patnm); return NULL; } - zone = zone_options_create(opt->region); + zone = pat->catalog_producer_zone + ? &(cmz = catalog_member_zone_create(opt->region))->options + : zone_options_create(opt->region); zone->part_of_config = 0; zone->name = region_strdup(opt->region, nm); zone->linesize = linesize; @@ -396,9 +447,41 @@ zone_list_zone_insert(struct nsd_options* opt, const char* nm, region_recycle(opt->region, zone, sizeof(*zone)); return NULL; } + if(!mem_idnm) { + if(cmz && new_member_id) + new_member_id(cmz); + if(cmz && cmz->member_id) { + /* Assume all bytes of member_id are printable. + * plus 1 for space + */ + zone->linesize += label_length(dname_name(cmz->member_id)) + 1; + DEBUG(DEBUG_XFRD, 2, (LOG_INFO, "new linesize: %d", + (int)zone->linesize)); + } + } else if(!cmz) + log_msg(LOG_ERR, "member ID '%s' given, but no catalog-producer-" + "zone value provided in zone '%s' or pattern '%s'", + mem_idnm, nm, patnm); + + else if(strlen(pat->catalog_producer_zone) + + strlen(mem_idnm) + 9 > sizeof(member_id_str)) + log_msg(LOG_ERR, "syntax error in member ID '%s.zones.%s' for " + "zone '%s'", mem_idnm, pat->catalog_producer_zone, nm); + + else if(!(cmz->member_id = dname_parse(opt->region, strcat(strcat( + strcpy(member_id_str, mem_idnm),".zones."), + pat->catalog_producer_zone)))) + log_msg(LOG_ERR, "parse error in member ID '%s' for " + "zone '%s'", member_id_str, nm); return zone; } +struct zone_options* +zone_list_zone_insert(struct nsd_options* opt,const char* nm,const char* patnm) +{ + return zone_list_member_zone_insert(opt, nm, patnm, 0, 0, NULL, NULL); +} + int parse_zone_list_file(struct nsd_options* opt) { @@ -465,8 +548,42 @@ parse_zone_list_file(struct nsd_options* opt) /* store offset and line size for zone entry */ /* and create zone entry in zonetree */ - (void)zone_list_zone_insert(opt, nm, patnm, linesize, - ftello(opt->zonelist)-linesize); + (void)zone_list_member_zone_insert(opt, nm, patnm, + linesize, ftello(opt->zonelist)-linesize, + NULL, NULL); + + } else if(strncmp(buf, "cat ", 4) == 0) { + int linesize = strlen(buf); + /* parse the 'add' line */ + /* pick last space on the line, so that the domain + * name can have a space in it (but not the pattern)*/ + char* nm = buf + 4; + char* mem_idnm = strrchr(nm, ' '), *patnm; + if(!mem_idnm) { + /* parse error */ + log_msg(LOG_ERR, "parse error in %s: '%s'", + opt->zonelistfile, buf); + continue; + } + *mem_idnm++ = 0; + patnm = strrchr(nm, ' '); + if(!patnm) { + *--mem_idnm = ' '; + /* parse error */ + log_msg(LOG_ERR, "parse error in %s: '%s'", + opt->zonelistfile, buf); + continue; + } + *patnm++ = 0; + if(linesize && buf[linesize-1] == '\n') + buf[linesize-1] = 0; + + /* store offset and line size for zone entry */ + /* and create zone entry in zonetree */ + (void)zone_list_member_zone_insert(opt, nm, patnm, + linesize, ftello(opt->zonelist)-linesize, + mem_idnm, NULL); + } else if(strncmp(buf, "del ", 4) == 0) { /* store offset and line size for deleted entry */ int linesize = strlen(buf); @@ -485,26 +602,62 @@ parse_zone_list_file(struct nsd_options* opt) void zone_options_delete(struct nsd_options* opt, struct zone_options* zone) { + struct catalog_member_zone* member_zone = as_catalog_member_zone(zone); + rbtree_delete(opt->zone_options, zone->node.key); region_recycle(opt->region, (void*)zone->node.key, dname_total_size( (dname_type*)zone->node.key)); - region_recycle(opt->region, zone, sizeof(*zone)); + if(!member_zone) { + region_recycle(opt->region, zone, sizeof(*zone)); + return; + } + /* Because catalog member zones are in xfrd only deleted through + * catalog_del_consumer_member_zone() or through + * xfrd_del_catalog_producer_member(), which both clear the node, + * and because member zones in the main and serve processes are not + * indexed, *member_zone->node == *RBTREE_NULL. + * member_id is cleared too by those delete function, but there may be + * leftover member_id's from the initial zone.list processing, which + * made it to the main and serve processes. + */ + assert(!memcmp(&member_zone->node, RBTREE_NULL, sizeof(*RBTREE_NULL))); + if(member_zone->member_id) { + region_recycle(opt->region, (void*)member_zone->member_id, + dname_total_size(member_zone->member_id)); + } + region_recycle(opt->region, member_zone, sizeof(*member_zone)); } + /* add a new zone to the zonelist */ struct zone_options* -zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) +zone_list_add_or_cat(struct nsd_options* opt, const char* zname, + const char* pname, new_member_id_type new_member_id) { int r; struct zonelist_free* e; struct zonelist_bucket* b; - int linesize = 6 + strlen(zname) + strlen(pname); + char zone_list_line[6 + 5 * MAXDOMAINLEN + 2024 + 65]; + struct catalog_member_zone* cmz; + /* create zone entry */ - struct zone_options* zone = zone_list_zone_insert(opt, zname, pname, - linesize, 0); + struct zone_options* zone = zone_list_member_zone_insert( + opt, zname, pname, 6 + strlen(zname) + strlen(pname), + 0, NULL, new_member_id); if(!zone) return NULL; + if(zone_is_catalog_producer_member(zone) + && (cmz = as_catalog_member_zone(zone)) + && cmz->member_id) { + snprintf(zone_list_line, sizeof(zone_list_line), + "cat %s %s %.*s\n", zname, pname, + (int)label_length(dname_name(cmz->member_id)), + (const char*)dname_name(cmz->member_id) + 1); + } else { + snprintf(zone_list_line, sizeof(zone_list_line), + "add %s %s\n", zname, pname); + } /* use free entry or append to file or create new file */ if(!opt->zonelist || opt->zonelist_off == 0) { /* create new file */ @@ -531,7 +684,7 @@ zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) zone->off = ftello(opt->zonelist); if(zone->off == -1) log_msg(LOG_ERR, "ftello(%s): %s", opt->zonelistfile, strerror(errno)); - r = fprintf(opt->zonelist, "add %s %s\n", zname, pname); + r = fprintf(opt->zonelist, "%s", zone_list_line); if(r != zone->linesize) { if(r == -1) log_msg(LOG_ERR, "could not write to %s: %s", @@ -561,7 +714,7 @@ zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) zone_options_delete(opt, zone); return NULL; } - r = fprintf(opt->zonelist, "add %s %s\n", zname, pname); + r = fprintf(opt->zonelist, "%s", zone_list_line); if(r != zone->linesize) { if(r == -1) log_msg(LOG_ERR, "could not write to %s: %s", @@ -572,7 +725,7 @@ zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) zone_options_delete(opt, zone); return NULL; } - opt->zonelist_off += linesize; + opt->zonelist_off += zone->linesize; if(fflush(opt->zonelist) != 0) { log_msg(LOG_ERR, "fflush %s: %s", opt->zonelistfile, strerror(errno)); } @@ -587,7 +740,7 @@ zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) zone_options_delete(opt, zone); return NULL; } - r = fprintf(opt->zonelist, "add %s %s\n", zname, pname); + r = fprintf(opt->zonelist, "%s", zone_list_line); if(r != zone->linesize) { if(r == -1) log_msg(LOG_ERR, "could not write to %s: %s", @@ -617,6 +770,11 @@ zone_list_add(struct nsd_options* opt, const char* zname, const char* pname) void zone_list_del(struct nsd_options* opt, struct zone_options* zone) { + if (zone_is_catalog_consumer_member(zone)) { + /* catalog consumer member zones are not in the zones.list file */ + zone_options_delete(opt, zone); + return; + } /* put its space onto the free entry */ if(fseeko(opt->zonelist, zone->off, SEEK_SET) == -1) { log_msg(LOG_ERR, "fseeko(%s): %s", opt->zonelistfile, strerror(errno)); @@ -691,10 +849,21 @@ zone_list_compact(struct nsd_options* opt) return; } RBTREE_FOR(zone, struct zone_options*, opt->zone_options) { + struct catalog_member_zone* cmz; + if(zone->part_of_config) continue; - r = fprintf(out, "add %s %s\n", zone->name, - zone->pattern->pname); + if(zone_is_catalog_producer_member(zone) + && (cmz = as_catalog_member_zone(zone)) + && cmz->member_id) { + r = fprintf(out, "cat %s %s %.*s\n", zone->name, + zone->pattern->pname, + (int)label_length(dname_name(cmz->member_id)), + (const char*)dname_name(cmz->member_id) + 1); + } else { + r = fprintf(out, "add %s %s\n", zone->name, + zone->pattern->pname); + } if(r < 0) { log_msg(LOG_ERR, "write %s failed: %s", outname, strerror(errno)); @@ -807,9 +976,26 @@ zone_options_create(region_type* region) zone->name = 0; zone->pattern = 0; zone->part_of_config = 0; + zone->is_catalog_member_zone = 0; return zone; } +struct catalog_member_zone* +catalog_member_zone_create(region_type* region) +{ + struct catalog_member_zone* member_zone; + member_zone = (struct catalog_member_zone*)region_alloc(region, + sizeof(struct catalog_member_zone)); + member_zone->options.node = *RBTREE_NULL; + member_zone->options.name = 0; + member_zone->options.pattern = 0; + member_zone->options.part_of_config = 0; + member_zone->options.is_catalog_member_zone = 1; + member_zone->member_id = NULL; + member_zone->node = *RBTREE_NULL; + return member_zone; +} + /* true is booleans are the same truth value */ #define booleq(x,y) ( ((x) && (y)) || (!(x) && !(y)) ) @@ -912,7 +1098,10 @@ pattern_options_create(region_type* region) p->verifier_feed_zone_is_default = 1; p->verifier_timeout = VERIFIER_TIMEOUT_INHERIT; p->verifier_timeout_is_default = 1; - + p->catalog_role = CATALOG_ROLE_INHERIT; + p->catalog_role_is_default = 1; + p->catalog_member_pattern = NULL; + p->catalog_producer_zone = NULL; return p; } @@ -1114,6 +1303,16 @@ copy_pat_fixed(region_type* region, struct pattern_options* orig, orig->verifier_timeout_is_default = p->verifier_timeout_is_default; orig->verifier_feed_zone = p->verifier_feed_zone; orig->verifier_feed_zone_is_default = p->verifier_feed_zone_is_default; + orig->catalog_role = p->catalog_role; + orig->catalog_role_is_default = p->catalog_role_is_default; + if(p->catalog_member_pattern) + orig->catalog_member_pattern = + region_strdup(region, p->catalog_member_pattern); + else orig->catalog_member_pattern = NULL; + if(p->catalog_producer_zone) + orig->catalog_producer_zone = + region_strdup(region, p->catalog_producer_zone); + else orig->catalog_producer_zone = NULL; } void @@ -1248,6 +1447,19 @@ pattern_options_equal(struct pattern_options* p, struct pattern_options* q) if(p->verifier_timeout != q->verifier_timeout) return 0; if(!booleq(p->verifier_timeout_is_default, q->verifier_timeout_is_default)) return 0; + if(p->catalog_role != q->catalog_role) return 0; + if(!booleq(p->catalog_role_is_default, + q->catalog_role_is_default)) return 0; + if(!p->catalog_member_pattern && q->catalog_member_pattern) return 0; + else if(p->catalog_member_pattern && !q->catalog_member_pattern) return 0; + else if(p->catalog_member_pattern && q->catalog_member_pattern) { + if(strcmp(p->catalog_member_pattern, q->catalog_member_pattern) != 0) return 0; + } + if(!p->catalog_producer_zone && q->catalog_producer_zone) return 0; + else if(p->catalog_producer_zone && !q->catalog_producer_zone) return 0; + else if(p->catalog_producer_zone && q->catalog_producer_zone) { + if(strcmp(p->catalog_producer_zone, q->catalog_producer_zone) != 0) return 0; + } return 1; } @@ -1472,6 +1684,10 @@ pattern_options_marshal(struct buffer* b, struct pattern_options* p) marshal_u8(b, p->verifier_feed_zone_is_default); marshal_u32(b, p->verifier_timeout); marshal_u8(b, p->verifier_timeout_is_default); + marshal_u8(b, p->catalog_role); + marshal_u8(b, p->catalog_role_is_default); + marshal_str(b, p->catalog_member_pattern); + marshal_str(b, p->catalog_producer_zone); } struct pattern_options* @@ -1522,6 +1738,10 @@ pattern_options_unmarshal(region_type* r, struct buffer* b) p->verifier_feed_zone_is_default = unmarshal_u8(b); p->verifier_timeout = unmarshal_u32(b); p->verifier_timeout_is_default = unmarshal_u8(b); + p->catalog_role = unmarshal_u8(b); + p->catalog_role_is_default = unmarshal_u8(b); + p->catalog_member_pattern = unmarshal_str(r, b); + p->catalog_producer_zone = unmarshal_str(r, b); return p; } @@ -2334,6 +2554,18 @@ config_apply_pattern(struct pattern_options *dest, const char* name) c_error("could not find pattern %s", name); return; } + if(strncmp(dest->pname, PATTERN_IMPLICIT_MARKER, + strlen(PATTERN_IMPLICIT_MARKER)) == 0 + && pat->catalog_producer_zone) { + c_error("patterns with an catalog-producer-zone option are to " + "be used with \"nsd-control addzone\" only and cannot " + "be included from zone clauses in the config file"); + return; + } + if((dest->catalog_role == CATALOG_ROLE_PRODUCER && pat->request_xfr) + || ( pat->catalog_role == CATALOG_ROLE_PRODUCER && dest->request_xfr)){ + c_error("catalog producer zones cannot be secondary zones"); + } /* apply settings */ if(pat->zonefile) @@ -2435,6 +2667,16 @@ config_apply_pattern(struct pattern_options *dest, const char* name) } dest->verifier = vec; } + if(!pat->catalog_role_is_default) { + dest->catalog_role = pat->catalog_role; + dest->catalog_role_is_default = 0; + } + if(pat->catalog_member_pattern) + dest->catalog_member_pattern = region_strdup( + cfg_parser->opt->region, pat->catalog_member_pattern); + if(pat->catalog_producer_zone) + dest->catalog_producer_zone = region_strdup( + cfg_parser->opt->region, pat->catalog_producer_zone); } void diff --git a/options.h b/options.h index 8c93a69c6..da4a5b7b4 100644 --- a/options.h +++ b/options.h @@ -20,6 +20,7 @@ struct buffer; struct nsd; struct proxy_protocol_port_list; + typedef struct nsd_options nsd_options_type; typedef struct pattern_options pattern_options_type; typedef struct zone_options zone_options_type; @@ -35,6 +36,9 @@ typedef struct config_parser_state config_parser_state_type; #define VERIFY_ZONE_INHERIT (2) #define VERIFIER_FEED_ZONE_INHERIT (2) #define VERIFIER_TIMEOUT_INHERIT (-1) +#define CATALOG_ROLE_INHERIT (0) +#define CATALOG_ROLE_CONSUMER (1) +#define CATALOG_ROLE_PRODUCER (2) /* * Options global for nsd. @@ -308,6 +312,10 @@ struct pattern_options { uint8_t verifier_feed_zone_is_default; int32_t verifier_timeout; uint8_t verifier_timeout_is_default; + uint8_t catalog_role; + uint8_t catalog_role_is_default; + const char* catalog_member_pattern; + const char* catalog_producer_zone; } ATTR_PACKED; #define PATTERN_IMPLICIT_MARKER "_implicit_" @@ -328,9 +336,27 @@ struct zone_options { * a anonymous pattern created in-place */ struct pattern_options* pattern; /* zone is fixed into the main config, not in zonelist, cannot delete */ - uint8_t part_of_config; + unsigned part_of_config : 1; + unsigned is_catalog_member_zone: 1; +} ATTR_PACKED; + +/* + * Options for catalog member zones + * assert(options->is_catalog_member_zone == 1) + * when options->pattern->catalog_producer_zone is set, this is a + * producer member zone, otherwise a consumer member zone. + * A catalog member zone is either a member zone of a catalog producer zone + * or a catalog consumer zone. They are mutually exclusive. + */ +struct catalog_member_zone { + struct zone_options options; + const struct dname* member_id; + /* node in the associated catalog consumer or producer zone */ + rbnode_type node; } ATTR_PACKED; +typedef void (*new_member_id_type)(struct catalog_member_zone* zone); + union acl_addr_storage { #ifdef INET6 struct in_addr addr; @@ -459,9 +485,13 @@ int nsd_options_insert_pattern(struct nsd_options* opt, /* parses options file. Returns false on failure. callback, if nonNULL, * gets called with error strings, default prints. */ int parse_options_file(struct nsd_options* opt, const char* file, - void (*err)(void*,const char*), void* err_arg); + void (*err)(void*,const char*), void* err_arg, + struct nsd_options* old_opts); struct zone_options* zone_options_create(region_type* region); void zone_options_delete(struct nsd_options* opt, struct zone_options* zone); +struct catalog_member_zone* catalog_member_zone_create(region_type* region); +static inline struct catalog_member_zone* as_catalog_member_zone(struct zone_options* zopt) +{ return zopt && zopt->is_catalog_member_zone ? (struct catalog_member_zone*)zopt : NULL; } /* find a zone by apex domain name, or NULL if not found. */ struct zone_options* zone_options_find(struct nsd_options* opt, const struct dname* apex); @@ -488,12 +518,16 @@ void tls_auth_options_insert(struct nsd_options* opt, struct tls_auth_options* a struct tls_auth_options* tls_auth_options_find(struct nsd_options* opt, const char* name); /* read in zone list file. Returns false on failure */ int parse_zone_list_file(struct nsd_options* opt); +/* create (potential) catalog producer member entry and add to the zonelist */ +struct zone_options* zone_list_add_or_cat(struct nsd_options* opt, + const char* zname, const char* pname, new_member_id_type new_member_id); /* create zone entry and add to the zonelist file */ -struct zone_options* zone_list_add(struct nsd_options* opt, const char* zname, - const char* pname); +static inline struct zone_options* zone_list_add(struct nsd_options* opt, + const char* zname, const char* pname) +{ return zone_list_add_or_cat(opt, zname, pname, NULL); } /* create zonelist entry, do not insert in file (called by _add) */ struct zone_options* zone_list_zone_insert(struct nsd_options* opt, - const char* nm, const char* patnm, int linesize, off_t off); + const char* nm, const char* patnm); void zone_list_del(struct nsd_options* opt, struct zone_options* zone); void zone_list_compact(struct nsd_options* opt); void zone_list_close(struct nsd_options* opt); @@ -544,6 +578,20 @@ int acl_equal(struct acl_options* p, struct acl_options* q); /* see if a zone is a slave or a master zone */ int zone_is_slave(struct zone_options* opt); +/* see if a zone is a catalog consumer */ +static inline int zone_is_catalog_consumer(struct zone_options* opt) +{ return opt && opt->pattern + && opt->pattern->catalog_role == CATALOG_ROLE_CONSUMER; } +static inline int zone_is_catalog_producer(struct zone_options* opt) +{ return opt && opt->pattern + && opt->pattern->catalog_role == CATALOG_ROLE_PRODUCER; } +static inline int zone_is_catalog_member(struct zone_options* opt) +{ return opt && opt->is_catalog_member_zone; } +static inline const char* zone_is_catalog_producer_member(struct zone_options* opt) +{ return opt && opt->pattern && opt->pattern->catalog_producer_zone + ? opt->pattern->catalog_producer_zone : NULL; } +static inline int zone_is_catalog_consumer_member(struct zone_options* opt) +{ return zone_is_catalog_member(opt) && !zone_is_catalog_producer_member(opt); } /* create zonefile name, returns static pointer (perhaps to options data) */ const char* config_make_zonefile(struct zone_options* zone, struct nsd* nsd); diff --git a/remote.c b/remote.c index ed1aeb5c2..36ba337f2 100644 --- a/remote.c +++ b/remote.c @@ -74,6 +74,7 @@ #include "remote.h" #include "util.h" #include "xfrd.h" +#include "xfrd-catalog-zones.h" #include "xfrd-notify.h" #include "xfrd-tcp.h" #include "nsd.h" @@ -101,8 +102,10 @@ #define REMOTE_CONTROL_TCP_TIMEOUT 120 /** repattern to master or slave */ -#define REPAT_SLAVE 1 -#define REPAT_MASTER 2 +#define REPAT_SLAVE 1 +#define REPAT_MASTER 2 +#define REPAT_CATALOG_CONSUMER 4 +#define REPAT_CATALOG_CONSUMER_DEINIT 8 /** if you want zero to be inhibited in stats output. * it omits zeroes for types that have no acronym and unused-rcodes */ @@ -1037,6 +1040,62 @@ print_zonestatus(RES* ssl, xfrd_state_type* xfrd, struct zone_options* zo) if(!ssl_printf(ssl, " pattern: %s\n", zo->pattern->pname)) return 0; } + if(zone_is_catalog_consumer(zo)) { + zone_type* zone = namedb_find_zone(xfrd->nsd->db, + (const dname_type*)zo->node.key); + struct xfrd_catalog_consumer_zone* consumer_zone = + (struct xfrd_catalog_consumer_zone*) + rbtree_search( xfrd->catalog_consumer_zones + , zo->node.key); + + if(!ssl_printf(ssl, " catalog: consumer")) + return 0; + if(zone && zone->soa_rrset && zone->soa_rrset->rrs + && zone->soa_rrset->rrs[0].rdata_count > 2 + && rdata_atom_size(zone->soa_rrset->rrs[0].rdatas[2]) == + sizeof(uint32_t)) { + if(!ssl_printf(ssl, " (serial: %u, # members: %zu)\n", + read_uint32(rdata_atom_data( + zone->soa_rrset->rrs[0].rdatas[2])), + consumer_zone + ? consumer_zone->member_ids.count : 0)) + return 0; + + } else if(!ssl_printf(ssl, "\n")) + return 0; + if(invalid_catalog_consumer_zone(zo)) { + if(!ssl_printf(ssl, " catalog-invalid: %s\n", + invalid_catalog_consumer_zone(zo))) + return 0; + } + } + if(zone_is_catalog_producer(zo)) { + struct xfrd_catalog_producer_zone* producer_zone = + (struct xfrd_catalog_producer_zone*) + rbtree_search( xfrd->catalog_producer_zones + , zo->node.key); + if(!ssl_printf(ssl, " catalog: producer")) + return 0; + if(producer_zone) { + if(!ssl_printf(ssl, " (serial: %u, # members: %zu)\n", + (unsigned)producer_zone->serial, + producer_zone->member_ids.count)) + return 0; + } else if(!ssl_printf(ssl, "\n")) + return 0; + if (zone_is_slave(zo)) { + if(!ssl_printf(ssl, " catalog-invalid: a catalog " + "producer cannot be a secondary zone")) + return 0; + } + } + if(zone_is_catalog_member(zo)) { + if(!ssl_printf(ssl, " catalog-member-id: %s\n", + as_catalog_member_zone(zo)->member_id + ? dname_to_string(as_catalog_member_zone(zo)->member_id, NULL) + : "ERROR member-id is missing!")) + return 0; + } if(nz) { if(nz->is_waiting) { if(!ssl_printf(ssl, " notify: \"waiting-for-fd\"\n")) @@ -1267,6 +1326,15 @@ perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) dname = NULL; return 0; } + if(zone_is_catalog_consumer_member(zopt)) { + (void)ssl_printf(ssl, "Error: Zone is a catalog " + "consumer member zone with id %s\nRepattern in the " + "catalog with a group property.\n", dname_to_string( + as_catalog_member_zone(zopt)->member_id, NULL)); + region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); + dname = NULL; + return 0; + } /* found the zone, now delete it */ /* create deletion task */ /* this deletion task is processed before the addition task, @@ -1281,6 +1349,10 @@ perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) xfrd_del_slave_zone(xfrd, dname); } xfrd_del_notify(xfrd, dname); + /* delete it in xfrd's catalog consumers list */ + if(zone_is_catalog_consumer(zopt)) { + xfrd_deinit_catalog_consumer_zone(xfrd, dname); + } /* delete from config */ zone_list_del(xfrd->nsd->options, zopt); } else { @@ -1290,7 +1362,8 @@ perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) dname = NULL; /* add to zonelist and adds to config in memory */ - zopt = zone_list_add(xfrd->nsd->options, arg, arg2); + zopt = zone_list_add_or_cat(xfrd->nsd->options, arg, arg2, + xfrd_add_catalog_producer_member); if(!zopt) { /* also dname parse error here */ (void)ssl_printf(ssl, "error could not add zonelist entry\n"); @@ -1302,6 +1375,10 @@ perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg) getzonestatid(xfrd->nsd->options, zopt)); zonestat_inc_ifneeded(xfrd); xfrd_set_reload_now(xfrd); + /* add to xfrd - catalog consumer zones */ + if (zone_is_catalog_consumer(zopt)) { + xfrd_init_catalog_consumer_zone(xfrd, zopt); + } /* add to xfrd - notify (for master and slaves) */ init_notify_send(xfrd->notify_zones, xfrd->region, zopt); /* add to xfrd - slave */ @@ -1353,7 +1430,8 @@ perform_addzone(RES* ssl, xfrd_state_type* xfrd, char* arg) dname = NULL; /* add to zonelist and adds to config in memory */ - zopt = zone_list_add(xfrd->nsd->options, arg, arg2); + zopt = zone_list_add_or_cat(xfrd->nsd->options, arg, arg2, + xfrd_add_catalog_producer_member); if(!zopt) { /* also dname parse error here */ (void)ssl_printf(ssl, "error could not add zonelist entry\n"); @@ -1365,6 +1443,10 @@ perform_addzone(RES* ssl, xfrd_state_type* xfrd, char* arg) getzonestatid(xfrd->nsd->options, zopt)); zonestat_inc_ifneeded(xfrd); xfrd_set_reload_now(xfrd); + /* add to xfrd - catalog consumer zones */ + if (zone_is_catalog_consumer(zopt)) { + xfrd_init_catalog_consumer_zone(xfrd, zopt); + } /* add to xfrd - notify (for master and slaves) */ init_notify_send(xfrd->notify_zones, xfrd->region, zopt); /* add to xfrd - slave */ @@ -1380,6 +1462,8 @@ perform_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg) { const dname_type* dname; struct zone_options* zopt; + /* dont recycle dname when it becomes part of a xfrd_producer_member */ + int recycle_dname = 1; dname = dname_parse(xfrd->region, arg); if(!dname) { @@ -1406,7 +1490,17 @@ perform_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg) "nsd.conf yourself and repattern\n"); return 0; } + if(zone_is_catalog_consumer_member(zopt) + && as_catalog_member_zone(zopt)->member_id) { + (void)ssl_printf(ssl, "Error: Zone is a catalog consumer " + "member zone with id %s\nRemove the member id from the " + "catalog to delete this zone.\n", dname_to_string( + as_catalog_member_zone(zopt)->member_id, NULL)); + region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); + dname = NULL; + return 0; + } /* create deletion task */ task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task, dname); @@ -1416,10 +1510,18 @@ perform_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg) xfrd_del_slave_zone(xfrd, dname); } xfrd_del_notify(xfrd, dname); + /* delete it in xfrd's catalog consumers list */ + if(zone_is_catalog_consumer(zopt)) { + xfrd_deinit_catalog_consumer_zone(xfrd, dname); + } else { + recycle_dname = !xfrd_del_catalog_producer_member(xfrd, dname); + } /* delete from config */ zone_list_del(xfrd->nsd->options, zopt); - region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); + if(recycle_dname) + region_recycle(xfrd->region, + (void*)dname, dname_total_size(dname)); return 1; } @@ -1580,6 +1682,10 @@ remove_cfgzone(xfrd_state_type* xfrd, const char* pname) xfrd_del_slave_zone(xfrd, dname); } xfrd_del_notify(xfrd, dname); + /* delete it in xfrd's catalog consumers list */ + if(zone_is_catalog_consumer(zopt)) { + xfrd_deinit_catalog_consumer_zone(xfrd, dname); + } /* delete from zoneoptions */ zone_options_delete(xfrd->nsd->options, zopt); @@ -1614,6 +1720,10 @@ add_cfgzone(xfrd_state_type* xfrd, const char* pname) getzonestatid(xfrd->nsd->options, zopt)); /* zonestat_inc is done after the entire config file has been done */ xfrd_set_reload_now(xfrd); + /* add to xfrd - catalog consumer zones */ + if (zone_is_catalog_consumer(zopt)) { + xfrd_init_catalog_consumer_zone(xfrd, zopt); + } /* add to xfrd - notify (for master and slaves) */ init_notify_send(xfrd->notify_zones, xfrd->region, zopt); /* add to xfrd - slave */ @@ -1762,12 +1872,19 @@ repat_patterns(xfrd_state_type* xfrd, struct nsd_options* newopt) } else if (!p->request_xfr && origp->request_xfr) { newstate = REPAT_MASTER; } + if ( p->catalog_role == CATALOG_ROLE_CONSUMER + && origp->catalog_role != CATALOG_ROLE_CONSUMER) { + newstate |= REPAT_CATALOG_CONSUMER; + } else if (p->catalog_role != CATALOG_ROLE_CONSUMER + && origp->catalog_role == CATALOG_ROLE_CONSUMER) { + newstate |= REPAT_CATALOG_CONSUMER_DEINIT; + } add_pat(xfrd, p); if (p->implicit && newstate) { const dname_type* dname = parse_implicit_name(xfrd, p->pname); if (dname) { - if (newstate == REPAT_SLAVE) { + if (newstate & REPAT_SLAVE) { struct zone_options* zopt = zone_options_find( oldopt, dname); @@ -1775,10 +1892,22 @@ repat_patterns(xfrd_state_type* xfrd, struct nsd_options* newopt) xfrd_init_slave_zone( xfrd, zopt); } - } else if (newstate == REPAT_MASTER) { + } else if (newstate & REPAT_MASTER) { xfrd_del_slave_zone(xfrd, dname); } + if (newstate & REPAT_CATALOG_CONSUMER) { + struct zone_options* zopt = + zone_options_find( + oldopt, dname); + if (zopt) { + xfrd_init_catalog_consumer_zone( + xfrd, zopt); + } + } else if (newstate & REPAT_CATALOG_CONSUMER_DEINIT) { + xfrd_deinit_catalog_consumer_zone( + xfrd, dname); + } region_recycle(xfrd->region, (void*)dname, dname_total_size(dname)); @@ -1797,15 +1926,23 @@ repat_patterns(xfrd_state_type* xfrd, struct nsd_options* newopt) RBTREE_FOR(zone_opt, struct zone_options*, oldopt->zone_options) { struct pattern_options* oldp = zone_opt->pattern; if (!oldp->implicit) { - if (oldp->xfrd_flags == REPAT_SLAVE) { + if (oldp->xfrd_flags & REPAT_SLAVE) { /* xfrd needs stable reference so get * it from the oldopt(modified) tree */ xfrd_init_slave_zone(xfrd, zone_opt); - } else if (oldp->xfrd_flags == REPAT_MASTER) { + } else if (oldp->xfrd_flags & REPAT_MASTER) { xfrd_del_slave_zone(xfrd, (const dname_type*) zone_opt->node.key); } + if (oldp->xfrd_flags & REPAT_CATALOG_CONSUMER) { + xfrd_init_catalog_consumer_zone(xfrd, + zone_opt); + } else if (oldp->xfrd_flags & REPAT_CATALOG_CONSUMER_DEINIT) { + xfrd_deinit_catalog_consumer_zone(xfrd, + (const dname_type*) + zone_opt->node.key); + } oldp->xfrd_flags = 0; } } @@ -1883,7 +2020,8 @@ do_repattern(RES* ssl, xfrd_state_type* xfrd) (void)ssl_printf(ssl, "reconfig start, read %s\n", cfgfile); opt = nsd_options_create(region); - if(!parse_options_file(opt, cfgfile, &print_ssl_cfg_err, &ssl)) { + if(!parse_options_file(opt, cfgfile, &print_ssl_cfg_err, &ssl, + xfrd->nsd->options)) { /* error already printed */ region_destroy(region); return; diff --git a/server.c b/server.c index 97858119d..6e9be50f2 100644 --- a/server.c +++ b/server.c @@ -1729,6 +1729,9 @@ server_start_xfrd(struct nsd *nsd, int del_db, int reload_active) #ifdef HAVE_SETPROCTITLE setproctitle("xfrd"); #endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("xfrd"); +#endif #ifdef HAVE_CPUSET_T if(nsd->use_cpu_affinity) { set_cpu_affinity(nsd->xfrd_cpuset); @@ -1745,6 +1748,12 @@ server_start_xfrd(struct nsd *nsd, int del_db, int reload_active) log_msg(LOG_ERR, "cannot fcntl pipe: %s", strerror(errno)); } nsd->xfrd_listener->fd = sockets[0]; +#ifdef HAVE_SETPROCTITLE + setproctitle("main"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("main"); +#endif break; } /* server-parent only */ @@ -2323,9 +2332,6 @@ server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio, ign_sigchld.sa_handler = SIG_IGN; sigaction(SIGCHLD, &ign_sigchld, &old_sigchld); -#ifdef HAVE_SETPROCTITLE - setproctitle("main"); -#endif #ifdef HAVE_CPUSET_T if(nsd->use_cpu_affinity) { set_cpu_affinity(nsd->cpuset); @@ -2596,6 +2602,12 @@ server_main(struct nsd *nsd) log_msg(LOG_WARNING, "Reload process %d failed with status %d, continuing with old database", (int) child_pid, status); +#ifdef HAVE_SETPROCTITLE + setproctitle("main"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("main"); +#endif reload_pid = -1; if(reload_listener.fd != -1) close(reload_listener.fd); netio_remove_handler(netio, &reload_listener); @@ -2694,6 +2706,12 @@ server_main(struct nsd *nsd) log_msg(LOG_WARNING, "Reload process %d failed, continuing with old database", (int) reload_pid); +#ifdef HAVE_SETPROCTITLE + setproctitle("main"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("main"); +#endif reload_pid = -1; if(reload_listener.fd != -1) close(reload_listener.fd); netio_remove_handler(netio, &reload_listener); @@ -2754,9 +2772,21 @@ server_main(struct nsd *nsd) default: /* PARENT */ close(reload_sockets[0]); +#ifdef HAVE_SETPROCTITLE + setproctitle("load"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("load"); +#endif server_reload(nsd, server_region, netio, reload_sockets[1]); DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload exited to become new main")); +#ifdef HAVE_SETPROCTITLE + setproctitle("main"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("main"); +#endif close(reload_sockets[1]); DEBUG(DEBUG_IPC,2, (LOG_INFO, "Reload closed")); /* drop stale xfrd ipc data */ @@ -2773,6 +2803,12 @@ server_main(struct nsd *nsd) /* server_main keep running until NSD_QUIT_SYNC * received from reload. */ close(reload_sockets[1]); +#ifdef HAVE_SETPROCTITLE + setproctitle("old-main"); +#endif +#ifdef USE_LOG_PROCESS_ROLE + log_set_process_role("old-main"); +#endif reload_listener.fd = reload_sockets[0]; reload_listener.timeout = NULL; reload_listener.user_data = nsd; @@ -3207,6 +3243,9 @@ server_child(struct nsd *nsd) region_type *server_region = region_create(xalloc, free); struct event_base* event_base = nsd_child_event_base(); sig_atomic_t mode; +#ifdef USE_LOG_PROCESS_ROLE + static char child_name[20]; +#endif if(!event_base) { log_msg(LOG_ERR, "nsd server could not create event base"); @@ -3220,11 +3259,17 @@ server_child(struct nsd *nsd) #endif assert(nsd->server_kind != NSD_SERVER_MAIN); - DEBUG(DEBUG_IPC, 2, (LOG_INFO, "child process started")); #ifdef HAVE_SETPROCTITLE setproctitle("server %d", nsd->this_child->child_num + 1); #endif +#ifdef USE_LOG_PROCESS_ROLE + snprintf(child_name, sizeof(child_name), "srv%d", + nsd->this_child->child_num + 1); + log_set_process_role(child_name); +#endif + DEBUG(DEBUG_IPC, 2, (LOG_INFO, "child process started")); + #ifdef HAVE_CPUSET_T if(nsd->use_cpu_affinity) { set_cpu_affinity(nsd->this_child->cpuset); diff --git a/tpkg/catzones.tdir/catzones.bad1.1.conf b/tpkg/catzones.tdir/catzones.bad1.1.conf new file mode 100644 index 000000000..c8bb6ffee --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad1.1.conf @@ -0,0 +1,6 @@ +# A catalog producer zone cannot be secondary + +zone: + name: "bad1.1" + request-xfr: 127.0.0.1 NOKEY + catalog: producer diff --git a/tpkg/catzones.tdir/catzones.bad1.2.conf b/tpkg/catzones.tdir/catzones.bad1.2.conf new file mode 100644 index 000000000..99bce3aa1 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad1.2.conf @@ -0,0 +1,6 @@ +# A catalog producer zone cannot be secondary + +pattern: + name: "bad1.2" + catalog: producer + request-xfr: 127.0.0.1 NOKEY diff --git a/tpkg/catzones.tdir/catzones.bad1.3.conf b/tpkg/catzones.tdir/catzones.bad1.3.conf new file mode 100644 index 000000000..8bfc2cfd4 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad1.3.conf @@ -0,0 +1,22 @@ +# A catalog producer zone cannot be secondary + +pattern: + name: "producer" + catalog: producer + +pattern: + name: "p" + include-pattern: "producer" + +pattern: + name: "secondary" + request-xfr: 127.0.0.1 NOKEY + +pattern: + name: "s" + include-pattern: "secondary" + +zone: + name: "bad1.3" + include-pattern: "p" + include-pattern: "s" diff --git a/tpkg/catzones.tdir/catzones.bad1.4.conf b/tpkg/catzones.tdir/catzones.bad1.4.conf new file mode 100644 index 000000000..1f70cf9a8 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad1.4.conf @@ -0,0 +1,22 @@ +# A catalog producer zone cannot be secondary + +pattern: + name: "producer" + catalog: producer + +pattern: + name: "p" + include-pattern: "producer" + +pattern: + name: "secondary" + request-xfr: 127.0.0.1 NOKEY + +pattern: + name: "s" + include-pattern: "secondary" + +pattern: + name: "bad1.4" + include-pattern: "s" + include-pattern: "p" diff --git a/tpkg/catzones.tdir/catzones.bad2.1.conf b/tpkg/catzones.tdir/catzones.bad2.1.conf new file mode 100644 index 000000000..3f958e3a7 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad2.1.conf @@ -0,0 +1,5 @@ +# catalog-producer-zone option can only be used in patterns not zones + +zone: + name: "bad2.1" + catalog-producer-zone: catalog1.invalid diff --git a/tpkg/catzones.tdir/catzones.bad2.2.conf b/tpkg/catzones.tdir/catzones.bad2.2.conf new file mode 100644 index 000000000..28bd9f1b2 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad2.2.conf @@ -0,0 +1,13 @@ +# catalog-producer-zone option can only be used in patterns not zones + +pattern: + name: "catalog-producer-zone" + catalog-producer-zone: catalog1.invalid + +zone: + name: "catalog1.invalid" + catalog: producer + +zone: + name: "bad2.2" + include-pattern: "catalog-producer-zone" diff --git a/tpkg/catzones.tdir/catzones.bad3.conf b/tpkg/catzones.tdir/catzones.bad3.conf new file mode 100644 index 000000000..7c99da45a --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad3.conf @@ -0,0 +1,15 @@ +# catalog-producer-zone cannot be found + +pattern: + name: "good3" + catalog-producer-zone: catalog1.invalid + +pattern: + name: "bad3" + catalog-producer-zone: catalog2.invalid + +zone: + name: "catalog1.invalid" + catalog: producer + + diff --git a/tpkg/catzones.tdir/catzones.bad4.conf b/tpkg/catzones.tdir/catzones.bad4.conf new file mode 100644 index 000000000..b17ee0223 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.bad4.conf @@ -0,0 +1,21 @@ +# catalog-producer-zone is not configured as a catalog producer + +pattern: + name: "good4" + catalog-producer-zone: catalog1.invalid + +pattern: + name: "bad4" + catalog-producer-zone: catalog2.invalid + +pattern: + name: "producer" + catalog: producer + +zone: + name: "catalog1.invalid" + include-pattern: "producer" + +zone: + name: "catalog2.invalid" + catalog: consumer diff --git a/tpkg/catzones.tdir/catzones.control.key b/tpkg/catzones.tdir/catzones.control.key new file mode 100644 index 000000000..ec291acc9 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.control.key @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQC9u0DBJJwVyErW +pRAYQU1kmKa59gQAkmg0mrNUMDa+JYJj5U//XoGODBWJwkhdyO65NwNSVI5bkRzi +vKcd7kIl8uwD5lTQYl4ArGKX8A1K0JtTXZ8L8+nQJRWexlVLEwjMbwT6MWHLt92A +8qLKZQxzOf7WN2lj6AuHBC2hSewZuo9rhW57y8uAOiH8OTygFh4inSeozdsCiEq5 +hI4kWpcHHj0KE2Sf2Ngje9U9hDXgcUwRABwIGfT1KG/gT6BQxvrVtaAPQBJxdJHX +RkM9l857x64LbFHHcM2G3GEHBlcLv+LlA+RKg9zMbppnaZzlfDK9zZx79qPTiPfv +kH2HEtiPiiulzzDbncXHHJguXE9WIBCpNedXeMffevUqiDb9rnsQ7r9sNtXu9FDH +qSMbSBX00ZJIEFaauAL1EyFyK6owqLFwbnWI9WlxW+C/2XzDhs9FUH1SRigT8JLY +wgwwv+uMTTb1x11qyihBrYJZAWXix4oqL9HVUaGetTFAP3LeLYMCAwEAAQKCAYA8 +SH3XoGEzjpMwxftqKX2Eo/f9iAxXfi074Zdu/W63VcWrJdYYJpf1YSOKxL8nhUtM +cA1PIGym7WGHLTfQC77aved9ibaTC14gyqT875nZlpRzlYCOVK6Kou3Op+e71c1q +3y9uqzgOeVoYuX893YNqLRgqSirjQnw/JvW0lH8+4YbmO9wR23vKPy5DLJB1sPFc +QCC0wst+b32GmnZrWJk5+sKcK/NB0q1CAPuM8zAHIxhvImDFtjGdn36Ef+hJIh9O +LXabPi/ujZ6FOilgl+kYzxcPvBQCxpnet4lGMUd4kXGJzlfkmX7BKQP+nnEHLIl6 +DZOxFPI8yvfJ2sFpBbtwICuJd+EgT7s9pXtI5jIO2qGj/q+ilpGcUemUc9DdVu2d +KEs7KFLoOTjmxwQq61rrchhpnreNBh4XUvJioHVYk/+ru8vudmb+wlkU7nE0OdPX +NjHgj6n4ATK4MI7gPrknisOYqQ7j6nkFeyNoDs7DRblP28a5SfTwb4tBYPNQW6kC +gcEA8BfQL+MRPYb3njPXHXRk7xd10i1EdRDXmD2Q9F3XuHIpXrtUjsgplshovmmo +9bprmDUJCGFp8RBuEHsktd+H623VFd8nfrvUUcFQBqLXtPmZr32kRwr9SOCkUETn +LpM9uArzBiTWXmk9RzVuIchhcY5YL1etIrQlVcDwOJ6mOLled/iW5IQ30KGCY6nn +auwk26ExG0ZG0+LFYoaE9C2AyqlEHW/BRBXB+Uw6NJXuka5z5c4KETcofYcvZHnh +SIaFAoHBAMpNQ4OwUdSqrCZNgz3QD+eKGONbprePXv5aEHXdYN/y+wyMek4wIhHW +3UpOKPQT2GaIY0ZuX3JClbzGssadDQZ34laa6s9ExMbrRWvT7+Cg5UU54hjlobyC +6Lq5RQy9QHgILAEd3H7AiqsgNMKftWmkOqc+xrUVfnkPtZnBC2GioWXcH7MUkIL/ +JMuy/krofIsGokbKcXbMJ85wdBwCgnsSxBK4BpsphrPtQ4eRrafdHxlzK1jwj02n +YN5D0hA2ZwKBwQDbchiSPGvcnckj62QaAvHiasIA+ekRSvSTrOoZwNBUQNw5PuXZ +n3AL5l9+ctrAxsRjGU6JP2hysr63B6cVKRt1fyz9Ubsqpgk5NIStnBemzWDAOwl0 ++cFp5BGrYF8hfekyl6drHS5mmrAsrtRbwmf2P9Gwa+XOy3M4fyHRzohFXoDPDQL2 +GTde1NB1wazIWNCTgjB9Km3AawxYsnVBLXoDQlUExsmSLoTeFOM6fzPQKfC6wekH +tS+oFHCn2JKmFHUCgcEAq3rSv821j48cXVtW5A55bMqiThZiOzZMT04IpeABPdO4 ++eoZwi0KtBRtf47BKCOqSz9Q52KnhTLruhizcaeuYs08Di1C3G5uXi8PqVb8/mtj +7EokXPZJIoHUxHJHFYVWIGjM5CVrwgVzhZrHHjJClEmhbiLITLbrfbLTPIFmJFy2 +cqEkPagw1tlvVztiUhy/dRkqdNclVo23MEYiJZv6/lfp2/QhpWSNyfZUR5Y0nRXS +RsBY/grrbedzNPRIG3YBAoHAYulVIX0Bt5HFJznpoZsAPt5z3LuX1I2qnyFwQOPE +k9QnSKKj7/BuG5qXWKEur3WgsCWnLMkieSZhfqK3+kBii2gyWgjR1uVsYT/SJuMh +tqDJOwew2RN3B+xCydIT0PRubeKns1A4qeuAv1Juxw0S9wg6hBGb6GO12SkncYk3 +uQ3Qyahhyo+OIBf5y7BOcLKRm07AI2x6C3MTYyTwAV9VS2ByNsGxtVQysooeaam/ +Lfohlvr92Vv2N1gGcBSyrwTY +-----END PRIVATE KEY----- diff --git a/tpkg/catzones.tdir/catzones.control.pem b/tpkg/catzones.tdir/catzones.control.pem new file mode 100644 index 000000000..6fd77a839 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.control.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEGjCCAoKgAwIBAgIUdd5j+EPMyI74clpGCDGKjZLy7iMwDQYJKoZIhvcNAQEL +BQAwDjEMMAoGA1UEAwwDbnNkMB4XDTIzMTExOTE2MjUxNFoXDTQzMDgwNjE2MjUx +NFowFjEUMBIGA1UEAwwLbnNkLWNvbnRyb2wwggGiMA0GCSqGSIb3DQEBAQUAA4IB +jwAwggGKAoIBgQC9u0DBJJwVyErWpRAYQU1kmKa59gQAkmg0mrNUMDa+JYJj5U// +XoGODBWJwkhdyO65NwNSVI5bkRzivKcd7kIl8uwD5lTQYl4ArGKX8A1K0JtTXZ8L +8+nQJRWexlVLEwjMbwT6MWHLt92A8qLKZQxzOf7WN2lj6AuHBC2hSewZuo9rhW57 +y8uAOiH8OTygFh4inSeozdsCiEq5hI4kWpcHHj0KE2Sf2Ngje9U9hDXgcUwRABwI +GfT1KG/gT6BQxvrVtaAPQBJxdJHXRkM9l857x64LbFHHcM2G3GEHBlcLv+LlA+RK +g9zMbppnaZzlfDK9zZx79qPTiPfvkH2HEtiPiiulzzDbncXHHJguXE9WIBCpNedX +eMffevUqiDb9rnsQ7r9sNtXu9FDHqSMbSBX00ZJIEFaauAL1EyFyK6owqLFwbnWI +9WlxW+C/2XzDhs9FUH1SRigT8JLYwgwwv+uMTTb1x11qyihBrYJZAWXix4oqL9HV +UaGetTFAP3LeLYMCAwEAAaNoMGYwDAYDVR0TAQH/BAIwADAWBgNVHREEDzANggtu +c2QtY29udHJvbDAdBgNVHQ4EFgQU0SC9KMxX0jihcCV1Clj2B7cG7P8wHwYDVR0j +BBgwFoAU9v/9HVF57j/JYTCObg0ODJkWxW4wDQYJKoZIhvcNAQELBQADggGBAEgk +p/GZU39ASEupbyo9G6GWYzp4FZQMrfTZsqGthg0jRad0ZymAcBEyFA1kyj2yI2N3 +9pJQ/tq0rt7eggPjSEn05hyWnRMWvsmztpsH4Bpj4wDkUUGvDB4jyPgEJVJQB0+U +IlhJx0FvfZu6Y2rl1bTwUUlxoaYX4Db297JzoM6nyAEtOeS7PiWpXL86DMCom/8I +jIRjagRVBtDioZ71OBXvraCy9juYnaUPzynJ8C4ETs5xcdDcMeKX1mUrKryoYAeS +T61yb1//B7FOMvWo5UKW2u2X/jJIFR1OtawXMwhGUm1TrUhbdD0Sr+dH5hgNx4Ad +j8idIAgfHOrrPVGMTVactX5b4Lv8m9H6xD7wQVw8NcO2O4aXHNxOmBl9Dex2kaOK +OntvaBTmbZJ42ydenfnR5U/FrK3u8cnk1oT8qfX31fHS/T4MPIRqvbUZcftGQ4Xn +xA3eqOFG7idiGENNWln8PbmxBootacpBNCf4Za1Tvr9u8eENAbcBv8mtGp45iQ== +-----END CERTIFICATE----- diff --git a/tpkg/catzones.tdir/catzones.dsc b/tpkg/catzones.tdir/catzones.dsc new file mode 100644 index 000000000..cd15130dd --- /dev/null +++ b/tpkg/catzones.tdir/catzones.dsc @@ -0,0 +1,22 @@ +BaseName: catzones +Version: 1.0 +Description: Catalog zones testing +CreationDate: vr 12 jan 2024 11:42:47 CET +Maintainer: Willem Toorop +Category: +Component: +Depends: +Help: +Pre: catzones.pre +Post: catzones.post +Test: catzones.test +AuxFiles: catzones.primary.conf.in, catzones.secondary.conf.in, + catzones.secondary2.conf.in, + catzones_server.key, catzones_server.pem, + catzones_control.key, catzones_control.pem, + catzones.bad1.1.conf, catzones.bad1.2.conf, + catzones.bad1.3.conf, catzones.bad1.4.conf, + catzones.bad2.1.conf, catzones.bad2.2.conf, + catzones.bad3.conf, catzones.bad4.conf +Passed: +Failure: diff --git a/tpkg/catzones.tdir/catzones.post b/tpkg/catzones.tdir/catzones.post new file mode 100755 index 000000000..a521ace7f --- /dev/null +++ b/tpkg/catzones.tdir/catzones.post @@ -0,0 +1,16 @@ +# #-- catzones.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh + +# do your teardown here +( + kill_from_pidfile secondary2.pid & + kill_from_pidfile secondary.pid & + kill_from_pidfile primary.pid & + wait +) + diff --git a/tpkg/catzones.tdir/catzones.pre b/tpkg/catzones.tdir/catzones.pre new file mode 100755 index 000000000..399ef2504 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.pre @@ -0,0 +1,42 @@ +# #-- catzones.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +if [ -z "$PRIMARY_CTRL_PORT" ]; then + get_random_port 6 + PRIMARY_PORT=$RND_PORT + PRIMARY_CTRL_PORT=`expr $RND_PORT + 1` + SECONDARY_PORT=`expr $RND_PORT + 2` + SECONDARY_CTRL_PORT=`expr $RND_PORT + 3` + SECONDARY2_PORT=`expr $RND_PORT + 4` + SECONDARY2_CTRL_PORT=`expr $RND_PORT + 5` + + # share the vars + echo "export PRIMARY_PORT=$PRIMARY_PORT" >> .tpkg.var.test + echo "export PRIMARY_CTRL_PORT=$PRIMARY_CTRL_PORT" >> .tpkg.var.test + echo "export SECONDARY_PORT=$SECONDARY_PORT" >> .tpkg.var.test + echo "export SECONDARY_CTRL_PORT=$SECONDARY_CTRL_PORT" >> .tpkg.var.test + echo "export SECONDARY2_PORT=$SECONDARY2_PORT" >> .tpkg.var.test + echo "export SECONDARY2_CTRL_PORT=$SECONDARY2_CTRL_PORT" >> .tpkg.var.test +fi + +PRE="../.." +TPKG_NSD="$PRE/nsd" + +for CONF in primary secondary secondary2; do + sed \ + -e "s/@PRIMARY_PORT@/${PRIMARY_PORT}/g" \ + -e "s/@PRIMARY_CTRL_PORT@/${PRIMARY_CTRL_PORT}/g" \ + -e "s/@SECONDARY_PORT@/${SECONDARY_PORT}/g" \ + -e "s/@SECONDARY_CTRL_PORT@/${SECONDARY_CTRL_PORT}/g" \ + -e "s/@SECONDARY2_PORT@/${SECONDARY2_PORT}/g" \ + -e "s/@SECONDARY2_CTRL_PORT@/${SECONDARY2_CTRL_PORT}/g" \ + catzones.${CONF}.conf.in > ${CONF}.conf + rm -f ${CONF}.log + $TPKG_NSD -c ${CONF}.conf + wait_nsd_up ${CONF}.log +done + diff --git a/tpkg/catzones.tdir/catzones.primary.conf.in b/tpkg/catzones.tdir/catzones.primary.conf.in new file mode 100644 index 000000000..6b59f1923 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.primary.conf.in @@ -0,0 +1,72 @@ +server: + zonesdir: "." + username: "" + database: "" + verbosity: 2 + ip-address: 127.0.0.1 + port: @PRIMARY_PORT@ + pidfile: "primary.pid" + logfile: "primary.log" + xfrdfile: "primary-xfrd.state" + zonelistfile: "primary-zone.list" + cookie-secret-file: "primary-cookiesecrets.txt" + #xfrd-reload-timeout: 0 + +remote-control: + control-enable: yes + control-port: @PRIMARY_CTRL_PORT@ + server-key-file: "catzones.server.key" + server-cert-file: "catzones.server.pem" + control-key-file: "catzones.control.key" + control-cert-file: "catzones.control.pem" + +pattern: + name: "primary" + provide-xfr: 127.0.0.0/8 NOKEY + +pattern: + name: "group0" + include-pattern: "primary" + zonefile: "catzones.primary.group0.zone" + catalog-producer-zone: "catalog1.invalid" + +pattern: + name: "group1" + include-pattern: "primary" + zonefile: "catzones.primary.group1.zone" + catalog-producer-zone: "catalog1.invalid" + +pattern: + name: "group2" + include-pattern: "primary" + zonefile: "catzones.primary.group2.zone" + catalog-producer-zone: "catalog2.invalid" + +pattern: + name: "group3" + include-pattern: "primary" + zonefile: "catzones.primary.group3.zone" + catalog-producer-zone: "catalog3.invalid" + +pattern: + name: "producer" + include-pattern: "primary" + zonefile: "primary.%s.zone" + catalog: producer + store-ixfr: yes + +zone: + name: "catalog1.invalid" + include-pattern: "producer" + notify: 127.0.0.1@@SECONDARY_PORT@ NOKEY + +zone: + name: "catalog2.invalid" + include-pattern: "producer" + notify: 127.0.0.1@@SECONDARY2_PORT@ NOKEY + +zone: + name: "catalog3.invalid" + include-pattern: "producer" + notify: 127.0.0.1@@SECONDARY2_PORT@ NOKEY + diff --git a/tpkg/catzones.tdir/catzones.primary.group0.zone b/tpkg/catzones.tdir/catzones.primary.group0.zone new file mode 100644 index 000000000..700b304ed --- /dev/null +++ b/tpkg/catzones.tdir/catzones.primary.group0.zone @@ -0,0 +1,3 @@ +@ 3600 SOA ns.invalid. noreply.invalid. 1 7200 3600 1209600 3600 + TXT group0 + diff --git a/tpkg/catzones.tdir/catzones.primary.group1.zone b/tpkg/catzones.tdir/catzones.primary.group1.zone new file mode 100644 index 000000000..54ef94c3e --- /dev/null +++ b/tpkg/catzones.tdir/catzones.primary.group1.zone @@ -0,0 +1,3 @@ +@ 3600 SOA ns.invalid. noreply.invalid. 1 7200 3600 1209600 3600 + TXT group1 + diff --git a/tpkg/catzones.tdir/catzones.primary.group2.zone b/tpkg/catzones.tdir/catzones.primary.group2.zone new file mode 100644 index 000000000..9c6a29859 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.primary.group2.zone @@ -0,0 +1,3 @@ +@ 3600 SOA ns.invalid. noreply.invalid. 1 7200 3600 1209600 3600 + TXT group2 + diff --git a/tpkg/catzones.tdir/catzones.primary.group3.zone b/tpkg/catzones.tdir/catzones.primary.group3.zone new file mode 100644 index 000000000..b5f94f97a --- /dev/null +++ b/tpkg/catzones.tdir/catzones.primary.group3.zone @@ -0,0 +1,3 @@ +@ 3600 SOA ns.invalid. noreply.invalid. 1 7200 3600 1209600 3600 + TXT group3 + diff --git a/tpkg/catzones.tdir/catzones.secondary.conf.in b/tpkg/catzones.tdir/catzones.secondary.conf.in new file mode 100644 index 000000000..a5d1ed332 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.secondary.conf.in @@ -0,0 +1,41 @@ +server: + zonesdir: "." + username: "" + database: "" + verbosity: 2 + ip-address: 127.0.0.1 + port: @SECONDARY_PORT@ + pidfile: "secondary.pid" + logfile: "secondary.log" + xfrdfile: "secondary-xfrd.state" + zonelistfile: "secondary-zone.list" + cookie-secret-file: "secondary_cookiesecrets.txt" + #xfrd-reload-timeout: 0 + +remote-control: + control-enable: yes + control-port: @SECONDARY_CTRL_PORT@ + server-key-file: "catzones.server.key" + server-cert-file: "catzones.server.pem" + control-key-file: "catzones.control.key" + control-cert-file: "catzones.control.pem" + +pattern: + name: "xfr-from-primary" + request-xfr: 127.0.0.1@@PRIMARY_PORT@ NOKEY + allow-notify: 127.0.0.1 NOKEY + +pattern: + name: "group0" + include-pattern: "xfr-from-primary" + +pattern: + name: "group1" + include-pattern: "xfr-from-primary" + +zone: + name: "catalog1.invalid" + zonefile: "secondary.%s.zone" + catalog: consumer + include-pattern: "xfr-from-primary" + diff --git a/tpkg/catzones.tdir/catzones.secondary2.conf.in b/tpkg/catzones.tdir/catzones.secondary2.conf.in new file mode 100644 index 000000000..e281710d3 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.secondary2.conf.in @@ -0,0 +1,41 @@ +server: + zonesdir: "." + username: "" + database: "" + verbosity: 2 + ip-address: 127.0.0.1 + port: @SECONDARY2_PORT@ + pidfile: "secondary2.pid" + logfile: "secondary2.log" + xfrdfile: "secondary2-xfrd.state" + zonelistfile: "secondary2-zone.list" + cookie-secret-file: "secondary2_cookiesecrets.txt" + #xfrd-reload-timeout: 0 + +remote-control: + control-enable: yes + control-port: @SECONDARY2_CTRL_PORT@ + server-key-file: "catzones.server.key" + server-cert-file: "catzones.server.pem" + control-key-file: "catzones.control.key" + control-cert-file: "catzones.control.pem" + +pattern: + name: "xfr-from-primary" + request-xfr: 127.0.0.1@@PRIMARY_PORT@ NOKEY + allow-notify: 127.0.0.1 NOKEY + +pattern: + name: "group2" + include-pattern: "xfr-from-primary" + +pattern: + name: "group3" + include-pattern: "xfr-from-primary" + +pattern: + name: "consumer" + catalog: consumer + zonefile: "secondary2.%s.zone" + include-pattern: "xfr-from-primary" + diff --git a/tpkg/catzones.tdir/catzones.server.key b/tpkg/catzones.tdir/catzones.server.key new file mode 100644 index 000000000..a069f7561 --- /dev/null +++ b/tpkg/catzones.tdir/catzones.server.key @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQCjsBHexLKQb89F +S+bj3t+QNWMvWgxWReBj7yCjDXGsMU/SaanMUs/4BgTE7IVeaqQQlYlr4TT0m5HS +8UWSIp9BSYH+NA6epbCa7iBFKLOsTOvDAsQuWV3aB1ApqQYGp6Ou2TqIF0DupE0x +jDE32+GEj8p/+jjKhC+BD9XtNf2RyOBQhCgTTlD8oKoPxo+V3T0N8fRymojXElTQ +SP8cgZdHEUEOUlyiG3ACgv0/wJbpxyvs12nIvLPmWQ4L/QvqROwUCd4uMUmeAPSz +ctD6kG+sf2lVPg0SOW9Of58DkaWHep6VeEoRoi0SQpJH+mg/EI6NsDLlvyjStkYk +QuKh26I//QNLetPmTMlCeevMoVPB17cr3AhwudKaKD7EwVobgnuj9cmunUDJG/Mn +jadbq//ZIQuSdha3ppx3A48CiX1+Iyi7PCv7r3rtQBkcmA1mIWir3zLHujZJ0wdB +QL636Xz9enVyrCu4D26eJLgH1Vbhwgra2UPdkL9phA70PrL5qD8CAwEAAQKCAYAH +3rxNOKnZNfcCPu7//8RB+78kFLFJKJzsUSABlvDjNY+/7yKh1012gFuUWEblTj1L +KtVroW1ZYW12dfFBYHbFvdIJNdmKGS9Pk6lridRxsVUYkDZ8gAF6LSXWivDmOwMs +sfcVegC+OJoZisMHC4RhSkSTOKWoXwilrOw9Nn5VlxHp7SC8JOTR8KucjJB0xSld +VUfKhtGTfackSI4zgauTWCoHlUAs7yLIjCG/AfR/kvryrTzB0QjZR2jcfLMo9V1M +tztJLE/xNEZR4T6QOVT+H7yOQFFrL5Pbsh0FOnjTlPjLhrVm+pY8KuQ8ctmFDk3b +OI6dUXsRJQpKUUoGg/CCqUETl/aLQ1YJMzZFZ6NhJ1eLLCol2zduz273JPWa48F0 ++gs8KXWzMsIRDsmEAVwHURyPUFn3/pxuyIuBB4eqKrzak07bN4oS/OLUZwPN/PXs +rwzqRgqSBTNo8i4DhWn6OAn+VY2v54VaImg7Y3lBNoc90x0LnnpVcILtkcwAhtkC +gcEA16qiAgLx3Vt1CqqL4WoFcg4/ZoMCu4dzuExrXzBvy70o8+16WQAlgiYt8e4C +cBnteblRd5QSghlIC0vksuMycGTCWLnt3Ie0xssrRncPJcQW1/apUZnaMUXQCh4u +bdx9LcP/onQWVNOgiTzQPG6DpcaQtQIb52VVVCROdv1m66LqWJLiXE+4pECr7HHC +1u0DHtSVQlOZrKS2HnRsb0cxUvRG0E6jimORkWug+v3eElmK0E9/Icsd3oQLV4s0 +kOBVAoHBAMJM368ZDCdSQx6z7EKsnM5o0lo6DTj10WYbGGXIJELyUZl1SZzZLUe2 +UQ279/ljGN7MFA42GuUeYQYRsX+rQLjIxxg0LDbzhv5iWCgXhKWa10nDoRcNV4tX +xGUDI+QiROwJ9mPZEgcNpJvPINPGTCFjGhGecooDVc8xSQdxDwqMBbucQOMEqSNN +wrbyMm3+s5L5bJX6aEmuPcm07SjAZYgTSSu0YU56HwxBTnmmwSSR0QNHUkhIHODO +bOXX2D0qQwKBwAha8PnY+VvVDjzCjLbH77wSdzxWeQWg4sfSFhrS+pHPG0YP/jxy +eAGJzx/fU7LaHkwn9NMIJotpxVPCtfKEQND/5lOfqGpxy3wkJOIO/MQ+y4/EW5zw +s/ZSuRObRbs13pemT0IQ80iIBmEqvPqKYYlLnxGUKlaVo2+mFHAhsxoP7rpNvn1L +DWE3rDF12tGOzx8jp0CxknOQ0gluA0FwZaXMFu49DbLv1pTmsY7nxPG7DFizq744 +a0jSnsned8PhHQKBwQC3M0aBUWudM7onm9v7bceX80vLWQsYluaukIOrjE9sdBAu +UmFrK3a/NA8ulPoVwbM0d1W9pJQtTjyLPWmQCAfJcz4N+ztpQKsa9d1LSAmBIAM4 +AOXF14Wg08tQtupDc3Bq4mXX9VdDIUEAzAaWx1UG8esli7FRoh1pc335L0fQWQAa +yhSAOOfFas9aMfkMr2ff/5+7tdRZDJojsp0enHHfINgQBk7tcMYPhxAYMfvQoFBj +9/6EOsJH0TrqaF7yJQMCgcATUPBv4R50vvD0AafWch/vJnt2EfbsH89aRmsM8quH +9+rMcVUqN0yy5YveRsYxDvIpGPMbvXijG2O07xnPX/5yDV2JDGbWEwg9YUS2mb3g +iGRc5KGJRZnFNdXnAhk8853k6FQbhLlB0fqbYH8x8KtwH9uMn9UkAXFdvr0JKrg7 +9xp+kDb98VxJtXSMeGB5iIgoi5jnAannMiNszk07IoClUpU5f1QZzmz5+UYTNEt9 +PPG/m1FTeHXH407sfqB8frY= +-----END PRIVATE KEY----- diff --git a/tpkg/catzones.tdir/catzones.server.pem b/tpkg/catzones.tdir/catzones.server.pem new file mode 100644 index 000000000..98f5e050c --- /dev/null +++ b/tpkg/catzones.tdir/catzones.server.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPDCCAqSgAwIBAgIUJGe+SRXT0yBhTv5pnLcuYt/29wgwDQYJKoZIhvcNAQEL +BQAwDjEMMAoGA1UEAwwDbnNkMB4XDTIzMTExOTE2MjUxNFoXDTQzMDgwNjE2MjUx +NFowDjEMMAoGA1UEAwwDbnNkMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC +AYEAo7AR3sSykG/PRUvm497fkDVjL1oMVkXgY+8gow1xrDFP0mmpzFLP+AYExOyF +XmqkEJWJa+E09JuR0vFFkiKfQUmB/jQOnqWwmu4gRSizrEzrwwLELlld2gdQKakG +Bqejrtk6iBdA7qRNMYwxN9vhhI/Kf/o4yoQvgQ/V7TX9kcjgUIQoE05Q/KCqD8aP +ld09DfH0cpqI1xJU0Ej/HIGXRxFBDlJcohtwAoL9P8CW6ccr7NdpyLyz5lkOC/0L +6kTsFAneLjFJngD0s3LQ+pBvrH9pVT4NEjlvTn+fA5Glh3qelXhKEaItEkKSR/po +PxCOjbAy5b8o0rZGJELioduiP/0DS3rT5kzJQnnrzKFTwde3K9wIcLnSmig+xMFa +G4J7o/XJrp1AyRvzJ42nW6v/2SELknYWt6acdwOPAol9fiMouzwr+6967UAZHJgN +ZiFoq98yx7o2SdMHQUC+t+l8/Xp1cqwruA9uniS4B9VW4cIK2tlD3ZC/aYQO9D6y ++ag/AgMBAAGjgZEwgY4wHQYDVR0OBBYEFPb//R1Ree4/yWEwjm4NDgyZFsVuMEkG +A1UdIwRCMECAFPb//R1Ree4/yWEwjm4NDgyZFsVuoRKkEDAOMQwwCgYDVQQDDANu +c2SCFCRnvkkV09MgYU7+aZy3LmLf9vcIMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD +VR0RBAcwBYIDbnNkMA0GCSqGSIb3DQEBCwUAA4IBgQBd6s8T0xbDqPOp4dTn3YDi +y16owyzuIp1yE7Cyi1DJnKYaV43Svsc/M0ERg2nR8mv9Xka5z2rLvma2K00D4rl4 +zIgNrUg99l6TVmf3HHtXbUP4RKRSLivlNj4v2Qf1SlAnFUju0XPNOH7EzuiA/vLi +DVwlLFrqyuayiJqnzmt9lLfwQCgpkzhxoHlAKy/YACLTv21sywzQYpWLupE2r/nG +LSvXCOYquQNeYeiwpyXFptAQFRXcSIYfA3ayiEQWmZ/l1/J/4Xd2PJ++eImPoTMN +rWRgymfVUah0e/G4SXrMmwpT07zdAZ87B2w6wRN3ZJekIoZY7Kqew0v4Knv7yY/Z +H+48gnqK6wznGODW4KRh4Kaje2aYicviCfSEnh+jVRkFZfdBO+OXw9jqdoeaJRRV ++71izHLTmWqMvMKjnYHggxeS7TXcQM761wHAjm+8Zl6p4iMTVqIIKpln2JEteaJ3 +FYZtYjczaItlB5LrP++BrFi95s9sAYJ3SP6Uer4NVPI= +-----END CERTIFICATE----- diff --git a/tpkg/catzones.tdir/catzones.test b/tpkg/catzones.tdir/catzones.test new file mode 100755 index 000000000..c209ca7cb --- /dev/null +++ b/tpkg/catzones.tdir/catzones.test @@ -0,0 +1,244 @@ +# #-- catzones.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh +PRE="../.." +TPKG_NSD_CONTROL="$PRE/nsd-control" +PRIMARY_CONTROL="$PRE/nsd-control -c primary.conf" +SECONDARY_CONTROL="$PRE/nsd-control -c secondary.conf" +SECONDARY2_CONTROL="$PRE/nsd-control -c secondary2.conf" +TPKG_NSD_CHECKCONF="$PRE/nsd-checkconf" +N_MEMBER_ZONES=256 +SLEEP="" + +n_zones() { + $TPKG_NSD_CONTROL -c $1.conf zonestatus | grep ^zone: | wc -l +} + +get_serial() { + $1 zonestatus $2 \ + | awk '/\(serial: /{s=substr($4,0,length($4)-1)}END{print s}' +} +get_n_members() { + $1 zonestatus $2 \ + | awk '/\(serial: /{m=substr($7,0,length($7)-1)}END{print m}' +} +print_catzone_status() { + CTRL="$TPKG_NSD_CONTROL -c $1.conf" + echo "$1: `n_zones $1` zones" + shift + while [ $# -gt 0 ] + do + Z=$1.invalid + S=`get_serial $CTRL $Z` + M=`get_n_members $CTRL $Z` + echo " $1 (serial: $S, # members: $M)" + shift + done + echo "" +} + +print_status() { + return + $SLEEP + print_catzone_status primary catalog1 catalog2 catalog3 + print_catzone_status secondary catalog1 + print_catzone_status secondary2 catalog2 +} + + +# Add some zones +# +for (( i = 0; i < $N_MEMBER_ZONES; i++)) +do echo zone${i}.invalid group$((${i} % 4)) +done | $PRIMARY_CONTROL addzones >> nsd-control.log + +# Add the second consumer by nsd-control addzone +# +$SECONDARY2_CONTROL addzone catalog2.invalid consumer + +print_status "$N_MEMBER_ZONES member zones added" + +for (( i = 0; i < $N_MEMBER_ZONES; i++)) +do echo zone${i}.invalid +done | $PRIMARY_CONTROL delzones >> nsd-control.log + +print_status "$N_MEMBER_ZONES member zones deleted" + +for (( i = 0; i < $N_MEMBER_ZONES; i++)) +do echo zone${i}.invalid group$((${i} % 4)) +done | $PRIMARY_CONTROL addzones >> nsd-control.log + +print_status "$N_MEMBER_ZONES member zones added" + +for (( i = 0; i < $(( $N_MEMBER_ZONES / 4 )); i++)) +do echo zone${i}.invalid +done | $PRIMARY_CONTROL delzones >> nsd-control.log + +print_status "$(($N_MEMBER_ZONES / 4)) member zones deleted" + +for (( i = $(( $N_MEMBER_ZONES / 4 * 3 )); i < $(( $N_MEMBER_ZONES )); i++)) +do echo zone${i}.invalid +done | $PRIMARY_CONTROL delzones >> nsd-control.log + +print_status "$(($N_MEMBER_ZONES / 4)) member zones deleted" + +$PRIMARY_CONTROL write +wait_logfile primary.log "writing zone catalog1.invalid" 10 +wait_logfile primary.log "writing zone catalog2.invalid" 10 +CATALOG1_SERIAL=`awk 'FNR==2{print $NF}' primary.catalog1.invalid.zone` +CATALOG2_SERIAL=`awk 'FNR==2{print $NF}' primary.catalog2.invalid.zone` +CATALOG3_SERIAL=`awk 'FNR==2{print $NF}' primary.catalog3.invalid.zone` + +wait_logfile secondary.log "received update to serial $CATALOG1_SERIAL" 10 +wait_logfile secondary2.log "received update to serial $CATALOG2_SERIAL" 10 + +$SECONDARY_CONTROL write +wait_logfile secondary.log "writing zone catalog1.invalid" 10 +$SECONDARY2_CONTROL write +wait_logfile secondary2.log "writing zone catalog2.invalid" 10 + +tail -n +3 primary.catalog1.invalid.zone > catalog1.invalid.zone.pri +tail -n +3 secondary.catalog1.invalid.zone > catalog1.invalid.zone.sec +if ! diff catalog1.invalid.zone.pri catalog1.invalid.zone.sec ; then + >&2 echo "catalog1.invalid differs between primary and secondary" + exit 1 +fi + +tail -n +3 primary.catalog2.invalid.zone > catalog2.invalid.zone.pri +tail -n +3 secondary2.catalog2.invalid.zone > catalog2.invalid.zone.sec2 +if ! diff catalog2.invalid.zone.pri catalog2.invalid.zone.sec2 ; then + >&2 echo "catalog2.invalid differs between primary and secondary" + exit 1 +fi + +print_status "Final stats" + +if [ `n_zones primary` -ne $(( $N_MEMBER_ZONES / 2 + 3 )) ]; then + >&2 echo "primary has wrong number of zones" + exit 1 +fi +if [ `n_zones secondary` -ne $(( $N_MEMBER_ZONES / 4 + 1 )) ]; then + >&2 echo "secondary has wrong number of zones" + exit 1 +fi +if [ `n_zones secondary2` -ne $(( $N_MEMBER_ZONES / 8 + 1 )) ]; then + >&2 echo "secondary2 has wrong number of zones" + exit 1 +fi + +CAT1="catalog producer zones cannot be secondary zones" +CAT2="catalog-producer-zone option can only be used in patterns not zones" +CAT3="catalog-producer-zone could not be found" +CAT4="catalog-producer-zone is not configured as a catalog producer" +for TEST in 1.1 1.2 1.3 1.4 2.1 2.2 3 4; do + if $TPKG_NSD_CHECKCONF catzones.bad${TEST}.conf 2>/dev/null; then + eval echo Failed test $TEST in category '\"'\$CAT${TEST%.*}'\"' + exit 1 + fi +done + +cp primary.conf primary.conf.bak +sed -i 's/-zone: "catalog2.invalid"/-zone: "catalog3.invalid"/' primary.conf +if $PRIMARY_CONTROL reconfig | tail -1 | grep '^ok$'; then + echo "Should not be able to change catalog producer member zones" + exit 1 +fi + +cp primary.conf.bak primary.conf +sed -i 's/catalog-producer-zone: "catalog2.invalid"//' primary.conf +if $PRIMARY_CONTROL reconfig | tail -1 | grep '^ok$'; then + echo "Should not be able to remove catalog producer member zones" + exit 1 +fi + +cp primary.conf.bak primary.conf +cat << EOF >> primary.conf +pattern: + name: "group4" + include-pattern: "primary" + zonefile: "catzones.primary.group4.zone" + catalog-producer-zone: "catalog2.invalid" + +pattern: + name: "group5" + include-pattern: "primary" + zonefile: "catzones.primary.group5.zone" + #catalog-producer-zone: "catalog3.invalid" +EOF +if ! $PRIMARY_CONTROL reconfig | tail -1 | grep '^ok$'; then + echo "It should be fine to add new catalog member zone patterns" + # Because there wouldn't be any zones configured with it anyway + exit 1 +fi + +cp primary.conf primary.conf.bak +sed -i 's/#catalog-producer-zone:/catalog-producer-zone:/' primary.conf +if $PRIMARY_CONTROL reconfig | tail -1 | grep '^ok$'; then + echo "An existing pattern may not become cat producer member zone pat" + exit 1 +fi + +cp primary.conf.bak primary.conf +if ! $PRIMARY_CONTROL reconfig | tail -1 | grep '^ok$'; then + echo "All previous errors should have had no effect" + exit 1 +fi + +# More than one consumer is invalid +$SECONDARY2_CONTROL addzone catalog3.invalid consumer +wait_logfile secondary2.log "catalog3.invalid serial 0 is updated to $CATALOG3_SERIAL" 10 + +for (( i = 0; i < $(( $N_MEMBER_ZONES / 4 )); i++)) +do echo zone${i}.invalid group$((${i} % 4)) +done | $PRIMARY_CONTROL addzones >> nsd-control.log + +for (( i = $(( $N_MEMBER_ZONES / 4 * 3 )); i < $(( $N_MEMBER_ZONES )); i++)) +do echo zone${i}.invalid group$((${i} % 4)) +done | $PRIMARY_CONTROL addzones >> nsd-control.log + +CATALOG2_SERIAL=`get_serial "$PRIMARY_CONTROL" catalog2.invalid` +CATALOG3_SERIAL=`get_serial "$PRIMARY_CONTROL" catalog3.invalid` + +wait_logfile secondary2.log " is updated to $CATALOG3_SERIAL" 10 + +if [ `n_zones secondary2` -ne $(( $N_MEMBER_ZONES / 8 + 2 )) ]; then + >&2 echo "secondary2 should not process and has wrong number of zones" + exit 1 +fi + +$SECONDARY2_CONTROL delzone catalog3.invalid +wait_logfile secondary2.log "catalog2.invalid serial is updated to $CATALOG2_SERIAL" 10 + +if [ `n_zones secondary2` -ne $(( $N_MEMBER_ZONES / 4 + 1 )) ]; then + >&2 echo "secondary2 should be processing again" + exit 1 +fi + +$SECONDARY2_CONTROL addzone catalog3.invalid consumer +$SECONDARY2_CONTROL delzone catalog2.invalid +wait_logfile secondary2.log "catalog3.invalid serial 0 is updated to $CATALOG3_SERIAL" 10 + +if [ `n_zones secondary2` -ne $(( $N_MEMBER_ZONES / 4 + 1 )) ]; then + >&2 echo "secondary2 should be processing again and have $(( $N_MEMBER_ZONES / 4 + 1)) zones" + exit 1 +fi + +wait_logfile secondary2.log "zone zone3.invalid serial 0 is updated to 1" 10 +if ! dig @127.0.0.1 -p $SECONDARY2_PORT zone3.invalid TXT +norec +short | grep group3 +then + >&2 echo "Consumer member zone should be loaded and served" + exit 1 +fi + +if $SECONDARY2_CONTROL delzone zone3.invalid | tail -1 | grep '^ok$'; then + echo "A consumer member zone cannot be deleted with nsd-control delzone" + # But must be deleted from the catalog + exit 1 +fi + +exit 0 + diff --git a/tpkg/checkconf.tdir/checkconf.check b/tpkg/checkconf.tdir/checkconf.check index 92ec60e83..bf0922b48 100644 --- a/tpkg/checkconf.tdir/checkconf.check +++ b/tpkg/checkconf.tdir/checkconf.check @@ -551,3 +551,79 @@ checkconf.nsd09.conf:9: error: expected yes or no read checkconf.nsd09.conf failed: 1 errors in configuration file checkconf.nsd10.conf:10: error: expected a number read checkconf.nsd10.conf failed: 1 errors in configuration file +# Read file checkconf.nsd11.conf: 1 patterns, 0 fixed-zones, 0 keys, 0 tls-auth. +# Config settings. +server: + debug-mode: no + ip-transparent: no + ip-freebind: no + reuseport: no + do-ip4: yes + do-ip6: yes + send-buffer-size: 0 + receive-buffer-size: 0 + hide-version: no + hide-identity: no + drop-updates: no + tcp-reject-overflow: no + #identity: + #version: + #nsid: + #logfile: + log-only-syslog: no + server-count: 1 + tcp-count: 100 + tcp-query-count: 0 + tcp-timeout: 120 + tcp-mss: 0 + outgoing-tcp-mss: 0 + xfrd-tcp-max: 128 + xfrd-tcp-pipeline: 128 + ipv4-edns-size: 1232 + ipv6-edns-size: 1232 + pidfile: "/var/run/nsd.pid" + port: "53" + statistics: 0 + #chroot: + username: "nsd" + zonesdir: "/etc/nsd" + xfrdfile: "/var/db/nsd/xfrd.state" + zonelistfile: "/var/db/nsd/zone.list" + xfrdir: "/tmp" + xfrd-reload-timeout: 1 + log-time-ascii: yes + round-robin: no + minimal-responses: no + confine-to-zone: no + refuse-any: no + verbosity: 0 + zonefiles-check: yes + zonefiles-write: 3600 + #tls-service-key: + #tls-service-pem: + #tls-service-ocsp: + tls-port: "853" + #tls-cert-bundle: + answer-cookie: no + cookie-secret-file: "/etc/nsd/nsd_cookiesecrets.txt" + +remote-control: + control-enable: no + control-port: 8952 + server-key-file: "/etc/nsd/nsd_server.key" + server-cert-file: "/etc/nsd/nsd_server.pem" + control-key-file: "/etc/nsd/nsd_control.key" + control-cert-file: "/etc/nsd/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 + +pattern: + name: "myzones" + catalog: consumer + catalog-member-pattern: "mycatalog2" diff --git a/tpkg/checkconf.tdir/checkconf.check2 b/tpkg/checkconf.tdir/checkconf.check2.in similarity index 73% rename from tpkg/checkconf.tdir/checkconf.check2 rename to tpkg/checkconf.tdir/checkconf.check2.in index 8110829b9..b909a1832 100644 --- a/tpkg/checkconf.tdir/checkconf.check2 +++ b/tpkg/checkconf.tdir/checkconf.check2.in @@ -34,10 +34,10 @@ server: statistics: 60 #chroot: username: "nsd" - zonesdir: "/etc/nsd" - xfrdfile: "/var/db/nsd/xfrd.state" - zonelistfile: "/var/db/nsd/zone.list" - xfrdir: "/tmp" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" xfrd-reload-timeout: 1 log-time-ascii: yes round-robin: no @@ -48,20 +48,30 @@ server: ip-address: 127.0.0.1 ip-address: 10.1.2.3 zonefiles-check: yes - zonefiles-write: 0 + zonefiles-write: 3600 #tls-service-key: #tls-service-pem: #tls-service-ocsp: tls-port: "853" + #tls-cert-bundle: answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" remote-control: control-enable: no control-port: 8952 - server-key-file: "/etc/nsd/nsd_server.key" - server-cert-file: "/etc/nsd/nsd_server.pem" - control-key-file: "/etc/nsd/nsd_control.key" - control-cert-file: "/etc/nsd/nsd_control.pem" + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 key: name: "BKEY" @@ -159,10 +169,10 @@ server: statistics: 0 #chroot: username: "nsd" - zonesdir: "/etc/nsd" - xfrdfile: "/var/db/nsd/xfrd.state" - zonelistfile: "/var/db/nsd/zone.list" - xfrdir: "/tmp" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" xfrd-reload-timeout: 1 log-time-ascii: yes round-robin: no @@ -171,20 +181,30 @@ server: refuse-any: no verbosity: 0 zonefiles-check: yes - zonefiles-write: 0 + zonefiles-write: 3600 #tls-service-key: #tls-service-pem: #tls-service-ocsp: tls-port: "853" + #tls-cert-bundle: answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" remote-control: control-enable: no control-port: 8952 - server-key-file: "/etc/nsd/nsd_server.key" - server-cert-file: "/etc/nsd/nsd_server.pem" - control-key-file: "/etc/nsd/nsd_control.key" - control-cert-file: "/etc/nsd/nsd_control.pem" + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 zone: name: "example.com" @@ -220,15 +240,15 @@ server: xfrd-tcp-pipeline: 128 ipv4-edns-size: 1232 ipv6-edns-size: 1232 - pidfile: "/var/run/nsd.pid" + pidfile: "@pidfile@" port: "53" statistics: 0 #chroot: username: "nsd" - zonesdir: "/etc/nsd" - xfrdfile: "/var/db/nsd/xfrd.state" - zonelistfile: "/var/db/nsd/zone.list" - xfrdir: "/tmp" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" xfrd-reload-timeout: 1 log-time-ascii: no round-robin: no @@ -237,20 +257,30 @@ server: refuse-any: no verbosity: 0 zonefiles-check: yes - zonefiles-write: 0 + zonefiles-write: 3600 #tls-service-key: #tls-service-pem: #tls-service-ocsp: tls-port: "853" + #tls-cert-bundle: answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" remote-control: control-enable: no control-port: 8952 - server-key-file: "/etc/nsd/nsd_server.key" - server-cert-file: "/etc/nsd/nsd_server.pem" - control-key-file: "/etc/nsd/nsd_control.key" - control-cert-file: "/etc/nsd/nsd_control.pem" + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 key: name: "tsig.example.org." @@ -295,15 +325,15 @@ server: xfrd-tcp-pipeline: 128 ipv4-edns-size: 1232 ipv6-edns-size: 1232 - pidfile: "/var/run/nsd.pid" + pidfile: "@pidfile@" port: "53" statistics: 0 #chroot: username: "nsd" - zonesdir: "/etc/nsd" - xfrdfile: "/var/db/nsd/xfrd.state" - zonelistfile: "/var/db/nsd/zone.list" - xfrdir: "/tmp" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" xfrd-reload-timeout: 1 log-time-ascii: yes round-robin: no @@ -312,20 +342,30 @@ server: refuse-any: no verbosity: 0 zonefiles-check: yes - zonefiles-write: 0 + zonefiles-write: 3600 #tls-service-key: #tls-service-pem: #tls-service-ocsp: tls-port: "853" + #tls-cert-bundle: answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" remote-control: control-enable: no control-port: 8952 - server-key-file: "/etc/nsd/nsd_server.key" - server-cert-file: "/etc/nsd/nsd_server.pem" - control-key-file: "/etc/nsd/nsd_control.key" - control-cert-file: "/etc/nsd/nsd_control.pem" + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 zone: name: "example.nl" @@ -414,15 +454,15 @@ server: xfrd-tcp-pipeline: 128 ipv4-edns-size: 1232 ipv6-edns-size: 1232 - pidfile: "/var/run/nsd.pid" + pidfile: "@pidfile@" port: "53" statistics: 0 #chroot: username: "nsd" - zonesdir: "/etc/nsd" - xfrdfile: "/var/db/nsd/xfrd.state" - zonelistfile: "/var/db/nsd/zone.list" - xfrdir: "/tmp" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" xfrd-reload-timeout: 1 log-time-ascii: yes round-robin: no @@ -431,20 +471,30 @@ server: refuse-any: no verbosity: 0 zonefiles-check: yes - zonefiles-write: 0 + zonefiles-write: 3600 #tls-service-key: #tls-service-pem: #tls-service-ocsp: tls-port: "853" + #tls-cert-bundle: answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" remote-control: control-enable: no control-port: 8952 - server-key-file: "/etc/nsd/nsd_server.key" - server-cert-file: "/etc/nsd/nsd_server.pem" - control-key-file: "/etc/nsd/nsd_control.key" - control-cert-file: "/etc/nsd/nsd_control.pem" + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 pattern: name: "bla" @@ -501,3 +551,95 @@ checkconf.nsd09.conf:9: error: expected yes or no read checkconf.nsd09.conf failed: 1 errors in configuration file checkconf.nsd10.conf:10: error: expected a number read checkconf.nsd10.conf failed: 1 errors in configuration file +# Read file checkconf.nsd11.conf: 5 patterns, 1 fixed-zones, 0 keys, 0 tls-auth. +# Config settings. +server: + debug-mode: no + ip-transparent: no + ip-freebind: no + reuseport: no + do-ip4: yes + do-ip6: yes + send-buffer-size: 0 + receive-buffer-size: 0 + hide-version: no + hide-identity: no + drop-updates: no + tcp-reject-overflow: no + #identity: + #version: + #nsid: + #logfile: + log-only-syslog: no + server-count: 1 + tcp-count: 100 + tcp-query-count: 0 + tcp-timeout: 120 + tcp-mss: 0 + outgoing-tcp-mss: 0 + xfrd-tcp-max: 128 + xfrd-tcp-pipeline: 128 + ipv4-edns-size: 1232 + ipv6-edns-size: 1232 + pidfile: "@pidfile@" + port: "53" + statistics: 0 + #chroot: + username: "nsd" + zonesdir: "@zonesdir@" + xfrdfile: "@xfrdfile@" + zonelistfile: "@zonelistfile@" + xfrdir: "@xfrdir@" + xfrd-reload-timeout: 1 + log-time-ascii: yes + round-robin: no + minimal-responses: no + confine-to-zone: no + refuse-any: no + verbosity: 0 + zonefiles-check: yes + zonefiles-write: 3600 + #tls-service-key: + #tls-service-pem: + #tls-service-ocsp: + tls-port: "853" + #tls-cert-bundle: + answer-cookie: no + cookie-secret-file: "@configdir@/nsd_cookiesecrets.txt" + +remote-control: + control-enable: no + control-port: 8952 + server-key-file: "@configdir@/nsd_server.key" + server-cert-file: "@configdir@/nsd_server.pem" + control-key-file: "@configdir@/nsd_control.key" + control-cert-file: "@configdir@/nsd_control.pem" + +verify: + enable: no + port: 5347 + verify-zones: yes + verifier-count: 1 + verifier-feed-zone: yes + verifier-timeout: 0 + +pattern: + name: "group1" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group2" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group3" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "myzones" + catalog: consumer + catalog-member-pattern: "mycatalog2" + +zone: + name: "producer.catalog.invalid" + catalog: producer diff --git a/tpkg/checkconf.tdir/checkconf.dsc b/tpkg/checkconf.tdir/checkconf.dsc index 072a7de2e..249b64857 100644 --- a/tpkg/checkconf.tdir/checkconf.dsc +++ b/tpkg/checkconf.tdir/checkconf.dsc @@ -7,9 +7,9 @@ Category: Component: Depends: 0000_nsd-compile.tpkg Help: checkconf.help -Pre: +Pre: checkconf.pre Post: Test: checkconf.test -AuxFiles:checkconf.check, checkconf.nsd01.conf, checkconf.nsd02.conf, checkconf.nsd03.conf, checkconf.nsd04.conf, checkconf.nsd05.conf, checkconf.nsd06.conf, checkconf.nsd07.conf, checkconf.check2 +AuxFiles:checkconf.check, checkconf.check2.in, checkconf.nsd01.conf, checkconf.nsd02.conf, checkconf.nsd03.conf, checkconf.nsd04.conf, checkconf.nsd05.conf, checkconf.nsd06.conf, checkconf.nsd07.conf Passed: Failure: diff --git a/tpkg/checkconf.tdir/checkconf.nsd11.conf b/tpkg/checkconf.tdir/checkconf.nsd11.conf new file mode 100644 index 000000000..02252d7d7 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.nsd11.conf @@ -0,0 +1,530 @@ +# +# nsd.conf -- the NSD(8) configuration file, nsd.conf(5). +# +# Copyright (c) 2001-2011, NLnet Labs. All rights reserved. +# +# See LICENSE for the license. +# + +# This is a comment. +# Sample configuration file +# include: "file" # include that file's text over here. Globbed, "*.conf" + +# options for the nsd server +server: + # Number of NSD servers to fork. Put the number of CPUs to use here. + # server-count: 1 + + # Set overall CPU affinity for NSD processes on Linux and FreeBSD. + # Any server/xfrd CPU affinity value will be masked by this value. + # cpu-affinity: 0 1 2 3 + + # Bind NSD server(s), configured by server-count (1-based), to a + # dedicated core. Single core affinity improves L1/L2 cache hits and + # reduces pipeline stalls/flushes. + # + # server-1-cpu-affinity: 0 + # server-2-cpu-affinity: 1 + # ... + # server--cpu-affinity: 2 + + # Bind xfrd to a dedicated core. + # xfrd-cpu-affinity: 3 + + # Specify specific interfaces to bind (default are the wildcard + # interfaces 0.0.0.0 and ::0). + # For servers with multiple IP addresses, list them one by one, + # or the source address of replies could be wrong. + # Use ip-transparent to be able to list addresses that turn on later. + # ip-address: 1.2.3.4 + # ip-address: 1.2.3.4@5678 + # ip-address: 12fe::8ef0 + # + # IP addresses can be configured per-server to avoid waking up more + # than one server when a packet comes in (thundering herd problem) or + # to partition sockets across servers to improve select/poll + # performance. + # + # ip-address: 1.2.3.4 servers="1-2 3" + # ip-address: 1.2.3.4@5678 servers="4-5 6" + # + # When several interfaces are configured to listen on the same subnet, + # care must be taken to ensure responses go out the same interface the + # corresponding query came in on to avoid problems with load balancers + # and VLAN tagged interfaces. Linux offers the SO_BINDTODEVICE socket + # option to bind a socket to a specified device. For FreeBSD, to + # achieve the same result, specify the routing table to use after the + # IP address to use SO_SETFIB. + # + # Complement with socket partitioning and CPU affinity for attack + # mitigation benefits. i.e. only a single core is maxed out if a + # specific IP address is under attack. + # + # ip-address: 1.2.3.4 setfib=0 bindtodevice=yes + # ip-address: 1.2.3.5@6789 setfib=1 bindtodevice=yes + + # Allow binding to non local addresses. Default no. + # ip-transparent: no + + # Allow binding to addresses that are down. Default no. + # ip-freebind: no + + # Use SO_REUSEPORT socket option for performance. Default no. + # reuseport: no + + # override maximum socket send buffer size. Default of 0 results in + # send buffer size being set to 1048576 (bytes). + # send-buffer-size: 1048576 + + # override maximum socket receive buffer size. Default of 0 results in + # receive buffer size being set to 1048576 (bytes). + # receive-buffer-size: 1048576 + + # enable debug mode, does not fork daemon process into the background. + # debug-mode: no + + # listen on IPv4 connections + # do-ip4: yes + + # listen on IPv6 connections + # do-ip6: yes + + # port to answer queries on. default is 53. + # port: 53 + + # Verbosity level. + # verbosity: 0 + + # After binding socket, drop user privileges. + # can be a username, id or id.gid. + # username: @user@ + + # Run NSD in a chroot-jail. + # make sure to have pidfile and database reachable from there. + # by default, no chroot-jail is used. + # chroot: "@configdir@" + + # The directory for zonefile: files. The daemon chdirs here. + # zonesdir: "@zonesdir@" + + # the list of dynamically added zones. + # zonelistfile: "@zonelistfile@" + + # the database to use + # if set to "" then no disk-database is used, less memory usage. + # database: "@dbfile@" + + # log messages to file. Default to stderr and syslog (with + # facility LOG_DAEMON). stderr disappears when daemon goes to bg. + # logfile: "@logfile@" + + # log only to syslog. + # log-only-syslog: no + + # File to store pid for nsd in. + # pidfile: "@pidfile@" + + # The file where secondary zone refresh and expire timeouts are kept. + # If you delete this file, all secondary zones are forced to be + # 'refreshing' (as if nsd got a notify). Set to "" to disable. + # xfrdfile: "@xfrdfile@" + + # The directory where zone transfers are stored, in a subdir of it. + # xfrdir: "@xfrdir@" + + # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries + # hide-version: no + + # don't answer HOSTNAME.BIND and ID.SERVER CHAOS class queries + # hide-identity: no + + # Drop UPDATE queries + # drop-updates: no + + # version string the server responds with for chaos queries. + # default is 'NSD x.y.z' with the server's version number. + # version: "NSD" + + # identify the server (CH TXT ID.SERVER entry). + # identity: "unidentified server" + + # NSID identity (hex string, or "ascii_somestring"). default disabled. + # nsid: "aabbccdd" + + # Maximum number of concurrent TCP connections per server. + # tcp-count: 100 + + # Accept (and immediately close) TCP connections after maximum number + # of connections is reached to prevent kernel connection queue from + # growing. + # tcp-reject-overflow: no + + # Maximum number of queries served on a single TCP connection. + # By default 0, which means no maximum. + # tcp-query-count: 0 + + # Override the default (120 seconds) TCP timeout. + # tcp-timeout: 120 + + # Maximum segment size (MSS) of TCP socket on which the server + # responds to queries. Default is 0, system default MSS. + # tcp-mss: 0 + + # Maximum segment size (MSS) of TCP socket for outgoing AXFR request. + # Default is 0, system default MSS. + # outgoing-tcp-mss: 0 + + # reduce these settings to save memory for NSD, to about + # xfrd-tcp-max: 32 and xfrd-tcp-pipeline: 128, also rrl-size: 1000 + # other memory is determined by server-count, tcp-count and zone data + # max number of sockets used for outgoing zone transfers. + # Increase this to allow more sockets for zone transfers. + # xfrd-tcp-max: 128 + # max number of simultaneous outgoing zone transfers over one socket. + # xfrd-tcp-pipeline: 128 + + # Preferred EDNS buffer size for IPv4. + # ipv4-edns-size: 1232 + + # Preferred EDNS buffer size for IPv6. + # ipv6-edns-size: 1232 + + # statistics are produced every number of seconds. Prints to log. + # Default is 0, meaning no statistics are produced. + # statistics: 3600 + + # Number of seconds between reloads triggered by xfrd. + # xfrd-reload-timeout: 1 + + # log timestamp in ascii (y-m-d h:m:s.msec), yes is default. + # log-time-ascii: yes + + # round robin rotation of records in the answer. + # round-robin: no + + # minimal-responses only emits extra data for referrals. + # minimal-responses: no + + # Do not return additional information if the apex zone of the + # additional information is configured but does not match the apex zone + # of the initial query. + # confine-to-zone: no + + # refuse queries of type ANY. For stopping floods. + # refuse-any: no + + # check mtime of all zone files on start and sighup + # zonefiles-check: yes + + # write changed zonefiles to disk, every N seconds. + # default is 0(disabled) or 3600(if database is ""). + # zonefiles-write: 3600 + + # RRLconfig + # Response Rate Limiting, size of the hashtable. Default 1000000. + # rrl-size: 1000000 + + # Response Rate Limiting, maximum QPS allowed (from one query source). + # If set to 0, ratelimiting is disabled. Also set + # rrl-whitelist-ratelimit to 0 to disable ratelimit processing. + # Default is @ratelimit_default@. + # rrl-ratelimit: 200 + + # Response Rate Limiting, number of packets to discard before + # sending a SLIP response (a truncated one, allowing an honest + # resolver to retry with TCP). Default is 2 (one half of the + # queries will receive a SLIP response, 0 disables SLIP (all + # packets are discarded), 1 means every request will get a + # SLIP response. When the ratelimit is hit the traffic is + # divided by the rrl-slip value. + # rrl-slip: 2 + + # Response Rate Limiting, IPv4 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv4-prefix-length: 24 + + # Response Rate Limiting, IPv6 prefix length. Addresses are + # grouped by netblock. + # rrl-ipv6-prefix-length: 64 + + # Response Rate Limiting, maximum QPS allowed (from one query source) + # for whitelisted types. Default is @ratelimit_default@. + # rrl-whitelist-ratelimit: 2000 + # RRLend + + # Service clients over TLS (on the TCP sockets), with plain DNS inside + # the TLS stream. Give the certificate to use and private key. + # Default is "" (disabled). Requires restart to take effect. + # tls-service-key: "path/to/privatekeyfile.key" + # tls-service-pem: "path/to/publiccertfile.pem" + # tls-service-ocsp: "path/to/ocsp.pem" + # tls-port: 853 + + # Certificates used to authenticate connections made upstream for + # Transfers over TLS (XoT). Default is "" (default verify locations). + # tls-cert-bundle: "path/to/ca-bundle.pem" + +verify: + # Enable zone verification. Default is no. + # enable: no + + # Port to answer verifier queries on. Default is 5347. + # port: 5347 + + # Interfaces to bind for zone verification (default are the localhost + # interfaces, usually 127.0.0.1 and ::1). To bind to to multiple IP + # addresses, list them one by one. Socket options cannot be specified + # for verify ip-address options. + # ip-address: 127.0.0.1 + # ip-address: 127.0.0.1@5347 + # ip-address: ::1 + + # Verify zones by default. Default is yes. + # verify-zones: yes + + # Command to execute for zone verification. + # verifier: ldns-verify-zone + # verifier: validns - + # verifier: drill -k @127.0.0.1 -p 5347 example.com SOA + + # Maximum number of verifiers to run concurrently. Default is 1. + # verifier-count: 1 + + # Feed updated zone to verifier over standard input. Default is yes. + # verifier-feed-zone: yes + + # Number of seconds before verifier is killed (0 is forever). + # verifier-timeout: 0 + +# DNSTAP config section, if compiled with that +# dnstap: + # set this to yes and set one or more of dnstap-log-..-messages to yes. + # dnstap-enable: no + # dnstap-socket-path: "@dnstap_socket_path@" + # dnstap-send-identity: no + # dnstap-send-version: no + # dnstap-identity: "" + # dnstap-version: "" + # dnstap-log-auth-query-messages: no + # dnstap-log-auth-response-messages: no + +# Remote control config section. +remote-control: + # Enable remote control with nsd-control(8) here. + # set up the keys and certificates with nsd-control-setup. + # control-enable: no + + # what interfaces are listened to for control, default is on localhost. + # interfaces can be specified by IP address or interface name. + # with an interface name, all IP addresses associated with that + # interface are used. + # with an absolute path, a unix local named pipe is used for control + # (and key and cert files are not needed, use directory permissions). + # control-interface: 127.0.0.1 + # control-interface: ::1 + # control-interface: lo + + # port number for remote control operations (uses TLS over TCP). + # control-port: 8952 + + # nsd server key file for remote control. + # server-key-file: "@configdir@/nsd_server.key" + + # nsd server certificate file for remote control. + # server-cert-file: "@configdir@/nsd_server.pem" + + # nsd-control key file. + # control-key-file: "@configdir@/nsd_control.key" + + # nsd-control certificate file. + # control-cert-file: "@configdir@/nsd_control.pem" + + +# Secret keys for TSIGs that secure zone transfers. +# You could include: "secret.keys" and put the 'key:' statements in there, +# and give that file special access control permissions. +# +# key: + # The key name is sent to the other party, it must be the same + #name: "keyname" + # algorithm hmac-md5, or sha1, sha256, sha224, sha384, sha512 + #algorithm: sha256 + # secret material, must be the same as the other party uses. + # base64 encoded random number. + # e.g. from dd if=/dev/random of=/dev/stdout count=1 bs=32 | base64 + #secret: "K2tf3TRjvQkVCmJF3/Z9vA==" + +# The tls-auth clause establishes authentication attributes to use when +# authenticating the far end of an outgoing TLS connection in access control +# lists used for XFR-over-TLS. If authentication fails, the XFR request will not +# be made. Support for TLS 1.3 is required for XFR-over-TLS. It has the +# following attributes: +# +# tls-auth: + # The tls-auth name. Used to refer to this TLS auth information in the access control list. + #name: "tls-authname" + # The authentication domain name as defined in RFC8310. + #auth-domain-name: "example.com" + + # Client certificate and private key for Mutual TLS authentication + #client-cert: "path/to/clientcert.pem" + #client-key: "path/to/clientkey.key" + #client-key-pw: "password" + +# Patterns have zone configuration and they are shared by one or more zones. +# +pattern: + # name by which the pattern is referred to + name: "myzones" + # the zonefile for the zones that use this pattern. + # if relative then from the zonesdir (inside the chroot). + # the name is processed: %s - zone name (as appears in zone:name). + # %1 - first character of zone name, %2 second, %3 third. + # %z - topleveldomain label of zone, %y, %x next labels in name. + # if label or character does not exist you get a dot '.'. + # for example "%s.zone" or "zones/%1/%2/%3/%s" or "secondary/%z/%s" + #zonefile: "%s.zone" + + # The allow-query allows an access control list to be specified + # for a zone to be queried. Without an allow-query option, any + # IP address is allowed to send queries for the zone. + # This could be useful for example to not leak content from a zone + # which is only offered for transfer to secondaries over TLS. + #allow-query: 192.0.2.0/24 NOKEY + + # If no master and slave access control elements are provided, + # this zone will not be served to/from other servers. + + # A master zone needs notify: and provide-xfr: lists. A slave + # may also allow zone transfer (for debug or other secondaries). + # notify these slaves when the master zone changes, address TSIG|NOKEY + # IP can be ipv4 and ipv6, with @port for a nondefault port number. + #notify: 192.0.2.1 NOKEY + # allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED + # address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40 + #provide-xfr: 192.0.2.0/24 my_tsig_key_name + # set the number of retries for notify. + #notify-retry: 5 + # if yes, store and provide IXFRs. + #store-ixfr: no + # number of IXFR versions to store, at most. + #ixfr-number: 5 + # size in bytes of max storage to use for IXFR versions. + #ixfr-size: 1048576 + # if yes, create IXFR when a zonefile is read by the server. + #create-ixfr: no + + # uncomment to provide AXFR to all the world + # provide-xfr: 0.0.0.0/0 NOKEY + # provide-xfr: ::0/0 NOKEY + + # A slave zone needs allow-notify: and request-xfr: lists. + #allow-notify: 2001:db8::0/64 my_tsig_key_name + # By default, a slave will request a zone transfer with IXFR/TCP. + # If you want to make use of IXFR/UDP use: UDP addr tsigkey + # for a master that only speaks AXFR (like NSD) use AXFR addr tsigkey + # If you want to require use of XFR-over-TLS use: addr tsigkey tlsauthname + #request-xfr: 192.0.2.2 the_tsig_key_name + #request-xfr: 192.0.2.2 the_tsig_key_name the_tls_auth_name + # Attention: You cannot use UDP and AXFR together. AXFR is always over + # TCP. If you use UDP, we highly recommend you to deploy TSIG. + # Allow AXFR fallback if the master does not support IXFR. Default + # is yes. + #allow-axfr-fallback: yes + # set local interface for sending zone transfer requests. + # default is let the OS choose. + #outgoing-interface: 10.0.0.10 + # limit the refresh and retry interval in seconds. + #max-refresh-time: 2419200 + #min-refresh-time: 0 + #max-retry-time: 1209600 + #min-retry-time: 0 + # Lower bound of expire interval in seconds. The value can be "refresh+retry+1" + # in which case the lower bound of expire interval is the sum of the refresh and + # retry values (limited to the bounds given with the above parameters), plus 1. + #min-expire-time: 0 + + # Slave server tries zone transfer to all masters and picks highest + # zone version available, for when masters have different versions. + #multi-master-check: no + + # limit the zone transfer size (in bytes), stops very large transfers + # 0 is no limits enforced. + # size-limit-xfr: 0 + + # if compiled with --enable-zone-stats, give name of stat block for + # this zone (or group of zones). Output from nsd-control stats. + # zonestats: "%s" + + # if you give another pattern name here, at this point the settings + # from that pattern are inserted into this one (as if it were a + # macro). The statement can be given in between other statements, + # because the order of access control elements can make a difference + # (which master to request from first, which slave to notify first). + #include-pattern: "common-masters" + + # Verify zone before publishing. + # Default is value of verify-zones in verify. + # verify-zone: yes + + # Command to execute for zone verification. + # Default is verifier in verify. + # verifier: ldns-verify-zone + # verifier: validns - + # verifier: drill -k @127.0.0.1 -p 5347 example.com SOA + + # Feed updated zone to verifier over standard input. + # Default is value of verifier-feed-zone in verify. + # verifier-feed-zone: yes + + # Number of seconds before verifier is killed (0 is forever). + # Default is verifier-timeout in verify. + # verifier-timeout: 0 + + # Pattern for the catalog producer that generates the contents of the + # catalog zone. + catalog: consumer + catalog-member-pattern: "mycatalog2" + +pattern: + name: "group1" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group2" + catalog-producer-zone: "producer.catalog.invalid" + +pattern: + name: "group3" + catalog-producer-zone: "producer.catalog.invalid" + +zone: + name: producer.catalog.invalid + catalog: producer + +# Fixed zone entries. Here you can config zones that cannot be deleted. +# Zones that are dynamically added and deleted are put in the zonelist file. +# +# zone: + # name: "example.com" + # you can give a pattern here, all the settings from that pattern + # are then inserted at this point + # include-pattern: "master" + # You can also specify (additional) options directly for this zone. + # zonefile: "example.com.zone" + # request-xfr: 192.0.2.1 example.com.key + + # RRLconfig + # Response Rate Limiting, whitelist types + # rrl-whitelist: nxdomain + # rrl-whitelist: error + # rrl-whitelist: referral + # rrl-whitelist: any + # rrl-whitelist: rrsig + # rrl-whitelist: wildcard + # rrl-whitelist: nodata + # rrl-whitelist: dnskey + # rrl-whitelist: positive + # rrl-whitelist: all + # RRLend + diff --git a/tpkg/checkconf.tdir/checkconf.pre b/tpkg/checkconf.tdir/checkconf.pre new file mode 100644 index 000000000..a1d1d0579 --- /dev/null +++ b/tpkg/checkconf.tdir/checkconf.pre @@ -0,0 +1,17 @@ +# #-- checkconf.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test +. ../common.sh + +( +awk '/^TARGETS/{exit}{print}' ../../Makefile +cat << EOT +checkconf.check2: checkconf.check2.in + \$(EDIT) checkconf.check2.in > checkconf.check2 +EOT +) > Makefile +rm -f checkconf.check2 +make + diff --git a/tpkg/cutest/qtest.c b/tpkg/cutest/qtest.c index b2fcce17d..11fa90a04 100644 --- a/tpkg/cutest/qtest.c +++ b/tpkg/cutest/qtest.c @@ -76,7 +76,7 @@ qsetup(nsd_type* nsd, region_type* region, query_type** query, char* config) /* options */ printf("read %s\n", config); nsd->options = nsd_options_create(region); - if(!parse_options_file(nsd->options, config, NULL, NULL)) { + if(!parse_options_file(nsd->options, config, NULL, NULL, NULL)) { printf("failed to read %s\n", config); exit(1); } diff --git a/util.c b/util.c index 7afe34e65..13a2ec9b4 100644 --- a/util.c +++ b/util.c @@ -63,6 +63,14 @@ static log_function_type *current_log_function = log_file; static FILE *current_log_file = NULL; int log_time_asc = 1; +#ifdef USE_LOG_PROCESS_ROLE +void +log_set_process_role(const char *process_role) +{ + global_ident = process_role; +} +#endif + void log_init(const char *ident) { diff --git a/util.h b/util.h index 97b7ba0cf..ab6315edf 100644 --- a/util.h +++ b/util.h @@ -43,6 +43,15 @@ struct nsd; */ void log_init(const char *ident); +#ifdef USE_LOG_PROCESS_ROLE +/* + * Set the name of the role for the process (for debugging purposes) + */ +void log_set_process_role(const char* role); +#else +#define log_set_process_role(role) /* empty */ +#endif + /* * Open the system log. If FILENAME is not NULL, a log file is opened * as well. diff --git a/xfrd-catalog-zones.c b/xfrd-catalog-zones.c new file mode 100644 index 000000000..bc0baa078 --- /dev/null +++ b/xfrd-catalog-zones.c @@ -0,0 +1,1285 @@ +/* + * xfrd-catalog-zones.c -- catalog zone implementation for NSD + * + * Copyright (c) 2024, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + */ +#include "config.h" +#include "difffile.h" +#include "nsd.h" +#include "packet.h" +#include "xfrd-catalog-zones.h" +#include "xfrd-notify.h" + + +/****************** ****************** + ****************** catalog consumer zone processing ****************** + ****************** ******************/ + +/** process a catalog consumer zone, load if needed */ +static void xfrd_process_catalog_consumer_zone( + struct xfrd_catalog_consumer_zone* consumer_zone); + +/** make the catalog consumer zone invalid for given reason */ +static void vmake_catalog_consumer_invalid( + struct xfrd_catalog_consumer_zone *consumer_zone, + const char *format, va_list args); + +/** return (static) dname with label prepended to dname */ +static dname_type* label_plus_dname(const char* label,const dname_type* dname); + +/** delete the catalog member zone */ +static void catalog_del_consumer_member_zone( + struct xfrd_catalog_consumer_zone* consumer_zone, + struct catalog_member_zone* consumer_member_zone); + +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES +/* return a single catalog consumer zone from xfrd struct */ +static inline struct xfrd_catalog_consumer_zone* +xfrd_one_catalog_consumer_zone() +{ + return xfrd + && xfrd->catalog_consumer_zones + && xfrd->catalog_consumer_zones->count == 1 + ? (struct xfrd_catalog_consumer_zone*) + rbtree_first(xfrd->catalog_consumer_zones) : NULL; +} +#endif + +/** return the catalog-member-pattern or NULL on error if not present */ +static inline struct pattern_options* +catalog_member_pattern(struct xfrd_catalog_consumer_zone* consumer_zone) +{ + if (!consumer_zone->options->pattern + || !consumer_zone->options->pattern->catalog_member_pattern) + return NULL; + return pattern_options_find(xfrd->nsd->options, + consumer_zone->options->pattern->catalog_member_pattern); +} + +/** see if we have more zonestatistics entries and it has to be incremented */ +static inline void +zonestat_inc_ifneeded() +{ +#ifdef USE_ZONE_STATS + if(xfrd->nsd->options->zonestatnames->count != xfrd->zonestat_safe) + task_new_zonestat_inc(xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, + xfrd->nsd->options->zonestatnames->count); +#endif /* USE_ZONE_STATS */ +} + + +/****************** ****************** + ****************** catalog producer zone processing ****************** + ****************** ******************/ + +/** process catalog producer zone producer_zone */ +static void xfrd_process_catalog_producer_zone( + struct xfrd_catalog_producer_zone* producer_zone); + +/** rbnode must be struct catalog_member_zone*; compares (key->member_id) */ +static int member_id_compare(const void *left, const void *right); + +/** return xfrd_catalog_producer_zone* pointed to by cmz' catalog-producer-zone + * pattern option. struct is created if necessary. returns NULL on failure. */ +static struct xfrd_catalog_producer_zone* xfrd_get_catalog_producer_zone( + struct catalog_member_zone* cmz); + +/** helper struct for generating XFR files, for conveying the catalog producer + * zone content to the server process. + */ +struct xfrd_xfr_writer { + struct xfrd_catalog_producer_zone* producer_zone; + char packet_space[16384]; + buffer_type packet; + uint32_t seq_nr; /* number of messages already handled */ + uint32_t old_serial, new_serial; /* host byte order */ + uint64_t xfrfilenumber; /* identifier for file to store xfr into */ +}; + +/** initialize xfrd_xfr_writer struct xw */ +static void xfr_writer_init(struct xfrd_xfr_writer* xw, + struct xfrd_catalog_producer_zone* producer_zone); + +/** write packet from xfrd_xfr_writer struct xw to xfr file */ +static void xfr_writer_write_packet(struct xfrd_xfr_writer* xw); + +/** commit xfr file (send to server process), with provided log message */ +static void xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, + ...); + +/** try writing SOA RR with serial to packet buffer. returns 0 on failure */ +static int try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, + uint32_t serial); + +/** try writing RR to packet buffer. returns 0 on failure */ +static int try_buffer_write_RR(buffer_type* packet, const dname_type* owner, + uint16_t rr_type, uint16_t rdata_len, const void* rdata); + +/** try writing PTR RR to packet buffer. returns 0 on failure */ +static inline int try_buffer_write_PTR(buffer_type* packet, + const dname_type* owner, const dname_type* name); + +/** try writing TXT RR to packet buffer. returns 0 on failure */ +static int try_buffer_write_TXT(buffer_type* packet, const dname_type* name, + const char *txt); + +/** add SOA RR with serial serial to xfrd_xfr_writer xw */ +static inline void xfr_writer_add_SOA(struct xfrd_xfr_writer* xw, + const dname_type* owner, uint32_t serial) +{ + if(try_buffer_write_SOA(&xw->packet, owner, serial)) + return; + xfr_writer_write_packet(xw); + assert(buffer_position(&xw->packet) == 12); + try_buffer_write_SOA(&xw->packet, owner, serial); +} + +/** add RR to xfrd_xfr_writer xw */ +static inline void xfr_writer_add_RR(struct xfrd_xfr_writer* xw, + const dname_type* owner, + uint16_t rr_type, uint16_t rdata_len, const void* rdata) +{ + if(try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata)) + return; + xfr_writer_write_packet(xw); + assert(buffer_position(&xw->packet) == 12); + try_buffer_write_RR(&xw->packet, owner, rr_type, rdata_len, rdata); +} + +/** add PTR RR to xfrd_xfr_writer xw */ +static inline void xfr_writer_add_PTR(struct xfrd_xfr_writer* xw, + const dname_type* owner, const dname_type* name) +{ + if(try_buffer_write_PTR(&xw->packet, owner, name)) + return; + xfr_writer_write_packet(xw); + assert(buffer_position(&xw->packet) == 12); + try_buffer_write_PTR(&xw->packet, owner, name); +} + +/** add TXT RR to xfrd_xfr_writer xw */ +static inline void xfr_writer_add_TXT(struct xfrd_xfr_writer* xw, + const dname_type* owner, const char* txt) +{ + if(try_buffer_write_TXT(&xw->packet, owner, txt)) + return; + xfr_writer_write_packet(xw); + assert(buffer_position(&xw->packet) == 12); + try_buffer_write_TXT(&xw->packet, owner, txt); +} + + +/****************** ****************** + ****************** catalog consumer zone processing ****************** + ****************** ******************/ + +void +xfrd_init_catalog_consumer_zone(xfrd_state_type* xfrd, + struct zone_options* zone) +{ + struct xfrd_catalog_consumer_zone* consumer_zone; + + if ((consumer_zone = (struct xfrd_catalog_consumer_zone*)rbtree_search( + xfrd->catalog_consumer_zones, zone->node.key))) { + log_msg(LOG_ERR, "cannot initialize new catalog consumer zone:" + " '%s: it already exists in xfrd's catalog " + " consumer zones index", zone->name); + /* Maybe we need to reprocess it? */ + make_catalog_consumer_valid(consumer_zone); + return; + } + consumer_zone = (struct xfrd_catalog_consumer_zone*) + region_alloc(xfrd->region, + sizeof(struct xfrd_catalog_consumer_zone)); + memset(consumer_zone, 0, sizeof(struct xfrd_catalog_consumer_zone)); + consumer_zone->node.key = zone->node.key; + consumer_zone->options = zone; + consumer_zone->member_ids.region = xfrd->region; + consumer_zone->member_ids.root = RBTREE_NULL; + consumer_zone->member_ids.count = 0; + consumer_zone->member_ids.cmp = member_id_compare; + consumer_zone->mtime.tv_sec = 0; + consumer_zone->mtime.tv_nsec = 0; + + consumer_zone->invalid = NULL; + rbtree_insert(xfrd->catalog_consumer_zones, + (rbnode_type*)consumer_zone); +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + if ((int)xfrd->catalog_consumer_zones->count > 1) { + log_msg(LOG_ERR, "catalog consumer processing disabled: " + "only one single catalog consumer zone allowed"); + } +#endif + if(zone->pattern && zone->pattern->store_ixfr) { + /* Don't process ixfrs from xfrd */ + zone->pattern->store_ixfr = 0; + } +} + +void +xfrd_deinit_catalog_consumer_zone(xfrd_state_type* xfrd, + const dname_type* dname) +{ + struct xfrd_catalog_consumer_zone* consumer_zone; + zone_type* zone; + + if (!(consumer_zone =(struct xfrd_catalog_consumer_zone*)rbtree_delete( + xfrd->catalog_consumer_zones, dname))) { + log_msg(LOG_ERR, "cannot de-initialize catalog consumer zone:" + " '%s: it did not exist in xfrd's catalog " + " consumer zones index", + dname_to_string(dname, NULL)); + return; + } + if (consumer_zone->member_ids.count) + log_msg(LOG_WARNING, "de-initialize catalog consumer zone:" + " '%s: will cause all member zones to be " + " deleted", consumer_zone->options->name); + + while (consumer_zone->member_ids.count) { + struct catalog_member_zone* cmz = (struct catalog_member_zone*) + rbtree_first(&consumer_zone->member_ids)->key; + + log_msg(LOG_INFO, "deleting member zone '%s' on " + "de-initializing catalog consumer zone '%s'", + cmz->options.name, consumer_zone->options->name); + catalog_del_consumer_member_zone(consumer_zone, cmz); + } + if ((zone = namedb_find_zone(xfrd->nsd->db, dname))) { + namedb_zone_delete(xfrd->nsd->db, zone); + } + region_recycle(xfrd->region, consumer_zone, sizeof(*consumer_zone)); +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + if((consumer_zone = xfrd_one_catalog_consumer_zone()) + && consumer_zone->options && consumer_zone->options->node.key) { + xfrd_zone_type* zone = (xfrd_zone_type*)rbtree_search( + xfrd->zones, + (const dname_type*)consumer_zone->options->node.key); + + if(zone) { + zone->soa_disk_acquired = 0; + zone->soa_nsd_acquired = 0; + xfrd_handle_notify_and_start_xfr(zone, NULL); + } + } +#endif +} + +/** make the catalog consumer zone invalid for given reason */ +static void +vmake_catalog_consumer_invalid( + struct xfrd_catalog_consumer_zone *consumer_zone, + const char *format, va_list args) +{ + char message[MAXSYSLOGMSGLEN]; + if (!consumer_zone || consumer_zone->invalid) return; + vsnprintf(message, sizeof(message), format, args); + log_msg(LOG_ERR, "invalid catalog consumer zone '%s': %s", + consumer_zone->options->name, message); + consumer_zone->invalid = region_strdup(xfrd->region, message); +} + +void +make_catalog_consumer_invalid(struct xfrd_catalog_consumer_zone *consumer_zone, + const char *format, ...) +{ + va_list args; + if (!consumer_zone || consumer_zone->invalid) return; + va_start(args, format); + vmake_catalog_consumer_invalid(consumer_zone, format, args); + va_end(args); +} + +void +make_catalog_consumer_valid(struct xfrd_catalog_consumer_zone *consumer_zone) +{ + if (consumer_zone->invalid) { + region_recycle(xfrd->region, consumer_zone->invalid, + strlen(consumer_zone->invalid) + 1); + consumer_zone->invalid = NULL; + } +} + +static dname_type* +label_plus_dname(const char* label, const dname_type* dname) +{ + static struct { + dname_type dname; + uint8_t bytes[MAXDOMAINLEN + 128 /* max number of labels */]; + } ATTR_PACKED name; + size_t i, ll; + + if (!label || !dname || dname->label_count > 127) + return NULL; + ll = strlen(label); + if ((int)dname->name_size + ll + 1 > MAXDOMAINLEN) + return NULL; + + /* In reversed order and first copy with memmove, so we can nest. + * i.e. label_plus_dname(label1, label_plus_dname(label2, dname)) + */ + memmove(name.bytes + dname->label_count + + 1 /* label_count increases by one */ + + 1 /* label type/length byte for label */ + ll, + ((void*)dname) + sizeof(dname_type) + dname->label_count, + dname->name_size); + memcpy(name.bytes + dname->label_count + + 1 /* label_count increases by one */ + + 1 /* label type/length byte for label */, label, ll); + name.bytes[dname->label_count + 1] = ll; /* label type/length byte */ + name.bytes[dname->label_count] = 0; /* first label follows last + * label_offsets element */ + for (i = 0; i < dname->label_count; i++) + name.bytes[i] = ((uint8_t*)(void*)dname)[sizeof(dname_type)+i] + + 1 /* label type/length byte for label */ + ll; + name.dname.label_count = dname->label_count + 1 /* label_count incr. */; + name.dname.name_size = dname->name_size + ll + + 1 /* label length */; + return &name.dname; +} + +static void +catalog_del_consumer_member_zone( + struct xfrd_catalog_consumer_zone* consumer_zone, + struct catalog_member_zone* consumer_member_zone) +{ + const dname_type* dname = consumer_member_zone->options.node.key; + + /* create deletion task */ + task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, dname); + xfrd_set_reload_now(xfrd); + /* delete it in xfrd */ + if(zone_is_slave(&consumer_member_zone->options)) { + xfrd_del_slave_zone(xfrd, dname); + } + xfrd_del_notify(xfrd, dname); +#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES + /* delete it in xfrd's catalog consumers list */ + if(zone_is_catalog_consumer(&consumer_member_zone->options)) { + xfrd_deinit_catalog_consumer_zone(xfrd, dname); + } +#endif + if(consumer_member_zone->member_id) { + rbtree_delete(&consumer_zone->member_ids,consumer_member_zone); + consumer_member_zone->node = *RBTREE_NULL; + region_recycle( xfrd->nsd->options->region, + (void*)consumer_member_zone->member_id, + dname_total_size(consumer_member_zone->member_id)); + consumer_member_zone->member_id = NULL; + } + zone_options_delete(xfrd->nsd->options,&consumer_member_zone->options); +} + +void xfrd_check_catalog_consumer_zonefiles(const dname_type* name) +{ + struct xfrd_catalog_consumer_zone* consumer_zone; + +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + consumer_zone = xfrd_one_catalog_consumer_zone(); + if (!consumer_zone) + return; + if (name && dname_compare(name, consumer_zone->node.key) != 0) + return; + name = consumer_zone->node.key; + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Mark %s " + "for checking", consumer_zone->options->name)); + make_catalog_consumer_valid(consumer_zone); + namedb_read_zonefile(xfrd->nsd, namedb_find_or_create_zone( + xfrd->nsd->db, name, consumer_zone->options), NULL, NULL); +#else + if (!name) { + RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, + xfrd->catalog_consumer_zones) { + make_catalog_consumer_valid(consumer_zone); + namedb_read_zonefile(xfrd->nsd, + namedb_find_or_create_zone(xfrd->nsd->db, + consumer_zone->options->node.key, + consumer_zone->options), + NULL, NULL); + } + } else if ((consumer_zone = (struct xfrd_catalog_consumer_zone*) + rbtree_search(xfrd->catalog_consumer_zones, name))) { + make_catalog_consumer_valid(consumer_zone); + namedb_read_zonefile(xfrd->nsd, + namedb_find_or_create_zone( + xfrd->nsd->db, name, consumer_zone->options), + NULL, NULL); + } +#endif +} + +const char *invalid_catalog_consumer_zone(struct zone_options* zone) +{ + struct xfrd_catalog_consumer_zone* consumer_zone; + const char *msg; + + if (!zone || !zone_is_catalog_consumer(zone)) + msg = NULL; + + else if (!xfrd) + msg = "asked for catalog information outside of xfrd process"; + + else if (!xfrd->catalog_consumer_zones) + msg = "zone not found: " + "xfrd's catalog consumer zones index is empty"; + +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + else if (xfrd->catalog_consumer_zones->count > 1) + return "not processing: more than one catalog consumer zone " + "configured and only a single one allowed"; +#endif + else if (!(consumer_zone = (struct xfrd_catalog_consumer_zone*) + rbtree_search(xfrd->catalog_consumer_zones, zone->node.key))) + msg = "zone not found in xfrd's catalog consumer zones index"; + else + return consumer_zone->invalid; + + if (msg) + log_msg(LOG_ERR, "catalog consumer zone '%s': %s", + zone->name, msg); + + return msg; +} + +void xfrd_process_catalog_consumer_zones() +{ + struct xfrd_catalog_consumer_zone* consumer_zone; + +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + if((consumer_zone = xfrd_one_catalog_consumer_zone())) + xfrd_process_catalog_consumer_zone(consumer_zone); +#else + RBTREE_FOR(consumer_zone, struct xfrd_catalog_consumer_zone*, + xfrd->catalog_consumer_zones) { + xfrd_process_catalog_consumer_zone(consumer_zone); + } +#endif +} + +static inline struct catalog_member_zone* cursor_cmz(rbnode_type* node) +{ return node != RBTREE_NULL ? (struct catalog_member_zone*)node->key : NULL; } +static inline const dname_type* cursor_member_id(rbnode_type* node) +{ return cursor_cmz(node) ? cursor_cmz(node)->member_id : NULL; } + +#if !defined(NDEBUG) && 1 /* Only disable for seriously slow debugging */ +static void debug_log_consumer_members( + struct xfrd_catalog_consumer_zone* consumer_zone) +{ + rbnode_type* cursor; + size_t i; + + for ( cursor = rbtree_first(&consumer_zone->member_ids), i = 0 + ; cursor != RBTREE_NULL; i++, cursor = rbtree_next(cursor)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Catalog member %.2zu: %s = %s", + i, dname_to_string(cursor_member_id(cursor), NULL), + cursor_cmz(cursor)->options.name)); + } +} +#else +# define debug_log_consumer_members(x) /* nothing */ +#endif + +static void +xfrd_process_catalog_consumer_zone( + struct xfrd_catalog_consumer_zone* consumer_zone) +{ + zone_type* zone; + const dname_type* dname; + domain_type *match, *closest_encloser, *member_id, *group; + rrset_type *rrset; + size_t i; + uint8_t version_2_found; + /* Currect catalog member zone */ + rbnode_type* cursor; + struct pattern_options *default_pattern = NULL; + /* A transfer of a catalog zone can contain deletion and adding of + * the same member zone. In such cases it can occur that the member + * is tried to be added before it is deleted. For these exceptional + * cases, we will rewalk the zone after the first pass, to retry + * adding those zones. + * + * Initial pass is mode "try_to_add". + * If a zone cannot be added, mode is set to "retry_to_add" + * If after the first pass the mode is "retry_to_add", + * mode will be set to "just_add", and a second pass is done. + */ + enum { try_to_add, retry_to_add, just_add } mode; + + assert(consumer_zone); + if (!xfrd->nsd->db) { + xfrd->nsd->db = namedb_open(xfrd->nsd->options); + } + dname = (const dname_type*)consumer_zone->node.key; + if (dname->name_size > 247) { + make_catalog_consumer_invalid(consumer_zone, "name too long"); + return; + } + if (dname->label_count > 126) { + make_catalog_consumer_invalid(consumer_zone,"too many labels"); + return; + } + zone = namedb_find_zone(xfrd->nsd->db, dname); + if (!zone) { + zone = namedb_zone_create(xfrd->nsd->db, dname, + consumer_zone->options); + namedb_read_zonefile(xfrd->nsd, zone, NULL, NULL); + } + if (timespec_compare(&consumer_zone->mtime, &zone->mtime) == 0) { + /* Not processing unchanged catalog consumer zone */ + return; + } + consumer_zone->mtime = zone->mtime; + /* start processing */ + /* Lookup version. TXT and check that it is version 2 */ + if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("version", dname), + &match, &closest_encloser) + || !(rrset = domain_find_rrset(match, zone, TYPE_TXT))) { + make_catalog_consumer_invalid(consumer_zone, + "'version.%s TXT RRset not found", + consumer_zone->options->name); + return; + } + version_2_found = 0; + for (i = 0; i < rrset->rr_count; i++) { + if (rrset->rrs[i].rdata_count != 1) + continue; + if (rrset->rrs[i].rdatas[0].data[0] == 2 + && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0] == 1 + && ((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[1] == '2') { + version_2_found = 1; + break; + } + } + if (!version_2_found) { + make_catalog_consumer_invalid(consumer_zone, + "'version.%s' TXT RR with value \"2\" not found", + consumer_zone->options->name); + return; + } + /* Walk over all names under zones. */ + if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("zones", dname), + &match, &closest_encloser)) { + /* zones. does not exist, so the catalog has no + * members. This is just fine. But there may be members that need + * to be deleted. + */ + cursor = rbtree_first(&consumer_zone->member_ids); + mode = just_add; + goto delete_members; + } + mode = consumer_zone->member_ids.count ? try_to_add : just_add; +retry_adding: + cursor = rbtree_first(&consumer_zone->member_ids); + for ( member_id = domain_next(match) + ; member_id && domain_is_subdomain(member_id, match) + ; member_id = domain_next(member_id)) { + domain_type *member_domain; + char member_domain_str[5 * MAXDOMAINLEN]; + struct zone_options* zopt; + int valid_group_values; + struct pattern_options *pattern = NULL; + struct catalog_member_zone* to_add; + + if (domain_dname(member_id)->label_count > dname->label_count+2 + || !(rrset = domain_find_rrset(member_id, zone, TYPE_PTR))) + continue; + + /* RFC9432 Section 4.1. Member Zones: + * + * `` This PTR record MUST be the only record in the PTR RRset + * with the same name. The presence of more than one record + * in the RRset indicates a broken catalog zone that MUST + * NOT be processed (see Section 5.1). + */ + if (rrset->rr_count != 1) { + make_catalog_consumer_invalid(consumer_zone, + "only a single PTR RR expected on '%s'", + domain_to_string(member_id)); + return; + } + /* A PTR rr always has 1 rdata element which is a dname */ + if (rrset->rrs[0].rdata_count != 1) + continue; + member_domain = rrset->rrs[0].rdatas[0].domain; + domain_to_string_buf(member_domain, member_domain_str); + /* remove trailing dot */ + member_domain_str[strlen(member_domain_str) - 1] = 0; + + valid_group_values = 0; + /* Lookup group. TXT for matching patterns */ + if(!namedb_lookup(xfrd->nsd->db, label_plus_dname("group", + domain_dname(member_id)), + &group, &closest_encloser) + || !(rrset = domain_find_rrset(group, zone, TYPE_TXT))) { + ; /* pass */ + + } else for (i = 0; i < rrset->rr_count; i++) { + /* Max single TXT rdata field length + '\x00' == 256 */ + char group_value[256]; + + /* Looking for a single TXT rdata field */ + if (rrset->rrs[i].rdata_count != 1 + + /* rdata field should be at least 1 char */ + || rrset->rrs[i].rdatas[0].data[0] < 2 + + /* single rdata atom with single TXT rdata field */ + || (uint16_t)(((uint8_t*)(rrset->rrs[i].rdatas[0].data + 1))[0]) + != (uint16_t) (rrset->rrs[i].rdatas[0].data[0]-1)) + continue; + + memcpy( group_value + , (uint8_t*)(rrset->rrs[i].rdatas[0].data+1) + 1 + ,((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] + ); + group_value[ + ((uint8_t*)(rrset->rrs[i].rdatas[0].data+1))[0] + ] = 0; + if ((pattern = pattern_options_find( + xfrd->nsd->options, group_value))) + valid_group_values += 1; + } + if (valid_group_values > 1) { + log_msg(LOG_ERR, "member zone '%s': only a single " + "group property that matches a pattern is " + "allowed." + "The pattern from \"catalog-member-pattern\" " + "will be used instead.", + domain_to_string(member_id)); + valid_group_values = 0; + + } else if (valid_group_values == 1 && pattern + && pattern->catalog_producer_zone) { + log_msg(LOG_ERR, "member zone '%s': group property " + "'%s' matches a catalog producer member zone " + "pattern. In NSD, catalog member zones can be " + "either a member of a catalog consumer zone or" + " a catalog producer zone, but not both.", + domain_to_string(member_id), pattern->pname); + valid_group_values = 0; + } + if (valid_group_values == 1) { + /* pass: pattern is already set */ + assert(pattern); + + } else if (default_pattern) + pattern = default_pattern; /* pass */ + + else if (!(pattern = default_pattern = + catalog_member_pattern(consumer_zone))) { + make_catalog_consumer_invalid(consumer_zone, + "missing 'group.%s' TXT RR and no default " + "pattern from \"catalog-member-pattern\"", + domain_to_string(member_id)); + return; + } + if (cursor == RBTREE_NULL) + ; /* End of the current member zones list. + * From here onwards, zones will only be added. + */ + else { + int cmp = 0; +#ifndef NDEBUG + char member_id_str[5 * MAXDOMAINLEN]; + domain_to_string_buf(member_id, member_id_str); +#endif + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Comparing %s with %s", + member_id_str, + dname_to_string(cursor_member_id(cursor), + NULL))); + + while (cursor != RBTREE_NULL && + (cmp = dname_compare( + domain_dname(member_id), + cursor_member_id(cursor))) > 0) { + /* member_id is ahead of the current catalog + * member zone pointed to by cursor. + * The member zone must be deleted. + */ + struct catalog_member_zone* to_delete = + cursor_cmz(cursor); +#ifndef NDEBUG + const char *member_id_to_delete_str = + dname_to_string(to_delete->member_id, NULL); +#endif + cursor = rbtree_next(cursor); + + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "%s > %s: delete %s", + member_id_str, + member_id_to_delete_str, + member_id_to_delete_str)); + catalog_del_consumer_member_zone( + consumer_zone, to_delete); + if(cursor != RBTREE_NULL) + DEBUG(DEBUG_XFRD,1, (LOG_INFO, + "Comparing %s with %s", + member_id_str, + dname_to_string( + cursor_member_id(cursor), + NULL))); + } + if (cursor != RBTREE_NULL && cmp == 0) { + /* member_id is also in an current catalog + * member zone, and cursor is pointing + * to it. So, move along ... + */ + /* ... but first check if the pattern needs + * a change + */ + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "%s == %s: " + "Compare pattern %s with %s", + member_id_str, member_id_str, + cursor_cmz(cursor)->options.pattern->pname, + pattern->pname)); + + if (cursor_cmz(cursor)->options.pattern == + pattern) + ; /* pass: Pattern remains the same */ + else { + /* Changing patterns is basically + * deleting and adding the zone again + */ + zopt = &cursor_cmz(cursor)->options; + dname = (dname_type *)zopt->node.key; + task_new_del_zone( + xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, + dname); + xfrd_set_reload_now(xfrd); + if(zone_is_slave(zopt)) { + xfrd_del_slave_zone( xfrd + , dname); + } + xfrd_del_notify(xfrd, dname); +#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES + if(zone_is_catalog_consumer(zopt)) { + xfrd_deinit_catalog_consumer_zone( + xfrd, dname); + } +#endif + /* It is a catalog consumer member, + * so no need to check if it was a + * catalog producer member zone to + * delete and add + */ + zopt->pattern = pattern; + task_new_add_zone( + xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, zopt->name, + pattern->pname, + getzonestatid( xfrd->nsd->options + , zopt)); + zonestat_inc_ifneeded(); + xfrd_set_reload_now(xfrd); +#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES + if(zone_is_catalog_consumer(zopt)) { + xfrd_init_catalog_consumer_zone( + xfrd, zopt); + } +#endif + init_notify_send(xfrd->notify_zones, + xfrd->region, zopt); + if(zone_is_slave(zopt)) { + xfrd_init_slave_zone( + xfrd, zopt); + } + } + cursor = rbtree_next(cursor); + continue; + } + /* member_id is not in the current catalog member zone + * list, so it must be added + */ + assert(cursor == RBTREE_NULL || cmp < 0); + } + /* See if the zone already exists */ + zopt = zone_options_find(xfrd->nsd->options, + domain_dname(member_domain)); + if (zopt) { + /* Produce warning if zopt is from other catalog. + * Give debug message if zopt is not from this catalog. + */ + switch(mode) { + case try_to_add: + mode = retry_to_add; + break; + case just_add: + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Cannot add " + "catalog member zone %s (from %s): " + "zone already exists", + member_domain_str, + domain_to_string(member_id))); + break; + default: + break; + } + continue; + } + /* Add member zone if not already there */ + log_msg(LOG_INFO, "Adding '%s' PTR '%s'", + domain_to_string(member_id), + member_domain_str); + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Adding %s PTR %s", + domain_to_string(member_id), member_domain_str)); + to_add= catalog_member_zone_create(xfrd->nsd->options->region); + to_add->options.name = region_strdup( + xfrd->nsd->options->region, member_domain_str); + to_add->options.pattern = pattern; + if (!nsd_options_insert_zone(xfrd->nsd->options, + &to_add->options)) { + log_msg(LOG_ERR, "bad domain name '%s' pattern %s", + member_domain_str, + ( pattern->pname ? pattern->pname: "")); + zone_options_delete(xfrd->nsd->options, + &to_add->options); + continue; + } + to_add->member_id = dname_copy( xfrd->nsd->options->region + , domain_dname(member_id)); + /* Insert into the members_id list */ + to_add->node.key = to_add; + if(!rbtree_insert( &consumer_zone->member_ids, &to_add->node)){ + log_msg(LOG_ERR, "Error adding '%s' PTR '%s' to " + "consumer_zone->member_ids", + domain_to_string(member_id), + member_domain_str); + break; + } else + cursor = rbtree_next(&to_add->node); + /* make addzone task and schedule reload */ + task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask], + xfrd->last_task, member_domain_str, + pattern->pname, + getzonestatid(xfrd->nsd->options, &to_add->options)); + zonestat_inc_ifneeded(); + xfrd_set_reload_now(xfrd); +#ifdef MULTIPLE_CATALOG_CONSUMER_ZONES + /* add to xfrd - catalog consumer zones */ + if(zone_is_catalog_consumer(&to_add->options)) { + xfrd_init_catalog_consumer_zone(xfrd,&to_add->options); + } +#endif + /* add to xfrd - notify (for master and slaves) */ + init_notify_send(xfrd->notify_zones, xfrd->region, + &to_add->options); + /* add to xfrd - slave */ + if(zone_is_slave(&to_add->options)) { + xfrd_init_slave_zone(xfrd, &to_add->options); + } + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "Added catalog " + "member zone %s (from %s)", + member_domain_str, domain_to_string(member_id))); + } +delete_members: + while (cursor != RBTREE_NULL) { + /* Any current catalog member zones remaining, don't have an + * member_id in the catalog anymore, so should be deleted too. + */ + struct catalog_member_zone* to_delete = cursor_cmz(cursor); + + cursor = rbtree_next(cursor); + catalog_del_consumer_member_zone(consumer_zone, to_delete); + } + if(mode == retry_to_add) { + mode = just_add; + goto retry_adding; + } + debug_log_consumer_members(consumer_zone); + make_catalog_consumer_valid(consumer_zone); +} + + +/****************** ****************** + ****************** catalog producer zone processing ****************** + ****************** ******************/ + +static int member_id_compare(const void *left, const void *right) +{ + return dname_compare( ((struct catalog_member_zone*)left )->member_id + , ((struct catalog_member_zone*)right)->member_id); +} + +static struct xfrd_catalog_producer_zone* +xfrd_get_catalog_producer_zone(struct catalog_member_zone* cmz) +{ + struct zone_options *producer_zopt; + struct xfrd_catalog_producer_zone* producer_zone; + const dname_type* producer_name; + const char* producer_name_str; + + assert(xfrd); + if(!cmz || !cmz->options.pattern->catalog_producer_zone) + return NULL; + + /* TODO: Store as dname in pattern->catalog_producer_zone */ + producer_name = dname_parse(xfrd->nsd->options->region, + cmz->options.pattern->catalog_producer_zone); + producer_zopt = zone_options_find(xfrd->nsd->options, producer_name); + producer_name_str = dname_to_string(producer_name, NULL); + region_recycle( xfrd->nsd->options->region, (void *)producer_name + , dname_total_size(producer_name)); + if(!producer_zopt) { + log_msg(LOG_ERR, "catalog producer zone '%s' not found for " + "zone '%s'", producer_name_str, cmz->options.name); + return NULL; + } + if(!zone_is_catalog_producer(producer_zopt)) { + log_msg(LOG_ERR, "cannot add catalog producer member " + "zone '%s' to non producer zone '%s'", + cmz->options.name, producer_zopt->name); + return NULL; + } + producer_name = (dname_type*)producer_zopt->node.key; + producer_zone = (struct xfrd_catalog_producer_zone*) + rbtree_search(xfrd->catalog_producer_zones, producer_name); + if (!producer_zone) { + /* Create a new one */ + DEBUG(DEBUG_XFRD, 1, (LOG_INFO,"creating catalog producer zone" + " '%s'", producer_zopt->name)); + producer_zone = (struct xfrd_catalog_producer_zone*) + region_alloc(xfrd->region, sizeof(*producer_zone)); + memset(producer_zone , 0, sizeof(*producer_zone)); + producer_zone->node.key = producer_zopt->node.key; + producer_zone->options = producer_zopt; + producer_zone->member_ids.region = xfrd->region; + producer_zone->member_ids.root = RBTREE_NULL; + producer_zone->member_ids.count = 0; + producer_zone->member_ids.cmp = member_id_compare; + producer_zone->serial = 0; + producer_zone->to_delete = NULL; + producer_zone->to_add = NULL; + producer_zone->latest_pxfr = NULL; + producer_zone->axfr = 1; + rbtree_insert(xfrd->catalog_producer_zones, + (rbnode_type*)producer_zone); + } + return producer_zone; +} + +void +xfrd_add_catalog_producer_member(struct catalog_member_zone* cmz) +{ + struct xfrd_catalog_producer_zone* producer_zone; + const dname_type* producer_name; + struct xfrd_producer_member* to_add; + + assert(xfrd); + if (!(producer_zone = xfrd_get_catalog_producer_zone(cmz))) { + return; + } + producer_name = producer_zone->node.key; + while(!cmz->member_id) { + /* Make new member_id with this catalog producer */ + char id_label[sizeof(uint32_t)*2+1]; + uint32_t new_id = (uint32_t)random_generate(0x7fffffff); + + hex_ntop((void*)&new_id, sizeof(uint32_t), id_label, sizeof(id_label)); + id_label[sizeof(uint32_t)*2] = 0; + cmz->member_id = label_plus_dname(id_label, + label_plus_dname("zones", producer_name)); + DEBUG(DEBUG_XFRD, 1, (LOG_INFO, "does member_id %s exist?", + dname_to_string(cmz->member_id, NULL))); + if (!rbtree_search(&producer_zone->member_ids, cmz)) { + cmz->member_id = dname_copy(xfrd->nsd->options->region, + cmz->member_id); + break; + } + cmz->member_id = NULL; + } + cmz->node.key = cmz; + rbtree_insert(&producer_zone->member_ids, &cmz->node); + + /* Put data to be added to the producer zone to the to_add stack */ + to_add = (struct xfrd_producer_member*)region_alloc(xfrd->region, + sizeof(struct xfrd_producer_member)); + to_add->member_id = cmz->member_id; + to_add->member_zone_name = (dname_type*)cmz->options.node.key; + to_add->group_name = cmz->options.pattern->pname; + to_add->next = producer_zone->to_add; + producer_zone->to_add = to_add; +} + +int +xfrd_del_catalog_producer_member(struct xfrd_state* xfrd, + const dname_type* member_zone_name) +{ + struct xfrd_producer_member* to_delete; + struct catalog_member_zone* cmz; + struct xfrd_catalog_producer_zone* producer_zone; + + if(!(cmz = as_catalog_member_zone(zone_options_find(xfrd->nsd->options, + member_zone_name))) + || !(producer_zone = xfrd_get_catalog_producer_zone(cmz)) + || !rbtree_delete(&producer_zone->member_ids, cmz)) + return 0; + to_delete = (struct xfrd_producer_member*)region_alloc(xfrd->region, + sizeof(struct xfrd_producer_member)); + to_delete->member_id = cmz->member_id; cmz->member_id = NULL; + cmz->node = *RBTREE_NULL; + to_delete->member_zone_name = member_zone_name; + to_delete->group_name = cmz->options.pattern->pname; + to_delete->next = producer_zone->to_delete; + producer_zone->to_delete = to_delete; + return 1; +} + +static int +try_buffer_write_SOA(buffer_type* packet, const dname_type* owner, + uint32_t serial) +{ + size_t mark = buffer_position(packet); + + if(try_buffer_write(packet, dname_name(owner), owner->name_size) + && try_buffer_write_u16(packet, TYPE_SOA) + && try_buffer_write_u16(packet, CLASS_IN) + && try_buffer_write_u32(packet, 0) /* TTL*/ + && try_buffer_write_u16(packet, 9 + 9 + 5 * sizeof(uint32_t)) + && try_buffer_write(packet, "\007invalid\000", 9) /* primary */ + && try_buffer_write(packet, "\007invalid\000", 9) /* mailbox */ + && try_buffer_write_u32(packet, serial) /* serial */ + && try_buffer_write_u32(packet, 3600) /* refresh*/ + && try_buffer_write_u32(packet, 600) /* retry */ + && try_buffer_write_u32(packet, 2147483646) /* expire */ + && try_buffer_write_u32(packet, 0) /* minimum */) { + ANCOUNT_SET(packet, ANCOUNT(packet) + 1); + return 1; + } + buffer_set_position(packet, mark); + return 0; +} + +static int +try_buffer_write_RR(buffer_type* packet, const dname_type* owner, + uint16_t rr_type, uint16_t rdata_len, const void* rdata) +{ + size_t mark = buffer_position(packet); + + if(try_buffer_write(packet, dname_name(owner), owner->name_size) + && try_buffer_write_u16(packet, rr_type) + && try_buffer_write_u16(packet, CLASS_IN) + && try_buffer_write_u32(packet, 0) /* TTL*/ + && try_buffer_write_u16(packet, rdata_len) + && try_buffer_write(packet, rdata, rdata_len)) { + ANCOUNT_SET(packet, ANCOUNT(packet) + 1); + return 1; + } + buffer_set_position(packet, mark); + return 0; +} + +static inline int +try_buffer_write_PTR(buffer_type* packet, const dname_type* owner, + const dname_type* name) +{ + return try_buffer_write_RR(packet, owner, TYPE_PTR, + name->name_size, dname_name(name)); +} + +static int +try_buffer_write_TXT(buffer_type* packet, const dname_type* name, + const char *txt) +{ + size_t mark = buffer_position(packet); + size_t len = strlen(txt); + + if(len > 255) { + log_msg(LOG_ERR, "cannot make '%s 0 IN TXT \"%s\"': rdata " + "field too long", dname_to_string(name, NULL), txt); + return 1; + } + if(try_buffer_write(packet, dname_name(name), name->name_size) + && try_buffer_write_u16(packet, TYPE_TXT) + && try_buffer_write_u16(packet, CLASS_IN) + && try_buffer_write_u32(packet, 0) /* TTL*/ + && try_buffer_write_u16(packet, len + 1) + && try_buffer_write_u8(packet, len) + && try_buffer_write_string(packet, txt)) { + ANCOUNT_SET(packet, ANCOUNT(packet) + 1); + return 1; + } + buffer_set_position(packet, mark); + return 0; +} + +static void +xfr_writer_init(struct xfrd_xfr_writer* xw, + struct xfrd_catalog_producer_zone* producer_zone) +{ + xw->producer_zone = producer_zone; + buffer_create_from( &xw->packet, &xw->packet_space + , sizeof(xw->packet_space)); + buffer_write(&xw->packet, "\000\000\000\000\000\000" + "\000\000\000\000\000\000", 12); /* header */ + xw->seq_nr = 0; + xw->old_serial = xw->producer_zone->serial; + xw->new_serial = (uint32_t)xfrd_time(); + if(xw->new_serial <= xw->old_serial) + xw->new_serial = xw->old_serial + 1; + if(producer_zone->axfr) { + xw->old_serial = 0; + producer_zone->axfr = 0; + } + xw->xfrfilenumber = xfrd->xfrfilenumber++; +} + +static void +xfr_writer_write_packet(struct xfrd_xfr_writer* xw) +{ + const dname_type* producer_name = + (const dname_type*)xw->producer_zone->options->node.key; + + /* We want some content at least, so not just a header + * This can occur when final SOA was already written. + */ + if(buffer_position(&xw->packet) == 12) + return; + buffer_flip(&xw->packet); + diff_write_packet( dname_to_string(producer_name, NULL) + , xw->producer_zone->options->pattern->pname + , xw->old_serial, xw->new_serial, xw->seq_nr + , buffer_begin(&xw->packet), buffer_limit(&xw->packet) + , xfrd->nsd, xw->xfrfilenumber); + xw->seq_nr += 1; + buffer_clear(&xw->packet); + buffer_write(&xw->packet, "\000\000\000\000\000\000" + "\000\000\000\000\000\000", 12); /* header */ +} + + +static void +xfr_writer_commit(struct xfrd_xfr_writer* xw, const char *fmt, ...) +{ + va_list args; + char msg[1024]; + const dname_type* producer_name = + (const dname_type*)xw->producer_zone->options->node.key; + + va_start(args, fmt); + if (vsnprintf(msg, sizeof(msg), fmt, args) >= (int)sizeof(msg)) { + log_msg(LOG_WARNING, "truncated diff commit message: '%s'", + msg); + } + xfr_writer_write_packet(xw); /* Write remaining data */ + diff_write_commit( dname_to_string(producer_name, NULL) + , xw->old_serial, xw->new_serial + , xw->seq_nr /* Number of packets */ + , 1, msg, xfrd->nsd, xw->xfrfilenumber); + task_new_apply_xfr( xfrd->nsd->task[xfrd->nsd->mytask], xfrd->last_task + , producer_name + , xw->old_serial, xw->new_serial, xw->xfrfilenumber); + xfrd_set_reload_now(xfrd); +} + +static void +xfrd_process_catalog_producer_zone( + struct xfrd_catalog_producer_zone* producer_zone) +{ + struct xfrd_xfr_writer xw; + dname_type* producer_name; + struct xfrd_producer_xfr* pxfr; + + if(!producer_zone->to_add && !producer_zone->to_delete) + return; /* No changes */ + + producer_name = (dname_type*)producer_zone->node.key; + xfr_writer_init(&xw, producer_zone); + xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); + + if(xw.old_serial == 0) { + /* initial deployment */ + assert(producer_zone->to_add && !producer_zone->to_delete); + + xfr_writer_add_RR (&xw, producer_name + , TYPE_NS, 9, "\007invalid\000"); + xfr_writer_add_TXT(&xw, label_plus_dname("version" + , producer_name), "2"); + goto add_member_zones; + } + /* IXFR */ + xfr_writer_add_SOA(&xw, producer_name, xw.old_serial); + while(producer_zone->to_delete) { + struct xfrd_producer_member* to_delete = + producer_zone->to_delete; + + /* Pop to_delete from stack */ + producer_zone->to_delete = to_delete->next; + to_delete->next = NULL; + + /* Write PTR */ + xfr_writer_add_PTR(&xw, to_delete->member_id + , to_delete->member_zone_name); + + /* Write group. TXT */ + xfr_writer_add_TXT( &xw + , label_plus_dname("group" + , to_delete->member_id) + , to_delete->group_name); + + region_recycle( xfrd->nsd->options->region + , (void *)to_delete->member_id + , dname_total_size(to_delete->member_id)); + region_recycle( xfrd->region /* allocated in perform_delzone */ + , (void *)to_delete->member_zone_name + , dname_total_size(to_delete->member_zone_name)); + /* Don't recycle to_delete->group_name it's pattern->pname */ + region_recycle( xfrd->region, to_delete, sizeof(*to_delete)); + } + xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); + +add_member_zones: + while(producer_zone->to_add) { + struct xfrd_producer_member* to_add = producer_zone->to_add; + + /* Pop to_add from stack */ + producer_zone->to_add = to_add->next; + to_add->next = NULL; + + /* Write PTR */ + xfr_writer_add_PTR(&xw, to_add->member_id, + to_add->member_zone_name); + + /* Write group. TXT */ + xfr_writer_add_TXT( &xw + , label_plus_dname("group" + , to_add->member_id) + , to_add->group_name); + + /* Don't recycle any of the struct attributes as they come + * from zone_option's that are in use + */ + region_recycle(xfrd->region, to_add, sizeof(*to_add)); + } + xfr_writer_add_SOA(&xw, producer_name, xw.new_serial); + xfr_writer_commit(&xw, "xfr for catalog producer zone " + "'%s' with %d members from %u to %u", + dname_to_string(producer_name, NULL), + producer_zone->member_ids.count, + xw.old_serial, xw.new_serial); + producer_zone->serial = xw.new_serial; + + /* Hook up an xfrd_producer_xfr, to delete the xfr file when applied */ + pxfr = (struct xfrd_producer_xfr*)region_alloc(xfrd->region, + sizeof(struct xfrd_producer_xfr)); + pxfr->serial = xw.new_serial; + pxfr->xfrfilenumber = xw.xfrfilenumber; + if((pxfr->next = producer_zone->latest_pxfr)) + pxfr->next->prev_next_ptr = &pxfr->next; + pxfr->prev_next_ptr = &producer_zone->latest_pxfr; + producer_zone->latest_pxfr = pxfr; +} + +void xfrd_process_catalog_producer_zones() +{ + struct xfrd_catalog_producer_zone* producer_zone; + + RBTREE_FOR(producer_zone, struct xfrd_catalog_producer_zone*, + xfrd->catalog_producer_zones) { + xfrd_process_catalog_producer_zone(producer_zone); + } +} + diff --git a/xfrd-catalog-zones.h b/xfrd-catalog-zones.h new file mode 100644 index 000000000..17992b15c --- /dev/null +++ b/xfrd-catalog-zones.h @@ -0,0 +1,128 @@ +/* + * xfrd-catalog-zones.h -- catalog zone implementation for NSD + * + * Copyright (c) 2024, NLnet Labs. All rights reserved. + * + * See LICENSE for the license. + */ +#ifndef XFRD_CATALOG_ZONES_H +#define XFRD_CATALOG_ZONES_H +#include "xfrd.h" +struct xfrd_producer_member; +struct xfrd_producer_xfr; + +/** + * Catalog zones withing the xfrd context + */ +struct xfrd_catalog_consumer_zone { + /* For indexing in struc xfrd_state { rbtree_type* catalog_consumer_zones; } */ + rbnode_type node; + + /* Associated zone options with this catalog consumer zone */ + struct zone_options* options; + + /* Member zones indexed by member_id */ + rbtree_type member_ids; + + /* Last time processed, compare with zone->mtime to see if we need to process */ + struct timespec mtime; + + /* The reason for this zone to be invalid, or NULL if it is valid */ + char *invalid; +} ATTR_PACKED; + +/** + * Catalog producer zones withing the xfrd context + */ +struct xfrd_catalog_producer_zone { + /* For indexing in struc xfrd_state { rbtree_type* catalog_producer_zones; } */ + rbnode_type node; + + /* Associated zone options with this catalog consumer zone */ + struct zone_options* options; + + /* Member zones indexed by member_id */ + rbtree_type member_ids; + + /* SOA serial for this zone */ + uint32_t serial; + + /* Stack of members to delete from this catalog producer zone */ + struct xfrd_producer_member* to_delete; + + /* Stack of member zones to add to this catalog producer zone */ + struct xfrd_producer_member* to_add; + + /* To cleanup on disk xfr files */ + struct xfrd_producer_xfr* latest_pxfr; + + /* Set if next generated xfr for the producer zone should be axfr */ + unsigned axfr: 1; +} ATTR_PACKED; + +/** + * Data to add or remove from a catalog producer zone + */ +struct xfrd_producer_member { + const dname_type* member_id; + const dname_type* member_zone_name; + const char* group_name; + struct xfrd_producer_member* next; +} ATTR_PACKED; + +/** + * To track applied generated transfers from catalog producer zones + */ +struct xfrd_producer_xfr { + uint32_t serial; + uint64_t xfrfilenumber; + struct xfrd_producer_xfr** prev_next_ptr; + struct xfrd_producer_xfr* next; +} ATTR_PACKED; + +/* Initialize as a catalog consumer zone */ +void xfrd_init_catalog_consumer_zone(xfrd_state_type* xfrd, + struct zone_options* zone); + +/* To be called if and a zone is no longer a catalog zone (changed pattern) */ +void xfrd_deinit_catalog_consumer_zone(xfrd_state_type* xfrd, + const dname_type* dname); + +/* make the catalog consumer zone invalid for given reason */ +void make_catalog_consumer_invalid( + struct xfrd_catalog_consumer_zone *consumer_zone, + const char *format, ...) ATTR_FORMAT(printf, 2, 3); + +/* Return the reason a zone is invalid, or NULL on a valid catalog */ +const char *invalid_catalog_consumer_zone(struct zone_options* zone); + +/* make the catalog consumer zone valid again */ +void make_catalog_consumer_valid( + struct xfrd_catalog_consumer_zone *consumer_zone); + +/* Check the catalog consumer zone files (or file if zone is given) */ +void xfrd_check_catalog_consumer_zonefiles(const dname_type* name); + +/* process the catalog consumer zones, load if needed */ +void xfrd_process_catalog_consumer_zones(); + + +/* Add (or change) PTR , and + * group. TXT pname> to the associated producer zone by + * constructed xfr. make cmz->member_id if needed. */ +void xfrd_add_catalog_producer_member(struct catalog_member_zone* cmz); + +/* Delete PTR , and + * group. TXT pname> from the associated producer zone by + * constructed xfr. Return 1 if zone is deleted. In this case, member_zone_name + * is taken over by xfrd and cannot be recycled by the caller. member_zone_name + * must have been allocated int the xfrd->nsd->options->region + */ +int xfrd_del_catalog_producer_member(xfrd_state_type* xfrd, + const dname_type* dname); + +/* process the catalog producer zones */ +void xfrd_process_catalog_producer_zones(); + +#endif + diff --git a/xfrd.c b/xfrd.c index eef3554e4..227ab22a7 100644 --- a/xfrd.c +++ b/xfrd.c @@ -20,6 +20,7 @@ #include "xfrd-tcp.h" #include "xfrd-disk.h" #include "xfrd-notify.h" +#include "xfrd-catalog-zones.h" #include "options.h" #include "util.h" #include "netio.h" @@ -315,6 +316,8 @@ xfrd_main(void) xfrd->shutdown = 0; while(!xfrd->shutdown) { + xfrd_process_catalog_producer_zones(); + xfrd_process_catalog_consumer_zones(); /* process activated zones before blocking in select again */ xfrd_process_activated(); /* dispatch may block for a longer period, so current is gone */ @@ -419,6 +422,10 @@ xfrd_shutdown() /* process-exit cleans up memory used by xfrd process */ DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown complete")); #ifdef MEMCLEAN /* OS collects memory pages */ + if(xfrd->nsd->db) { + namedb_close(nsd->db); + } + /* TODO: cleanup xfrd->catalog_consumer_zones and xfrd->catalog_producer_zones */ if(xfrd->zones) { xfrd_zone_type* z; RBTREE_FOR(z, xfrd_zone_type*, xfrd->zones) { @@ -542,12 +549,25 @@ xfrd_init_zones() (int (*)(const void *, const void *)) dname_compare); xfrd->notify_zones = rbtree_create(xfrd->region, (int (*)(const void *, const void *)) dname_compare); + xfrd->catalog_consumer_zones = rbtree_create(xfrd->region, + (int (*)(const void *, const void *)) dname_compare); + xfrd->catalog_producer_zones = rbtree_create(xfrd->region, + (int (*)(const void *, const void *)) dname_compare); RBTREE_FOR(zone_opt, struct zone_options*, xfrd->nsd->options->zone_options) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: adding %s zone", zone_opt->name)); + if(zone_is_catalog_consumer(zone_opt)) { + DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s " + "is a catalog consumer zone", zone_opt->name)); + xfrd_init_catalog_consumer_zone(xfrd, zone_opt); + } + if(zone_is_catalog_producer_member(zone_opt)) { + xfrd_add_catalog_producer_member( + as_catalog_member_zone(zone_opt)); + } init_notify_send(xfrd->notify_zones, xfrd->region, zone_opt); if(!zone_is_slave(zone_opt)) { DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: zone %s, " @@ -558,7 +578,77 @@ xfrd_init_zones() xfrd_init_slave_zone(xfrd, zone_opt); } DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd: started server %d " - "secondary zones", (int)xfrd->zones->count)); + "secondary zones, %d catalog zones", (int)xfrd->zones->count, + (int)xfrd->catalog_consumer_zones->count)); +} + +static void +apply_xfrs_to_consumer_zone(struct xfrd_catalog_consumer_zone* consumer_zone, + zone_type* dbzone, xfrd_xfr_type* xfr) +{ + FILE* df; + + if(xfr->msg_is_ixfr) { + uint32_t soa_serial; + xfrd_xfr_type* prev; + + if(dbzone->soa_rrset == NULL || dbzone->soa_rrset->rrs == NULL + || dbzone->soa_rrset->rrs[0].rdata_count <= 2 + || rdata_atom_size(dbzone->soa_rrset->rrs[0].rdatas[2]) + != sizeof(uint32_t)) { + + make_catalog_consumer_invalid(consumer_zone, + "could not apply ixfr on catalog consumer zone " + "\'%s\': invalid SOA resource record", + consumer_zone->options->name); + return; + } + soa_serial = read_uint32(rdata_atom_data( + dbzone->soa_rrset->rrs[0].rdatas[2])); + if(soa_serial == xfr->msg_old_serial) + goto apply_xfr; + for(prev = xfr->prev; prev; prev = prev->prev) { + if(!prev->sent) + continue; + if(xfr->msg_old_serial != prev->msg_new_serial) + continue; + apply_xfrs_to_consumer_zone(consumer_zone, dbzone, prev); + break; + } + if(!prev || xfr->msg_old_serial != read_uint32(rdata_atom_data( + dbzone->soa_rrset->rrs[0].rdatas[2]))){ + make_catalog_consumer_invalid(consumer_zone, + "could not find and/or apply xfrs for catalog " + "consumer zone \'%s\': to update to serial %u", + consumer_zone->options->name, + xfr->msg_new_serial); + return; + } + } +apply_xfr: + DEBUG(DEBUG_IPC,1, (LOG_INFO, "apply %sXFR %u -> %u to consumer zone " + "\'%s\'", (xfr->msg_is_ixfr ? "I" : "A"), xfr->msg_old_serial, + xfr->msg_new_serial, consumer_zone->options->name)); + + if(!(df = xfrd_open_xfrfile(xfrd->nsd, xfr->xfrfilenumber, "r"))) { + make_catalog_consumer_invalid(consumer_zone, + "could not open transfer file %lld: %s", + (long long)xfr->xfrfilenumber, strerror(errno)); + + } else if(0 >= apply_ixfr_for_zone(xfrd->nsd, dbzone, df, + xfrd->nsd->options, NULL, NULL, xfr->xfrfilenumber)) { + make_catalog_consumer_invalid(consumer_zone, + "error processing transfer file %lld", + (long long)xfr->xfrfilenumber); + fclose(df); + } else { + /* Make valid for reprocessing */ + make_catalog_consumer_valid(consumer_zone); + fclose(df); + DEBUG(DEBUG_IPC,1, (LOG_INFO, "%sXFR %u -> %u to consumer zone \'%s\' " + "applied", (xfr->msg_is_ixfr ? "I" : "A"), xfr->msg_old_serial, + xfr->msg_new_serial, consumer_zone->options->name)); + } } static void @@ -567,6 +657,9 @@ xfrd_process_soa_info_task(struct task_list_d* task) xfrd_soa_type soa; xfrd_soa_type* soa_ptr = &soa; xfrd_zone_type* zone; + struct xfrd_catalog_producer_zone* producer_zone; + struct xfrd_catalog_consumer_zone* consumer_zone = NULL; + zone_type* dbzone = NULL; xfrd_xfr_type* xfr; xfrd_xfr_type* prev_xfr; enum soainfo_hint hint; @@ -627,14 +720,71 @@ xfrd_process_soa_info_task(struct task_list_d* task) #endif } - if(!zone) { + if(zone) + ; /* pass */ + + else if((producer_zone = (struct xfrd_catalog_producer_zone*) + rbtree_search(xfrd->catalog_producer_zones, task->zname))) { + struct xfrd_producer_xfr* pxfr, *next_pxfr; + + DEBUG(DEBUG_IPC,1, (LOG_INFO, "Zone %s is catalog producer", + dname_to_string(task->zname,0))); + + if(hint != soainfo_ok) + producer_zone->axfr = 1; + + for(pxfr = producer_zone->latest_pxfr; pxfr; pxfr = next_pxfr) { + next_pxfr = pxfr->next; + + DEBUG(DEBUG_IPC,1, (LOG_INFO, "pxfr for zone %s for serial %u", + dname_to_string(task->zname,0), pxfr->serial)); + + if(hint != soainfo_ok) + ; /* pass */ + else if(!soa_ptr || soa_ptr->serial != htonl(pxfr->serial)) + continue; + + else if(xfrd->reload_failed) { + DEBUG(DEBUG_IPC, 1, + (LOG_INFO, "xfrd: zone %s mark update " + "to serial %u verified", + producer_zone->options->name, + pxfr->serial)); + diff_update_commit( + producer_zone->options->name, + DIFF_VERIFIED, xfrd->nsd, + pxfr->xfrfilenumber); + return; + } + DEBUG(DEBUG_IPC, 1, + (LOG_INFO, "xfrd: zone %s delete update to " + "serial %u", producer_zone->options->name, + pxfr->serial)); + xfrd_unlink_xfrfile(xfrd->nsd, pxfr->xfrfilenumber); + if((*pxfr->prev_next_ptr = pxfr->next)) + pxfr->next->prev_next_ptr = pxfr->prev_next_ptr; + region_recycle(xfrd->region, pxfr, sizeof(*pxfr)); + notify_handle_master_zone_soainfo(xfrd->notify_zones, + task->zname, soa_ptr); + } + return; + } else { DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: zone %s primary zone updated", dname_to_string(task->zname,0))); notify_handle_master_zone_soainfo(xfrd->notify_zones, task->zname, soa_ptr); return; } - + if(xfrd->nsd->db + && xfrd->catalog_consumer_zones +#ifndef MULTIPLE_CATALOG_CONSUMER_ZONES + && xfrd->catalog_consumer_zones->count == 1 +#endif + && (consumer_zone = (struct xfrd_catalog_consumer_zone*)rbtree_search( + xfrd->catalog_consumer_zones, task->zname))) { + dbzone = namedb_find_or_create_zone( xfrd->nsd->db, task->zname + , consumer_zone->options); + } /* soainfo_gone and soainfo_bad are straightforward, delete all updates that were transfered, i.e. acquired != 0. soainfo_ok is more complicated as it is possible that there are subsequent corrupt or @@ -670,6 +820,9 @@ xfrd_process_soa_info_task(struct task_list_d* task) xfrd->nsd, xfr->xfrfilenumber); return; } + if(consumer_zone && dbzone) + apply_xfrs_to_consumer_zone( + consumer_zone, dbzone, xfr); } DEBUG(DEBUG_IPC, 1, (LOG_INFO, "xfrd: zone %s delete update to serial %u", diff --git a/xfrd.h b/xfrd.h index f2b821540..72f3a0565 100644 --- a/xfrd.h +++ b/xfrd.h @@ -119,6 +119,12 @@ struct xfrd_state { int notify_udp_num; /* first and last notify_zone* entries waiting for a UDP socket */ struct notify_zone *notify_waiting_first, *notify_waiting_last; + + /* tree of catalog consumer zones. Processing is disabled if > 1. */ + rbtree_type *catalog_consumer_zones; + + /* tree of updated catalog producer zones for which the content to serve */ + rbtree_type *catalog_producer_zones; }; /*