Miscellaneous minor code cleanup for MRW's review comments
[trust_router.git] / common / tr_config.c
index d527ede..2ddda12 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, JANET(UK)
+ * Copyright (c) 2012-2018, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include <talloc.h>
 
 #include <tr_cfgwatch.h>
+#include <tr_comm.h>
 #include <tr_config.h>
+#include <tr_gss_names.h>
 #include <tr_debug.h>
 #include <tr_filter.h>
 #include <trust_router/tr_constraint.h>
 #include <tr_idp.h>
 #include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
 
 void tr_print_config (TR_CFG *cfg) {
   tr_notice("tr_print_config: Logging running trust router configuration.");
-  tr_print_comms(cfg->comms);
+  tr_print_comms(cfg->ctable);
 }
 
-void tr_print_comms (TR_COMM *comm_list) {
+void tr_print_comms (TR_COMM_TABLE *ctab)
+{
   TR_COMM *comm = NULL;
 
-  for (comm = comm_list; NULL != comm; comm = comm->next) {
+  for (comm = ctab->comms; NULL != comm; comm = comm->next) {
     tr_notice("tr_print_config: Community %s:", comm->id->buf);
 
     tr_notice("tr_print_config:  - Member IdPs:");
-    tr_print_comm_idps(comm->idp_realms);
+    tr_print_comm_idps(ctab, comm);
 
     tr_notice("tr_print_config:  - Member RPs:");
-    tr_print_comm_rps(comm->rp_realms);
+    tr_print_comm_rps(ctab, comm);
   }
 }
 
-void tr_print_comm_idps (TR_IDP_REALM *idp_list) {
+void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_ITER *iter=NULL;
   TR_IDP_REALM *idp = NULL;
+  char *s=NULL;
 
-  for (idp = idp_list; NULL != idp; idp = idp->comm_next) {
-    tr_notice("tr_print_config:    - @%s", idp->realm_id->buf);
+  iter=tr_comm_iter_new(tmp_ctx);
+  if (iter==NULL) {
+    tr_notice("tr_print_config: unable to allocate IdP iterator.");
+    talloc_free(tmp_ctx);
+    return;
   }
+  
+  for (idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
+       NULL!=idp;
+       idp=tr_idp_realm_iter_next(iter)) {
+    s=tr_idp_realm_to_str(tmp_ctx, idp);
+    if (s!=NULL)
+      tr_notice("tr_print_config:    - @%s", s);
+    else
+      tr_notice("tr_print_config: unable to allocate IdP output string.");
+  }
+  talloc_free(tmp_ctx);
 }
 
-void tr_print_comm_rps(TR_RP_REALM *rp_list) {
+void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_ITER *iter=NULL;
   TR_RP_REALM *rp = NULL;
+  char *s=NULL;
 
-  for (rp = rp_list; NULL != rp; rp = rp->next) {
-    tr_notice("tr_print_config:    - %s", rp->realm_name->buf);
+  iter=tr_comm_iter_new(tmp_ctx);
+  if (iter==NULL) {
+    tr_notice("tr_print_config: unable to allocate RP iterator.");
+    talloc_free(tmp_ctx);
+    return;
   }
+  
+  for (rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
+       NULL!=rp;
+       rp=tr_rp_realm_iter_next(iter)) {
+    s=tr_rp_realm_to_str(tmp_ctx, rp);
+    if (s!=NULL)
+      tr_notice("tr_print_config:    - @%s", s);
+    else
+      tr_notice("tr_print_config: unable to allocate RP output string.");
+  }
+  talloc_free(tmp_ctx);
 }
 
+static int tr_cfg_destructor(void *object)
+{
+  TR_CFG *cfg = talloc_get_type_abort(object, TR_CFG);
+  if (cfg->files)
+    g_array_unref(cfg->files);
+  return 0;
+}
 TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
 {
-  return talloc_zero(mem_ctx, TR_CFG);
+  TR_CFG *cfg=talloc(mem_ctx, TR_CFG);
+  if (cfg!=NULL) {
+    cfg->internal=NULL;
+    cfg->rp_clients=NULL;
+    cfg->peers=NULL;
+    cfg->default_servers=NULL;
+    cfg->ctable=tr_comm_table_new(cfg);
+    if (cfg->ctable==NULL) {
+      talloc_free(cfg);
+      return NULL;
+    }
+    cfg->files = g_array_new(FALSE, FALSE, sizeof(TR_CFG_FILE));
+    if (cfg->files == NULL) {
+      talloc_free(cfg);
+      return NULL;
+    }
+    talloc_set_destructor((void *)cfg, tr_cfg_destructor);
+  }
+  return cfg;
 }
 
-void tr_cfg_free (TR_CFG *cfg) {
+void tr_cfg_free (TR_CFG *cfg)
+{
   talloc_free(cfg);
 }
 
@@ -117,807 +187,221 @@ TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
   return TR_CFG_SUCCESS;
 }
 
-static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
-{
-  json_t *jint = NULL;
-  json_t *jmtd = NULL;
-  json_t *jtidsp = NULL;
-  json_t *jtrpsp = NULL;
-  json_t *jhname = NULL;
-  json_t *jlog = NULL;
-  json_t *jconthres = NULL;
-  json_t *jlogthres = NULL;
-  json_t *jcfgpoll = NULL;
-  json_t *jcfgsettle = NULL;
-  json_t *jroutesweep = NULL;
-  json_t *jrouteupdate = NULL;
-  json_t *jrouteconnect = NULL;
-
-  if ((!trc) || (!jcfg))
-    return TR_CFG_BAD_PARAMS;
-
-  if (NULL == trc->internal) {
-    if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
-      return TR_CFG_NOMEM;
-  }
-
-  if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
-    if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
-      if (json_is_number(jmtd)) {
-        trc->internal->max_tree_depth = json_integer_value(jmtd);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
-    }
-    if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
-      if (json_is_number(jtidsp)) {
-        trc->internal->tids_port = json_integer_value(jtidsp);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
-    }
-    if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
-      if (json_is_number(jtrpsp)) {
-        trc->internal->trps_port = json_integer_value(jtrpsp);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
-    }
-    if (NULL != (jhname = json_object_get(jint, "hostname"))) {
-      if (json_is_string(jhname)) {
-        trc->internal->hostname = json_string_value(jhname);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
-        return TR_CFG_NOPARSE;
-      }
-    }
-    if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
-      if (json_is_number(jcfgpoll)) {
-        trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
-    }
-
-    if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
-      if (json_is_number(jcfgsettle)) {
-        trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
-    }
-
-    if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
-      if (json_is_number(jrouteconnect)) {
-        trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
-    }
-
-    if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
-      if (json_is_number(jroutesweep)) {
-        trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
-    }
-
-    if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
-      if (json_is_number(jrouteupdate)) {
-        trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
-    }
-
-    if (NULL != (jlog = json_object_get(jint, "logging"))) {
-      if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
-        if (json_is_string(jlogthres)) {
-          trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
-        } else {
-          tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
-          return TR_CFG_NOPARSE;
-        }
-      } else {
-        /* If not configured, use the default */
-        trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
-      }
-
-      if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) {
-        if (json_is_string(jconthres)) {
-            trc->internal->console_threshold = str2sev(json_string_value(jconthres));
-        } else {
-          tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
-          return TR_CFG_NOPARSE;
-        }
-      } else {
-        /* If not configured, use the default */
-        trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
-      }
-    } else {
-        /* If not configured, use the default */
-        trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
-        trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
-    }
-
-    tr_debug("tr_cfg_parse_internal: Internal config parsed.");
-    return TR_CFG_SUCCESS;
-  }
-  return TR_CFG_SUCCESS;
-}
 
-static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
+TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
 {
-  TR_CONSTRAINT *cons=NULL;
-  int i=0;
-
-  if ((!ctype) || (!jc) || (!rc) ||
-      (!json_is_array(jc)) ||
-      (0 >= json_array_size(jc)) ||
-      (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
-      (!json_is_string(json_array_get(jc, 0)))) {
-    tr_err("tr_cfg_parse_one_constraint: config error.");
-    *rc=TR_CFG_NOPARSE;
-    return NULL;
-  }
+  TR_CFG_RC rc = TR_CFG_SUCCESS;
 
-  if (NULL==(cons=tr_constraint_new(mem_ctx))) {
-    tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
-    *rc=TR_CFG_NOMEM;
-    return NULL;
-  }
+  if (!trc)
+    return TR_CFG_BAD_PARAMS;
 
-  if (NULL==(cons->type=tr_new_name(ctype))) {
-    tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
-    *rc=TR_CFG_NOMEM;
-    tr_constraint_free(cons);
-    return NULL;
+  if ((NULL == trc->internal)||
+      (NULL == trc->internal->hostname)) {
+    tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
+    rc = TR_CFG_ERROR;
   }
 
-  for (i=0; i < json_array_size(jc); i++) {
-    cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
-    if (cons->matches[i]==NULL) {
-      tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
-      *rc=TR_CFG_NOMEM;
-      tr_constraint_free(cons);
-      return NULL;
-    }
+  if (NULL == trc->rp_clients) {
+    tr_debug("tr_cfg_validate: Error: No RP Clients configured");
+    rc = TR_CFG_ERROR;
   }
 
-  return cons;
-}
-
-static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_FILTER *filt=NULL;
-  json_t *jfaction=NULL;
-  json_t *jfspecs=NULL;
-  json_t *jffield=NULL;
-  json_t *jfmatch=NULL;
-  json_t *jrc=NULL;
-  json_t *jdc=NULL;
-  int i=0, j=0;
-
-  *rc=TR_CFG_ERROR;
-
-  if ((jfilt==NULL) || (rc==NULL)) {
-    tr_err("tr_cfg_parse_one_filter: null argument");
-    *rc=TR_CFG_BADARG;
-    goto cleanup;
-  }
-    
-  if (NULL==(filt=tr_filter_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_filter: Out of memory.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
+  if (0==tr_comm_table_size(trc->ctable)) {
+    tr_debug("tr_cfg_validate: Error: No Communities configured");
+    rc = TR_CFG_ERROR;
   }
-  tr_filter_set_type(filt, ftype);
 
-  /* make sure we have space to represent the filter */
-  if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
-    tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup
+  if ((NULL == trc->default_servers) && (NULL == trc->ctable->idp_realms)) {
+    tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
+    rc = TR_CFG_ERROR;
   }
-
-  /* For each entry in the filter... */
-  for (i=0; i < json_array_size(jfilt); i++) {
-    if ((NULL==(jfaction=json_object_get(json_array_get(jfilt, i), "action"))) ||
-        (!json_is_string(jfaction))) {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-    if ((NULL==(jfspecs=json_object_get(json_array_get(jfilt, i), "filter_specs"))) ||
-        (!json_is_array(jfspecs)) ||
-        (0==json_array_size(jfspecs))) {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
   
-    if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
-      tr_debug("tr_cfg_parse_one_filter: Filter has too many filter_specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if (NULL==(filt->lines[i]=tr_fline_new(filt))) {
-      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i+1);
-      *rc=TR_CFG_NOMEM;
-      goto cleanup;
-    }
-
-    if (!strcmp(json_string_value(jfaction), "accept")) {
-        filt->lines[i]->action=TR_FILTER_ACTION_ACCEPT;
-    }
-    else if (!strcmp(json_string_value(jfaction), "reject")) {
-      filt->lines[i]->action=TR_FILTER_ACTION_REJECT;
-    }
-    else {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.", json_string_value(jfaction));
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if (NULL!=(jrc=json_object_get(json_array_get(jfilt, i), "realm_constraints")) {
-        if (!json_is_array(jrc)) {
-          tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
-          *rc=TR_CFG_NOPARSE;
-          goto cleanup;
-        } else if (json_array_size(jrc)>TR_MAX_CONST_MATCHES) {
-          tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
-                 TR_MAX_CONST_MATCHES);
-          *rc=TR_CFG_NOPARSE;
-          goto cleanup;
-        } else if (json_array_size(jrc)>0) {
-          /* ok we actually have entries to process */
-          if (NULL==(filt->lines[i]->realm_cons=tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
-            tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
-            *rc=TR_CFG_NOPARSE;
-            goto cleanup;
-          }
-        }
-      }
-    }
-
-    if (NULL!=(jdc=json_object_get(json_array_get(jfilt, i), "domain_constraints"))) {
-      if (!json_is_array(jdc)) {
-          tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
-          *rc=TR_CFG_NOPARSE;
-          goto cleanup;
-      } else if (json_array_size(jdc)>TR_MAX_CONST_MATCHES) {
-          tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
-                 TR_MAX_CONST_MATCHES);
-          *rc=TR_CFG_NOPARSE;
-          goto cleanup;
-      } else if (json_array_size(jdc)>0) {
-        if (NULL==(filt->lines[i]->domain_cons=tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
-          tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
-          *rc=TR_CFG_NOPARSE;
-          goto cleanup;
-        }
-      }
-    }
-
-    /*For each filter spec within the filter line... */
-    for (j=0; j <json_array_size(jfspecs); j++) {
-      if ((NULL==(jffield=json_object_get(json_array_get(jfspecs, j), "field"))) ||
-          (!json_is_string(jffield)) ||
-          (NULL==(jfmatch=json_object_get(json_array_get(jfspecs, j), "match"))) ||
-          (!json_is_string(jfmatch))) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter field and match for filter spec %d, filter line %d.", i, j);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-
-      if (NULL==(filt->lines[i]->specs[j]=tr_fspec_new(filt->lines[i]))) {
-        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc=TR_CFG_NOMEM;
-        goto cleanup;
-      }
-
-      if ((NULL==(filt->lines[i]->specs[j]->field=tr_new_name(json_string_value(jffield)))) ||
-          (NULL==(filt->lines[i]->specs[j]->match=tr_new_name(json_string_value(jfmatch))))) {
-        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc=TR_CFG_NOMEM;
-        goto cleanup;
-      }
-    }
-  }
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, filt);
-
- cleanup:
-  talloc_free(tmp_ctx);
-  return filt;
+  return rc;
 }
 
-static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
+static void tr_cfg_log_json_error(const char *label, json_error_t *rc)
 {
-  TR_AAA_SERVER *aaa = NULL;
-  TR_NAME *name=NULL;
-
-  if ((!jaddr) || (!json_is_string(jaddr))) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
-    *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  name=tr_new_name(json_string_value(jaddr));
-  if (name==NULL) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  aaa=tr_aaa_server_new(mem_ctx, name);
-  if (aaa==NULL) {
-    tr_free_name(name);
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  return aaa;
+  tr_debug("%s: JSON parse error on line %d: %s",
+          label,
+          rc->line,
+          rc->text);
 }
 
-static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
+/**
+ * Parse a config file and return its JSON structure. Also emits a serial number to the log
+ * if one is present.
+ *
+ * @param file_with_path The file (with path!) to parse
+ * @return Pointer to the result of parsing, or null on error
+ */
+static json_t *tr_cfg_parse_one_config_file(const char *file_with_path)
 {
-  TR_AAA_SERVER *aaa = NULL;
-  TR_NAME *name=NULL;
-
-  if ((!jaddr) || (!json_is_string(jaddr))) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
-    *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  name=tr_new_name(json_string_value(jaddr));
-  if (name==NULL) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
+  json_t *jcfg=NULL;
+  json_error_t rc;
 
-  aaa=tr_aaa_server_new(mem_ctx, name);
-  if (aaa==NULL) {
-    tr_free_name(name);
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
-    *rc = TR_CFG_NOMEM;
+  if (NULL==(jcfg=json_load_file(file_with_path, 
+                                 JSON_DISABLE_EOF_CHECK|JSON_REJECT_DUPLICATES, &rc))) {
+    tr_debug("tr_cfg_parse_one_config_file: Error parsing config file %s.", 
+             file_with_path);
+    tr_cfg_log_json_error("tr_cfg_parse_one_config_file", &rc);
     return NULL;
   }
 
-  return aaa;
-}
-
-static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) 
-{
-  TALLOC_CTX *tmp_ctx=NULL;
-  TR_AAA_SERVER *aaa = NULL;
-  TR_AAA_SERVER *temp_aaa = NULL;
-  int i = 0;
-
-  for (i = 0; i < json_array_size(jaaas); i++) {
-    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
-      talloc_free(tmp_ctx);
-      return NULL;
-    }
-    /* TBD -- IPv6 addresses */
-    //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
-    temp_aaa->next = aaa;
-    aaa = temp_aaa;
-  }
-  tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
-
-  for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
-    talloc_steal(mem_ctx, temp_aaa);
-  talloc_free(tmp_ctx);
-  return aaa;
+  return jcfg;
 }
 
-/* not talloc-aware */
-static TR_APC *tr_cfg_parse_apc(json_t *japc, TR_CFG_RC *rc)
+/* extract serial number */
+static json_int_t get_cfg_serial(json_t *jcfg)
 {
-  TR_APC *apc=NULL;
-  TR_NAME *name=NULL;
-  
-  *rc = TR_CFG_SUCCESS;         /* presume success */
-
-  if ((!japc) || (!rc) || (!json_is_string(japc))) {
-    tr_debug("tr_cfg_parse_apc: Bad parameters.");
-    if (rc) 
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  apc=tr_apc_new(mem_ctx);
-  if (apc==NULL) {
-    tr_debug("tr_cfg_parse_apc: Out of memory.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
+  json_t *jser=NULL;
 
-  name=tr_new_name(json_string_value(json));
-  if (name==NULL) {
-    tr_debug("tr_cfg_parse_apc: No memory for APC name.");
-    tr_apc_free(apc);
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-  tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
-
-  return apc;
-}
-
-static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
-{
-  TR_NAME *name=NULL;
-  *rc=TR_CFG_ERROR;
-
-  if ((jname==NULL) || (!json_is_string(jname))) {
-    tr_err("tr_cfg_parse_name: name missing or not a string");
-    *rc=TR_CFG_BADARG;
-    name=NULL;
-  } else {
-    name=tr_new_name(json_string_value(jname));
-    if (name==NULL) {
-      tr_err("tr_cfg_parse_name: could not allocate name");
-      *rc=TR_CFG_NOMEM;
-    } else {
-      *rc=TR_CFG_SUCCESS;
+  if (NULL != (jser = json_object_get(jcfg, "serial_number"))) {
+    if (json_is_integer(jser)) {
+      return json_integer_value(jser);
     }
   }
-  return name;  
-}
-
-static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
-{
-  const char *shared=NULL;
-  *rc=TR_CFG_SUCCESS;
-  
-  if ((jsc==NULL) ||
-      (!json_is_string(jsc)) ||
-      (NULL==(shared=json_string_value(jsc)))) {
-    *rc=TR_CFG_BADARG;
-    return -1;
-  }
-
-  if (0==strcmp(shared, "no"))
-    return 0;
-  else if (0==strcmp(shared, "yes"))
-    return 1;
-
-  /* any other value is an error */
-  tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
-           shared);
-  *rc=TR_CFG_NOPARSE;
-  return -1;
+  return TR_CFG_INVALID_SERIAL;
 }
 
-/* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
-static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
+/**
+ * Helper to free an array returned by tr_cfg_parse_config_files
+ * @param n_jcfgs
+ * @param jcfgs
+ */
+static void tr_cfg_parse_free_jcfgs(unsigned int n_jcfgs, json_t **jcfgs)
 {
-  TR_NAME *apc=NULL;
-  TR_AAA_SERVER *aaa=NULL;
-  TR_CFG_RC rc=TR_CFG_ERROR;
-
-  if (jidp==NULL)
-    goto cleanup;
-
-  idp->origin=TR_REALM_LOCAL; /* if we're parsing it from a config file, it's local */
-  idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
-  if (rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  apc=tr_cfg_parse_name(json_object_get(jidp, "apc"), &rc);
-  if ((rc!=TR_CFG_SUCCESS) || (apc==NULL)) {
-    tr_err("tr_cfg_parse_idp: unable to parse APC");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
-  if ((rc!=TR_CFG_SUCCESS) || (aaa==NULL)) {
-    tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  /* done, fill in the idp structures */
-  idp->apcs=tr_apc_new(idp);
-  if (idp->apcs==NULL) {
-    tr_err("tr_cfg_parse_idp: unable to allocate APC.");
-    rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  tr_apc_set_id(idp->apcs, apc);
-  idp->aaa_servers=aaa;
-  rc=TR_CxFG_SUCCESS;
-
- cleanup:
-  if (rc!=TR_CFG_SUCCESS) {
-    if (apc!=NULL)
-      tr_free_name(apc);
-    if (aaa!=NULL)
-      tr_aaa_server_free(aaa);
-  }
-
-  return rc;
+  int ii=0;
+  for (ii=0; ii<n_jcfgs; ii++)
+    json_decref(jcfgs[ii]);
+  talloc_free(jcfgs);
 }
 
-/* parses idp realm */
-static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jrealm, TR_CFG_RC *rc)
+/**
+ * Parse a list of configuration files. Returns an array of JSON objects, free this with
+ * tr_cfg_parse_free_jcfgs(), a helper function
+ *
+ * @param config_dir
+ * @param n_files
+ * @param cfg_files
+ * @return
+ */
+static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, GArray *files)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_IDP_REALM *realm=NULL;
-  json_t *jremote=NULL;
-  json_t *jscfg=NULL;
-  json_t *jsrvrs=NULL;
-  json_t *japcs=NULL;
-  TR_CFG_RC call_rc=TR_CFG_ERROR;
-
-  *rc=TR_CFG_ERROR; /* default to error if not set */
-
-  if ((!trc) || (!jrealm) || (!rc)) {
-    tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
-    if (rc)
-      *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  /* must have a name */
-  realm->realm_id=tr_cfg_parse_realm_name(realm,
-                                          json_object_get(jrealm, "realm"),
-                                         &call_rc);
-  if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-        
-  call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
-  if (call_rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
-    *rc=TR_CFG_NOPARSE;
+  unsigned int ii=0;
+  json_t **jcfgs=NULL;
+  TR_CFG_FILE *this_file = NULL;
+
+  /* first allocate the jcfgs */
+  jcfgs=talloc_array(NULL, json_t *, n_files);
+  if (jcfgs==NULL) {
+    tr_crit("tr_parse_config_files: cannot allocate JSON structure array");
     goto cleanup;
   }
-
-  cleanup:
-    if (*rc==TR_CFG_SUCCESS)
-      talloc_steal(mem_ctx, realm);
-    else {
-      talloc_free(realm);
-      realm=NULL;
+  for (ii=0; ii<n_files; ii++) {
+    this_file = &g_array_index(files, TR_CFG_FILE, ii);
+    jcfgs[ii]=tr_cfg_parse_one_config_file(this_file->name);
+    if (jcfgs[ii]==NULL) {
+      tr_err("tr_parse_config: Error parsing JSON in %s", this_file->name);
+      tr_cfg_parse_free_jcfgs(ii, jcfgs); /* frees the JSON objects and the jcfgs array */
+      jcfgs=NULL;
+      goto cleanup;
     }
 
-    talloc_free(tmp_ctx);
-    return realm;
-  }
-
-  /* Determine whether the realm is an IDP realm */
-static int tr_cfg_is_idp_realm(json_t *jrealm)
-{
-  /* If a realm spec contains an identity_provider, it's an IDP realm. */
-  if (NULL != json_object_get(jrealm, "identity_provider"))
-    return 1;
-  else
-    return 0;
-}
-
-/* Parse any idp realms in the j_realms object. Ignores other realm types. */
-static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_IDP_REALM *realms=NULL;
-  TR_IDP_REALM *new_realm=NULL;
-  json_t *this_jrealm=NULL;
-  int ii=0;
-  TR_CFG_RC rc=TR_CFG_ERROR;
-
-  *rc=TR_CFG_ERROR;
-  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
-    tr_err("tr_cfg_parse_idp_realms: realms not an array");
-    *rc=TR_CFG_BADARG;
-    goto cleanup;
-  }
-
-  for (ii=0; ii<json_array_size(jrealms); ii++) {
-    this_jrealm=json_array_get(jrealms, ii);
-    if (tr_cfg_is_idp_realm(this_jrealm)) {
-      new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, &rc);
-      if (rc!=TR_CFG_SUCCESS) {
-        tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-      realms=tr_idp_realm_add(realms, new_realm);
+    this_file->serial = get_cfg_serial(jcfgs[ii]);
+    if (this_file->serial != TR_CFG_INVALID_SERIAL) {
+      tr_notice("tr_parse_config_files: Attempting to load revision %"
+                    JSON_INTEGER_FORMAT
+                    " of '%s'.",
+                this_file->serial,
+                this_file->name);
+    } else {
+      tr_notice("tr_parse_config_files: Attempting to load '%s'.",
+                this_file->name);
     }
   }
-  
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, realms);
 
 cleanup:
+  if (jcfgs)
+    talloc_steal(mem_ctx, jcfgs); /* give this to the caller's context if we succeeded */
   talloc_free(tmp_ctx);
-  return realms;
+  return jcfgs;
 }
 
-/* takes a talloc context, but currently does not use it */
-static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
+/* define a type for config parse functions */
+typedef TR_CFG_RC (TR_CFG_PARSE_FN)(TR_CFG *, json_t *);
+/**
+ * Helper function to parse a collection of JSON structures using a generic parse function.
+ *
+ * @param cfg Config structure to receive results
+ * @param parse_fn Function to apply
+ * @param n_jcfg Number of JSON structures in the array
+ * @param jcfgs Pointer to an array of decoded JSON structures
+ * @param key Key to extract from each jcfg before parsing, or NULL to use the object itself
+ * @return TR_CFG_SUCCESS on success, _FAIL or an error code on failure
+ */
+static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg,
+                                     TR_CFG_PARSE_FN parse_fn,
+                                     unsigned int n_jcfg,
+                                     json_t **jcfgs,
+                                     const char *key)
 {
-  TR_NAME *name=NULL;
+  size_t ii=0;
+  json_t *this_jcfg=NULL;
+  TR_CFG_RC ret=TR_CFG_ERROR;
 
-  if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
-    tr_debug("tr_cfg_parse_org_name: Bad parameters.");
-    if (rc!=NULL)
-      *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
-    return NULL;
-  }
+  if ((cfg==NULL) || (jcfgs==NULL) || (parse_fn==NULL))
+    return TR_CFG_ERROR;
 
-  name=tr_new_name(json_string_value(j_org));
-  if (name==NULL)
-    *rc=TR_CFG_NOMEM;
-  else
-    *rc=TR_CFG_SUCCESS;
-  return name;
-}
+  for (ii=0; ii<n_jcfg; ii++) {
+    if (key)
+      this_jcfg = json_object_get(jcfgs[ii], key);
+    else
+      this_jcfg = jcfgs[ii];
 
-static TR_CFG_RC tr_cfg_parse_one_local_org(TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jlorg)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
-  TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
-  TR_NAME *org_name=NULL;
-  json_t *j_org=NULL;
-  json_t *j_realms=NULL;
-  TRP_IDP_REALM *new_idp_realms=NULL;
-  TR_RP_REALM *new_rp_realms=NULL;
-
-  tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
-
-  /* get organization_name (optional) */
-  if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
-    tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
-  } else {
-    org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
-    if (rc==TR_SUCCESS) {
-      tr_debug("tr_cfg_parse_one_local_org: organization_name=%.*s", org_name->len, org_name->buf);
-      /* we don't actually do anything with this, but we could */
-      tr_free_name(org_name);
-      org_name=NULL; 
-    }
-  }
-
-  /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
-   * it's harmless. Report a warning because that might be unintentional. */
-  if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
-    tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
-  } else {
-    /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
-    new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
-    if (rc!=TR_CFG_SUCCESS)
-      goto cleanup;
+    /* do not try to parse a missing jcfg */
+    if (this_jcfg == NULL)
+      continue;
 
-    new_rp_realms=tr_cfg_parse_rp_realms(tmp_ctx, j_realms, &rc);
-    if (rc!=TR_CFG_SUCCESS)
-      goto cleanup;
+    ret=parse_fn(cfg, this_jcfg);
+    if (ret!=TR_CFG_SUCCESS)
+      break;
   }
-  retval=TR_CFG_SUCCESS;
-  
-cleanup:
-  /* if we succeeded, link things to the configuration and move out of tmp context */
-  if (retval==TR_CFG_SUCCESS) {
-    if (new_idp_realms!=NULL)
-      trc->idp_realms=tr_idp_realm_add(trc->idp_realms, new_idp_realms); /* fixes talloc contexts */
-    if (new_rp_realms!=NULL)
-      trc->rp_realms=tr_rp_realm_add(trc->rp_realms, new_rp_realms); /* fixes talloc contexts */
-  }
-
-  talloc_free(tmp_ctx);
-  return rc;
+  return ret;
 }
 
-/* Parse local organizations if present. Returns success if there are none. */
-
-             
-
-TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
+static void add_files(TR_CFG *cfg, unsigned int n, char **filenames)
 {
-  TR_CFG_RC rc = TR_CFG_SUCCESS;
-
-  if (!trc)
-    return TR_CFG_BAD_PARAMS;
-
-  if ((NULL == trc->internal)||
-      (NULL == trc->internal->hostname)) {
-    tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
-    rc = TR_CFG_ERROR;
-  }
-
-  if (NULL == trc->rp_clients) {
-    tr_debug("tr_cfg_validate: Error: No RP Clients configured");
-    rc = TR_CFG_ERROR;
-  }
+  TR_CFG_FILE frec = {0};
 
-  if (NULL == trc->comms) {
-    tr_debug("tr_cfg_validate: Error: No Communities configured");
-    rc = TR_CFG_ERROR;
-  }
+  while ((n--) > 0) {
+    frec.name = talloc_strdup(cfg, filenames[n]);
+    frec.serial = TR_CFG_INVALID_SERIAL;
 
-  if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
-    tr_debug("tr_cfg_validate: Error: No default servers or IDPs configured.");
-    rc = TR_CFG_ERROR;
+    g_array_append_val(cfg->files, frec);
   }
-  
-  return rc;
 }
 
-/* Join two paths and return a pointer to the result. This should be freed
- * via talloc_free. Returns NULL on failure. */
-static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
-{
-  return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
-}
-
-/* Reads configuration files in config_dir ("" or "./" will use the current directory). */
-TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files)
+/**
+ *  Read a list of configuration files
+ *
+ * @param cfg_mgr Configuration manager
+ * @param n_files Number of entries in cfg_files
+ * @param files_with_paths Array of filenames with path to load
+ * @return TR_CFG_SUCCESS on success, TR_CFG_ERROR or a more specific error on failure
+ */
+TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **files_with_paths)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  json_t *jcfg;
-  json_t *jser;
-  json_error_t rc;
-  char *file_with_path;
-  int ii;
+  json_t **jcfgs=NULL;
   TR_CFG_RC cfg_rc=TR_CFG_ERROR;
 
-  if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
+  if ((!cfg_mgr) || (!files_with_paths)) {
     cfg_rc=TR_CFG_BAD_PARAMS;
     goto cleanup;
   }
 
+  /* get a fresh config to fill in, freeing old one if needed */
   if (cfg_mgr->new != NULL)
     tr_cfg_free(cfg_mgr->new);
   cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
@@ -926,44 +410,27 @@ TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, s
     goto cleanup;
   }
 
-  /* Parse configuration information from each config file */
-  for (ii=0; ii<n; ii++) {
-    file_with_path=join_paths(tmp_ctx, config_dir, cfg_files[ii]->d_name); /* must free result with talloc_free */
-    if(file_with_path == NULL) {
-      tr_crit("tr_parse_config: error joining path.");
-      cfg_rc=TR_CFG_NOMEM;
-      goto cleanup;
-    }
-    tr_debug("tr_parse_config: Parsing %s.", cfg_files[ii]->d_name); /* print the filename without the path */
-    if (NULL == (jcfg = json_load_file(file_with_path, 
-                                       JSON_DISABLE_EOF_CHECK, &rc))) {
-      tr_debug("tr_parse_config: Error parsing config file %s.", 
-               cfg_files[ii]->d_name);
-      cfg_rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-    talloc_free(file_with_path); /* done with filename */
-
-    // Look for serial number and log it if it exists
-    if (NULL != (jser = json_object_get(jcfg, "serial_number"))) {
-      if (json_is_number(jser)) {
-        tr_notice("tr_read_config: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
-                  json_integer_value(jser),
-                  cfg_files[n]->d_name);
-      }
-    }
+  /* add the list of files to the config */
+  add_files(cfg_mgr->new, n_files, files_with_paths);
 
-    /* TODO: parse using the new functions */
-    if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(cfg_mgr->new, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(cfg_mgr->new, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(cfg_mgr->new, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(cfg_mgr->new, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_comms(cfg_mgr->new, jcfg))) {
-      cfg_rc=TR_CFG_ERROR;
-      goto cleanup;
-    }
+  /* first parse the json */
+  jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, cfg_mgr->new->files);
+  if (jcfgs==NULL) {
+    cfg_rc=TR_CFG_NOPARSE;
+    goto cleanup;
   }
 
+  cfg_mgr->new->peers=trp_ptable_new(cfg_mgr); /* not sure why this isn't in cfg_mgr->new's context */
+
+  /* now run through the parsers on the JSON */
+  if ((TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_internal, n_files, jcfgs, "tr_internal"))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_local_orgs, n_files, jcfgs, NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_peer_orgs, n_files, jcfgs, NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_default_servers, n_files, jcfgs,
+                                                      NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_comms, n_files, jcfgs, NULL))))
+    goto cleanup; /* cfg_rc was set above */
+
   /* make sure we got a complete, consistent configuration */
   if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
     tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
@@ -976,56 +443,14 @@ TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, s
   cfg_rc=TR_CFG_SUCCESS;
 
 cleanup:
+  if (jcfgs!=NULL)
+    tr_cfg_parse_free_jcfgs(n_files, jcfgs);
   talloc_free(tmp_ctx);
   return cfg_rc;
 }
 
-TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
-{
-
-  TR_IDP_REALM *cfg_idp;
-
-  if ((!tr_cfg) || (!idp_id)) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
-    if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
-      tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
-      return cfg_idp;
-    }
-  }
-  /* if we didn't find one, return NULL */ 
-  return NULL;
-}
-
-TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
-{
-  TR_RP_CLIENT *cfg_rp;
-  int i;
-
-  if ((!tr_cfg) || (!rp_gss)) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
-    for (i = 0; i < TR_MAX_GSS_NAMES; i++) {
-      if (!tr_name_cmp (rp_gss, cfg_rp->gss_names[i])) {
-        tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
-        return cfg_rp;
-      }
-    }
-  }
-  /* if we didn't find one, return NULL */ 
-  return NULL;
-}
-
 static int is_cfg_file(const struct dirent *dent) {
-  int n;
+  size_t n;
 
   /* Only accept filenames ending in ".cfg" and starting with a character
    * other than an ASCII '.' */
@@ -1057,7 +482,7 @@ static int is_cfg_file(const struct dirent *dent) {
  * allocated array of pointers to struct dirent entries, as returned
  * by scandir(). These can be freed with tr_free_config_file_list().
  */
-int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files) {
+int tr_find_config_files(const char *config_dir, struct dirent ***cfg_files) {
   int n = 0;
   
   n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);