/* This program is based on (IIRC)
 * getdns/spec/example/example-simple-answers.c.
 *
 * Resolve DNS name argv[1] of type argv[2] using libgetdns and write
 * the resulting RRs to two files, in DNS binary wire format:
 *
 * - binout: /just_address_answers/0/address_data and
 *           /just_address_answers/1/address_data
 *
 * - treeout: output from getdns_rr_dict2wire for all RR's in the
 *            answer section
 *
 * Based on getdns/spec/example/example-simple-answers.c.
 */

#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <getdns/getdns.h>
#include <getdns/getdns_ext_libevent.h>
#include <event2/event.h>

#include "common.h"

/* Set up the callback function, which will also do the processing of the results */
void callback(getdns_context        *context,
              getdns_callback_type_t callback_type,
              getdns_dict           *response,
              void                  *userarg,
              getdns_transaction_t   transaction_id)
{
	getdns_return_t r;  /* Holder for all function returns */
	uint32_t        status;
	getdns_bindata  *address_data;
	char            *first = NULL, *second = NULL;
	FILE *binoutfp = NULL, *treeoutfp = NULL;

	(void) context; /* unused parameter */

	binoutfp = fopen("binout", "w");
	assert(binoutfp != NULL);
	treeoutfp = fopen("treeout", "w");
	assert(treeoutfp != NULL);

	printf( "Callback for query \"%s\" with request ID %"PRIu64".\n"
	      , (char *)userarg, transaction_id );

	switch(callback_type) {
	case GETDNS_CALLBACK_CANCEL:
		printf("Transaction with ID %"PRIu64" was cancelled.\n", transaction_id);
		return;
	case GETDNS_CALLBACK_TIMEOUT:
		printf("Transaction with ID %"PRIu64" timed out.\n", transaction_id);
		return;
	case GETDNS_CALLBACK_ERROR:
		printf("An error occurred for transaction ID %"PRIu64".\n", transaction_id);
		return;
	default: break;
	}
	assert( callback_type == GETDNS_CALLBACK_COMPLETE );

	if ((r = getdns_dict_get_int(response, "status", &status)))
		fprintf(stderr, "Could not get \"status\" from reponse");

	else if (status != GETDNS_RESPSTATUS_GOOD)
		fprintf(stderr, "The search had no results, and a return value of %"PRIu32".\n", status);

	static char *TREES[] = {"replies_tree", "validation_chain"};
	static const int TREES_LEN = 2;
	for (int i = 0; i < TREES_LEN; i++) {
                if (dump_tree(treeoutfp, response, TREES[i], "answer"))
			fprintf(stderr, "Could not dump %s to file\n", TREES[i]);

		else if ((r = getdns_dict_get_bindata(response, "/just_address_answers/0/address_data", &address_data)))
			fprintf(stderr, "Could not get first address");

		else if (fwrite(address_data->data, 1, address_data->size, binoutfp) !=
			 address_data->size)
			fprintf(stderr, "first fwrite: %s\n", strerror(errno));

		else if (!(first = getdns_display_ip_address(address_data)))
			fprintf(stderr, "Could not convert first address to string\n");

		else if ((r = getdns_dict_get_bindata(response, "/just_address_answers/1/address_data", &address_data)))
			fprintf(stderr, "Could not get second address");

		else if (fwrite(address_data->data, 1, address_data->size, binoutfp) !=
			 address_data->size)
			fprintf(stderr, "second fwrite: %s\n", strerror(errno));

		else if (!(second = getdns_display_ip_address(address_data)))
			fprintf(stderr, "Could not convert second address to string\n");
		if (first) {
			printf("The address is %s\n", first);
			free(first);
		}
		if (second) {
			printf("The address is %s\n", second);
			free(second);
		}
		if (r) {
			assert( r != GETDNS_RETURN_GOOD );
			fprintf(stderr, ": %d\n", r);
		}
	}
	getdns_dict_destroy(response);
	if (binoutfp) fclose(binoutfp);
	if (treeoutfp) fclose(treeoutfp);
}

static getdns_return_t
send_query(getdns_context *context,
	   char *query_name,
	   const uint16_t query_type,
	   getdns_transaction_t *transaction_id)
{
	getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR;
	getdns_dict *extensions = getdns_dict_create();
	char *userarg = query_name;

	assert(extensions != NULL);

	r = getdns_dict_set_int(extensions,
				"dnssec_return_validation_chain",
				GETDNS_EXTENSION_TRUE);
	if (r) return r;

#if 0
	r = getdns_dict_set_int(extensions,
				"dnssec_return_status",
				GETDNS_EXTENSION_TRUE);
	if (r) return r;
#endif

	if (query_type == 0)
		r = getdns_address(context, query_name, extensions, userarg,
				   transaction_id, callback);
	else
		r = getdns_general(context, query_name, query_type,
				   extensions, userarg,
				   transaction_id, callback);
	if (r) return r;

	return r;
}

#define OWN_ROOT

#ifdef OWN_ROOT
getdns_list *
read_own_rootfile()
{
  FILE *fp = fopen("root.txt", "r");
  getdns_list *list = NULL;

  if (fp && !getdns_fp2rr_list(fp, &list, ".", 0))
    return list;
  return NULL;
}
#endif

int main(int argc, char *argv[])
{
	getdns_return_t      r;  /* Holder for all function returns */
	getdns_context      *context    = NULL;
	struct event_base   *event_base = NULL;
	char                *query_name = "www.example.com";
	uint16_t            query_type = 0;
	getdns_transaction_t transaction_id;
	getdns_list *trust_anchors;
        getdns_list *root_servers = NULL;


	if (argc > 1)
		query_name = argv[1];
	if (argc > 2)
		query_type = (uint16_t) atoi(argv[2]);

	if ((r = getdns_context_create(&context, 0)))
		fprintf(stderr, "Trying to create the context failed");

#ifdef OWN_ROOT
        else if (!(root_servers = read_own_rootfile()))
                fprintf(stderr, "Reading root hints file failed.\n");
#endif

        else if ((r = getdns_context_set_dns_root_servers(context,
                                                          root_servers)))
                fprintf(stderr, "Setting root name servers failed.\n");

	else if ((r = getdns_context_get_dnssec_trust_anchors(
			  context, &trust_anchors)) ||
		 trust_anchors == NULL)
		fprintf(stderr, "No trust anchor.\n");

	else if (!(event_base = event_base_new()))
		fprintf(stderr, "Trying to create the event base failed.\n");

	else if ((r = getdns_extension_set_libevent_base(context, event_base)))
		fprintf(stderr, "Setting the event base failed");

	else if ((r = send_query(context, query_name, query_type,
				 &transaction_id)))
		fprintf(stderr, "Queueing query failed: %d\n", r);

	else {
		printf("Request with transaction ID %"PRIu64" scheduled.\n",
		       transaction_id);
		if (event_base_dispatch(event_base) < 0)
			fprintf(stderr, "Error dispatching events\n");
	}

	/* Clean up */
	if (event_base)
		event_base_free(event_base);

	if (context)
		getdns_context_destroy(context);

	/* Assuming we get here, leave gracefully */
	exit(EXIT_SUCCESS);
}