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 "options.h" 23 #include "nsd.h" 24 #include "ixfr.h" 25 26 /* pathname directory separator character */ 27 #define PATHSEP '/' 28 29 int 30 print_rrs(FILE* out, struct zone* zone) 31 { 32 rrset_type *rrset; 33 domain_type *domain = zone->apex; 34 region_type* region = region_create(xalloc, free); 35 region_type* rr_region = region_create(xalloc, free); 36 buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH); 37 struct state_pretty_rr* state = create_pretty_rr(region); 38 /* first print the SOA record for the zone */ 39 if(zone->soa_rrset) { 40 size_t i; 41 for(i=0; i < zone->soa_rrset->rr_count; i++) { 42 if(!print_rr(out, state, zone->soa_rrset->rrs[i], 43 rr_region, rr_buffer)){ 44 log_msg(LOG_ERR, "There was an error " 45 "printing SOARR to zone %s", 46 zone->opts->name); 47 region_destroy(region); 48 region_destroy(rr_region); 49 return 0; 50 } 51 } 52 } 53 /* go through entire tree below the zone apex (incl subzones) */ 54 while(domain && domain_is_subdomain(domain, zone->apex)) 55 { 56 for(rrset = domain->rrsets; rrset; rrset=rrset->next) 57 { 58 size_t i; 59 if(rrset->zone != zone || rrset == zone->soa_rrset) 60 continue; 61 for(i=0; i < rrset->rr_count; i++) { 62 if(!print_rr(out, state, rrset->rrs[i], 63 rr_region, rr_buffer)){ 64 log_msg(LOG_ERR, "There was an error " 65 "printing RR to zone %s", 66 zone->opts->name); 67 region_destroy(region); 68 region_destroy(rr_region); 69 return 0; 70 } 71 } 72 } 73 domain = domain_next(domain); 74 } 75 region_destroy(region); 76 region_destroy(rr_region); 77 return 1; 78 } 79 80 static int 81 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs) 82 { 83 char buf[4096+16]; 84 /* ctime prints newline at end of this line */ 85 snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s", 86 zone->opts->name, PACKAGE_VERSION, ctime(now)); 87 if(!write_data(out, buf, strlen(buf))) 88 return 0; 89 if(!logs || logs[0] == 0) return 1; 90 snprintf(buf, sizeof(buf), "; %s\n", logs); 91 return write_data(out, buf, strlen(buf)); 92 } 93 94 static int 95 write_to_zonefile(zone_type* zone, const char* filename, const char* logs) 96 { 97 time_t now = time(0); 98 FILE *out = fopen(filename, "w"); 99 if(!out) { 100 log_msg(LOG_ERR, "cannot write zone %s file %s: %s", 101 zone->opts->name, filename, strerror(errno)); 102 return 0; 103 } 104 if(!print_header(zone, out, &now, logs)) { 105 fclose(out); 106 log_msg(LOG_ERR, "There was an error printing " 107 "the header to zone %s", zone->opts->name); 108 return 0; 109 } 110 if(!print_rrs(out, zone)) { 111 fclose(out); 112 return 0; 113 } 114 if(fclose(out) != 0) { 115 log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s", 116 zone->opts->name, filename, strerror(errno)); 117 return 0; 118 } 119 return 1; 120 } 121 122 /** create directories above this file, .../dir/dir/dir/file */ 123 int 124 create_dirs(const char* path) 125 { 126 char dir[4096]; 127 char* p; 128 strlcpy(dir, path, sizeof(dir)); 129 /* if we start with / then do not try to create '' */ 130 if(dir[0] == PATHSEP) 131 p = strchr(dir+1, PATHSEP); 132 else p = strchr(dir, PATHSEP); 133 /* create each directory component from the left */ 134 while(p) { 135 assert(*p == PATHSEP); 136 *p = 0; /* end the directory name here */ 137 if(mkdir(dir 138 #ifndef MKDIR_HAS_ONE_ARG 139 , 0750 140 #endif 141 ) == -1) { 142 if(errno != EEXIST) { 143 log_msg(LOG_ERR, "create dir %s: %s", 144 dir, strerror(errno)); 145 *p = PATHSEP; /* restore input string */ 146 return 0; 147 } 148 /* it already exists, OK, continue */ 149 } 150 *p = PATHSEP; 151 p = strchr(p+1, PATHSEP); 152 } 153 return 1; 154 } 155 156 /** create pathname components and check if file exists */ 157 static int 158 create_path_components(const char* path, int* notexist) 159 { 160 /* stat the file, to see if it exists, and if its directories exist */ 161 struct stat s; 162 if(stat(path, &s) != 0) { 163 if(errno == ENOENT) { 164 *notexist = 1; 165 /* see if we need to create pathname components */ 166 return create_dirs(path); 167 } 168 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno)); 169 return 0; 170 } 171 *notexist = 0; 172 return 1; 173 } 174 175 void 176 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt) 177 { 178 const char* zfile; 179 int notexist = 0; 180 zone_type* zone; 181 /* if no zone exists, it has no contents or it has no zonefile 182 * configured, then no need to write data to disk */ 183 if(!zopt->pattern->zonefile) 184 return; 185 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key); 186 if(!zone || !zone->apex || !zone->soa_rrset) 187 return; 188 /* write if file does not exist, or if changed */ 189 /* so, determine filename, create directory components, check exist*/ 190 zfile = config_make_zonefile(zopt, nsd); 191 if(!create_path_components(zfile, ¬exist)) { 192 log_msg(LOG_ERR, "could not write zone %s to file %s because " 193 "the path could not be created", zopt->name, zfile); 194 return; 195 } 196 197 /* if not changed, do not write. */ 198 if(notexist || zone->is_changed) { 199 char logs[4096]; 200 char bakfile[4096]; 201 struct timespec mtime; 202 /* write to zfile~ first, then rename if that works */ 203 snprintf(bakfile, sizeof(bakfile), "%s~", zfile); 204 if(zone->logstr) 205 strlcpy(logs, zone->logstr, sizeof(logs)); 206 else 207 logs[0] = 0; 208 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s", 209 zone->opts->name, zfile)); 210 if(!write_to_zonefile(zone, bakfile, logs)) { 211 (void)unlink(bakfile); /* delete failed file */ 212 return; /* error already printed */ 213 } 214 if(rename(bakfile, zfile) == -1) { 215 log_msg(LOG_ERR, "rename(%s to %s) failed: %s", 216 bakfile, zfile, strerror(errno)); 217 (void)unlink(bakfile); /* delete failed file */ 218 return; 219 } 220 zone->is_changed = 0; 221 VERBOSITY(3, (LOG_INFO, "zone %s written to file %s", 222 zone->opts->name, zfile)); 223 /* fetch the mtime of the just created zonefile so we 224 * do not waste effort reading it back in */ 225 if(!file_get_mtime(zfile, &mtime, ¬exist)) { 226 get_time(&mtime); 227 } 228 zone->mtime = mtime; 229 if(zone->filename) 230 region_recycle(nsd->db->region, zone->filename, 231 strlen(zone->filename)+1); 232 zone->filename = region_strdup(nsd->db->region, zfile); 233 if(zone->logstr) 234 region_recycle(nsd->db->region, zone->logstr, 235 strlen(zone->logstr)+1); 236 zone->logstr = NULL; 237 if(zone_is_ixfr_enabled(zone) && zone->ixfr) 238 ixfr_write_to_file(zone, zfile); 239 } 240 } 241 242 void 243 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options) 244 { 245 struct zone_options* zo; 246 RBTREE_FOR(zo, struct zone_options*, options->zone_options) { 247 namedb_write_zonefile(nsd, zo); 248 } 249 } 250