diff options
Diffstat (limited to 'lib/dns/master.c')
-rw-r--r-- | lib/dns/master.c | 1707 |
1 files changed, 1358 insertions, 349 deletions
diff --git a/lib/dns/master.c b/lib/dns/master.c index 23212d29..13c27ad6 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -1,31 +1,37 @@ /* * Copyright (C) 1999, 2000 Internet Software Consortium. - * + * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.c,v 1.54.2.6 2000/09/12 21:18:12 gson Exp $ */ +/* $Id: master.c,v 1.85 2000/12/04 04:17:00 marka Exp $ */ #include <config.h> +#include <isc/event.h> #include <isc/lex.h> +#include <isc/magic.h> #include <isc/mem.h> +#include <isc/print.h> #include <isc/stdtime.h> #include <isc/string.h> +#include <isc/task.h> #include <isc/util.h> #include <dns/callbacks.h> +#include <dns/events.h> +#include <dns/fixedname.h> #include <dns/master.h> #include <dns/name.h> #include <dns/rdata.h> @@ -43,7 +49,7 @@ * * RDLSZ reflects the number of different types with the same name expected. * RDSZ reflects the number of rdata expected at a give name that can fit into - * 64k. + * 64k. */ #define RDLSZ 32 @@ -55,7 +61,7 @@ /* * Target buffer size and minimum target size. * MINTSIZ must be big enough to hold the largest rdata record. - * + * * TSIZ >= MINTSIZ */ #define TSIZ (128*1024) @@ -63,33 +69,85 @@ * max message size - header - root - type - class - ttl - rdlen */ #define MINTSIZ (65535 - 12 - 1 - 2 - 2 - 4 - 2) -/* - * Size for tokens in the presentation format, +/* + * Size for tokens in the presentation format, * The largest tokens are the base64 blocks in KEY and CERT records, - * Largest key allowed is about 1372 bytes but + * Largest key allowed is about 1372 bytes but * there is no fixed upper bound on CERT records. * 2K is too small for some X.509s, 8K is overkill. */ #define TOKENSIZ (8*1024) +#define DNS_MASTER_BUFSZ 2048 + typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t; /* * Master file loading state that persists across $INCLUDEs. */ -typedef struct { - isc_boolean_t ttl_known; - isc_boolean_t default_ttl_known; - isc_uint32_t ttl; - isc_uint32_t default_ttl; - isc_boolean_t warn_1035; -} loadctx_t; + +struct dns_loadctx { + isc_uint32_t magic; + isc_mem_t *mctx; + isc_lex_t *lex; + dns_loadctx_t *parent; + dns_rdatacallbacks_t *callbacks; + isc_task_t *task; + dns_loaddonefunc_t done; + void *done_arg; + isc_boolean_t ttl_known; + isc_boolean_t default_ttl_known; + isc_boolean_t warn_1035; + isc_boolean_t age_ttl; + isc_boolean_t seen_include; + isc_uint32_t ttl; + isc_uint32_t default_ttl; + dns_rdataclass_t zclass; + dns_fixedname_t fixed_top; + dns_fixedname_t fixed[NBUFS]; /* working buffers */ + unsigned int in_use[NBUFS]; /* covert to bitmap? */ + dns_name_t *top; /* top of zone */ + dns_name_t *origin; + dns_name_t *current; + dns_name_t *glue; + /* Which fixed buffers we are using? */ + int glue_in_use; + int current_in_use; + int origin_in_use; + unsigned int loop_cnt; /* records per quantum, + * 0 => all. */ + isc_boolean_t canceled; + /* Rate limit goo. */ + isc_boolean_t rate_limited; + ISC_LINK(dns_loadctx_t) link; + char *master_file; + dns_loadmgr_t *loadmgr; + isc_event_t event; + + isc_mutex_t lock; + /* locked by lock */ + isc_uint32_t references; +}; + +#define DNS_LCTX_MAGIC ISC_MAGIC('L','c','t','x') +#define DNS_LCTX_VALID(ctx) ISC_MAGIC_VALID(ctx, DNS_LCTX_MAGIC) + +struct dns_loadmgr { + isc_uint32_t magic; + isc_mem_t *mctx; + isc_uint32_t erefs; + isc_uint32_t irefs; + isc_mutex_t lock; + isc_uint32_t active; + isc_uint32_t limit; + ISC_LIST(dns_loadctx_t) list; +}; + +#define DNS_LMGR_MAGIC ISC_MAGIC('L','m','g','r') +#define DNS_LMGR_VALID(ctx) ISC_MAGIC_VALID(ctx, DNS_LMGR_MAGIC) static isc_result_t -loadfile(const char *master_file, dns_name_t *top, dns_name_t *origin, - dns_rdataclass_t zclass, isc_boolean_t age_ttl, - dns_rdatacallbacks_t *callbacks, loadctx_t *ctx, - isc_mem_t *mctx); +pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t **ctxp); static isc_result_t commit(dns_rdatacallbacks_t *, isc_lex_t *, rdatalist_head_t *, dns_name_t *, @@ -106,8 +164,29 @@ static dns_rdata_t * grow_rdata(int, dns_rdata_t *, int, rdatalist_head_t *, rdatalist_head_t *, isc_mem_t *); -static isc_boolean_t -on_list(dns_rdatalist_t *this, dns_rdata_t *rdata); +static void +load_quantum(isc_task_t *task, isc_event_t *event); + +static isc_result_t +task_send(dns_loadctx_t *ctx); + +static void +loadctx_destroy(dns_loadctx_t *ctx); + +static void +loadmgr_start(isc_task_t *task, isc_event_t *event); + +static void +loadmgr_cancel(dns_loadmgr_t *mgr); + +static void +loadmgr_iattach(dns_loadmgr_t *source, dns_loadmgr_t **target); + +static void +loadmgr_idetach(dns_loadmgr_t **mgrp); + +static void +loadmgr_destroy(dns_loadmgr_t *mgr); #define GETTOKEN(lexer, options, token, eol) \ do { \ @@ -116,23 +195,44 @@ on_list(dns_rdatalist_t *this, dns_rdata_t *rdata); case ISC_R_SUCCESS: \ break; \ case ISC_R_UNEXPECTED: \ - goto cleanup; \ + goto insist_and_cleanup; \ default: \ - goto error_cleanup; \ + goto log_and_cleanup; \ + } \ + if ((token)->type == isc_tokentype_special) { \ + result = DNS_R_SYNTAX; \ + goto log_and_cleanup; \ } \ - if ((token)->type == isc_tokentype_special) \ - goto error_cleanup; \ + } while (0) + +#define COMMITALL \ + do { \ + result = commit(callbacks, ctx->lex, ¤t_list, \ + ctx->current, ctx->top); \ + if (result != ISC_R_SUCCESS) \ + goto log_and_cleanup; \ + result = commit(callbacks, ctx->lex, &glue_list, \ + ctx->glue, ctx->top); \ + if (result != ISC_R_SUCCESS) \ + goto log_and_cleanup; \ + rdcount = 0; \ + rdlcount = 0; \ + isc_buffer_init(&target, target_mem, target_size); \ + rdcount_save = rdcount; \ + rdlcount_save = rdlcount; \ } while (0) #define WARNUNEXPECTEDEOF(lexer) \ do { \ if (isc_lex_isfile(lexer)) \ (*callbacks->warn)(callbacks, \ - "%s:%lu: file does not end with newline", \ + "%s:%lu: file does not end with newline", \ isc_lex_getsourcename(lexer), \ isc_lex_getsourceline(lexer)); \ } while (0) +#define CTX_COPYVAR(ctx, new, var) (new)->var = (ctx)->var + static inline isc_result_t gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token, isc_boolean_t eol, dns_rdatacallbacks_t *callbacks) @@ -169,29 +269,427 @@ gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token, return (ISC_R_SUCCESS); } + +void +dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target) { + + REQUIRE(target != NULL && *target == NULL); + REQUIRE(DNS_LCTX_VALID(source)); + + LOCK(&source->lock); + INSIST(source->references > 0); + source->references++; + INSIST(source->references != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +void +dns_loadctx_detach(dns_loadctx_t **ctxp) { + dns_loadctx_t *ctx; + isc_boolean_t need_destroy = ISC_FALSE; + + REQUIRE(ctxp != NULL); + ctx = *ctxp; + REQUIRE(DNS_LCTX_VALID(ctx)); + + LOCK(&ctx->lock); + INSIST(ctx->references > 0); + ctx->references--; + if (ctx->references == 0) + need_destroy = ISC_TRUE; + UNLOCK(&ctx->lock); + + if (need_destroy) + loadctx_destroy(ctx); + *ctxp = NULL; +} + static void -loadctx_init(loadctx_t *ctx) { +loadctx_destroy(dns_loadctx_t *ctx) { + isc_mem_t *mctx; + + REQUIRE(DNS_LCTX_VALID(ctx)); + + ctx->magic = 0; + if (ctx->parent != NULL) + dns_loadctx_detach(&ctx->parent); + + if (ctx->lex != NULL) { + isc_lex_close(ctx->lex); + isc_lex_destroy(&ctx->lex); + } + if (ctx->task != NULL) + isc_task_detach(&ctx->task); + if (ctx->master_file != NULL) { + isc_mem_free(ctx->mctx, ctx->master_file); + ctx->master_file = NULL; + } + if (ctx->loadmgr != NULL) + loadmgr_idetach(&ctx->loadmgr); + DESTROYLOCK(&ctx->lock); + mctx = NULL; + isc_mem_attach(ctx->mctx, &mctx); + isc_mem_detach(&ctx->mctx); + isc_mem_put(mctx, ctx, sizeof(*ctx)); + isc_mem_detach(&mctx); +} + +static isc_result_t +loadctx_create(isc_mem_t *mctx, isc_boolean_t age_ttl, dns_name_t *top, + dns_rdataclass_t zclass, dns_name_t *origin, + dns_rdatacallbacks_t *callbacks, isc_task_t *task, + dns_loaddonefunc_t done, void *done_arg, + dns_loadctx_t **ctxp) +{ + dns_loadctx_t *ctx; + isc_result_t result; + isc_region_t r; + int i; + isc_lexspecials_t specials; + + REQUIRE(ctxp != NULL && *ctxp == NULL); + REQUIRE(callbacks != NULL); + REQUIRE(callbacks->add != NULL); + REQUIRE(callbacks->error != NULL); + REQUIRE(callbacks->warn != NULL); + REQUIRE(mctx != NULL); + REQUIRE(dns_name_isabsolute(top)); + REQUIRE(dns_name_isabsolute(origin)); + REQUIRE((task == NULL && done == NULL) || + (task != NULL && done != NULL)); + + ctx = isc_mem_get(mctx, sizeof(*ctx)); + if (ctx == NULL) + return (ISC_R_NOMEMORY); + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, ctx, sizeof *ctx); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_mutex_init() failed: %s", + isc_result_totext(result)); + return (ISC_R_UNEXPECTED); + } + + ctx->lex = NULL; + result = isc_lex_create(mctx, TOKENSIZ, &ctx->lex); + if (result != ISC_R_SUCCESS) + goto cleanup_ctx; + memset(specials, 0, sizeof specials); + specials['('] = 1; + specials[')'] = 1; + specials['"'] = 1; + isc_lex_setspecials(ctx->lex, specials); + isc_lex_setcomments(ctx->lex, ISC_LEXCOMMENT_DNSMASTERFILE); + ctx->ttl_known = ISC_FALSE; ctx->ttl = 0; ctx->default_ttl_known = ISC_FALSE; ctx->default_ttl = 0; ctx->warn_1035 = ISC_TRUE; /* XXX Argument? */ + ctx->age_ttl = age_ttl; + ctx->seen_include = ISC_FALSE; + ctx->zclass = zclass; + + dns_fixedname_init(&ctx->fixed_top); + ctx->top = dns_fixedname_name(&ctx->fixed_top); + dns_name_toregion(top, &r); + dns_name_fromregion(ctx->top, &r); + + for (i = 0; i < NBUFS; i++) { + dns_fixedname_init(&ctx->fixed[i]); + ctx->in_use[i] = ISC_FALSE; + } + + ctx->origin_in_use = 0; + ctx->origin = dns_fixedname_name(&ctx->fixed[ctx->origin_in_use]); + ctx->in_use[ctx->origin_in_use] = ISC_TRUE; + dns_name_toregion(origin, &r); + dns_name_fromregion(ctx->origin, &r); + + ctx->glue = NULL; + ctx->current = NULL; + ctx->glue_in_use = -1; + ctx->current_in_use = -1; + ctx->loop_cnt = (done != NULL) ? 100 : 0; + ctx->callbacks = callbacks; + ctx->parent = NULL; + ctx->task = NULL; + if (task != NULL) + isc_task_attach(task, &ctx->task); + ctx->done = done; + ctx->done_arg = done_arg; + + ctx->rate_limited = ISC_FALSE; + ctx->master_file = NULL; + ctx->loadmgr = NULL; + ISC_LINK_INIT(ctx, link); + ISC_EVENT_INIT(&ctx->event, sizeof(ctx->event), 0, NULL, + DNS_EVENT_MASTERNEXTZONE, loadmgr_start, + ctx, ctx, NULL, NULL); + + ctx->canceled = ISC_FALSE; + ctx->mctx = NULL; + isc_mem_attach(mctx, &ctx->mctx); + ctx->references = 1; /* Implicit attach. */ + ctx->magic = DNS_LCTX_MAGIC; + *ctxp = ctx; + return (ISC_R_SUCCESS); + + cleanup_ctx: + isc_mem_put(mctx, ctx, sizeof(*ctx)); + return (result); } - + static isc_result_t -load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, - dns_rdataclass_t zclass, isc_boolean_t age_ttl, - dns_rdatacallbacks_t *callbacks, loadctx_t *ctx, isc_mem_t *mctx) -{ +genname(char *name, int it, char *buffer, size_t length) { + char fmt[sizeof("%04000000000d")]; + char numbuf[128]; + char *cp; + char mode; + int delta = 0; + isc_textregion_t r; + unsigned int n; + unsigned int width; + + r.base = buffer; + r.length = length; + + while (*name != '\0') { + if (*name == '$') { + name++; + if (*name == '$') { + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = *name++; + isc_textregion_consume(&r, 1); + continue; + } + strcpy(fmt, "%d"); + /* Get format specifier. */ + if (*name == '{' ) { + n = sscanf(name, "{%d,%u,%1[doxX]}", + &delta, &width, &mode); + switch (n) { + case 1: + break; + case 2: + n = snprintf(fmt, sizeof(fmt), + "%%0%ud", width); + break; + case 3: + n = snprintf(fmt, sizeof(fmt), + "%%0%u%c", width, mode); + break; + default: + return (DNS_R_SYNTAX); + } + if (n >= sizeof(fmt)) + return (ISC_R_NOSPACE); + /* Skip past closing brace. */ + while (*name != '\0' && *name++ != '}') + continue; + } + n = snprintf(numbuf, sizeof(numbuf), fmt, it + delta); + if (n >= sizeof(numbuf)) + return (ISC_R_NOSPACE); + cp = numbuf; + while (*cp != '\0') { + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = *cp++; + isc_textregion_consume(&r, 1); + } + } else if (*name == '\\') { + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = *name++; + isc_textregion_consume(&r, 1); + if (*name == '\0') + continue; + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = *name++; + isc_textregion_consume(&r, 1); + } else { + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = *name++; + isc_textregion_consume(&r, 1); + } + } + if (r.length == 0) + return (ISC_R_NOSPACE); + r.base[0] = '\0'; + return (ISC_R_SUCCESS); +} + +static isc_result_t +generate(dns_loadctx_t *ctx, char *range, char *lhs, char *gtype, char *rhs) { + char *target_mem = NULL; + char *lhsbuf = NULL; + char *rhsbuf = NULL; + dns_fixedname_t ownerfixed; + dns_name_t *owner; + dns_rdata_t rdata = DNS_RDATA_INIT; + dns_rdatacallbacks_t *callbacks; + dns_rdatalist_t rdatalist; + dns_rdatatype_t type; + rdatalist_head_t head; + int n; + int target_size = MINTSIZ; /* only one rdata at a time */ + isc_buffer_t buffer; + isc_buffer_t target; + isc_result_t result; + isc_textregion_t r; + unsigned int start, stop, step, i; + + callbacks = ctx->callbacks; + dns_fixedname_init(&ownerfixed); + owner = dns_fixedname_name(&ownerfixed); + ISC_LIST_INIT(head); + + target_mem = isc_mem_get(ctx->mctx, target_size); + rhsbuf = isc_mem_get(ctx->mctx, DNS_MASTER_BUFSZ); + lhsbuf = isc_mem_get(ctx->mctx, DNS_MASTER_BUFSZ); + if (target_mem == NULL || rhsbuf == NULL || lhsbuf == NULL) { + result = ISC_R_NOMEMORY; + goto error_cleanup; + } + isc_buffer_init(&target, target_mem, target_size); + + n = sscanf(range, "%u-%u/%u", &start, &stop, &step); + if (n < 2 || stop < stop) { + (*callbacks->warn)(callbacks, + "%s: %s:%lu: invalid range '%s'", + "$GENERATE", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + range); + result = DNS_R_SYNTAX; + goto insist_cleanup; + } + if (n == 2) + step = 1; + + /* + * Get type. + */ + r.base = gtype; + r.length = strlen(gtype); + result = dns_rdatatype_fromtext(&type, &r); + if (result != ISC_R_SUCCESS) { + (*callbacks->warn)(callbacks, + "%s: %s:%lu: unknown RR type '%s'", + "$GENERATE", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + gtype); + goto insist_cleanup; + } + + switch (type) { + case dns_rdatatype_ns: + case dns_rdatatype_ptr: + case dns_rdatatype_cname: + break; + + case dns_rdatatype_a: + case dns_rdatatype_aaaa: + if (ctx->zclass == dns_rdataclass_in || + ctx->zclass == dns_rdataclass_hs) + break; + /* FALLTHROUGH */ + default: + (*callbacks->warn)(callbacks, + "%s: %s:%lu: unsupported type '%s'", + "$GENERATE", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + gtype); + result = ISC_R_NOTIMPLEMENTED; + goto error_cleanup; + } + + for (i = start; i < stop; i += step) { + result = genname(lhs, i, lhsbuf, DNS_MASTER_BUFSZ); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + result = genname(rhs, i, rhsbuf, DNS_MASTER_BUFSZ); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + + isc_buffer_init(&buffer, lhsbuf, strlen(lhsbuf)); + isc_buffer_add(&buffer, strlen(lhsbuf)); + isc_buffer_setactive(&buffer, strlen(lhsbuf)); + result = dns_name_fromtext(owner, &buffer, ctx->origin, + ISC_FALSE, NULL); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + + isc_buffer_init(&buffer, rhsbuf, strlen(rhsbuf)); + isc_buffer_add(&buffer, strlen(rhsbuf)); + isc_buffer_setactive(&buffer, strlen(rhsbuf)); + + result = isc_lex_openbuffer(ctx->lex, &buffer); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + + isc_buffer_init(&target, target_mem, target_size); + result = dns_rdata_fromtext(&rdata, ctx->zclass, type, + ctx->lex, ctx->origin, ISC_FALSE, + ctx->mctx, &target, callbacks); + isc_lex_close(ctx->lex); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + + rdatalist.type = type; + rdatalist.covers = 0; + rdatalist.rdclass = ctx->zclass; + rdatalist.ttl = ctx->ttl; + ISC_LIST_INIT(rdatalist.rdata); + ISC_LIST_PREPEND(head, &rdatalist, link); + ISC_LIST_APPEND(rdatalist.rdata, &rdata, link); + result = commit(callbacks, ctx->lex, &head, owner, + ctx->top); + ISC_LIST_UNLINK(rdatalist.rdata, &rdata, link); + if (result != ISC_R_SUCCESS) + goto error_cleanup; + dns_rdata_reset(&rdata); + } + result = ISC_R_SUCCESS; + goto cleanup; + + error_cleanup: + if (result == ISC_R_NOMEMORY) + (*callbacks->error)(callbacks, "$GENERATE: %s", + dns_result_totext(result)); + else + (*callbacks->error)(callbacks, "$GENERATE: %s:%lu: %s", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + dns_result_totext(result)); + + insist_cleanup: + INSIST(result != ISC_R_SUCCESS); + + cleanup: + if (target_mem != NULL) + isc_mem_put(ctx->mctx, target_mem, target_size); + if (lhsbuf != NULL) + isc_mem_put(ctx->mctx, lhsbuf, DNS_MASTER_BUFSZ); + if (rhsbuf != NULL) + isc_mem_put(ctx->mctx, rhsbuf, DNS_MASTER_BUFSZ); + return (result); +} + +static isc_result_t +load(dns_loadctx_t **ctxp) { dns_rdataclass_t rdclass; dns_rdatatype_t type, covers; isc_uint32_t ttl_offset = 0; - dns_name_t current_name; - dns_name_t glue_name; - dns_name_t new_name; - dns_name_t origin_name = *origin; - isc_boolean_t current_known = ISC_FALSE; - isc_boolean_t in_glue = ISC_FALSE; + dns_name_t *new_name; isc_boolean_t current_has_delegation = ISC_FALSE; isc_boolean_t done = ISC_FALSE; isc_boolean_t finish_origin = ISC_FALSE; @@ -200,7 +698,7 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, isc_boolean_t initialws; char *include_file = NULL; isc_token_t token; - isc_result_t result = ISC_R_UNEXPECTED; + isc_result_t result = ISC_R_UNEXPECTED; rdatalist_head_t glue_list; rdatalist_head_t current_list; dns_rdatalist_t *this; @@ -219,37 +717,25 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, int rdata_size = 0; unsigned char *target_mem = NULL; int target_size = TSIZ; - unsigned char name_buf[NBUFS][MAXWIRESZ]; - isc_boolean_t name_in_use[NBUFS]; - int glue_in_use = -1; - int current_in_use = -1; - int origin_in_use = -1; int new_in_use; - isc_buffer_t name; - isc_lexspecials_t specials; + unsigned int loop_cnt = 0; + isc_mem_t *mctx; + dns_rdatacallbacks_t *callbacks; + dns_loadctx_t *ctx; + char *range = NULL; + char *lhs = NULL; + char *gtype = NULL; + char *rhs = NULL; - REQUIRE(lex != NULL); - REQUIRE(dns_name_isabsolute(top)); - REQUIRE(dns_name_isabsolute(origin)); - REQUIRE(callbacks != NULL); - REQUIRE(callbacks->add != NULL); - REQUIRE(callbacks->error != NULL); - REQUIRE(callbacks->warn != NULL); - REQUIRE(mctx != NULL); - dns_name_init(¤t_name, NULL); - dns_name_init(&glue_name, NULL); + ctx = *ctxp; + REQUIRE(DNS_LCTX_VALID(ctx)); + callbacks = ctx->callbacks; + mctx = ctx->mctx; ISC_LIST_INIT(glue_list); ISC_LIST_INIT(current_list); - memset(specials, 0, sizeof specials); - specials['('] = 1; - specials[')'] = 1; - specials['"'] = 1; - isc_lex_setspecials(lex, specials); - isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); - /* * Allocate target_size of buffer space. This is greater than twice * the maximum individual RR data size. @@ -257,20 +743,33 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, target_mem = isc_mem_get(mctx, target_size); if (target_mem == NULL) { result = ISC_R_NOMEMORY; - goto error_cleanup; + goto log_and_cleanup; } isc_buffer_init(&target, target_mem, target_size); target_save = target; - memset(name_in_use, 0, NBUFS * sizeof(isc_boolean_t)); - do { initialws = ISC_FALSE; - GETTOKEN(lex, ISC_LEXOPT_INITIALWS, &token, ISC_TRUE); + GETTOKEN(ctx->lex, ISC_LEXOPT_INITIALWS, &token, ISC_TRUE); if (token.type == isc_tokentype_eof) { if (read_till_eol) - WARNUNEXPECTEDEOF(lex); + WARNUNEXPECTEDEOF(ctx->lex); + /* Pop the include stack? */ + if (ctx->parent != NULL) { + COMMITALL; + *ctxp = ctx->parent; + ctx->parent = NULL; + CTX_COPYVAR(ctx, *ctxp, ttl_known); + CTX_COPYVAR(ctx, *ctxp, default_ttl_known); + CTX_COPYVAR(ctx, *ctxp, ttl); + CTX_COPYVAR(ctx, *ctxp, default_ttl); + CTX_COPYVAR(ctx, *ctxp, warn_1035); + CTX_COPYVAR(ctx, *ctxp, seen_include); + dns_loadctx_detach(&ctx); + ctx = *ctxp; + continue; + } done = ISC_TRUE; continue; } @@ -300,25 +799,25 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, if (strcasecmp(token.value.as_pointer, "$ORIGIN") == 0) { - GETTOKEN(lex, 0, &token, ISC_FALSE); + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); read_till_eol = ISC_TRUE; finish_origin = ISC_TRUE; } else if (strcasecmp(token.value.as_pointer, - "$TTL") == 0) { - GETTOKEN(lex, 0, &token, ISC_FALSE); + "$TTL") == 0) { + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); result = dns_ttl_fromtext(&token.value.as_textregion, &ctx->ttl); if (result != ISC_R_SUCCESS) - goto cleanup; + goto insist_and_cleanup; if (ctx->ttl > 0x7fffffffUL) { (callbacks->warn)(callbacks, "%s: %s:%lu: " "$TTL %lu > MAXTTL, " "setting $TTL to 0", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), ctx->ttl); ctx->ttl = 0; } @@ -328,16 +827,18 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, continue; } else if (strcasecmp(token.value.as_pointer, "$INCLUDE") == 0) { + COMMITALL; if (ttl_offset != 0) { (callbacks->error)(callbacks, "%s: %s:%lu: $INCLUDE " - "may not be used with $DATE", + "may not be used with $DATE", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); - goto cleanup; + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); + result = DNS_R_SYNTAX; + goto insist_and_cleanup; } - GETTOKEN(lex, ISC_LEXOPT_QSTRING, &token, + GETTOKEN(ctx->lex, ISC_LEXOPT_QSTRING, &token, ISC_FALSE); if (include_file != NULL) isc_mem_free(mctx, include_file); @@ -345,28 +846,24 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, token.value.as_pointer); if (include_file == NULL) { result = ISC_R_NOMEMORY; - goto error_cleanup; + goto log_and_cleanup; } - GETTOKEN(lex, 0, &token, ISC_TRUE); + GETTOKEN(ctx->lex, 0, &token, ISC_TRUE); if (token.type == isc_tokentype_eol || token.type == isc_tokentype_eof) { if (token.type == isc_tokentype_eof) - WARNUNEXPECTEDEOF(lex); + WARNUNEXPECTEDEOF(ctx->lex); + isc_lex_ungettoken(ctx->lex, &token); /* * No origin field. */ - result = loadfile(include_file, - top, - &origin_name, - zclass, - age_ttl, - callbacks, - ctx, - mctx); + result = pushfile(include_file, + ctx->origin, + ctxp); if (result != ISC_R_SUCCESS) - goto cleanup; - isc_lex_ungettoken(lex, &token); + goto log_and_cleanup; + ctx = *ctxp; continue; } /* @@ -380,44 +877,112 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, "$DATE") == 0) { isc_int64_t dump_time64; isc_stdtime_t dump_time, current_time; - GETTOKEN(lex, 0, &token, ISC_FALSE); + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); isc_stdtime_get(¤t_time); result = dns_time64_fromtext(token.value. as_pointer, &dump_time64); if (result != ISC_R_SUCCESS) - goto error_cleanup; + goto log_and_cleanup; dump_time = (isc_stdtime_t)dump_time64; if (dump_time != dump_time64) { UNEXPECTED_ERROR(__FILE__, __LINE__, "%s: %s:%lu: " "$DATE outside epoch", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); - goto cleanup; + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); + result = ISC_R_UNEXPECTED; + goto insist_and_cleanup; } if (dump_time > current_time) { UNEXPECTED_ERROR(__FILE__, __LINE__, "%s: %s:%lu: " "$DATE in future, using current date", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); dump_time = current_time; } ttl_offset = current_time - dump_time; read_till_eol = ISC_TRUE; continue; - } else if (strncasecmp(token.value.as_pointer, + } else if (strcasecmp(token.value.as_pointer, + "$GENERATE") == 0) { + /* + * Use default ttl if known otherwise + * inherit or error. + */ + if (!ctx->ttl_known && + !ctx->default_ttl_known) { + (*callbacks->error)(callbacks, + "%s: %s:%lu: no TTL specified", + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); + result = DNS_R_NOTTL; + goto insist_and_cleanup; + } else if (ctx->default_ttl_known) { + ctx->ttl = ctx->default_ttl; + } + /* + * Lazy cleanup. + */ + if (range != NULL) + isc_mem_free(mctx, range); + if (lhs != NULL) + isc_mem_free(mctx, lhs); + if (gtype != NULL) + isc_mem_free(mctx, gtype); + if (rhs != NULL) + isc_mem_free(mctx, rhs); + /* range */ + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); + range = isc_mem_strdup(mctx, + token.value.as_pointer); + if (range == NULL) { + result = ISC_R_NOMEMORY; + goto log_and_cleanup; + } + /* LHS */ + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); + lhs = isc_mem_strdup(mctx, + token.value.as_pointer); + if (lhs == NULL) { + result = ISC_R_NOMEMORY; + goto log_and_cleanup; + } + /* TYPE */ + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); + gtype = isc_mem_strdup(mctx, + token.value.as_pointer); + if (gtype == NULL) { + result = ISC_R_NOMEMORY; + goto log_and_cleanup; + } + /* RHS */ + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); + rhs = isc_mem_strdup(mctx, + token.value.as_pointer); + if (rhs == NULL) { + result = ISC_R_NOMEMORY; + goto log_and_cleanup; + } + result = generate(ctx, range, lhs, gtype, rhs); + if (result != ISC_R_SUCCESS) + goto insist_and_cleanup; + read_till_eol = ISC_TRUE; + continue; + } else if (strncasecmp(token.value.as_pointer, "$", 1) == 0) { - (callbacks->error)(callbacks, - "%s: %s:%lu: " - "unknown $ directive '%s'", - "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), - token.value.as_pointer); - goto cleanup; + (callbacks->error)(callbacks, + "%s: %s:%lu: " + "unknown $ directive '%s'", + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + token.value.as_pointer); + result = DNS_R_SYNTAX; + goto insist_and_cleanup; } /* @@ -426,46 +991,41 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, * Find a free name buffer. */ for (new_in_use = 0; new_in_use < NBUFS ; new_in_use++) - if (!name_in_use[new_in_use]) + if (!ctx->in_use[new_in_use]) break; INSIST(new_in_use < NBUFS); - isc_buffer_init(&name, &name_buf[new_in_use][0], - MAXWIRESZ); - dns_name_init(&new_name, NULL); + dns_fixedname_init(&ctx->fixed[new_in_use]); + new_name = dns_fixedname_name(&ctx->fixed[new_in_use]); isc_buffer_init(&buffer, token.value.as_region.base, token.value.as_region.length); isc_buffer_add(&buffer, token.value.as_region.length); isc_buffer_setactive(&buffer, token.value.as_region.length); - result = dns_name_fromtext(&new_name, &buffer, - &origin_name, ISC_FALSE, &name); + result = dns_name_fromtext(new_name, &buffer, + ctx->origin, ISC_FALSE, NULL); if (result != ISC_R_SUCCESS) - goto error_cleanup; + goto log_and_cleanup; /* * Finish $ORIGIN / $INCLUDE processing if required. */ if (finish_origin) { - if (origin_in_use != -1) - name_in_use[origin_in_use] = ISC_FALSE; - origin_in_use = new_in_use; - name_in_use[origin_in_use] = ISC_TRUE; - origin_name = new_name; + if (ctx->origin_in_use != -1) + ctx->in_use[ctx->origin_in_use] = + ISC_FALSE; + ctx->origin_in_use = new_in_use; + ctx->in_use[ctx->origin_in_use] = ISC_TRUE; + ctx->origin = new_name; finish_origin = ISC_FALSE; continue; } if (finish_include) { - result = loadfile(include_file, - top, - &new_name, - zclass, - age_ttl, - callbacks, - ctx, - mctx); - if (result != ISC_R_SUCCESS) - goto cleanup; finish_include = ISC_FALSE; + result = pushfile(include_file, + new_name, ctxp); + if (result != ISC_R_SUCCESS) + goto log_and_cleanup; + ctx = *ctxp; continue; } @@ -479,17 +1039,18 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, * and pop stacks leaving us in 'normal' processing * state. Linked lists are undone by commit(). */ - if (in_glue && dns_name_compare(&glue_name, - &new_name) != 0) { - result = commit(callbacks, lex, &glue_list, - &glue_name, top); + if (ctx->glue != NULL && + dns_name_compare(ctx->glue, new_name) != 0) { + result = commit(callbacks, ctx->lex, + &glue_list, + ctx->glue, ctx->top); if (result != ISC_R_SUCCESS) - goto cleanup; - if (glue_in_use != -1) - name_in_use[glue_in_use] = ISC_FALSE; - glue_in_use = -1; - dns_name_invalidate(&glue_name); - in_glue = ISC_FALSE; + goto log_and_cleanup; + if (ctx->glue_in_use != -1) + ctx->in_use[ctx->glue_in_use] = + ISC_FALSE; + ctx->glue_in_use = -1; + ctx->glue = NULL; rdcount = rdcount_save; rdlcount = rdlcount_save; target = target_save; @@ -502,32 +1063,33 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, * otherwise we have a new name so commit what we * have. */ - if (!in_glue && (!current_known || - dns_name_compare(¤t_name, &new_name) != 0)) { + if ((ctx->glue == NULL) && (ctx->current == NULL || + dns_name_compare(ctx->current, new_name) != 0)) { if (current_has_delegation && - is_glue(¤t_list, &new_name)) { - in_glue = ISC_TRUE; + is_glue(¤t_list, new_name)) { rdcount_save = rdcount; rdlcount_save = rdlcount; target_save = target; - glue_name = new_name; - glue_in_use = new_in_use; - name_in_use[glue_in_use] = ISC_TRUE; + ctx->glue = new_name; + ctx->glue_in_use = new_in_use; + ctx->in_use[ctx->glue_in_use] = + ISC_TRUE; } else { - result = commit(callbacks, lex, + result = commit(callbacks, ctx->lex, ¤t_list, - ¤t_name, top); + ctx->current, + ctx->top); if (result != ISC_R_SUCCESS) - goto cleanup; + goto log_and_cleanup; rdcount = 0; rdlcount = 0; - if (current_in_use != -1) - name_in_use[current_in_use] - = ISC_FALSE; - current_in_use = new_in_use; - name_in_use[current_in_use] = ISC_TRUE; - current_name = new_name; - current_known = ISC_TRUE; + if (ctx->current_in_use != -1) + ctx->in_use[ctx->current_in_use] = + ISC_FALSE; + ctx->current_in_use = new_in_use; + ctx->in_use[ctx->current_in_use] = + ISC_TRUE; + ctx->current = new_name; current_has_delegation = ISC_FALSE; isc_buffer_init(&target, target_mem, target_size); @@ -537,11 +1099,11 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, UNEXPECTED_ERROR(__FILE__, __LINE__, "%s:%lu: isc_lex_gettoken() returned " "unexpeced token type (%d)", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), token.type); result = ISC_R_UNEXPECTED; - goto cleanup; + goto insist_and_cleanup; } /* @@ -556,7 +1118,7 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, type = 0; rdclass = 0; - GETTOKEN(lex, 0, &token, initialws); + GETTOKEN(ctx->lex, 0, &token, initialws); if (initialws) { if (token.type == isc_tokentype_eol) { @@ -565,42 +1127,43 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, } if (token.type == isc_tokentype_eof) { - WARNUNEXPECTEDEOF(lex); - done = ISC_TRUE; + WARNUNEXPECTEDEOF(ctx->lex); + read_till_eol = ISC_FALSE; + isc_lex_ungettoken(ctx->lex, &token); continue; } - if (!current_known) { + if (ctx->current == NULL) { (*callbacks->error)(callbacks, "%s: %s:%lu: No current owner name", - "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); result = DNS_R_NOOWNER; - goto cleanup; + goto insist_and_cleanup; } } if (dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion) == ISC_R_SUCCESS) - GETTOKEN(lex, 0, &token, ISC_FALSE); + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); if (dns_ttl_fromtext(&token.value.as_textregion, &ctx->ttl) == ISC_R_SUCCESS) { if (ctx->ttl > 0x7fffffffUL) { (callbacks->warn)(callbacks, - "%s: %s:%lu: " - "TTL %lu > MAXTTL, " - "setting TTL to 0", - "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), - ctx->ttl); + "%s: %s:%lu: " + "TTL %lu > MAXTTL, " + "setting TTL to 0", + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + ctx->ttl); ctx->ttl = 0; } ctx->ttl_known = ISC_TRUE; - GETTOKEN(lex, 0, &token, ISC_FALSE); + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); } else if (!ctx->ttl_known && !ctx->default_ttl_known) { /* * BIND 4 / 8 'USE_SOA_MINIMUM' could be set here. @@ -608,10 +1171,10 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, (*callbacks->error)(callbacks, "%s: %s:%lu: no TTL specified", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); result = DNS_R_NOTTL; - goto cleanup; + goto insist_and_cleanup; } else if (ctx->default_ttl_known) { ctx->ttl = ctx->default_ttl; } else if (ctx->warn_1035) { @@ -619,96 +1182,75 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, "%s: %s:%lu: " "using RFC 1035 TTL semantics", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex)); + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex)); ctx->warn_1035 = ISC_FALSE; - } + } if (token.type != isc_tokentype_string) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_lex_gettoken() returned unexpected token type"); result = ISC_R_UNEXPECTED; - goto cleanup; + goto insist_and_cleanup; } - + if (rdclass == 0 && dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion) == ISC_R_SUCCESS) - GETTOKEN(lex, 0, &token, ISC_FALSE); + GETTOKEN(ctx->lex, 0, &token, ISC_FALSE); if (token.type != isc_tokentype_string) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_lex_gettoken() returned unexpected token type"); result = ISC_R_UNEXPECTED; - goto cleanup; + goto insist_and_cleanup; } result = dns_rdatatype_fromtext(&type, &token.value.as_textregion); - if (result != ISC_R_SUCCESS) { - (*callbacks->warn)(callbacks, - "%s: %s:%lu: unknown RR type '%.*s'", - "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), - token.value.as_textregion.length, - token.value.as_textregion.base); - goto cleanup; - } + if (result != ISC_R_SUCCESS) { + (*callbacks->warn)(callbacks, + "%s: %s:%lu: unknown RR type '%.*s'", + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + token.value.as_textregion.length, + token.value.as_textregion.base); + goto insist_and_cleanup; + } /* * If the class specified does not match the zone's class * print out a error message and exit. */ - if (rdclass != 0 && rdclass != zclass) { - char buf1[32]; - char buf2[32]; - unsigned int len1, len2; - isc_buffer_t buffer; - isc_region_t region; - - isc_buffer_init(&buffer, buf1, sizeof(buf1)); - result = dns_rdataclass_totext(rdclass, &buffer); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "dns_rdataclass_totext() failed: %s", - dns_result_totext(result)); - result = ISC_R_UNEXPECTED; - goto cleanup; - } - isc_buffer_usedregion(&buffer, ®ion); - len1 = region.length; - isc_buffer_init(&buffer, buf2, sizeof(buf2)); - result = dns_rdataclass_totext(zclass, &buffer); - if (result != ISC_R_SUCCESS) { - UNEXPECTED_ERROR(__FILE__, __LINE__, - "dns_rdataclass_totext() failed: %s", - dns_result_totext(result)); - result = ISC_R_UNEXPECTED; - goto cleanup; - } - isc_buffer_usedregion(&buffer, ®ion); - len2 = region.length; + if (rdclass != 0 && rdclass != ctx->zclass) { + char classname1[DNS_RDATACLASS_FORMATSIZE]; + char classname2[DNS_RDATACLASS_FORMATSIZE]; + + dns_rdataclass_format(rdclass, classname1, + sizeof(classname1)); + dns_rdataclass_format(ctx->zclass, classname2, + sizeof(classname2)); (*callbacks->error)(callbacks, - "%s: %s:%lu: class (%.*s) != " - "zone class (%.*s)", + "%s: %s:%lu: class '%s' != " + "zone class '%s'", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), - len1, buf1, len2, buf2); + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + classname1, classname2); result = DNS_R_BADCLASS; - goto cleanup; + goto insist_and_cleanup; } - if (type == dns_rdatatype_ns && !in_glue) + if (type == dns_rdatatype_ns && ctx->glue == NULL) current_has_delegation = ISC_TRUE; - if (age_ttl) { + if (ctx->age_ttl) { /* * Adjust the TTL for $DATE. If the RR has already * expired, ignore it without even parsing the rdata - * part (good for performance, bad for catching + * part (good for performance, bad for catching * syntax errors). */ if (ctx->ttl < ttl_offset) { @@ -727,7 +1269,7 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, &glue_list, mctx); if (new_rdata == NULL) { result = ISC_R_NOMEMORY; - goto error_cleanup; + goto log_and_cleanup; } rdata_size += RDSZ; rdata = new_rdata; @@ -736,11 +1278,12 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, /* * Read rdata contents. */ - result = dns_rdata_fromtext(&rdata[rdcount], zclass, type, - lex, &origin_name, ISC_FALSE, &target, - callbacks); + dns_rdata_init(&rdata[rdcount]); + result = dns_rdata_fromtext(&rdata[rdcount], ctx->zclass, type, + ctx->lex, ctx->origin, ISC_FALSE, ctx->mctx, + &target, callbacks); if (result != ISC_R_SUCCESS) - goto cleanup; + goto insist_and_cleanup; if (type == dns_rdatatype_sig) covers = dns_rdata_covers(&rdata[rdcount]); else @@ -752,7 +1295,7 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, * If it does not exist create new one and prepend to list * as this will mimimise list traversal. */ - if (in_glue) + if (ctx->glue != NULL) this = ISC_LIST_HEAD(glue_list); else this = ISC_LIST_HEAD(current_list); @@ -774,7 +1317,7 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, mctx); if (new_rdatalist == NULL) { result = ISC_R_NOMEMORY; - goto error_cleanup; + goto log_and_cleanup; } rdatalist = new_rdatalist; rdatalist_size += RDLSZ; @@ -782,85 +1325,72 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, this = &rdatalist[rdlcount++]; this->type = type; this->covers = covers; - this->rdclass = zclass; + this->rdclass = ctx->zclass; this->ttl = ctx->ttl; ISC_LIST_INIT(this->rdata); - ISC_LINK_INIT(this, link); - if (in_glue) - ISC_LIST_PREPEND(glue_list, this, link); + if (ctx->glue != NULL) + ISC_LIST_PREPENDUNSAFE(glue_list, this, link); else - ISC_LIST_PREPEND(current_list, this, link); + ISC_LIST_PREPENDUNSAFE(current_list, this, + link); } else if (this->ttl != ctx->ttl) { (*callbacks->warn)(callbacks, "%s: %s:%lu: " "TTL set to prior TTL (%lu)", "dns_master_load", - isc_lex_getsourcename(lex), - isc_lex_getsourceline(lex), + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), this->ttl); ctx->ttl = this->ttl; } - /* - * If the new rdata is not on the list add it. - * - * If the new rdata is on the list do not worry about - * recovering the space it is using in target as it will be - * recovered when we next call commit. The worst that can - * happen is that we make a few extra calls to commit. - */ - - if (!on_list(this, &rdata[rdcount])) { - ISC_LIST_APPEND(this->rdata, &rdata[rdcount], link); - rdcount++; - } + ISC_LIST_APPEND(this->rdata, &rdata[rdcount], link); + rdcount++; /* * We must have at least 64k as rdlen is 16 bits. * If we don't commit everything we have so far. */ - if ((target.length - target.used) < MINTSIZ) { - result = commit(callbacks, lex, ¤t_list, - ¤t_name, top); - if (result != ISC_R_SUCCESS) - goto cleanup; - result = commit(callbacks, lex, &glue_list, &glue_name, - top); - if (result != ISC_R_SUCCESS) - goto cleanup; - rdcount = 0; - rdlcount = 0; - isc_buffer_init(&target, target_mem, target_size); - rdcount_save = rdcount; - rdlcount_save = rdlcount; - target_save = target; - } - } while (!done); + if ((target.length - target.used) < MINTSIZ) + COMMITALL; + } while (!done && (ctx->loop_cnt == 0 || loop_cnt++ < ctx->loop_cnt)); + /* * Commit what has not yet been committed. */ - result = commit(callbacks, lex, ¤t_list, ¤t_name, top); + result = commit(callbacks, ctx->lex, ¤t_list, + ctx->current, ctx->top); if (result != ISC_R_SUCCESS) - goto cleanup; - result = commit(callbacks, lex, &glue_list, &glue_name, top); + goto log_and_cleanup; + result = commit(callbacks, ctx->lex, &glue_list, ctx->glue, ctx->top); if (result != ISC_R_SUCCESS) - goto cleanup; - else - result = ISC_R_SUCCESS; + goto log_and_cleanup; + + if (!done) { + INSIST(ctx->done != NULL && ctx->task != NULL); + result = DNS_R_CONTINUE; + } else if (result == ISC_R_SUCCESS && ctx->seen_include) + result = DNS_R_SEENINCLUDE; goto cleanup; - error_cleanup: - (*callbacks->error)(callbacks, "dns_master_load: %s", - dns_result_totext(result)); + log_and_cleanup: + if (result == ISC_R_NOMEMORY) + (*callbacks->error)(callbacks, "dns_master_load: %s", + dns_result_totext(result)); + else + (*callbacks->error)(callbacks, "%s: %s:%lu: %s", + "dns_master_load", + isc_lex_getsourcename(ctx->lex), + isc_lex_getsourceline(ctx->lex), + dns_result_totext(result)); + + insist_and_cleanup: + INSIST(result != ISC_R_SUCCESS); cleanup: - if (lex != NULL) { - isc_lex_close(lex); - isc_lex_destroy(&lex); - } - while ((this = ISC_LIST_HEAD(current_list)) != NULL) + while ((this = ISC_LIST_HEAD(current_list)) != NULL) ISC_LIST_UNLINK(current_list, this, link); - while ((this = ISC_LIST_HEAD(glue_list)) != NULL) + while ((this = ISC_LIST_HEAD(glue_list)) != NULL) ISC_LIST_UNLINK(glue_list, this, link); if (rdatalist != NULL) isc_mem_put(mctx, rdatalist, @@ -871,30 +1401,72 @@ load(isc_lex_t *lex, dns_name_t *top, dns_name_t *origin, isc_mem_put(mctx, target_mem, target_size); if (include_file != NULL) isc_mem_free(mctx, include_file); + if (range != NULL) + isc_mem_free(mctx, range); + if (lhs != NULL) + isc_mem_free(mctx, lhs); + if (gtype != NULL) + isc_mem_free(mctx, gtype); + if (rhs != NULL) + isc_mem_free(mctx, rhs); return (result); } static isc_result_t -loadfile(const char *master_file, dns_name_t *top, dns_name_t *origin, - dns_rdataclass_t zclass, isc_boolean_t age_ttl, - dns_rdatacallbacks_t *callbacks, loadctx_t *ctx, isc_mem_t *mctx) -{ +pushfile(const char *master_file, dns_name_t *origin, dns_loadctx_t **ctxp) { isc_result_t result; - isc_lex_t *lex = NULL; + dns_loadctx_t *ctx; + dns_loadctx_t *new = NULL; + isc_region_t r; + int new_in_use; REQUIRE(master_file != NULL); + REQUIRE(ctxp != NULL); + ctx = *ctxp; + REQUIRE(DNS_LCTX_VALID(ctx)); - result = isc_lex_create(mctx, TOKENSIZ, &lex); + ctx->seen_include = ISC_TRUE; + + result = loadctx_create(ctx->mctx, ctx->age_ttl, ctx->top, + ctx->zclass, origin, ctx->callbacks, + ctx->task, ctx->done, ctx->done_arg, + &new); if (result != ISC_R_SUCCESS) return (result); - result = isc_lex_openfile(lex, master_file); - if (result != ISC_R_SUCCESS) { - isc_lex_destroy(&lex); - return (result); + /* Set current domain. */ + if (ctx->glue != NULL || ctx->current != NULL) { + for (new_in_use = 0; new_in_use < NBUFS ; new_in_use++) + if (!new->in_use[new_in_use]) + break; + INSIST(new_in_use < NBUFS); + new->current_in_use = new_in_use; + new->current = + dns_fixedname_name(&new->fixed[new->current_in_use]); + new->in_use[new->current_in_use] = ISC_TRUE; + dns_name_toregion((ctx->glue != NULL) ? + ctx->glue : ctx->current, &r); + dns_name_fromregion(new->current, &r); } - return (load(lex, top, origin, zclass, age_ttl, callbacks, ctx, mctx)); + CTX_COPYVAR(ctx, new, ttl_known); + CTX_COPYVAR(ctx, new, default_ttl_known); + CTX_COPYVAR(ctx, new, ttl); + CTX_COPYVAR(ctx, new, default_ttl); + CTX_COPYVAR(ctx, new, warn_1035); + CTX_COPYVAR(ctx, new, seen_include); + + result = isc_lex_openfile(new->lex, master_file); + if (result != ISC_R_SUCCESS) + goto cleanup; + new->parent = ctx; + *ctxp = new; + return (ISC_R_SUCCESS); + + cleanup: + if (new != NULL) + dns_loadctx_detach(&new); + return (result); } isc_result_t @@ -903,13 +1475,166 @@ dns_master_loadfile(const char *master_file, dns_name_t *top, dns_rdataclass_t zclass, isc_boolean_t age_ttl, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { - loadctx_t ctx; + dns_loadctx_t *ctx = NULL; + isc_result_t result; + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, NULL, NULL, NULL, &ctx); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_lex_openfile(ctx->lex, master_file); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = load(&ctx); + INSIST(result != DNS_R_CONTINUE); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} + +isc_result_t +dns_master_loadfilequota(const char *master_file, dns_name_t *top, + dns_name_t *origin, dns_rdataclass_t zclass, + isc_boolean_t age_ttl, + dns_rdatacallbacks_t *callbacks, + isc_task_t *task, dns_loaddonefunc_t done, + void *done_arg, dns_loadmgr_t *lmgr, + dns_loadctx_t **ctxp, isc_mem_t *mctx) +{ + isc_boolean_t queue; + dns_loadctx_t *ctx = NULL; + isc_result_t result; + isc_event_t *event; + + REQUIRE(DNS_LMGR_VALID(lmgr)); + REQUIRE(ctxp != NULL && *ctxp == NULL); + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, task, done, done_arg, &ctx); + if (result != ISC_R_SUCCESS) + goto cleanup; + + ctx->rate_limited = ISC_TRUE; + ctx->master_file = isc_mem_strdup(mctx, master_file); + if (ctx->master_file == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + loadmgr_iattach(lmgr, &ctx->loadmgr); + + LOCK(&lmgr->lock); + lmgr->active++; + queue = ISC_TF((lmgr->limit != 0 && lmgr->active > lmgr->limit)); + if (queue) + ISC_LIST_APPEND(lmgr->list, ctx, link); + INSIST(queue || ISC_LIST_EMPTY(lmgr->list)); + UNLOCK(&lmgr->lock); + + dns_loadctx_attach(ctx, ctxp); + result = DNS_R_CONTINUE; + if (!queue) { + event = &ctx->event; + isc_task_send(ctx->task, &event); + } + return (result); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} + +static void +loadmgr_done(dns_loadctx_t *ctx, isc_result_t result) { + dns_loadctx_t *next; + isc_event_t *event; + + if (ctx->done != NULL) + (ctx->done)(ctx->done_arg, result); + + LOCK(&ctx->loadmgr->lock); + INSIST(ctx->loadmgr->active > 0); + ctx->loadmgr->active--; + /* dequeue */ + next = ISC_LIST_HEAD(ctx->loadmgr->list); + if (next != NULL) + ISC_LIST_UNLINK(ctx->loadmgr->list, next, link); + UNLOCK(&ctx->loadmgr->lock); + if (next != NULL) { + event = &next->event; + isc_task_send(next->task, &event); + } +} + + +static void +loadmgr_start(isc_task_t *task, isc_event_t *event) { + dns_loadctx_t *ctx = event->ev_arg; + isc_result_t result; - loadctx_init(&ctx); - return (loadfile(master_file, top, origin, zclass, age_ttl, - callbacks, &ctx, mctx)); + INSIST(task == ctx->task); + + UNUSED(task); + + if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) { + result = ISC_R_CANCELED; + goto done; + } + result = isc_lex_openfile(ctx->lex, ctx->master_file); + if (result == ISC_R_SUCCESS) + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + result = task_send(ctx); + if (result == ISC_R_SUCCESS) + isc_event_free(&event); + return; + } + done: + loadmgr_done(ctx, result); + isc_event_free(&event); + dns_loadctx_detach(&ctx); + return; } +isc_result_t +dns_master_loadfileinc(const char *master_file, dns_name_t *top, + dns_name_t *origin, dns_rdataclass_t zclass, + isc_boolean_t age_ttl, dns_rdatacallbacks_t *callbacks, + isc_task_t *task, dns_loaddonefunc_t done, + void *done_arg, isc_mem_t *mctx) +{ + dns_loadctx_t *ctx = NULL; + isc_result_t tresult; + isc_result_t result; + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, task, done, done_arg, &ctx); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_lex_openfile(ctx->lex, master_file); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + tresult = task_send(ctx); + if (tresult == ISC_R_SUCCESS) + return (result); + result = tresult; + } + if (ctx->done != NULL) + (ctx->done)(ctx->done_arg, result); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} isc_result_t dns_master_loadstream(FILE *stream, dns_name_t *top, dns_name_t *origin, @@ -917,25 +1642,64 @@ dns_master_loadstream(FILE *stream, dns_name_t *top, dns_name_t *origin, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { isc_result_t result; - isc_lex_t *lex = NULL; - loadctx_t ctx; + dns_loadctx_t *ctx = NULL; REQUIRE(stream != NULL); - loadctx_init(&ctx); + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, NULL, NULL, NULL, &ctx); + if (result != ISC_R_SUCCESS) + goto cleanup; - result = isc_lex_create(mctx, TOKENSIZ, &lex); + result = isc_lex_openstream(ctx->lex, stream); if (result != ISC_R_SUCCESS) - return (result); + goto cleanup; - result = isc_lex_openstream(lex, stream); - if (result != ISC_R_SUCCESS) { - isc_lex_destroy(&lex); - return (result); + result = load(&ctx); + INSIST(result != DNS_R_CONTINUE); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} + +isc_result_t +dns_master_loadstreaminc(FILE *stream, dns_name_t *top, dns_name_t *origin, + dns_rdataclass_t zclass, isc_boolean_t age_ttl, + dns_rdatacallbacks_t *callbacks, isc_task_t *task, + dns_loaddonefunc_t done, void *done_arg, + isc_mem_t *mctx) +{ + isc_result_t result; + isc_result_t tresult; + dns_loadctx_t *ctx = NULL; + + REQUIRE(stream != NULL); + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, task, done, done_arg, &ctx); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = isc_lex_openstream(ctx->lex, stream); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + tresult = task_send(ctx); + if (tresult == ISC_R_SUCCESS) + return (result); + result = tresult; } + if (ctx->done != NULL) + (ctx->done)(ctx->done_arg, result); - return (load(lex, top, origin, zclass, age_ttl, - callbacks, &ctx, mctx)); + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); } isc_result_t @@ -945,25 +1709,65 @@ dns_master_loadbuffer(isc_buffer_t *buffer, dns_name_t *top, dns_rdatacallbacks_t *callbacks, isc_mem_t *mctx) { isc_result_t result; - isc_lex_t *lex = NULL; - loadctx_t ctx; + dns_loadctx_t *ctx = NULL; REQUIRE(buffer != NULL); - loadctx_init(&ctx); - - result = isc_lex_create(mctx, TOKENSIZ, &lex); + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, NULL, NULL, NULL, &ctx); if (result != ISC_R_SUCCESS) return (result); - result = isc_lex_openbuffer(lex, buffer); - if (result != ISC_R_SUCCESS) { - isc_lex_destroy(&lex); + result = isc_lex_openbuffer(ctx->lex, buffer); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = load(&ctx); + INSIST(result != DNS_R_CONTINUE); + + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); +} + +isc_result_t +dns_master_loadbufferinc(isc_buffer_t *buffer, dns_name_t *top, + dns_name_t *origin, dns_rdataclass_t zclass, + isc_boolean_t age_ttl, + dns_rdatacallbacks_t *callbacks, isc_task_t *task, + dns_loaddonefunc_t done, void *done_arg, + isc_mem_t *mctx) +{ + isc_result_t result; + isc_result_t tresult; + dns_loadctx_t *ctx = NULL; + + REQUIRE(buffer != NULL); + + result = loadctx_create(mctx, age_ttl, top, zclass, origin, + callbacks, task, done, done_arg, &ctx); + if (result != ISC_R_SUCCESS) return (result); + + result = isc_lex_openbuffer(ctx->lex, buffer); + if (result != ISC_R_SUCCESS) + goto cleanup; + + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + tresult = task_send(ctx); + if (tresult == ISC_R_SUCCESS) + return (result); + result = tresult; } + if (ctx->done != NULL) + (ctx->done)(ctx->done_arg, result); - return (load(lex, top, origin, zclass, age_ttl, - callbacks, &ctx, mctx)); + cleanup: + if (ctx != NULL) + dns_loadctx_detach(&ctx); + return (result); } /* @@ -1054,7 +1858,7 @@ grow_rdata(int new_len, dns_rdata_t *old, int old_len, } this = ISC_LIST_NEXT(this, link); } - + /* * Copy glue relinking. */ @@ -1160,19 +1964,224 @@ is_glue(rdatalist_head_t *head, dns_name_t *owner) { return (ISC_FALSE); } +static void +load_quantum(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + dns_loadctx_t *ctx; + + REQUIRE(event != NULL); + ctx = event->ev_arg; + REQUIRE(DNS_LCTX_VALID(ctx)); + + if (ctx->canceled) + result = ISC_R_CANCELED; + else + result = load(&ctx); + if (result == DNS_R_CONTINUE) { + isc_task_send(task, &event); + } else if (result == ISC_R_SUCCESS && ctx->parent) { + /* Pop ctx and continue. */ + event->ev_arg = ctx->parent; + ctx->parent = NULL; + dns_loadctx_detach(&ctx); + isc_task_send(task, &event); + } else { + if (ctx->rate_limited) + loadmgr_done(ctx, result); + else + (ctx->done)(ctx->done_arg, result); + isc_event_free(&event); + dns_loadctx_detach(&ctx); + } +} + +static isc_result_t +task_send(dns_loadctx_t *ctx) { + isc_event_t *event; + + event = isc_event_allocate(ctx->mctx, NULL, + DNS_EVENT_MASTERQUANTUM, + load_quantum, ctx, sizeof(*event)); + if (event == NULL) + return (ISC_R_NOMEMORY); + isc_task_send(ctx->task, &event); + return (ISC_R_SUCCESS); +} + /* - * Returns ISC_TRUE if the 'rdata' is already on 'rdatalist'. + * DNS load manager. */ -static isc_boolean_t -on_list(dns_rdatalist_t *rdatalist, dns_rdata_t *rdata) { - dns_rdata_t *rdata2; +isc_result_t +dns_loadmgr_create(isc_mem_t *mctx, dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_result_t result; - rdata2 = ISC_LIST_HEAD(rdatalist->rdata); - while (rdata2 != NULL) { - if (dns_rdata_compare(rdata, rdata2) == 0) - return (ISC_TRUE); - rdata2 = ISC_LIST_NEXT(rdata2, link); + REQUIRE(mgrp != NULL && *mgrp == NULL); + + mgr = isc_mem_get(mctx, sizeof(*mgr)); + if (mgr == NULL) + return (ISC_R_NOMEMORY); + result = isc_mutex_init(&mgr->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, mgr, sizeof(*mgr)); + return (result); } - return (ISC_FALSE); + mgr->erefs = 1; + mgr->irefs = 0; + mgr->limit = 0; + mgr->active = 0; + mgr->mctx = NULL; + isc_mem_attach(mctx, &mgr->mctx); + ISC_LIST_INIT(mgr->list); + mgr->magic = DNS_LMGR_MAGIC; + *mgrp = mgr; + return (ISC_R_SUCCESS); +} + +void +dns_loadmgr_setlimit(dns_loadmgr_t *mgr, isc_uint32_t limit) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgr->limit = limit; +} + +isc_uint32_t +dns_loadmgr_getlimit(dns_loadmgr_t *mgr) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + return(mgr->limit); +} + +void +dns_loadmgr_cancel(dns_loadmgr_t *mgr) { + + REQUIRE(DNS_LMGR_VALID(mgr)); + + LOCK(&mgr->lock); + loadmgr_cancel(mgr); + UNLOCK(&mgr->lock); +} + +static void +loadmgr_cancel(dns_loadmgr_t *mgr) { + dns_loadctx_t *ctx; + isc_event_t *event; + + for (ctx = ISC_LIST_HEAD(mgr->list); ctx != NULL; ) { + ISC_LIST_UNLINK(mgr->list, ctx, link); + event = &ctx->event; + event->ev_attributes |= ISC_EVENTATTR_CANCELED; + isc_task_send(ctx->task, &event); + } +} + +void +dns_loadctx_cancel(dns_loadctx_t *ctx) { + isc_event_t *event; + + REQUIRE(DNS_LCTX_VALID(ctx)); + + LOCK(&ctx->lock); + ctx->canceled = ISC_TRUE; + /* + * If we are queued to be run dequeue. + */ + if (ctx->loadmgr != NULL && ISC_LINK_LINKED(ctx, link)) { + LOCK(&ctx->loadmgr->lock); + ISC_LIST_UNLINK(ctx->loadmgr->list, ctx, link); + UNLOCK(&ctx->loadmgr->lock); + event = &ctx->event; + event->ev_attributes |= ISC_EVENTATTR_CANCELED; + isc_task_send(ctx->task, &event); + } + UNLOCK(&ctx->lock); +} + + +void +dns_loadmgr_attach(dns_loadmgr_t *source, dns_loadmgr_t **target) { + + REQUIRE(DNS_LMGR_VALID(source)); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&source->lock); + INSIST(source->erefs != 0); + source->erefs++; + INSIST(source->erefs != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +void +dns_loadmgr_detach(dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_boolean_t destroy = ISC_FALSE; + + REQUIRE(mgrp != NULL); + mgr = *mgrp; + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgrp = NULL; + + LOCK(&mgr->lock); + INSIST(mgr->erefs != 0); + mgr->erefs--; + if (mgr->erefs == 0) { + if (mgr->irefs == 0) + destroy = ISC_TRUE; + else + loadmgr_cancel(mgr); + } + UNLOCK(&mgr->lock); + if (destroy) + loadmgr_destroy(mgr); +} + +static void +loadmgr_iattach(dns_loadmgr_t *source, dns_loadmgr_t **target) { + + REQUIRE(DNS_LMGR_VALID(source)); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&source->lock); + source->irefs++; + INSIST(source->irefs != 0); /* Overflow? */ + UNLOCK(&source->lock); + + *target = source; +} + +static void +loadmgr_idetach(dns_loadmgr_t **mgrp) { + dns_loadmgr_t *mgr; + isc_boolean_t destroy = ISC_FALSE; + + REQUIRE(mgrp != NULL); + mgr = *mgrp; + REQUIRE(DNS_LMGR_VALID(mgr)); + + mgrp = NULL; + + LOCK(&mgr->lock); + INSIST(mgr->irefs != 0); + mgr->irefs--; + if (mgr->erefs == 0 && mgr->irefs == 0) + destroy = ISC_TRUE; + UNLOCK(&mgr->lock); + if (destroy) + loadmgr_destroy(mgr); +} + +static void +loadmgr_destroy(dns_loadmgr_t *mgr) { + + INSIST(ISC_LIST_EMPTY(mgr->list)); + + mgr->magic = 0; + DESTROYLOCK(&mgr->lock); + isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr)); } |