/* *************************************************************
   RWhois Software

   Copyright (c) 1994 Scott Williamson and Mark Kosters
   Copyright (c) 1996-2000 Network Solutions, Inc.

   See the file LICENSE for conditions of use and distribution.
**************************************************************** */

#include "auth_area.h"

#include "attributes.h"
#include "common_regexps.h"
#include "compat.h"
#include "conf.h"
#include "defines.h"
#include "dl_list.h"
#include "fileutils.h"
#include "ip_network.h"
#include "log.h"
#include "main_config.h"
#include "misc.h"
#include "schema.h"
#include "strutil.h"

/* local definations */
#define DEFAULT_ATTRIB_DIR         "attribute_defs"
#define DEFAULT_DATA_DIR           "data"
#define DEFAULT_SCHEMA_FILE        "schema"
#define DEFAULT_SOA_FILE           "soa"
#define DEFAULT_HOSTMASTER         "hostmaster"
#define DEFAULT_REFRESH_INTERVAL   3600
#define DEFAULT_INCREMENT_INTERVAL 1800
#define DEFAULT_RETRY_INTERVAL     60
#define DEFAULT_TIME_TO_LIVE       86400

static int check_soa PROTO((auth_area_struct *aa));

static int same_auth_area PROTO((auth_area_struct *aa,
                                 auth_area_struct *bb));

static int write_new_soa_file PROTO((char *file, char *suffix,
                                     auth_area_struct *aa,
                                     dl_list_type     *paths_list));

static int write_server_list PROTO((FILE         *fp,
                                    char         *type,
                                    dl_list_type *serv_list));

static int write_guardian_list PROTO((FILE *fp, dl_list_type *guard_list));

static int write_auth_area PROTO((char             *suffix,
                                  auth_area_struct *aa,
                                  dl_list_type     *paths_list));


static char *get_server_hostaddr PROTO((server_struct *serv));

static int count_server_entries PROTO((dl_list_type *servlst,
                                       server_struct *serv));

static int verify_server_data PROTO((server_struct *server, char *aa_name));

static int verify_server_list PROTO((auth_area_struct *aa, char *list_type));

static int count_guardian_entries PROTO((dl_list_type *guard_list,
                                         char         *guard_str));

static int verify_guardian_list PROTO((auth_area_struct *aa));

static int verify_auth_area PROTO((auth_area_struct *aa));

/* local statics */

static dl_list_type *auth_area_list = NULL;


/* ------------------ Local Functions ------------------ */


/* check_soa: given an auth-area record, check for null or illegal
   values.  If found, log errors and return FALSE */
static int
check_soa(aa)
  auth_area_struct *aa;
{
  char hostname[MAX_LINE];
  char port[MAX_LINE];

  if (!aa || NOT_STR_EXISTS(aa->name))
  {
    log(L_LOG_ERR, CONFIG, "SOA: null record");
    return FALSE;
  }

  if (aa->refresh_interval == 0)
  {
    log(L_LOG_ERR, CONFIG, "SOA: Refresh-Interval required for '%s'",
        aa->name);
    return FALSE;
  }
  if (aa->increment_interval == 0)
  {
    log(L_LOG_ERR, CONFIG, "SOA: Increment-Interval required for '%s'",
        aa->name);
    return FALSE;
  }
  if (aa->retry_interval == 0)
  {
    log(L_LOG_ERR, CONFIG, "SOA: Retry-Interval required for '%s'",
        aa->name);
    return FALSE;
  }
  if (aa->time_to_live == 0)
  {
    log(L_LOG_ERR, CONFIG, "SOA: Time-To-Live required for '%s'",
        aa->name);
    return FALSE;
  }
  if (NOT_STR_EXISTS(aa->primary_server))
  {
    log(L_LOG_ERR, CONFIG, "SOA: Primary-Server required for '%s'",
        aa->name);
    return FALSE;
  }

  /* primary server syntax check, format:  hostname:port */
  if (!parse_line(aa->primary_server, hostname, port))
  {
    log(L_LOG_ERR, CONFIG,
        "SOA: Primary-Server for '%s' has incorrect format: %s",
        aa->name,
        aa->primary_server);
    return FALSE;
  }
  if (!is_valid_hostname(hostname))
  {
    log(L_LOG_ERR, CONFIG,
        "SOA: Primary-Server for '%s' has incorrect hostname: %s",
        aa->name,
        hostname);
    return FALSE;
  }
  if (!is_valid_port(port))
  {
    log(L_LOG_ERR, CONFIG,
        "SOA: Primary-Server '%s' for '%s' has incorrect port: %s",
        aa->name,
        hostname,
        port);
    return FALSE;
  }

  if (NOT_STR_EXISTS(aa->hostmaster))
  {
    log(L_LOG_ERR, CONFIG, "SOA: Hostmaster required for '%s'",
        aa->name);
    return FALSE;
  }
  if (NOT_STR_EXISTS(aa->serial_no))
  {
    log(L_LOG_ERR, CONFIG, "SOA: Serial-Number required for '%s'",
        aa->name);
    return FALSE;
  }

  return TRUE;
}

/* compare the two auth-areas: just compare the name and the type */
static int
same_auth_area(aa,  bb)
  auth_area_struct *aa;
  auth_area_struct *bb;
{

  if (!aa || !bb) return FALSE;

  /* compare auth_area name and type */
  if (aa->type == bb->type && STR_EQ(aa->name, bb->name))
  {
    return TRUE;
  }

  return FALSE;
}

/* write authority area soa file. The optional suffix is used to create
   a new temporary soa file name. If file was created on disk,
   add the file name to the paths_list. */
static int
write_new_soa_file(file, suffix, aa, paths_list)
  char *file;
  char *suffix;
  auth_area_struct  *aa;
  dl_list_type *paths_list;
{
  FILE *fp = NULL;
  char new_file[MAX_FILE];

  if (!file || !*file || !aa || !paths_list) return FALSE;

  bzero(new_file, sizeof(new_file));
  strncpy(new_file, file, sizeof(new_file)-1);
  strncat(new_file, suffix, sizeof(new_file)-1);

  if ((fp = open_file_to_write(new_file, 60, paths_list)) == NULL)
  {
    log(L_LOG_ERR, CONFIG, "could not open soa file '%s': %s",
        new_file, strerror(errno));
    return FALSE;
  }

  fprintf(fp, "%s: %s\n", SOA_SERIAL_NUMBER, SAFE_STR(aa->serial_no, ""));
  fprintf(fp, "%s: %ld\n", SOA_REFRESH_INTERVAL, aa->refresh_interval);
  fprintf(fp, "%s: %ld\n", SOA_INCREMENT_INTERVAL, aa->increment_interval);
  fprintf(fp, "%s: %ld\n", SOA_RETRY_INTERVAL, aa->retry_interval);
  fprintf(fp, "%s: %ld\n", SOA_TIME_TO_LIVE, aa->time_to_live);
  fprintf(fp, "%s: %s\n", SOA_PRIMARY_SERVER,
                          SAFE_STR(aa->primary_server, ""));
  fprintf(fp, "%s: %s\n", SOA_HOSTMASTER, SAFE_STR(aa->hostmaster, ""));

  release_file_lock(new_file, fp);

  dl_list_append(paths_list, xstrdup(new_file));

  return TRUE;
}

/* writes master/slave server entries to rwhois auth-area file if the list
   is not empty. */
static int
write_server_list(fp, type, serv_list)
  FILE         *fp;
  char         *type;
  dl_list_type *serv_list;
{
  int           not_done;
  server_struct *serv;

  if (!fp || !type || !*type) return FALSE;

  if (dl_list_empty(serv_list))
  {
    /* list is empty */
    return TRUE;
  }

  not_done = dl_list_first(serv_list);
  while (not_done)
  {
    serv = dl_list_value(serv_list);

    /* print server list information */
    fprintf(fp, "%s: %s %d\n", type, get_server_hostaddr(serv), serv->port);

    not_done = dl_list_next(serv_list);
  }
  return TRUE;
}

/* write guardian entries to the rwhois auth-area file, if the
   list is not empty */
static int
write_guardian_list(fp, guard_list)
  FILE         *fp;
  dl_list_type *guard_list;
{
  int  not_done;
  char *guard_item;

  if (!fp) return FALSE;

  if (dl_list_empty(guard_list))
  {
    return TRUE;
  }

  not_done = dl_list_first(guard_list);
  while (not_done)
  {
    guard_item = dl_list_value(guard_list);

    fprintf(fp, "%s: %s\n", AA_GUARDIAN_ARG, guard_item);

    not_done = dl_list_next(guard_list);
  }
  return TRUE;
}

/* creates authority area directory, its data and attribute-defs directories,
   and calls to write the auhority area schema, and soa file. */
static int
write_auth_area(suffix, aa, paths_list)
  char             *suffix;
  auth_area_struct *aa;
  dl_list_type     *paths_list;
{
  char aa_dir[MAX_FILE];
  char attr_dir[MAX_FILE];

  if (!aa || !paths_list) return FALSE;

  if (!check_aa_syntax(aa->name, aa_dir))
  {
    log(L_LOG_ERR, CONFIG, "'%s' is not a valid authority area syntax",
        SAFE_STR(aa->name, ""));
    return FALSE;
  }

  /* create authority area directory */
  if (!directory_exists(aa_dir))
  {
    if (!make_config_dir(aa_dir, 0755, paths_list))
    {
      log(L_LOG_ERR, CONFIG,
          "could not create auth-area directory '%s': %s",
          SAFE_STR(aa_dir, ""), strerror(errno));
      return FALSE;
    }
  }

  /* Secondary server does not need anything from here on */
  if (aa->type == AUTH_AREA_PRIMARY)
  {
    /* create authority area data directory */
    if (!directory_exists(aa->data_dir))
    {
      if (!make_config_dir(aa->data_dir, 0755, paths_list))
      {
        log(L_LOG_ERR, CONFIG,
            "could not create auth-area data directory '%s': %s",
            SAFE_STR(aa->data_dir, ""), strerror(errno));
        return FALSE;
      }
    }

    /* create authority area attribute definitions directory */
    bzero(attr_dir, sizeof(attr_dir));
    strncat(attr_dir, aa_dir, sizeof(attr_dir)-1);
    strncat(attr_dir, "/", sizeof(attr_dir)-1);
    strncat(attr_dir, DEFAULT_ATTRIB_DIR, sizeof(attr_dir)-1);
    if (!directory_exists(attr_dir))
    {
      if (!make_config_dir(attr_dir, 0755, paths_list))
      {
        log(L_LOG_ERR, CONFIG,
            "could not create auth-area attribute-defs directory '%s': %s",
            SAFE_STR(attr_dir, ""), strerror(errno));
        return FALSE;
      }
    }

    /* write soa file - under authority area */
    if (!write_new_soa_file(aa->soa_file, suffix, aa, paths_list))
    {
      log(L_LOG_ERR, CONFIG, "error in writing soa file '%s'",
          SAFE_STR(aa->soa_file, ""));
      return FALSE;
    }

    /* write schema file - under authority area */
    if (!write_schema_file(aa->schema_file, suffix, aa, paths_list))
    {
      log(L_LOG_ERR, CONFIG, "error in writing schema file '%s'",
          SAFE_STR(aa->schema_file, ""));
      return FALSE;
    }
  }

  return TRUE;
}

/* server info can be defined by its host name or ip address. This
   function returns whichever(hostname/ip-addr) is defined for the
   server. */
static char *
get_server_hostaddr(serv)
  server_struct *serv;
{
  if (STR_EXISTS(serv->name)) return( serv->name );

  return( SAFE_STR(serv->addr, "") );
}

/* count the number of times a given server occurs in the server
   list. */
static int
count_server_entries(servlst, serv)
  dl_list_type  *servlst;
  server_struct *serv;
{
  int           not_done;
  int           n_serv;
  dl_node_type  *orig_posn;
  server_struct *serv_item;

  if (!serv) return( 0 );

  if (dl_list_empty(servlst))
  {
    return( 0 );
  }

  /* save the current position */
  orig_posn = dl_list_get_pos(servlst);

  n_serv   = 0;
  not_done = dl_list_first(servlst);

  while (not_done)
  {
    serv_item = dl_list_value(servlst);
    if ( STR_EQ(get_server_hostaddr(serv_item), get_server_hostaddr(serv)) &&
         (serv_item->port == serv->port) )
    {
      n_serv++;
    }
    not_done = dl_list_next(servlst);
  }

  /* restore the saved position */
  dl_list_put_pos(servlst, orig_posn);

  return( n_serv );
}

/* verifies the contents of master/slave server info structure. */
static int
verify_server_data(server, aa_name)
  server_struct *server;
  char          *aa_name;
{
  int ret;

  if (!server || !aa_name || !*aa_name) return FALSE;

  if ((ret = examin_hostname(get_server_hostaddr(server))))
  {
    log(L_LOG_ERR, CONFIG,
      "'%s' authority area master/slave server hostname not valid (%s:%d)",
      aa_name, get_server_hostaddr(server), server->port,
      examin_error_string(ret));
    return FALSE;
  }
  if (server->port < 1)
  {
    log(L_LOG_ERR, CONFIG,
      "'%s' authority area master/slave server (%s:%d) port: %s",
      aa_name, get_server_hostaddr(server), server->port,
      examin_error_string(ERW_NUMVAL));
  }

  return TRUE;
}

/* verify the master/slave server list. Make sure a secondary authority
   area does not have master list empty, and make sure a primary does
   does not have any master servers defined. */
static int
verify_server_list(aa, list_type)
  auth_area_struct *aa;
  char             *list_type;
{
  int           n;
  int           not_done;
  server_struct *serv;
  dl_list_type  *servlst;

  if (!aa || !list_type) return FALSE;

  n = 0;

  if (STR_EQ(list_type, AA_MASTER))
  {
    servlst = aa->master;
  }
  else if (STR_EQ(list_type, AA_SLAVE))
  {
    servlst = aa->slave;
  }
  else
  {
    return FALSE;
  }

  not_done = dl_list_first(servlst);
  while (not_done)
  {
    serv = dl_list_value(servlst);

    if (!verify_server_data(serv, aa->name))
    {
      return FALSE;
    }
    if (count_server_entries(servlst, serv) > 1)
    {
      log(L_LOG_ERR, CONFIG,
        "duplicate entries in '%s' server list of '%s' authority area: %s:%d",
          list_type, aa->name, get_server_hostaddr(serv), serv->port);
      return FALSE;
    }
    n++;

    not_done = dl_list_next(servlst);
  }

  if (aa->type == AUTH_AREA_PRIMARY && n > 0 && STR_EQ(list_type, AA_MASTER))
  {
    log(L_LOG_ERR, CONFIG,
        "Master server list must be empty for primary authority area: %s",
        aa->name);
    return FALSE;
  }
  if (aa->type == AUTH_AREA_SECONDARY)
  {
    if (n < 1 && STR_EQ(list_type, AA_MASTER))
    {
      log(L_LOG_ERR, CONFIG,
          "Secondary authority area '%s' must have atleast one master server",
          aa->name, n);
      return FALSE;
    }
    if (n > 0 && STR_EQ(list_type, AA_SLAVE))
    {
      log(L_LOG_ERR, CONFIG,
          "Slave server list must be empty for secondary authority area: %s",
          aa->name);
      return FALSE;
    }
  }

  return TRUE;
}

/* count the number of times a guardian string occurs in the guardian
   list of the authority area. */
static int
count_guardian_entries(guard_list, guard_str)
  dl_list_type *guard_list;
  char         *guard_str;
{
  int          not_done;
  int          n;
  char         *guard_item;
  dl_node_type *orig_posn;

  if (!guard_str) return( 0 );

  if (dl_list_empty(guard_list)) return( 0 );

  /* save the current position */
  orig_posn = dl_list_get_pos(guard_list);

  n = 0;
  not_done = dl_list_first(guard_list);
  while (not_done)
  {
    guard_item = dl_list_value(guard_list);
    if (STR_EQ(guard_item, guard_str))
    {
      n++;
    }
    not_done = dl_list_next(guard_list);
  }

  /* restore the saved position */
  dl_list_put_pos(guard_list, orig_posn);

  return( n );
}

/* verify the contents of guardian list of an authority area. Check
   for any duplicates. */
static int
verify_guardian_list(aa)
  auth_area_struct *aa;
{
  int  not_done;
  int  ret;
  char *guard_item;

  if (!aa) return FALSE;

  if (dl_list_empty(aa->guardian_list))
  {
    return TRUE;
  }

  not_done = dl_list_first(aa->guardian_list);
  while (not_done)
  {
    guard_item = dl_list_value(aa->guardian_list);

    if ((ret = examin_guardian_item(guard_item)))
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area guardian item '%s': %s",
        aa->name, guard_item, examin_error_string(ret));
      return FALSE;
    }
    if (count_guardian_entries(aa->guardian_list, guard_item) > 1)
    {
      log(L_LOG_ERR, CONFIG,
        "duplicate entries in the guardian list of '%s' authority area: %s",
        aa->name, guard_item);
      return FALSE;
    }

    not_done = dl_list_next(aa->guardian_list);
  }

  return TRUE;
}

/* verify the complete authority area. Verify path names, value ranges,
   schema, server and guardian lists etc.. Take into consideration
   the authority area type (primary/secondary). */
static int
verify_auth_area(aa)
  auth_area_struct *aa;
{
  int  ret;
  char directory[MAX_FILE];

  /* verify schema, soa, class list, attribute lists */
  if (!aa)
  {
    log(L_LOG_ERR, CONFIG, "authority area not defined - internal error");
    return FALSE;
  }

  if (!check_aa_syntax(aa->name, directory))
  {
    log(L_LOG_ERR, CONFIG, "'%s' is not a valid authority area syntax",
        aa->name );
    return FALSE;
  }
  if (aa->type == AUTH_AREA_PRIMARY)
  {
    if ((ret = examin_aa_data_dir(aa->data_dir)))
    {
      log(L_LOG_ERR, CONFIG,
          "invalid '%s' authority area data directory '%s': %s",
          aa->name, SAFE_STR(aa->data_dir, ""), examin_error_string(ret));
      return FALSE;
    }
    if ((ret = examin_aa_schema_file(aa->schema_file)))
    {
      log(L_LOG_ERR, CONFIG,
          "invalid '%s' authority area schema file name '%s': %s",
          aa->name, SAFE_STR(aa->schema_file, ""), examin_error_string(ret));
      return FALSE;
    }
    if ((ret = examin_aa_soa_file(aa->soa_file)))
    {
      log(L_LOG_ERR, CONFIG,
          "invalid '%s' authority area soa file name '%s': %s",
          aa->name, SAFE_STR(aa->soa_file, ""), examin_error_string(ret));
      return FALSE;
    }
    if ((ret = examin_primary_server_str(aa->primary_server)))
    {
      log(L_LOG_ERR, CONFIG,
          "invalid '%s' authority area primary server '%s': %s",
          aa->name, SAFE_STR(aa->primary_server, ""),
          examin_error_string(ret));
      return FALSE;
    }
    if ((ret = examin_aa_hostmaster_str(aa->hostmaster)))
    {
      log(L_LOG_ERR, CONFIG,
          "invalid '%s' authority area hostmaster address '%s': %s",
          aa->name, SAFE_STR(aa->hostmaster, ""), examin_error_string(ret));
      return FALSE;
    }
    if ((ret = examin_serial_num(aa->serial_no)))
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area serial number '%s': %s",
          aa->name, SAFE_STR(aa->serial_no, ""), examin_error_string(ret));
      return FALSE;
    }
    if (aa->xfer_arg && *aa->xfer_arg &&
        (ret = examin_aa_xfer_arg(aa->xfer_arg)))
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area xfer arguments '%s': %s",
          aa->name, SAFE_STR(aa->xfer_arg, ""), examin_error_string(ret));
      return FALSE;
    }
    if (aa->refresh_interval < 1)
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area Refresh-Interval '%d': %s",
          aa->name, aa->refresh_interval, examin_error_string(ERW_NUMVAL));
      return FALSE;
    }
    if (aa->increment_interval < 1)
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area Increment-Interval '%d': %s",
          aa->name, aa->increment_interval, examin_error_string(ERW_NUMVAL));
      return FALSE;
    }
    if (aa->retry_interval < 1)
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area Retry-Interval '%d': %s",
          aa->name, aa->retry_interval, examin_error_string(ERW_NUMVAL));
      return FALSE;
    }
    if (aa->time_to_live < 1)
    {
      log(L_LOG_ERR, CONFIG,
        "invalid '%s' authority area Time-To-Live '%d': %s",
          aa->name, aa->time_to_live, examin_error_string(ERW_NUMVAL));
      return FALSE;
    }
  }

  if (!verify_server_list(aa, AA_MASTER))
  {
    log(L_LOG_ERR, CONFIG,
      "'%s' authority area master server list is not valid",
        aa->name);
    return FALSE;
  }

  if (!verify_server_list(aa, AA_SLAVE))
  {
    log(L_LOG_ERR, CONFIG,
      "'%s' authority area slave server list is not valid",
      aa->name);
    return FALSE;
  }

  if (!verify_guardian_list(aa))
  {
    log(L_LOG_ERR, CONFIG,
      "'%s' authority area guardian list is not valid",
      aa->name);
    return FALSE;
  }

  /* check schema for only primary auth area */
  if (aa->type == AUTH_AREA_PRIMARY)
  {
    if (!verify_schema(aa))
    {
      return FALSE;
    }
  }

  return TRUE;
}

/* ------------------ Public Functions ----------------- */

int
add_auth_area_guardian(aa, id_str)
  auth_area_struct *aa;
  char             *id_str;
{
  if (!aa || !STR_EXISTS(id_str))
  {
    return FALSE;
  }

  if (!aa->guardian_list)
  {
    aa->guardian_list = xcalloc(1, sizeof(*(aa->guardian_list)));
    dl_list_default(aa->guardian_list, TRUE, simple_destroy_data);
  }

  dl_list_append(aa->guardian_list, xstrdup(id_str));

  return TRUE;
}

/* translate_auth_area_type: given a string, translate it into one of
   the auth_area_type values */
auth_area_type
translate_auth_area_type(val)
  char  *val;
{
  if (!val) return AUTH_AREA_PRIMARY;

  if (STR_EQ(val, AA_SECONDARY) || STR_EQ(val, AA_SLAVE))
  {
    return AUTH_AREA_SECONDARY;
  }

  return AUTH_AREA_PRIMARY;
}

char *
translate_auth_area_type_str(val)
  auth_area_type    val;
{
  if (val == AUTH_AREA_PRIMARY)
  {
    return(AA_PRIMARY);
  }

  return(AA_SECONDARY);
}

/* check to see if it is a valid hostname with format:
 *  rs.internic.net  -OR- 198.41.0.21
 * return TRUE if it is valid (don't care its reachability)
 * else return FALSE;
 */
int
is_valid_hostname ( name)
  char   *name;
{
  char           *p;
  char           *op_str;
  static regexp  *host1_prog = NULL;
  static regexp  *host2_prog = NULL;
  int            status1     = FALSE;
  int            status2     = FALSE;

  if (NOT_STR_EXISTS(name)) return FALSE;

  trim(name);

  if (!host1_prog)
  {
    host1_prog = regcomp(DOMAIN_NAME_REGEXP);
  }
  if (!host2_prog)
  {
    host2_prog = regcomp(IP_ADDR_REGEXP);
  }

  status1 = regexec(host1_prog, name);
  if (!status1)
  {
    status2 = regexec(host2_prog, name);
    if (!status2)
    {
      return FALSE;
    }
  }

  op_str = NEW_STRING(name);

  /* hostname format: e.g. rs.internic.net  */
  if (status1)
  {
    p = strrchr(op_str, '.');
    p++;
    if (strlen(p) == 2 && is_country_code(p))
    {
      free(op_str);
      return TRUE;
    }
    if (strlen(p) == 3 && strSTR("EDU GOV COM NET ORG MIL INT", p))
    {
      free(op_str);
      return TRUE;
    }
    free(op_str);
    return FALSE;
  }

  if (status2)
  {
    int octs;
    int o1=0;
    int o2=0;
    int o3=0;
    int o4=0;

    /* read in octets */
    octs = sscanf(op_str, "%d.%d.%d.%d", &o1, &o2, &o3, &o4);

    if (octs <= 0) return FALSE;

    if (o1 < 0 || o1 > 255 || o2 < 0 || o2 > 255 ||
        o3 < 0 || o3 > 255 || o4 < 0 || o4 > 255)
    {
      free(op_str);
      return FALSE;
    }

    free(op_str);
    return TRUE;
  }

  return TRUE;
}

/* is_valid_port: check the port number. Return TRUE if a valid port
   number, else return FALSE */
int
is_valid_port(port)
  char  *port;
{
  char  *p;

  trim(port);

  for (p = port; *p; p++)
  {
    if (!isdigit(*p)) return FALSE;
  }

  if (atoi(port) <= 0)
  {
    return FALSE;
  }

  return TRUE;
}


/* check to see if  aa is already in aa_list */
int
is_duplicate_aa(aa, aa_list)
  auth_area_struct *aa;
  dl_list_type     *aa_list;
{
  int not_done;

  if (!aa) return FALSE;

  if (dl_list_empty(aa_list))
  {
    return FALSE;
  }

  not_done = dl_list_first(aa_list);
  while (not_done)
  {
    if (same_auth_area(dl_list_value(aa_list), aa))
    {
      return TRUE;
    }

    not_done = dl_list_next(aa_list);
  }

  return FALSE;
}


/* read_auth_areas: given an auth-area conf file name, read the
   configuration file (and associated configuration files.  Return
   FALSE if a fatal error was detected */
int
read_auth_areas(file)
  char *file;
{
  char              line[BUFSIZ];
  char              tag[MAX_TEMPLATE_DESC];
  char              datum[MAX_TEMPLATE_DESC];
  FILE             *fp                        = NULL;
  auth_area_struct *aa                        = NULL;
  int               content_flag              = FALSE;

  if ((fp = fopen(file, "r")) == NULL)
  {
    log(L_LOG_ERR, CONFIG, "cannot open file '%s': %s", file, strerror(errno));
    return FALSE;
  }

  /* free old auth area stuff, if necessary */
  if (auth_area_list)
  {
    log(L_LOG_DEBUG, CONFIG, "destroying pre-existing schema"); /* temp */
    destroy_auth_area_list();
    destroy_class_ref_list();
  }

  set_log_context(file, 0, -1);

  aa = xcalloc(1, sizeof(*aa));

  bzero(line, sizeof(line));

  while (readline(fp, line, BUFSIZ))
  {
    inc_log_context_line_num(1);

    /* commit to the list the record we just read */
    if (new_record(line))
    {
      if (!add_auth_area(aa))
      {
        destroy_auth_area_data(aa);
      }

      aa = xcalloc(1, sizeof(*aa));

      content_flag = FALSE;

      continue;
    }

    if (parse_line(line, tag, datum))
    {
      content_flag = TRUE; /* we have read and parsed something */

      if (STR_EQ(tag, AA_TYPE))
      {
        aa->type = translate_auth_area_type(datum);
      }
      else if (STR_EQ(tag, AA_NAME))
      {
        if (aa->name)
        {
          log(L_LOG_WARNING, CONFIG,
              "auth area name '%s' replaces previous value '%s' %s",
              datum, aa->name, file_context_str());
          free(aa->name);
        }
        aa->name = xstrdup(datum);
      }
      else if (STR_EQ(tag, AA_SCHEMA_FILE))
      {
        if (aa->schema_file)
        {
          log(L_LOG_WARNING, CONFIG,
        "auth area schema file '%s' (in '%s') replaces previous value '%s' %s",
              datum, SAFE_STR(aa->name, "unknown"), aa->schema_file,
              file_context_str());
          free(aa->schema_file);
        }

        if (!file_exists(datum))
        {
          if (aa->type == AUTH_AREA_PRIMARY)
          {
            log(L_LOG_ERR, CONFIG, "schema file '%s' unreadable: %s %s",
                datum, strerror(errno), file_context_str());
            destroy_auth_area_data(aa);
            fclose(fp);
            return FALSE;
          }
        }
        aa->schema_file = xstrdup(datum);
      }
      else if (STR_EQ(tag, AA_SOA_FILE))
      {
        if (aa->soa_file)
        {
          log(L_LOG_WARNING, CONFIG,
          "auth area soa file '%s' (in '%s') replaces previous value '%s' %s",
              datum, SAFE_STR(aa->name, "unknown"), aa->soa_file,
              file_context_str());
          free(aa->soa_file);
        }

        if (! file_exists(datum))
        {
          if (aa->type == AUTH_AREA_PRIMARY)
          {
            log(L_LOG_WARNING, CONFIG,
                "auth area soa file '%s' unreadable: %s %s",
                datum, strerror(errno), file_context_str());
            destroy_auth_area_data(aa);
            fclose(fp);
            return FALSE;
          }
        }

        aa->soa_file = xstrdup(datum);
      }
      else if (STR_EQ(tag, AA_DATA_DIR))
      {
        if (aa->data_dir)
        {
          log(L_LOG_WARNING, CONFIG,
           "auth area data dir '%s' (in '%s') replaces previous value '%s' %s",
              datum, SAFE_STR(aa->name, "unknown"), aa->data_dir,
              file_context_str());
          free(aa->data_dir);
        }

        if (!directory_exists(datum))
        {
          if (aa->type == AUTH_AREA_PRIMARY)
          {
            log(L_LOG_WARNING, CONFIG,
                "auth area data dir '%s' unreadable: %s %s",
                datum, strerror(errno), file_context_str());
            destroy_auth_area_data(aa);
            fclose(fp);
            return FALSE;
          }
        }

        aa->data_dir = xstrdup(datum);
      }
      else if (STR_EQ(tag, AA_MASTER))
      {
        if (!add_server(&(aa->master), datum))
        {
          destroy_auth_area_data(aa);
          fclose(fp);
          return FALSE;
        }
      }
      else if (STR_EQ(tag, AA_SLAVE))
      {
        if (!add_server(&(aa->slave), datum))
        {
          destroy_auth_area_data(aa);
          fclose(fp);
          return FALSE;
        }
      }
      else if (STR_EQ(tag, AA_XFER_ARG))
      {
        if (aa->xfer_arg)
        {
          log(L_LOG_WARNING, CONFIG,
              "auth area xfer arg '%s' replaces previous value '%s' %s",
              datum, aa->xfer_arg, file_context_str());
          free(aa->xfer_arg);
        }
        aa->xfer_arg = xstrdup(datum);
      }
      else if (STR_EQ(tag, AA_GUARDIAN_ARG))
      {
        add_auth_area_guardian(aa, datum);
      }
      else
      {
        log(L_LOG_WARNING, CONFIG,
            "tag '%s' in '%s' is unrecognized; ignoring %s",
            tag, aa->name, file_context_str());
      }
    } /* parse_line */

  } /* readline */

  /* commit last auth area; if there is no content, or the add fails,
     just free the data. */
  if (!content_flag || !add_auth_area(aa))
  {
    destroy_auth_area_data(aa);
  }

  fclose(fp);
  return TRUE;
}

/* add_auth_area: commit the auth area structure to the global list.
     Checks for illegal variable conditions first, and refuse to add
     structures that fail.  Returns TRUE on success, otherwise FALSE */
int
add_auth_area(aa)
  auth_area_struct  *aa;
{
  char               directory[MAX_FILE];
  log_context_struct local_context;

  if (!aa) return FALSE;

  /* instantiate the auth-area-list first, if necessary */
  if (!auth_area_list)
  {
    auth_area_list = xcalloc(1, sizeof(*auth_area_list));

    dl_list_default(auth_area_list, TRUE, destroy_auth_area_data);
  }

  /* check the aa for existance, syntax, etc .. first */

  if (!aa->name)
  {
    log(L_LOG_ERR, CONFIG, "authority area missing name %s",
        file_context_str());
    return FALSE;
  }

  /* check for a duplicate auth area */
  if (is_duplicate_aa(aa, auth_area_list))
  {
    log(L_LOG_ERR, CONFIG, "authority area '%s' already exists %s",
        aa->name, file_context_str());
    return FALSE;
  }

  if (!check_aa_syntax(aa->name, directory))
  {
    log(L_LOG_ERR, CONFIG, "'%s' is not a valid authority area syntax %s",
        aa->name, file_context_str());
    return FALSE;
  }

  if (!aa->data_dir)
  {
    if (aa->type == AUTH_AREA_PRIMARY)
    {
      log(L_LOG_ERR, CONFIG, "authority area '%s' missing data directory %s",
          aa->name, file_context_str());
      return FALSE;
    }
  }

  if (!aa->schema_file)
  {
    if (aa->type == AUTH_AREA_PRIMARY)
    {
      log(L_LOG_ERR, CONFIG, "authority area '%s' missing schema file %s",
          aa->name, file_context_str());
      return FALSE;
    }
  }

  if (!aa->soa_file)
  {
    if (aa->type == AUTH_AREA_PRIMARY)
    {
      log(L_LOG_ERR, CONFIG, "authority area '%s' missing soa file %s",
          aa->name, file_context_str());
      return FALSE;
    }
  }

  /* Non-primary auth-areas get to do this stuff later */
  if (aa->type == AUTH_AREA_PRIMARY)
  {
    save_log_context(&local_context);

    /* now actually read the SOA data */
    if (!read_soa_file(aa))
    {
      return FALSE;
    }

    aa->schema = xcalloc(1, sizeof(*(aa->schema)));

    log(L_LOG_DEBUG, CONFIG, "reading schema for auth-area '%s'", aa->name);

    if (!read_schema(aa))
    {
      return FALSE;
    }

    restore_log_context(&local_context);
  }


  dl_list_append(auth_area_list, aa);

  return TRUE;
}


/* check_aa_syntax(): check auth_area syntax, also create auth_area directory.
   Return TRUE if valid auth-area else FALSE */
int
check_aa_syntax (aa_name, directory)
  char *aa_name;
  char *directory;
{
  char      *p;
  char      *op_str;
  static    regexp *prog  = NULL;

  if (!aa_name) return FALSE;

  /* root server */
  if (!strcmp(aa_name, "."))
  {
     strcpy(directory, "root");
     return TRUE;
  }

  /* check:    domain format: a.1-b.biz
   *            -OR-
   *           network format: 1234.111/156
   */
  if (!prog)
  {
    prog = regcomp(AUTH_AREA_REGEXP);
  }

  if (!regexec(prog, aa_name))
  {
    return FALSE;
  }

  strip_leading(aa_name, '.');
  strip_trailing(aa_name, '.');

  op_str = NEW_STRING(aa_name);

  /* domain format: the second level has to be:
   *  country-code  -OR-  EDU, COM, GOV, NET, ORG
   */
  if (!(p = strrchr(op_str, '/')))
  {
    /* create directory */
    strcpy (directory, aa_name);

    /* grab second level */
    if ((p = strrchr(op_str, '.')))
    {
      p++;
    }
    else
    {
      p = op_str;
    }
    if (strlen(p) == 2 && is_country_code(p))
    {
      free(op_str);
      return TRUE;
    }
    if (strlen(p) == 3 && strSTR("EDU GOV COM NET ORG INT", p))
    {
      free(op_str);
      return TRUE;
    }

    free(op_str);
    return FALSE;
  }
  else
  /* network format:
   *    Convert the IP-network to bit stream (32-bit long integer),
   *    compare the trailing number of zeros with prefix-length.
   *        127.253.252.0/22    01111111111111011111110000000000
   *                                                  ^^^^^^^^^^
   *           len = 22                               32-len=10
   */
  {
    struct netinfo prefix;

    if ( ! get_network_prefix_and_len( op_str, &prefix ) )
    {
      free(op_str);
      return FALSE;
    }

    /* default directory name for network:  net-ipaddress-length */
    sprintf( directory, "net-%s-%d", natop( &prefix ), prefix.masklen );

    free(op_str);
    return TRUE;
  } /*   */

  free(op_str);
  return TRUE;
}


/* read_soa_file: given an auth-area structure, read the SOA file
   contained within, and fill out the record.  Returns FALSE if it
   discovered something wrong. */
int
read_soa_file(aa)
  auth_area_struct  *aa;
{
  char              line[MAX_LINE + 1];
  char              tag[MAX_TEMPLATE_DESC];
  char              value[MAX_TEMPLATE_DESC];
  FILE              *fp                        = NULL;

  fp = fopen(aa->soa_file, "r");
  if (!fp)
  {
    log(L_LOG_ERR, CONFIG,
        "cannot open soa file '%s' for auth-area '%s': %s",
        aa->soa_file, aa->name,
        strerror(errno));
    return FALSE;
  }

  /* free any possible old SOA data */
  destroy_soa_in_auth_area(aa);

  set_log_context(aa->soa_file, 1, -1);

  bzero(line, sizeof(*line));

  while (readline(fp, line, MAX_LINE))
  {
    if (parse_line(line, tag, value))
    {
      if (STR_EQ(tag, SOA_REFRESH_INTERVAL))
      {
        aa->refresh_interval = atol(value);
      }
      else if (STR_EQ(tag, SOA_INCREMENT_INTERVAL))
      {
        aa->increment_interval = atol(value);
      }
      else if (STR_EQ(tag, SOA_RETRY_INTERVAL))
      {
        aa->retry_interval = atol(value);
      }
      else if (STR_EQ(tag, SOA_TIME_TO_LIVE))
      {
        aa->time_to_live = atol(value);
      }
      else if (STR_EQ(tag, SOA_PRIMARY_SERVER))
      {
        if (aa->primary_server)
        {
          log(L_LOG_WARNING, CONFIG,
   "SOA primary server '%s' replaces previous value '%s' in auth-area '%s' %s",
              value, aa->primary_server, aa->name, file_context_str());
          free(aa->primary_server);
        }
        aa->primary_server = xstrdup(value);
      }
      else if (STR_EQ(tag, SOA_HOSTMASTER))
      {
        if (aa->hostmaster)
        {
          log(L_LOG_WARNING, CONFIG,
       "SOA hostmaster '%s' replaces previous value '%s' in auth-area '%s' %s",
              value, aa->hostmaster, aa->name, file_context_str());
          free(aa->hostmaster);
        }
        aa->hostmaster = NEW_STRING(value);
      }
      else if (STR_EQ(tag, SOA_SERIAL_NUMBER))
      {
        if (aa->serial_no)
        {
          log(L_LOG_WARNING, CONFIG,
    "SOA serial number '%s' replaces previous value '%s' in auth-area '%s' %s",
              value, aa->serial_no, aa->name, file_context_str());
          free(aa->serial_no);
        }
        aa->serial_no = NEW_STRING(value);
      }
      else
      {
        log(L_LOG_WARNING, CONFIG,
            "SOA tag '%s' unrecognized in auth-area '%s' %s",
            aa->name, file_context_str());
      }
    }   /* if parse_line */
  } /* while (readline) */

  fclose(fp);

  return( check_soa(aa) );
}

/* write_soa_file: given an auth-area structure, writes the SOA file
   contained within, and fill out the record.  Returns FALSE if it
   discovered something wrong. */
int
write_soa_file(aa)
  auth_area_struct  *aa;
{
  FILE *fp = NULL;
  int   lock_fd;

  /* first establish a file lock */
  if (!get_placeholder_lock(aa->soa_file, 5, &lock_fd))
  {
    log(L_LOG_ERR, CONFIG,
        "write_soa_file: can not establish lock for soa file %s: %s",
        aa->soa_file, strerror(errno));
    return FALSE;
  }

  /* now that we have the file locked, open the soa file, truncating it */

  if ((fp = fopen(aa->soa_file, "w")) == NULL)
  {
    log(L_LOG_ERR, CONFIG, "write_soa_file: can not open soa file %s: %s",
        aa->soa_file, strerror(errno));
    release_placeholder_lock(aa->soa_file, lock_fd);
    return FALSE;
  }

  fprintf(fp, "%s: %s\n", SOA_SERIAL_NUMBER, SAFE_STR(aa->serial_no, ""));
  fprintf(fp, "%s: %ld\n", SOA_REFRESH_INTERVAL, aa->refresh_interval);
  fprintf(fp, "%s: %ld\n", SOA_INCREMENT_INTERVAL, aa->increment_interval);
  fprintf(fp, "%s: %ld\n", SOA_RETRY_INTERVAL, aa->retry_interval);
  fprintf(fp, "%s: %ld\n", SOA_TIME_TO_LIVE, aa->time_to_live);
  fprintf(fp, "%s: %s\n", SOA_PRIMARY_SERVER,
          SAFE_STR(aa->primary_server, ""));
  fprintf(fp, "%s: %s\n", SOA_HOSTMASTER, SAFE_STR(aa->hostmaster, ""));

  fclose(fp);
  release_placeholder_lock(aa->soa_file, lock_fd);

  return TRUE;
}

/* destroy_soa_in_auth_area: destroy the soa related data in an auth_area */
int
destroy_soa_in_auth_area(aa)
  auth_area_struct *aa;
{
  if (!aa) return TRUE;

  if (aa->primary_server)
  {
    free(aa->primary_server);
    aa->primary_server = NULL;
  }

  if (aa->hostmaster)
  {
    free(aa->hostmaster);
    aa->hostmaster = NULL;
  }

  if (aa->serial_no)
  {
    free(aa->serial_no);
    aa->serial_no = NULL;
  }

  return TRUE;

}


int
add_server(srv_list_ptr, val)
  dl_list_type      **srv_list_ptr;
  char              *val;
{
  dl_list_type    *list         = *srv_list_ptr;
  char            name[BUFSIZ];
  char            port[BUFSIZ];
  char            addr[BUFSIZ];
  char           *p             = NULL;
  server_struct  *server        = NULL;
  int             ipaddr        = TRUE;
#ifdef HAVE_GETADDRINFO
  struct addrinfo hints, *res1;
  int             error;
#else
  struct hostent *hp            = NULL;
  struct in_addr *ptr           = NULL;
#endif

  bzero(name, sizeof(name));
  bzero(port, sizeof(port));

  /* actually read the server line */
  sscanf(val, "%s %s", name, port);

  trim(name);
  trim(port);

  server = xcalloc(1, sizeof(*server));

  if (NOT_STR_EXISTS(name))
  {
    log(L_LOG_ERR, CONFIG, "add_server: null data detected");
    return FALSE;
  }

  server->name = xstrdup(name);

  /* look for dotted quad style IP addresses */
  for (p = name; *p; p++)
  {
#ifdef HAVE_IPV6
    if ( !isxdigit(*p) && *p != ':' && *p != '.'  )
#else
    if (!isdigit(*p) && *p != '.')
#endif
    {
      ipaddr = FALSE;
      break;
    }
  }

  if (ipaddr)
  {
    server->addr = xstrdup(name);
    /* use ipaddr as server->name */
  }
  else
  {


#ifdef HAVE_GETADDRINFO
      memset( &hints, 0, sizeof(hints) );
      hints.ai_family = PF_UNSPEC;
      hints.ai_socktype = SOCK_STREAM;

      error = getaddrinfo( name, "0", &hints, &res1 );
      if(error) {
         log(L_LOG_ERR, CONFIG, "add_server: %s", gai_strerror(error) );
         return FALSE;
      }

      bzero(addr, sizeof(addr));
      inet_ntop( res1->ai_family, res1->ai_addr, addr, sizeof addr );
      freeaddrinfo( res1 );
#ifdef DEBUG
      fprintf( stderr, "SERVER_ADDR = %s\n", addr );
#endif /* DEBUG */

#else  /* ! HAVE_GETADDRINFO */
    if ((hp = gethostbyname(name)) == NULL)
    {
      log(L_LOG_ERR, CONFIG, "add_server: could not resolve hostname '%s': %s",
          name, strerror(errno));
      return FALSE;
    }

    ptr = (struct in_addr *) (hp->h_addr);
    bzero(addr, sizeof(addr));
    sprintf(addr, "%s", inet_ntoa(*ptr));
#endif  /* HAVE_GETADDRINFO */

    server->addr = xstrdup(addr);
  }

  server->port = atoi(port);
  /* check port */
  if (server->port <= 0)
  {
    log(L_LOG_ERR, CONFIG, "add_server: incorrect port '%s'",
        port);
    return FALSE;
  }

  if (!list)
  {
    list = xcalloc(1, sizeof(*list));
  }

  if (dl_list_empty(list))
  {
    dl_list_default(list, TRUE, destroy_server_data);
  }

  dl_list_append(list, server);

  *srv_list_ptr = list;

  return TRUE;
}

void
display_auth_area(aa)
  auth_area_struct  *aa;
{
  if (!aa)
  {
    return;
  }

  printf("auth-area:            %s\n", SAFE_STR_NONE(aa->name));
  printf("type:                 %s\n", translate_auth_area_type_str(aa->type));
  printf("data-dir:             %s\n", SAFE_STR_NONE(aa->data_dir));
  printf("schema-file:          %s\n", SAFE_STR_NONE(aa->schema_file));
  printf("soa-file:             %s\n", SAFE_STR_NONE(aa->soa_file));
  printf("primary-server:       %s\n", SAFE_STR_NONE(aa->primary_server));
  printf("hostmaster:           %s\n", SAFE_STR_NONE(aa->hostmaster));
  printf("serial-number:        %s\n", SAFE_STR_NONE(aa->serial_no));
  printf("refresh-interval:     %ld\n", aa->refresh_interval);
  printf("increment-interval:   %ld\n", aa->increment_interval);
  printf("retry-interval:       %ld\n", aa->retry_interval);
  printf("time-to-live:         %ld\n", aa->time_to_live);
  printf("xfer-time:            %ld\n", aa->xfer_time);

  display_schema(aa->schema);
}

void
display_all_auth_areas()
{
  int   not_done;

  if ( dl_list_empty(auth_area_list) )
  {
    return ;
  }

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    display_auth_area(dl_list_value(auth_area_list));

    not_done = dl_list_next(auth_area_list);
    if (not_done)
    {
      printf("--------------------\n");
    }
  }
}


int
destroy_server_data(server)
  server_struct *server;
{
  if (!server) return TRUE;

  if (server->name)
  {
    free(server->name);
  }

  if (server->addr)
  {
    free(server->addr);
  }

  free(server);

  return TRUE;
}

void
destroy_auth_area_list()
{
  dl_list_destroy(auth_area_list);
  auth_area_list = NULL;
}

int
destroy_auth_area_data(aa)
  auth_area_struct  *aa;
{
  if (!aa) return TRUE;

  if (aa->name)
  {
    free(aa->name);
  }

  if (aa->data_dir)
  {
    free(aa->data_dir);
  }

  if (aa->schema_file)
  {
    free(aa->schema_file);
  }

  if (aa->soa_file)
  {
    free(aa->soa_file);
  }

  if (aa->primary_server)
  {
    free(aa->primary_server);
  }

  if (aa->hostmaster)
  {
    free(aa->hostmaster);
  }

  if (aa->serial_no)
  {
    free(aa->serial_no);
  }

  if (aa->xfer_arg)
  {
    free(aa->xfer_arg);
  }

  destroy_schema_data(aa->schema);

  dl_list_destroy(aa->master);
  dl_list_destroy(aa->slave);

  dl_list_destroy(aa->guardian_list);

  free(aa);

  return TRUE;
}



dl_list_type *
get_auth_area_list()
{
  return(auth_area_list);
}


auth_area_struct *
find_auth_area_by_name(name)
  char *name;
{
  auth_area_struct  *auth_area;
  int               not_done;

  if (!auth_area_list) return NULL;

  if (NOT_STR_EXISTS(name)) return NULL;

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    auth_area = dl_list_value(auth_area_list);
    if (STR_EQ(auth_area->name, name))
    {
      return(auth_area);
    }

    not_done = dl_list_next(auth_area_list);
  }

  return NULL;
}


attribute_ref_struct *
find_truly_global_attr_by_name(name)
  char *name;
{
  dl_list_type         *auth_area_list;
  dl_list_type         *attr_ref_list;
  auth_area_struct     *auth_area;
  attribute_ref_struct *ar;
  int                   not_done;

  if (NOT_STR_EXISTS(name)) return NULL;

  auth_area_list = get_auth_area_list();
  if (!auth_area_list)
  {
    return NULL;
  }

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    auth_area = dl_list_value(auth_area_list);

    if (!auth_area || !auth_area->schema)
    {
      not_done = dl_list_next(auth_area_list);
      continue;
    }

    attr_ref_list = &(auth_area->schema->attribute_ref_list);
    ar = find_global_attr_by_name(attr_ref_list, name);
    if (ar)
    {
      return(ar);
    }

    not_done = dl_list_next(auth_area_list);
  }

  return NULL;
}

/* check_root_referral: check the syntax of punt file(rwhois.root)
 * format: <hostname>:<port>:<protocol>
 * return TRUE if not error, else return FALSE
 */
int
check_root_referral(file)
  char *file;
{
  FILE         *fp                = NULL;
  char         *pound             = NULL;
  char         line[BUFSIZ];
  char         hostname[BUFSIZ];
  char         port[BUFSIZ];
  char         proto[BUFSIZ];
  static regexp *old_ref_exp      = NULL;
  static regexp *url_exp          = NULL;

  if ((fp = fopen(file, "r")) == NULL)
  {
    log(L_LOG_ERR, CONFIG,
        "cannot open root_referral_file '%s': %s", file, strerror(errno));
    return FALSE;
  }

  if (!old_ref_exp || !url_exp)
  {
    /* FIXME: regexps should be #defined */
    old_ref_exp = regcomp(OLD_REF_REGEXP);
    url_exp = regcomp(URL_REGEXP);
  }

  bzero(line, sizeof(line));

  while (readline(fp, line, BUFSIZ))
  {
    /* skip comment line and empty line */
    if (NOT_STR_EXISTS(line))
    {
      continue;
    }

    pound = skip_whitespace(line);
    if (*pound == '#')
    {
      continue;
    }

    trim(line);

    if (NOT_STR_EXISTS(line))
    {
      continue;
    }

    bzero(hostname, sizeof(hostname));
    bzero(port, sizeof(port));
    bzero(proto, sizeof(proto));

    if (regexec(old_ref_exp, line))
    {
      regncpy(hostname, old_ref_exp, 1, sizeof(hostname));
      regncpy(port, old_ref_exp, 2, sizeof(port));
      regncpy(proto, old_ref_exp, 3, sizeof(proto));
    }
    else if (regexec(url_exp, line))
    {
      regncpy(proto, url_exp, 1, sizeof(proto));
      regncpy(hostname, url_exp, 2, sizeof(hostname));
      regncpy(port, url_exp, 3, sizeof(port));
    }
    else
    {
      log(L_LOG_ERR, CONFIG,
          "root referral server record has invalid format: %s", line);
      fclose(fp);
      return FALSE;
    }

    if (!is_valid_hostname(hostname))
    {
      log(L_LOG_ERR, CONFIG,
          "root referral server has invalid hostname: %s",
          hostname);
      fclose(fp);
      return FALSE;
    }
    if (STR_EXISTS(port) && !is_valid_port(port))
    {
      log(L_LOG_ERR, CONFIG,
          "root referral server '%s' has invalid port: %s",
          hostname, port);
      fclose(fp);
      return FALSE;
    }

    /* check protocol, starting with "rwhois" ? */
    if (!STRN_EQ(proto, "rwhois", 6))
    {
      log(L_LOG_ERR, CONFIG, "root referral has incorrect proto: %s", proto);
      fclose(fp);
      return FALSE;
    }

  } /* while */

  fclose(fp);
  return TRUE;
}

/* check to see if it is a country-code */
int
is_country_code(p)
  char  *p;
{
  /* FIXME: need the country-code database */
  return TRUE;
}

char *
get_default_aa_directory(aa)
  auth_area_struct *aa;
{
  struct netinfo prefix;
  char           buf[MAX_LINE];

  if ( get_network_prefix_and_len( aa->name, &prefix ) ) {
    sprintf( buf, "net-%s-%d", natop( &prefix ), prefix.masklen );
    return(xstrdup(buf));
  }
  else
  {
    return(xstrdup(aa->name));
  }
}

char *
get_aa_schema_directory(aa)
  auth_area_struct *aa;
{
  char  dir[MAX_FILE];
  char  file[MAX_FILE];

  bzero(dir, sizeof(dir));
  bzero(file, sizeof(file));

  if (!aa || NOT_STR_EXISTS(aa->schema_file))
  {
    return NULL;
  }

  split_path(aa->schema_file, dir, file);

  if (STR_EXISTS(dir))
  {
    return(xstrdup(dir));
  }

  return NULL;
}

/* writes all authority areas in the list to disk. Creates the top level
   authority area file while going through the auth-area list. Saves
   any directory and file names created into paths_list structure. */
int
write_all_auth_areas(file, suffix, paths_list)
  char         *file;
  char         *suffix;
  dl_list_type *paths_list;
{
  int              not_done;
  auth_area_struct *aa;
  FILE             *fp;
  char             new_file[MAX_FILE];

  if (!file || !*file || !paths_list) return FALSE;

  bzero(new_file, sizeof(new_file));
  strncpy(new_file, file, sizeof(new_file)-1);
  strncat(new_file, suffix, sizeof(new_file)-1);

  if ((fp = open_file_to_write(new_file, 60, paths_list)) == NULL)
  {
    log(L_LOG_ERR, CONFIG,
        "could not create rwhois server auth-area file '%s': %s",
        new_file, strerror(errno));
    return FALSE;
  }

  not_done = dl_list_first(auth_area_list);

  while (not_done)
  {
    aa = dl_list_value(auth_area_list);

    /* write the top level auth_area file contents */
    /* required */
    fprintf(fp, "%s: %s\n", AA_TYPE, translate_auth_area_type_str(aa->type));
    fprintf(fp, "%s: %s\n", AA_NAME, SAFE_STR(aa->name, ""));
    /* optional */
    if (aa->data_dir)
    {
      fprintf(fp, "%s: %s\n", AA_DATA_DIR, SAFE_STR(aa->data_dir, ""));
    }
    if (aa->schema_file)
    {
      fprintf(fp, "%s: %s\n", AA_SCHEMA_FILE, SAFE_STR(aa->schema_file, ""));
    }
    if (aa->soa_file)
    {
      fprintf(fp, "%s: %s\n", AA_SOA_FILE, SAFE_STR(aa->soa_file, ""));
    }
    if (aa->xfer_arg)
    {
      fprintf(fp, "%s: %s\n", AA_XFER_ARG, SAFE_STR(aa->xfer_arg, ""));
    }

    /* optional */
    write_server_list(fp, AA_MASTER, aa->master);
    write_server_list(fp, AA_SLAVE, aa->slave);
    write_guardian_list(fp, aa->guardian_list);

    /* write inidividual authority area */
    if (!write_auth_area(suffix, aa, paths_list))
    {
      log(L_LOG_ERR, CONFIG, "error in writing auth-area '%s'",
          SAFE_STR(aa->name, ""));
      release_file_lock(new_file, fp);
      dl_list_append(paths_list, xstrdup(new_file));
      return FALSE;
    }

    /* go to the next auth-area information */
    not_done = dl_list_next(auth_area_list);
    if (not_done)
    {
      fprintf(fp, "-----\n");
    }
  }

  release_file_lock(new_file, fp);

  dl_list_append(paths_list, xstrdup(new_file));

  return TRUE;
}

/* creates a new authority area, uses defaults where somethings are not
   defined by the user. And finally appends the created authority area
   to the list. */
int
create_auth_area(aa)
  auth_area_struct *aa;
{
  char             aa_dir[MAX_FILE];
  char             buffer[MAX_FILE];
  char             portstr[BUFSIZ];
  auth_area_struct *newaa;

  /* check if all essentials are initialized */
  if ( !aa || !aa->name || !*(aa->name) ) return FALSE;

  if (!check_aa_syntax(aa->name, aa_dir))
  {
    log(L_LOG_ERR, CONFIG, "'%s' is not a valid authority area name syntax",
        aa->name);
    return FALSE;
  }
  if (is_duplicate_aa(aa, auth_area_list))
  {
    log(L_LOG_ERR, CONFIG, "authority area '%s' already exists",
        aa->name);
    return FALSE;
  }

  newaa = xcalloc(1, sizeof(*newaa));

  newaa->name = xstrdup(aa->name);

  if (aa->serial_no && *(aa->serial_no))
  {
    newaa->serial_no = xstrdup(aa->serial_no);
  }
  else
  {
    newaa->serial_no = xstrdup(make_timestamp());
  }

  if (aa->schema_file && *(aa->schema_file))
  {
    newaa->schema_file = xstrdup(aa->schema_file);
  }
  else
  {
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa_dir, sizeof(buffer)-1);
    strncat(buffer, "/", sizeof(buffer)-1);
    strncat(buffer, DEFAULT_SCHEMA_FILE, sizeof(buffer)-1);
    newaa->schema_file = xstrdup(buffer);
  }

  if (aa->soa_file && *(aa->soa_file))
  {
    newaa->soa_file = xstrdup(aa->soa_file);
  }
  else
  {
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa_dir, sizeof(buffer)-1);
    strncat(buffer, "/", sizeof(buffer)-1);
    strncat(buffer, DEFAULT_SOA_FILE, sizeof(buffer)-1);
    newaa->soa_file = xstrdup(buffer);
  }
  if (aa->data_dir && *(aa->data_dir))
  {
    newaa->data_dir = xstrdup(aa->data_dir);
  }
  else
  {
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa_dir, sizeof(buffer)-1);
    strncat(buffer, "/", sizeof(buffer)-1);
    strncat(buffer, DEFAULT_DATA_DIR, sizeof(buffer)-1);
    newaa->data_dir = xstrdup(buffer);
  }
  if (aa->type == AUTH_AREA_PRIMARY)
  {
    newaa->type = AUTH_AREA_PRIMARY;
  }
  else
  {
    newaa->type = AUTH_AREA_SECONDARY;
  }

  if (aa->refresh_interval > 0)
  {
    newaa->refresh_interval = aa->refresh_interval;
  }
  else
  {
    newaa->refresh_interval = DEFAULT_REFRESH_INTERVAL;
  }
  if (aa->increment_interval > 0)
  {
    newaa->increment_interval = aa->increment_interval;
  }
  else
  {
    newaa->increment_interval = DEFAULT_INCREMENT_INTERVAL;
  }
  if (aa->retry_interval > 0)
  {
    newaa->retry_interval = aa->retry_interval;
  }
  else
  {
    newaa->retry_interval = DEFAULT_RETRY_INTERVAL;
  }
  if (aa->time_to_live > 0)
  {
    newaa->time_to_live = aa->time_to_live;
  }
  else
  {
    newaa->time_to_live = DEFAULT_TIME_TO_LIVE;
  }
  if ( aa->primary_server && *(aa->primary_server) )
  {
    newaa->primary_server = xstrdup( aa->primary_server );
  }
  else
  {
    sprintf(portstr, "%d", DEFAULT_PORT);
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, sys_gethostname(), sizeof(buffer)-1);
    strncat(buffer, ":", sizeof(buffer)-1);
    strncat(buffer, portstr, sizeof(buffer)-1);
    newaa->primary_server = xstrdup( buffer );
  }
  if ( aa->hostmaster && *(aa->hostmaster) )
  {
    newaa->hostmaster = xstrdup( aa->hostmaster );
  }
  else
  {
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, DEFAULT_HOSTMASTER, sizeof(buffer)-1);
    strncat(buffer, "@", sizeof(buffer)-1);
    strncat(buffer, sys_gethostname(), sizeof(buffer)-1);
    newaa->hostmaster = xstrdup( buffer );
  }

  /* Non-primary auth-areas get to do this stuff later */
  if (newaa->type == AUTH_AREA_PRIMARY)
  {
    newaa->schema = xcalloc(1, sizeof(*(aa->schema)));
  }

  /* commit authority area */
  if (!auth_area_list)
  {
    auth_area_list = xcalloc(1, sizeof(*auth_area_list));

    dl_list_default(auth_area_list, TRUE, destroy_auth_area_data);
  }

  dl_list_append(auth_area_list, newaa);

  return TRUE;
}

/* deletes an authority area (by name) from the configuration */
int
delete_auth_area(name)
  char *name;
{
  if (!find_auth_area_by_name(name))
  {
    log(L_LOG_ERR, CONFIG,
        "'%s' authority area does not exist", name);
    return FALSE;
  }
  if (!dl_list_delete(auth_area_list))
  {
    log(L_LOG_ERR, CONFIG,
        "'%s' authority area could not be deleted", name);
    return FALSE;
  }
  return TRUE;
}

/* examine the format of email address string. Currently excuses the
   hostname to have an non-dns hostname. Returns non-zero value if
   some error was found. */
int
examin_email_address(addr)
  char *addr;
{
  static regexp  *email_exp = NULL;

  if (!addr) return ERW_NDEF;
  if (!*addr) return ERW_EMTYSTR;

  if (!email_exp)
  {
    email_exp = regcomp(EMAIL_REGEXP);
  }
  if (!regexec(email_exp, addr))
  {
    log(L_LOG_WARNING, CONFIG,
      "email address '%s': %s", addr,
      examin_error_string(ERW_FMTMAIL));
  }

  return( 0 );
}

/* examine the format of x-fer arguments. Returns non-zero value on
   failure. */
int
examin_aa_xfer_arg(path)
  char *path;
{
  return ( 0 );
}

/* examine the validity of authority area directory. Returns non-zero
   value on failure. */
int
examin_aa_data_dir(path)
  char *path;
{
  int ret;

  if (NOT_STR_EXISTS(path)) return ERW_EMTYSTR;
  if ((ret = examin_directory_name(path))) return( ret );
  if (!path_under_root_dir(path, get_root_dir())) return ERW_UNDROOT;

  return( 0 );
}

/* examine the validity of authority area schema file name. Returns
   non-zero value on failure. */
int
examin_aa_schema_file(path)
  char *path;
{
  int ret;

  if (NOT_STR_EXISTS(path)) return ERW_EMTYSTR;
  if ((ret = examin_file_name(path))) return( ret );
  if (!path_under_root_dir(path, get_root_dir())) return ERW_UNDROOT;

  return( 0 );
}

/* examine the validity of authority area soa file name. Returns non-zero
   value on failure. */
int
examin_aa_soa_file(path)
  char *path;
{
  int ret;

  if (NOT_STR_EXISTS(path)) return ERW_EMTYSTR;
  if ((ret = examin_file_name(path))) return( ret );
  if (!path_under_root_dir(path, get_root_dir())) return ERW_UNDROOT;

  return( 0 );
}

/* examine the validity of authority area hostmaster e-mail address.
   Returns non-zero value on failure. */
int
examin_aa_hostmaster_str(contact)
  char *contact;
{
  int ret;

  if (NOT_STR_EXISTS(contact)) return ERW_EMTYSTR;
  if ((ret = examin_email_address(contact))) return( ret );

  return( 0 );
}

/* examine the vailidity of authority area serial number. Makes sure
   it is a number string. Returns non-zero value on failure. */
int
examin_serial_num(num)
  char *num;
{
  int ret;

  if ((ret = examin_timestamp(num))) return( ret );

  return( 0 );
}

/* examine the validity of guardian item string. Returns non-zero value
   on failure. */
int
examin_guardian_item(guard_str)
  char *guard_str;
{
  if (NOT_STR_EXISTS(guard_str)) return ERW_EMTYSTR;

  return( 0 );
}


/* examins the validity of a host name. It logs a warning if the hostname
   is not a complete dns host name. Returns non-zero value on
   error. */
int
examin_hostname(name)
  char *name;
{
  if (NOT_STR_EXISTS(name)) return ERW_EMTYSTR;

  if (strchr(name, '.') == NULL)
  {
    log(L_LOG_WARNING, CONFIG,
        "'%s' is not a complete dns host name", name);
    return( 0 );
  }
  else if (!is_valid_hostname(name))
  {
    return ERW_DNSSTR;
  }
  return( 0 );
}

/* examins the validity of a port number string. Returns non-zero
   value on error. */
int
examin_port_str(portstr)
  char *portstr;
{
  if (NOT_STR_EXISTS(portstr)) return ERW_EMTYSTR;
  if (!is_number_str(portstr)) return ERW_NUMSTR;
  if (atoi(portstr) < 1) return ERW_NUMVAL;

  return( 0 );
}

/* examine the validity of authority area primary server string. Returns
   non-zero value if failed. */
int
examin_primary_server_str(server)
  char *server;
{
  char hostname[BUFSIZ];
  char port[BUFSIZ];
  int argc;
  char **argv;
  int ret;

  if (NOT_STR_EXISTS(server)) return ERW_EMTYSTR;

  /* extract host and port number */
  if (!split_list(server, ':', 2, &argc, &argv)) return ERW_FMTSERV;
  if (argc != 2)
  {
    free_arg_list(argv);
    return ERW_FMTSERV;
  }

  bzero(hostname, sizeof(hostname));
  strncpy(hostname, argv[0], sizeof(hostname)-1);

  bzero(port, sizeof(port));
  strncpy(port, argv[1], sizeof(port)-1);

  free_arg_list(argv);

  if ((ret = examin_hostname(hostname))) return ret;

  if ((ret = examin_port_str(port))) return ret;

  return( 0 );
}

/* examine the validity of master or slave server string. Returns
   a non-zero value if failed. Calls functions to examine hostname and
   port number string separately. */
int
examin_server_str(server)
  char *server;
{
  int             ret;
  char            name[BUFSIZ];
  char            port[BUFSIZ];

  bzero(name, sizeof(name));
  bzero(port, sizeof(port));

  /* actually read the server line */
  sscanf(server, "%s %s", name, port);

  trim(name);
  trim(port);

  if ((ret = examin_hostname(name))) return ret;

  if ((ret = examin_port_str(port))) return ret;

  return( 0 );
}


/* verify all authority area definitions in the server configuration. */
int
verify_all_auth_areas()
{
  int              not_done;
  auth_area_struct *aa;

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    aa = dl_list_value(auth_area_list);

    if (!verify_auth_area(aa))
    {
      return FALSE;
    }

    not_done = dl_list_next(auth_area_list);
  }

  return TRUE;
}

/* Adds path names in the authority area structure to paths_list if not
   already in the list. Logs an error if a path name is already used in
   the configuration. */
int
verify_all_auth_area_paths(paths_list)
  dl_list_type *paths_list;
{
  int              ret = 0;
  char             buffer[MAX_LINE];
  auth_area_struct *aa;
  int              not_done;

  if (!paths_list) return( 1 );

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    aa = dl_list_value(auth_area_list);

    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa->name, sizeof(buffer)-1);
    strncat(buffer, ":", sizeof(buffer)-1);
    strncat(buffer, AA_DATA_DIR, sizeof(buffer)-1);
    ret += dup_config_path_name(paths_list, aa->data_dir,
                                buffer);
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa->name, sizeof(buffer)-1);
    strncat(buffer, ":", sizeof(buffer)-1);
    strncat(buffer, AA_SCHEMA_FILE, sizeof(buffer)-1);
    ret += dup_config_path_name(paths_list, aa->schema_file,
                                buffer);
    bzero(buffer, sizeof(buffer));
    strncpy(buffer, aa->name, sizeof(buffer)-1);
    strncat(buffer, ":", sizeof(buffer)-1);
    strncat(buffer, AA_SOA_FILE, sizeof(buffer)-1);
    ret += dup_config_path_name(paths_list, aa->soa_file,
                                buffer);

    ret += verify_all_class_paths(paths_list, aa);

    not_done = dl_list_next(auth_area_list);
  }

  return( ret );
}

/* verifies to make sure none of the parse programs defined for the
   classes are used as configuration path names (contents of
   paths_list). */
int
verify_aa_parse_progs(paths_list)
  dl_list_type *paths_list;
{
  int              ret = 0;
  auth_area_struct *aa;
  int              not_done;

  if (!paths_list) return( 1 );

  not_done = dl_list_first(auth_area_list);
  while (not_done)
  {
    aa = dl_list_value(auth_area_list);

    ret += verify_class_parse_progs(paths_list, aa);

    not_done = dl_list_next(auth_area_list);
  }

  return( ret );
}
