Edit

IABSD.fr/src/usr.sbin/nsd/zonec.c

Branch :

  • Show log

    Commit

  • Author : florian
    Date : 2023-06-29 19:38:49
    Hash : 3efee2e1
    Message : Update to nsd 4.7.0 OK tb

  • usr.sbin/nsd/zonec.c
  • /*
     * zonec.c -- zone compiler.
     *
     * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
     *
     * See LICENSE for the license.
     *
     */
    
    #include "config.h"
    
    #include <assert.h>
    #include <fcntl.h>
    #include <ctype.h>
    #include <errno.h>
    #include <limits.h>
    #include <stdio.h>
    #include <string.h>
    #ifdef HAVE_STRINGS_H
    #include <strings.h>
    #endif
    #include <unistd.h>
    #include <stdlib.h>
    #include <time.h>
    #ifdef HAVE_SYS_STAT_H
    #include <sys/stat.h>
    #endif
    
    #include <netinet/in.h>
    
    #ifdef HAVE_NETDB_H
    #include <netdb.h>
    #endif
    
    #include "zonec.h"
    
    #include "dname.h"
    #include "dns.h"
    #include "namedb.h"
    #include "rdata.h"
    #include "region-allocator.h"
    #include "util.h"
    #include "zparser.h"
    #include "options.h"
    #include "nsec3.h"
    
    #define ILNP_MAXDIGITS 4
    #define ILNP_NUMGROUPS 4
    #define SVCB_MAX_COMMA_SEPARATED_VALUES 1000
    
    
    const dname_type *error_dname;
    domain_type *error_domain;
    
    static time_t startzonec = 0;
    static long int totalrrs = 0;
    
    extern uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE];
    extern uint16_t nsec_highest_rcode;
    
    
    /*
     * Allocate SIZE+sizeof(uint16_t) bytes and store SIZE in the first
     * element.  Return a pointer to the allocation.
     */
    static uint16_t *
    alloc_rdata(region_type *region, size_t size)
    {
    	uint16_t *result = region_alloc(region, sizeof(uint16_t) + size);
    	*result = size;
    	return result;
    }
    
    uint16_t *
    alloc_rdata_init(region_type *region, const void *data, size_t size)
    {
    	uint16_t *result = region_alloc(region, sizeof(uint16_t) + size);
    	*result = size;
    	memcpy(result + 1, data, size);
    	return result;
    }
    
    /*
     * These are parser function for generic zone file stuff.
     */
    uint16_t *
    zparser_conv_hex(region_type *region, const char *hex, size_t len)
    {
    	/* convert a hex value to wireformat */
    	uint16_t *r = NULL;
    	uint8_t *t;
    	int i;
    
    	if(len == 1 && hex[0] == '0') {
    		/* single 0 represents empty buffer */
    		return alloc_rdata(region, 0);
    	}
    	if (len % 2 != 0) {
    		zc_error_prev_line("number of hex digits must be a multiple of 2");
    	} else if (len > MAX_RDLENGTH * 2) {
    		zc_error_prev_line("hex data exceeds maximum rdata length (%d)",
    				   MAX_RDLENGTH);
    	} else {
    		/* the length part */
    		r = alloc_rdata(region, len/2);
    		t = (uint8_t *)(r + 1);
    
    		/* Now process octet by octet... */
    		while (*hex) {
    			*t = 0;
    			for (i = 16; i >= 1; i -= 15) {
    				if (isxdigit((unsigned char)*hex)) {
    					*t += hexdigit_to_int(*hex) * i;
    				} else {
    					zc_error_prev_line(
    						"illegal hex character '%c'",
    						(int) *hex);
    					return NULL;
    				}
    				++hex;
    			}
    			++t;
    		}
    	}
    	return r;
    }
    
    /* convert hex, precede by a 1-byte length */
    uint16_t *
    zparser_conv_hex_length(region_type *region, const char *hex, size_t len)
    {
    	uint16_t *r = NULL;
    	uint8_t *t;
    	int i;
    	if (len % 2 != 0) {
    		zc_error_prev_line("number of hex digits must be a multiple of 2");
    	} else if (len > 255 * 2) {
    		zc_error_prev_line("hex data exceeds 255 bytes");
    	} else {
    		uint8_t *l;
    
    		/* the length part */
    		r = alloc_rdata(region, len/2+1);
    		t = (uint8_t *)(r + 1);
    
    		l = t++;
    		*l = '\0';
    
    		/* Now process octet by octet... */
    		while (*hex) {
    			*t = 0;
    			for (i = 16; i >= 1; i -= 15) {
    				if (isxdigit((unsigned char)*hex)) {
    					*t += hexdigit_to_int(*hex) * i;
    				} else {
    					zc_error_prev_line(
    						"illegal hex character '%c'",
    						(int) *hex);
    					return NULL;
    				}
    				++hex;
    			}
    			++t;
    			++*l;
    		}
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_time(region_type *region, const char *time)
    {
    	/* convert a time YYHM to wireformat */
    	uint16_t *r = NULL;
    	struct tm tm;
    
    	/* Try to scan the time... */
    	if (!strptime(time, "%Y%m%d%H%M%S", &tm)) {
    		zc_error_prev_line("date and time is expected");
    	} else {
    		uint32_t l = htonl(mktime_from_utc(&tm));
    		r = alloc_rdata_init(region, &l, sizeof(l));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_services(region_type *region, const char *protostr,
    		      char *servicestr)
    {
    	/*
    	 * Convert a protocol and a list of service port numbers
    	 * (separated by spaces) in the rdata to wireformat
    	 */
    	uint16_t *r = NULL;
    	uint8_t *p;
    	uint8_t bitmap[65536/8];
    	char sep[] = " ";
    	char *word;
    	int max_port = -8;
    	/* convert a protocol in the rdata to wireformat */
    	struct protoent *proto;
    
    	memset(bitmap, 0, sizeof(bitmap));
    
    	proto = getprotobyname(protostr);
    	if (!proto) {
    		proto = getprotobynumber(atoi(protostr));
    	}
    	if (!proto) {
    		zc_error_prev_line("unknown protocol '%s'", protostr);
    		return NULL;
    	}
    
    	for (word = strtok(servicestr, sep); word; word = strtok(NULL, sep)) {
    		struct servent *service;
    		int port;
    
    		service = getservbyname(word, proto->p_name);
    		if (service) {
    			/* Note: ntohs not ntohl!  Strange but true.  */
    			port = ntohs((uint16_t) service->s_port);
    		} else {
    			char *end;
    			port = strtol(word, &end, 10);
    			if (*end != '\0') {
    				zc_error_prev_line("unknown service '%s' for protocol '%s'",
    						   word, protostr);
    				continue;
    			}
    		}
    
    		if (port < 0 || port > 65535) {
    			zc_error_prev_line("bad port number %d", port);
    		} else {
    			set_bit(bitmap, port);
    			if (port > max_port)
    				max_port = port;
    		}
    	}
    
    	r = alloc_rdata(region, sizeof(uint8_t) + max_port / 8 + 1);
    	p = (uint8_t *) (r + 1);
    	*p = proto->p_proto;
    	memcpy(p + 1, bitmap, *r-1);
    
    	return r;
    }
    
    uint16_t *
    zparser_conv_serial(region_type *region, const char *serialstr)
    {
    	uint16_t *r = NULL;
    	uint32_t serial;
    	const char *t;
    
    	serial = strtoserial(serialstr, &t);
    	if (*t != '\0') {
    		zc_error_prev_line("serial is expected or serial too big");
    	} else {
    		serial = htonl(serial);
    		r = alloc_rdata_init(region, &serial, sizeof(serial));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_period(region_type *region, const char *periodstr)
    {
    	/* convert a time period (think TTL's) to wireformat) */
    	uint16_t *r = NULL;
    	uint32_t period;
    	const char *end;
    
    	/* Allocate required space... */
    	period = strtottl(periodstr, &end);
    	if (*end != '\0') {
    		zc_error_prev_line("time period is expected");
    	} else {
    		period = htonl(period);
    		r = alloc_rdata_init(region, &period, sizeof(period));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_short(region_type *region, const char *text)
    {
    	uint16_t *r = NULL;
    	uint16_t value;
    	char *end;
    
    	value = htons((uint16_t) strtol(text, &end, 10));
    	if (*end != '\0') {
    		zc_error_prev_line("integer value is expected");
    	} else {
    		r = alloc_rdata_init(region, &value, sizeof(value));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_byte(region_type *region, const char *text)
    {
    	uint16_t *r = NULL;
    	uint8_t value;
    	char *end;
    
    	value = (uint8_t) strtol(text, &end, 10);
    	if (*end != '\0') {
    		zc_error_prev_line("integer value is expected");
    	} else {
    		r = alloc_rdata_init(region, &value, sizeof(value));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_algorithm(region_type *region, const char *text)
    {
    	const lookup_table_type *alg;
    	uint8_t id;
    
    	alg = lookup_by_name(dns_algorithms, text);
    	if (alg) {
    		id = (uint8_t) alg->id;
    	} else {
    		char *end;
    		id = (uint8_t) strtol(text, &end, 10);
    		if (*end != '\0') {
    			zc_error_prev_line("algorithm is expected");
    			return NULL;
    		}
    	}
    
    	return alloc_rdata_init(region, &id, sizeof(id));
    }
    
    uint16_t *
    zparser_conv_certificate_type(region_type *region, const char *text)
    {
    	/* convert an algorithm string to integer */
    	const lookup_table_type *type;
    	uint16_t id;
    
    	type = lookup_by_name(dns_certificate_types, text);
    	if (type) {
    		id = htons((uint16_t) type->id);
    	} else {
    		char *end;
    		id = htons((uint16_t) strtol(text, &end, 10));
    		if (*end != '\0') {
    			zc_error_prev_line("certificate type is expected");
    			return NULL;
    		}
    	}
    
    	return alloc_rdata_init(region, &id, sizeof(id));
    }
    
    uint16_t *
    zparser_conv_a(region_type *region, const char *text)
    {
    	in_addr_t address;
    	uint16_t *r = NULL;
    
    	if (inet_pton(AF_INET, text, &address) != 1) {
    		zc_error_prev_line("invalid IPv4 address '%s'", text);
    	} else {
    		r = alloc_rdata_init(region, &address, sizeof(address));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_aaaa(region_type *region, const char *text)
    {
    	uint8_t address[IP6ADDRLEN];
    	uint16_t *r = NULL;
    
    	if (inet_pton(AF_INET6, text, address) != 1) {
    		zc_error_prev_line("invalid IPv6 address '%s'", text);
    	} else {
    		r = alloc_rdata_init(region, address, sizeof(address));
    	}
    	return r;
    }
    
    
    uint16_t *
    zparser_conv_ilnp64(region_type *region, const char *text)
    {
    	uint16_t *r = NULL;
    	int ngroups, num;
    	unsigned long hex;
    	const char *ch;
    	char digits[ILNP_MAXDIGITS+1];
    	unsigned int ui[ILNP_NUMGROUPS];
    	uint16_t a[ILNP_NUMGROUPS];
    
    	ngroups = 1; /* Always at least one group */
    	num = 0;
    	for (ch = text; *ch != '\0'; ch++) {
    		if (*ch == ':') {
    			if (num <= 0) {
    				zc_error_prev_line("ilnp64: empty group of "
    					"digits is not allowed");
    				return NULL;
    			}
    			digits[num] = '\0';
    			hex = (unsigned long) strtol(digits, NULL, 16);
    			num = 0;
    			ui[ngroups - 1] = hex;
    			if (ngroups >= ILNP_NUMGROUPS) {
    				zc_error_prev_line("ilnp64: more than %d groups "
    					"of digits", ILNP_NUMGROUPS);
    				return NULL;
    			}
    			ngroups++;
    		} else {
    			/* Our grammar is stricter than the one accepted by
    			 * strtol. */
    			if (!isxdigit((unsigned char)*ch)) {
    				zc_error_prev_line("ilnp64: invalid "
    					"(non-hexadecimal) character %c", *ch);
    				return NULL;
    			}
    			if (num >= ILNP_MAXDIGITS) {
    				zc_error_prev_line("ilnp64: more than %d digits "
    					"in a group", ILNP_MAXDIGITS);
    				return NULL;
    			}
    			digits[num++] = *ch;
    		}
    	}
    	if (num <= 0) {
    		zc_error_prev_line("ilnp64: empty group of digits is not "
    			"allowed");
    		return NULL;
    	}
    	digits[num] = '\0';
    	hex = (unsigned long) strtol(digits, NULL, 16);
    	ui[ngroups - 1] = hex;
    	if (ngroups < 4) {
    		zc_error_prev_line("ilnp64: less than %d groups of digits",
    			ILNP_NUMGROUPS);
    		return NULL;
    	}
    
    	a[0] = htons(ui[0]);
    	a[1] = htons(ui[1]);
    	a[2] = htons(ui[2]);
    	a[3] = htons(ui[3]);
    	r = alloc_rdata_init(region, a, sizeof(a));
    	return r;
    }
    
    static uint16_t *
    zparser_conv_eui48(region_type *region, const char *text)
    {
    	uint8_t nums[6];
    	uint16_t *r = NULL;
    	unsigned int a, b, c, d, e, f;
    	int l;
    
    	if (sscanf(text, "%2x-%2x-%2x-%2x-%2x-%2x%n",
    		&a, &b, &c, &d, &e, &f, &l) != 6 ||
    		l != (int)strlen(text)){
    		zc_error_prev_line("eui48: invalid rr");
    		return NULL;
    	}
    	nums[0] = (uint8_t)a;
    	nums[1] = (uint8_t)b;
    	nums[2] = (uint8_t)c;
    	nums[3] = (uint8_t)d;
    	nums[4] = (uint8_t)e;
    	nums[5] = (uint8_t)f;
    	r = alloc_rdata_init(region, nums, sizeof(nums));
    	return r;
    }
    
    static uint16_t *
    zparser_conv_eui64(region_type *region, const char *text)
    {
    	uint8_t nums[8];
    	uint16_t *r = NULL;
    	unsigned int a, b, c, d, e, f, g, h;
    	int l;
    	if (sscanf(text, "%2x-%2x-%2x-%2x-%2x-%2x-%2x-%2x%n",
    		&a, &b, &c, &d, &e, &f, &g, &h, &l) != 8 ||
    		l != (int)strlen(text)) {
    		zc_error_prev_line("eui64: invalid rr");
    		return NULL;
    	}
    	nums[0] = (uint8_t)a;
    	nums[1] = (uint8_t)b;
    	nums[2] = (uint8_t)c;
    	nums[3] = (uint8_t)d;
    	nums[4] = (uint8_t)e;
    	nums[5] = (uint8_t)f;
    	nums[6] = (uint8_t)g;
    	nums[7] = (uint8_t)h;
    	r = alloc_rdata_init(region, nums, sizeof(nums));
    	return r;
    }
    
    uint16_t *
    zparser_conv_eui(region_type *region, const char *text, size_t len)
    {
    	uint16_t *r = NULL;
    	int nnum, num;
    	const char* ch;
    
    	nnum = len/8;
    	num = 1;
    	for (ch = text; *ch != '\0'; ch++) {
    		if (*ch == '-') {
    			num++;
    		} else if (!isxdigit((unsigned char)*ch)) {
    			zc_error_prev_line("eui%u: invalid (non-hexadecimal) "
    				"character %c", (unsigned) len, *ch);
    			return NULL;
    		}
    	}
    	if (num != nnum) {
    		zc_error_prev_line("eui%u: wrong number of hex numbers",
    			(unsigned) len);
    		return NULL;
    	}
    
    	switch (len) {
    		case 48:
    			r = zparser_conv_eui48(region, text);
    			break;
    		case 64:
    			r = zparser_conv_eui64(region, text);
    		break;
    		default:
    			zc_error_prev_line("eui%u: invalid length",
    				(unsigned) len);
    			return NULL;
    			break;
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_text(region_type *region, const char *text, size_t len)
    {
    	uint16_t *r = NULL;
    	uint8_t *p;
    
    	if (len > 255) {
    		zc_error_prev_line("text string is longer than 255 characters,"
    				   " try splitting it into multiple parts");
    		len = 255;
    	}
    	r = alloc_rdata(region, len + 1);
    	p = (uint8_t *) (r + 1);
    	*p = len;
    	memcpy(p + 1, text, len);
    	return r;
    }
    
    /* for CAA Value [RFC 6844] */
    uint16_t *
    zparser_conv_long_text(region_type *region, const char *text, size_t len)
    {
    	uint16_t *r = NULL;
    	if (len > MAX_RDLENGTH) {
    		zc_error_prev_line("text string is longer than max rdlen");
    		return NULL;
    	}
    	r = alloc_rdata_init(region, text, len);
    	return r;
    }
    
    /* for CAA Tag [RFC 6844] */
    uint16_t *
    zparser_conv_tag(region_type *region, const char *text, size_t len)
    {
    	uint16_t *r = NULL;
    	uint8_t *p;
    	const char* ptr;
    
    	if (len < 1) {
    		zc_error_prev_line("invalid tag: zero length");
    		return NULL;
    	}
    	if (len > 15) {
    		zc_error_prev_line("invalid tag %s: longer than 15 characters (%u)",
    			text, (unsigned) len);
    		return NULL;
    	}
    	for (ptr = text; *ptr; ptr++) {
    		if (!isdigit((unsigned char)*ptr) && !islower((unsigned char)*ptr)) {
    			zc_error_prev_line("invalid tag %s: contains invalid char %c",
    				text, *ptr);
    			return NULL;
    		}
    	}
    	r = alloc_rdata(region, len + 1);
    	p = (uint8_t *) (r + 1);
    	*p = len;
    	memmove(p + 1, text, len);
    	return r;
    }
    
    uint16_t *
    zparser_conv_dns_name(region_type *region, const uint8_t* name, size_t len)
    {
    	uint16_t* r = NULL;
    	uint8_t* p = NULL;
    	r = alloc_rdata(region, len);
    	p = (uint8_t *) (r + 1);
    	memcpy(p, name, len);
    
    	return r;
    }
    
    uint16_t *
    zparser_conv_b32(region_type *region, const char *b32)
    {
    	uint8_t buffer[B64BUFSIZE];
    	uint16_t *r = NULL;
    	int i;
    
    	if(strcmp(b32, "-") == 0) {
    		return alloc_rdata_init(region, "", 1);
    	}
    	i = b32_pton(b32, buffer+1, B64BUFSIZE-1);
    	if (i == -1 || i > 255) {
    		zc_error_prev_line("invalid base32 data");
    	} else {
    		buffer[0] = i; /* store length byte */
    		r = alloc_rdata_init(region, buffer, i+1);
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_b64(region_type *region, const char *b64)
    {
    	uint8_t buffer[B64BUFSIZE];
    	uint16_t *r = NULL;
    	int i;
    
    	if(strcmp(b64, "0") == 0) {
    		/* single 0 represents empty buffer */
    		return alloc_rdata(region, 0);
    	}
    	i = __b64_pton(b64, buffer, B64BUFSIZE);
    	if (i == -1) {
    		zc_error_prev_line("invalid base64 data");
    	} else {
    		r = alloc_rdata_init(region, buffer, i);
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_rrtype(region_type *region, const char *text)
    {
    	uint16_t *r = NULL;
    	uint16_t type = rrtype_from_string(text);
    
    	if (type == 0) {
    		zc_error_prev_line("unrecognized RR type '%s'", text);
    	} else {
    		type = htons(type);
    		r = alloc_rdata_init(region, &type, sizeof(type));
    	}
    	return r;
    }
    
    uint16_t *
    zparser_conv_nxt(region_type *region, uint8_t nxtbits[])
    {
    	/* nxtbits[] consists of 16 bytes with some zero's in it
    	 * copy every byte with zero to r and write the length in
    	 * the first byte
    	 */
    	uint16_t i;
    	uint16_t last = 0;
    
    	for (i = 0; i < 16; i++) {
    		if (nxtbits[i] != 0)
    			last = i + 1;
    	}
    
    	return alloc_rdata_init(region, nxtbits, last);
    }
    
    
    /* we potentially have 256 windows, each one is numbered. empty ones
     * should be discarded
     */
    uint16_t *
    zparser_conv_nsec(region_type *region,
    		  uint8_t nsecbits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE])
    {
    	/* nsecbits contains up to 64K of bits which represent the
    	 * types available for a name. Walk the bits according to
    	 * nsec++ draft from jakob
    	 */
    	uint16_t *r;
    	uint8_t *ptr;
    	size_t i,j;
    	uint16_t window_count = 0;
    	uint16_t total_size = 0;
    	uint16_t window_max = 0;
    
    	/* The used windows.  */
    	int used[NSEC_WINDOW_COUNT];
    	/* The last byte used in each the window.  */
    	int size[NSEC_WINDOW_COUNT];
    
    	window_max = 1 + (nsec_highest_rcode / 256);
    
    	/* used[i] is the i-th window included in the nsec
    	 * size[used[0]] is the size of window 0
    	 */
    
    	/* walk through the 256 windows */
    	for (i = 0; i < window_max; ++i) {
    		int empty_window = 1;
    		/* check each of the 32 bytes */
    		for (j = 0; j < NSEC_WINDOW_BITS_SIZE; ++j) {
    			if (nsecbits[i][j] != 0) {
    				size[i] = j + 1;
    				empty_window = 0;
    			}
    		}
    		if (!empty_window) {
    			used[window_count] = i;
    			window_count++;
    		}
    	}
    
    	for (i = 0; i < window_count; ++i) {
    		total_size += sizeof(uint16_t) + size[used[i]];
    	}
    
    	r = alloc_rdata(region, total_size);
    	ptr = (uint8_t *) (r + 1);
    
    	/* now walk used and copy it */
    	for (i = 0; i < window_count; ++i) {
    		ptr[0] = used[i];
    		ptr[1] = size[used[i]];
    		memcpy(ptr + 2, &nsecbits[used[i]], size[used[i]]);
    		ptr += size[used[i]] + 2;
    	}
    
    	return r;
    }
    
    static uint16_t
    svcbparam_lookup_key(const char *key, size_t key_len)
    {
    	char buf[64];
    	char *endptr;
    	unsigned long int key_value;
    
    	if (key_len >= 4  && key_len <= 8 && !strncmp(key, "key", 3)) {
    		memcpy(buf, key + 3, key_len - 3);
    		buf[key_len - 3] = 0;
    		key_value = strtoul(buf, &endptr, 10);
    		if (endptr > buf	/* digits seen */
    		&& *endptr == 0		/* no non-digit chars after digits */
    		&&  key_value <= 65535)	/* no overflow */
    			return key_value;
    
    	} else switch (key_len) {
    	case sizeof("mandatory")-1:
    		if (!strncmp(key, "mandatory", sizeof("mandatory")-1))
    			return SVCB_KEY_MANDATORY;
    		if (!strncmp(key, "echconfig", sizeof("echconfig")-1))
    			return SVCB_KEY_ECH; /* allow "echconfig" as well as "ech" */
    		break;
    
    	case sizeof("alpn")-1:
    		if (!strncmp(key, "alpn", sizeof("alpn")-1))
    			return SVCB_KEY_ALPN;
    		if (!strncmp(key, "port", sizeof("port")-1))
    			return SVCB_KEY_PORT;
    		break;
    
    	case sizeof("no-default-alpn")-1:
    		if (!strncmp( key  , "no-default-alpn"
    		            , sizeof("no-default-alpn")-1))
    			return SVCB_KEY_NO_DEFAULT_ALPN;
    		break;
    
    	case sizeof("ipv4hint")-1:
    		if (!strncmp(key, "ipv4hint", sizeof("ipv4hint")-1))
    			return SVCB_KEY_IPV4HINT;
    		if (!strncmp(key, "ipv6hint", sizeof("ipv6hint")-1))
    			return SVCB_KEY_IPV6HINT;
    		break;
    	case sizeof("dohpath")-1:
    		if (!strncmp(key, "dohpath", sizeof("dohpath")-1))
    			return SVCB_KEY_DOHPATH;
    		break;
    	case sizeof("ech")-1:
    		if (!strncmp(key, "ech", sizeof("ech")-1))
    			return SVCB_KEY_ECH;
    		break;
    	default:
    		break;
    	}
    	if (key_len > sizeof(buf) - 1)
    		zc_error_prev_line("Unknown SvcParamKey");
    	else {
    		memcpy(buf, key, key_len);
    		buf[key_len] = 0;
    		zc_error_prev_line("Unknown SvcParamKey: %s", buf);
    	}
    	/* Although the returned value might be used by the caller,
    	 * the parser has erred, so the zone will not be loaded.
    	 */
    	return -1;
    }
    
    static uint16_t *
    zparser_conv_svcbparam_port_value(region_type *region, const char *val)
    {
    	unsigned long int port;
    	char *endptr;
    	uint16_t *r;
    
    	port = strtoul(val, &endptr, 10);
    	if (endptr > val	/* digits seen */
    	&& *endptr == 0		/* no non-digit chars after digits */
    	&&  port <= 65535) {	/* no overflow */
    
    		r = alloc_rdata(region, 3 * sizeof(uint16_t));
    		r[1] = htons(SVCB_KEY_PORT);
    		r[2] = htons(sizeof(uint16_t));
    		r[3] = htons(port);
    		return r;
    	}
    	zc_error_prev_line("Could not parse port SvcParamValue: \"%s\"", val);
    	return NULL;
    }
    
    static uint16_t *
    zparser_conv_svcbparam_ipv4hint_value(region_type *region, const char *val)
    {
    	uint16_t *r;
    	int count;
    	char ip_str[INET_ADDRSTRLEN+1];
    	char *next_ip_str;
    	uint32_t *ip_wire_dst;
    	size_t i;
    
    	for (i = 0, count = 1; val[i]; i++) {
    		if (val[i] == ',')
    			count += 1;
    		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
    			zc_error_prev_line("Too many IPV4 addresses in ipv4hint");
    			return NULL;
    		}
    	}
    
    	/* count == number of comma's in val + 1, so the actual number of IPv4
    	 * addresses in val
    	 */
    	r = alloc_rdata(region, 2 * sizeof(uint16_t) + IP4ADDRLEN * count);
    	r[1] = htons(SVCB_KEY_IPV4HINT);
    	r[2] = htons(IP4ADDRLEN * count);
    	ip_wire_dst = (void *)&r[3];
    
    	while (count) {
    		if (!(next_ip_str = strchr(val, ','))) {
    			if (inet_pton(AF_INET, val, ip_wire_dst) != 1)
    				break;
    
    			assert(count == 1);
    
    		} else if (next_ip_str - val >= (int)sizeof(ip_str))
    			break;
    
    		else {
    			memcpy(ip_str, val, next_ip_str - val);
    			ip_str[next_ip_str - val] = 0;
    			if (inet_pton(AF_INET, ip_str, ip_wire_dst) != 1) {
    				val = ip_str; /* to use in error reporting below */
    				break;
    			}
    
    			val = next_ip_str + 1;
    		}
    		ip_wire_dst++;
    		count--;
    	}
    	if (count)
    		zc_error_prev_line("Could not parse ipv4hint SvcParamValue: %s", val);
    
    	return r;
    }
    
    static uint16_t *
    zparser_conv_svcbparam_ipv6hint_value(region_type *region, const char *val)
    {
    	uint16_t *r;
    	int i, count;
    	char ip6_str[INET6_ADDRSTRLEN+1];
    	char *next_ip6_str;
    	uint8_t *ipv6_wire_dst;
    
    	for (i = 0, count = 1; val[i]; i++) {
    		if (val[i] == ',')
    			count += 1;
    		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
    			zc_error_prev_line("Too many IPV6 addresses in ipv6hint");
    			return NULL;
    		}
    	}
    
    	/* count == number of comma's in val + 1 
    	 * so actually the number of IPv6 addresses in val
    	 */
    	r = alloc_rdata(region, 2 * sizeof(uint16_t) + IP6ADDRLEN * count);
    	r[1] = htons(SVCB_KEY_IPV6HINT);
    	r[2] = htons(IP6ADDRLEN * count);
    	ipv6_wire_dst = (void *)&r[3];
    
    	while (count) {
    		if (!(next_ip6_str = strchr(val, ','))) {
    			if ((inet_pton(AF_INET6, val, ipv6_wire_dst) != 1))
    				break;
    
    			assert(count == 1);
    
    		} else if (next_ip6_str - val >= (int)sizeof(ip6_str))
    			break;
    
    		else {
    			memcpy(ip6_str, val, next_ip6_str - val);
    			ip6_str[next_ip6_str - val] = 0;
    			if (inet_pton(AF_INET6, ip6_str, ipv6_wire_dst) != 1) {
    				val = ip6_str; /* for error reporting below */
    				break;
    			}
    
    			val = next_ip6_str + 1; /* skip the comma */
    		}
    		ipv6_wire_dst += IP6ADDRLEN;
    		count--;
    	}
    	if (count)
    		zc_error_prev_line("Could not parse ipv6hint SvcParamValue: %s", val);
    
    	return r;
    }
    
    static int
    network_uint16_cmp(const void *a, const void *b)
    {
    	return ((int)read_uint16(a)) - ((int)read_uint16(b));
    }
    
    static uint16_t *
    zparser_conv_svcbparam_mandatory_value(region_type *region,
    		const char *val, size_t val_len)
    {
    	uint16_t *r;
    	size_t i, count;
    	char* next_key;
    	uint16_t* key_dst;
    
    	for (i = 0, count = 1; val[i]; i++) {
    		if (val[i] == ',')
    			count += 1;
    		if (count > SVCB_MAX_COMMA_SEPARATED_VALUES) {
    			zc_error_prev_line("Too many keys in mandatory");
    			return NULL;
    		}
    	}
    
    	r = alloc_rdata(region, (2 + count) * sizeof(uint16_t));
    	r[1] = htons(SVCB_KEY_MANDATORY);
    	r[2] = htons(sizeof(uint16_t) * count);
    	key_dst = (void *)&r[3];
    
    	for(;;) {
    		if (!(next_key = strchr(val, ','))) {
    			*key_dst = htons(svcbparam_lookup_key(val, val_len));
    			break;	
    		} else {
    			*key_dst = htons(svcbparam_lookup_key(val, next_key - val));
    		}
    
    		val_len -= next_key - val + 1;
    		val = next_key + 1; /* skip the comma */
    		key_dst += 1;
    	}
    
    	/* In draft-ietf-dnsop-svcb-https-04 Section 7:
    	 *
    	 *     In wire format, the keys are represented by their numeric
    	 *     values in network byte order, concatenated in ascending order.
    	 */
    	qsort((void *)&r[3], count, sizeof(uint16_t), network_uint16_cmp);
    
    	return r;
    }
    
    static uint16_t *
    zparser_conv_svcbparam_ech_value(region_type *region, const char *b64)
    {
    	uint8_t buffer[B64BUFSIZE];
    	uint16_t *r = NULL;
    	int wire_len;
    
    	if(strcmp(b64, "0") == 0) {
    		/* single 0 represents empty buffer */
    		return alloc_rdata(region, 0);
    	}
    	wire_len = __b64_pton(b64, buffer, B64BUFSIZE);
    	if (wire_len == -1) {
    		zc_error_prev_line("invalid base64 data in ech");
    	} else {
    		r = alloc_rdata(region, 2 * sizeof(uint16_t) + wire_len);
    		r[1] = htons(SVCB_KEY_ECH);
    		r[2] = htons(wire_len);
    		memcpy(&r[3], buffer, wire_len);
    	}
    
    	return r;
    }
    
    static const char* parse_alpn_next_unescaped_comma(const char *val)
    {
    	while (*val) {
    		/* Only return when the comma is not escaped*/
    		if (*val == '\\'){
    			++val;
    			if (!*val)
    				break;
    		} else if (*val == ',')
    				return val;
    
    		val++;
    	}
    	return NULL;
    }
    
    static size_t
    parse_alpn_copy_unescaped(uint8_t *dst, const char *src, size_t len)
    {
    	uint8_t *orig_dst = dst;
    
    	while (len) {
    		if (*src == '\\') {
    			src++;
    			len--;
    			if (!len)
    				break;
    		}
    		*dst++ = *src++;
    		len--;
    	}
    	return (size_t)(dst - orig_dst);
    }
    
    static uint16_t *
    zparser_conv_svcbparam_alpn_value(region_type *region,
    		const char *val, size_t val_len)
    {
    	uint8_t     unescaped_dst[65536];
    	uint8_t    *dst = unescaped_dst;
    	const char *next_str;
    	size_t      str_len;
    	size_t      dst_len;
    	uint16_t   *r = NULL;
    
    	if (val_len > sizeof(unescaped_dst)) {
    		zc_error_prev_line("invalid alpn");
    		return r;
    	}
    	while (val_len) {
    		size_t dst_len;
    
    		str_len = (next_str = parse_alpn_next_unescaped_comma(val))
    		        ? (size_t)(next_str - val) : val_len;
    
    		if (str_len > 255) {
    			zc_error_prev_line("alpn strings need to be"
    					   " smaller than 255 chars");
    			return r;
    		}
    		dst_len = parse_alpn_copy_unescaped(dst + 1, val, str_len);
    		*dst++ = dst_len;
    		 dst  += dst_len;
    
    		if (!next_str)
    			break;
    
    		/* skip the comma for the next iteration */
    		val_len -= next_str - val + 1;
    		val = next_str + 1;
    	}
    	dst_len = dst - unescaped_dst;
    	r = alloc_rdata(region, 2 * sizeof(uint16_t) + dst_len);
    	r[1] = htons(SVCB_KEY_ALPN);
    	r[2] = htons(dst_len);
    	memcpy(&r[3], unescaped_dst, dst_len);
    	return r;
    }
    
    static uint16_t *
    zparser_conv_svcbparam_key_value(region_type *region,
        const char *key, size_t key_len, const char *val, size_t val_len)
    {
    	uint16_t svcparamkey = svcbparam_lookup_key(key, key_len);
    	uint16_t *r;
    
    	switch (svcparamkey) {
    	case SVCB_KEY_PORT:
    		return zparser_conv_svcbparam_port_value(region, val);
    	case SVCB_KEY_IPV4HINT:
    		return zparser_conv_svcbparam_ipv4hint_value(region, val);
    	case SVCB_KEY_IPV6HINT:
    		return zparser_conv_svcbparam_ipv6hint_value(region, val);
    	case SVCB_KEY_MANDATORY:
    		return zparser_conv_svcbparam_mandatory_value(region, val, val_len);
    	case SVCB_KEY_NO_DEFAULT_ALPN:
    		if(zone_is_slave(parser->current_zone->opts))
    			zc_warning_prev_line("no-default-alpn should not have a value");
    		else
    			zc_error_prev_line("no-default-alpn should not have a value");
    		break;
    	case SVCB_KEY_ECH:
    		return zparser_conv_svcbparam_ech_value(region, val);
    	case SVCB_KEY_ALPN:
    		return zparser_conv_svcbparam_alpn_value(region, val, val_len);
    	case SVCB_KEY_DOHPATH:
    		/* fallthrough */
    	default:
    		break;
    	}
    	r = alloc_rdata(region, 2 * sizeof(uint16_t) + val_len);
    	r[1] = htons(svcparamkey);
    	r[2] = htons(val_len);
    	memcpy(r + 3, val, val_len);
    	return r;
    }
    
    uint16_t *
    zparser_conv_svcbparam(region_type *region, const char *key, size_t key_len
                                              , const char *val, size_t val_len)
    {
    	const char *eq;
    	uint16_t *r;
    	uint16_t svcparamkey;
    
    	/* Form <key>="<value>" (or at least with quoted value) */
    	if (val && val_len) {
    		/* Does key end with '=' */
    		if (key_len && key[key_len - 1] == '=')
    			return zparser_conv_svcbparam_key_value(
    			    region, key, key_len - 1, val, val_len);
    
    		zc_error_prev_line( "SvcParam syntax error in param: %s\"%s\""
    		                  , key, val);
    	}
    	assert(val == NULL);
    	if ((eq = memchr(key, '=', key_len))) {
    		size_t new_key_len = eq - key;
    
    		if (key_len - new_key_len - 1 > 0)
    			return zparser_conv_svcbparam_key_value(region,
    			    key, new_key_len, eq+1, key_len - new_key_len - 1);
    		key_len = new_key_len;
    	}
    	/* Some SvcParamKeys require values */
    	svcparamkey = svcbparam_lookup_key(key, key_len);
    	switch (svcparamkey) {
    		case SVCB_KEY_MANDATORY:
    		case SVCB_KEY_ALPN:
    		case SVCB_KEY_PORT:
    		case SVCB_KEY_IPV4HINT:
    		case SVCB_KEY_IPV6HINT:
    		case SVCB_KEY_DOHPATH:
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line("value expected for SvcParam: %s", key);
    			else
    				zc_error_prev_line("value expected for SvcParam: %s", key);
    			break;
    		default:
    			break;
    	}
    	/* SvcParam is only a SvcParamKey */
    	r = alloc_rdata(region, 2 * sizeof(uint16_t));
    	r[1] = htons(svcparamkey);
    	r[2] = 0;
    	return r;
    }
    
    /* Parse an int terminated in the specified range. */
    static int
    parse_int(const char *str,
    	  char **end,
    	  int *result,
    	  const char *name,
    	  int min,
    	  int max)
    {
    	*result = (int) strtol(str, end, 10);
    	if (*result < min || *result > max) {
    		zc_error_prev_line("%s must be within the range [%d .. %d]",
    				   name,
    				   min,
    				   max);
    		return 0;
    	} else {
    		return 1;
    	}
    }
    
    /* RFC1876 conversion routines */
    static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
    				1000000,10000000,100000000,1000000000};
    
    /*
     * Converts ascii size/precision X * 10**Y(cm) to 0xXY.
     * Sets the given pointer to the last used character.
     *
     */
    static uint8_t
    precsize_aton (char *cp, char **endptr)
    {
    	unsigned int mval = 0, cmval = 0;
    	uint8_t retval = 0;
    	int exponent;
    	int mantissa;
    
    	while (isdigit((unsigned char)*cp))
    		mval = mval * 10 + hexdigit_to_int(*cp++);
    
    	if (*cp == '.') {	/* centimeters */
    		cp++;
    		if (isdigit((unsigned char)*cp)) {
    			cmval = hexdigit_to_int(*cp++) * 10;
    			if (isdigit((unsigned char)*cp)) {
    				cmval += hexdigit_to_int(*cp++);
    			}
    		}
    	}
    
    	if(mval >= poweroften[7]) {
    		assert(poweroften[7] != 0);
    		/* integer overflow possible for *100 */
    		mantissa = mval / poweroften[7];
    		exponent = 9; /* max */
    	}
    	else {
    		cmval = (mval * 100) + cmval;
    
    		for (exponent = 0; exponent < 9; exponent++)
    			if (cmval < poweroften[exponent+1])
    				break;
    
    		assert(poweroften[exponent] != 0);
    		mantissa = cmval / poweroften[exponent];
    	}
    	if (mantissa > 9)
    		mantissa = 9;
    
    	retval = (mantissa << 4) | exponent;
    
    	if (*cp == 'm') cp++;
    
    	*endptr = cp;
    
    	return (retval);
    }
    
    /*
     * Parses a specific part of rdata.
     *
     * Returns:
     *
     *	number of elements parsed
     *	zero on error
     *
     */
    uint16_t *
    zparser_conv_loc(region_type *region, char *str)
    {
    	uint16_t *r;
    	uint32_t *p;
    	int i;
    	int deg, min, secs;	/* Secs is stored times 1000.  */
    	uint32_t lat = 0, lon = 0, alt = 0;
    	/* encoded defaults: version=0 sz=1m hp=10000m vp=10m */
    	uint8_t vszhpvp[4] = {0, 0x12, 0x16, 0x13};
    	char *start;
    	double d;
    
    	for(;;) {
    		deg = min = secs = 0;
    
    		/* Degrees */
    		if (*str == '\0') {
    			zc_error_prev_line("unexpected end of LOC data");
    			return NULL;
    		}
    
    		if (!parse_int(str, &str, &deg, "degrees", 0, 180))
    			return NULL;
    		if (!isspace((unsigned char)*str)) {
    			zc_error_prev_line("space expected after degrees");
    			return NULL;
    		}
    		++str;
    
    		/* Minutes? */
    		if (isdigit((unsigned char)*str)) {
    			if (!parse_int(str, &str, &min, "minutes", 0, 60))
    				return NULL;
    			if (!isspace((unsigned char)*str)) {
    				zc_error_prev_line("space expected after minutes");
    				return NULL;
    			}
    			++str;
    		}
    
    		/* Seconds? */
    		if (isdigit((unsigned char)*str)) {
    			start = str;
    			if (!parse_int(str, &str, &i, "seconds", 0, 60)) {
    				return NULL;
    			}
    
    			if (*str == '.' && !parse_int(str + 1, &str, &i, "seconds fraction", 0, 999)) {
    				return NULL;
    			}
    
    			if (!isspace((unsigned char)*str)) {
    				zc_error_prev_line("space expected after seconds");
    				return NULL;
    			}
    			/* No need for precision specifiers, it's a double */
    			if (sscanf(start, "%lf", &d) != 1) {
    				zc_error_prev_line("error parsing seconds");
    			}
    
    			if (d < 0.0 || d > 60.0) {
    				zc_error_prev_line("seconds not in range 0.0 .. 60.0");
    			}
    
    			secs = (int) (d * 1000.0 + 0.5);
    			++str;
    		}
    
    		switch(*str) {
    		case 'N':
    		case 'n':
    			lat = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs);
    			break;
    		case 'E':
    		case 'e':
    			lon = ((uint32_t)1<<31) + (deg * 3600000 + min * 60000 + secs);
    			break;
    		case 'S':
    		case 's':
    			lat = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs);
    			break;
    		case 'W':
    		case 'w':
    			lon = ((uint32_t)1<<31) - (deg * 3600000 + min * 60000 + secs);
    			break;
    		default:
    			zc_error_prev_line("invalid latitude/longtitude: '%c'", *str);
    			return NULL;
    		}
    		++str;
    
    		if (lat != 0 && lon != 0)
    			break;
    
    		if (!isspace((unsigned char)*str)) {
    			zc_error_prev_line("space expected after latitude/longitude");
    			return NULL;
    		}
    		++str;
    	}
    
    	/* Altitude */
    	if (*str == '\0') {
    		zc_error_prev_line("unexpected end of LOC data");
    		return NULL;
    	}
    
    	if (!isspace((unsigned char)*str)) {
    		zc_error_prev_line("space expected before altitude");
    		return NULL;
    	}
    	++str;
    
    	start = str;
    
    	/* Sign */
    	if (*str == '+' || *str == '-') {
    		++str;
    	}
    
    	/* Meters of altitude... */
    	if(strtol(str, &str, 10) == LONG_MAX) {
    		zc_error_prev_line("altitude too large, number overflow");
    		return NULL;
    	}
    	switch(*str) {
    	case ' ':
    	case '\0':
    	case 'm':
    		break;
    	case '.':
    		if (!parse_int(str + 1, &str, &i, "altitude fraction", 0, 99)) {
    			return NULL;
    		}
    		if (!isspace((unsigned char)*str) && *str != '\0' && *str != 'm') {
    			zc_error_prev_line("altitude fraction must be a number");
    			return NULL;
    		}
    		break;
    	default:
    		zc_error_prev_line("altitude must be expressed in meters");
    		return NULL;
    	}
    	if (!isspace((unsigned char)*str) && *str != '\0')
    		++str;
    
    	if (sscanf(start, "%lf", &d) != 1) {
    		zc_error_prev_line("error parsing altitude");
    	}
    
    	alt = (uint32_t) (10000000.0 + d * 100 + 0.5);
    
    	if (!isspace((unsigned char)*str) && *str != '\0') {
    		zc_error_prev_line("unexpected character after altitude");
    		return NULL;
    	}
    
    	/* Now parse size, horizontal precision and vertical precision if any */
    	for(i = 1; isspace((unsigned char)*str) && i <= 3; i++) {
    		vszhpvp[i] = precsize_aton(str + 1, &str);
    
    		if (!isspace((unsigned char)*str) && *str != '\0') {
    			zc_error_prev_line("invalid size or precision");
    			return NULL;
    		}
    	}
    
    	/* Allocate required space... */
    	r = alloc_rdata(region, 16);
    	p = (uint32_t *) (r + 1);
    
    	memmove(p, vszhpvp, 4);
    	write_uint32(p + 1, lat);
    	write_uint32(p + 2, lon);
    	write_uint32(p + 3, alt);
    
    	return r;
    }
    
    /*
     * Convert an APL RR RDATA element.
     */
    uint16_t *
    zparser_conv_apl_rdata(region_type *region, char *str)
    {
    	int negated = 0;
    	uint16_t address_family;
    	uint8_t prefix;
    	uint8_t maximum_prefix;
    	uint8_t length;
    	uint8_t address[IP6ADDRLEN];
    	char *colon = strchr(str, ':');
    	char *slash = strchr(str, '/');
    	int af;
    	int rc;
    	uint16_t rdlength;
    	uint16_t *r;
    	uint8_t *t;
    	char *end;
    	long p;
    
    	if (!colon) {
    		zc_error("address family separator is missing");
    		return NULL;
    	}
    	if (!slash) {
    		zc_error("prefix separator is missing");
    		return NULL;
    	}
    
    	*colon = '\0';
    	*slash = '\0';
    
    	if (*str == '!') {
    		negated = 1;
    		++str;
    	}
    
    	if (strcmp(str, "1") == 0) {
    		address_family = htons(1);
    		af = AF_INET;
    		length = sizeof(in_addr_t);
    		maximum_prefix = length * 8;
    	} else if (strcmp(str, "2") == 0) {
    		address_family = htons(2);
    		af = AF_INET6;
    		length = IP6ADDRLEN;
    		maximum_prefix = length * 8;
    	} else {
    		zc_error("invalid address family '%s'", str);
    		return NULL;
    	}
    
    	rc = inet_pton(af, colon + 1, address);
    	if (rc == 0) {
    		zc_error("invalid address '%s'", colon + 1);
    		return NULL;
    	} else if (rc == -1) {
    		zc_error("inet_pton failed: %s", strerror(errno));
    		return NULL;
    	}
    
    	/* Strip trailing zero octets.	*/
    	while (length > 0 && address[length - 1] == 0)
    		--length;
    
    
    	p = strtol(slash + 1, &end, 10);
    	if (p < 0 || p > maximum_prefix) {
    		zc_error("prefix not in the range 0 .. %d", maximum_prefix);
    		return NULL;
    	} else if (*end != '\0') {
    		zc_error("invalid prefix '%s'", slash + 1);
    		return NULL;
    	}
    	prefix = (uint8_t) p;
    
    	rdlength = (sizeof(address_family) + sizeof(prefix) + sizeof(length)
    		    + length);
    	r = alloc_rdata(region, rdlength);
    	t = (uint8_t *) (r + 1);
    
    	memcpy(t, &address_family, sizeof(address_family));
    	t += sizeof(address_family);
    	memcpy(t, &prefix, sizeof(prefix));
    	t += sizeof(prefix);
    	memcpy(t, &length, sizeof(length));
    	if (negated) {
    		*t |= APL_NEGATION_MASK;
    	}
    	t += sizeof(length);
    	memcpy(t, address, length);
    
    	return r;
    }
    
    /*
     * Below some function that also convert but not to wireformat
     * but to "normal" (int,long,char) types
     */
    
    uint32_t
    zparser_ttl2int(const char *ttlstr, int* error)
    {
    	/* convert a ttl value to a integer
    	 * return the ttl in a int
    	 * -1 on error
    	 */
    
    	uint32_t ttl;
    	const char *t;
    
    	ttl = strtottl(ttlstr, &t);
    	if (*t != 0) {
    		zc_error_prev_line("invalid TTL value: %s",ttlstr);
    		*error = 1;
    	}
    
    	return ttl;
    }
    
    
    void
    zadd_rdata_wireformat(uint16_t *data)
    {
    	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
    		zc_error_prev_line("too many rdata elements");
    	} else {
    		parser->current_rr.rdatas[parser->current_rr.rdata_count].data
    			= data;
    		++parser->current_rr.rdata_count;
    	}
    }
    
    /**
     * Used for TXT RR's to grow with undefined number of strings.
     */
    void
    zadd_rdata_txt_wireformat(uint16_t *data, int first)
    {
    	rdata_atom_type *rd;
    	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
    		zc_error_prev_line("too many rdata txt elements");
    		return;
    	}
    	
    	/* First STR in str_seq, allocate 65K in first unused rdata
    	 * else find last used rdata */
    	if (first) {
    		rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count];
    		if ((rd->data = (uint16_t *) region_alloc(parser->rr_region,
    			sizeof(uint16_t) + 65535 * sizeof(uint8_t))) == NULL) {
    			zc_error_prev_line("Could not allocate memory for TXT RR");
    			return;
    		}
    		parser->current_rr.rdata_count++;
    		rd->data[0] = 0;
    	}
    	else
    		rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count-1];
    	
    	if ((size_t)rd->data[0] + (size_t)data[0] > 65535) {
    		zc_error_prev_line("too large rdata element");
    		return;
    	}
    	
    	memcpy((uint8_t *)rd->data + 2 + rd->data[0], data + 1, data[0]);
    	rd->data[0] += data[0];
    }
    
    /**
     * Clean up after last call of zadd_rdata_txt_wireformat
     */
    void
    zadd_rdata_txt_clean_wireformat()
    {
    	uint16_t *tmp_data;
    	rdata_atom_type *rd = &parser->current_rr.rdatas[parser->current_rr.rdata_count-1];
    	if(!rd || !rd->data)
    		return; /* previous syntax failure */
    	if ((tmp_data = (uint16_t *) region_alloc(parser->region, 
    		((size_t)rd->data[0]) + ((size_t)2))) != NULL) {
    		memcpy(tmp_data, rd->data, rd->data[0] + 2);
    		/* rd->data of u16+65535 freed when rr_region is freed */
    		rd->data = tmp_data;
    	}
    	else {
    		/* We could not get memory in non-volatile region */
    		zc_error_prev_line("could not allocate memory for rdata");
    		return;
    	}
    }
    
    static int
    svcparam_key_cmp(const void *a, const void *b)
    {
    	return ((int)read_uint16(rdata_atom_data(*(rdata_atom_type *)a)))
    	     - ((int)read_uint16(rdata_atom_data(*(rdata_atom_type *)b)));
    }
    
    void
    zadd_rdata_svcb_check_wireformat()
    {
    	size_t i;
    	uint8_t paramkeys[65536];
    	int prev_key = - 1;
    	int key = 0;
    	size_t size;
    	uint16_t *mandatory_values;
    
    	if (parser->current_rr.rdata_count <= 2) {
    		if (!parser->error_occurred)
    			zc_error_prev_line("invalid SVCB or HTTPS rdata");
    		return;
    	} else for (i = 2; i < parser->current_rr.rdata_count; i++) {
    		if (parser->current_rr.rdatas[i].data == NULL
    		||  rdata_atom_data(parser->current_rr.rdatas[i]) == NULL
    		||  rdata_atom_size(parser->current_rr.rdatas[i]) < 4) {
    			if (!parser->error_occurred)
    				zc_error_prev_line("invalid SVCB or HTTPS rdata");
    			return;
    		}
    	}
    	/* After this point, all rdatas do have data larger than 4 bytes.
    	 * So we may assume a uint16_t SVCB key followed by uint16_t length
    	 * in each rdata in the remainder of this function.
    	 */
    	memset(paramkeys, 0, sizeof(paramkeys));
    	/* 
    	 * In draft-ietf-dnsop-svcb-https-04 Section 7:
    	 * In wire format, the keys are represented by their numeric values in
    	 * network byte order, concatenated in ascending order.
    	 *
    	 * svcparam_key_cmp assumes the rdatas to have a SVCB key, which is
    	 * safe because we checked.
    	 *
    	 */
    	qsort( (void *)&parser->current_rr.rdatas[2]
    	     , parser->current_rr.rdata_count - 2
    	     , sizeof(rdata_atom_type)
    	     , svcparam_key_cmp
    	     );
    
    	for (i = 2; i < parser->current_rr.rdata_count; i++) {
    		assert(parser->current_rr.rdatas[i].data);
    		assert(rdata_atom_data(parser->current_rr.rdatas[i]));
    		assert(rdata_atom_size(parser->current_rr.rdatas[i]) >= sizeof(uint16_t));
    		 
    		key = read_uint16(rdata_atom_data(parser->current_rr.rdatas[i]));
    
    		/* In draft-ietf-dnsop-svcb-https-04 Section 7:
    		 *
    		 *     Keys (...) MUST NOT appear more than once.
    		 * 
    		 * If they key has already been seen, we have a duplicate
    		 */
    		if (!paramkeys[key])
    			/* keep track of keys that are present */
    			paramkeys[key] = 1;
    
    		else if (key < SVCPARAMKEY_COUNT) {
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line(
    					"Duplicate key found: %s",
    					svcparamkey_strs[key]);
    			else {
    				zc_error_prev_line(
    					"Duplicate key found: %s",
    					svcparamkey_strs[key]);
    			}
    		} else if(zone_is_slave(parser->current_zone->opts))
    			zc_warning_prev_line(
    					"Duplicate key found: key%d", key);
    		else
    			zc_error_prev_line(
    					"Duplicate key found: key%d", key);
    	}
    	/* Checks when a mandatory key is present */
    	if (!paramkeys[SVCB_KEY_MANDATORY])
    		return;
    
    	size = rdata_atom_size(parser->current_rr.rdatas[2]);
    	assert(size >= 4);
    	mandatory_values = (void*)rdata_atom_data(parser->current_rr.rdatas[2]);
    	mandatory_values += 2; /* skip the key type and length */
    
    	if (size % 2)
    		zc_error_prev_line("mandatory rdata must be a multiple of shorts");
    		
    	else for (i = 0; i < (size - 4)/2; i++) {
    		key = ntohs(mandatory_values[i]);
    
    		if (paramkeys[key])
    			; /* pass */
    
    		else if (key < SVCPARAMKEY_COUNT) {
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line("mandatory SvcParamKey: %s is missing "
    						     "the record", svcparamkey_strs[key]);
    			else
    				zc_error_prev_line("mandatory SvcParamKey: %s is missing "
    						   "the record", svcparamkey_strs[key]);
    		} else {
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line("mandatory SvcParamKey: key%d is missing "
    						     "the record", key);
    			else
    				zc_error_prev_line("mandatory SvcParamKey: key%d is missing "
    						   "the record", key);
    		}
    
    		/* In draft-ietf-dnsop-svcb-https-04 Section 8
    		 * automatically mandatory MUST NOT appear in its own value-list
    		 */
    		if (key == SVCB_KEY_MANDATORY) {
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line("mandatory MUST not be included"
    						     " as mandatory parameter");
    			else
    				zc_error_prev_line("mandatory MUST not be included"
    						   " as mandatory parameter");
    		}
    		if (key == prev_key) {
    			if(zone_is_slave(parser->current_zone->opts))
    				zc_warning_prev_line("Keys inSvcParam mandatory "
    				                   "MUST NOT appear more than once.");
    			else
    				zc_error_prev_line("Keys in SvcParam mandatory "
    				                   "MUST NOT appear more than once.");
    		}
    		prev_key = key;
    	}
    }
    
    void
    zadd_rdata_domain(domain_type *domain)
    {
    	if (parser->current_rr.rdata_count >= MAXRDATALEN) {
    		zc_error_prev_line("too many rdata elements");
    	} else {
    		parser->current_rr.rdatas[parser->current_rr.rdata_count].domain
    			= domain;
    		domain->usage ++; /* new reference to domain */
    		++parser->current_rr.rdata_count;
    	}
    }
    
    void
    parse_unknown_rdata(uint16_t type, uint16_t *wireformat)
    {
    	buffer_type packet;
    	uint16_t size;
    	ssize_t rdata_count;
    	ssize_t i;
    	rdata_atom_type *rdatas;
    
    	if (wireformat) {
    		size = *wireformat;
    	} else {
    		return;
    	}
    
    	buffer_create_from(&packet, wireformat + 1, *wireformat);
    	rdata_count = rdata_wireformat_to_rdata_atoms(parser->region,
    						      parser->db->domains,
    						      type,
    						      size,
    						      &packet,
    						      &rdatas);
    	if (rdata_count == -1) {
    		zc_error_prev_line("bad unknown RDATA");
    		return;
    	}
    
    	for (i = 0; i < rdata_count; ++i) {
    		if (rdata_atom_is_domain(type, i)) {
    			zadd_rdata_domain(rdatas[i].domain);
    		} else {
    			zadd_rdata_wireformat(rdatas[i].data);
    		}
    	}
    	region_recycle(parser->region, rdatas,
    		rdata_count*sizeof(rdata_atom_type));
    }
    
    
    /*
     * Compares two rdata arrays.
     *
     * Returns:
     *
     *	zero if they are equal
     *	non-zero if not
     *
     */
    static int
    zrdatacmp(uint16_t type, rr_type *a, rr_type *b)
    {
    	int i = 0;
    
    	assert(a);
    	assert(b);
    
    	/* One is shorter than another */
    	if (a->rdata_count != b->rdata_count)
    		return 1;
    
    	/* Compare element by element */
    	for (i = 0; i < a->rdata_count; ++i) {
    		if (rdata_atom_is_domain(type, i)) {
    			if (rdata_atom_domain(a->rdatas[i])
    			    != rdata_atom_domain(b->rdatas[i]))
    			{
    				return 1;
    			}
    		} else if(rdata_atom_is_literal_domain(type, i)) {
    			if (rdata_atom_size(a->rdatas[i])
    			    != rdata_atom_size(b->rdatas[i]))
    				return 1;
    			if (!dname_equal_nocase(rdata_atom_data(a->rdatas[i]),
    				   rdata_atom_data(b->rdatas[i]),
    				   rdata_atom_size(a->rdatas[i])))
    				return 1;
    		} else {
    			if (rdata_atom_size(a->rdatas[i])
    			    != rdata_atom_size(b->rdatas[i]))
    			{
    				return 1;
    			}
    			if (memcmp(rdata_atom_data(a->rdatas[i]),
    				   rdata_atom_data(b->rdatas[i]),
    				   rdata_atom_size(a->rdatas[i])) != 0)
    			{
    				return 1;
    			}
    		}
    	}
    
    	/* Otherwise they are equal */
    	return 0;
    }
    
    /*
     *
     * Opens a zone file.
     *
     * Returns:
     *
     *	- pointer to the parser structure
     *	- NULL on error and errno set
     *
     */
    static int
    zone_open(const char *filename, uint32_t ttl, uint16_t klass,
    	  const dname_type *origin)
    {
    	/* Open the zone file... */
    	if (strcmp(filename, "-") == 0) {
    		yyin = stdin;
    		filename = "<stdin>";
    		warn_if_directory("zonefile from stdin", yyin, filename);
    	} else {
    		if (!(yyin = fopen(filename, "r"))) {
    			return 0;
    		}
    		warn_if_directory("zonefile", yyin, filename);
    	}
    
    	zparser_init(filename, ttl, klass, origin);
    
    	return 1;
    }
    
    
    void
    set_bitnsec(uint8_t bits[NSEC_WINDOW_COUNT][NSEC_WINDOW_BITS_SIZE],
    	    uint16_t index)
    {
    	/*
    	 * The bits are counted from left to right, so bit #0 is the
    	 * left most bit.
    	 */
    	uint8_t window = index / 256;
    	uint8_t bit = index % 256;
    
    	bits[window][bit / 8] |= (1 << (7 - bit % 8));
    }
    
    
    static int
    has_soa(domain_type* domain)
    {
    	rrset_type* p = NULL;
    	if(!domain) return 0;
    	for(p = domain->rrsets; p; p = p->next)
    		if(rrset_rrtype(p) == TYPE_SOA)
    			return 1;
    	return 0;
    }
    
    int
    process_rr(void)
    {
    	zone_type *zone = parser->current_zone;
    	rr_type *rr = &parser->current_rr;
    	rrset_type *rrset;
    	size_t max_rdlength;
    	int i;
    	rrtype_descriptor_type *descriptor
    		= rrtype_descriptor_by_type(rr->type);
    
    	/* We only support IN class */
    	if (rr->klass != CLASS_IN) {
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("only class IN is supported");
    		else
    			zc_error_prev_line("only class IN is supported");
    		return 0;
    	}
    
    	/* Make sure the maximum RDLENGTH does not exceed 65535 bytes.	*/
    	max_rdlength = rdata_maximum_wireformat_size(
    		descriptor, rr->rdata_count, rr->rdatas);
    
    	if (max_rdlength > MAX_RDLENGTH) {
    		zc_error_prev_line("maximum rdata length exceeds %d octets", MAX_RDLENGTH);
    		return 0;
    	}
    
    	/* We cannot print invalid owner names,
    	 * so error on that before it is used in printing other errors.
    	 */
    	if (rr->owner == error_domain
    	||  domain_dname(rr->owner) == error_dname) {
    		zc_error_prev_line("invalid owner name");
    		return 0;
    	}
    
    	/* we have the zone already */
    	assert(zone);
    	if (rr->type == TYPE_SOA) {
    		if (rr->owner != zone->apex) {
    			char s[MAXDOMAINLEN*5];
    			snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex));
    			zc_error_prev_line(
    				"SOA record with invalid domain name, '%s' is not '%s'", domain_to_string(rr->owner), s);
    			return 0;
    		}
    		if(has_soa(rr->owner)) {
    			if(zone_is_slave(zone->opts))
    				zc_warning_prev_line("this SOA record was already encountered");
    			else
    				zc_error_prev_line("this SOA record was already encountered");
    			return 0;
    		}
    		rr->owner->is_apex = 1;
    	}
    
    	if (!domain_is_subdomain(rr->owner, zone->apex))
    	{
    		char s[MAXDOMAINLEN*5];
    		snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex));
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s);
    		else
    			zc_error_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s);
    		return 0;
    	}
    
    	/* Do we have this type of rrset already? */
    	rrset = domain_find_rrset(rr->owner, zone, rr->type);
    	if (!rrset) {
    		rrset = (rrset_type *) region_alloc(parser->region,
    						    sizeof(rrset_type));
    		rrset->zone = zone;
    		rrset->rr_count = 1;
    		rrset->rrs = (rr_type *) region_alloc(parser->region,
    						      sizeof(rr_type));
    		rrset->rrs[0] = *rr;
    
    		/* Add it */
    		domain_add_rrset(rr->owner, rrset);
    	} else {
    		rr_type* o;
    		if (rr->type != TYPE_RRSIG && rrset->rrs[0].ttl != rr->ttl) {
    			zc_warning_prev_line(
    				"%s TTL %u does not match the TTL %u of the %s RRset",
    				domain_to_string(rr->owner), (unsigned)rr->ttl,
    				(unsigned)rrset->rrs[0].ttl,
    				rrtype_to_string(rr->type));
    		}
    
    		/* Search for possible duplicates... */
    		for (i = 0; i < rrset->rr_count; i++) {
    			if (!zrdatacmp(rr->type, rr, &rrset->rrs[i])) {
    				break;
    			}
    		}
    
    		/* Discard the duplicates... */
    		if (i < rrset->rr_count) {
    			/* add rdatas to recycle bin. */
    			size_t i;
    			for (i = 0; i < rr->rdata_count; i++) {
    				if(!rdata_atom_is_domain(rr->type, i))
    					region_recycle(parser->region, rr->rdatas[i].data,
    						rdata_atom_size(rr->rdatas[i])
    						+ sizeof(uint16_t));
    			}
    			region_recycle(parser->region, rr->rdatas,
    				sizeof(rdata_atom_type)*rr->rdata_count);
    			return 0;
    		}
    		if(rrset->rr_count == 65535) {
    			zc_error_prev_line("too many RRs for domain RRset");
    			return 0;
    		}
    
    		/* Add it... */
    		o = rrset->rrs;
    		rrset->rrs = (rr_type *) region_alloc_array(parser->region,
    			(rrset->rr_count + 1), sizeof(rr_type));
    		memcpy(rrset->rrs, o, (rrset->rr_count) * sizeof(rr_type));
    		region_recycle(parser->region, o,
    			(rrset->rr_count) * sizeof(rr_type));
    		rrset->rrs[rrset->rr_count] = *rr;
    		++rrset->rr_count;
    	}
    
    	if(rr->type == TYPE_DNAME && rrset->rr_count > 1) {
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("multiple DNAMEs at the same name");
    		else
    			zc_error_prev_line("multiple DNAMEs at the same name");
    	}
    	if(rr->type == TYPE_CNAME && rrset->rr_count > 1) {
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("multiple CNAMEs at the same name");
    		else
    			zc_error_prev_line("multiple CNAMEs at the same name");
    	}
    	if((rr->type == TYPE_DNAME && domain_find_rrset(rr->owner, zone, TYPE_CNAME))
    	 ||(rr->type == TYPE_CNAME && domain_find_rrset(rr->owner, zone, TYPE_DNAME))) {
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("DNAME and CNAME at the same name");
    		else
    			zc_error_prev_line("DNAME and CNAME at the same name");
    	}
    	if(domain_find_rrset(rr->owner, zone, TYPE_CNAME) &&
    		domain_find_non_cname_rrset(rr->owner, zone)) {
    		if(zone_is_slave(zone->opts))
    			zc_warning_prev_line("CNAME and other data at the same name");
    		else
    			zc_error_prev_line("CNAME and other data at the same name");
    	}
    
    	/* Check we have SOA */
    	if(rr->owner == zone->apex)
    		apex_rrset_checks(parser->db, rrset, rr->owner);
    
    	if(parser->line % ZONEC_PCT_COUNT == 0 && time(NULL) > startzonec + ZONEC_PCT_TIME) {
    		struct stat buf;
    		startzonec = time(NULL);
    		buf.st_size = 0;
    		fstat(fileno(yyin), &buf);
    		if(buf.st_size == 0) buf.st_size = 1;
    		VERBOSITY(1, (LOG_INFO, "parse %s %d %%",
    			parser->current_zone->opts->name,
    			(int)((uint64_t)ftell(yyin)*(uint64_t)100/(uint64_t)buf.st_size)));
    	}
    	++totalrrs;
    	return 1;
    }
    
    /*
     * Find rrset type for any zone
     */
    static rrset_type*
    domain_find_rrset_any(domain_type *domain, uint16_t type)
    {
    	rrset_type *result = domain->rrsets;
    	while (result) {
    		if (rrset_rrtype(result) == type) {
    			return result;
    		}
    		result = result->next;
    	}
    	return NULL;
    }
    
    /*
     * Check for DNAME type. Nothing is allowed below it
     */
    static void
    check_dname(zone_type* zone)
    {
    	domain_type* domain;
    	for(domain = zone->apex; domain && domain_is_subdomain(domain,
    		zone->apex); domain=domain_next(domain))
    	{
    		if(domain->is_existing) {
    			/* there may not be DNAMEs above it */
    			domain_type* parent = domain->parent;
    #ifdef NSEC3
    			if(domain_has_only_NSEC3(domain, NULL))
    				continue;
    #endif
    			while(parent) {
    				if(domain_find_rrset_any(parent, TYPE_DNAME)) {
    					zc_error("While checking node %s,",
    						domain_to_string(domain));
    					zc_error("DNAME at %s has data below it. "
    						"This is not allowed (rfc 2672).",
    						domain_to_string(parent));
    					return;
    				}
    				parent = parent->parent;
    			}
    		}
    	}
    }
    
    /*
     * Reads the specified zone into the memory
     * nsd_options can be NULL if no config file is passed.
     */
    unsigned int
    zonec_read(const char* name, const char* zonefile, zone_type* zone)
    {
    	const dname_type *dname;
    
    	totalrrs = 0;
    	startzonec = time(NULL);
    	parser->errors = 0;
    
    	dname = dname_parse(parser->rr_region, name);
    	if (!dname) {
    		zc_error("incorrect zone name '%s'", name);
    		return 1;
    	}
    
    	/* Open the zone file */
    	if (!zone_open(zonefile, 3600, CLASS_IN, dname)) {
    		zc_error("cannot open '%s': %s", zonefile, strerror(errno));
    		return 1;
    	}
    	parser->current_zone = zone;
    
    	/* Parse and process all RRs.  */
    	yyparse();
    
    	/* remove origin if it was unused */
    	if(parser->origin != error_domain)
    		domain_table_deldomain(parser->db, parser->origin);
    	/* rr_region has been emptied by now */
    	dname = dname_parse(parser->rr_region, name);
    
    	/* check if zone file contained a correct SOA record */
    	if (!parser->current_zone) {
    		zc_error("zone configured as '%s' has no content.", name);
    	} else if(!parser->current_zone->soa_rrset ||
    		parser->current_zone->soa_rrset->rr_count == 0) {
    		zc_error("zone configured as '%s' has no SOA record.", name);
    	} else if(dname_compare(domain_dname(
    		parser->current_zone->soa_rrset->rrs[0].owner), dname) != 0) {
    		zc_error("zone configured as '%s', but SOA has owner '%s'.",
    			name, domain_to_string(
    			parser->current_zone->soa_rrset->rrs[0].owner));
    	}
    	region_free_all(parser->rr_region);
    
    	parser_flush();
    	fclose(yyin);
    	if(!zone_is_slave(zone->opts))
    		check_dname(zone);
    
    	parser->filename = NULL;
    	return parser->errors;
    }
    
    
    /*
     * setup parse
     */
    void
    zonec_setup_parser(namedb_type* db)
    {
    	region_type* rr_region = region_create(xalloc, free);
    	parser = zparser_create(db->region, rr_region, db);
    	assert(parser);
    	/* Unique pointers used to mark errors.	 */
    	error_dname = (dname_type *) region_alloc(db->region, 1);
    	error_domain = (domain_type *) region_alloc(db->region, 1);
    	/* Open the network database */
    	setprotoent(1);
    	setservent(1);
    }
    
    /** desetup parse */
    void
    zonec_desetup_parser(void)
    {
    	if(parser) {
    		endservent();
    		endprotoent();
    		region_destroy(parser->rr_region);
    		/* removed when parser->region(=db->region) is destroyed:
    		 * region_recycle(parser->region, (void*)error_dname, 1);
    		 * region_recycle(parser->region, (void*)error_domain, 1); */
    		/* clear memory for exit, but this is not portable to
    		 * other versions of lex. yylex_destroy(); */
    #ifdef MEMCLEAN /* OS collects memory pages */
    		yylex_destroy();
    #endif
    	}
    }
    
    static domain_table_type* orig_domains = NULL;
    static region_type* orig_region = NULL;
    static region_type* orig_dbregion = NULL;
    
    /** setup for string parse */
    void
    zonec_setup_string_parser(region_type* region, domain_table_type* domains)
    {
    	assert(parser); /* global parser must be setup */
    	orig_domains = parser->db->domains;
    	orig_region = parser->region;
    	orig_dbregion = parser->db->region;
    	parser->region = region;
    	parser->db->region = region;
    	parser->db->domains = domains;
    	zparser_init("string", 3600, CLASS_IN, domain_dname(domains->root));
    }
    
    /** desetup string parse */
    void
    zonec_desetup_string_parser(void)
    {
    	parser->region = orig_region;
    	parser->db->domains = orig_domains;
    	parser->db->region = orig_dbregion;
    }
    
    /** parse a string into temporary storage */
    int
    zonec_parse_string(region_type* region, domain_table_type* domains,
    	zone_type* zone, char* str, domain_type** parsed, int* num_rrs)
    {
    	int errors;
    	zonec_setup_string_parser(region, domains);
    	parser->current_zone = zone;
    	parser->errors = 0;
    	totalrrs = 0;
    	startzonec = time(NULL)+100000; /* disable */
    	parser_push_stringbuf(str);
    	yyparse();
    	parser_pop_stringbuf();
    	errors = parser->errors;
    	*num_rrs = totalrrs;
    	if(*num_rrs == 0)
    		*parsed = NULL;
    	else	*parsed = parser->prev_dname;
    	/* remove origin if it was not used during the parse */
    	if(parser->origin != error_domain)
    		domain_table_deldomain(parser->db, parser->origin);
    	region_free_all(parser->rr_region);
    	zonec_desetup_string_parser();
    	parser_flush();
    	return errors;
    }
    
    /** check SSHFP type for failures and emit warnings */
    void check_sshfp(void)
    {
    	uint8_t hash;
    	uint16_t size;
    	if(parser->current_rr.rdata_count < 3)
    		return; /* cannot check it, too few rdata elements */
    	if(!parser->current_rr.rdatas[0].data ||
    		!parser->current_rr.rdatas[1].data ||
    		!parser->current_rr.rdatas[2].data ||
    		!parser->current_rr.owner)
    		return; /* cannot check, NULLs (due to earlier errors) */
    	if(rdata_atom_size(parser->current_rr.rdatas[1]) != 1)
    		return; /* wrong size of the hash type rdata element */
    	hash = rdata_atom_data(parser->current_rr.rdatas[1])[0];
    	size = rdata_atom_size(parser->current_rr.rdatas[2]);
    	if(hash == 1 && size != 20) {
    		zc_warning_prev_line("SSHFP %s of type SHA1 has hash of "
    			"wrong length, %d bytes, should be 20",
    			domain_to_string(parser->current_rr.owner),
    			(int)size);
    	} else if(hash == 2 && size != 32) {
    		zc_warning_prev_line("SSHFP %s of type SHA256 has hash of "
    			"wrong length, %d bytes, should be 32",
    			domain_to_string(parser->current_rr.owner),
    			(int)size);
    	}
    }
    
    void
    apex_rrset_checks(namedb_type* db, rrset_type* rrset, domain_type* domain)
    {
    	uint32_t soa_minimum;
    	unsigned i;
    	zone_type* zone = rrset->zone;
    	assert(domain == zone->apex);
    	(void)domain;
    	if (rrset_rrtype(rrset) == TYPE_SOA) {
    		zone->soa_rrset = rrset;
    
    		/* BUG #103 add another soa with a tweaked ttl */
    		if(zone->soa_nx_rrset == 0) {
    			zone->soa_nx_rrset = region_alloc(db->region,
    				sizeof(rrset_type));
    			zone->soa_nx_rrset->rr_count = 1;
    			zone->soa_nx_rrset->next = 0;
    			zone->soa_nx_rrset->zone = zone;
    			zone->soa_nx_rrset->rrs = region_alloc(db->region,
    				sizeof(rr_type));
    		}
    		memcpy(zone->soa_nx_rrset->rrs, rrset->rrs, sizeof(rr_type));
    
    		/* check the ttl and MINIMUM value and set accordingly */
    		memcpy(&soa_minimum, rdata_atom_data(rrset->rrs->rdatas[6]),
    				rdata_atom_size(rrset->rrs->rdatas[6]));
    		if (rrset->rrs->ttl > ntohl(soa_minimum)) {
    			zone->soa_nx_rrset->rrs[0].ttl = ntohl(soa_minimum);
    		}
    	} else if (rrset_rrtype(rrset) == TYPE_NS) {
    		zone->ns_rrset = rrset;
    	} else if (rrset_rrtype(rrset) == TYPE_RRSIG) {
    		for (i = 0; i < rrset->rr_count; ++i) {
    			if(rr_rrsig_type_covered(&rrset->rrs[i])==TYPE_DNSKEY){
    				zone->is_secure = 1;
    				break;
    			}
    		}
    	}
    }