"Fossies" - the Fresh Open Source Software Archive

Member "nsd-4.3.6/dbcreate.c" (6 Apr 2021, 11325 Bytes) of package /linux/misc/dns/nsd-4.3.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "dbcreate.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 4.3.5_vs_4.3.6.

    1 /*
    2  * dbcreate.c -- routines to create an nsd(8) name database
    3  *
    4  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
    5  *
    6  * See LICENSE for the license.
    7  *
    8  */
    9 
   10 #include "config.h"
   11 
   12 #include <sys/stat.h>
   13 #include <sys/types.h>
   14 #include <errno.h>
   15 #include <fcntl.h>
   16 #include <stdlib.h>
   17 #include <string.h>
   18 #include <unistd.h>
   19 
   20 #include "namedb.h"
   21 #include "udb.h"
   22 #include "udbradtree.h"
   23 #include "udbzone.h"
   24 #include "options.h"
   25 #include "nsd.h"
   26 
   27 /* pathname directory separator character */
   28 #define PATHSEP '/'
   29 
   30 /** add an rdata (uncompressed) to the destination */
   31 static size_t
   32 add_rdata(rr_type* rr, unsigned i, uint8_t* buf, size_t buflen)
   33 {
   34     switch(rdata_atom_wireformat_type(rr->type, i)) {
   35         case RDATA_WF_COMPRESSED_DNAME:
   36         case RDATA_WF_UNCOMPRESSED_DNAME:
   37         {
   38             const dname_type* dname = domain_dname(
   39                 rdata_atom_domain(rr->rdatas[i]));
   40             if(dname->name_size > buflen)
   41                 return 0;
   42             memmove(buf, dname_name(dname), dname->name_size);
   43             return dname->name_size;
   44         }
   45         default:
   46             break;
   47     }
   48     if(rdata_atom_size(rr->rdatas[i]) > buflen)
   49         return 0;
   50     memmove(buf, rdata_atom_data(rr->rdatas[i]),
   51         rdata_atom_size(rr->rdatas[i]));
   52     return rdata_atom_size(rr->rdatas[i]);
   53 }
   54 
   55 /* marshal rdata into buffer, must be MAX_RDLENGTH in size */
   56 size_t
   57 rr_marshal_rdata(rr_type* rr, uint8_t* rdata, size_t sz)
   58 {
   59     size_t len = 0;
   60     unsigned i;
   61     assert(rr);
   62     for(i=0; i<rr->rdata_count; i++) {
   63         len += add_rdata(rr, i, rdata+len, sz-len);
   64     }
   65     return len;
   66 }
   67 
   68 /** delete an RR */
   69 void
   70 udb_del_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
   71 {
   72     /* marshal the rdata (uncompressed) into a buffer */
   73     uint8_t rdata[MAX_RDLENGTH];
   74     size_t rdatalen = rr_marshal_rdata(rr, rdata, sizeof(rdata));
   75     assert(udb);
   76     udb_zone_del_rr(udb, z, dname_name(domain_dname(rr->owner)),
   77         domain_dname(rr->owner)->name_size, rr->type, rr->klass,
   78         rdata, rdatalen);
   79 }
   80 
   81 /** write rr */
   82 int
   83 udb_write_rr(udb_base* udb, udb_ptr* z, rr_type* rr)
   84 {
   85     /* marshal the rdata (uncompressed) into a buffer */
   86     uint8_t rdata[MAX_RDLENGTH];
   87     size_t rdatalen = 0;
   88     unsigned i;
   89     assert(rr);
   90     for(i=0; i<rr->rdata_count; i++) {
   91         rdatalen += add_rdata(rr, i, rdata+rdatalen,
   92             sizeof(rdata)-rdatalen);
   93     }
   94     assert(udb);
   95     return udb_zone_add_rr(udb, z, dname_name(domain_dname(rr->owner)),
   96         domain_dname(rr->owner)->name_size, rr->type, rr->klass,
   97         rr->ttl, rdata, rdatalen);
   98 }
   99 
  100 /** write rrset */
  101 static int
  102 write_rrset(udb_base* udb, udb_ptr* z, rrset_type* rrset)
  103 {
  104     unsigned i;
  105     for(i=0; i<rrset->rr_count; i++) {
  106         if(!udb_write_rr(udb, z, &rrset->rrs[i]))
  107             return 0;
  108     }
  109     return 1;
  110 }
  111 
  112 /** write a zone */
  113 static int
  114 write_zone(udb_base* udb, udb_ptr* z, zone_type* zone)
  115 {
  116     /* write all domains in the zone */
  117     domain_type* walk;
  118     rrset_type* rrset;
  119     unsigned long n = 0, c = 0;
  120     time_t t = time(NULL);
  121 
  122     /* count domains: for pct logging */
  123     for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
  124         walk=domain_next(walk)) {
  125         n++;
  126     }
  127     /* write them */
  128     for(walk=zone->apex; walk && domain_is_subdomain(walk, zone->apex);
  129         walk=domain_next(walk)) {
  130         /* write all rrsets (in the zone) for this domain */
  131         for(rrset=walk->rrsets; rrset; rrset=rrset->next) {
  132             if(rrset->zone == zone) {
  133                 if(!write_rrset(udb, z, rrset))
  134                     return 0;
  135             }
  136         }
  137         /* only check every ... domains, and print pct */
  138         if(++c % ZONEC_PCT_COUNT == 0 && time(NULL) > t + ZONEC_PCT_TIME) {
  139             t = time(NULL);
  140             VERBOSITY(1, (LOG_INFO, "write %s %d %%",
  141                 zone->opts->name, (int)(c*((unsigned long)100)/n)));
  142         }
  143     }
  144     return 1;
  145 }
  146 
  147 /** create and write a zone */
  148 int
  149 write_zone_to_udb(udb_base* udb, zone_type* zone, struct timespec* mtime,
  150     const char* file_str)
  151 {
  152     udb_ptr z;
  153     /* make udb dirty */
  154     udb_base_set_userflags(udb, 1);
  155     /* find or create zone */
  156     if(udb_zone_search(udb, &z, dname_name(domain_dname(zone->apex)),
  157         domain_dname(zone->apex)->name_size)) {
  158         /* wipe existing contents */
  159         udb_zone_clear(udb, &z);
  160     } else {
  161         if(!udb_zone_create(udb, &z, dname_name(domain_dname(
  162             zone->apex)), domain_dname(zone->apex)->name_size)) {
  163             udb_base_set_userflags(udb, 0);
  164             return 0;
  165         }
  166     }
  167     /* set mtime */
  168     ZONE(&z)->mtime = (uint64_t)mtime->tv_sec;
  169     ZONE(&z)->mtime_nsec = (uint64_t)mtime->tv_nsec;
  170     ZONE(&z)->is_changed = 0;
  171     udb_zone_set_log_str(udb, &z, NULL);
  172     udb_zone_set_file_str(udb, &z, file_str);
  173     /* write zone */
  174     if(!write_zone(udb, &z, zone)) {
  175         udb_base_set_userflags(udb, 0);
  176         return 0;
  177     }
  178     udb_ptr_unlink(&z, udb);
  179     udb_base_set_userflags(udb, 0);
  180     return 1;
  181 }
  182 
  183 int
  184 print_rrs(FILE* out, struct zone* zone)
  185 {
  186     rrset_type *rrset;
  187     domain_type *domain = zone->apex;
  188     region_type* region = region_create(xalloc, free);
  189     region_type* rr_region = region_create(xalloc, free);
  190     buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
  191     struct state_pretty_rr* state = create_pretty_rr(region);
  192     /* first print the SOA record for the zone */
  193     if(zone->soa_rrset) {
  194         size_t i;
  195         for(i=0; i < zone->soa_rrset->rr_count; i++) {
  196             if(!print_rr(out, state, &zone->soa_rrset->rrs[i],
  197                 rr_region, rr_buffer)){
  198                 log_msg(LOG_ERR, "There was an error "
  199                    "printing SOARR to zone %s",
  200                    zone->opts->name);
  201                 region_destroy(region);
  202                 region_destroy(rr_region);
  203                 return 0;
  204             }
  205         }
  206     }
  207     /* go through entire tree below the zone apex (incl subzones) */
  208     while(domain && domain_is_subdomain(domain, zone->apex))
  209     {
  210         for(rrset = domain->rrsets; rrset; rrset=rrset->next)
  211         {
  212             size_t i;
  213             if(rrset->zone != zone || rrset == zone->soa_rrset)
  214                 continue;
  215             for(i=0; i < rrset->rr_count; i++) {
  216                 if(!print_rr(out, state, &rrset->rrs[i],
  217                     rr_region, rr_buffer)){
  218                     log_msg(LOG_ERR, "There was an error "
  219                        "printing RR to zone %s",
  220                        zone->opts->name);
  221                     region_destroy(region);
  222                     region_destroy(rr_region);
  223                     return 0;
  224                 }
  225             }
  226         }
  227         domain = domain_next(domain);
  228     }
  229     region_destroy(region);
  230     region_destroy(rr_region);
  231     return 1;
  232 }
  233 
  234 static int
  235 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
  236 {
  237     char buf[4096+16];
  238     /* ctime prints newline at end of this line */
  239     snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
  240         zone->opts->name, PACKAGE_VERSION, ctime(now));
  241     if(!write_data(out, buf, strlen(buf)))
  242         return 0;
  243     if(!logs || logs[0] == 0) return 1;
  244     snprintf(buf, sizeof(buf), "; %s\n", logs);
  245     return write_data(out, buf, strlen(buf));
  246 }
  247 
  248 static int
  249 write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
  250 {
  251     time_t now = time(0);
  252     FILE *out = fopen(filename, "w");
  253     if(!out) {
  254         log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
  255             zone->opts->name, filename, strerror(errno));
  256         return 0;
  257     }
  258     if(!print_header(zone, out, &now, logs)) {
  259         fclose(out);
  260         log_msg(LOG_ERR, "There was an error printing "
  261             "the header to zone %s", zone->opts->name);
  262         return 0;
  263     }
  264     if(!print_rrs(out, zone)) {
  265         fclose(out);
  266         return 0;
  267     }
  268     if(fclose(out) != 0) {
  269         log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
  270             zone->opts->name, filename, strerror(errno));
  271         return 0;
  272     }
  273     return 1;
  274 }
  275 
  276 /** create directories above this file, .../dir/dir/dir/file */
  277 int
  278 create_dirs(const char* path)
  279 {
  280     char dir[4096];
  281     char* p;
  282     strlcpy(dir, path, sizeof(dir));
  283     /* if we start with / then do not try to create '' */
  284     if(dir[0] == PATHSEP)
  285         p = strchr(dir+1, PATHSEP);
  286     else    p = strchr(dir, PATHSEP);
  287     /* create each directory component from the left */
  288     while(p) {
  289         assert(*p == PATHSEP);
  290         *p = 0; /* end the directory name here */
  291         if(mkdir(dir
  292 #ifndef MKDIR_HAS_ONE_ARG
  293             , 0750
  294 #endif
  295             ) == -1) {
  296             if(errno != EEXIST) {
  297                 log_msg(LOG_ERR, "create dir %s: %s",
  298                     dir, strerror(errno));
  299                 *p = PATHSEP; /* restore input string */
  300                 return 0;
  301             }
  302             /* it already exists, OK, continue */
  303         }
  304         *p = PATHSEP;
  305         p = strchr(p+1, PATHSEP);
  306     }
  307     return 1;
  308 }
  309 
  310 /** create pathname components and check if file exists */
  311 static int
  312 create_path_components(const char* path, int* notexist)
  313 {
  314     /* stat the file, to see if it exists, and if its directories exist */
  315     struct stat s;
  316     if(stat(path, &s) != 0) {
  317         if(errno == ENOENT) {
  318             *notexist = 1;
  319             /* see if we need to create pathname components */
  320             return create_dirs(path);
  321         }
  322         log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
  323         return 0;
  324     }
  325     *notexist = 0;
  326     return 1;
  327 }
  328 
  329 void
  330 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
  331 {
  332     const char* zfile;
  333     int notexist = 0;
  334     zone_type* zone;
  335     /* if no zone exists, it has no contents or it has no zonefile
  336      * configured, then no need to write data to disk */
  337     if(!zopt->pattern->zonefile)
  338         return;
  339     zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
  340     if(!zone || !zone->apex || !zone->soa_rrset)
  341         return;
  342     /* write if file does not exist, or if changed */
  343     /* so, determine filename, create directory components, check exist*/
  344     zfile = config_make_zonefile(zopt, nsd);
  345     if(!create_path_components(zfile, &notexist)) {
  346         log_msg(LOG_ERR, "could not write zone %s to file %s because "
  347             "the path could not be created", zopt->name, zfile);
  348         return;
  349     }
  350 
  351     /* if not changed, do not write. */
  352     if(notexist || zone->is_changed) {
  353         char logs[4096];
  354         char bakfile[4096];
  355         struct timespec mtime;
  356         udb_ptr zudb;
  357         if(nsd->db->udb) {
  358             if(!udb_zone_search(nsd->db->udb, &zudb,
  359                 dname_name(domain_dname(zone->apex)),
  360                 domain_dname(zone->apex)->name_size))
  361                 return; /* zone does not exist in db */
  362         }
  363         /* write to zfile~ first, then rename if that works */
  364         snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
  365         if(nsd->db->udb && ZONE(&zudb)->log_str.data) {
  366             udb_ptr s;
  367             udb_ptr_new(&s, nsd->db->udb, &ZONE(&zudb)->log_str);
  368             strlcpy(logs, (char*)udb_ptr_data(&s), sizeof(logs));
  369             udb_ptr_unlink(&s, nsd->db->udb);
  370         } else if(zone->logstr) {
  371             strlcpy(logs, zone->logstr, sizeof(logs));
  372         } else logs[0] = 0;
  373         VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
  374             zone->opts->name, zfile));
  375         if(!write_to_zonefile(zone, bakfile, logs)) {
  376             if(nsd->db->udb)
  377                 udb_ptr_unlink(&zudb, nsd->db->udb);
  378             (void)unlink(bakfile); /* delete failed file */
  379             return; /* error already printed */
  380         }
  381         if(rename(bakfile, zfile) == -1) {
  382             log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
  383                 bakfile, zfile, strerror(errno));
  384             if(nsd->db->udb)
  385                 udb_ptr_unlink(&zudb, nsd->db->udb);
  386             (void)unlink(bakfile); /* delete failed file */
  387             return;
  388         }
  389         zone->is_changed = 0;
  390         /* fetch the mtime of the just created zonefile so we
  391          * do not waste effort reading it back in */
  392         if(!file_get_mtime(zfile, &mtime, &notexist)) {
  393             get_time(&mtime);
  394         }
  395         if(nsd->db->udb) {
  396             ZONE(&zudb)->mtime = (uint64_t)mtime.tv_sec;
  397             ZONE(&zudb)->mtime_nsec = (uint64_t)mtime.tv_nsec;
  398             ZONE(&zudb)->is_changed = 0;
  399             udb_zone_set_log_str(nsd->db->udb, &zudb, NULL);
  400             udb_ptr_unlink(&zudb, nsd->db->udb);
  401         } else {
  402             zone->mtime = mtime;
  403             if(zone->filename)
  404                 region_recycle(nsd->db->region, zone->filename,
  405                     strlen(zone->filename)+1);
  406             zone->filename = region_strdup(nsd->db->region, zfile);
  407             if(zone->logstr)
  408                 region_recycle(nsd->db->region, zone->logstr,
  409                     strlen(zone->logstr)+1);
  410             zone->logstr = NULL;
  411         }
  412     }
  413 }
  414 
  415 void
  416 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
  417 {
  418     struct zone_options* zo;
  419     RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
  420         namedb_write_zonefile(nsd, zo);
  421     }
  422 }