Home | History | Annotate | Line # | Download | only in dist
      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