1 /* 2 * zonec.c -- zone compiler. 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 <inttypes.h> 13 #include <assert.h> 14 #include <fcntl.h> 15 #include <ctype.h> 16 #include <errno.h> 17 #include <limits.h> 18 #include <stdio.h> 19 #include <string.h> 20 #ifdef HAVE_STRINGS_H 21 #include <strings.h> 22 #endif 23 #include <unistd.h> 24 #include <stdlib.h> 25 #ifdef HAVE_SYS_STAT_H 26 #include <sys/stat.h> 27 #endif 28 29 #include <netinet/in.h> 30 31 #ifdef HAVE_NETDB_H 32 #include <netdb.h> 33 #endif 34 35 #include "zonec.h" 36 37 #include "dname.h" 38 #include "dns.h" 39 #include "namedb.h" 40 #include "rdata.h" 41 #include "region-allocator.h" 42 #include "util.h" 43 #include "options.h" 44 #include "nsec3.h" 45 #include "zone.h" 46 47 /* 48 * Find rrset type for any zone 49 */ 50 static rrset_type* 51 domain_find_rrset_any(domain_type *domain, uint16_t type) 52 { 53 rrset_type *result = domain->rrsets; 54 while (result) { 55 if (rrset_rrtype(result) == type) { 56 return result; 57 } 58 result = result->next; 59 } 60 return NULL; 61 } 62 63 /* 64 * Check for DNAME type. Nothing is allowed below it 65 */ 66 static int 67 check_dname(zone_type* zone) 68 { 69 domain_type* domain; 70 for(domain = zone->apex; domain && domain_is_subdomain(domain, 71 zone->apex); domain=domain_next(domain)) 72 { 73 if(domain->is_existing) { 74 /* there may not be DNAMEs above it */ 75 domain_type* parent = domain->parent; 76 #ifdef NSEC3 77 if(domain_has_only_NSEC3(domain, NULL)) 78 continue; 79 #endif 80 while(parent) { 81 if(domain_find_rrset_any(parent, TYPE_DNAME)) { 82 log_msg(LOG_ERR, "While checking node %s,", 83 domain_to_string(domain)); 84 log_msg(LOG_ERR, "DNAME at %s has data below it. " 85 "This is not allowed (rfc 2672).", 86 domain_to_string(parent)); 87 return 0; 88 } 89 parent = parent->parent; 90 } 91 } 92 } 93 94 return 1; 95 } 96 97 static int 98 has_soa(domain_type* domain) 99 { 100 rrset_type* p = NULL; 101 if(!domain) return 0; 102 for(p = domain->rrsets; p; p = p->next) 103 if(rrset_rrtype(p) == TYPE_SOA) 104 return 1; 105 return 0; 106 } 107 108 struct zonec_state { 109 struct namedb *database; 110 struct domain_table *domains; 111 struct zone *zone; 112 struct domain *domain; 113 size_t errors; 114 size_t records; 115 }; 116 117 int32_t zonec_accept( 118 zone_parser_t *parser, 119 const zone_name_t *owner, 120 uint16_t type, 121 uint16_t class, 122 uint32_t ttl, 123 uint16_t rdlength, 124 const uint8_t *rdata, 125 void *user_data) 126 { 127 struct rr *rr; 128 struct rrset *rrset; 129 struct dname_buffer dname; 130 struct domain *domain; 131 struct buffer buffer; 132 int priority; 133 int32_t code; 134 const struct nsd_type_descriptor *descriptor; 135 struct zonec_state *state = (struct zonec_state *)user_data; 136 #ifdef PACKED_STRUCTS 137 rrset_type* rrset_prev; 138 #endif 139 140 assert(state); 141 142 buffer_create_from(&buffer, rdata, rdlength); 143 144 priority = parser->options.secondary ? ZONE_WARNING : ZONE_ERROR; 145 /* limit to IN class */ 146 if (class != CLASS_IN) 147 zone_log(parser, priority, "only class IN is supported"); 148 149 if(!dname_make_buffered(&dname, (uint8_t*)owner->octets, 1)) { 150 zone_log(parser, ZONE_ERROR, "the owner cannot be converted"); 151 return ZONE_BAD_PARAMETER; 152 } 153 154 domain = domain_table_insert(state->domains, (void*)&dname); 155 assert(domain); 156 157 descriptor = nsd_type_descriptor(type); 158 code = descriptor->read_rdata(state->domains, rdlength, &buffer, &rr); 159 if(code < 0) { 160 zone_log(parser, ZONE_ERROR, "the RR rdata fields are wrong for the type, %s %s %s", 161 dname_to_string((void*)&dname,0), 162 rrtype_to_string(type), 163 read_rdata_fail_str(code)); 164 if(code == TRUNCATED) 165 return ZONE_OUT_OF_MEMORY; 166 return ZONE_BAD_PARAMETER; 167 } 168 rr->owner = domain; 169 rr->type = type; 170 rr->klass = class; 171 rr->ttl = ttl; 172 173 /* we have the zone already */ 174 if (type == TYPE_SOA) { 175 if (domain != state->zone->apex) { 176 char s[MAXDOMAINLEN*5]; 177 snprintf(s, sizeof(s), "%s", domain_to_string(domain)); 178 zone_log(parser, priority, "SOA record with invalid domain name, '%s' is not '%s'", 179 domain_to_string(state->zone->apex), s); 180 } else if (has_soa(domain)) { 181 zone_log(parser, priority, "this SOA record was already encountered"); 182 } 183 domain->is_apex = 1; 184 } 185 186 if (!domain_is_subdomain(domain, state->zone->apex)) { 187 char s[MAXDOMAINLEN*5]; 188 snprintf(s, sizeof(s), "%s", domain_to_string(state->zone->apex)); 189 zone_log(parser, priority, "out of zone data: %s is outside the zone for fqdn %s", 190 s, domain_to_string(domain)); 191 if (!parser->options.secondary) { 192 return ZONE_SEMANTIC_ERROR; 193 } 194 } 195 196 /* Do we have this type of rrset already? */ 197 #ifndef PACKED_STRUCTS 198 rrset = domain_find_rrset(domain, state->zone, type); 199 #else 200 rrset = domain_find_rrset_and_prev(domain, state->zone, type, &rrset_prev); 201 #endif 202 if (!rrset) { 203 rrset = region_alloc(state->database->region, sizeof(*rrset) 204 #ifdef PACKED_STRUCTS 205 + sizeof(rr_type*) /* Add space for one RR. */ 206 #endif 207 ); 208 rrset->zone = state->zone; 209 rrset->rr_count = 0; 210 #ifndef PACKED_STRUCTS 211 rrset->rrs = region_alloc(state->database->region, sizeof(rr_type*)); 212 #endif 213 214 switch (type) { 215 case TYPE_CNAME: 216 if (!domain_find_non_cname_rrset(domain, state->zone)) 217 break; 218 zone_log(parser, priority, "CNAME and other data at the same name"); 219 break; 220 case TYPE_RRSIG: 221 case TYPE_NXT: 222 case TYPE_SIG: 223 case TYPE_NSEC: 224 case TYPE_NSEC3: 225 break; 226 default: 227 if (!domain_find_rrset(domain, state->zone, TYPE_CNAME)) 228 break; 229 zone_log(parser, priority, "CNAME and other data at the same name"); 230 break; 231 } 232 233 /* Add it */ 234 domain_add_rrset(domain, rrset); 235 } else { 236 #ifndef PACKED_STRUCTS 237 struct rr **rrs; 238 #else 239 struct rrset *rrset_orig; 240 #endif 241 if (type != TYPE_RRSIG && ttl != rrset->rrs[0]->ttl) { 242 zone_log(parser, ZONE_WARNING, "%s TTL %"PRIu32" does not match TTL %u of %s RRset", 243 domain_to_string(domain), ttl, rrset->rrs[0]->ttl, 244 rrtype_to_string(type)); 245 } 246 247 /* Search for possible duplicates... */ 248 for (int i = 0; i < rrset->rr_count; i++) { 249 if (!equal_rr_rdata(descriptor, rr, rrset->rrs[i])) 250 continue; 251 /* Discard the duplicates... */ 252 /* Lower the usage counter for domains in the rdata. */ 253 rr_lower_usage(state->database, rr); 254 region_recycle(state->database->region, rr, sizeof(*rr) + rr->rdlength); 255 return 0; 256 } 257 258 switch (type) { 259 case TYPE_CNAME: 260 zone_log(parser, priority, "multiple CNAMEs at the same name"); 261 break; 262 case TYPE_DNAME: 263 zone_log(parser, priority, "multiple DNAMEs at the same name"); 264 break; 265 default: 266 break; 267 } 268 269 /* Add it... */ 270 #ifndef PACKED_STRUCTS 271 rrs = rrset->rrs; 272 rrset->rrs = region_alloc_array( 273 state->database->region, rrset->rr_count + 1, sizeof(*rrs)); 274 memcpy(rrset->rrs, rrs, rrset->rr_count * sizeof(*rrs)); 275 region_recycle(state->database->region, rrs, rrset->rr_count * sizeof(*rrs)); 276 #else 277 rrset_orig = rrset; 278 rrset = region_alloc(state->database->region, 279 sizeof(rrset_type) + 280 (rrset_orig->rr_count+1)*sizeof(rr_type*)); 281 memcpy(rrset, rrset_orig, 282 sizeof(rrset_type) + 283 rrset_orig->rr_count*sizeof(rr_type*)); 284 if(rrset_prev) 285 rrset_prev->next = rrset; 286 else domain->rrsets = rrset; 287 region_recycle(state->database->region, rrset_orig, 288 sizeof(rrset_type) + 289 rrset_orig->rr_count*sizeof(rr_type*)); 290 #endif /* PACKED_STRUCTS */ 291 } 292 293 rrset->rrs[rrset->rr_count++] = rr; 294 295 /* Check we have SOA */ 296 if (rr->owner == state->zone->apex) 297 apex_rrset_checks(state->database, rrset, rr->owner); 298 299 state->records++; 300 return 0; 301 } 302 303 static int32_t zonec_include( 304 zone_parser_t *parser, 305 const char *file, 306 const char *path, 307 void *user_data) 308 { 309 char **paths; 310 struct zonec_state *state; 311 struct namedb *database; 312 struct zone *zone; 313 314 (void)parser; 315 (void)file; 316 317 state = (struct zonec_state *)user_data; 318 database = state->database; 319 zone = state->zone; 320 321 assert((zone->includes.count == 0) == (zone->includes.paths == NULL)); 322 323 for (size_t i=0; i < zone->includes.count; i++) 324 if (strcmp(path, zone->includes.paths[i]) == 0) 325 return 0; 326 327 paths = region_alloc_array( 328 database->region, zone->includes.count + 1, sizeof(*paths)); 329 if (zone->includes.count) { 330 const size_t size = zone->includes.count * sizeof(*paths); 331 memcpy(paths, zone->includes.paths, size); 332 region_recycle(database->region, zone->includes.paths, size); 333 } 334 paths[zone->includes.count] = region_strdup(database->region, path); 335 zone->includes.count++; 336 zone->includes.paths = paths; 337 return 0; 338 } 339 340 static void zonec_log( 341 zone_parser_t *parser, 342 uint32_t category, 343 const char *file, 344 size_t line, 345 const char *message, 346 void *user_data) 347 { 348 int priority; 349 struct zonec_state *state = (struct zonec_state *)user_data; 350 351 assert(state); 352 (void)parser; 353 354 switch (category) { 355 case ZONE_INFO: 356 priority = LOG_INFO; 357 break; 358 case ZONE_WARNING: 359 priority = LOG_WARNING; 360 break; 361 default: 362 priority = LOG_ERR; 363 state->errors++; 364 break; 365 } 366 367 if (file) 368 log_msg(priority, "%s:%zu: %s", file, line, message); 369 else 370 log_msg(priority, "%s", message); 371 } 372 373 /* 374 * Reads the specified zone into the memory 375 * nsd_options can be NULL if no config file is passed. 376 */ 377 unsigned int 378 zonec_read( 379 struct namedb *database, 380 struct domain_table *domains, 381 const char *name, 382 const char *zonefile, 383 struct zone *zone) 384 { 385 const struct dname *origin; 386 zone_parser_t parser; 387 zone_options_t options; 388 zone_name_buffer_t name_buffer; 389 zone_rdata_buffer_t rdata_buffer; 390 struct zonec_state state; 391 zone_buffers_t buffers = { 1, &name_buffer, &rdata_buffer }; 392 393 state.database = database; 394 state.domains = domains; 395 state.zone = zone; 396 state.domain = NULL; 397 state.errors = 0; 398 state.records = 0; 399 400 origin = domain_dname(zone->apex); 401 memset(&options, 0, sizeof(options)); 402 options.origin.octets = dname_name(origin); 403 options.origin.length = origin->name_size; 404 options.default_ttl = DEFAULT_TTL; 405 options.default_class = CLASS_IN; 406 options.secondary = zone_is_slave(zone->opts) != 0; 407 options.pretty_ttls = true; /* non-standard, for backwards compatibility */ 408 options.log.callback = &zonec_log; 409 options.accept.callback = &zonec_accept; 410 options.include.callback = &zonec_include; 411 412 /* Parse and process all RRs. */ 413 if (zone_parse(&parser, &options, &buffers, zonefile, &state) != 0) { 414 return state.errors; 415 } 416 417 /* Check if zone file contained a correct SOA record */ 418 if (!zone) { 419 log_msg(LOG_ERR, "zone configured as '%s' has no content.", name); 420 state.errors++; 421 } else if (!zone->soa_rrset || zone->soa_rrset->rr_count == 0) { 422 log_msg(LOG_ERR, "zone configured as '%s' has no SOA record", name); 423 state.errors++; 424 } else if (dname_compare(domain_dname(zone->soa_rrset->rrs[0]->owner), origin) != 0) { 425 log_msg(LOG_ERR, "zone configured as '%s', but SOA has owner '%s'", 426 name, domain_to_string(zone->soa_rrset->rrs[0]->owner)); 427 state.errors++; 428 } 429 430 if(!zone_is_slave(zone->opts) && !check_dname(zone)) 431 state.errors++; 432 433 return state.errors; 434 } 435 436 void 437 apex_rrset_checks(namedb_type* db, rrset_type* rrset, domain_type* domain) 438 { 439 uint32_t soa_minimum = 0; 440 unsigned i; 441 zone_type* zone = rrset->zone; 442 assert(domain == zone->apex); 443 (void)domain; 444 if (rrset_rrtype(rrset) == TYPE_SOA) { 445 zone->soa_rrset = rrset; 446 447 /* BUG #103 add another soa with a tweaked ttl */ 448 if(zone->soa_nx_rrset == 0) { 449 zone->soa_nx_rrset = region_alloc(db->region, 450 sizeof(rrset_type) 451 #ifdef PACKED_STRUCTS 452 + sizeof(rr_type*) 453 #endif 454 ); 455 zone->soa_nx_rrset->rr_count = 1; 456 zone->soa_nx_rrset->next = 0; 457 zone->soa_nx_rrset->zone = zone; 458 #ifndef PACKED_STRUCTS 459 zone->soa_nx_rrset->rrs = region_alloc(db->region, 460 sizeof(rr_type*)); 461 #endif 462 zone->soa_nx_rrset->rrs[0] = region_alloc(db->region, 463 sizeof(rr_type)+rrset->rrs[0]->rdlength); 464 } 465 memcpy(zone->soa_nx_rrset->rrs[0], rrset->rrs[0], 466 sizeof(rr_type)+rrset->rrs[0]->rdlength); 467 468 /* check the ttl and MINIMUM value and set accordingly */ 469 retrieve_soa_rdata_minttl(rrset->rrs[0], &soa_minimum); 470 if (rrset->rrs[0]->ttl > soa_minimum) { 471 zone->soa_nx_rrset->rrs[0]->ttl = soa_minimum; 472 } 473 } else if (rrset_rrtype(rrset) == TYPE_NS) { 474 zone->ns_rrset = rrset; 475 } else if (rrset_rrtype(rrset) == TYPE_RRSIG) { 476 for (i = 0; i < rrset->rr_count; ++i) { 477 if(rr_rrsig_type_covered(rrset->rrs[i])==TYPE_DNSKEY){ 478 zone->is_secure = 1; 479 break; 480 } 481 } 482 } 483 } 484