diff --git a/lib/dns/master.c b/lib/dns/master.c index 016fc0ef91..2933e3ee91 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -15,12 +15,14 @@ * SOFTWARE. */ - /* $Id: master.c,v 1.13 1999/03/06 04:08:29 halley Exp $ */ + /* $Id: master.c,v 1.14 1999/03/22 06:21:29 marka Exp $ */ #include #include #include +#include +#include #include #include @@ -37,6 +39,33 @@ #include #include +/* + * Grow the number of dns_rdatalist_t (RDLSZ) and dns_rdata_t (RDSZ) structures + * by these sizes when we need to. + * + * 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. + */ + +#define RDLSZ 32 +#define RDSZ 512 + +#define NBUFS 3 +#define MAXWIRESZ 255 + +/* + * Target buffer size and minimum target size. + * MINTSIZ must be big enougth to hold the largest rdata record. + * + * TSIZ >= MINTSIZ + */ +#define TSIZ (128*1024) +/* + * max message size - header - root - type - class - ttl - rdlen + */ +#define MINTSIZ (65535 - 12 - 1 - 2 - 2 - 4 - 2) + typedef ISC_LIST(dns_rdatalist_t) rdatalist_head_t; static dns_result_t commit(dns_rdatacallbacks_t *, rdatalist_head_t *, @@ -49,37 +78,39 @@ static dns_rdatalist_t *grow_rdatalist(int, dns_rdatalist_t *, int, static dns_rdata_t *grow_rdata(int, dns_rdata_t *, int, rdatalist_head_t *, rdatalist_head_t *, isc_mem_t *); +static isc_boolean_t bind_ttl(char *s, isc_uint32_t *ttl); +static isc_boolean_t on_list(dns_rdatalist_t *this, dns_rdata_t *rdata); #define GETTOKEN(lexer, options, token, eol) \ do { \ - unsigned int __o; \ - isc_result_t __r; \ - isc_token_t *__t = (token); \ - __o = (options) | ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | \ + unsigned int isc_o; \ + isc_result_t isc_r; \ + isc_token_t *isc_t = (token); \ + isc_o = (options) | ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | \ ISC_LEXOPT_DNSMULTILINE; \ - if ((__r = isc_lex_gettoken(lexer, __o, __t)) \ - != ISC_R_SUCCESS) { \ - switch (__r) { \ + isc_r = isc_lex_gettoken(lexer, isc_o, isc_t); \ + if (isc_r != ISC_R_SUCCESS) { \ + switch (isc_r) { \ case ISC_R_NOMEMORY: \ result = DNS_R_NOMEMORY; \ break; \ default: \ UNEXPECTED_ERROR(__FILE__, __LINE__, \ "isc_lex_gettoken() failed: %s\n", \ - isc_result_totext(__r)); \ + isc_result_totext(isc_r)); \ result = DNS_R_UNEXPECTED; \ goto cleanup; \ } \ goto error_cleanup; \ } \ if (eol != ISC_TRUE) \ - if (__t->type == isc_tokentype_eol || \ - __t->type == isc_tokentype_eof) { \ + if (isc_t->type == isc_tokentype_eol || \ + isc_t->type == isc_tokentype_eof) { \ (*callbacks->error)(callbacks, \ "dns_load_master: %s:%d unexpected end of %s\n", \ master_file, \ isc_lex_getsourceline(lex), \ - (__t->type == isc_tokentype_eol) ? \ + (isc_t->type == isc_tokentype_eol) ? \ "line" : "file"); \ result = DNS_R_UNEXPECTEDEND; \ goto cleanup; \ @@ -108,6 +139,7 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, isc_boolean_t finish_origin = ISC_FALSE; isc_boolean_t finish_include = ISC_FALSE; isc_boolean_t read_till_eol = ISC_FALSE; + isc_boolean_t warn_1035 = ISC_TRUE; /* XXX Arguement? */ char *include_file = NULL; isc_token_t token; isc_lex_t *lex = NULL; @@ -130,9 +162,9 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, int rdcount_save = 0; int rdata_size = 0; unsigned char *target_mem = NULL; - int target_size = 128*1024; - unsigned char name_buf[5][255]; - isc_boolean_t name_in_use[5]; + 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; @@ -157,6 +189,9 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, ISC_LIST_INIT(glue_list); ISC_LIST_INIT(current_list); + /* + * Set up lexer to process master file syntax. + */ iresult = isc_lex_create(mctx, 256, &lex); if (iresult != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -172,6 +207,9 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, isc_lex_setspecials(lex, specials); isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); + /* + * Open master file. + */ iresult = isc_lex_openfile(lex, master_file); if (iresult != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -180,6 +218,10 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, goto cleanup; } + /* + * Allocate target_size of buffer space. This is greater than twice + * the maximum individual RR data size. + */ target_mem = isc_mem_get(mctx, target_size); if (target_mem == NULL) { result = DNS_R_NOMEMORY; @@ -188,7 +230,9 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, isc_buffer_init(&target, target_mem, target_size, ISC_BUFFERTYPE_BINARY); target_save = target; - memset(name_in_use, 0, 5 * sizeof(isc_boolean_t)); + + memset(name_in_use, 0, NBUFS * sizeof(isc_boolean_t)); + do { GETTOKEN(lex, ISC_LEXOPT_INITIALWS, &token, ISC_TRUE); @@ -215,10 +259,17 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, result = DNS_R_NOOWNER; goto cleanup; } - /* still working on the same name */ + /* Still working on the same name. */ } else if (token.type == isc_tokentype_string) { - /* "$" Support */ + /* + * "$" Support. + * + * "$ORIGIN" and "$INCLUDE" can both take domain names. + * The processing of "$ORIGIN" and "$INCLUDE" extends + * across the normal domain name processing. + */ + if (strcasecmp(token.value.as_pointer, "$ORIGIN") == 0) { GETTOKEN(lex, 0, &token, ISC_FALSE); @@ -228,8 +279,16 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, "$TTL") == 0) { GETTOKEN(lex, ISC_LEXOPT_NUMBER, &token, ISC_FALSE); + if (token.type != isc_tokentype_number) { + (callbacks->warn)(callbacks, + "dns_load_master: %s:%d $TTL expects number\n", + master_file, + isc_lex_getsourceline(lex)); + result = DNS_R_BADTTL; + goto cleanup; + } ttl = token.value.as_ulong; - if (ttl > 0x7fffffff) { + if (ttl > 0x7fffffffUL) { (callbacks->warn)(callbacks, "dns_load_master: %s:%d $TTL %lu > MAXTLL, setting TTL to 0\n", master_file, @@ -273,12 +332,17 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, finish_include = ISC_TRUE; } - for (new_in_use = 0; new_in_use < 5 ; new_in_use++) + /* + * Normal processing resumes. + * + * Find a free name buffer. + */ + for (new_in_use = 0; new_in_use < NBUFS ; new_in_use++) if (!name_in_use[new_in_use]) break; - INSIST(new_in_use < 5); - isc_buffer_init(&name, &name_buf[new_in_use][0], 255, - ISC_BUFFERTYPE_BINARY); + INSIST(new_in_use < NBUFS); + isc_buffer_init(&name, &name_buf[new_in_use][0], + MAXWIRESZ, ISC_BUFFERTYPE_BINARY); dns_name_init(&new_name, NULL); isc_buffer_init(&buffer, token.value.as_region.base, token.value.as_region.length, @@ -291,6 +355,10 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, if (result != DNS_R_SUCCESS) goto error_cleanup; + + /* + * Finish $ORIGIN / $INCLUDE processing if required. + */ if (finish_origin) { if (origin_in_use != -1) name_in_use[origin_in_use] = ISC_FALSE; @@ -314,8 +382,16 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, finish_include = ISC_FALSE; continue; } + /* - * commit glue and pop stacks + * "$" Processing Finished + */ + + /* + * If we are processing glue and the new name does + * not match the current glue name, commit the glue + * and pop stacks leaving us in 'normal' processing + * state. */ if (in_glue && dns_name_compare(&glue_name, &new_name) != 0) { @@ -333,6 +409,13 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, target = target_save; } + /* + * If we are in 'normal' processing state and the new + * name does not match the current name, see if the + * new name is for glue and treat it as such, + * 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 (current_has_delegation && @@ -375,14 +458,29 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, goto cleanup; } + /* + * Find TTL, class and type. Both TTL and class are optional + * and may occur in any order if they exist. TTL and class + * come before type which must exist. + * + * [] [] + * [] [] + */ + type = 0; rdclass = 0; GETTOKEN(lex, ISC_LEXOPT_NUMBER, &token, ISC_FALSE); + if (token.type == isc_tokentype_string && + dns_rdataclass_fromtext(&rdclass, + &token.value.as_textregion) + == DNS_R_SUCCESS) + GETTOKEN(lex, ISC_LEXOPT_NUMBER, &token, ISC_FALSE); + if (token.type == isc_tokentype_number) { ttl = token.value.as_ulong; - if (ttl > 0x7fffffff) { + if (ttl > 0x7fffffffUL) { (callbacks->warn)(callbacks, "dns_load_master: %s:%d TTL %lu > maxtll, setting ttl to 0\n", master_file, @@ -392,15 +490,28 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, } ttl_known = ISC_TRUE; GETTOKEN(lex, 0, &token, ISC_FALSE); + } else if (bind_ttl(token.value.as_pointer, &ttl)) { + ttl_known = ISC_TRUE; + GETTOKEN(lex, 0, &token, ISC_FALSE); } else if (!ttl_known && !default_ttl_known) { + /* + * BIND 4 / 8 'USE_SOA_MINIMUM' could be set here. + */ (*callbacks->error)(callbacks, "%s: %s:%d no TTL specified\n", "dns_load_master", master_file, isc_lex_getsourceline(lex)); result = DNS_R_NOTTL; goto cleanup; - } else if (default_ttl_known) + } else if (default_ttl_known) { ttl = default_ttl; + } else if (warn_1035) { + (*callbacks->warn)(callbacks, + "%s: %s:%d using RFC 1035 TTL semantics\n", + "dns_load_master", master_file, + isc_lex_getsourceline(lex)); + warn_1035 = ISC_FALSE; + } if (token.type != isc_tokentype_string) { UNEXPECTED_ERROR(__FILE__, __LINE__, @@ -409,8 +520,8 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, goto cleanup; } - if (dns_rdataclass_fromtext(&rdclass, - &token.value.as_textregion) + if (rdclass == 0 && + dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion) == DNS_R_SUCCESS) GETTOKEN(lex, 0, &token, ISC_FALSE); @@ -420,12 +531,16 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, result = DNS_R_UNEXPECTED; goto cleanup; } - + result = dns_rdatatype_fromtext(&type, &token.value.as_textregion); if (result != DNS_R_SUCCESS) goto 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]; @@ -466,19 +581,14 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, goto cleanup; } - if (!in_glue && type == dns_rdatatype_soa && - dns_name_compare(top, ¤t_name) == 0) { - (*soacount)++; - } - - if (!in_glue && type == dns_rdatatype_ns && - dns_name_compare(top, ¤t_name) == 0) { - (*nscount)++; - } - if (type == dns_rdatatype_ns && !in_glue) current_has_delegation = ISC_TRUE; + /* + * Find type in rdatalist. + * If it does not exit create new one and prepend to list + * as this will mimimise list traversal. + */ if (in_glue) this = ISC_LIST_HEAD(glue_list); else @@ -489,10 +599,11 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, break; this = ISC_LIST_NEXT(this, link); } + if (this == NULL) { if (rdlcount == rdatalist_size) { new_rdatalist = - grow_rdatalist(rdatalist_size + 32, + grow_rdatalist(rdatalist_size + RDLSZ, rdatalist, rdatalist_size, ¤t_list, @@ -503,7 +614,7 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, goto error_cleanup; } rdatalist = new_rdatalist; - rdatalist_size += 32; + rdatalist_size += RDLSZ; } this = &rdatalist[rdlcount++]; this->type = type; @@ -524,26 +635,49 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, ttl = this->ttl; } + /* + * Find a rdata structure. + */ if (rdcount == rdata_size) { - new_rdata = grow_rdata(rdata_size + 512, rdata, + new_rdata = grow_rdata(rdata_size + RDSZ, rdata, rdata_size, ¤t_list, &glue_list, mctx); if (new_rdata == NULL) { result = DNS_R_NOMEMORY; goto error_cleanup; } - rdata_size += 512; + rdata_size += RDSZ; rdata = new_rdata; } + + /* + * Read rdata contents. + */ result = dns_rdata_fromtext(&rdata[rdcount], rdclass, type, lex, &origin_name, ISC_FALSE, &target, callbacks); if (result != DNS_R_SUCCESS) goto cleanup; - ISC_LIST_PREPEND(this->rdata, &rdata[rdcount], link); - rdcount++; - /* We must have at least 64k as rdlen is 16 bits. */ - if (target.used > (64*1024)) { + + /* + * 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_PREPEND(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, ¤t_list, ¤t_name); if (result != DNS_R_SUCCESS) @@ -562,6 +696,9 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, ISC_BUFFERTYPE_BINARY); } } while (!done); + /* + * Commit what has not yet been committed. + */ result = commit(callbacks, ¤t_list, ¤t_name); if (result != DNS_R_SUCCESS) goto cleanup; @@ -597,6 +734,10 @@ dns_master_load(char *master_file, dns_name_t *top, dns_name_t *origin, return (result); } +/* + * Grow the slab of dns_rdatalist_t structures. + * Re-link glue and current list. + */ static dns_rdatalist_t * grow_rdatalist(int new_len, dns_rdatalist_t *old, int old_len, rdatalist_head_t *current, rdatalist_head_t *glue, @@ -643,6 +784,10 @@ grow_rdatalist(int new_len, dns_rdatalist_t *old, int old_len, return (new); } +/* + * Grow the slab of rdata structs. + * Re-link the current and glue chains. + */ static dns_rdata_t * grow_rdata(int new_len, dns_rdata_t *old, int old_len, rdatalist_head_t *current, rdatalist_head_t *glue, @@ -658,7 +803,10 @@ grow_rdata(int new_len, dns_rdata_t *old, int old_len, if (new == NULL) return (NULL); memset(new, 0, new_len * sizeof *new); - /* copy current relinking */ + + /* + * Copy current relinking. + */ this = ISC_LIST_HEAD(*current); while (this != NULL) { ISC_LIST_INIT(save); @@ -674,7 +822,10 @@ grow_rdata(int new_len, dns_rdata_t *old, int old_len, } this = ISC_LIST_NEXT(this, link); } - /* copy glue relinking */ + + /* + * Copy glue relinking. + */ this = ISC_LIST_HEAD(*glue); while (this != NULL) { ISC_LIST_INIT(save); @@ -696,6 +847,11 @@ grow_rdata(int new_len, dns_rdata_t *old, int old_len, return (new); } +/* + * Convert each element from a rdatalist_t to rdataset then call commit. + * Unlink each element as we go. + */ + static dns_result_t commit(dns_rdatacallbacks_t *callbacks, rdatalist_head_t *head, dns_name_t *owner) @@ -716,6 +872,10 @@ commit(dns_rdatacallbacks_t *callbacks, rdatalist_head_t *head, return (DNS_R_SUCCESS); } +/* + * Returns ISC_TRUE if one of the NS rdata's contains 'owner'. + */ + static isc_boolean_t is_glue(rdatalist_head_t *head, dns_name_t *owner) { dns_rdatalist_t *this; @@ -744,3 +904,71 @@ is_glue(rdatalist_head_t *head, dns_name_t *owner) { } return (ISC_FALSE); } + +/* + * Convert BIND 8.x ttl representation to a number. + * Returns ISC_TRUE is string contained a valid string. + */ + +static isc_boolean_t +bind_ttl(char *s, isc_uint32_t *ttl) { + isc_uint32_t tmp = 0; + unsigned long n; + char *e; + + do { + n = strtoul(s, &e, 10); + if (s == e) + return (ISC_FALSE); + switch (*e) { + case 'w': + case 'W': + tmp += n * 7 * 24 * 3600; + s = e + 1; + break; + case 'd': + case 'D': + tmp += n * 24 * 3600; + s = e + 1; + break; + case 'h': + case 'H': + tmp += n * 3600; + s = e + 1; + break; + case 'm': + case 'M': + tmp += n * 60; + s = e + 1; + break; + case 's': + case 'S': + tmp += n * 60; + s = e + 1; + break; + default: + return (ISC_FALSE); + } + } while (*s != 0); + if (tmp > 0x7fffffffUL) + tmp = 0; + *ttl = tmp; + return (ISC_TRUE); +} + +/* + * Returns ISC_TRUE if the 'rdata' is already on 'rdatalist'. + */ + +static isc_boolean_t +on_list(dns_rdatalist_t *rdatalist, dns_rdata_t *rdata) { + dns_rdata_t *rdata2; + + 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); + } + return (ISC_FALSE); +}