/* Copyright (C) 2022 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "knot/dnssec/zone-nsec.h" #include "knot/zone/zone-dump.h" #include "libknot/libknot.h" /*! \brief Size of auxiliary buffer. */ #define DUMP_BUF_LEN (70 * 1024) /*! \brief Dump parameters. */ typedef struct { FILE *file; char *buf; size_t buflen; uint64_t rr_count; bool dump_rrsig; bool dump_nsec; const knot_dname_t *origin; const knot_dump_style_t *style; const char *first_comment; } dump_params_t; static int apex_node_dump_text(zone_node_t *node, dump_params_t *params) { knot_rrset_t soa = node_rrset(node, KNOT_RRTYPE_SOA); // Dump SOA record as a first. if (!params->dump_nsec) { int ret = knot_rrset_txt_dump(&soa, ¶ms->buf, ¶ms->buflen, params->style); if (ret < 0) { return ret; } params->rr_count += soa.rrs.count; fprintf(params->file, "%s", params->buf); params->buf[0] = '\0'; } // Dump other records. for (uint16_t i = 0; i < node->rrset_count; i++) { knot_rrset_t rrset = node_rrset_at(node, i); switch (rrset.type) { case KNOT_RRTYPE_NSEC: continue; case KNOT_RRTYPE_RRSIG: continue; case KNOT_RRTYPE_SOA: continue; default: break; } int ret = knot_rrset_txt_dump(&rrset, ¶ms->buf, ¶ms->buflen, params->style); if (ret < 0) { return ret; } params->rr_count += rrset.rrs.count; fprintf(params->file, "%s", params->buf); params->buf[0] = '\0'; } return KNOT_EOK; } static int node_dump_text(zone_node_t *node, void *data) { dump_params_t *params = (dump_params_t *)data; // Zone apex rrsets. if (node->owner == params->origin && !params->dump_rrsig && !params->dump_nsec) { apex_node_dump_text(node, params); return KNOT_EOK; } // Dump non-apex rrsets. for (uint16_t i = 0; i < node->rrset_count; i++) { knot_rrset_t rrset = node_rrset_at(node, i); switch (rrset.type) { case KNOT_RRTYPE_RRSIG: if (params->dump_rrsig) { break; } continue; case KNOT_RRTYPE_NSEC: if (params->dump_nsec) { break; } continue; case KNOT_RRTYPE_NSEC3: if (params->dump_nsec) { break; } continue; default: if (params->dump_nsec || params->dump_rrsig) { continue; } break; } // Dump block comment if available. if (params->first_comment != NULL) { fprintf(params->file, "%s", params->first_comment); params->first_comment = NULL; } int ret = knot_rrset_txt_dump(&rrset, ¶ms->buf, ¶ms->buflen, params->style); if (ret < 0) { return ret; } params->rr_count += rrset.rrs.count; fprintf(params->file, "%s", params->buf); params->buf[0] = '\0'; } return KNOT_EOK; } int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments, const char *color) { if (file == NULL) { return KNOT_EINVAL; } if (zone == NULL) { return KNOT_EEMPTYZONE; } // Allocate auxiliary buffer for dumping operations. char *buf = malloc(DUMP_BUF_LEN); if (buf == NULL) { return KNOT_ENOMEM; } if (comments) { fprintf(file, ";; Zone dump (Knot DNS %s)\n", PACKAGE_VERSION); } // Set structure with parameters. knot_dump_style_t style = KNOT_DUMP_STYLE_DEFAULT; style.color = color; style.now = knot_time(); dump_params_t params = { .file = file, .buf = buf, .buflen = DUMP_BUF_LEN, .rr_count = 0, .origin = zone->apex->owner, .style = &style, .dump_rrsig = false, .dump_nsec = false }; // Dump standard zone records without RRSIGS. int ret = zone_contents_apply(zone, node_dump_text, ¶ms); if (ret != KNOT_EOK) { free(params.buf); return ret; } // Dump RRSIG records if available. params.dump_rrsig = true; params.dump_nsec = false; params.first_comment = comments ? ";; DNSSEC signatures\n" : NULL; ret = zone_contents_apply(zone, node_dump_text, ¶ms); if (ret != KNOT_EOK) { free(params.buf); return ret; } // Dump NSEC chain if available. params.dump_rrsig = false; params.dump_nsec = true; params.first_comment = comments ? ";; DNSSEC NSEC chain\n" : NULL; ret = zone_contents_apply(zone, node_dump_text, ¶ms); if (ret != KNOT_EOK) { free(params.buf); return ret; } // Dump NSEC3 chain if available. params.dump_rrsig = false; params.dump_nsec = true; params.first_comment = comments ? ";; DNSSEC NSEC3 chain\n" : NULL; ret = zone_contents_nsec3_apply(zone, node_dump_text, ¶ms); if (ret != KNOT_EOK) { free(params.buf); return ret; } params.dump_rrsig = true; params.dump_nsec = false; params.first_comment = comments ? ";; DNSSEC NSEC3 signatures\n" : NULL; ret = zone_contents_nsec3_apply(zone, node_dump_text, ¶ms); if (ret != KNOT_EOK) { free(params.buf); return ret; } if (comments) { // Create formatted date-time string. time_t now = time(NULL); struct tm tm; localtime_r(&now, &tm); char date[64]; strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm); // Dump trailing statistics. fprintf(file, ";; Written %"PRIu64" records\n" ";; Time %s\n", params.rr_count, date); } free(params.buf); // params.buf may be != buf because of knot_rrset_txt_dump_dynamic() return KNOT_EOK; }