/*************************************************
*     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",
  "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_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)
{
int rc;
int size = 0;
int ptr = 0;
int type = T_TXT;
uschar *yield = NULL;
uschar *equals = Ustrchr(keystring, '=');
uschar buffer[256];

dns_record *rr;
dns_answer dnsa;
dns_scan dnss;

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

/* 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))
  {
  int len = 0;        /* Stop picky compilers warning */
  uschar *s = NULL;

  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)
      {
      len = Ustrlen(da->address);
      if (yield == NULL)
        {
        yield = da->address;
        ptr = len;
        size = ptr + 1;
        }
      else
        {
        yield = string_cat(yield, &size, &ptr, US"\n", 1);
        yield = string_cat(yield, &size, &ptr, da->address, len);
        }
      }
    continue;
    }

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

  if (type == T_TXT)
    {
    len = (rr->data)[0];
    s = string_copyn((uschar *)(rr->data+1), len);
    }
  else   /* T_CNAME, T_MX, T_NS, T_PTR */
    {
    uschar *p = (uschar *)(rr->data);
    int ssize = 264;
    s = store_get(ssize);
    if (type == T_MX)
      {
      int number;
      GETSHORT(number, p);      /* pointer is advanced */
      sprintf(CS s, "%d ", number);
      len = Ustrlen(s);
      }
    p += dn_expand(dnsa.answer, dnsa.answer + dnsa.answerlen, p,
      (DN_EXPAND_ARG4_TYPE)(s + len), ssize - len);
    len += Ustrlen(s+len);
    store_reset(s + len + 1);
    }

  if (yield == NULL)
    {
    yield = s;
    ptr = len;
    size = ptr + 1;
    }
  else
    {
    yield = string_cat(yield, &size, &ptr, US"\n", 1);
    yield = string_cat(yield, &size, &ptr, s, len);
    }
  }

yield[ptr] = 0;
*result = yield;

return OK;
}

/* End of lookups/dnsdb.c */
