Merge branch 'master' into jennifer/trp-devel
authorJennifer Richards <jennifer@painless-security.com>
Wed, 10 Aug 2016 18:29:01 +0000 (14:29 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Wed, 10 Aug 2016 18:29:01 +0000 (14:29 -0400)
Conflicts:
common/tr_config.c
common/tr_name.c
include/tr_config.h
include/trust_router/tid.h
tr/tr_main.c

1  2 
common/tr_comm.c
common/tr_config.c
common/tr_name.c
configure.ac
include/tr_config.h
include/trust_router/tid.h

diff --combined common/tr_comm.c
   *
   */
  
 +#include <tr_rp.h>
  #include <trust_router/tr_name.h>
 -#include <tr_config.h>
 -#include <tr.h>
  #include <tr_comm.h>
 -#include <tr_rp.h>
  #include <tr_debug.h>
  
  TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm)
@@@ -45,9 -47,9 +45,9 @@@
      return NULL;
    }
  
-   for (idp = comm->idp_realms; NULL != idp; idp = idp->next) {
+   for (idp = comm->idp_realms; NULL != idp; idp = idp->comm_next) {
      if (!tr_name_cmp (idp_realm, idp->realm_id)) {
-       tr_debug("tr_find_comm_idp: Found %s.", idp_realm->buf);
+       tr_debug("tr_find_comm_idp: Found IdP %s in community %s.", idp_realm->buf, comm->id->buf);
        return idp;
      }
    }
@@@ -65,7 -67,7 +65,7 @@@ TR_RP_REALM *tr_find_comm_rp (TR_COMM *
  
    for (rp = comm->rp_realms; NULL != rp; rp = rp->next) {
      if (!tr_name_cmp (rp_realm, rp->realm_name)) {
-       tr_debug("tr_find_comm_idp: Found %s.", rp_realm->buf);
+       tr_debug("tr_find_comm_rp: Found RP %s in community %s.", rp_realm->buf, comm->id->buf);
        return rp;
      }
    }
    return NULL;
  }
  
 -TR_COMM *tr_comm_lookup(TR_INSTANCE *tr, TR_NAME *comm
 +TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name
  {
    TR_COMM *cfg_comm = NULL;
  
 -  for (cfg_comm = tr->active_cfg->comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
 -    if ((cfg_comm->id->len == comm->len) &&
 -      (!strncmp(cfg_comm->id->buf, comm->buf, comm->len)))
 +  for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
 +    if ((cfg_comm->id->len == comm_name->len) &&
 +      (!strncmp(cfg_comm->id->buf, comm_name->buf, comm_name->len)))
        return cfg_comm;
    }
    return NULL;
diff --combined common/tr_config.c
  #include <dirent.h>
  #include <talloc.h>
  
 +#include <tr_cfgwatch.h>
  #include <tr_config.h>
  #include <tr_debug.h>
 -#include <tr.h>
  #include <tr_filter.h>
  #include <trust_router/tr_constraint.h>
 +#include <tr_idp.h>
 +#include <tr.h>
  
- void tr_print_config (FILE *stream, TR_CFG *cfg) {
-   fprintf(stream, "tr_print_config: Not yet implemented.");
-   return;
+ void tr_print_config (TR_CFG *cfg) {
+   tr_notice("tr_print_config: Logging running trust router configuration.");
+   tr_print_comms(cfg->comms);
+ }
+ void tr_print_comms (TR_COMM *comm_list) {
+   TR_COMM *comm = NULL;
+   for (comm = comm_list; 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_notice("tr_print_config:  - Member RPs:");
+     tr_print_comm_rps(comm->rp_realms);
+   }
+ }
+ void tr_print_comm_idps (TR_IDP_REALM *idp_list) {
+   TR_IDP_REALM *idp = NULL;
+   for (idp = idp_list; NULL != idp; idp = idp->comm_next) {
+     tr_notice("tr_print_config:    - @%s", idp->realm_id->buf);
+   }
+ }
+ void tr_print_comm_rps(TR_RP_REALM *rp_list) {
+   TR_RP_REALM *rp = NULL;
+   for (rp = rp_list; NULL != rp; rp = rp->next) {
+     tr_notice("tr_print_config:    - %s", rp->realm_name->buf);
+   }
  }
  
 +TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
 +{
 +  return talloc_zero(mem_ctx, TR_CFG);
 +}
 +
  void tr_cfg_free (TR_CFG *cfg) {
    talloc_free(cfg);
 -  return;
  }
  
 -TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr) {
 -  if (!tr)
 +TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
 +{
 +  return talloc_zero(mem_ctx, TR_CFG_MGR);
 +}
 +
 +void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
 +  talloc_free(cfg_mgr);
 +}
 +
 +TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
 +{
 +  /* cfg_mgr->active is allowed to be null, but new cannot be */
 +  if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL))
      return TR_CFG_BAD_PARAMS;
  
 -  if (tr->active_cfg)
 -    tr_cfg_free(tr->active_cfg);
 +  if (cfg_mgr->active != NULL)
 +    tr_cfg_free(cfg_mgr->active);
  
 -  tr->active_cfg = tr->new_cfg;
 +  cfg_mgr->active = cfg_mgr->new;
 +  cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
  
 -  tr_log_threshold(tr->active_cfg->internal->log_threshold);
 -  tr_console_threshold(tr->active_cfg->internal->console_threshold);
 +  tr_log_threshold(cfg_mgr->active->internal->log_threshold);
 +  tr_console_threshold(cfg_mgr->active->internal->console_threshold);
  
    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 *jtp = 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(trc, TR_CFG_INTERNAL)))
 +    if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
        return TR_CFG_NOMEM;
 -
 -    memset(trc->internal, 0, sizeof(TR_CFG_INTERNAL));
    }
  
    if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
        /* If not configured, use the default */
        trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
      }
 -    if (NULL != (jtp = json_object_get(jint, "tids_port"))) {
 -      if (json_is_number(jtp)) {
 -      trc->internal->tids_port = json_integer_value(jtp);
 +    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, port is not a number.");
 +      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);
        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"))) {
@@@ -261,12 -201,14 +291,12 @@@ static TR_CONSTRAINT *tr_cfg_parse_one_
      return NULL;
    }
  
 -  if (NULL == (cons = talloc(trc, TR_CONSTRAINT))) {
 +  if (NULL == (cons = talloc_zero(trc, TR_CONSTRAINT))) {
      tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
      *rc = TR_CFG_NOMEM;
      return NULL;
    }
  
 -  memset(cons, 0, sizeof(TR_CONSTRAINT));
 -
    if (NULL == (cons->type = tr_new_name(ctype))) {
      tr_debug("tr_cfg_parse_one_constraint: Out of memory (type).");
      *rc = TR_CFG_NOMEM;
@@@ -313,12 -255,14 +343,12 @@@ static TR_FILTER *tr_cfg_parse_one_filt
      return NULL;
    }
  
 -  if (NULL == (filt = talloc(trc, TR_FILTER))) {
 +  if (NULL == (filt = talloc_zero(trc, TR_FILTER))) {
      tr_debug("tr_cfg_parse_one_filter: Out of memory.");
      *rc = TR_CFG_NOMEM;
      return NULL;
    }
  
 -  memset(filt, 0, sizeof(TR_FILTER));
 -
    if (!strcmp(json_string_value(jftype), "rp_permitted")) {
      filt->type = TR_FILTER_TYPE_RP_PERMITTED;
    }
        return NULL;
      }
  
 -    if (NULL == (filt->lines[i] = talloc(trc, TR_FLINE))) {
 +    if (NULL == (filt->lines[i] = talloc_zero(trc, TR_FLINE))) {
        tr_debug("tr_cfg_parse_one_filter: Out of memory (fline).");
        *rc = TR_CFG_NOMEM;
        tr_filter_free(filt);
        return NULL;
      }
  
 -    memset(filt->lines[i], 0, sizeof(TR_FLINE));
 -
      if (!strcmp(json_string_value(jfaction), "accept")) {
        filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
      }
        return NULL;
        }
  
 -      if (NULL == (filt->lines[i]->specs[j] = talloc(trc, TR_FSPEC))) {
 +      if (NULL == (filt->lines[i]->specs[j] = talloc_zero(trc, TR_FSPEC))) {
        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
        *rc = TR_CFG_NOMEM;
        tr_filter_free(filt);
        return NULL;
        }
  
 -      memset(filt->lines[i]->specs[j], 0, sizeof(TR_FSPEC));
 -    
        if ((NULL == (filt->lines[i]->specs[j]->field = tr_new_name((char *)json_string_value(jffield)))) ||
          (NULL == (filt->lines[i]->specs[j]->match = tr_new_name((char *)json_string_value(jfmatch))))) {
        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
@@@ -477,11 -425,13 +507,11 @@@ static TR_RP_CLIENT *tr_cfg_parse_one_r
      return NULL;
    }
  
 -  if (NULL == (rp = talloc(trc, TR_RP_CLIENT))) {
 +  if (NULL == (rp = talloc_zero(trc, TR_RP_CLIENT))) {
      tr_debug("tr_cfg_parse_one_rp_realm: Out of memory.");
      *rc = TR_CFG_NOMEM;
      return NULL;
    }
 -  
 -  memset(rp, 0, sizeof(TR_RP_CLIENT));
  
    /* TBD -- support more than one filter entry per RP Client? */
    if (NULL == (rp->filter = tr_cfg_parse_one_filter(trc, jfilt, rc))) {
@@@ -527,13 -477,11 +557,13 @@@ static TR_CFG_RC tr_cfg_parse_rp_client
        trc->rp_clients = rp;
      }
    }
 +  tr_debug("tr_cfg_parse_rp_clients: Finished (rc=%d)", rc);
    return rc;
  }
  
 -static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
 +static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
    TR_AAA_SERVER *aaa = NULL;
 +  TR_NAME *name=NULL;
  
    if ((!trc) || (!jaddr) || (!json_is_string(jaddr))) {
      tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
      return NULL;
    }
  
 -  if (NULL == (aaa = talloc(trc, TR_AAA_SERVER))) {
 -    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory.");
 +  name=tr_new_name((char *)(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;
    }
  
 -  memset(aaa, 0, sizeof(TR_AAA_SERVER));
 -
 -  aaa->hostname = tr_new_name((char *)(json_string_value(jaddr)));
 +  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;
  }
  
 -static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TR_CFG *trc, json_t *jaaas, TR_CFG_RC *rc) 
 +static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TALLOC_CTX *mem_ctx, TR_CFG *trc, 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(trc, json_array_get(jaaas, i), rc))) {
 +    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(mem_ctx, trc, json_array_get(jaaas, i), rc))) {
 +      talloc_free(tmp_ctx);
        return NULL;
      }
      /* TBD -- IPv6 addresses */
      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;
  }
  
 -static TR_APC *tr_cfg_parse_apcs (TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
 +static TR_APC *tr_cfg_parse_apcs (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
  {
    TR_APC *apc;
  
      return NULL;
    }
  
 -  if (NULL == (apc = talloc(trc, TR_APC))) {
 +  apc=tr_apc_new(mem_ctx);
 +  if (apc==NULL) {
      tr_debug("tr_cfg_parse_apcs: Out of memory.");
      *rc = TR_CFG_NOMEM;
      return NULL;
    }
  
 -  memset(apc, 0, sizeof(TR_APC));
 -
    /* TBD, deal with more than one APC.  In the meantime, though...                */
    /* Only parse the first APC, because we only know how to deal with one, anyway. */
 -  if (0 == json_array_size(japcs))
 +  if (0 == json_array_size(japcs)) {
 +    talloc_free(apc);
      return NULL;
 +  }
  
    if (NULL == (apc->id = tr_new_name((char *)json_string_value(json_array_get(japcs, 0))))) {
      tr_debug("tr_cfg_parse_apcs: No memory for APC name.");
      *rc = TR_CFG_NOMEM;
 +    talloc_free(apc);
      return NULL;
    }
  
 +  tr_debug("tr_cfg_parse_apcs: Finished (rc=%d)", *rc);
    return apc;
  }
  
  static TR_IDP_REALM *tr_cfg_parse_one_idp_realm (TR_CFG *trc, json_t *jidp, TR_CFG_RC *rc) {
 +  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
    TR_IDP_REALM *idp = NULL;
 +  json_t *jremote = NULL;
    json_t *jrid = NULL;
    json_t *jscfg = NULL;
    json_t *jsrvrs = NULL;
      return NULL;
    }
  
 -  if (NULL == (idp = talloc(trc, TR_IDP_REALM))) {
 +  if (NULL == (idp = tr_idp_realm_new(tmp_ctx))) {
      tr_debug("tr_cfg_parse_one_idp_realm: Out of memory.");
      *rc = TR_CFG_NOMEM;
 +    talloc_free(tmp_ctx);
      return NULL;
    }
  
 -  memset(idp, 0, sizeof(TR_IDP_REALM));
 -
 -  if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
 -      (!json_is_string(jrid)) ||
 -      (NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
 -      (!json_is_string(jscfg)) ||
 -      (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
 -      (!json_is_array(jsrvrs))) {
 -    tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
 -    *rc = TR_CFG_NOPARSE;
 +  /* Assume local route unless specified as remote. */
 +  jremote = json_object_get(jidp, "remote");
 +  if ((jremote!=NULL) && (!json_is_number(jremote))) {
 +    tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (remote is not a number).");
 +    *rc=TR_CFG_NOPARSE;
 +    talloc_free(tmp_ctx);
      return NULL;
    }
  
 -  if (0 == strcmp(json_string_value(jscfg), "no")) {
 -    idp->shared_config = 0;
 -  } else {
 -    idp->shared_config = 1;
 +  if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
 +      (!json_is_string(jrid))) {
 +      tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (realm_id missing or invalid).");
 +      *rc = TR_CFG_NOPARSE;
 +      talloc_free(tmp_ctx);
 +      return NULL;
    }
 +        
 +  if ((jremote==NULL) || (0==json_integer_value(jremote))) {
 +    idp->origin=TR_REALM_LOCAL;
 +
 +    if ((NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
 +        (!json_is_string(jscfg)) ||
 +        (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
 +        (!json_is_array(jsrvrs))) {
 +      tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
 +      *rc = TR_CFG_NOPARSE;
 +      talloc_free(tmp_ctx);
 +      return NULL;
 +    }
 +
 +    if (0 == strcmp(json_string_value(jscfg), "no")) {
 +      idp->shared_config = 0;
 +    } else {
 +      idp->shared_config = 1;
 +    }
 +  } else
 +    idp->origin=TR_REALM_REMOTE_INCOMPLETE;
  
    if (NULL == (idp->realm_id = tr_new_name((char *)json_string_value(jrid)))) {
      tr_debug("tr_cfg_parse_one_idp_realm: No memory for realm id.");
      *rc = TR_CFG_NOMEM;
 -    return NULL;
 -  }
 -
 -  if (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(trc, jsrvrs, rc))) {
 -    tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
 -    tr_free_name(idp->realm_id);
 +    talloc_free(tmp_ctx);
      return NULL;
    }
  
    if ((NULL != (japcs = json_object_get(jidp, "apcs"))) &&
        (json_is_array(japcs))) {
 -    if (NULL == (idp->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
 +    if (NULL == (idp->apcs = tr_cfg_parse_apcs(idp, trc, japcs, rc))) {
        tr_debug("tr_cfg_parse_one_idp_realm: Can't parse APCs for realm %s .", idp->realm_id->buf);
 -      tr_free_name(idp->realm_id);
 -      /* TBD -- free aaa_servers */;
 +      talloc_free(tmp_ctx);
        return NULL;
      }
    } 
 +
 +  if ((idp->origin==TR_REALM_LOCAL) &&
 +      (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(idp, trc, jsrvrs, rc)))) {
 +    tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
 +    talloc_free(tmp_ctx);
 +    return NULL;
 +  }
 +
 +  talloc_steal(trc, idp);
 +  talloc_free(tmp_ctx);
    return idp;
  }
  
@@@ -727,7 -634,7 +757,7 @@@ static TR_CFG_RC tr_cfg_parse_default_s
        (0 < json_array_size(jdss))) {
  
      for (i = 0; i < json_array_size(jdss); i++) {
 -      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, 
 +      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, trc, 
                                                  json_array_get(jdss, i), 
                                                  &rc))) {
        return rc;
      }
    } 
  
 +  tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
    return rc;
  }
  
@@@ -767,13 -673,13 +797,14 @@@ static TR_CFG_RC tr_cfg_parse_idp_realm
      }
    }
  
 +  tr_debug("tr_cfg_parse_idp_realms: Finished (rc=%d)", rc);
    return rc;
  }
  
  static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_RC *rc)
  {
    TR_IDP_REALM *idp = NULL;
+   TR_IDP_REALM *found_idp = NULL;
    TR_IDP_REALM *temp_idp = NULL;
    int i = 0;
  
    }
  
    for (i = 0; i < json_array_size(jidps); i++) {
-     if (NULL == (temp_idp = (tr_cfg_find_idp(trc, 
-                                              tr_new_name((char *)json_string_value(json_array_get(jidps, i))), 
-                                              rc)))) {
+     if (NULL == (temp_idp = talloc(trc, TR_IDP_REALM))) {
+       tr_debug("tr_cfg_parse_comm_idps: Can't allocate memory for IdP Realm.");
+       if (rc)
+       *rc = TR_CFG_NOMEM;
+       return NULL;
+     }
+     memset (temp_idp, 0, sizeof(TR_IDP_REALM));
+     if (NULL == (found_idp = (tr_cfg_find_idp(trc, 
+                                            tr_new_name((char *)json_string_value(json_array_get(jidps, i))), 
+                                            rc)))) {
        tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", 
              (char *)json_string_value(json_array_get(jidps, i)));
        return NULL;
      }
  
+     // We *MUST* do a dereferenced copy here or the second community will corrupt the linked list we create here.
+     *temp_idp = *found_idp;
      temp_idp->comm_next = idp;
      idp = temp_idp;
    }
@@@ -816,12 -733,13 +858,12 @@@ static TR_RP_REALM *tr_cfg_parse_comm_r
    }
  
    for (i = (json_array_size(jrps)-1); i >= 0; i--) {
 -    if (NULL == (temp_rp = talloc(trc, TR_RP_REALM))) {
 +    if (NULL == (temp_rp = talloc_zero(trc, TR_RP_REALM))) {
        tr_debug("tr_cfg_parse_comm_rps: Can't allocate memory for RP Realm.");
        if (rc)
        *rc = TR_CFG_NOMEM;
        return NULL;
      }
 -    memset (temp_rp, 0, sizeof(TR_RP_REALM));
  
      if (NULL == (temp_rp->realm_name = tr_new_name((char *)json_string_value(json_array_get(jrps, i))))) {
        tr_debug("tr_cfg_parse_comm_rps: No memory for RP Realm Name.");
@@@ -884,7 -802,7 +926,7 @@@ static TR_COMM *tr_cfg_parse_one_comm (
      comm->type = TR_COMM_APC;
    } else if (0 == strcmp(json_string_value(jtype), "coi")) {
      comm->type = TR_COMM_COI;
 -    if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
 +    if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, trc, japcs, rc))) {
        tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
        tr_free_name(comm->id);
        return NULL;
@@@ -957,7 -875,6 +999,7 @@@ static TR_CFG_RC tr_cfg_parse_comms (TR
        trc->comms = comm;
      }
    }
 +  tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
    return rc;
  }
  
@@@ -993,74 -910,72 +1035,85 @@@ TR_CFG_RC tr_cfg_validate (TR_CFG *trc
  
  /* 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(const char *p1, const char *p2) {
 -  return talloc_asprintf(NULL, "%s/%s", p1, p2); /* returns NULL on a 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_INSTANCE *tr, const char *config_dir, int n, struct dirent **cfg_files) {
 +/* 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)
 +{
 +  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
    json_t *jcfg;
+   json_t *jser;
    json_error_t rc;
    char *file_with_path;
 +  int ii;
 +  TR_CFG_RC cfg_rc=TR_CFG_ERROR;
  
 -  if ((!tr) || (!cfg_files) || (n<=0))
 -    return TR_CFG_BAD_PARAMS;
 -
 -  /* If there is a partial/abandoned config lying around, free it */
 -  if (tr->new_cfg) 
 -    tr_cfg_free(tr->new_cfg);
 -  
 -  if (NULL == (tr->new_cfg = talloc(NULL, TR_CFG)))
 -    return TR_CFG_NOMEM;
 +  if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
 +    cfg_rc=TR_CFG_BAD_PARAMS;
 +    goto cleanup;
 +  }
  
 -  memset(tr->new_cfg, 0, sizeof(TR_CFG));
 +  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 */
 +  if (cfg_mgr->new == NULL) {
 +    cfg_rc=TR_CFG_NOMEM;
 +    goto cleanup;
 +  }
  
    /* Parse configuration information from each config file */
 -  while (n--) {
 -    file_with_path=join_paths(config_dir, cfg_files[n]->d_name); /* must free result with talloc_free */
 +  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.");
 -      return TR_CFG_NOMEM;
 +      cfg_rc=TR_CFG_NOMEM;
 +      goto cleanup;
      }
 -    tr_debug("tr_parse_config: Parsing %s.", cfg_files[n]->d_name); /* print the filename without the path */
 +    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[n]->d_name);
 -      talloc_free(file_with_path);
 -      return TR_CFG_NOPARSE;
 +               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);
+       }
+     }
  
 -    if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(tr->new_cfg, jcfg)) ||
 -        (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(tr->new_cfg, jcfg)) ||
 -        (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(tr->new_cfg, jcfg)) ||
 -        (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(tr->new_cfg, jcfg)) ||
 -        (TR_CFG_SUCCESS != tr_cfg_parse_comms(tr->new_cfg, jcfg))) {
 -      tr_cfg_free(tr->new_cfg);
 -      return TR_CFG_ERROR;
 +    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;
      }
    }
  
    /* make sure we got a complete, consistent configuration */
 -  if (TR_CFG_SUCCESS != tr_cfg_validate(tr->new_cfg)) {
 -    tr_debug("tr_parse_config: Error: INVALID CONFIGURATION, EXITING");
 -    return TR_CFG_ERROR;
 +  if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
 +    tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
 +    cfg_rc=TR_CFG_ERROR;
 +    goto cleanup;
    }
  
 -  return TR_CFG_SUCCESS;
 +  /* success! */
 +  talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
 +  cfg_rc=TR_CFG_SUCCESS;
 +
 +cleanup:
 +  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)
@@@ -1141,14 -1056,21 +1194,14 @@@ static int is_cfg_file(const struct dir
   * 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 n = 0, i = 0;
 +  int n = 0;
    
 -  n = scandir(config_dir, cfg_files, &is_cfg_file, 0);
 +  n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
  
    if (n < 0) {
      perror("scandir");
      tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
 -  } else if (n == 0) {
 -    tr_debug("tr_find_config: No config files found.");
 -  } else {
 -    i = n;
 -    while(i--) {
 -      tr_debug("tr_find_config: Config file found (%s).", (*cfg_files)[i]->d_name);
 -    }
 -  }
 +  } 
  
    return n;
  }
diff --combined common/tr_name.c
@@@ -43,27 -43,24 +43,27 @@@ void tr_free_name (TR_NAME *name
      free (name->buf);
      name->buf = NULL;
    }
-   
    free(name);
  }
  
- TR_NAME *tr_new_name (char *name) 
+ TR_NAME *tr_new_name (const char *name) 
  {
    TR_NAME *new;
  
-   if (new = malloc(sizeof(TR_NAME))) { 
+   if (new = malloc(sizeof(TR_NAME))) {
      new->len = strlen(name);
      if (new->buf = malloc((new->len)+1)) {
        strcpy(new->buf, name);
 +    } else {
 +      free(new);
 +      new=NULL;
      }
    }
    return new;
  }
  
- TR_NAME *tr_dup_name (TR_NAME *from) 
+ TR_NAME *tr_dup_name (TR_NAME *from)
  {
    TR_NAME *to;
  
  
  int tr_name_cmp(TR_NAME *one, TR_NAME *two)
  {
 -  if (one->len != two->len)
 -    return 1;
 -  else {
 -    /* lengths equal */
 -    return strncmp(one->buf, two->buf, one->len);
 +  int len=one->len;
 +  int cmp=0;
 +
 +  if (two->len<one->len)
 +    len=two->len; /* len now min(one->len,two->len) */
 +
 +  cmp=strncmp(one->buf, two->buf, len);
 +  if (cmp==0) {
 +    if (one->len<two->len)
 +      return -1;
 +    else if (one->len==two->len)
 +      return 0;
 +    else
 +      return 1;
    }
 +  return cmp;
  }
  
  void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len)
@@@ -122,4 -109,5 +122,3 @@@ char * tr_name_strdup(TR_NAME *src
    }
    return s;
  }
 -
--
diff --combined configure.ac
@@@ -1,5 -1,5 +1,5 @@@
  AC_PREREQ(2.63)
- AC_INIT([trust_router],[1.5.1],
+ AC_INIT([trust_router],[1.5.2],
  [bugs@project-moonshot.org])
  AC_CONFIG_MACRO_DIR(m4)
  AC_CONFIG_AUX_DIR(build-aux)
@@@ -28,7 -28,6 +28,7 @@@ AC_CHECK_LIB([sqlite3], [sqlite3_open],
      [AC_MSG_ERROR([Please install sqlite3 development])])
  AC_CHECK_LIB([jansson], [json_object])
  AC_CHECK_LIB([crypto], [DH_new])
 -AC_CHECK_HEADERS(gssapi.h gssapi_ext.h jansson.h talloc.h openssl/dh.h openssl/bn.h syslog.h)
 +AC_CHECK_LIB([event], [event_base_new])
 +AC_CHECK_HEADERS(gssapi.h gssapi_ext.h jansson.h talloc.h openssl/dh.h openssl/bn.h syslog.h event2/event.h)
  AC_CONFIG_FILES([Makefile gsscon/Makefile])
  AC_OUTPUT
diff --combined include/tr_config.h
  #include <dirent.h>
  #include <jansson.h>
  #include <syslog.h>
 +#include <sys/time.h>
 +#include <talloc.h>
  
 -#include <tr.h>
 +#include <tr_comm.h>
  #include <tr_rp.h>
  #include <tr_idp.h>
 -#include <tr_comm.h>
 +#include <trp_internal.h>
  
  #define TR_DEFAULT_MAX_TREE_DEPTH 12
  #define TR_DEFAULT_TR_PORT 12308
  #define TR_DEFAULT_TIDS_PORT 12309
 +#define TR_DEFAULT_TRPS_PORT 12310
  #define TR_DEFAULT_LOG_THRESHOLD LOG_INFO
  #define TR_DEFAULT_CONSOLE_THRESHOLD LOG_NOTICE
 -
 +#define TR_DEFAULT_TRP_CONNECT_INTERVAL 10
 +#define TR_DEFAULT_TRP_UPDATE_INTERVAL 120
 +#define TR_DEFAULT_TRP_SWEEP_INTERVAL 30
  typedef enum tr_cfg_rc {
    TR_CFG_SUCCESS = 0, /* No error */
    TR_CFG_ERROR,               /* General processing error */
    TR_CFG_BAD_PARAMS,  /* Bad parameters passed to tr_config function */
    TR_CFG_NOPARSE,     /* Parsing error */
 -  TR_CFG_NOMEM                /* Memory allocation error */
 +  TR_CFG_NOMEM,               /* Memory allocation error */
  } TR_CFG_RC;
  
  typedef struct tr_cfg_internal {
    unsigned int max_tree_depth;
    unsigned int tids_port;
 +  unsigned int trps_port;
    const char *hostname;
    int log_threshold;
    int console_threshold;
 +  unsigned int cfg_poll_interval;
 +  unsigned int cfg_settling_time;
 +  unsigned int trp_sweep_interval;
 +  unsigned int trp_update_interval;
 +  unsigned int trp_connect_interval;
  } TR_CFG_INTERNAL;
  
  typedef struct tr_cfg {
    TR_COMM *comms;                     /* locally-known communities */
    TR_AAA_SERVER *default_servers;     /* default server list */
    /* TBD -- Global Filters */
 -  /* TBD -- Trust Router Peers */
 -  /* TBD -- Trust Links */
  } TR_CFG;
  
 +typedef struct tr_cfg_mgr {
 +  TR_CFG *active;
 +  TR_CFG *new;
 +} TR_CFG_MGR;
 +
  int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files);
  void tr_free_config_file_list(int n, struct dirent ***cfg_files);
 -TR_CFG_RC tr_parse_config (TR_INSTANCE *tr, const char *config_dir, int n, struct dirent **cfg_files);
 -TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr);
 +TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files);
 +TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr);
  TR_CFG_RC tr_cfg_validate (TR_CFG *trc);
 +TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx);
 +TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx);
  void tr_cfg_free(TR_CFG *cfg);
- void tr_print_config(FILE *stream, TR_CFG *cfg);
 +void tr_cfg_mgr_free(TR_CFG_MGR *cfg);
+ void tr_print_config(TR_CFG *cfg);
+ void tr_print_comms(TR_COMM *comm_list);
+ void tr_print_comm_idps(TR_IDP_REALM *idp_list);
+ void tr_print_comm_rps(TR_RP_REALM *rp_list);
  
 -TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc);
 -TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc);
 -TR_RP_CLIENT *tr_rp_client_lookup(TR_INSTANCE *tr, TR_NAME *gss_name);
 +TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *cfg, TR_NAME *idp_id, TR_CFG_RC *rc);
 +TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *cfg, TR_NAME *rp_gss, TR_CFG_RC *rc);
  
  #endif
@@@ -35,8 -35,6 +35,8 @@@
  #ifndef TID_H
  #define TID_H
  
 +#include <talloc.h>
 +
  #include <arpa/inet.h>
  #include <openssl/dh.h>
  
@@@ -71,7 -69,7 +71,7 @@@ typedef void (TIDC_RESP_FUNC)(TIDC_INST
  
  
  typedef int (TIDS_REQ_FUNC)(TIDS_INSTANCE *, TID_REQ *, TID_RESP *, void *);
 -typedef int (tids_auth_func)(gss_name_t client_name, TR_NAME *display_name, void *cookie);
 +typedef int (TIDS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie);
  
  
  
@@@ -100,12 -98,9 +100,12 @@@ void tid_req_set_resp_func(TID_REQ *req
  TR_EXPORT void *tid_req_get_cookie(TID_REQ *req);
  void tid_req_set_cookie(TID_REQ *req, void *cookie);
  TR_EXPORT TID_REQ *tid_dup_req (TID_REQ *orig_req);
 -void TR_EXPORT tid_req_free( TID_REQ *req);
 +TR_EXPORT void tid_req_free( TID_REQ *req);
  
  /* Utility functions for TID_RESP structure, in tid/tid_resp.c */
 +
 +TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx);
 +void tid_resp_free(TID_RESP *resp);
  TR_EXPORT int tid_resp_get_result(TID_RESP *resp);
  void tid_resp_set_result(TID_RESP *resp, int result);
  TR_EXPORT TR_NAME *tid_resp_get_err_msg(TID_RESP *resp);
@@@ -141,22 -136,18 +141,22 @@@ TR_EXPORT const TID_PATH *tid_srvr_get_
  
  /* TID Client functions, in tid/tidc.c */
  TR_EXPORT TIDC_INSTANCE *tidc_create (void);
- TR_EXPORT int tidc_open_connection (TIDC_INSTANCE *tidc, char *server, unsigned int port, gss_ctx_id_t *gssctx);
- TR_EXPORT int tidc_send_request (TIDC_INSTANCE *tidc, int conn, gss_ctx_id_t gssctx, char *rp_realm, char *realm, char *coi, TIDC_RESP_FUNC *resp_handler, void *cookie);
+ TR_EXPORT int tidc_open_connection (TIDC_INSTANCE *tidc, const char *server, unsigned int port, gss_ctx_id_t *gssctx);
+ TR_EXPORT int tidc_send_request (TIDC_INSTANCE *tidc, int conn, gss_ctx_id_t gssctx, const char *rp_realm, const char *realm, const char *coi, TIDC_RESP_FUNC *resp_handler, void *cookie);
  TR_EXPORT int tidc_fwd_request (TIDC_INSTANCE *tidc, TID_REQ *req, TIDC_RESP_FUNC *resp_handler, void *cookie);
  TR_EXPORT DH *tidc_get_dh(TIDC_INSTANCE *);
  TR_EXPORT DH *tidc_set_dh(TIDC_INSTANCE *, DH *);
  TR_EXPORT void tidc_destroy (TIDC_INSTANCE *tidc);
  
  /* TID Server functions, in tid/tids.c */
 -TR_EXPORT TIDS_INSTANCE *tids_create (void);
 +TR_EXPORT TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx);
  TR_EXPORT int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
 -                        tids_auth_func *auth_handler, const char *hostname,
 +                        TIDS_AUTH_FUNC *auth_handler, const char *hostname, 
 +                        unsigned int port, void *cookie);
 +TR_EXPORT int tids_get_listener (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
 +                        TIDS_AUTH_FUNC *auth_handler, const char *hostname, 
                          unsigned int port, void *cookie);
 +TR_EXPORT int tids_accept(TIDS_INSTANCE *tids, int listen);
  TR_EXPORT int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp);
  TR_EXPORT int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg);
  TR_EXPORT void tids_destroy (TIDS_INSTANCE *tids);