/*
 * higher.c
 *
 * Specify some higher level functions that would
 * be usefull to would be developers
 *
 * a Net::DNS like library for C
 *
 * (c) NLnet Labs, 2004-2006
 *
 * See the file LICENSE for the license
 */

#include <ldns/config.h>

#include <ldns/ldns.h>

#ifdef HAVE_SSL
#include <openssl/ssl.h>
#include <openssl/sha.h>
#endif /* HAVE_SSL */

ldns_rr_list *
ldns_get_rr_list_addr_by_name(ldns_resolver *res, ldns_rdf *name, ldns_rr_class c, 
		uint16_t flags)
{
	ldns_pkt *pkt;
	ldns_rr_list *aaaa;
	ldns_rr_list *a;
	ldns_rr_list *result = NULL;
	ldns_rr_list *hostsfilenames;
	size_t i;
	uint8_t ip6;

	a = NULL; 
	aaaa = NULL; 
	result = NULL;

	if (!res) {
		return NULL;
	}
	if (ldns_rdf_get_type(name) != LDNS_RDF_TYPE_DNAME) {
		return NULL;
	}

	ip6 = ldns_resolver_ip6(res); /* we use INET_ANY here, save
					 what was there */

	ldns_resolver_set_ip6(res, LDNS_RESOLV_INETANY);
	
	hostsfilenames = ldns_get_rr_list_hosts_frm_file(NULL);
	for (i = 0; i < ldns_rr_list_rr_count(hostsfilenames); i++) {
		if (ldns_rdf_compare(name, 
					ldns_rr_owner(ldns_rr_list_rr(hostsfilenames, 
							i))) == 0) {
			if (!result) {
				result = ldns_rr_list_new();
			}
			ldns_rr_list_push_rr(result, 
					ldns_rr_clone(ldns_rr_list_rr(hostsfilenames, i)));
		}
	}
	ldns_rr_list_deep_free(hostsfilenames);

	if (result) {
		return result;
	}

	/* add the RD flags, because we want an answer */
	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_AAAA, c, flags | LDNS_RD);
	if (pkt) {
		/* extract the data we need */
		aaaa = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_AAAA, 
			LDNS_SECTION_ANSWER);
		ldns_pkt_free(pkt);
	} 

	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_A, c, flags | LDNS_RD);
	if (pkt) {
		/* extract the data we need */
		a = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_A, LDNS_SECTION_ANSWER);
		ldns_pkt_free(pkt);
	} 
	ldns_resolver_set_ip6(res, ip6);

	if (aaaa && a) {
		result = ldns_rr_list_cat_clone(aaaa, a);
		ldns_rr_list_deep_free(aaaa);
		ldns_rr_list_deep_free(a);
		return result;
	}
	
	if (aaaa) {
		result = ldns_rr_list_clone(aaaa);
	}
	
	if (a) {
		result = ldns_rr_list_clone(a);
	}

	ldns_rr_list_deep_free(aaaa);
	ldns_rr_list_deep_free(a);
	return result;
}

ldns_rr_list *
ldns_get_rr_list_name_by_addr(ldns_resolver *res, ldns_rdf *addr, ldns_rr_class c, 
		uint16_t flags)
{
	ldns_pkt *pkt;
	ldns_rr_list *names;
	ldns_rdf *name;

	names = NULL;

	if (!res || !addr) {
		return NULL;
	}

	if (ldns_rdf_get_type(addr) != LDNS_RDF_TYPE_A &&
			ldns_rdf_get_type(addr) != LDNS_RDF_TYPE_AAAA) {
		return NULL;
	}

	name = ldns_rdf_address_reverse(addr);
	
	/* add the RD flags, because we want an answer */
	pkt = ldns_resolver_query(res, name, LDNS_RR_TYPE_PTR, c, flags | LDNS_RD);
	if (pkt) {
		/* extract the data we need */
		names = ldns_pkt_rr_list_by_type(pkt, 
				LDNS_RR_TYPE_PTR, LDNS_SECTION_ANSWER);
	}
	return names;
}

/* read a line, put it in a buffer, parse the buffer */
ldns_rr_list *
ldns_get_rr_list_hosts_frm_fp(FILE *fp)
{
	return ldns_get_rr_list_hosts_frm_fp_l(fp, NULL);
}

ldns_rr_list *
ldns_get_rr_list_hosts_frm_fp_l(FILE *fp, int *line_nr)
{
	ssize_t i, j;
	size_t cnt;
	char *line;
	char *word;
	char *addr;
	char *rr_str;
	ldns_buffer *linebuf;
	ldns_rr *rr;
	ldns_rr_list *list;
	ldns_rdf *tmp;
	bool ip6;
	ldns_status parse_result;

	line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
	word = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
	addr = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
	rr_str = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
	ip6 = false;
	list = ldns_rr_list_new();
	rr = NULL;
	if(!line || !word || !addr || !rr_str || !list) {
		LDNS_FREE(line);
		LDNS_FREE(word);
		LDNS_FREE(addr);
		LDNS_FREE(rr_str);
		ldns_rr_list_free(list);
		return NULL;
	}

	for(i = ldns_fget_token_l(fp, line, "\n", LDNS_MAX_LINELEN, line_nr);
			i > 0; i = ldns_fget_token_l(fp, line, "\n", LDNS_MAX_LINELEN, line_nr)) {
		/* # is comment */
		if (line[0] == '#') {
			continue;
		}
		/* put it in a buffer for further processing */
		linebuf = LDNS_MALLOC(ldns_buffer);
		if(!linebuf) {
			LDNS_FREE(line);
			LDNS_FREE(word);
			LDNS_FREE(addr);
			LDNS_FREE(rr_str);
			ldns_rr_list_deep_free(list);
			return NULL;
		}

		ldns_buffer_new_frm_data(linebuf, line, (size_t) i);
		for(cnt = 0, j = ldns_bget_token(linebuf, word, LDNS_PARSE_NO_NL, LDNS_MAX_LINELEN);
				j > 0;
				j = ldns_bget_token(linebuf, word, LDNS_PARSE_NO_NL, LDNS_MAX_LINELEN), cnt++) {
			if (cnt == 0) {
				/* the address */
				if ((tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, 
								word))) {
					/* ip6 */
					ldns_rdf_deep_free(tmp);
					ip6 = true;
				} else {
					if ((tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, 
									word))) {
						/* ip4 */
						ldns_rdf_deep_free(tmp);
						ip6 = false;
					} else {
						/* kaput */
						break;
					}
				}
				(void)strlcpy(addr, word, LDNS_MAX_LINELEN+1);
			} else {
				/* la al la la */
				if (ip6) {
					snprintf(rr_str, LDNS_MAX_LINELEN, 
						"%s IN AAAA %s", word, addr);
				} else {
					snprintf(rr_str, LDNS_MAX_LINELEN, 
						"%s IN A %s", word, addr);
				}
				parse_result = ldns_rr_new_frm_str(&rr, rr_str, 0, NULL, NULL);
				if (parse_result == LDNS_STATUS_OK && ldns_rr_owner(rr) && ldns_rr_rd_count(rr) > 0) {
					ldns_rr_list_push_rr(list, ldns_rr_clone(rr));
				}
				ldns_rr_free(rr);
			}
		}
		ldns_buffer_free(linebuf);
	}
	LDNS_FREE(line);
	LDNS_FREE(word);
	LDNS_FREE(addr);
	LDNS_FREE(rr_str);
	return list;
}

ldns_rr_list *
ldns_get_rr_list_hosts_frm_file(char *filename)
{
	ldns_rr_list *names;
	FILE *fp;

	if (!filename) {
                fp = fopen(LDNS_RESOLV_HOSTS, "r");
        
        } else {
                fp = fopen(filename, "r");
        }
        if (!fp) {
                return NULL;
        }

	names = ldns_get_rr_list_hosts_frm_fp(fp);
	fclose(fp);
	return names;
}

uint16_t
ldns_getaddrinfo(ldns_resolver *res, ldns_rdf *node, ldns_rr_class c, 
		ldns_rr_list **ret)
{
	ldns_rdf_type t;
	uint16_t names_found;
	ldns_resolver *r;
	ldns_status s;

	t = ldns_rdf_get_type(node);
	names_found = 0;
	r = res;

	if (res == NULL) {
		/* prepare a new resolver, using /etc/resolv.conf as a guide  */
		s = ldns_resolver_new_frm_file(&r, NULL);
		if (s != LDNS_STATUS_OK) {
			return 0;
		} 
	}

	if (t == LDNS_RDF_TYPE_DNAME) {
		/* we're asked to query for a name */
		*ret = ldns_get_rr_list_addr_by_name(r, node, c, 0);
		names_found = ldns_rr_list_rr_count(*ret);
	}

	if (t == LDNS_RDF_TYPE_A || t == LDNS_RDF_TYPE_AAAA) {
		/* an address */
		*ret = ldns_get_rr_list_name_by_addr(r, node, c, 0);
		names_found = ldns_rr_list_rr_count(*ret);
	}

	if (res == NULL) {
		ldns_resolver_deep_free(r);
	}
	
	return names_found;
}

bool
ldns_nsec_type_check(ldns_rr *nsec, ldns_rr_type t)
{
	/* does the nsec cover the t given? */
	/* copied from host2str.c line 465: ldns_rdf2buffer_str_nsec */
        uint8_t window_block_nr;
        uint8_t bitmap_length;
        uint16_t type;
        uint16_t pos = 0;
        uint16_t bit_pos;
	ldns_rdf *nsec_type_list = ldns_rr_rdf(nsec, 1); 
	uint8_t *data = ldns_rdf_data(nsec_type_list);

	while(pos < ldns_rdf_size(nsec_type_list)) {
		window_block_nr = data[pos];
		bitmap_length = data[pos + 1];
		pos += 2;

		for (bit_pos = 0; bit_pos < (bitmap_length) * 8; bit_pos++) {
			if (ldns_get_bit(&data[pos], bit_pos)) {
				type = 256 * (uint16_t) window_block_nr + bit_pos;

				if ((ldns_rr_type)type == t) {
					/* we have a winner */
					return true;
				}
			}
		}
		pos += (uint16_t) bitmap_length;
	}
	return false;
}

void
ldns_print_rr_rdf(FILE *fp, ldns_rr *r, int rdfnum, ...)
{
	int16_t rdf;
	ldns_rdf *rd;
	va_list va_rdf;
	va_start(va_rdf, rdfnum);

	for (rdf = (int16_t)rdfnum; rdf != -1; rdf = (int16_t)va_arg(va_rdf, int)) 
	{
		rd = ldns_rr_rdf(r, rdf);
		if (!rd) {
			continue;
		} else {
			ldns_rdf_print(fp, rd);
			fprintf(fp, " "); /* not sure if we want to do this */
		}
	}
	va_end(va_rdf);
}
