OpenDNSSEC-signer 1.2.1

/build/buildd-opendnssec_1.2.1.dfsg-1-mips-p9AT07/opendnssec-1.2.1.dfsg/signer/src/adapter/adfile.c

Go to the documentation of this file.
00001 /*
00002  * $Id: adfile.c 4294 2011-01-13 19:58:29Z jakob $
00003  *
00004  * Copyright (c) 2009 NLNet Labs. All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00016  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00017  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00019  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00020  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00021  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00023  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00024  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
00025  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026  *
00027  */
00028 
00034 #include "adapter/adapter.h"
00035 #include "adapter/adfile.h"
00036 #include "config.h"
00037 #include "signer/zone.h"
00038 #include "signer/zonedata.h"
00039 #include "util/duration.h"
00040 #include "util/file.h"
00041 #include "util/log.h"
00042 #include "util/se_malloc.h"
00043 #include "util/util.h"
00044 
00045 #include <ldns/ldns.h> /* ldns_*() */
00046 #include <stdio.h> /* rewind() */
00047 
00048 static int adfile_read_file(FILE* fd, struct zone_struct* zone, int include,
00049     int recover);
00050 
00051 
00056 static int
00057 line_contains_space_only(char* line, int line_len)
00058 {
00059     int i;
00060     for (i = 0; i < line_len; i++) {
00061         if (!isspace((int)line[i])) {
00062             return 0;
00063         }
00064     }
00065     return 1;
00066 }
00067 
00068 
00069 /*
00070  * Trim trailing whitespace.
00071  *
00072  */
00073 static void
00074 adfile_rtrim(char* line, int* line_len)
00075 {
00076     int i = strlen(line), nl = 0;
00077     int trimmed = 0;
00078 
00079     while (i>0) {
00080         --i;
00081         if (line[i] == '\n') {
00082             nl = 1;
00083         }
00084         if (line[i] == ' ' || line[i] == '\t' || line[i] == '\n') {
00085             line[i] = '\0';
00086             trimmed++;
00087         } else {
00088             break;
00089         }
00090     }
00091     if (nl) {
00092         line[++i] = '\n';
00093     }
00094     *line_len -= trimmed;
00095     return;
00096 }
00097 
00098 
00103 static int
00104 adfile_read_line(FILE* fd, char* line, unsigned int* l)
00105 {
00106     int i = 0;
00107     int li = 0;
00108     int in_string = 0;
00109     int depth = 0;
00110     int comments = 0;
00111     char c = 0;
00112     char lc = 0;
00113 
00114     for (i = 0; i < SE_ADFILE_MAXLINE; i++) {
00115         c = (char) se_fgetc(fd, l);
00116         if (comments) {
00117             while (c != EOF && c != '\n') {
00118                 c = (char) se_fgetc(fd, l);
00119             }
00120         }
00121 
00122         if (c == EOF) {
00123             if (depth != 0) {
00124                 se_log_error("read line: bracket mismatch discovered at "
00125                     "line %i, missing ')'", l&&*l?*l:0);
00126             }
00127             if (li > 0) {
00128                 line[li] = '\0';
00129                 return li;
00130             } else {
00131                 return -1;
00132             }
00133         } else if (c == '"' && lc != '\\') {
00134             in_string = 1 - in_string; /* swap status */
00135             line[li] = c;
00136             li++;
00137         } else if (c == '(') {
00138             if (in_string) {
00139                 line[li] = c;
00140                 li++;
00141             } else if (lc != '\\') {
00142                 depth++;
00143                 line[li] = ' ';
00144                 li++;
00145             } else {
00146                 line[li] = c;
00147                 li++;
00148             }
00149         } else if (c == ')') {
00150             if (in_string) {
00151                 line[li] = c;
00152                 li++;
00153             } else if (lc != '\\') {
00154                 if (depth < 1) {
00155                     se_log_error("read line: bracket mismatch discovered at "
00156                         "line %i, missing '('", l&&*l?*l:0);
00157                     line[li] = '\0';
00158                     return li;
00159                 }
00160                 depth--;
00161                 line[li] = ' ';
00162                 li++;
00163             } else {
00164                 line[li] = c;
00165                 li++;
00166             }
00167         } else if (c == ';') {
00168             if (in_string) {
00169                 line[li] = c;
00170                 li++;
00171             } else if (lc != '\\') {
00172                 comments = 1;
00173             } else {
00174                 line[li] = c;
00175                 li++;
00176             }
00177         } else if (c == '\n' && lc != '\\') {
00178             comments = 0;
00179             /* if no depth issue, we are done */
00180             if (depth == 0) {
00181                 break;
00182             }
00183             line[li] = ' ';
00184             li++;
00185         } else {
00186             line[li] = c;
00187             li++;
00188         }
00189         /* continue with line */
00190         lc = c;
00191     }
00192 
00193     /* done */
00194     if (depth != 0) {
00195         se_log_error("read line: bracket mismatch discovered at line %i, "
00196             "missing ')'", l&&*l?*l:0);
00197         return li;
00198     }
00199     line[li] = '\0';
00200     return li;
00201 }
00202 
00203 
00208 static ldns_rr*
00209 adfile_lookup_soa_rr(FILE* fd)
00210 {
00211     ldns_rr *cur_rr = NULL;
00212     char line[SE_ADFILE_MAXLINE];
00213     ldns_status status = LDNS_STATUS_OK;
00214     int line_len = 0;
00215     unsigned int l = 0;
00216 
00217     while (line_len >= 0) {
00218         line_len = adfile_read_line(fd, (char*) line, &l);
00219         adfile_rtrim(line, &line_len);
00220 
00221         if (line_len > 0) {
00222             if (line[0] != ';') {
00223                 status = ldns_rr_new_frm_str(&cur_rr, line, 0, NULL, NULL);
00224                 if (status == LDNS_STATUS_OK) {
00225                     if (ldns_rr_get_type(cur_rr) == LDNS_RR_TYPE_SOA) {
00226                         return cur_rr;
00227                     } else {
00228                         ldns_rr_free(cur_rr);
00229                         cur_rr = NULL;
00230                     }
00231                 }
00232             }
00233         }
00234     }
00235     return NULL;
00236 }
00237 
00238 
00243 static ldns_rr*
00244 adfile_read_rr(FILE* fd, zone_type* zone_in, char* line, ldns_rdf** orig,
00245     ldns_rdf** prev, uint32_t* ttl, ldns_status* status, unsigned int* l,
00246     int recover)
00247 {
00248     ldns_rr* rr = NULL;
00249     ldns_rdf* tmp = NULL;
00250     FILE* fd_include = NULL;
00251     int len = 0, error = 0;
00252     uint32_t new_ttl = 0;
00253     const char *endptr;  /* unused */
00254     int offset = 0;
00255 
00256 adfile_read_line:
00257     if (ttl) {
00258         new_ttl = *ttl;
00259     }
00260 
00261     len = adfile_read_line(fd, line, l);
00262     adfile_rtrim(line, &len);
00263 
00264     if (len >= 0) {
00265         switch (line[0]) {
00266             /* directive */
00267             case '$':
00268                 if (strncmp(line, "$ORIGIN", 7) == 0 && isspace(line[7])) {
00269                     /* copy from ldns */
00270                     if (*orig) {
00271                         ldns_rdf_deep_free(*orig);
00272                         *orig = NULL;
00273                     }
00274                     offset = 8;
00275                     while (isspace(line[offset])) {
00276                         offset++;
00277                     }
00278                     tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_DNAME, line + offset);
00279                     if (!tmp) {
00280                         /* could not parse what next to $ORIGIN */
00281                         *status = LDNS_STATUS_SYNTAX_DNAME_ERR;
00282                         return NULL;
00283                     }
00284                     *orig = tmp;
00285                     /* end copy from ldns */
00286                     goto adfile_read_line; /* perhaps next line is rr */
00287                     break;
00288                 } else if (strncmp(line, "$TTL", 4) == 0 &&
00289                     isspace(line[4])) {
00290                     /* override default ttl */
00291                     offset = 5;
00292                     while (isspace(line[offset])) {
00293                         offset++;
00294                     }
00295                     if (ttl) {
00296                         *ttl = ldns_str2period(line + offset, &endptr);
00297                         new_ttl = *ttl;
00298                     }
00299                     goto adfile_read_line; /* perhaps next line is rr */
00300                     break;
00301                 } else if (strncmp(line, "$INCLUDE", 8) == 0 &&
00302                     isspace(line[8])) {
00303                     /* dive into this file */
00304                     offset = 9;
00305                     while (isspace(line[offset])) {
00306                         offset++;
00307                     }
00308                     fd_include = se_fopen(line + offset, NULL, "r");
00309                     if (fd_include) {
00310                         error = adfile_read_file(fd_include, zone_in, 1,
00311                             recover);
00312                         se_fclose(fd_include);
00313                     } else {
00314                         se_log_error("unable to open include file %s",
00315                             (line+offset)?(line+offset):"(null)");
00316                         *status = LDNS_STATUS_SYNTAX_ERR;
00317                         return NULL;
00318                     }
00319                     if (error) {
00320                         *status = LDNS_STATUS_ERR;
00321                         se_log_error("error in include file %s",
00322                             (line+offset)?(line+offset):"(null)");
00323                         return NULL;
00324                     }
00325                     /* restore current ttl */
00326                     *ttl = new_ttl;
00327                     goto adfile_read_line; /* perhaps next line is rr */
00328                     break;
00329                 }
00330 
00331                 goto adfile_read_rr; /* this can be an owner name */
00332                 break;
00333             /* comments, empty lines */
00334             case ';':
00335             case '\n':
00336                 goto adfile_read_line; /* perhaps next line is rr */
00337                 break;
00338             /* let's hope its a RR */
00339             default:
00340 adfile_read_rr:
00341                 if (line_contains_space_only(line, len)) {
00342                     goto adfile_read_line; /* perhaps next line is rr */
00343                     break;
00344                 }
00345 
00346                 *status = ldns_rr_new_frm_str(&rr, line, new_ttl, *orig, prev);
00347                 if (*status == LDNS_STATUS_OK) {
00348                     ldns_rr2canonical(rr); /* TODO: canonicalize or not? */
00349                     return rr;
00350                 } else if (*status == LDNS_STATUS_SYNTAX_EMPTY) {
00351                     if (rr) {
00352                         ldns_rr_free(rr);
00353                         rr = NULL;
00354                     }
00355                     *status = LDNS_STATUS_OK;
00356                     goto adfile_read_line; /* perhaps next line is rr */
00357                     break;
00358                 } else {
00359                     se_log_error("error parsing RR at line %i (%s): %s",
00360                         l&&*l?*l:0, ldns_get_errorstr_by_id(*status), line);
00361                     while (len >= 0) {
00362                         len = adfile_read_line(fd, line, l);
00363                     }
00364                     if (rr) {
00365                         ldns_rr_free(rr);
00366                         rr = NULL;
00367                     }
00368                     return NULL;
00369                 }
00370                 break;
00371         }
00372     }
00373 
00374     /* -1, EOF */
00375     *status = LDNS_STATUS_OK;
00376     return NULL;
00377 }
00378 
00379 
00384 static int
00385 adfile_read_file(FILE* fd, struct zone_struct* zone, int include, int recover)
00386 {
00387     int result = 0;
00388     uint32_t soa_min = 0;
00389     zone_type* zone_in = zone;
00390     ldns_rr* rr = NULL;
00391     ldns_rdf* prev = NULL;
00392     ldns_rdf* orig = NULL;
00393     ldns_status status = LDNS_STATUS_OK;
00394     char line[SE_ADFILE_MAXLINE];
00395     unsigned int line_update_interval = 100000;
00396     unsigned int line_update = line_update_interval;
00397     unsigned int l = 0;
00398 
00399     se_log_assert(fd);
00400     se_log_assert(zone_in);
00401     se_log_assert(zone_in->stats);
00402 
00403     if (!include) {
00404         rr = adfile_lookup_soa_rr(fd);
00405         /* default TTL: taking the SOA MINIMUM is in conflict with RFC2308 */
00406         if (zone_in->signconf->soa_min) {
00407             soa_min = (uint32_t) duration2time(zone_in->signconf->soa_min);
00408         } else if (rr) {
00409             soa_min = ldns_rdf2native_int32(ldns_rr_rdf(rr,
00410                 SE_SOA_RDATA_MINIMUM));
00411         }
00412         zone_in->zonedata->default_ttl = soa_min;
00413         /* serial */
00414         if (rr) {
00415             zone_in->zonedata->inbound_serial =
00416                 ldns_rdf2native_int32(ldns_rr_rdf(rr, SE_SOA_RDATA_SERIAL));
00417             ldns_rr_free(rr);
00418         }
00419         rewind(fd);
00420 
00421         if (se_strcmp(zone_in->signconf->soa_serial, "keep") == 0) {
00422             if (zone_in->zonedata->inbound_serial <=
00423                 zone_in->zonedata->outbound_serial) {
00424                 se_log_error("cannot read zone %s: SOA SERIAL is set to keep "
00425                     "but serial %u in input zone is not incremental",
00426                     zone_in->name?zone_in->name:"(null)",
00427                     zone_in->zonedata->inbound_serial);
00428                 return 1;
00429             }
00430         }
00431     }
00432 
00433     /* $ORIGIN <zone name> */
00434     orig = ldns_rdf_clone(zone_in->dname);
00435 
00436     /* read records */
00437     while ((rr = adfile_read_rr(fd, zone_in, line, &orig, &prev,
00438         &(zone_in->zonedata->default_ttl), &status, &l, recover)) != NULL) {
00439 
00440         if (status != LDNS_STATUS_OK) {
00441             se_log_error("error reading RR at line %i (%s): %s", l,
00442                 ldns_get_errorstr_by_id(status), line);
00443             result = 1;
00444             break;
00445         }
00446 
00447         if (l > line_update) {
00448             se_log_debug("...at line %i: %s", l, line);
00449             line_update += line_update_interval;
00450         }
00451 
00452         /* filter out DNSSEC RRs (except DNSKEY) from the Input File Adapter */
00453         if (util_is_dnssec_rr(rr)) {
00454             ldns_rr_free(rr);
00455             rr = NULL;
00456             continue;
00457         }
00458 
00459         /* add to the zonedata */
00460         result = zone_add_rr(zone_in, rr, recover);
00461         if (result != 0) {
00462             se_log_error("error adding RR at line %i: %s", l, line);
00463             break;
00464         }
00465         zone_in->stats->sort_count += 1;
00466     }
00467 
00468     /* and done */
00469     if (orig) {
00470         ldns_rdf_deep_free(orig);
00471         orig = NULL;
00472     }
00473     if (prev) {
00474         ldns_rdf_deep_free(prev);
00475         prev = NULL;
00476     }
00477 
00478     if (!result && status != LDNS_STATUS_OK) {
00479         se_log_error("error reading RR at line %i (%s): %s", l,
00480             ldns_get_errorstr_by_id(status), line);
00481         result = 1;
00482     }
00483 
00484     /* reset the default ttl (directives only affect the zone file) */
00485     zone_in->zonedata->default_ttl = soa_min;
00486 
00487     return result;
00488 }
00489 
00490 
00495 int
00496 adfile_read(struct zone_struct* zone, const char* filename, int recover)
00497 {
00498     FILE* fd = NULL;
00499     zone_type* zone_in = zone;
00500     int error = 0;
00501 
00502     se_log_assert(zone_in);
00503     se_log_assert(zone_in->name);
00504     se_log_assert(filename);
00505     se_log_debug("read zone %s from file %s",
00506         zone_in->name?zone_in->name:"(null)", filename?filename:"(null)");
00507 
00508     /* read the zonefile */
00509     fd = se_fopen(filename, NULL, "r");
00510     if (fd) {
00511         if (recover) {
00512             error = adfile_read_file(fd, zone_in, 1, 1);
00513         } else {
00514             error = adfile_read_file(fd, zone_in, 0, 0);
00515         }
00516         se_fclose(fd);
00517     } else {
00518         error = 1;
00519     }
00520     if (error) {
00521         se_log_error("error reading zone %s from file %s",
00522             zone_in->name?zone_in->name:"(null)",
00523             filename?filename:"(null)");
00524         return error;
00525     }
00526 
00527     if (!recover) {
00528         /* remove current rrs */
00529         error = zonedata_del_rrs(zone_in->zonedata);
00530         if (error) {
00531             se_log_error("error removing current RRs in zone %s",
00532                 zone_in->name?zone_in->name:"(null)");
00533         }
00534     }
00535     return error;
00536 }
00537 
00538 
00543 int
00544 adfile_write(struct zone_struct* zone, const char* filename)
00545 {
00546     FILE* fd = NULL;
00547     zone_type* zone_out = zone;
00548 
00549     se_log_assert(zone_out);
00550     se_log_assert(zone_out->name);
00551 
00552     if (filename) {
00553         se_log_debug("write zone %s to file %s",
00554             zone_out->name, filename);
00555         fd = se_fopen(filename, NULL, "w");
00556     } else {
00557         se_log_assert(zone_out->outbound_adapter);
00558         se_log_debug("write zone %s to output file adapter %s",
00559             zone_out->name,
00560             zone_out->outbound_adapter->filename?zone_out->outbound_adapter->filename:"(null)");
00561         fd = se_fopen(zone_out->outbound_adapter->filename, NULL, "w");
00562     }
00563     if (fd) {
00564         zone_print(fd, zone_out);
00565         se_fclose(fd);
00566     }
00567     return 0;
00568 }