OpenDNSSEC-signer 1.2.1
|
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 }