/*
 * Copyright (c) 2016, NORDUnet A/S.
 * See LICENSE for licensing information.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <getopt.h>
#include <getdns/getdns.h>
#include "erlport.h"
#include "dnssec_test.h"

static char *testmode = NULL;

/* getdns/src/convert.c */
getdns_return_t getdns_wire2rr_dict(const uint8_t *wire, size_t wire_len,
                                    getdns_dict **rr_dict);

#if !defined(TEST)
static getdns_return_t
validate(const unsigned char *records,
         size_t records_len,
         getdns_list *trust_anchors)
{
  getdns_return_t r = GETDNS_DNSSEC_INDETERMINATE;
  getdns_list *to_validate = getdns_list_create();
  getdns_list *support_records = getdns_list_create();
  getdns_dict *records_dict = NULL;

  if (to_validate == NULL || support_records == NULL)
    {
      r = GETDNS_RETURN_MEMORY_ERROR;
      goto out;
    }

  /* TODO: figure out if we get _all_ RRs in records here bc i have
     the feeling that we're not supposed to mix RR types in the same
     dict; maybe this will help some:
     https://getdnsapi.net/pipermail/users/2015-May/000032.html
  */
  r = getdns_wire2rr_dict(records, records_len, &records_dict);
  if (r)
    goto out;

  /*
    to_validate: one dict with the DS and one with a RRSIG for that DS
    support_records: DS and DNSKEY dicts with accompanying RRSIG's
    trust_anchors: DNSKEY (or DS)
   */
  r = getdns_list_set_dict(to_validate, i, records_dict);
  if (r)
    goto out;

  r = getdns_validate_dnssec(to_validate,
                             support_records,
                             trust_anchors);

out:
  if (to_validate)
    getdns_list_destroy(to_validate);
  if (support_records)
    getdns_list_destroy(support_records);

  return r;
}
#endif  /* !TEST */

static void
loop(getdns_list *trust_anchors)
{
  getdns_return_t r = GETDNS_RETURN_GENERIC_ERROR;
  unsigned char buf[65536];
  ssize_t len;

  while ((len = read_command(buf, sizeof(buf), 4)) > 0)
    {
      unsigned char *reply = NULL;

#if !defined(TEST)
      r = validate(buf, len, trust_anchors);
#else
      r = test_validate(buf, len, trust_anchors, testmode);
#endif

      switch (r)
        {
        case GETDNS_RETURN_GOOD:
          reply = (unsigned char *) "valid";
          break;
        default:
          fprintf(stderr, "error %d\n", r); /* DEBUG */
          reply = (unsigned char *) "err";
        }

      write_reply(reply, strlen((const char *) reply), 4);
    }
}

int
main(int argc, char *argv[])
{
  int c;
  getdns_list *trust_anchors = NULL;
  time_t trust_anchor_date;

  while (1) {
    static struct option long_options[] = {
      {"testmode", required_argument, NULL, 't'},
      {0, 0, 0, 0}};

    c = getopt_long(argc, argv, "", long_options, NULL);
    if (c == -1)
      break;

    switch (c)
      {
#if defined(TEST)
      case 't':
        testmode = optarg;
        break;
#endif
      default:
        fprintf(stderr, "dnssecport: bad option: %s", argv[optind]);
        return -1;
      }
  }

  if (optind < argc) /* Using getdns trust anchor. */
    {
      trust_anchors = getdns_root_trust_anchor(&trust_anchor_date);
    }

  loop(trust_anchors);

  return 0;
}