/*
 * Verify or create TLS authentication with DANE (RFC6698)
 *
 * (c) NLnetLabs 2012
 *
 * See the file LICENSE for the license.
 *
 * wish list:
 * - nicer reporting (tracing of evaluation process)
 * - verbosity levels
 * - STARTTLS support
 */

#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>

#include <ldns/ldns.h>
#include <errno.h>

#ifdef USE_DANE
#ifdef HAVE_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>

#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif

#define LDNS_ERR(code, msg) do { if (code != LDNS_STATUS_OK) \
					ldns_err(msg, code); } while (false)
#define MEMERR(msg) do { fprintf(stderr, "memory error in %s\n", msg); \
			 exit(EXIT_FAILURE); } while (false)
#define BUFSIZE 16384

/* Exit status on a PKIX validated connection but without TLSA records
 * when the -T option was given:
 */
#define NO_TLSAS_EXIT_STATUS 2

/* int verbosity = 3; */

static void
print_usage(const char* progname)
{
#ifdef USE_DANE_VERIY
	printf("Usage: %s [OPTIONS] verify <name> <port>\n", progname);
	printf("   or: %s [OPTIONS] -t <tlsafile> verify\n", progname);
	printf("\n\tVerify the TLS connection at <name>:<port> or"
	       "\n\tuse TLSA record(s) from <tlsafile> to verify the\n"
			"\tTLS service they reference.\n");
	printf("\n   or: %s [OPTIONS] create <name> <port> [<usage> "
#else
	printf("Usage: %s [OPTIONS] create <name> <port> [<usage> "
#endif
			"[<selector> [<type>]]]\n", progname);
	printf("\n\tUse the TLS connection(s) to <name> <port> "
			"to create the TLSA\n\t"
			"resource record(s) that would "
			"authenticate the connection.\n");
	printf("\n\t<usage>"
			"\t\t0 | PKIX-TA  : CA constraint\n"
			"\t\t\t1 | PKIX-EE  : Service certificate constraint\n"
			"\t\t\t2 | DANE-TA  : Trust anchor assertion\n"
			"\t\t\t3 | DANE-EE  : Domain-issued certificate "
			"(default)\n");
	printf("\n\t<selector>"
			"\t0 | Cert     : Full certificate\n"
			"\t\t\t1 | SPKI     : SubjectPublicKeyInfo "
			"(default)\n");
	printf("\n\t<type>"
			"\t\t0 | Full     : No hash used\n"
			"\t\t\t1 | SHA2-256 : SHA-256 (default)\n"
			"\t\t\t2 | SHA2-512 : SHA-512\n");

	printf("OPTIONS:\n");
	printf("\t-h\t\tshow this text\n");
	printf("\t-4\t\tTLS connect IPv4 only\n");
	printf("\t-6\t\tTLS connect IPv6 only\n");
	printf("\t-r <address>\t"
	       "use resolver at <address> instead of local resolver\n");
	printf("\t-a <address>\t"
	       "don't resolve <name>, but connect to <address>(es)\n");
	printf("\t-b\t\t"
	       "print \"<name>. TYPE52 \\#<size> <hex data>\" form\n"
	      );
	printf("\t-c <certfile>\t"
	       "verify or create TLSA records for the\n"
	       "\t\t\tcertificate (chain) in <certfile>\n"
	      );
	printf("\t-d\t\tassume DNSSEC validity even when insecure or bogus\n");
	printf("\t-f <CAfile>\tuse CAfile to validate\n");
#if HAVE_DANE_CA_FILE
	printf("\t\t\tDefault is %s\n", LDNS_DANE_CA_FILE);
#endif
	printf("\t-i\t\tinteract after connecting\n");
	printf("\t-k <keyfile>\t"
	       "use DNSKEY/DS rr(s) in <keyfile> to validate TLSAs\n"
	       "\t\t\twhen signature chasing (i.e. -S)\n"
	      );
	printf("\t\t\tDefault is %s\n", LDNS_TRUST_ANCHOR_FILE);
	printf("\t-n\t\tdo *not* verify server name in certificate\n");
	printf("\t-o <offset>\t"
	       "select <offset>th certificate from the end of\n"
	       "\t\t\tthe validation chain. -1 means self-signed at end\n"
	      );
	printf("\t-p <CApath>\t"
	       "use certificates in the <CApath> directory to validate\n"
	      );
#if HAVE_DANE_CA_PATH
	printf("\t\t\tDefaults is %s\n", LDNS_DANE_CA_PATH);
#endif
	printf("\t-s\t\tassume PKIX validity\n");
	printf("\t-S\t\tChase signature(s) to a known key\n");
	printf("\t-t <tlsafile>\tdo not use DNS, "
	       "but read TLSA record(s) from <tlsafile>\n"
	      );
	printf("\t-T\t\tReturn exit status 2 for PKIX validated connections\n"
	       "\t\t\twithout (secure) TLSA records(s)\n");
	printf("\t-u\t\tuse UDP transport instead of TCP\n");
	printf("\t-v\t\tshow version and exit\n");
	/* printf("\t-V [0-5]\tset verbosity level (default 3)\n"); */
	exit(EXIT_SUCCESS);
}

static int
dane_int_within_range(const char* arg, int max, const char* name)
{
	char* endptr; /* utility var for strtol usage */
	int val = strtol(arg, &endptr, 10);

	if ((val < 0 || val > max)
			|| (errno != 0 && val == 0) /* out of range */
			|| endptr == arg            /* no digits */
			|| *endptr != '\0'          /* more chars */
			) {
		fprintf(stderr, "<%s> should be in range [0-%d]\n", name, max);
		exit(EXIT_FAILURE);
	}
	return val;
}

struct dane_param_choice_struct {
	const char* name;
	int number;
};
typedef struct dane_param_choice_struct dane_param_choice;

dane_param_choice dane_certificate_usage_table[] = {
	{ "PKIX-TA"				,   0 },
	{ "CA constraint"			,   0 },
	{ "CA-constraint"			,   0 },
	{ "PKIX-EE"				,   1 },
	{ "Service certificate constraint"	,   1 },
	{ "Service-certificate-constraint"	,   1 },
	{ "DANE-TA"				,   2 },
	{ "Trust anchor assertion"		,   2 },
	{ "Trust-anchor-assertion"		,   2 },
	{ "anchor"				,   2 },
	{ "DANE-EE"				,   3 },
	{ "Domain-issued certificate"		,   3 },
	{ "Domain-issued-certificate"		,   3 },
	{ "PrivCert"				, 255 },
	{ NULL, -1 }
};

dane_param_choice dane_selector_table[] = {
	{ "Cert"		,   0 },
	{ "Full certificate"	,   0 },
	{ "Full-certificate"	,   0 },
	{ "certificate"		,   0 },
	{ "SPKI"		,   1 },
	{ "SubjectPublicKeyInfo",   1 },
	{ "PublicKey"		,   1 },
	{ "pubkey"		,   1 },
	{ "key"			,   1 },
	{ "PrivSel"		, 255 },
	{ NULL, -1 }
};

dane_param_choice dane_matching_type_table[] = {
	{ "Full"		,   0 },
	{ "no-hash-used"	,   0 },
	{ "no hash used"	,   0 },
	{ "SHA2-256"		,   1 },
	{ "sha256"		,   1 },
	{ "sha-256"		,   1 },
	{ "SHA2-512"		,   2 },
	{ "sha512"		,   2 },
	{ "sha-512"		,   2 },
	{ "PrivMatch"		, 255 },
	{ NULL, -1 }
};

static int
dane_int_within_range_table(const char* arg, int max, const char* name,
		dane_param_choice table[])
{
	dane_param_choice* t;

	if (*arg) {
		for (t = table; t->name; t++) {
			if (strncasecmp(arg, t->name, strlen(arg)) == 0) {
				return t->number;
			}
		}
	}
	return dane_int_within_range(arg, max, name);
}

static void
ssl_err(const char* s)
{
	fprintf(stderr, "error: %s\n", s);
	ERR_print_errors_fp(stderr);
	exit(EXIT_FAILURE);
}

static void
ldns_err(const char* s, ldns_status err)
{
	if (err == LDNS_STATUS_SSL_ERR) {
		ssl_err(s);
	} else {
		fprintf(stderr, "%s: %s\n", s, ldns_get_errorstr_by_id(err));
		exit(EXIT_FAILURE);
	}
}

static ldns_status
ssl_connect_and_get_cert_chain(
		X509** cert, STACK_OF(X509)** extra_certs,
	       	SSL* ssl, const char* name_str,
		ldns_rdf* address, uint16_t port,
		ldns_dane_transport transport)
{
	struct sockaddr_storage *a = NULL;
	size_t a_len = 0;
	int sock;
	int r;

	assert(cert != NULL);
	assert(extra_certs != NULL);

	a = ldns_rdf2native_sockaddr_storage(address, port, &a_len);
	switch (transport) {
	case LDNS_DANE_TRANSPORT_TCP:

		sock = socket((int)((struct sockaddr*)a)->sa_family,
				SOCK_STREAM, IPPROTO_TCP);
		break;

	case LDNS_DANE_TRANSPORT_UDP:

		sock = socket((int)((struct sockaddr*)a)->sa_family,
				SOCK_DGRAM, IPPROTO_UDP);
		break;

	case LDNS_DANE_TRANSPORT_SCTP:

		sock = socket((int)((struct sockaddr*)a)->sa_family,
				SOCK_STREAM, IPPROTO_SCTP);
		break;

	default:
		LDNS_FREE(a);
		return LDNS_STATUS_DANE_UNKNOWN_TRANSPORT;
	}
	if (sock == -1) {
		LDNS_FREE(a);
		return LDNS_STATUS_NETWORK_ERR;
	}
	if (connect(sock, (struct sockaddr*)a, (socklen_t)a_len) == -1) {
		LDNS_FREE(a);
		return LDNS_STATUS_NETWORK_ERR;
	}
	LDNS_FREE(a);
	if (! SSL_clear(ssl)) {
		close(sock);
		fprintf(stderr, "SSL_clear\n");
		return LDNS_STATUS_SSL_ERR;
	}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
	(void) SSL_set_tlsext_host_name(ssl, name_str);
#endif
	SSL_set_connect_state(ssl);
	(void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
	if (! SSL_set_fd(ssl, sock)) {
		close(sock);
		fprintf(stderr, "SSL_set_fd\n");
		return LDNS_STATUS_SSL_ERR;
	}
	for (;;) {
		ERR_clear_error();
		if ((r = SSL_do_handshake(ssl)) == 1) {
			break;
		}
		r = SSL_get_error(ssl, r);
		if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) {
			fprintf(stderr, "handshaking SSL_get_error: %d\n", r);
			return LDNS_STATUS_SSL_ERR;
		}
	}
	*cert = SSL_get_peer_certificate(ssl);
	*extra_certs = SSL_get_peer_cert_chain(ssl);

	return LDNS_STATUS_OK;
}


#ifdef USE_DANE_VERIFY
static void
ssl_interact(SSL* ssl)
{
	fd_set rfds;
	int maxfd;
	int sock;
	int r;

	char buf[BUFSIZE];
	char* bufptr;
	int to_write;
	int written;

	sock = SSL_get_fd(ssl);
	if (sock == -1) {
		return;
	}
	maxfd = (STDIN_FILENO > sock ? STDIN_FILENO : sock) + 1;
	for (;;) {
#ifndef S_SPLINT_S
		FD_ZERO(&rfds);
#endif /* splint */
		FD_SET(sock, &rfds);
		FD_SET(STDIN_FILENO, &rfds);

		r = select(maxfd, &rfds, NULL, NULL, NULL);
		if (r == -1) {
			perror("select");
			break;
		}
		if (FD_ISSET(sock, &rfds)) {
			to_write = SSL_read(ssl, buf, BUFSIZE);
			if (to_write <= 0) {
				r = SSL_get_error(ssl, to_write);
				if (r != SSL_ERROR_ZERO_RETURN) {
					fprintf(stderr,
						"reading SSL_get_error:"
						" %d\n", r);
				}
				break;
			}
			bufptr = buf;
			while (to_write > 0) {
				written = (int) fwrite(bufptr, 1, 
						(size_t) to_write, stdout);
				if (written == 0) {
					perror("fwrite");
					break;
				}
				to_write -= written;
				bufptr += written;
			}
		} /* if (FD_ISSET(sock, &rfds)) */

		if (FD_ISSET(STDIN_FILENO, &rfds)) {
			to_write = (int) read(STDIN_FILENO, buf, BUFSIZE - 1);
			if (to_write <= 0) {
				if (to_write == -1) {
					perror("read");
				}
				break;
			}
			if (buf[to_write - 1] == '\n') {
				buf[to_write - 1] = '\r';
				buf[to_write    ] = '\n';
				to_write += 1;
			}
			bufptr = buf;
			while (to_write > 0) {
				written = SSL_write(ssl, bufptr, to_write);
				if (written <= 0) {
					r = SSL_get_error(ssl, to_write);
					if (r != SSL_ERROR_ZERO_RETURN) {
						fprintf(stderr,
							"writing SSL_get_error"
							": %d\n", r);
					}
					break;
				}
				to_write -= written;
				bufptr += written;
			}
		} /* if (FD_ISSET(STDIN_FILENO, &rfds)) */

	} /* for (;;) */
}
#endif /* USE_DANE_VERIFY */


static ldns_rr_list*
rr_list_filter_rr_type(ldns_rr_list* l, ldns_rr_type t)
{
	size_t i;
	ldns_rr* rr;
	ldns_rr_list* r = ldns_rr_list_new();

	if (r == NULL) {
		return r;
	}
	for (i = 0; i < ldns_rr_list_rr_count(l); i++) {
		rr = ldns_rr_list_rr(l, i);
		if (ldns_rr_get_type(rr) == t) {
			if (! ldns_rr_list_push_rr(r, rr)) {
				ldns_rr_list_free(r);
				return NULL;
			}
		}
	}
	return r;
}


/* Return a copy of the list of tlsa records where the usage types
 * "CA constraint" are replaced with "Trust anchor assertion" and the usage
 * types "Service certificate constraint" are replaced with 
 * "Domain-issued certificate".
 *
 * This to check what would happen if PKIX validation was successfull always.
 */
static ldns_rr_list*
dane_no_pkix_transform(const ldns_rr_list* tlas)
{
	size_t i;
	ldns_rr* rr;
	ldns_rr* new_rr;
	ldns_rdf* rdf;
	ldns_rr_list* r = ldns_rr_list_new();

	if (r == NULL) {
		return r;
	}
	for (i = 0; i < ldns_rr_list_rr_count(tlas); i++) {
		rr = ldns_rr_list_rr(tlas, i);
		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TLSA) {

			new_rr = ldns_rr_clone(rr);
			if (!new_rr) {
				ldns_rr_list_deep_free(r);
				return NULL;
			}
			switch(ldns_rdf2native_int8(ldns_rr_rdf(new_rr, 0))) {

			case LDNS_TLSA_USAGE_CA_CONSTRAINT:

				rdf = ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8,
			(uint8_t) LDNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION);
				if (! rdf) {
					ldns_rr_free(new_rr);
					ldns_rr_list_deep_free(r);
					return NULL;
				}
				(void) ldns_rr_set_rdf(new_rr, rdf, 0);
				break;


			case LDNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT:

				rdf = ldns_native2rdf_int8(LDNS_RDF_TYPE_INT8,
			(uint8_t) LDNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE);
				if (! rdf) {
					ldns_rr_free(new_rr);
					ldns_rr_list_deep_free(r);
					return NULL;
				}
				(void) ldns_rr_set_rdf(new_rr, rdf, 0);
				break;


			default:
				break;
			}
			if (! ldns_rr_list_push_rr(r, new_rr)) {
				ldns_rr_free(new_rr);
				ldns_rr_list_deep_free(r);
				return NULL;
			}
		}
	}
	return r;
}

static void
print_rr_as_TYPEXXX(FILE* out, ldns_rr* rr)
{
	size_t i, sz;
	ldns_status s;
	ldns_buffer* buf = ldns_buffer_new(LDNS_MAX_PACKETLEN);
	char* str;

	ldns_buffer_clear(buf);
	s = ldns_rdf2buffer_str_dname(buf, ldns_rr_owner(rr));
	LDNS_ERR(s, "could not ldns_rdf2buffer_str_dname");
	ldns_buffer_printf(buf, "\t%d", ldns_rr_ttl(rr));
	ldns_buffer_printf(buf, "\t");
	s = ldns_rr_class2buffer_str(buf, ldns_rr_get_class(rr));
	LDNS_ERR(s, "could not ldns_rr_class2buffer_str");
	ldns_buffer_printf(buf, "\tTYPE%d", ldns_rr_get_type(rr));
	sz = 0;
	for (i = 0; i < ldns_rr_rd_count(rr); i++) {
		 sz += ldns_rdf_size(ldns_rr_rdf(rr, i));
	}
	ldns_buffer_printf(buf, "\t\\# %d ", sz);
	for (i = 0; i < ldns_rr_rd_count(rr); i++) {
		s = ldns_rdf2buffer_str_hex(buf, ldns_rr_rdf(rr, i));
		LDNS_ERR(s, "could not ldns_rdf2buffer_str_hex");
	}
	str = ldns_buffer_export2str(buf);
	ldns_buffer_free(buf);
	fprintf(out, "%s\n", str);
	LDNS_FREE(str);
}

static void
print_rr_list_as_TYPEXXX(FILE* out, ldns_rr_list* l)
{
	size_t i;

	for (i = 0; i < ldns_rr_list_rr_count(l); i++) {
		print_rr_as_TYPEXXX(out, ldns_rr_list_rr(l, i));
	}
}

static ldns_status
read_key_file(const char *filename, ldns_rr_list *keys)
{
	ldns_status status = LDNS_STATUS_ERR;
	ldns_rr *rr;
	FILE *fp;
	uint32_t my_ttl = 0;
	ldns_rdf *my_origin = NULL;
	ldns_rdf *my_prev = NULL;
	int line_nr;

	if (!(fp = fopen(filename, "r"))) {
		return LDNS_STATUS_FILE_ERR;
	}
	while (!feof(fp)) {
		status = ldns_rr_new_frm_fp_l(&rr, fp, &my_ttl, &my_origin,
				&my_prev, &line_nr);

		if (status == LDNS_STATUS_OK) {

			if (   ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS
			    || ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY)

				ldns_rr_list_push_rr(keys, rr);

		} else if (   status == LDNS_STATUS_SYNTAX_EMPTY
		           || status == LDNS_STATUS_SYNTAX_TTL
		           || status == LDNS_STATUS_SYNTAX_ORIGIN
		           || status == LDNS_STATUS_SYNTAX_INCLUDE)

			status = LDNS_STATUS_OK;
		else
			break;
	}
	fclose(fp);
	return status;
}


static ldns_status
dane_setup_resolver(ldns_resolver** res, ldns_rdf* nameserver_addr,
		ldns_rr_list* keys, bool dnssec_off)
{
	ldns_status s = LDNS_STATUS_OK;

	assert(res != NULL);

	if (nameserver_addr) {
		*res = ldns_resolver_new();
		if (*res) {
			s = ldns_resolver_push_nameserver(*res, nameserver_addr);
		} else {
			s = LDNS_STATUS_MEM_ERR;
		}
	} else {
	        s = ldns_resolver_new_frm_file(res, NULL);
	}
	if (s == LDNS_STATUS_OK) {
		ldns_resolver_set_dnssec(*res, ! dnssec_off);

		if (keys && ldns_rr_list_rr_count(keys) > 0) {
			/* anchors must trigger signature chasing */
			ldns_resolver_set_dnssec_anchors(*res, keys);
			ldns_resolver_set_dnssec_cd(*res, true);
		}
	}
	return s;
}


static ldns_status
dane_query(ldns_rr_list** rrs, ldns_resolver* r,
		ldns_rdf *name, ldns_rr_type t, ldns_rr_class c,
		bool insecure_is_ok)
{
	ldns_pkt* p = NULL;
	ldns_rr_list* keys = NULL;
	ldns_rr_list* rrsigs  = NULL;
	ldns_rdf* signame = NULL;
	ldns_status s;

	assert(rrs != NULL);

	p = ldns_resolver_query(r, name, t, c, LDNS_RD);
	if (! p) {
		return LDNS_STATUS_MEM_ERR;
	}
	*rrs = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANSWER);

	if (! ldns_resolver_dnssec(r)) { /* DNSSEC explicitly disabled,
					    anything goes */
		ldns_pkt_free(p);
		return LDNS_STATUS_OK;
	}
	if (ldns_rr_list_rr_count(*rrs) == 0) { /* assert(*rrs == NULL) */

		if (ldns_pkt_get_rcode(p) == LDNS_RCODE_SERVFAIL) {

			ldns_pkt_free(p);
			return LDNS_STATUS_DANE_BOGUS;
		} else {
			ldns_pkt_free(p);
			return LDNS_STATUS_OK;
		}
	}
	/* We have answers and we have dnssec. */

	if (! ldns_pkt_cd(p)) { /* we act as stub resolver (no sigchase) */

		if (! ldns_pkt_ad(p)) { /* Not secure */

			goto insecure;
		}
		ldns_pkt_free(p);
		return LDNS_STATUS_OK;
	}

	/* sigchase */

	/* TODO: handle cname reference check */

	rrsigs = ldns_pkt_rr_list_by_type(p,
			LDNS_RR_TYPE_RRSIG,
			LDNS_SECTION_ANSWER);

	if (! rrsigs || ldns_rr_list_rr_count(rrsigs) == 0) {
		goto insecure;
	}

	signame = ldns_rr_rrsig_signame(ldns_rr_list_rr(rrsigs, 0));
	if (! signame) {
		s = LDNS_STATUS_ERR;
		goto error;
	}
	/* First try with the keys we already have */
	s = ldns_verify(*rrs, rrsigs, ldns_resolver_dnssec_anchors(r), NULL);
	if (s == LDNS_STATUS_OK) {
		goto cleanup;
	}
	/* Fetch the necessary keys and recheck */
	keys = ldns_fetch_valid_domain_keys(r, signame,
			ldns_resolver_dnssec_anchors(r), &s);

	if (s != LDNS_STATUS_OK) {
		goto error;
	}
	if (ldns_rr_list_rr_count(keys) == 0) { /* An insecure island */
		goto insecure;
	}
	s = ldns_verify(*rrs, rrsigs, keys, NULL);
	switch (s) {
	case LDNS_STATUS_CRYPTO_BOGUS: goto bogus;
	case LDNS_STATUS_OK          : goto cleanup;
	default                      : break;
	}
insecure:
	s = LDNS_STATUS_DANE_INSECURE;
bogus:
	if (! insecure_is_ok) {
error:
		ldns_rr_list_deep_free(*rrs);
		*rrs = ldns_rr_list_new();
	}
cleanup:
	if (keys) {
		ldns_rr_list_deep_free(keys);
	}
	if (rrsigs) {
		ldns_rr_list_deep_free(rrsigs);
	}
	ldns_pkt_free(p);
	return s;
}


static ldns_rr_list*
dane_lookup_addresses(ldns_resolver* res, ldns_rdf* dname,
		int ai_family)
{
	ldns_status s;
	ldns_rr_list *as = NULL;
	ldns_rr_list *aaas = NULL;
	ldns_rr_list *r = ldns_rr_list_new();

	if (r == NULL) {
		MEMERR("ldns_rr_list_new");
	}
	if (ai_family == AF_UNSPEC || ai_family == AF_INET) {

		s = dane_query(&as, res,
				dname, LDNS_RR_TYPE_A, LDNS_RR_CLASS_IN,
			       	true);

		if (s == LDNS_STATUS_DANE_INSECURE &&
			ldns_rr_list_rr_count(as) > 0) {
			fprintf(stderr, "Warning! Insecure IPv4 addresses. "
					"Continuing with them...\n");

		} else if (s == LDNS_STATUS_DANE_BOGUS ||
				LDNS_STATUS_CRYPTO_BOGUS == s) {
			fprintf(stderr, "Warning! Bogus IPv4 addresses. "
					"Discarding...\n");
			ldns_rr_list_deep_free(as);
			as = ldns_rr_list_new();

		} else if (s != LDNS_STATUS_OK) {
			LDNS_ERR(s, "dane_query");

		}
		if (! ldns_rr_list_push_rr_list(r, as)) {
			MEMERR("ldns_rr_list_push_rr_list");
		}
	}
	if (ai_family == AF_UNSPEC || ai_family == AF_INET6) {

		s = dane_query(&aaas, res,
				dname, LDNS_RR_TYPE_AAAA, LDNS_RR_CLASS_IN,
			       	true);

		if (s == LDNS_STATUS_DANE_INSECURE &&
			ldns_rr_list_rr_count(aaas) > 0) {
			fprintf(stderr, "Warning! Insecure IPv6 addresses. "
					"Continuing with them...\n");

		} else if (s == LDNS_STATUS_DANE_BOGUS ||
				LDNS_STATUS_CRYPTO_BOGUS == s) {
			fprintf(stderr, "Warning! Bogus IPv6 addresses. "
					"Discarding...\n");
			ldns_rr_list_deep_free(aaas);
			aaas = ldns_rr_list_new();

		} else if (s != LDNS_STATUS_OK) {
			LDNS_ERR(s, "dane_query");

		}
		if (! ldns_rr_list_push_rr_list(r, aaas)) {
			MEMERR("ldns_rr_list_push_rr_list");
		}
	}
	return r;
}

static ldns_status
dane_read_tlsas_from_file(ldns_rr_list** tlsas,
		char* filename, ldns_rdf* origin)
{
	FILE* fp = NULL;
	ldns_rr* rr = NULL;
	ldns_rdf *my_origin = NULL;
	ldns_rdf *my_prev = NULL;
	ldns_rdf *origin_lc = NULL;
	int line_nr;
	ldns_status s = LDNS_STATUS_MEM_ERR;

	assert(tlsas != NULL);
	assert(filename != NULL);

	if (strcmp(filename, "-") == 0) {
		fp = stdin;
	} else {
		fp = fopen(filename, "r");
		if (!fp) {
			fprintf(stderr, "Unable to open %s: %s\n",
					filename, strerror(errno));
			exit(EXIT_FAILURE);
		}
	}
	if (origin) {
		my_origin = ldns_rdf_clone(origin);
		if (! my_origin) {
			goto error;
		}
		my_prev   = ldns_rdf_clone(origin);
		if (! my_prev) {
			goto error;
		}
		origin_lc = ldns_rdf_clone(origin);
		if (! origin_lc) {
			goto error;
		}
		ldns_dname2canonical(origin_lc);
	}
	*tlsas = ldns_rr_list_new();
	if (! *tlsas) {
		goto error;
	}
	while (! feof(fp)) {
		s = ldns_rr_new_frm_fp_l(&rr, fp, NULL,
				&my_origin, &my_prev, &line_nr);
		if (s != LDNS_STATUS_OK) {
			goto error;
		}
		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_TLSA) {
			ldns_dname2canonical(ldns_rr_owner(rr));
			if (! origin || ldns_dname_compare(ldns_rr_owner(rr),
						origin_lc) == 0) {
				if (ldns_rr_list_push_rr(*tlsas, rr)) {
					continue;
				} else {
					s = LDNS_STATUS_MEM_ERR;
					goto error;
				}
			}
		}
		ldns_rr_free(rr);
	}

	ldns_rdf_deep_free(origin_lc);
	ldns_rdf_deep_free(my_prev);
	ldns_rdf_deep_free(my_origin);
	fclose(fp);

	return LDNS_STATUS_OK;

error:
	if (*tlsas) {
		ldns_rr_list_deep_free(*tlsas);
		*tlsas = NULL;
	}
	if (origin_lc) {
		ldns_rdf_deep_free(origin_lc);
	}
	if (my_prev) {
		ldns_rdf_deep_free(my_prev);
	}
	if (my_origin) {
		ldns_rdf_deep_free(my_origin);
	}
	if (fp && fp != stdin) {
		fclose(fp);
	}
	return s;
}

static bool
dane_wildcard_label_cmp(uint8_t iw, const char* w, uint8_t il, const char* l)
{
	if (iw == 0) { /* End of match label */
		if (il == 0) { /* And end in the to be matched label */
			return true;
		}
		return false;
	}
	do {
		if (*w == '*') {
			if (iw == 1) { /* '*' is the last match char,
					  remainder matches wildcard */
				return true;
			}
			while (il > 0) { /* more to match? */

				if (w[1] == *l) { /* Char after '*' matches.
						   * Recursion for backtracking
						   */
					if (dane_wildcard_label_cmp(
								iw - 1, w + 1,
								il    , l)) {
						return true;
					}
				}
				l += 1;
				il -= 1;
			}
		}
		/* Skip up till next wildcard (if possible) */
		while (il > 0 && iw > 0 && *w != '*' && *w == *l) {
			w += 1;
			l += 1;
			il -= 1;
			iw -= 1;
		}
	} while (iw > 0 && *w == '*' &&  /* More to match a next wildcard? */
			(il > 0 || iw == 1));

	return iw == 0 && il == 0;
}

static bool
dane_label_matches_label(ldns_rdf* w, ldns_rdf* l)
{
	uint8_t iw;
	uint8_t il;

	iw = ldns_rdf_data(w)[0];
	il = ldns_rdf_data(l)[0];
	return dane_wildcard_label_cmp(
			iw, (const char*)ldns_rdf_data(w) + 1,
			il, (const char*)ldns_rdf_data(l) + 1);
}

static bool
dane_name_matches_server_name(const char* name_str, ldns_rdf* server_name)
{
	ldns_rdf* name;
	uint8_t nn, ns, i;
	ldns_rdf* ln;
	ldns_rdf* ls;
	
	name = ldns_dname_new_frm_str((const char*)name_str);
	if (! name) {
		LDNS_ERR(LDNS_STATUS_ERR, "ldns_dname_new_frm_str");
	}
	nn = ldns_dname_label_count(name);
	ns = ldns_dname_label_count(server_name);
	if (nn != ns) {
		ldns_rdf_free(name);
		return false;
	}
	ldns_dname2canonical(name);
	for (i = 0; i < nn; i++) {
		ln = ldns_dname_label(name, i);
		if (! ln) {
			return false;
		}
		ls = ldns_dname_label(server_name, i);
		if (! ls) {
			ldns_rdf_free(ln);
			return false;
		}
		if (! dane_label_matches_label(ln, ls)) {
			ldns_rdf_free(ln);
			ldns_rdf_free(ls);
			return false;
		}
		ldns_rdf_free(ln);
		ldns_rdf_free(ls);
	}
	return true;
}

static bool
dane_X509_any_subject_alt_name_matches_server_name(
		X509 *cert, ldns_rdf* server_name)
{
	GENERAL_NAMES* names;
	GENERAL_NAME*  name;
	unsigned char* subject_alt_name_str = NULL;
	int i, n;

	names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0 );
	if (! names) { /* No subjectAltName extension */
		return false;
	}
	n = sk_GENERAL_NAME_num(names);
	for (i = 0; i < n; i++) {
		name = sk_GENERAL_NAME_value(names, i);
		if (name->type == GEN_DNS) {
			(void) ASN1_STRING_to_UTF8(&subject_alt_name_str,
					name->d.dNSName);
			if (subject_alt_name_str) {
				if (dane_name_matches_server_name((char*)
							subject_alt_name_str,
							server_name)) {
					OPENSSL_free(subject_alt_name_str);
					return true;
				}
				OPENSSL_free(subject_alt_name_str);
			}
		}
	}
	/* sk_GENERAL_NAMES_pop_free(names, sk_GENERAL_NAME_free); */
	return false;
}

static bool
dane_X509_subject_name_matches_server_name(X509 *cert, ldns_rdf* server_name)
{
	X509_NAME* subject_name;
	int i;
	X509_NAME_ENTRY* entry;
	ASN1_STRING* entry_data;
	unsigned char* subject_name_str = NULL;
	bool r;
 
	subject_name = X509_get_subject_name(cert);
	if (! subject_name ) {
		ssl_err("could not X509_get_subject_name");
	}
	i = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
	entry = X509_NAME_get_entry(subject_name, i);
	entry_data = X509_NAME_ENTRY_get_data(entry);
	(void) ASN1_STRING_to_UTF8(&subject_name_str, entry_data);
	if (subject_name_str) {
		r = dane_name_matches_server_name(
				(char*)subject_name_str, server_name);
		OPENSSL_free(subject_name_str);
		return r;
	} else {
		return false;
	}
}

static bool
dane_verify_server_name(X509* cert, ldns_rdf* server_name)
{
	ldns_rdf* server_name_lc;
	bool r;
	server_name_lc = ldns_rdf_clone(server_name);
	if (! server_name_lc) {
		LDNS_ERR(LDNS_STATUS_MEM_ERR, "ldns_rdf_clone");
	}
	ldns_dname2canonical(server_name_lc);
	r = dane_X509_any_subject_alt_name_matches_server_name(
			cert, server_name_lc) || 
	    dane_X509_subject_name_matches_server_name(
			cert, server_name_lc);
	ldns_rdf_free(server_name_lc);
	return r;
}

static void
dane_create(ldns_rr_list* tlsas, ldns_rdf* tlsa_owner,
		ldns_tlsa_certificate_usage certificate_usage, int offset,
		ldns_tlsa_selector          selector,
		ldns_tlsa_matching_type     matching_type,
		X509* cert, STACK_OF(X509)* extra_certs,
		X509_STORE* validate_store,
		bool verify_server_name, ldns_rdf* name)
{
	ldns_status s;
	X509* selected_cert;
	ldns_rr* tlsa_rr;

	if (verify_server_name && ! dane_verify_server_name(cert, name)) {
		fprintf(stderr, "The certificate does not match the "
				"server name\n");
		exit(EXIT_FAILURE);
	}

	s = ldns_dane_select_certificate(&selected_cert,
			cert, extra_certs, validate_store,
			certificate_usage, offset);
	LDNS_ERR(s, "could not select certificate");

	s = ldns_dane_create_tlsa_rr(&tlsa_rr,
			certificate_usage, selector, matching_type,
			selected_cert);
	LDNS_ERR(s, "could not create tlsa rr");

	ldns_rr_set_owner(tlsa_rr, ldns_rdf_clone(tlsa_owner));
			     
	if (! ldns_rr_list_contains_rr(tlsas, tlsa_rr)) {
		if (! ldns_rr_list_push_rr(tlsas, tlsa_rr)) {
			MEMERR("ldns_rr_list_push_rr");
		}
	}
}

#if defined(USE_DANE_VERIFY) && ( OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL) )
static bool
dane_verify(ldns_rr_list* tlsas, ldns_rdf* address,
		X509* cert, STACK_OF(X509)* extra_certs,
		X509_STORE* validate_store,
		bool verify_server_name, ldns_rdf* name,
		bool assume_pkix_validity)
{
	ldns_status s;
	char* address_str = NULL;

	s = ldns_dane_verify(tlsas, cert, extra_certs, validate_store);
	if (address) {
		address_str = ldns_rdf2str(address);
		fprintf(stdout, "%s", address_str ? address_str : "<address>");
		free(address_str);
	} else {
		X509_NAME_print_ex_fp(stdout,
				X509_get_subject_name(cert), 0, 0);
	}
	if (s == LDNS_STATUS_OK) {
		if (verify_server_name &&
				! dane_verify_server_name(cert, name)) {

			fprintf(stdout, " did not dane-validate, because:"
					" the certificate name did not match"
					" the server name\n");
			return false;
		}
		fprintf(stdout, " dane-validated successfully\n");
		return true;
	} else if (assume_pkix_validity &&
			s == LDNS_STATUS_DANE_PKIX_DID_NOT_VALIDATE) {
		fprintf(stdout, " dane-validated successfully,"
				" because PKIX is assumed valid\n");
		return true;
	}
	fprintf(stdout, " did not dane-validate, because: %s\n",
			ldns_get_errorstr_by_id(s));
	return false;
}
#endif /* defined(USE_DANE_VERIFY) && OPENSSL_VERSION_NUMBER < 0x10100000 */

/**
 * Return either an A or AAAA rdf, based on the given
 * string. If it it not a valid ip address, return null.
 *
 * Caller receives ownership of returned rdf (if not null),
 * and must free it.
 */
static inline ldns_rdf* rdf_addr_frm_str(const char* str) {
	ldns_rdf *a = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, str);
	if (!a) {
		a = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, str);
	}
	return a;
}


int
main(int argc, char* const* argv)
{
	int c;
	enum { UNDETERMINED, VERIFY, CREATE } mode = UNDETERMINED;

	ldns_status   s;
	size_t        i;

#if OPENSSL_VERSION_NUMBER >= 0x10100000 && ! defined(HAVE_LIBRESSL)
	size_t        j, usable_tlsas = 0;
	X509_STORE_CTX *store_ctx = NULL;
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000 */

	bool print_tlsa_as_type52   = false;
	bool assume_dnssec_validity = false;
	bool assume_pkix_validity   = false;
	bool verify_server_name     = true;
	bool interact               = false;

#if HAVE_DANE_CA_FILE
	const char* CAfile    = LDNS_DANE_CA_FILE;
#else
	const char* CAfile    = NULL;
#endif
#if HAVE_DANE_CA_PATH
	const char* CApath    = LDNS_DANE_CA_PATH;
#else
	const char* CApath    = NULL;
#endif
	char* cert_file = NULL;
	X509* cert                  = NULL;
	STACK_OF(X509)* extra_certs = NULL;
	
	ldns_rr_list*  keys = ldns_rr_list_new();
	size_t        nkeys = 0;
	bool    do_sigchase = false;

	ldns_rr_list* addresses = ldns_rr_list_new();
	ldns_rr*      address_rr;
	ldns_rdf*     address;

	int           ai_family = AF_UNSPEC;
	int           transport = LDNS_DANE_TRANSPORT_TCP;

	char*         name_str = NULL;	/* supress uninitialized warning */
	ldns_rdf*     name;
	uint16_t      port = 0;		/* supress uninitialized warning */

	ldns_resolver* res            = NULL;
	ldns_rdf*      nameserver_rdf = NULL;
	ldns_rdf*      tlsa_owner     = NULL;
	char*          tlsa_owner_str = NULL;
	ldns_rr_list*  tlsas          = NULL;
	char*          tlsas_file     = NULL;

	/* For extracting service port and transport from tla_owner. */
	ldns_rdf*      port_rdf       = NULL;
	char*          port_str       = NULL;
	ldns_rdf*      transport_rdf  = NULL;
	char*          transport_str  = NULL;

	ldns_rr_list*  originals      = NULL; /* original tlsas (before
					       * transform), but also used
					       * as temporary.
					       */

	ldns_tlsa_certificate_usage certificate_usage = 666;
	int                         offset            =  -1;
	ldns_tlsa_selector          selector          = 666;
	ldns_tlsa_matching_type     matching_type     = 666;
	

	X509_STORE *store  = NULL;

	SSL_CTX* ctx = NULL;
	SSL*     ssl = NULL;

	int  no_tlsas_exit_status  = EXIT_SUCCESS;
	int           exit_success = EXIT_SUCCESS; 

	bool success = true;

	if (! keys || ! addresses) {
		MEMERR("ldns_rr_list_new");
	}
	while((c = getopt(argc, argv, "46a:bc:df:hik:no:p:r:sSt:TuvV:")) != -1){
		switch(c) {
		case 'h':
			print_usage("ldns-dane");
			break;
		case '4':
			ai_family = AF_INET;
			break;
		case '6':
			ai_family = AF_INET6;
			break;
		case 'r':
			if (nameserver_rdf) {
				fprintf(stderr, "Can only specify -r once\n");
				exit(EXIT_FAILURE);
			}
			nameserver_rdf = rdf_addr_frm_str(optarg);
			if (!nameserver_rdf) {
				fprintf(stderr,
				        "Could not interpret address %s\n",
				        optarg);
				exit(EXIT_FAILURE);
			}
			break;
		case 'a':
			s = ldns_str2rdf_a(&address, optarg);
			if (s == LDNS_STATUS_OK) {
				address_rr = ldns_rr_new_frm_type(
						LDNS_RR_TYPE_A);
			} else {
				s = ldns_str2rdf_aaaa(&address, optarg);
				if (s == LDNS_STATUS_OK) {
					address_rr = ldns_rr_new_frm_type(
							LDNS_RR_TYPE_AAAA);
				} else {
					fprintf(stderr,
						"Could not interpret address "
						"%s\n",
						optarg);
					exit(EXIT_FAILURE);
				}
			}
			(void) ldns_rr_a_set_address(address_rr, address);
			for (i = 0; i < ldns_rr_list_rr_count(addresses); i++){
				if (ldns_rdf_compare(address,
				     ldns_rr_a_address(
				      ldns_rr_list_rr(addresses, i))) == 0) {
					break;
				}
			}
			if (i >= ldns_rr_list_rr_count(addresses)) {
				if (! ldns_rr_list_push_rr(addresses,
							address_rr)) {
					MEMERR("ldns_rr_list_push_rr");
				}
			}
			break;
		case 'b':
			print_tlsa_as_type52 = true;
			/* TODO: do it with output formats... maybe... */
			break;
		case 'c':
			cert_file = optarg; /* checking in SSL stuff below */
			break;
		case 'd':
			assume_dnssec_validity = true;
			break;
		case 'f':
			CAfile = optarg;
			break;
		case 'i':
			interact = true;
			break;
		case 'k':
			s = read_key_file(optarg, keys);
			if (s == LDNS_STATUS_FILE_ERR) {
				fprintf(stderr, "Error opening %s: %s\n",
						optarg, strerror(errno));
			}
			LDNS_ERR(s, "Could not parse key file");
			if (ldns_rr_list_rr_count(keys) == nkeys) {
				fprintf(stderr, "No keys found in file"
						" %s\n", optarg);
				exit(EXIT_FAILURE);
			}
			nkeys = ldns_rr_list_rr_count(keys);
			break;
		case 'n':
			verify_server_name = false;
			break;
		case 'o':
			offset = atoi(optarg); /* todo check if all numeric */
			break;
		case 'p':
			CApath = optarg;
			break;
		case 's':
			assume_pkix_validity = true;
			break;
		case 'S':
			do_sigchase = true;
			break;
		case 't':
			tlsas_file = optarg;
			break;
		case 'T':
			no_tlsas_exit_status = NO_TLSAS_EXIT_STATUS;
			break;
		case 'u':
			transport = LDNS_DANE_TRANSPORT_UDP;
			break;
		case 'v':
			printf("ldns-dane version %s (ldns version %s)\n",
					LDNS_VERSION, ldns_version());
			exit(EXIT_SUCCESS);
			break;
/*		case 'V':
			verbosity = atoi(optarg);
			break;
 */
		}
	}

	/* Filter out given IPv4 addresses when -6 was given, 
	 * and IPv6 addresses when -4 was given.
	 */
	if (ldns_rr_list_rr_count(addresses) > 0 &&
			ai_family != AF_UNSPEC) {
		originals = addresses;
		addresses = rr_list_filter_rr_type(originals,
				(ai_family == AF_INET
				 ? LDNS_RR_TYPE_A : LDNS_RR_TYPE_AAAA));
		ldns_rr_list_free(originals);
		if (addresses == NULL) {
			MEMERR("rr_list_filter_rr_type");
		}
		if (ldns_rr_list_rr_count(addresses) == 0) {
			fprintf(stderr,
				"No addresses of the specified type remain\n");
			exit(EXIT_FAILURE);
		}
	}

	if (do_sigchase) {
		if (nkeys == 0) {
			(void) read_key_file(LDNS_TRUST_ANCHOR_FILE, keys);
			nkeys = ldns_rr_list_rr_count(keys);

			if (nkeys == 0) {
				fprintf(stderr, "Unable to chase "
						"signature without keys.\n");
				exit(EXIT_FAILURE);
			}
		}
	} else {
		keys = NULL;
	}

	argc -= optind;
	argv += optind;

	if (argc == 0) {

		print_usage("ldns-dane");
	}
	if (strncasecmp(*argv, "create", strlen(*argv)) == 0) {

		mode = CREATE;
		argc--;
		argv++;

#ifdef USE_DANE_VERIFY
	} else if (strncasecmp(*argv, "verify", strlen(*argv)) == 0) {

		mode = VERIFY;
		argc--;
		argv++;

	} else {
		fprintf(stderr, "Specify create or verify mode\n");
#else
	} else {
		fprintf(stderr, "Specify create mode\n");
#endif
		exit(EXIT_FAILURE);
	}

#ifndef USE_DANE_VERIFY
	(void)transport_str;
	(void)transport_rdf;
	(void)port_str;
	(void)port_rdf;
	(void)interact;
#else
	if (mode == VERIFY && argc == 0) {

		if (! tlsas_file) {
			fprintf(stderr, "ERROR! Nothing given to verify\n");
			exit(EXIT_FAILURE);
		}
		s = dane_read_tlsas_from_file(&tlsas, tlsas_file, NULL);
		LDNS_ERR(s, "could not read tlsas from file");

		/* extract port, transport and hostname from TLSA owner name */

		if (ldns_rr_list_rr_count(tlsas) == 0) {

			fprintf(stderr, "ERROR! No TLSA records to extract "
					"service port, transport and hostname"
					"\n");
			exit(EXIT_FAILURE);
		}
		tlsa_owner = ldns_rr_list_owner(tlsas);
		if (ldns_dname_label_count(tlsa_owner) < 2) {
			fprintf(stderr, "ERROR! To few labels in TLSA owner\n");
			exit(EXIT_FAILURE);
		}
		do {
			s = LDNS_STATUS_MEM_ERR;
			port_rdf = ldns_dname_label(tlsa_owner, 0);
			if (! port_rdf) {
				break;
			}
			port_str = ldns_rdf2str(port_rdf);
			if (! port_str) {
				break;
			}
			if (*port_str != '_') {
				fprintf(stderr, "ERROR! Badly formatted "
						"service port label in the "
						"TLSA owner name\n");
				exit(EXIT_FAILURE);
			}
			if (port_str[strlen(port_str) - 1] == '.') {
				port_str[strlen(port_str) - 1] = '\000';
			}
			port = (uint16_t) dane_int_within_range(
					port_str + 1, 65535, "port");
			s = LDNS_STATUS_OK;
		} while (false);
		LDNS_ERR(s, "could not extract service port from TLSA owner");

		do {
			s = LDNS_STATUS_MEM_ERR;
			transport_rdf = ldns_dname_label(tlsa_owner, 1);
			if (! transport_rdf) {
				break;
			}
			transport_str = ldns_rdf2str(transport_rdf);
			if (! transport_str) {
				break;
			}
			if (transport_str[strlen(transport_str) - 1] == '.') {
				transport_str[strlen(transport_str) - 1] =
					'\000';
			}
			if (strcmp(transport_str, "_tcp") == 0) {

				transport = LDNS_DANE_TRANSPORT_TCP;

			} else if (strcmp(transport_str, "_udp") == 0) {

				transport = LDNS_DANE_TRANSPORT_UDP;

			} else if (strcmp(transport_str, "_sctp") == 0) {

				transport = LDNS_DANE_TRANSPORT_SCTP;

			} else {
				fprintf(stderr, "ERROR! Badly formatted "
						"transport label in the "
						"TLSA owner name\n");
				exit(EXIT_FAILURE);
			}
			s = LDNS_STATUS_OK;
			break;
		} while(false);
		LDNS_ERR(s, "could not extract transport from TLSA owner");

		tlsa_owner_str = ldns_rdf2str(tlsa_owner);
		if (! tlsa_owner_str) {
			MEMERR("ldns_rdf2str");
		}
		name = ldns_dname_clone_from(tlsa_owner, 2);
		if (! name) {
			MEMERR("ldns_dname_clone_from");
		}
		name_str = ldns_rdf2str(name);
		if (! name_str) {
			MEMERR("ldns_rdf2str");
		}


	} else 
#endif /* USE_DANE_VERIFY */
		if (argc < 2) {

		print_usage("ldns-dane");

	} else {
		name_str = *argv++; argc--;
		s = ldns_str2rdf_dname(&name, name_str);
		LDNS_ERR(s, "could not ldns_str2rdf_dname");

		port = (uint16_t)dane_int_within_range(*argv++, 65535, "port");
		--argc;

		s = ldns_dane_create_tlsa_owner(&tlsa_owner,
				name, port, transport);
		LDNS_ERR(s, "could not create TLSA owner name");
		tlsa_owner_str = ldns_rdf2str(tlsa_owner);
		if (! tlsa_owner_str) {
			MEMERR("ldns_rdf2str");
		}
	}

	switch (mode) {
	case VERIFY:
		if (argc > 0) {

			print_usage("ldns-dane");
		}
		if (tlsas_file) {

			s = dane_read_tlsas_from_file(&tlsas, tlsas_file,
					tlsa_owner);
			LDNS_ERR(s, "could not read tlas from file");
		} else {
			/* lookup tlsas */
			s = dane_setup_resolver(&res, nameserver_rdf,
			                        keys, assume_dnssec_validity);
			LDNS_ERR(s, "could not dane_setup_resolver");
			s = dane_query(&tlsas, res, tlsa_owner,
					LDNS_RR_TYPE_TLSA, LDNS_RR_CLASS_IN,
					false);
			ldns_resolver_free(res);
		}

		if (s == LDNS_STATUS_DANE_INSECURE) {

			fprintf(stderr, "Warning! TLSA records for %s "
				"were found, but were insecure.\n"
				"PKIX validation without DANE will be "
				"performed. If you wish to perform DANE\n"
				"even though the RR's are insecure, "
				"use the -d option.\n", tlsa_owner_str);

			exit_success = no_tlsas_exit_status;

		} else if (s != LDNS_STATUS_OK) {

			ldns_err("dane_query", s);

		} else if (ldns_rr_list_rr_count(tlsas) == 0) {

			fprintf(stderr, "Warning! No TLSA records for %s "
				"were found.\n"
				"PKIX validation without DANE will be "
				"performed.\n", ldns_rdf2str(tlsa_owner));

			exit_success = no_tlsas_exit_status;

		} else if (assume_pkix_validity) { /* number of  tlsa's > 0 */
			
			/* transform type "CA constraint" to "Trust anchor
			 * assertion" and "Service Certificate Constraint"
			 * to "Domain Issues Certificate"
			 */
			originals = tlsas;
			tlsas = dane_no_pkix_transform(originals);
		}

		break;

	case CREATE:
		if (argc > 0) {
			certificate_usage = dane_int_within_range_table(
					*argv++, 3, "certificate usage",
					dane_certificate_usage_table);
			argc--;
		} else {
			certificate_usage = LDNS_TLSA_USAGE_DANE_EE;
		}
		if (argc > 0) {
			selector = dane_int_within_range_table(
					*argv++, 1, "selector",
					dane_selector_table);
			argc--;
		} else {
			selector = LDNS_TLSA_SELECTOR_SPKI;
		}
		if (argc > 0) {
			matching_type = dane_int_within_range_table(
					*argv++, 2, "matching type",
					dane_matching_type_table);

			argc--;
		} else {
			matching_type = LDNS_TLSA_MATCHING_TYPE_SHA2_256;
		}
		if (argc > 0) {

			print_usage("ldns-dane");
		}
		if ((certificate_usage == LDNS_TLSA_USAGE_CA_CONSTRAINT ||
		     certificate_usage ==
			     LDNS_TLSA_USAGE_SERVICE_CERTIFICATE_CONSTRAINT) &&
		     ! CAfile && ! CApath && ! assume_pkix_validity) {

			fprintf(stderr,
				"When using the \"CA constraint\" or "
			        "\"Service certificate constraint\",\n"
				"-f <CAfile> and/or -p <CApath> options "
				"must be given to perform PKIX validation.\n\n"
				"PKIX validation may be turned off "
				"with the -s option. Note that with\n"
				"\"CA constraint\" the verification process "
				"should then end with a self-signed\n"
				"certificate which must be present "
				"in the server certificate chain.\n\n");

			exit(EXIT_FAILURE);
		}
		tlsas = ldns_rr_list_new();
		break;
	default:
		fprintf(stderr, "Unreachable code\n");
		assert(0);
	}

	/* ssl inititalize */
	SSL_load_error_strings();
	SSL_library_init();

	/* ssl load validation store */
	if (! assume_pkix_validity || CAfile || CApath) {
		store = X509_STORE_new();
		if (! store) {
			ssl_err("could not X509_STORE_new");
		}
		if ((CAfile || CApath) && X509_STORE_load_locations(
					store, CAfile, CApath) != 1) {
			ssl_err("error loading CA certificates");
		}
	}

#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
	ctx =  SSL_CTX_new(SSLv23_client_method());
#else
	ctx =  SSL_CTX_new(TLS_client_method());
	if (ctx && SSL_CTX_dane_enable(ctx) <= 0) {
		ssl_err("could not SSL_CTX_dane_enable");
	}
#endif
	if (! ctx) {
		ssl_err("could not SSL_CTX_new");
	}
	if (cert_file &&
	    SSL_CTX_use_certificate_chain_file(ctx, cert_file) != 1) {
		ssl_err("error loading certificate");
	}

	if (cert_file) { /* ssl load certificate */

		ssl = SSL_new(ctx);
		if (! ssl) {
			ssl_err("could not SSL_new");
		}
		cert = SSL_get_certificate(ssl);
		if (! cert) {
			ssl_err("could not SSL_get_certificate");
		}
#ifndef SSL_CTX_get_extra_chain_certs
#ifndef S_SPLINT_S
		extra_certs = ctx->extra_certs;
#endif /* splint */
#else
		if(!SSL_CTX_get_extra_chain_certs(ctx, &extra_certs)) {
			ssl_err("could not SSL_CTX_get_extra_chain_certs");
		}
#endif
		switch (mode) {
		case CREATE: dane_create(tlsas, tlsa_owner, certificate_usage,
					     offset, selector, matching_type,
					     cert, extra_certs, store,
					     verify_server_name, name);
			     break;
#ifdef USE_DANE_VERIFY
#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
		case VERIFY: if (! dane_verify(tlsas, NULL,
			                       cert, extra_certs, store,
					       verify_server_name, name,
					       assume_pkix_validity)) {
				     success = false;
			     }
			     break;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
		case VERIFY: 
			     usable_tlsas = 0;
			     SSL_set_connect_state(ssl);
			     if (SSL_dane_enable(ssl, name_str) <= 0) {
			     	ssl_err("could not SSL_dane_enable");
			     }
			     if (!verify_server_name) {
			     	SSL_dane_set_flags(ssl, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
			     }
			     for (j = 0; j < ldns_rr_list_rr_count(tlsas); j++) {
			     	int ret;
			     	ldns_rr *tlsa_rr = ldns_rr_list_rr(tlsas, j);

			     	if (ldns_rr_get_type(tlsa_rr) != LDNS_RR_TYPE_TLSA) {
			     		fprintf(stderr, "Skipping non TLSA RR: ");
			     		ldns_rr_print(stderr, tlsa_rr);
			     		fprintf(stderr, "\n");
			     		continue;
			     	}
			     	if (ldns_rr_rd_count(tlsa_rr) != 4) {
			     		fprintf(stderr, "Skipping TLSA with wrong rdata RR: ");
			     		ldns_rr_print(stderr, tlsa_rr);
			     		fprintf(stderr, "\n");
			     		continue;
			     	}
			     	ret = SSL_dane_tlsa_add(ssl,
			     			ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 0)),
			     			ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 1)),
			     			ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 2)),
			     			ldns_rdf_data(ldns_rr_rdf(tlsa_rr, 3)),
			     			ldns_rdf_size(ldns_rr_rdf(tlsa_rr, 3)));
			     	if (ret < 0) {
			     		ssl_err("could not SSL_dane_tlsa_add");
			     	}
			     	if (ret == 0) {
			     		fprintf(stderr, "Skipping unusable TLSA RR: ");
			     		ldns_rr_print(stderr, tlsa_rr);
			     		fprintf(stderr, "\n");
			     		continue;
			     	}
			     	usable_tlsas += 1;
			     }
			     if (!usable_tlsas) {
			     	fprintf(stderr, "No usable TLSA records were found.\n"
			     			"PKIX validation without DANE will be performed.\n");
			     }
			     if (!(store_ctx = X509_STORE_CTX_new())) {
				     ssl_err("could not SSL_new");
			     }
			     if (!X509_STORE_CTX_init(store_ctx, store, cert, extra_certs)) {
				     ssl_err("could not X509_STORE_CTX_init");
			     }
			     X509_STORE_CTX_set_default(store_ctx,
					     SSL_is_server(ssl) ? "ssl_client" : "ssl_server");
			     X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(store_ctx),
					     SSL_get0_param(ssl));
			     X509_STORE_CTX_set0_dane(store_ctx, SSL_get0_dane(ssl));
			     X509_NAME_print_ex_fp(stdout,
					     X509_get_subject_name(cert), 0, 0);
			     if (X509_verify_cert(store_ctx)) {
				fprintf(stdout, " %s-validated successfully\n",
						  usable_tlsas 
						? "dane" : "PKIX");
			     } else {
				fprintf(stdout, " did not dane-validate, because: %s\n",
						X509_verify_cert_error_string(
							X509_STORE_CTX_get_error(store_ctx)));
				success = false;
			     }
			     if (store_ctx) {
				     X509_STORE_CTX_free(store_ctx);
			     }
			     break;
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
#endif /* ifdef USE_DANE_VERIFY */
		default:     break; /* suppress warning */
		}
		SSL_free(ssl);

	} else {/* No certificate file given, creation/validation via TLS. */

		/* We need addresses to connect to */
		if (ldns_rr_list_rr_count(addresses) == 0) {
			s = dane_setup_resolver(&res, nameserver_rdf,
			                        keys, assume_dnssec_validity);
			LDNS_ERR(s, "could not dane_setup_resolver");
			ldns_rr_list_free(addresses);
			addresses =dane_lookup_addresses(res, name, ai_family);
			ldns_resolver_free(res);
		}
		if (ldns_rr_list_rr_count(addresses) == 0) {
			fprintf(stderr, "No addresses for %s\n", name_str);
			exit(EXIT_FAILURE);
		}

		/* for all addresses, setup SSL and retrieve certificates */
		for (i = 0; i < ldns_rr_list_rr_count(addresses); i++) {

			ssl = SSL_new(ctx);
			if (! ssl) {
				ssl_err("could not SSL_new");
			}
			address = ldns_rr_a_address(
					ldns_rr_list_rr(addresses, i));
			assert(address != NULL);
#if OPENSSL_VERSION_NUMBER >= 0x10100000  && ! defined(HAVE_LIBRESSL)
			if (mode == VERIFY) {
				usable_tlsas = 0;
				if (SSL_dane_enable(ssl, name_str) <= 0) {
					ssl_err("could not SSL_dane_enable");
				}
				if (!verify_server_name) {
					SSL_dane_set_flags(ssl, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
				}
				for (j = 0; j < ldns_rr_list_rr_count(tlsas); j++) {
					int ret;
					ldns_rr *tlsa_rr = ldns_rr_list_rr(tlsas, j);

					if (ldns_rr_get_type(tlsa_rr) != LDNS_RR_TYPE_TLSA) {
						fprintf(stderr, "Skipping non TLSA RR: ");
						ldns_rr_print(stderr, tlsa_rr);
						fprintf(stderr, "\n");
						continue;
					}
					if (ldns_rr_rd_count(tlsa_rr) != 4) {
						fprintf(stderr, "Skipping TLSA with wrong rdata RR: ");
						ldns_rr_print(stderr, tlsa_rr);
						fprintf(stderr, "\n");
						continue;
					}
					ret = SSL_dane_tlsa_add(ssl,
							ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 0)),
							ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 1)),
							ldns_rdf2native_int8(ldns_rr_rdf(tlsa_rr, 2)),
							ldns_rdf_data(ldns_rr_rdf(tlsa_rr, 3)),
							ldns_rdf_size(ldns_rr_rdf(tlsa_rr, 3)));
					if (ret < 0) {
						ssl_err("could not SSL_dane_tlsa_add");
					}
					if (ret == 0) {
						fprintf(stderr, "Skipping unusable TLSA RR: ");
						ldns_rr_print(stderr, tlsa_rr);
						fprintf(stderr, "\n");
						continue;
					}
					usable_tlsas += 1;
				}
				if (!usable_tlsas) {
					fprintf(stderr, "No usable TLSA records were found.\n"
							"PKIX validation without DANE will be performed.\n");
				}
			}
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000 */
			s = ssl_connect_and_get_cert_chain(&cert, &extra_certs,
					ssl, name_str, address,port, transport);
			if (s == LDNS_STATUS_NETWORK_ERR) {
				fprintf(stderr, "Could not connect to ");
				ldns_rdf_print(stderr, address);
				fprintf(stderr, " %d\n", (int) port);

				/* All addresses should succeed */
				success = false;
				continue;
			}
			LDNS_ERR(s, "could not get cert chain from ssl");
#if OPENSSL_VERSION_NUMBER >= 0x10100000 && ! defined(HAVE_LIBRESSL)

			if (mode == VERIFY) {
				char *address_str = ldns_rdf2str(address);
				long verify_result = SSL_get_verify_result(ssl);

				fprintf(stdout, "%s", address_str ? address_str : "<address>");
				free(address_str);

				if (verify_result == X509_V_OK) {
					fprintf(stdout, " %s-validated successfully\n",
							  usable_tlsas 
							? "dane" : "PKIX");
				} else {
					fprintf(stdout, " did not dane-validate, because: %s\n",
							X509_verify_cert_error_string(verify_result));
					success = false;
				}
			}
#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000 */
			switch (mode) {
			case CREATE: dane_create(tlsas, tlsa_owner,
						     certificate_usage, offset,
						     selector, matching_type,
						     cert, extra_certs, store,
						     verify_server_name, name);
				     break;

#ifdef USE_DANE_VERIFY
			case VERIFY:
#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(HAVE_LIBRESSL)
				     if (! dane_verify(tlsas, address,
						cert, extra_certs, store,
						verify_server_name, name,
						assume_pkix_validity)) {
					success = false;

				     }
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
				     if (success && interact) {
					     ssl_interact(ssl);
				     }
				     break;
#endif /* USE_DANE_VERIFY */

			default:     break; /* suppress warning */
			}
			while (SSL_shutdown(ssl) == 0);
			SSL_free(ssl);
		} /* end for all addresses */
	} /* end No certification file */

	if (mode == CREATE) {
		if (print_tlsa_as_type52) {
			print_rr_list_as_TYPEXXX(stdout, tlsas);
		} else {
			ldns_rr_list_print(stdout, tlsas);
		}
	}
	ldns_rr_list_deep_free(tlsas);

	/* cleanup */
	SSL_CTX_free(ctx);

	if (nameserver_rdf) {
		ldns_rdf_deep_free(nameserver_rdf);
	}
	if (store) {
		X509_STORE_free(store);
	}
	if (tlsa_owner_str) {
		LDNS_FREE(tlsa_owner_str);
	}
	if (tlsa_owner) {
		ldns_rdf_free(tlsa_owner);
	}
	if (addresses) {
		ldns_rr_list_deep_free(addresses);
	}
	if (success) {
		exit(exit_success);
	} else {
		exit(EXIT_FAILURE);
	}
}
#else  /* HAVE_SSL */

int
main(int argc, char **argv)
{
	fprintf(stderr, "ldns-dane needs OpenSSL support, "
			"which has not been compiled in\n");
	return 1;
}
#endif /* HAVE_SSL */

#else  /* USE_DANE */

int
main(int argc, char **argv)
{
	(void)argc;
	(void)argv;

	fprintf(stderr, "dane support was disabled with this build of ldns, "
			"and has not been compiled in\n");
	return 1;
}

#endif /* USE_DANE */
