/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 1995 - 2004 */
/* See the file NOTICE for conditions of use and distribution. */

#include "../exim.h"
#include "lf_functions.h"
#include "dnsdb.h"



/* Ancient systems (e.g. SunOS4) don't appear to have T_TXT defined in their
header files. */

#ifndef T_TXT
#define T_TXT 16
#endif

/* Table of recognized DNS record types and their integer values. */

static char *type_names[] = {
  "a",
#if HAVE_IPV6
  "aaaa",
  #ifdef SUPPORT_A6
  "a6",
  #endif
#endif
  "cname",
  "mx",
  "ns",
  "ptr",
  "srv",
  "txt" };

static int type_values[] = {
  T_A,
#if HAVE_IPV6
  T_AAAA,
  #ifdef SUPPORT_A6
  T_A6,
  #endif
#endif
  T_CNAME,
  T_MX,
  T_NS,
  T_PTR,
  T_SRV,
  T_TXT };


/*************************************************
*              Open entry point                  *
*************************************************/

/* See local README for interface description. */

void *
dnsdb_open(uschar *filename, uschar **errmsg)
{
filename = filename;   /* Keep picky compilers happy */
errmsg = errmsg;       /* Ditto */
return (void *)(-1);   /* Any non-0 value */
}



/*************************************************
*           Find entry point for dnsdb           *
*************************************************/

/* See local README for interface description. */

int
dnsdb_find(void *handle, uschar *filename, uschar *keystring, int length,
  uschar **result, uschar **errmsg, BOOL *do_cache)
{
int rc;
int size = 256;
int ptr = 0;
int type = T_TXT;
uschar *orig_keystring = keystring;
uschar *equals = Ustrchr(keystring, '=');
uschar buffer[256];

/* Because we're the working in the search pool, we try to reclaim as much
store as possible later, so we preallocate the result here */

uschar *yield = store_get(size);

dns_record *rr;
dns_answer dnsa;
dns_scan dnss;

handle = handle;           /* Keep picky compilers happy */
filename = filename;
length = length;
do_cache = do_cache;

/* If the keystring contains an = this is preceded by a type name. */

if (equals != NULL)
  {
  int i;
  int len = equals - keystring;
  for (i = 0; i < sizeof(type_names)/sizeof(uschar *); i++)
    {
    if (len == Ustrlen(type_names[i]) &&
        strncmpic(keystring, US type_names[i], len) == 0)
      {
      type = type_values[i];
      break;
      }
    }
  if (i >= sizeof(type_names)/sizeof(uschar *))
    {
    *errmsg = US"unsupported DNS record type";
    return DEFER;
    }
  keystring += len + 1;
  }

/* If the type is PTR, we have to construct the relevant magic lookup
key. This code is now in a separate function. */

if (type == T_PTR)
  {
  dns_build_reverse(keystring, buffer);
  keystring = buffer;
  }

DEBUG(D_lookup) debug_printf("dnsdb key: %s\n", keystring);

/* Initialize the resolver, in case this is the first time it is used
in this run. Then do the lookup and sort out the result. */

dns_init(FALSE, FALSE);
rc = dns_lookup(&dnsa, keystring, type, NULL);

if (rc == DNS_NOMATCH || rc == DNS_NODATA) return FAIL;
if (rc != DNS_SUCCEED) return DEFER;

for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
     rr != NULL;
     rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
  {
  if (rr->type != type) continue;

  /* There may be several addresses from an A6 record. Put newlines between
  them, just as for between several records. */

  if (type == T_A ||
      #ifdef SUPPORT_A6
      type == T_A6 ||
      #endif
      type == T_AAAA)
    {
    dns_address *da;
    for (da = dns_address_from_rr(&dnsa, rr); da != NULL; da = da->next)
      {
      if (ptr != 0) yield = string_cat(yield, &size, &ptr, US"\n", 1);
      yield = string_cat(yield, &size, &ptr, da->address, Ustrlen(da->address));
      }
    continue;
    }

  /* Other kinds of record just have one piece of data each. */

  if (ptr != 0) yield = string_cat(yield, &size, &ptr, US"\n", 1);

  if (type == T_TXT)
    {
    yield = string_cat(yield, &size, &ptr, (uschar *)(rr->data+1),
      (rr->data)[0]);
    }
  else   /* T_CNAME, T_MX, T_NS, T_PTR */
    {
    uschar s[264];
    uschar *p = (uschar *)(rr->data);
    if (type == T_MX)
      {
      int num;
      GETSHORT(num, p);            /* pointer is advanced */
      sprintf(CS s, "%d ", num);
      yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
      }
    else if (type == T_SRV)
      {
      int num, weight, port;
      GETSHORT(num, p);            /* pointer is advanced */
      GETSHORT(weight, p);
      GETSHORT(port, p);
      sprintf(CS s, "%d %d %d ", num, weight, port);
      yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
      }
    rc = dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
      (DN_EXPAND_ARG4_TYPE)(s), sizeof(s));

    /* If an overlong response was received, the data will have been
    truncated and dn_expand may fail. */

    if (rc < 0)
      {
      log_write(0, LOG_MAIN, "host name alias list truncated for %s",
        orig_keystring);
      break;
      }
    else yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
    }
  }

yield[ptr] = 0;
store_reset(yield + ptr + 1);    /* Reclaim unused */
*result = yield;

return OK;
}

/* End of lookups/dnsdb.c */
