Merge branch 'master' into debian
authorJennifer Richards <jennifer@painless-security.com>
Thu, 12 Jan 2017 21:53:15 +0000 (16:53 -0500)
committerJennifer Richards <jennifer@painless-security.com>
Thu, 12 Jan 2017 21:53:15 +0000 (16:53 -0500)
61 files changed:
Makefile.am
common/cfg_test/idp.cfg [deleted file]
common/tests/cfg_test.c [moved from common/cfg_test/cfg_test.c with 91% similarity]
common/tests/commtest.c [new file with mode: 0644]
common/tests/dh_test.c [moved from common/dh_test/dh_test.c with 100% similarity]
common/tests/idp.cfg [new file with mode: 0644]
common/tests/mq_test.c [moved from common/mq_test/mq_test.c with 91% similarity]
common/tests/thread_test.c [moved from common/mq_test/thread_test.c with 89% similarity]
common/tr_apc.c
common/tr_comm.c
common/tr_config.c
common/tr_constraint.c
common/tr_dh.c
common/tr_idp.c
common/tr_mq.c
common/tr_msg.c
common/tr_name.c
common/tr_rp.c
common/tr_util.c
configure.ac
gsscon/gsscon_active.c
gsscon/gsscon_passive.c
include/tid_internal.h
include/tr_apc.h
include/tr_comm.h
include/tr_config.h
include/tr_event.h
include/tr_idp.h
include/tr_mq.h
include/tr_rp.h
include/tr_tid.h
include/tr_util.h [new file with mode: 0644]
include/trp_internal.h
include/trp_ptable.h
include/trust_router/tid.h
include/trust_router/tr_constraint.h
include/trust_router/tr_dh.h
include/trust_router/tr_name.h
include/trust_router/trp.h
redhat/default-internal.cfg [new file with mode: 0644]
redhat/default-main.cfg [deleted file]
redhat/organizations.cfg [new file with mode: 0644]
redhat/tr-test-internal.cfg [moved from redhat/tr-test-main.cfg with 100% similarity]
redhat/trusts.cfg [deleted file]
tid/example/tids_main.c
tid/tid_req.c
tid/tid_resp.c
tid/tidc.c
tid/tids.c
tr/internal.cfg [new file with mode: 0644]
tr/manual.cfg [deleted file]
tr/organizations.cfg [new file with mode: 0644]
tr/portal.cfg [deleted file]
tr/tr_main.c
tr/tr_tid.c
tr/tr_trp.c
trp/trp_ptable.c
trp/trp_upd.c
trp/trpc.c
trp/trps.c
trust_router.spec

index 3b434c3..4c32c24 100644 (file)
@@ -1,6 +1,6 @@
 DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
-bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/dh_test/tr_dh_test common/mq_test/mq_test common/mq_test/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/cfg_test/cfg_test
+bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/tests/tr_dh_test common/tests/mq_test common/tests/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/tests/cfg_test common/tests/commtest
 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS)
 AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS)
 SUBDIRS = gsscon 
@@ -10,7 +10,13 @@ common_srcs = common/tr_name.c \
        common/tr_msg.c \
        common/tr_dh.c \
         common/tr_debug.c \
-       common/tr_util.c
+       common/tr_util.c \
+       common/tr_apc.c \
+       common/tr_comm.c \
+       common/tr_rp.c \
+       common/tr_idp.c \
+       common/tr_filter.c \
+       common/tr_gss.c
 
 tid_srcs = tid/tid_resp.c \
 tid/tid_req.c \
@@ -24,18 +30,7 @@ trp/trp_ptable.c \
 trp/trp_rtable.c \
 trp/trp_req.c \
 trp/trp_upd.c \
-tid/tid_resp.c \
-tid/tid_req.c \
-common/tr_gss.c \
 common/tr_config.c \
-common/tr_idp.c \
-common/tr_apc.c \
-common/tr_comm.c \
-common/tr_filter.c \
-common/tr_rp.c \
-common/tr_msg.c \
-common/tr_constraint.c \
-common/tr_name.c \
 common/tr_mq.c
 
 check_PROGRAMS = common/t_constraint
@@ -50,7 +45,7 @@ trp/trp_upd.c
 
 libtr_tid_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
 libtr_tid_la_LIBADD = gsscon/libgsscon.la $(GLIB_LIBS)
-libtr_tid_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2 -no-undefined
+libtr_tid_la_LDFLAGS = $(AM_LDFLAGS) -version-info 3:0:1 -no-undefined
 
 common_t_constraint_SOURCES = common/t_constraint.c \
 common/tr_debug.c \
@@ -68,26 +63,28 @@ tr/tr_event.c \
 tr/tr_cfgwatch.c \
 tr/tr_tid.c \
 tr/tr_trp.c \
-$(trp_srcs)
+$(tid_srcs) \
+$(trp_srcs) \
+$(common_srcs)
 
-tr_trust_router_CFLAGS = $(AM_CFLAGS) -pthread
 tr_trust_router_LDFLAGS = $(AM_LDFLAGS) -levent_pthreads -pthread
-tr_trust_router_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
-
+tr_trust_router_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 tr_trpc_SOURCES =tr/trpc_main.c \
 tr/tr_trp.c \
-$(trp_srcs)
-tr_trpc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+$(trp_srcs) \
+$(tid_srcs) \
+$(common_srcs)
+
+tr_trpc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 tr_trpc_LDFLAGS = $(AM_LDFLAGS) -pthread
-tr_trpc_CFLAGS = $(AM_CFLAGS) -pthread
 
 trp_msgtst_SOURCES = trp/msgtst.c \
-common/tr_msg.c \
+$(common_srcs) \
 trp/trp_req.c \
 trp/trp_upd.c \
 tid/tid_resp.c \
 tid/tid_req.c
-trp_msgtst_LDADD = libtr_tid.la $(GLIB_LIBS)
+trp_msgtst_LDADD =  $(GLIB_LIBS)
 
 trp_test_rtbl_test_SOURCES = trp/test/rtbl_test.c \
 common/tr_name.c \
@@ -97,41 +94,58 @@ trp/trp_rtable.c
 trp_test_rtbl_test_LDADD =  $(GLIB_LIBS)
 
 trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \
-$(trp_srcs) 
-trp_test_ptbl_test_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
-
-tid_example_tidc_SOURCES = tid/example/tidc_main.c
-tid_example_tidc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
-
-tid_example_tids_SOURCES = tid/example/tids_main.c
-tid_example_tids_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
-
-common_dh_test_tr_dh_test_SOURCES = common/tr_dh.c \
+$(tid_srcs) \
+$(trp_srcs) \
+$(common_srcs)
+trp_test_ptbl_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+trp_test_ptbl_test_LDFLAGS = $(AM_LDFLAGS) -pthread
+
+tid_example_tidc_SOURCES = tid/example/tidc_main.c \
+$(tid_srcs) \
+$(trp_srcs) \
+$(common_srcs)
+tid_example_tidc_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+tid_example_tidc_LDFLAGS = $(AM_LDFLAGS) -pthread
+
+tid_example_tids_SOURCES = tid/example/tids_main.c \
+$(tid_srcs) \
+$(trp_srcs) \
+$(common_srcs)
+tid_example_tids_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+tid_example_tids_LDFLAGS = $(AM_LDFLAGS) -pthread
+
+common_tests_tr_dh_test_SOURCES = common/tr_dh.c \
 common/tr_debug.c \
-common/dh_test/dh_test.c
+common/tests/dh_test.c
 
-common_mq_test_mq_test_SOURCES = common/tr_mq.c \
-common/mq_test/mq_test.c
+common_tests_mq_test_SOURCES = common/tr_mq.c \
+common/tests/mq_test.c \
+common/tr_debug.c
 
-common_mq_test_mq_test_CFLAGS = -pthread
-common_mq_test_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+common_tests_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
-common_cfg_test_cfg_test_SOURCES = common/cfg_test/cfg_test.c \
+common_tests_cfg_test_SOURCES = common/tests/cfg_test.c \
+$(common_srcs) \
+$(tid_srcs) \
 $(trp_srcs)
+common_tests_cfg_test_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+common_tests_cfg_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
-common_cfg_test_cfg_test_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
-common_cfg_test_cfg_test_CFLAGS = -pthread
-common_cfg_test_cfg_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
-
-common_mq_test_thread_test_SOURCES = common/tr_mq.c \
+common_tests_thread_test_SOURCES = common/tr_mq.c \
 common/tr_debug.c \
-common/mq_test/thread_test.c
+common/tests/thread_test.c
+
+common_tests_commtest_SOURCES = common/tests/commtest.c \
+$(common_srcs) \
+$(tid_srcs) \
+$(trp_srcs)
+common_tests_commtest_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+common_tests_commtest_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
-common_mq_test_thread_test_CFLAGS = -pthread
-common_mq_test_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+common_tests_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
 pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \
-       include/tr_debug.h \
+       include/tr_debug.h include/trust_router/trp.h \
        include/trust_router/tr_dh.h \
        include/trust_router/tr_constraint.h \
        include/trust_router/tr_versioning.h 
@@ -141,16 +155,19 @@ noinst_HEADERS = include/gsscon.h include/tr_config.h \
        include/tr_idp.h include/tr_rp.h \
        include/tr_comm.h include/tr_apc.h \
        include/tr_tid.h include/tr_trp.h \
-       include/tr_filter.h \
-       include/tid_internal.h
+       include/tr_filter.h include/tr_gss.h \
+       include/tid_internal.h include/trp_internal.h \
+       include/tr_cfgwatch.h include/tr_event.h \
+       include/tr_mq.h include/trp_ptable.h \
+       include/trp_rtable.h include/tr_util.h
 
 pkgdata_DATA=schema.sql
-nobase_dist_pkgdata_DATA=redhat/init redhat/sysconfig redhat/trusts.cfg redhat/tidc-wrapper redhat/trust_router-wrapper redhat/tr-test-main.cfg redhat/default-main.cfg redhat/tids-wrapper redhat/sysconfig.tids
+nobase_dist_pkgdata_DATA=redhat/init redhat/sysconfig redhat/organizations.cfg redhat/tidc-wrapper redhat/trust_router-wrapper redhat/tr-test-internal.cfg redhat/default-internal.cfg redhat/tids-wrapper redhat/sysconfig.tids
 
 if HAVE_SYSTEMD
 systemdsystemunit_DATA = tids.service
 endif
 
 EXTRA_DIST = trust_router.spec common/tests.json schema.sql tids.service \
-       tr/manual.cfg tr/portal.cfg \
+       tr/internal.cfg tr/organizations.cfg \
        redhat/tids.init
diff --git a/common/cfg_test/idp.cfg b/common/cfg_test/idp.cfg
deleted file mode 100644 (file)
index 0bfc87f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "local_organizations": [
-        {"organization_name": "idp.cfg test org",
-            "realms": [
-                {"realm": "A.idp.cfg",
-                    "identity_provider": {
-                        "aaa_servers": ["rad1.A.idp.cfg",
-                                      "rad2.A.idp.cfg"],
-                        "apc": "apc.example.com",
-                        "shared_config": "no"
-                    },
-                    "gss_names": ["gss@example.com"]
-                }
-            ]
-        }
-    ]
-}
similarity index 91%
rename from common/cfg_test/cfg_test.c
rename to common/tests/cfg_test.c
index d8a0117..f487c53 100644 (file)
@@ -51,16 +51,15 @@ static int verify_idp_cfg(TR_CFG *cfg)
 {
   TR_COMM *comm=NULL;
   TR_NAME *name=NULL;
-  TR_APC *apc=NULL;
   TR_IDP_REALM *idp_realm=NULL;
   TR_AAA_SERVER *aaa=NULL;
 
   assert(cfg!=NULL);
 
   /* test the comms attribute */
-  assert(cfg->comms!=NULL);
+  assert(cfg->ctable!=NULL);
   name=tr_new_name("apc.example.com");
-  comm=tr_comm_lookup(cfg->comms, name);
+  comm=tr_comm_table_find_comm(cfg->ctable, name);
   tr_free_name(name);
   assert(comm!=NULL);
 
@@ -69,9 +68,8 @@ static int verify_idp_cfg(TR_CFG *cfg)
   assert(comm->apcs==NULL);
 
   name=tr_new_name("A.idp.cfg");
-  for (idp_realm=comm->idp_realms;
-       (idp_realm!=NULL) && (tr_name_cmp(name, idp_realm->realm_id)!=0);
-       idp_realm=idp_realm->comm_next) { }
+  assert(name!=NULL);
+  idp_realm=tr_comm_find_idp(cfg->ctable, comm, name);
   assert(idp_realm!=NULL);
   assert(idp_realm->shared_config==0);
   assert(idp_realm->origin==TR_REALM_LOCAL);
@@ -107,14 +105,13 @@ static int verify_rp_cfg(TR_CFG *cfg)
   assert(cfg->rp_clients!=NULL);
   assert(cfg->rp_clients->next==NULL);
   assert(cfg->rp_clients->comm_next==NULL);
-  /* need to update next test to use TR_GSS_NAMES structure */
-#if 0
+
+  assert(cfg->rp_clients->gss_names!=NULL);
   for (ii=1; ii<TR_MAX_GSS_NAMES; ii++)
-    assert(cfg->rp_clients->gss_names[ii]==NULL);
-  assert(cfg->rp_clients->gss_names[0]!=NULL);
+    assert(cfg->rp_clients->gss_names->names[ii]==NULL);
+  assert(cfg->rp_clients->gss_names->names[0]!=NULL);
   name=tr_new_name("gss@example.com");
-  assert(tr_name_cmp(name, cfg->rp_clients->gss_names[0])==0);
-#endif
+  assert(tr_name_cmp(name, cfg->rp_clients->gss_names->names[0])==0);
   return 0;
 }
 
diff --git a/common/tests/commtest.c b/common/tests/commtest.c
new file mode 100644 (file)
index 0000000..34153cb
--- /dev/null
@@ -0,0 +1,838 @@
+#include <talloc.h>
+#include <assert.h>
+#include <jansson.h>
+
+#include <tr_apc.h>
+#include <tr_comm.h>
+#include <tr_rp.h>
+#include <trust_router/tr_name.h>
+
+/**********************************************************************/
+/* APC test stuff */
+struct apc_entry {
+  const char *id; /* only allows a single entry for now */
+};
+
+static TR_APC *apc_entry_to_apc(TALLOC_CTX *mem_ctx, struct apc_entry *ae)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC *apcs=NULL;
+
+  apcs=tr_apc_new(tmp_ctx);
+  if (apcs!=NULL) {
+    tr_apc_set_id(apcs, tr_new_name(ae->id));
+    talloc_steal(mem_ctx, apcs);
+  }
+
+  talloc_free(tmp_ctx); 
+  return apcs;
+}
+
+/**********************************************************************/
+/* TR_COMM test stuff */
+
+struct comm_entry {
+  const char *id;
+  TR_COMM_TYPE type;
+  struct apc_entry *apcs;
+};
+
+static TR_COMM *comm_entry_to_comm(TALLOC_CTX *mem_ctx, struct comm_entry *ce)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM *comm=tr_comm_new(tmp_ctx);
+
+  if (comm!=NULL) {
+    tr_comm_set_id(comm, tr_new_name(ce->id));
+    tr_comm_set_type(comm, ce->type);
+    if (ce->apcs!=NULL)
+      tr_comm_set_apcs(comm, apc_entry_to_apc(tmp_ctx, ce->apcs));
+
+    if ((tr_comm_get_id(comm)==NULL) ||
+        ((ce->apcs!=NULL)&&(tr_comm_get_apcs(comm)==NULL)))
+      comm=NULL; /* it's in tmp_ctx, so will be freed */
+    else
+      talloc_steal(mem_ctx, comm);
+  }
+
+  talloc_free(tmp_ctx); 
+  return comm;
+}
+
+static int add_comm_set(TR_COMM_TABLE *ctab, struct comm_entry *entries)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct comm_entry *this=NULL;
+  size_t ii=0;
+  TR_COMM *new=NULL;
+  int rc=-1;
+
+  for (this=entries,ii=0; this->id!=NULL; this++, ii++) {
+    new=comm_entry_to_comm(tmp_ctx, this);
+    if (new==NULL) {
+      printf("Error creating community %u.\n", (unsigned int)ii+1);
+      rc=1;
+      goto cleanup;
+    }
+    tr_comm_table_add_comm(ctab, new);
+  }
+  /* success */
+  rc=0;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static int verify_comm_set(TR_COMM_TABLE *ctab, struct comm_entry *entries)
+{
+  struct comm_entry *this=NULL;
+  TR_COMM *comm=NULL;
+  TR_NAME *this_id=NULL;
+
+  for (this=entries; this->id!=NULL; this++) {
+    this_id=tr_new_name(this->id);
+    comm=tr_comm_table_find_comm(ctab, this_id);
+    tr_free_name(this_id); this_id=NULL;
+    
+    if (comm==NULL) {
+      printf("Error, community %s missing from community table.\n", this->id);
+      return -1;
+    }
+    if (tr_comm_get_type(comm)!=this->type) {
+      printf("Error, community %s has wrong type (was %s, expected %s).\n",
+             this->id,
+             tr_comm_type_to_str(tr_comm_get_type(comm)),
+             tr_comm_type_to_str(this->type));
+      return -1;
+    }
+    /* TODO: verify apcs */
+  }
+  return 0;
+}
+
+/* removes entry n from ctab */
+static int remove_comm_set_member(TR_COMM_TABLE *ctab, struct comm_entry *entries, size_t n)
+{
+  TR_NAME *comm_name=tr_new_name(entries[n].id);
+  TR_COMM *comm=tr_comm_table_find_comm(ctab, comm_name);
+  TR_COMM *comm2=NULL;
+
+  if (comm==NULL) {
+    printf("Can't remove community %s, not in table.\n", entries[n].id);
+    tr_free_name(comm_name);
+    return 1;
+  }
+  
+  tr_comm_table_remove_comm(ctab, comm);
+  comm2=tr_comm_table_find_comm(ctab, comm_name);
+  if (comm2!=NULL) {
+    printf("Community %s still in table after removal.\n", entries[n].id);
+    tr_comm_free(comm);
+    tr_free_name(comm_name);
+    return 2;
+  }
+
+  tr_comm_free(comm);
+  tr_free_name(comm_name);
+  return 0;
+}
+
+/**********************************************************************/
+/* TR_RP_REALM test stuff */
+
+struct rp_realm_entry {
+  const char *id;
+};
+
+static TR_RP_REALM *rp_realm_entry_to_rp_realm(TALLOC_CTX *mem_ctx, struct rp_realm_entry *re)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_RP_REALM *realm=NULL;
+
+  realm=tr_rp_realm_new(tmp_ctx);
+  if (realm!=NULL) {
+    tr_rp_realm_set_id(realm, tr_new_name(re->id));
+    talloc_steal(mem_ctx, realm);
+  }
+
+  talloc_free(tmp_ctx); 
+  return realm;
+}
+
+static int add_rp_realm_set(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct rp_realm_entry *this=NULL;
+  TR_RP_REALM *realm=NULL;
+  int rc=-1;
+
+  for (this=entries; this->id!=NULL; this++) {
+    realm=rp_realm_entry_to_rp_realm(tmp_ctx, this);
+    if (realm==NULL) {
+      printf("Error creating RP realm %s.\n", this->id);
+      rc=1;
+      goto cleanup;
+    }
+    tr_comm_table_add_rp_realm(ctab, realm);
+  }
+  rc=0;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static int verify_rp_realm_set(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries)
+{
+  struct rp_realm_entry *this=NULL;
+  TR_RP_REALM *rp_realm=NULL;
+  TR_NAME *this_id=NULL;
+
+  for (this=entries; this->id!=NULL; this++) {
+    this_id=tr_new_name(this->id);
+    rp_realm=tr_comm_table_find_rp_realm(ctab, this_id);
+    tr_free_name(this_id); this_id=NULL;
+    
+    if (rp_realm==NULL) {
+      printf("Error, RP realm %s missing from community table.\n", this->id);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* removes entry n from ctab */
+static int remove_rp_realm_set_member(TR_COMM_TABLE *ctab, struct rp_realm_entry *entries, size_t n)
+{
+  TR_NAME *rp_realm_name=tr_new_name(entries[n].id);
+  TR_RP_REALM *rp_realm=tr_comm_table_find_rp_realm(ctab, rp_realm_name);
+  TR_RP_REALM *rp_realm2=NULL;
+
+  if (rp_realm==NULL) {
+    printf("Can't remove RP realm %s, not in table.\n", entries[n].id);
+    tr_free_name(rp_realm_name);
+    return 1;
+  }
+  
+  tr_comm_table_remove_rp_realm(ctab, rp_realm);
+  rp_realm2=tr_comm_table_find_rp_realm(ctab, rp_realm_name);
+  if (rp_realm2!=NULL) {
+    printf("RP realm %s still in table after removal.\n", entries[n].id);
+    tr_rp_realm_free(rp_realm);
+    tr_free_name(rp_realm_name);
+    return 2;
+  }
+
+  tr_rp_realm_free(rp_realm);
+  tr_free_name(rp_realm_name);
+  return 0;
+}
+
+/**********************************************************************/
+/* TR_AAA_SERVER test stuff */
+
+struct aaa_entry {
+  const char *hostname; /* only supports one for testing right now */
+};
+
+static TR_AAA_SERVER *aaa_entry_to_aaa_server(TALLOC_CTX *mem_ctx, struct aaa_entry *ae)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_AAA_SERVER *aaa=tr_aaa_server_new(tmp_ctx, tr_new_name(ae->hostname));
+
+  if ((aaa==NULL) || (aaa->hostname==NULL))
+    aaa=NULL;
+  else
+    talloc_steal(mem_ctx, aaa);
+
+  talloc_free(tmp_ctx); 
+  return aaa;
+}
+
+
+/**********************************************************************/
+/* TR_IDP_REALM test stuff */
+
+struct idp_realm_entry {
+  const char *id;
+  struct aaa_entry *aaa_servers;
+  struct apc_entry *apcs;
+};
+
+static TR_IDP_REALM *idp_realm_entry_to_idp_realm(TALLOC_CTX *mem_ctx, struct idp_realm_entry *re)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *realm=NULL;
+
+  realm=tr_idp_realm_new(tmp_ctx);
+  if (realm!=NULL) {
+    tr_idp_realm_set_id(realm, tr_new_name(re->id));
+    realm->aaa_servers=aaa_entry_to_aaa_server(realm, re->aaa_servers);
+    if (realm->aaa_servers==NULL)
+      realm=NULL; /* still in tmp_ctx so will be freed */
+    else {
+      tr_idp_realm_set_apcs(realm, apc_entry_to_apc(tmp_ctx, re->apcs));
+      if (tr_idp_realm_get_apcs(realm)==NULL)
+        realm=NULL;
+    }
+  }
+
+  if (realm!=NULL)
+    talloc_steal(mem_ctx, realm);
+
+  talloc_free(tmp_ctx); 
+  return realm;
+}
+
+static int add_idp_realm_set(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct idp_realm_entry *this=NULL;
+  TR_IDP_REALM *realm=NULL;
+  int rc=-1;
+
+  for (this=entries; this->id!=NULL; this++) {
+    realm=idp_realm_entry_to_idp_realm(tmp_ctx, this);
+    if (realm==NULL) {
+      printf("Error creating IDP realm %s.\n", this->id);
+      rc=1;
+      goto cleanup;
+    }
+    tr_comm_table_add_idp_realm(ctab, realm);
+  }
+  rc=0;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static int verify_idp_realm_set(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries)
+{
+  struct idp_realm_entry *this=NULL;
+  TR_IDP_REALM *idp_realm=NULL;
+  TR_NAME *this_id=NULL;
+
+  for (this=entries; this->id!=NULL; this++) {
+    this_id=tr_new_name(this->id);
+    idp_realm=tr_comm_table_find_idp_realm(ctab, this_id);
+    tr_free_name(this_id); this_id=NULL;
+    
+    if (idp_realm==NULL) {
+      printf("Error, IDP realm %s missing from community table.\n", this->id);
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* removes entry n from ctab */
+static int remove_idp_realm_set_member(TR_COMM_TABLE *ctab, struct idp_realm_entry *entries, size_t n)
+{
+  TR_NAME *idp_realm_name=tr_new_name(entries[n].id);
+  TR_IDP_REALM *idp_realm=tr_comm_table_find_idp_realm(ctab, idp_realm_name);
+  TR_IDP_REALM *idp_realm2=NULL;
+
+  if (idp_realm==NULL) {
+    printf("Can't remove IDP realm %s, not in table.\n", entries[n].id);
+    tr_free_name(idp_realm_name);
+    return 1;
+  }
+  
+  tr_comm_table_remove_idp_realm(ctab, idp_realm);
+  idp_realm2=tr_comm_table_find_idp_realm(ctab, idp_realm_name);
+  if (idp_realm2!=NULL) {
+    printf("IDP realm %s still in table after removal.\n", entries[n].id);
+    tr_idp_realm_free(idp_realm);
+    tr_free_name(idp_realm_name);
+    return 2;
+  }
+
+  tr_idp_realm_free(idp_realm);
+  tr_free_name(idp_realm_name);
+  return 0;
+}
+
+/**********************************************************************/
+/* Community Membership test stuff */
+
+struct comm_memb_entry {
+  TR_REALM_ROLE role;
+  const char *realm_name;
+  const char *comm_name;
+  const char *origin;
+  /* TODO: test provenance */
+};
+
+/* add an existing realm to an existing community (these must
+ * exist in the community table lists) */
+static int add_comm_membership(TR_COMM_TABLE *ctab, struct comm_memb_entry *entry)
+{
+  TR_NAME *comm_name=tr_new_name(entry->comm_name);
+  TR_NAME *realm_name=tr_new_name(entry->realm_name);
+  TR_COMM *comm=tr_comm_table_find_comm(ctab, comm_name);
+  TR_RP_REALM *rp_realm=(entry->role==TR_ROLE_RP)?(tr_comm_table_find_rp_realm(ctab, realm_name)):(NULL);
+  TR_IDP_REALM *idp_realm=(entry->role==TR_ROLE_IDP)?(tr_comm_table_find_idp_realm(ctab, realm_name)):(NULL);
+  json_t *prov=NULL;
+  
+  if ((comm==NULL) || ((rp_realm==NULL)&&(idp_realm==NULL)))
+    return 1;
+
+  prov=json_array();
+  if (entry->origin!=NULL)
+    json_array_append(prov, json_string(entry->origin));
+
+  switch (entry->role) {
+  case TR_ROLE_IDP:
+    tr_comm_add_idp_realm(ctab, comm, idp_realm, 0, prov, NULL); /* Expiry!? */
+    break;
+  case TR_ROLE_RP:
+    tr_comm_add_rp_realm(ctab, comm, rp_realm, 0, prov, NULL); /* Expiry!? */
+    break;
+  default:
+    return 2;
+  }
+  
+  return 0;
+}
+
+static int add_member_set(TR_COMM_TABLE *ctab, struct comm_memb_entry *entries)
+{
+  struct comm_memb_entry *this=NULL;
+
+  for (this=entries; this->role!=TR_ROLE_UNKNOWN; this++) {
+    if (0!=add_comm_membership(ctab, this)) {
+      printf("Error adding %s realm %s to community %s (origin %s).\n",
+             (this->role==TR_ROLE_RP)?"RP":"IDP",
+             this->realm_name,
+             this->comm_name,
+             (this->origin!=NULL)?(this->origin):"null");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int remove_membership(TR_COMM_TABLE *ctab, struct comm_memb_entry *entries, size_t n)
+{
+  TR_NAME *realm_name=tr_new_name(entries[n].realm_name);
+  TR_NAME *comm_name=tr_new_name(entries[n].comm_name);
+  TR_NAME *origin=(entries[n].origin!=NULL)?(tr_new_name(entries[n].origin)):NULL;
+  TR_COMM_MEMB *memb=NULL;
+  int rc=-1;
+
+  switch (entries[n].role) {
+  case TR_ROLE_IDP:
+    memb=tr_comm_table_find_idp_memb_origin(ctab, realm_name, comm_name, origin);
+    break;
+  case TR_ROLE_RP:
+    memb=tr_comm_table_find_rp_memb_origin(ctab, realm_name, comm_name, origin);
+    break;
+  default:
+    rc=1;
+    goto cleanup;
+  }
+
+  if (memb==NULL) {
+    printf("%s realm %s not in comm %s from origin %s, can't remove membership.\n",
+           (entries[n].role==TR_ROLE_RP)?"RP":"IDP",
+           entries[n].realm_name,
+           entries[n].comm_name,
+           (entries[n].origin!=NULL)?(entries[n].origin):"null");
+    rc=2;
+    goto cleanup;
+  }
+  tr_comm_table_remove_memb(ctab, memb);
+  tr_comm_memb_free(memb);
+  rc=0;
+
+cleanup:
+  tr_free_name(realm_name);
+  tr_free_name(comm_name);
+  if (origin!=NULL)
+    tr_free_name(origin);
+  return rc;
+}
+
+/**********************************************************************/
+/* Test data */
+
+struct apc_entry apc_1={ "apc" };
+
+struct comm_entry comm_set_1[]={
+  { "apc", TR_COMM_APC, NULL },
+  { "comm 1", TR_COMM_COI, &apc_1 },
+  { "comm 2", TR_COMM_COI, &apc_1 },
+  { NULL }
+};
+
+struct rp_realm_entry rp_realm_set_1[]={
+  { "rp 1" },
+  { "rp 2" },
+  { "rp 3" },
+  { NULL }
+};
+
+struct aaa_entry aaa_1= { "aaa 1" };
+struct aaa_entry aaa_2= { "aaa 2" };
+struct aaa_entry aaa_3= { "aaa 3" };
+
+struct idp_realm_entry idp_realm_set_1[]={
+  { "idp 1", &aaa_1, &apc_1 },
+  { "idp 2", &aaa_2, &apc_1 },
+  { "idp 3", &aaa_3, &apc_1 },
+  { NULL }
+};
+
+struct comm_memb_entry member_set_1[]={
+  { TR_ROLE_RP, "rp 1", "apc", NULL },
+  { TR_ROLE_RP, "rp 2", "apc", NULL },
+  { TR_ROLE_RP, "rp 3", "apc", NULL },
+  { TR_ROLE_IDP, "idp 1", "apc", NULL },
+  { TR_ROLE_IDP, "idp 2", "apc", NULL },
+  { TR_ROLE_IDP, "idp 3", "apc", NULL },
+  { TR_ROLE_RP, "rp 1", "comm 1", NULL },
+  { TR_ROLE_RP, "rp 2", "comm 1", NULL },
+  { TR_ROLE_RP, "rp 2", "comm 1", "peer 1" },
+  { TR_ROLE_RP, "rp 2", "comm 1", "peer 2" },
+  { TR_ROLE_IDP, "idp 1", "comm 1", NULL },
+  { TR_ROLE_IDP, "idp 1", "comm 1", "peer 1" },
+  { TR_ROLE_IDP, "idp 1", "comm 1", "peer 2" },
+  { TR_ROLE_IDP, "idp 2", "comm 1", NULL },
+  { TR_ROLE_RP, "rp 1", "comm 2", NULL },
+  { TR_ROLE_RP, "rp 2", "comm 2", NULL },
+  { TR_ROLE_RP, "rp 2", "comm 2", "peer 1" },
+  { TR_ROLE_RP, "rp 2", "comm 2", "peer 2" },
+  { TR_ROLE_IDP, "idp 1", "comm 2", NULL },
+  { TR_ROLE_IDP, "idp 1", "comm 2", "peer 1" },
+  { TR_ROLE_IDP, "idp 1", "comm 2", "peer 2" },
+  { TR_ROLE_IDP, "idp 2", "comm 2", NULL },
+  { TR_ROLE_UNKNOWN }
+};
+
+
+/**********************************************************************/
+/* Test routines */
+
+/* the first few tests here insert a few things into the community table (comms,
+ * rp_realms, or idp_realms), then verify that they're all there. They
+ * then remove them in various orders, put them back, try removing
+ * things that are not present, etc. */
+
+static int community_test(void)
+{
+  TALLOC_CTX *mem_ctx=talloc_new(NULL);
+  TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx);
+
+  assert(0==tr_comm_table_size(ctab));
+
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(0==tr_comm_table_size(ctab));
+  
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(0==tr_comm_table_size(ctab));
+  
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(0==tr_comm_table_size(ctab));
+  
+  assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */
+  assert(0==tr_comm_table_size(ctab));
+
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(2==tr_comm_table_size(ctab));
+  assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(0==tr_comm_table_size(ctab));
+  
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(0==tr_comm_table_size(ctab));
+  assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */
+  assert(0==tr_comm_table_size(ctab));
+  
+  /* add communities */
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(3==tr_comm_table_size(ctab));
+  assert(0==verify_comm_set(ctab, comm_set_1));
+
+  /* remove */
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 2));
+  assert(2==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 1));
+  assert(1==tr_comm_table_size(ctab));
+  assert(1==remove_comm_set_member(ctab, comm_set_1, 1)); /* should not be in the table */
+  assert(1==tr_comm_table_size(ctab));
+  assert(0==remove_comm_set_member(ctab, comm_set_1, 0));
+  assert(0==tr_comm_table_size(ctab));
+
+  talloc_free(mem_ctx);
+  return 0;
+}
+
+static int rp_realm_test(void)
+{
+  TALLOC_CTX *mem_ctx=talloc_new(NULL);
+  TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx);
+
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  
+  assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */
+
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==verify_rp_realm_set(ctab, rp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 2));
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1));
+  assert(1==remove_rp_realm_set_member(ctab, rp_realm_set_1, 1)); /* should not be in the table */
+  assert(0==remove_rp_realm_set_member(ctab, rp_realm_set_1, 0));
+
+  talloc_free(mem_ctx);
+  return 0;
+}
+
+static int idp_realm_test(void)
+{
+  TALLOC_CTX *mem_ctx=talloc_new(NULL);
+  TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx);
+
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  
+  assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */
+
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */
+  
+  /* add realms */
+  assert(ctab!=NULL);
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==verify_idp_realm_set(ctab, idp_realm_set_1));
+
+  /* remove */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 2));
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1));
+  assert(1==remove_idp_realm_set_member(ctab, idp_realm_set_1, 1)); /* should not be in the table */
+  assert(0==remove_idp_realm_set_member(ctab, idp_realm_set_1, 0));
+
+  talloc_free(mem_ctx);
+  return 0;
+}
+
+static int membership_test(void)
+{
+  TALLOC_CTX *mem_ctx=talloc_new(NULL);
+  TR_COMM_TABLE *ctab=tr_comm_table_new(mem_ctx);
+  size_t ii=0;
+  size_t size=0;
+
+  assert(ctab!=NULL);
+  assert(0==add_comm_set(ctab, comm_set_1));
+  assert(0==add_rp_realm_set(ctab, rp_realm_set_1));
+  assert(0==add_idp_realm_set(ctab, idp_realm_set_1));
+  assert(0==add_member_set(ctab, member_set_1));
+
+  size=tr_comm_table_size(ctab);
+  tr_comm_table_sweep(ctab);
+  assert(size==tr_comm_table_size(ctab));
+
+  /* now remove memberships */
+  for (ii=0; member_set_1[ii].role!=TR_ROLE_UNKNOWN; ii++) {
+    assert(0==remove_membership(ctab, member_set_1, ii));
+    assert(2==remove_membership(ctab, member_set_1, ii)); /* should not be in the table */
+  }
+
+  assert(NULL==ctab->memberships);
+
+  /* put them back */
+  assert(0==add_member_set(ctab, member_set_1));
+  /* tr_comm_table_print(stdout, ctab); */
+
+  tr_comm_table_sweep(ctab);
+  assert(size==tr_comm_table_size(ctab));
+
+  /* remove in the reverse order */
+  for(; ii>0; ii--) {
+    assert(0==remove_membership(ctab, member_set_1, ii-1));
+    assert(2==remove_membership(ctab, member_set_1, ii-1)); /* should not be in the table */
+    /* tr_comm_table_print(stdout, ctab); */
+  }
+
+  assert(NULL==ctab->memberships);
+
+  assert(size==tr_comm_table_size(ctab));
+  tr_comm_table_sweep(ctab);
+  assert(0==tr_comm_table_size(ctab));
+
+  talloc_free(mem_ctx);
+  return 0;
+}
+
+
+/**********************************************************************/
+/* main */
+int main(void)
+{
+  assert(0==community_test());
+  printf("Community tests passed.\n");
+  assert(0==rp_realm_test());
+  printf("RP realm tests passed.\n");
+  assert(0==idp_realm_test());
+  printf("IDP realm tests passed.\n");
+  assert(0==membership_test());
+  printf("Membership tests passed.\n");
+  return 0;
+}
diff --git a/common/tests/idp.cfg b/common/tests/idp.cfg
new file mode 100644 (file)
index 0000000..cfb1729
--- /dev/null
@@ -0,0 +1,27 @@
+{
+    "communities": [
+      {
+        "apcs": [],
+        "community_id": "apc.example.com",
+        "idp_realms": ["A.idp.cfg"],
+        "rp_realms": ["A.idp.cfg"],
+        "type": "apc"
+      }
+    ],
+    "local_organizations": [
+        {"organization_name": "idp.cfg test org",
+            "realms": [
+                {
+                  "realm": "A.idp.cfg",
+                  "identity_provider": {
+                    "aaa_servers": ["rad1.A.idp.cfg",
+                                    "rad2.A.idp.cfg"],
+                    "apcs": ["apc.example.com"],
+                    "shared_config": "no"
+                  },
+                  "gss_names": ["gss@example.com"]
+                }
+            ]
+        }
+    ]
+}
similarity index 91%
rename from common/mq_test/mq_test.c
rename to common/tests/mq_test.c
index b1c4be7..5c04b37 100644 (file)
@@ -60,7 +60,7 @@ int main(void)
   mq->notify_cb_arg=mq_name;
 
   msg1=tr_mq_msg_new(NULL,"Message 1", TR_MQ_PRIO_NORMAL);
-  assert(asprintf((char **)&(msg2->p), "First message.\n")!=-1);
+  assert(asprintf((char **)&(msg1->p), "First message.\n")!=-1);
   msg1->p_free=free;
   tr_mq_add(mq, msg1);
   assert(mq->head==msg1);
@@ -76,7 +76,7 @@ int main(void)
   assert(mq->tail==msg2);
   assert(msg2->next==NULL);
 
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==msg1);
   assert(mq->head==msg2);
   assert(mq->tail==msg2);
@@ -88,7 +88,7 @@ int main(void)
     printf("no message to pop\n");
   
   msg3=tr_mq_msg_new(NULL, "Message 3", TR_MQ_PRIO_NORMAL);
-  assert(asprintf("%s",(char **)&(msg3->p), "Third message.\n")!=-1);
+  assert(asprintf((char **)&(msg3->p), "%s", "Third message.\n")!=-1);
   msg3->p_free=free;
   tr_mq_add(mq, msg3);
   assert(mq->head==msg2);
@@ -96,7 +96,7 @@ int main(void)
   assert(msg2->next==msg3);
   assert(msg3->next==NULL);
 
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==msg2);
   assert(mq->head==msg3);
   assert(mq->tail==msg3);
@@ -107,7 +107,7 @@ int main(void)
   } else
     printf("no message to pop\n");
   
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==msg3);
   assert(mq->head==NULL);
   assert(mq->tail==NULL);
@@ -117,7 +117,7 @@ int main(void)
   } else
     printf("no message to pop\n");
   
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==NULL);
   assert(mq->head==NULL);
   assert(mq->tail==NULL);
@@ -128,14 +128,14 @@ int main(void)
     printf("no message to pop\n");
 
   msg4=tr_mq_msg_new(NULL, "Message 4", TR_MQ_PRIO_NORMAL);
-  assert(asprintf("%s",(char **)&(msg4->p), "Fourth message.\n")!=-1);
+  assert(asprintf((char **)&(msg4->p), "%s", "Fourth message.\n")!=-1);
   msg4->p_free=free;
   tr_mq_add(mq, msg4);
   assert(mq->head==msg4);
   assert(mq->tail==msg4);
   assert(msg4->next==NULL);
 
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==msg4);
   assert(mq->head==NULL);
   assert(mq->tail==NULL);
@@ -145,7 +145,7 @@ int main(void)
   } else
     printf("no message to pop\n");
   
-  msg=tr_mq_pop(mq);
+  msg=tr_mq_pop(mq, NULL);
   assert(msg==NULL);
   assert(mq->head==NULL);
   assert(mq->tail==NULL);
@@ -157,5 +157,6 @@ int main(void)
 
   tr_mq_free(mq);
 
+  printf("success\n");
   return 0;
 }
similarity index 89%
rename from common/mq_test/thread_test.c
rename to common/tests/thread_test.c
index 0e5aeb4..14aac6f 100644 (file)
@@ -39,6 +39,7 @@
 #include <talloc.h>
 #include <time.h>
 #include <errno.h>
+#include <assert.h>
 
 #include <tr_mq.h>
 
@@ -49,21 +50,21 @@ struct thread_data {
   char *label;
 };
 
-TR_MQ_MSG *make_msg(label, n)
+static TR_MQ_MSG *make_msg(char *label, int n)
 {
   TR_MQ_MSG *msg=NULL;
   msg=tr_mq_msg_new(NULL, "Message", TR_MQ_PRIO_NORMAL);
-  asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n);
+  assert(-1!=asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n));
   msg->p_free=free;
   return msg;
 }
 
-void *thread_start(void *arg)
+static void *thread_start(void *arg)
 {
   TR_MQ *mq=((struct thread_data *)arg)->mq;
   int n_msgs=((struct thread_data *)arg)->n_msgs;
   useconds_t msg_dly=((struct thread_data *)arg)->msg_dly;
-  const char *label=((struct thread_data *)arg)->label;
+  char *label=((struct thread_data *)arg)->label;
   
   while (n_msgs>=0) {
     usleep(msg_dly);
@@ -80,7 +81,7 @@ struct message_data {
   int ready;
 };
 
-void handle_messages(TR_MQ *mq, void *arg)
+static void handle_messages(TR_MQ *mq, void *arg)
 {
   struct message_data *status=(struct message_data *)arg;
   pthread_mutex_lock(&(status->lock));
@@ -89,12 +90,12 @@ void handle_messages(TR_MQ *mq, void *arg)
   pthread_mutex_unlock(&(status->lock));
 }
 
-void output_messages(TR_MQ *mq)
+static void output_messages(TR_MQ *mq)
 {
   TR_MQ_MSG *msg=NULL;
 
   printf("\n* handle_messages notified of new messages in queue.\n");
-  for (msg=tr_mq_pop(mq); msg!=NULL; msg=tr_mq_pop(mq)) {
+  for (msg=tr_mq_pop(mq, NULL); msg!=NULL; msg=tr_mq_pop(mq, NULL)) {
     printf("  > %s\n", (char *)msg->p);
     tr_mq_msg_free(msg);
   }
@@ -128,7 +129,7 @@ int main(void)
     thread_data[ii].mq=mq;
     thread_data[ii].msg_dly=dly[ii];
     thread_data[ii].n_msgs=10;
-    asprintf(&(thread_data[ii].label), "thread %d", ii+1);
+    assert(-1!=asprintf(&(thread_data[ii].label), "thread %d", ii+1));
     pthread_create(&(thread[ii]), NULL, thread_start, &thread_data[ii]);
     printf("%s started.\n", thread_data[ii].label);
   }
@@ -153,5 +154,6 @@ int main(void)
   for (ii=0; ii<N_THREADS; ii++)
     pthread_join(thread[ii], NULL);
 
+  printf("success\n");
   return 0;
 }
index b810fd8..9fa9294 100644 (file)
@@ -36,6 +36,7 @@
 
 #include <trust_router/tr_name.h>
 #include <tr_apc.h>
+#include <tr_debug.h>
 
 static int tr_apc_destructor(void *obj)
 {
@@ -71,7 +72,8 @@ static TR_APC *tr_apc_tail(TR_APC *apc)
   return apc;
 }
 
-TR_APC *tr_apc_add(TR_APC *head, TR_APC *new)
+/* do not call this directly, use the tr_apc_add() macro */
+TR_APC *tr_apc_add_func(TR_APC *head, TR_APC *new)
 {
   if (head==NULL)
     head=new;
@@ -86,10 +88,39 @@ TR_APC *tr_apc_add(TR_APC *head, TR_APC *new)
 }
 
 /* does not copy next pointer */
-TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc)
+TR_APC *tr_apc_dup_one(TALLOC_CTX *mem_ctx, TR_APC *apc)
 {
   TR_APC *new=tr_apc_new(mem_ctx);
-  tr_apc_set_id(new, tr_apc_dup_id(apc));
+  if (new!=NULL) 
+    tr_apc_set_id(new, tr_apc_dup_id(apc));
+  return new;
+}
+
+/* copies next pointer */
+TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC *cur=NULL;
+  TR_APC *new=NULL;
+
+  if (apc==NULL)
+    return NULL;
+
+  if (NULL==(new=tr_apc_dup_one(tmp_ctx, apc)))
+    return NULL;
+  
+  for (cur=new,apc=apc->next; apc!=NULL; cur=cur->next,apc=apc->next) {
+    cur->next=tr_apc_dup_one(new, apc);
+    if (cur->next==NULL) {
+      new=NULL;
+      goto cleanup;
+    }
+  }
+
+  talloc_steal(mem_ctx, new);
+
+cleanup:
+  talloc_free(tmp_ctx);
   return new;
 }
 
@@ -107,7 +138,7 @@ TR_NAME *tr_apc_get_id(TR_APC *apc)
 
 TR_NAME *tr_apc_dup_id(TR_APC *apc)
 {
-  return tr_dup_name(apc->id);;
+  return tr_dup_name(apc->id);
 }
 
 
@@ -115,3 +146,48 @@ char *tr_apc_to_str(TALLOC_CTX *mem_ctx, TR_APC *apc)
 {
   return talloc_strndup(mem_ctx, apc->id->buf, apc->id->len);
 }
+
+TR_APC_ITER *tr_apc_iter_new(TALLOC_CTX *mem_ctx)
+{
+  return talloc(mem_ctx, TR_APC_ITER);
+}
+
+TR_APC *tr_apc_iter_first(TR_APC_ITER *iter, TR_APC *apc)
+{
+  *iter=apc;
+  return *iter;
+}
+
+TR_APC *tr_apc_iter_next(TR_APC_ITER *iter)
+{
+  (*iter)=(*iter)->next;
+  return *iter;
+}
+
+void tr_apc_iter_free(TR_APC_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+/* 1 on match, 0 on no match, -1 on error */
+int tr_apc_in_common(TR_APC *one, TR_APC *two)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC_ITER *i_one=tr_apc_iter_new(tmp_ctx);
+  TR_APC_ITER *i_two=tr_apc_iter_new(tmp_ctx);
+  TR_APC *cur_one=NULL;
+  TR_APC *cur_two=NULL;
+
+  if ((i_one==NULL) || (i_two==NULL)) {
+    tr_err("tr_apc_in_common: unable to allocate iterators.");
+    talloc_free(tmp_ctx);
+    return -1;
+  }
+  for (cur_one=tr_apc_iter_first(i_one, one); cur_one!=NULL; cur_one=tr_apc_iter_next(i_one)) {
+    for (cur_two=tr_apc_iter_first(i_two, two); cur_two!=NULL; cur_two=tr_apc_iter_next(i_two)) {
+      if (cur_one==cur_two)
+        return 1;
+    }
+  }
+  return 0;
+}
index fe4476a..73c8023 100644 (file)
  *
  */
 
+#include <stdio.h>
+#include <jansson.h>
 #include <talloc.h>
+#include <sys/time.h>
 
 #include <tr_rp.h>
+#include <tr_idp.h>
 #include <trust_router/tr_name.h>
 #include <tr_comm.h>
 #include <tr_debug.h>
 
+
 static int tr_comm_destructor(void *obj)
 {
   TR_COMM *comm=talloc_get_type_abort(obj, TR_COMM);
   if (comm->id!=NULL)
     tr_free_name(comm->id);
+  if (comm->owner_realm!=NULL)
+    tr_free_name(comm->owner_realm);
+  if (comm->owner_contact!=NULL)
+    tr_free_name(comm->owner_contact);
   return 0;
 }
 
@@ -55,8 +64,10 @@ TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx)
     comm->id=NULL;
     comm->type=TR_COMM_UNKNOWN;
     comm->apcs=NULL;
-    comm->idp_realms=NULL;
-    comm->rp_realms=NULL;
+    comm->owner_realm=NULL;
+    comm->owner_contact=NULL;
+    comm->expiration_interval=0;
+    comm->refcount=0;
     talloc_set_destructor((void *)comm, tr_comm_destructor);
   }
   return comm;
@@ -67,30 +78,215 @@ void tr_comm_free(TR_COMM *comm)
   talloc_free(comm);
 }
 
+void tr_comm_set_id(TR_COMM *comm, TR_NAME *id)
+{
+  if (comm->id != NULL)
+    tr_free_name(comm->id);
+  comm->id=id;
+}
+
+void tr_comm_incref(TR_COMM *comm)
+{
+  comm->refcount++;
+}
+
+void tr_comm_decref(TR_COMM *comm)
+{
+  if (comm->refcount>0)
+    comm->refcount--;
+}
+
+void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc)
+{
+  if (comm->apcs!=NULL)
+    tr_apc_free(comm->apcs);
+  comm->apcs=apc;
+  talloc_steal(comm, apc);
+}
+
+TR_APC *tr_comm_get_apcs(TR_COMM *comm)
+{
+  return comm->apcs;
+}
+
+TR_NAME *tr_comm_get_id(TR_COMM *comm)
+{
+  return comm->id;
+}
+
+TR_NAME *tr_comm_dup_id(TR_COMM *comm)
+{
+  return tr_dup_name(comm->id);
+}
+
+void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type)
+{
+  comm->type=type;
+}
+
+TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm)
+{
+  return comm->type;
+}
+
+void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm)
+{
+  if (comm->owner_realm!=NULL)
+    tr_free_name(comm->owner_realm);
+  comm->owner_realm=realm;
+}
+
+TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm)
+{
+  return comm->owner_realm;
+}
+
+TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm)
+{
+  return tr_dup_name(comm->owner_realm);
+}
+
+void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact)
+{
+  if (comm->owner_contact != NULL)
+    tr_free_name(comm->owner_contact);
+  comm->owner_contact=contact;
+}
+
+TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm)
+{
+  return comm->owner_contact;
+}
+
+TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm)
+{
+  return tr_dup_name(comm->owner_contact);
+}
+
+unsigned int tr_comm_get_refcount(TR_COMM *comm)
+{
+  return comm->refcount;
+}
+
+/* 0 if equivalent, nonzero if different, only considers
+ * nhops last hops (nhops==0 means consider all, nhops==1
+ * only considers last hop) */
+static int tr_comm_memb_provenance_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2, int nhops)
+{
+  size_t ii;
+
+  if ((m1->provenance==NULL) || (m2->provenance==NULL))
+    return m1->provenance!=m2->provenance; /* return 0 if both null, 1 if only one null */
+
+  if (json_array_size(m1->provenance)!=json_array_size(m2->provenance))
+    return 1;
+
+  if (nhops==0)
+    nhops=json_array_size(m1->provenance); /* same as size(m2->provenance) */
+
+  for (ii=0; ii<json_array_size(m1->provenance); ii++) {
+    if (0==strcmp(json_string_value(json_array_get(m1->provenance, ii)),
+                  json_string_value(json_array_get(m2->provenance, ii)))) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+/* Accepts an update that either came from the same peer as the previous
+ * origin, has a shorter provenance list, or can replace an expired
+ * membership. Otherwise keeps the existing one.
+ * On replacement, frees the old member and moves new member to ctab's
+ * context. Caller should not free newmemb except by freeing its original
+ * context. */
+static void tr_comm_add_if_shorter(TR_COMM_TABLE *ctab, TR_COMM_MEMB *existing, TR_COMM_MEMB *newmemb)
+{
+  int accept=0;
+
+  if (existing==NULL) {
+    /* not in the table */
+    tr_comm_table_add_memb(ctab, newmemb);
+  } else {
+    if (0==tr_comm_memb_provenance_cmp(existing, newmemb, 1))
+      accept=1; /* always accept a replacement from the same peer */
+    else if (tr_comm_memb_provenance_len(newmemb) < tr_comm_memb_provenance_len(existing))
+      accept=1; /* accept a shorter provenance */
+    else if (existing->times_expired>0)
+      accept=1;
+    else
+      accept=0;
+
+    if (accept) {
+      tr_comm_table_remove_memb(ctab, existing);
+      tr_comm_memb_free(existing);
+      tr_comm_table_add_memb(ctab, newmemb);
+    }
+  }
+}
+
 /* does not take responsibility for freeing IDP realm */
-void tr_comm_add_idp_realm(TR_COMM *comm, TR_IDP_REALM *realm)
+void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab,
+                           TR_COMM *comm,
+                           TR_IDP_REALM *realm,
+                           unsigned int interval,
+                           json_t *provenance,
+                           struct timespec *expiry)
 {
-  TR_IDP_REALM *cur=NULL;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
+  TR_COMM_MEMB *existing=NULL;
 
-  if (comm->idp_realms==NULL)
-    comm->idp_realms=realm;
-  else {
-    for (cur=comm->idp_realms; cur->comm_next!=NULL; cur=cur->comm_next) { }
-    cur->comm_next=realm;
+  if (newmemb==NULL) {
+    tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
+    talloc_free(tmp_ctx);
+    return;
   }
+
+  tr_comm_memb_set_idp_realm(newmemb, realm);
+  tr_comm_memb_set_comm(newmemb, comm);
+  tr_comm_memb_set_interval(newmemb, interval);
+  tr_comm_memb_set_provenance(newmemb, provenance);
+  tr_comm_memb_set_expiry(newmemb, expiry);
+
+  existing=tr_comm_table_find_idp_memb_origin(ctab,
+                                              tr_idp_realm_get_id(realm),
+                                              tr_comm_get_id(comm),
+                                              tr_comm_memb_get_origin(newmemb));
+  tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
+
+  talloc_free(tmp_ctx);
 }
 
 /* does not take responsibility for freeing RP realm */
-void tr_comm_add_rp_realm(TR_COMM *comm, TR_RP_REALM *realm)
+void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab,
+                          TR_COMM *comm,
+                          TR_RP_REALM *realm,
+                          unsigned int interval,
+                          json_t *provenance,
+                          struct timespec *expiry)
 {
-  TR_RP_REALM *cur=NULL;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
+  TR_COMM_MEMB *existing=NULL;
 
-  if (comm->rp_realms==NULL)
-    comm->rp_realms=realm;
-  else {
-    for (cur=comm->rp_realms; cur->next!=NULL; cur=cur->next) { }
-    cur->next=realm;
+  if (newmemb==NULL) {
+    tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
+    talloc_free(tmp_ctx);
+    return;
   }
+
+  tr_comm_memb_set_rp_realm(newmemb, realm);
+  tr_comm_memb_set_comm(newmemb, comm);
+  tr_comm_memb_set_interval(newmemb, interval);
+  tr_comm_memb_set_provenance(newmemb, provenance);
+  tr_comm_memb_set_expiry(newmemb, expiry);
+
+  existing=tr_comm_table_find_rp_memb_origin(ctab,
+                                             tr_rp_realm_get_id(realm),
+                                             tr_comm_get_id(comm),
+                                             tr_comm_memb_get_origin(newmemb));
+  tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
+  talloc_free(tmp_ctx);
 }
 
 static TR_COMM *tr_comm_tail(TR_COMM *comm)
@@ -105,9 +301,10 @@ static TR_COMM *tr_comm_tail(TR_COMM *comm)
 
 /* All list members are in the talloc context of the head.
  * This will require careful thought if entries are ever removed
- * or shuffled between lists. 
- * Call like comms=tr_comm_add(comms, new_comm); */
-TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new)
+ * Call like comms=tr_comm_add_func(comms, new_comm);
+ * or just use the tr_comm_add(comms, new) macro. */
+#define tr_comm_add(comms, new) ((comms)=tr_comm_add_func((comms), (new)))
+static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new)
 {
   if (comms==NULL)
     comms=new;
@@ -121,50 +318,1133 @@ TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new)
   return comms;
 }
 
-TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm)
+/* Guarantees comm is not in the list, not an error if it was't there.
+ * Does not free the removed element, nor change its talloc context. */
+#define tr_comm_remove(comms, c) ((comms)=tr_comm_remove_func((comms), (c)))
+static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
+{
+  TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */
+  TR_COMM *this=NULL;
+
+  if (comms==NULL)
+    return NULL;
+
+  if (comms==remove) {
+    /* if we're removing the head, put the next element (if present) into the context
+     * the list head was in. */
+    comms=comms->next;
+    if (comms!=NULL) {
+      talloc_steal(list_ctx, comms);
+      /* now put all the other elements in the context of the list head */
+      for (this=comms->next; this!=NULL; this=this->next)
+        talloc_steal(comms, this);
+    }
+  } else {
+    /* not removing the head; no need to play with contexts */
+    for (this=comms; this->next!=NULL; this=this->next) {
+      if (this->next==remove) {
+        this->next=remove->next;
+        break;
+      }
+    }
+  }
+  return comms;
+}
+
+/* remove any with zero refcount 
+ * Call via macro. */
+#define tr_comm_sweep(head) ((head)=tr_comm_sweep_func((head)))
+static TR_COMM *tr_comm_sweep_func(TR_COMM *head)
 {
-  TR_IDP_REALM *idp;
+  TR_COMM *comm=NULL;
+  TR_COMM *old_next=NULL;
 
-  if ((!comm) || (!idp_realm)) {
+  if (head==NULL)
     return NULL;
+
+  while ((head!=NULL) && (head->refcount==0)) {
+    comm=head; /* keep a pointer so we can remove it */
+    tr_comm_remove(head, comm); /* use this to get talloc contexts right */
+    tr_comm_free(comm);
   }
 
-  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 IdP %s in community %s.", idp_realm->buf, comm->id->buf);
-      return idp;
+  if (head==NULL)
+    return NULL;
+
+  /* will not remove the head here, that has already been done */
+  for (comm=head; comm->next!=NULL; comm=comm->next) {
+    if (comm->next->refcount==0) {
+      old_next=comm->next;
+      tr_comm_remove(head, comm->next); /* changes comm->next */
+      tr_comm_free(old_next);
     }
   }
-  /* if we didn't find one, return NULL */ 
+
+  return head;
+}
+
+TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_ITER *iter=NULL;
+  TR_IDP_REALM *this_idp=NULL;
+
+  if ((NULL==ctab) || (NULL==comm) || (NULL==idp_realm)) {
+    talloc_free(tmp_ctx);
+    return NULL;
+  }
+
+  iter=tr_comm_iter_new(tmp_ctx);
+  for (this_idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
+       this_idp!=NULL;
+       this_idp=tr_idp_realm_iter_next(iter)) {
+    if (0==tr_name_cmp(idp_realm, tr_idp_realm_get_id(this_idp))) {
+      tr_debug("tr_comm_find_idp: Found IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
+      talloc_free(tmp_ctx);
+      return this_idp;
+    }
+  }
+  tr_debug("tr_comm_find_idp: Unable to find IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
+  talloc_free(tmp_ctx);
   return NULL;
 }
 
-TR_RP_REALM *tr_find_comm_rp (TR_COMM *comm, TR_NAME *rp_realm)
+TR_RP_REALM *tr_comm_find_rp (TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm)
 {
-  TR_RP_REALM *rp;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_ITER *iter=NULL;
+  TR_RP_REALM *this_rp=NULL;
 
-  if ((!comm) || (!rp_realm)) {
+  if ((NULL==ctab) || (NULL==comm) || (NULL==rp_realm)) {
+    talloc_free(tmp_ctx);
     return NULL;
   }
 
-  for (rp = comm->rp_realms; NULL != rp; rp = rp->next) {
-    if (!tr_name_cmp (rp_realm, rp->realm_name)) {
-      tr_debug("tr_find_comm_rp: Found RP %s in community %s.", rp_realm->buf, comm->id->buf);
-      return rp;
+  iter=tr_comm_iter_new(tmp_ctx);
+  for (this_rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
+       this_rp!=NULL;
+       this_rp=tr_rp_realm_iter_next(iter)) {
+    if (0==tr_name_cmp(rp_realm, tr_rp_realm_get_id(this_rp))) {
+      tr_debug("tr_comm_find_rp: Found RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
+      talloc_free(tmp_ctx);
+      return this_rp;
     }
   }
-  /* if we didn't find one, return NULL */ 
+  tr_debug("tr_comm_find_rp: Unable to find RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
+  talloc_free(tmp_ctx);
   return NULL;
 }
 
-TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) 
+static TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) 
 {
   TR_COMM *cfg_comm = NULL;
 
   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)))
+    if (0==tr_name_cmp(cfg_comm->id, comm_name))
       return cfg_comm;
   }
   return NULL;
 }
+
+TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx)
+{
+  TR_COMM_ITER *iter=talloc(mem_ctx, TR_COMM_ITER);
+  if (iter!=NULL) {
+    iter->cur_comm=NULL;
+    iter->cur_memb=NULL;
+    iter->cur_orig_head=NULL;
+    iter->match=NULL;
+    iter->realm=NULL;
+  }
+  return iter;
+}
+
+void tr_comm_iter_free(TR_COMM_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+
+TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
+{
+  iter->match=realm;
+
+  /* find memberships for this realm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter)
+{
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+/* iterate only over RPs */
+TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
+{
+  iter->match=realm;
+
+  /* find memberships for this realm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter)
+{
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+/* iterate only over IDPs */
+TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
+{
+  iter->match=realm;
+
+  /* find memberships for this realm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter)
+{
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
+      return tr_comm_memb_get_comm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+static TR_REALM *tr_realm_new(TALLOC_CTX *mem_ctx)
+{
+  TR_REALM *realm=talloc(mem_ctx, TR_REALM);
+  if (realm!=NULL) {
+    realm->role=TR_ROLE_UNKNOWN;
+    realm->rp=NULL;
+    realm->idp=NULL;
+  }
+  return realm;
+}
+
+static void tr_realm_free(TR_REALM *realm)
+{
+  talloc_free(realm);
+}
+
+static void tr_realm_set_rp(TR_REALM *realm, TR_RP_REALM *rp)
+{
+  if (realm->idp!=NULL)
+    realm->idp=NULL;
+  realm->role=TR_ROLE_RP;
+  realm->rp=rp;
+}
+
+static void tr_realm_set_idp(TR_REALM *realm, TR_IDP_REALM *idp)
+{
+  if (realm->rp!=NULL)
+    realm->rp=NULL;
+  realm->role=TR_ROLE_IDP;
+  realm->idp=idp;
+}
+
+TR_NAME *tr_realm_get_id(TR_REALM *realm)
+{
+  switch (realm->role) {
+  case TR_ROLE_RP:
+    return tr_rp_realm_get_id(realm->rp);
+  case TR_ROLE_IDP:
+    return tr_idp_realm_get_id(realm->idp);
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TR_NAME *tr_realm_dup_id(TR_REALM *realm)
+{
+  return tr_dup_name(tr_realm_get_id(realm));
+}
+
+/* Iterate over either sort of realm. Do not free the TR_REALM returned. It becomes
+ * undefined/invalid after the next operation affecting the iterator. */
+TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
+{
+  iter->match=comm;
+  if (iter->realm==NULL)
+    iter->realm=tr_realm_new(iter);
+  if (iter->realm==NULL)
+    return NULL;
+
+  /* find memberships for this comm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if (0==tr_name_cmp(iter->match,
+                       tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
+      /* found a match, determine whether it's an rp realm or an idp realm */
+      if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
+        tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
+      else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
+        tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
+      else {
+        if (iter->realm!=NULL)
+          tr_realm_free(iter->realm);
+        iter->realm=NULL;
+      }
+      return iter->realm;
+    }
+  }
+  if (iter->realm!=NULL)
+    tr_realm_free(iter->realm);
+  iter->realm=NULL;
+  return NULL;
+}
+
+TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter)
+{
+  if (iter->realm==NULL)
+    return NULL;
+
+  /* find memberships for this comm */
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if (0==tr_name_cmp(iter->match,
+                       tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
+      /* found a match, determine whether it's an rp realm or an idp realm */
+      if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
+        tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
+      else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
+        tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
+      else {
+        if (iter->realm!=NULL)
+          tr_realm_free(iter->realm);
+        iter->realm=NULL;
+      }
+      return iter->realm;
+    }
+  }
+  if (iter->realm!=NULL)
+    tr_realm_free(iter->realm);
+  iter->realm=NULL;
+  return NULL;
+}
+
+TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
+{
+  iter->match=comm;
+
+  /* find memberships for this comm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
+      return tr_comm_memb_get_rp_realm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter)
+{
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
+      return tr_comm_memb_get_rp_realm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
+{
+  iter->match=comm;
+
+  /* find memberships for this comm */
+  for (iter->cur_memb=ctab->memberships;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
+      return tr_comm_memb_get_idp_realm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter)
+{
+  for (iter->cur_memb=iter->cur_memb->next;
+       iter->cur_memb!=NULL;
+       iter->cur_memb=iter->cur_memb->next) {
+    if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
+        (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
+      return tr_comm_memb_get_idp_realm(iter->cur_memb);
+  }
+  return NULL;
+}
+
+/* iterators for all communities in a table */
+TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
+{
+  iter->cur_comm=ctab->comms;
+  return iter->cur_comm;
+}
+
+TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *iter)
+{
+  return iter->cur_comm=iter->cur_comm->next;
+}
+
+const char *tr_comm_type_to_str(TR_COMM_TYPE type)
+{
+  const char *s=NULL;
+  switch(type) {
+  case TR_COMM_UNKNOWN:
+    s="unknown";
+    break;
+  case TR_COMM_APC:
+    s="apc";
+    break;
+  case TR_COMM_COI:
+    s="coi";
+    break;
+  default:
+    s="invalid";
+  }
+  return s;
+}
+
+/* iterate along the origin list for this member */
+TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb)
+{
+  iter->cur_memb=memb;
+  return iter->cur_memb;
+}
+
+TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
+{
+  if (iter->cur_memb!=NULL)
+    iter->cur_memb=iter->cur_memb->origin_next;
+  return iter->cur_memb;
+}
+
+
+/* iterate over all memberships in the table */
+TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
+{
+  iter->cur_memb=ctab->memberships;
+  iter->cur_orig_head=ctab->memberships;
+  return iter->cur_memb;
+}
+
+TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter)
+{
+  if (iter->cur_memb->next==NULL) {
+    if (iter->cur_orig_head->next==NULL) {
+      /* we're done */
+      return NULL;
+    } else {
+      iter->cur_memb=iter->cur_orig_head->next;
+      iter->cur_orig_head=iter->cur_orig_head->next;
+    }
+  } else {
+    iter->cur_memb=iter->cur_memb->origin_next;
+  }
+  return iter->cur_memb;
+}
+
+
+TR_COMM_TYPE tr_comm_type_from_str(const char *s)
+{
+  if (strcmp(s, "apc")==0)
+    return TR_COMM_APC;
+  if (strcmp(s,"coi")==0)
+    return TR_COMM_COI;
+  return TR_COMM_UNKNOWN;
+}
+
+
+static int tr_comm_memb_destructor(void *obj)
+{
+  TR_COMM_MEMB *memb=talloc_get_type_abort(obj, TR_COMM_MEMB);
+  if (memb->origin!=NULL)
+    tr_free_name(memb->origin);
+
+  if (memb->rp!=NULL)
+    tr_rp_realm_decref(memb->rp);
+  if (memb->idp!=NULL)
+    tr_idp_realm_decref(memb->idp);
+  if (memb->comm!=NULL)
+    tr_comm_decref(memb->comm);
+  if (memb->provenance!=NULL)
+    json_decref(memb->provenance);
+  return 0;
+}
+
+TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
+{
+  TR_COMM_MEMB *memb=talloc(mem_ctx, TR_COMM_MEMB);
+  if (memb!=NULL) {
+    memb->next=NULL;
+    memb->origin_next=NULL;
+    memb->idp=NULL;
+    memb->rp=NULL;
+    memb->comm=NULL;
+    memb->origin=NULL;
+    memb->provenance=NULL;
+    memb->interval=0;
+    memb->triggered=0;
+    memb->times_expired=0;
+    memb->expiry=talloc(memb, struct timespec);
+    if (memb->expiry==NULL) {
+      talloc_free(memb);
+      return NULL;
+    }
+    *(memb->expiry)=(struct timespec){0,0};
+    talloc_set_destructor((void *)memb, tr_comm_memb_destructor);
+  }
+  return memb;
+}
+
+void tr_comm_memb_free(TR_COMM_MEMB *memb)
+{
+  talloc_free(memb);
+}
+
+/* Returns 0 if they are the same, nonzero if they differ.
+ * Ignores expiry, triggered, and times_expired, next pointers */
+int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2)
+{
+  if ((m1->idp==m2->idp) &&
+      (m1->rp==m2->rp) &&
+      (m1->comm==m2->comm) &&
+      (tr_comm_memb_provenance_cmp(m1, m2, 0)==0) &&
+      (m1->interval==m2->interval))
+    return 0;
+  return 1;
+}
+
+TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb)
+{
+  if (memb->rp!=NULL)
+    return TR_ROLE_RP;
+  if (memb->idp!=NULL)
+    return TR_ROLE_IDP;
+  return TR_ROLE_UNKNOWN;
+}
+
+void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm)
+{
+  if (memb->idp!=NULL) {
+    tr_idp_realm_decref(memb->idp);
+    memb->idp=NULL;
+  }
+  if (memb->rp!=NULL)
+    tr_rp_realm_decref(memb->rp);
+
+
+  memb->rp=realm;
+  tr_rp_realm_incref(realm);
+}
+
+TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb)
+{
+  return memb->rp;
+}
+
+void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm)
+{
+  if (memb->rp!=NULL) {
+    tr_rp_realm_decref(memb->rp);
+    memb->rp=NULL;
+  }
+  if (memb->idp!=NULL)
+    tr_idp_realm_decref(memb->idp);
+
+  memb->idp=realm;
+  tr_idp_realm_incref(realm);
+}
+
+TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb)
+{
+  return memb->idp;
+}
+
+void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm)
+{
+  if (memb->comm!=NULL)
+    tr_comm_decref(memb->comm);
+  memb->comm=comm;
+  tr_comm_incref(comm);
+}
+
+TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb)
+{
+  return memb->comm;
+}
+
+static void tr_comm_memb_set_origin(TR_COMM_MEMB *memb, TR_NAME *origin)
+{
+  if (memb->origin!=NULL)
+    tr_free_name(memb->origin);
+  memb->origin=origin;
+}
+
+TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb)
+{
+  return memb->origin;
+}
+
+TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb)
+{
+  if (memb->origin!=NULL)
+    return tr_dup_name(memb->origin);
+  return NULL;
+}
+
+json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb)
+{
+  if (memb!=NULL)
+    return memb->provenance;
+  return NULL;
+}
+
+void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
+{
+  const char *s=NULL;
+
+  if (memb->provenance)
+    json_decref(memb->provenance);
+
+  memb->provenance=prov;
+  if (prov!=NULL) {
+    json_incref(prov);
+
+    /* next line sets origin to NULL if provenance is empty because jansson
+     * routines return NULL on error */
+    s=json_string_value(json_array_get(prov, 0));
+    if (s==NULL)
+      tr_comm_memb_set_origin(memb, NULL);
+    else
+      memb->origin=tr_new_name(s);
+  } else {
+    tr_comm_memb_set_origin(memb, NULL);
+  }
+}
+
+void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop)
+{
+  if (memb->provenance==NULL) {
+    memb->provenance=json_array();
+    if (memb->provenance==NULL) {
+      tr_err("tr_comm_memb_add_to_provenance: unable to allocate provenance list.");
+      return;
+    }
+    /* this is the first entry in the provenance, so it is the origin */
+    tr_comm_memb_set_origin(memb,tr_dup_name(hop));
+    if (memb->origin==NULL) {
+      tr_err("tr_comm_memb_add_to_provenance: unable to allocate origin.");
+      json_decref(memb->provenance);
+      memb->provenance=NULL;
+      return;
+    }
+  }
+  if (0!=json_array_append_new(memb->provenance, tr_name_to_json_string(hop)))
+    tr_err("tr_comm_memb_add_to_provenance: unable to extend provenance list.");
+}
+
+size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb)
+{
+  if (memb->provenance==NULL)
+    return 0;
+  return json_array_size(memb->provenance);
+}
+
+void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval)
+{
+  memb->interval=interval;
+}
+
+unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb)
+{
+  return memb->interval;
+}
+
+void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time)
+{
+  if (time==NULL)
+    *(memb->expiry)=(struct timespec){0,0};
+  else {
+    memb->expiry->tv_sec=time->tv_sec;
+    memb->expiry->tv_nsec=time->tv_nsec;
+  }
+}
+
+struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
+{
+  return memb->expiry;
+}
+
+int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
+{
+  tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s",
+           curtime->tv_sec,
+           memb->expiry->tv_sec,
+           (curtime->tv_sec > memb->expiry->tv_sec)?"true":"false");
+
+  return ((curtime->tv_sec > memb->expiry->tv_sec)
+         || ((curtime->tv_sec == memb->expiry->tv_sec)
+            &&(curtime->tv_nsec >= memb->expiry->tv_nsec)));
+}
+
+void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig)
+{
+  memb->triggered=trig;
+}
+
+int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb)
+{
+  return memb->triggered;
+}
+
+void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb)
+{
+  memb->times_expired=0;
+}
+
+/* bumps the expiration count */
+void tr_comm_memb_expire(TR_COMM_MEMB *memb)
+{
+  /* avoid overflow */
+  if (memb->times_expired+1>memb->times_expired)
+    memb->times_expired++;
+}
+
+unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb)
+{
+  return memb->times_expired;
+}
+
+TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx)
+{
+  TR_COMM_TABLE *ctab=talloc(mem_ctx, TR_COMM_TABLE);
+  if (ctab!=NULL) {
+    ctab->comms=NULL;
+    ctab->memberships=NULL;
+    ctab->idp_realms=NULL;
+    ctab->rp_realms=NULL;
+  }
+  return ctab;
+}
+
+void tr_comm_table_free(TR_COMM_TABLE *ctab)
+{
+  talloc_free(ctab);
+}
+
+static TR_REALM_ROLE tr_comm_memb_role(TR_COMM_MEMB *memb)
+{
+  if (memb->rp!=NULL)
+    return TR_ROLE_RP;
+  if (memb->idp!=NULL)
+    return TR_ROLE_IDP;
+
+  return TR_ROLE_UNKNOWN;
+}
+
+void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
+{
+  TR_COMM_MEMB *cur=NULL;
+
+  /* TODO: further validate the member (must have valid comm and realm) */
+  if ((new->next!=NULL) || (new->origin_next!=NULL)) {
+    tr_debug("tr_comm_table_add_memb: attempting to add member already in a list.");
+  }
+
+  /* handle the empty list case */
+  if (ctab->memberships==NULL) {
+    ctab->memberships=new;
+    talloc_steal(ctab, new);
+    return;
+  }
+
+  /* The list was not empty. See if we already have a membership for this realm/comm/role */
+  switch (tr_comm_memb_role(new)) {
+  case TR_ROLE_RP:
+    cur=tr_comm_table_find_rp_memb(ctab,
+                                   tr_rp_realm_get_id(tr_comm_memb_get_rp_realm(new)),
+                                   tr_comm_get_id(tr_comm_memb_get_comm(new)));
+    break;
+  case TR_ROLE_IDP:
+    cur=tr_comm_table_find_idp_memb(ctab,
+                                    tr_idp_realm_get_id(tr_comm_memb_get_idp_realm(new)),
+                                    tr_comm_get_id(tr_comm_memb_get_comm(new)));
+    break;
+  case TR_ROLE_UNKNOWN:
+  default:
+    tr_err("tr_comm_table_add_memb: realm with unknown role added.");
+    cur=NULL;
+  }
+
+  if (cur==NULL) {
+    /* no entry for this realm/comm/role, tack it on the end */
+    for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { }
+    cur->next=new;
+  } else {
+    /* Found an entry. Add to the end of its same-origin list. */
+    while (cur->origin_next!=NULL) {
+      cur=cur->origin_next;
+    }
+    cur->origin_next=new;
+  }
+
+  talloc_steal(ctab, new);
+}
+
+/* Remove memb from ctab. Do not free anything. Do nothing if memb not in ctab. */
+void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
+{
+  TR_COMM_MEMB *cur=NULL; /* for walking the main list */
+  TR_COMM_MEMB *orig_cur=NULL; /* for walking the origin list */
+
+  if ((memb==NULL) || (ctab->memberships==NULL))
+    return;
+
+  /* see if it's the first member */
+  if (ctab->memberships==memb) {
+    if (memb->origin_next!=NULL) {
+      memb->origin_next->next=memb->next;
+      ctab->memberships=memb->origin_next;
+    } else
+      ctab->memberships=memb->next;
+
+    return;
+  }
+
+  /* see if it's in first member's origin list */
+  for (orig_cur=ctab->memberships;
+       orig_cur->origin_next!=NULL;
+       orig_cur=orig_cur->origin_next) {
+    if (orig_cur->origin_next==memb) {
+      orig_cur->origin_next=memb->origin_next;
+      return;
+    }
+  }
+
+  /* now we have to walk the rest of the tree */
+  for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) {
+    if (cur->next==memb) {
+      /* it matched an entry on the main list */
+      if (memb->origin_next==NULL)
+        cur->next=memb->next; /* no origin list, just drop memb */
+      else {
+        /* replace the entry in the main list with the next element on the origin list */
+        memb->origin_next->next=memb->next;
+        cur->next=memb->origin_next;
+      }
+      return;
+    } else {
+      /* it was not on the main list, walk the origin list */
+      for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
+        if (orig_cur->origin_next==memb) {
+          orig_cur->origin_next=memb->origin_next;
+          return; /* just drop the element from the origin list */
+        }
+      }
+    }
+  }
+  /* if we got here, cur->next was null. Still have to check the origin_next list */
+  for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
+    if (orig_cur->origin_next==memb) {
+      orig_cur->origin_next=memb->origin_next;
+      return; /* just drop the element from the origin list */
+    }
+  }
+}
+
+TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
+{
+  if (memb->rp!=NULL)
+    return tr_rp_realm_get_id(memb->rp);
+  else
+    return tr_idp_realm_get_id(memb->idp);
+}
+
+/* find a membership from any origin */
+TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
+{
+  TR_COMM_MEMB *cur=NULL;
+  TR_NAME *cur_realm_name=NULL;
+
+  for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
+    cur_realm_name=tr_comm_memb_get_realm_id(cur);
+    if (cur_realm_name==NULL) {
+      tr_warning("tr_comm_table_find: encountered realm with no name.");
+      continue;
+    }
+    if ((0==tr_name_cmp(realm, cur_realm_name)) &&
+        (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
+      return cur;
+    }
+  }
+  return NULL;
+}
+
+/* find a membership from a particular origin */
+TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
+{
+  TR_NAME *cur_orig=NULL;
+  TR_COMM_MEMB *cur=tr_comm_table_find_memb(ctab, realm, comm);
+  if (cur==NULL)
+    return NULL; /* no match */
+
+  /* had a match for comm/realm; find origin match */
+  while (cur!=NULL) {
+    if (((origin==NULL) && (cur_orig==NULL)) ||
+        ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
+      return cur; /* found a match */
+    cur=cur->origin_next;
+  }
+  return NULL; /* no match */
+}
+
+
+/* find an idp membership regardless of its origin */
+TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
+{
+  TR_COMM_MEMB *cur=NULL;
+  TR_IDP_REALM *idp_realm=NULL;
+
+  for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
+    idp_realm=tr_comm_memb_get_idp_realm(cur);
+    if (idp_realm==NULL)
+      continue; /* was not an idp */
+
+    if ((0==tr_name_cmp(realm, idp_realm->realm_id)) &&
+        (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
+      return cur;
+    }
+  }
+  return NULL;
+}
+
+/* find an idp membership from a particular origin */
+TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
+{
+  TR_NAME *cur_orig=NULL;
+  TR_COMM_MEMB *cur=tr_comm_table_find_idp_memb(ctab, realm, comm);
+  if (cur==NULL)
+    return NULL; /* no match */
+
+  /* had a match for comm/realm; find origin match */
+  while (cur!=NULL) {
+    cur_orig=tr_comm_memb_get_origin(cur);
+    if (((origin==NULL) && (cur_orig==NULL)) ||
+        ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
+      return cur; /* found a match */
+    cur=cur->origin_next;
+  }
+  return NULL; /* no match */
+}
+
+/* find an rp membership from any origin */
+TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
+{
+  TR_COMM_MEMB *cur=NULL;
+  TR_RP_REALM *rp_realm=NULL;
+
+  for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
+    rp_realm=tr_comm_memb_get_rp_realm(cur);
+    if (rp_realm==NULL)
+      continue; /* was not an rp */
+
+    if ((0==tr_name_cmp(realm, tr_rp_realm_get_id(rp_realm))) &&
+        (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
+      return cur;
+    }
+  }
+  return NULL;
+}
+
+/* find an rp membership from a particular origin */
+TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
+{
+  TR_NAME *cur_orig=NULL;
+  TR_COMM_MEMB *cur=tr_comm_table_find_rp_memb(ctab, realm, comm);
+  if (cur==NULL)
+    return NULL; /* no match */
+
+  /* had a match for comm/realm; find origin match */
+  while (cur!=NULL) {
+    cur_orig=tr_comm_memb_get_origin(cur);
+    if (((origin==NULL) && (cur_orig==NULL)) ||
+        ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
+      return cur; /* found a match */
+    cur=cur->origin_next;
+  }
+  return NULL; /* no match */
+}
+
+TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id)
+{
+  return tr_comm_lookup(ctab->comms, comm_id);
+}
+
+void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
+{
+  tr_comm_add(ctab->comms, new);
+  if (ctab->comms!=NULL)
+    talloc_steal(ctab, ctab->comms); /* make sure it's in the right context */
+}
+
+void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm)
+{
+  tr_comm_remove(ctab->comms, comm);
+}
+
+TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
+{
+  return tr_rp_realm_lookup(ctab->rp_realms, realm_id);
+}
+
+void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new)
+{
+  tr_rp_realm_add(ctab->rp_realms, new);
+  if (ctab->rp_realms!=NULL)
+    talloc_steal(ctab, ctab->rp_realms); /* make sure it's in the right context */
+}
+
+void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm)
+{
+  tr_rp_realm_remove(ctab->rp_realms, realm);
+}
+
+TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
+{
+  return tr_idp_realm_lookup(ctab->idp_realms, realm_id);
+}
+
+void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new)
+{
+  tr_idp_realm_add(ctab->idp_realms, new);
+  if (ctab->idp_realms!=NULL)
+    talloc_steal(ctab, ctab->idp_realms); /* make sure it's in the right context */
+}
+
+void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm)
+{
+  tr_idp_realm_remove(ctab->idp_realms, realm);
+}
+
+
+/* how many communities in the table? */
+size_t tr_comm_table_size(TR_COMM_TABLE *ctab)
+{
+  size_t count=0;
+  TR_COMM *this=ctab->comms;
+  while(this!=NULL) {
+    this=this->next;
+    count++;
+  }
+  return count;
+}
+
+/* clean up unreferenced realms, etc */
+void tr_comm_table_sweep(TR_COMM_TABLE *ctab)
+{
+  tr_rp_realm_sweep(ctab->rp_realms);
+  tr_idp_realm_sweep(ctab->idp_realms);
+  tr_comm_sweep(ctab->comms);
+}
+
+
+const char *tr_realm_role_to_str(TR_REALM_ROLE role)
+{
+  switch(role) {
+  case TR_ROLE_IDP:
+    return "idp";
+  case TR_ROLE_RP:
+    return "rp";
+  default:
+    return NULL;
+  }
+}
+
+TR_REALM_ROLE tr_realm_role_from_str(const char *s)
+{
+  if (strcmp(s, "idp")==0)
+    return TR_ROLE_IDP;
+  if (strcmp(s, "rp")==0)
+    return TR_ROLE_RP;
+  return TR_ROLE_UNKNOWN;
+}
+
+static void tr_comm_table_print_provenance(FILE *f, json_t *prov)
+{
+  const char *s=NULL;
+  size_t ii=0;
+
+  for (ii=0; ii<json_array_size(prov); ii++) {
+    s=json_string_value(json_array_get(prov, ii));
+    if (s!=NULL)
+      fprintf(f, "%s%s", s, ((ii+1)==json_array_size(prov))?"":", ");
+  }
+}
+
+void tr_comm_table_print(FILE *f, TR_COMM_TABLE *ctab)
+{
+  TR_COMM_MEMB *p1=NULL; /* for walking the main list */
+  TR_COMM_MEMB *p2=NULL; /* for walking the same-origin lists */
+
+  fprintf(f, ">> Membership table start <<\n");
+  for (p1=ctab->memberships; p1!=NULL; p1=p1->next) {
+    fprintf(f, "* %s %s/%s\n  %s (%p) - prov: ",
+            tr_realm_role_to_str(tr_comm_memb_get_role(p1)),
+            tr_comm_memb_get_realm_id(p1)->buf,
+            tr_comm_get_id(tr_comm_memb_get_comm(p1))->buf,
+            (tr_comm_memb_get_origin(p1)==NULL)?"null origin":(tr_comm_memb_get_origin(p1)->buf),
+            p1);
+    tr_comm_table_print_provenance(f, p1->provenance);
+    fprintf(f, "\n");
+    for (p2=p1->origin_next; p2!=NULL; p2=p2->origin_next) {
+      fprintf(f, "  %s (%p) - prov: ",
+              (tr_comm_memb_get_origin(p2)==NULL)?"null origin":(tr_comm_memb_get_origin(p2)->buf),
+              p2);
+      tr_comm_table_print_provenance(f, p2->provenance);
+      fprintf(f, "\n");
+    }
+    fprintf(f, "\n");
+  }
+}
index 0bdaab6..b451860 100644 (file)
 
 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) {
-    s=tr_idp_realm_to_str(NULL, idp);
+  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.");
+      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);
 }
 
 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);
+      cfg=NULL;
+    }
+  }
+  return cfg;
 }
 
-void tr_cfg_free (TR_CFG *cfg) {
+void tr_cfg_free (TR_CFG *cfg)
+{
   talloc_free(cfg);
 }
 
@@ -139,6 +184,9 @@ static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
   json_t *jcfgsettle = NULL;
   json_t *jroutesweep = NULL;
   json_t *jrouteupdate = NULL;
+  json_t *jtidreq_timeout = NULL;
+  json_t *jtidresp_numer = NULL;
+  json_t *jtidresp_denom = NULL;
   json_t *jrouteconnect = NULL;
 
   if ((!trc) || (!jcfg))
@@ -249,6 +297,42 @@ static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
       trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
     }
 
+    if (NULL != (jtidreq_timeout = json_object_get(jint, "tid_request_timeout"))) {
+      if (json_is_number(jtidreq_timeout)) {
+        trc->internal->tid_req_timeout = json_integer_value(jtidreq_timeout);
+      } else {
+        tr_debug("tr_cfg_parse_internal: Parsing error, tid_request_timeout is not a number.");
+        return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->tid_req_timeout=TR_DEFAULT_TID_REQ_TIMEOUT;
+    }
+
+    if (NULL != (jtidresp_numer = json_object_get(jint, "tid_response_numerator"))) {
+      if (json_is_number(jtidresp_numer)) {
+        trc->internal->tid_resp_numer = json_integer_value(jtidresp_numer);
+      } else {
+        tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_numerator is not a number.");
+        return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->tid_resp_numer=TR_DEFAULT_TID_RESP_NUMER;
+    }
+
+    if (NULL != (jtidresp_denom = json_object_get(jint, "tid_response_denominator"))) {
+      if (json_is_number(jtidresp_denom)) {
+        trc->internal->tid_resp_denom = json_integer_value(jtidresp_denom);
+      } else {
+        tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_denominator is not a number.");
+        return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->tid_resp_denom=TR_DEFAULT_TID_RESP_DENOM;
+    }
+
     if (NULL != (jlog = json_object_get(jint, "logging"))) {
       if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
         if (json_is_string(jlogthres)) {
@@ -651,7 +735,7 @@ static TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *
       *rc=TR_CFG_NOPARSE;
       goto cleanup;
     }
-    apcs=tr_apc_add(apcs, new_apc);
+    tr_apc_add(apcs, new_apc);
   }
 
   talloc_steal(mem_ctx, apcs);
@@ -932,7 +1016,7 @@ static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealm
         *rc=TR_CFG_NOPARSE;
         goto cleanup;
       }
-      realms=tr_idp_realm_add(realms, new_realm);
+      tr_idp_realm_add(realms, new_realm);
     } else if (tr_cfg_is_remote_realm(this_jrealm)) {
       new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
       if ((*rc)!=TR_CFG_SUCCESS) {
@@ -940,7 +1024,7 @@ static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealm
         *rc=TR_CFG_NOPARSE;
         goto cleanup;
       }
-      realms=tr_idp_realm_add(realms, new_realm);
+      tr_idp_realm_add(realms, new_realm);
     }
   }
   
@@ -952,44 +1036,6 @@ cleanup:
   return realms;
 }
 
-#if 0
-static TR_IDP_REALM *tr_cfg_parse_remote_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;
-
-  *rc=TR_CFG_ERROR;
-  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
-    tr_err("tr_cfg_parse_remote_realms: realms not an array");
-    *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  for (ii=0; ii<json_array_size(jrealms); ii++) {
-    this_jrealm=json_array_get(jrealms, ii);
-    if (tr_cfg_is_remote_realm(this_jrealm)) {
-      new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
-      if ((*rc)!=TR_CFG_SUCCESS) {
-        tr_err("tr_cfg_parse_remote_realms: error decoding remote realm entry %d", ii+1);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-      realms=tr_idp_realm_add(realms, new_realm);
-    }
-  }
-  
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, realms);
-
-cleanup:
-  talloc_free(tmp_ctx);
-  return realms;
-}
-#endif /* 0 */
-
 static TR_GSS_NAMES *tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_CFG_RC *rc)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
@@ -1313,7 +1359,7 @@ static TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealm
         *rc=TR_CFG_NOPARSE;
         goto cleanup;
       }
-      clients=tr_rp_client_add(clients, new_client);
+      tr_rp_client_add(clients, new_client);
     }
   }
   
@@ -1346,10 +1392,14 @@ static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG
 }
 
 #if 0
+/* TODO: are we using this? JLR */
 /* Update the community information with data from a new batch of IDP realms.
  * May partially add realms if there is a failure, no guarantees.
  * Call like comms=tr_comm_idp_update(comms, new_realms, &rc) */
-static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx, TR_COMM *comms, TR_IDP_REALM *new_realms, TR_CFG_RC *rc)
+static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx,
+                                       TR_COMM_TABLE *ctab,
+                                       TR_IDP_REALM *new_realms,
+                                       TR_CFG_RC *rc)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_COMM *comm=NULL; /* community looked up in comms table */
@@ -1376,9 +1426,9 @@ static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx, TR_COMM *comms, TR_I
         /* fill in the community with info */
         comm->type=TR_COMM_APC; /* realms added this way are in APCs */
         comm->expiration_interval=TR_DEFAULT_APC_EXPIRATION_INTERVAL;
-        comm->id=tr_dup_name(apc->id);
+        tr_comm_set_id(comm, tr_dup_name(apc->id));
         tr_comm_add_idp_realm(comm, realm);
-        new_comms=tr_comm_add(new_comms, comm);
+        tr_comm_add(new_comms, comm);
       } else {
         /* add this realm to the comm */
         tr_comm_add_idp_realm(comm, realm);
@@ -1387,7 +1437,7 @@ static TR_COMM *tr_cfg_comm_idp_update(TALLOC_CTX *mem_ctx, TR_COMM *comms, TR_I
   }
 
   /* we successfully built a list, add it to the other list */
-  comms=tr_comm_add(comms, new_comms);
+  tr_comm_add(comms, new_comms);
   talloc_steal(mem_ctx, comms);
  cleanup:
   talloc_free(tmp_ctx);
@@ -1443,12 +1493,12 @@ 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 except for head*/
-      talloc_steal(trc, trc->idp_realms); /* make sure the head is in the right context */
+      tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/
+      talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */
     }
 
     if (new_rp_clients!=NULL) {
-      trc->rp_clients=tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */
+      tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */
       talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */
     }
   }
@@ -1601,11 +1651,9 @@ static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg)
   return rc;
 }
 
-static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_RC *rc)
+static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc)
 {
-  TR_IDP_REALM *idp = NULL;
-  TR_IDP_REALM *found_idp = NULL;
-  TR_IDP_REALM *temp_idp = NULL;
+  TR_IDP_REALM *found_idp=NULL;
   int i = 0;
 
   if ((!trc) ||
@@ -1613,73 +1661,91 @@ static TR_IDP_REALM *tr_cfg_parse_comm_idps (TR_CFG *trc, json_t *jidps, TR_CFG_
       (!json_is_array(jidps))) {
     if (rc)
       *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
+    return;
   }
 
-  for (i = 0; i < json_array_size(jidps); i++) {
-    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)))) {
+  for (i=0; i < json_array_size(jidps); i++) {
+    found_idp=tr_cfg_find_idp(trc, 
+                              tr_new_name((char *)json_string_value(json_array_get(jidps, i))), 
+                              rc);
+    if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) {
       tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", 
                (char *)json_string_value(json_array_get(jidps, i)));
-      return NULL;
+      *rc=TR_CFG_ERROR;
+      return;
     }
-
-    // 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;
+    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */
   }
 
-  return idp;
+  *rc=TR_CFG_SUCCESS;
+  return;
 }
 
-static TR_RP_REALM *tr_cfg_parse_comm_rps (TR_CFG *trc, json_t *jrps, TR_CFG_RC *rc)
+static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc)
 {
-  TR_RP_REALM *rp = NULL;
-  TR_RP_REALM *temp_rp = NULL;
-  int i = 0;
+  TR_RP_REALM *found_rp=NULL;
+  TR_RP_REALM *new_rp=NULL;
+  TR_NAME *rp_name=NULL;
+  const char *s=NULL;
+  int ii=0;
 
   if ((!trc) ||
       (!jrps) ||
       (!json_is_array(jrps))) {
     if (rc)
       *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
+    return;
   }
 
-  for (i = (json_array_size(jrps)-1); i >= 0; i--) {
-    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;
+  for (ii=0; ii<json_array_size(jrps); ii++) {
+    /* get the RP name as a string */
+    s=json_string_value(json_array_get(jrps, ii));
+    if (s==NULL) {
+      tr_notice("tr_cfg_parse_comm_rps: null RP found in community %.*s, ignoring.",
+                tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      continue;
     }
 
-    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.");
-      if (rc)
-       *rc = TR_CFG_NOMEM;
-      return NULL;
+    /* convert string to TR_NAME */
+    rp_name=tr_new_name(s);
+    if (rp_name==NULL) {
+      tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.",
+             s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
     }
 
-    temp_rp->next = rp;
-    rp = temp_rp;
-  }
+    /* see if we already have this RP in this community */
+    found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name);
+    if (found_rp!=NULL) {
+      tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.",
+                s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      tr_free_name(rp_name);
+      continue;
+    }
 
-  return rp;
+    /* Add the RP to the community, first see if we have the RP in any community */
+    found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name);
+    if (found_rp!=NULL) {
+      tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s);
+      new_rp=found_rp; /* use it rather than creating a new realm record */
+    } else {
+      new_rp=tr_rp_realm_new(NULL);
+      if (new_rp==NULL) {
+        tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.",
+               s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      }
+      tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf);
+      tr_rp_realm_set_id(new_rp, rp_name);
+      rp_name=NULL; /* rp_name no longer belongs to us */
+      tr_rp_realm_add(trc->ctable->rp_realms, new_rp);
+      talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */
+    }
+    tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL);
+  }
 }
 
-static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc) {
+static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_COMM *comm = NULL;
   json_t *jid = NULL;
   json_t *jtype = NULL;
@@ -1691,13 +1757,14 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
     tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
     if (rc)
       *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
+    goto cleanup;
   }
 
-  if (NULL == (comm = talloc_zero(trc, TR_COMM))) {
+  comm=tr_comm_new(tmp_ctx);
+  if (comm==NULL) {
     tr_crit("tr_cfg_parse_one_comm: Out of memory.");
     *rc = TR_CFG_NOMEM;
-    return NULL;
+    goto cleanup;
   }
 
 
@@ -1713,13 +1780,16 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
       (!json_is_array(jrps))) {
     tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
     *rc = TR_CFG_NOPARSE;
-    return NULL;
+    comm=NULL;
+    goto cleanup;
   }
 
-  if (NULL == (comm->id = tr_new_name((char *)json_string_value(jid)))) {
+  tr_comm_set_id(comm, tr_new_name(json_string_value(jid)));
+  if (NULL == tr_comm_get_id(comm)) {
     tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
     *rc = TR_CFG_NOMEM;
-    return NULL;
+    comm=NULL;
+    goto cleanup;
   }
 
   if (0 == strcmp(json_string_value(jtype), "apc")) {
@@ -1727,29 +1797,33 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
   } 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))) {
-      tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
-      tr_free_name(comm->id);
-      return NULL;
+      tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.",
+               tr_comm_get_id(comm)->buf);
+      comm=NULL;
+      goto cleanup;
     }
   } else {
-    tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s", comm->id->buf, json_string_value(jtype));
-    tr_free_name(comm->id);
+    tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s",
+             tr_comm_get_id(comm)->buf, json_string_value(jtype));
     *rc = TR_CFG_NOPARSE;
-    return NULL;
+    comm=NULL;
+    goto cleanup;
   }
 
-  comm->idp_realms = tr_cfg_parse_comm_idps(trc, jidps, rc);
+  tr_cfg_parse_comm_idps(trc, jidps, comm, rc);
   if (TR_CFG_SUCCESS != *rc) {
-    tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.", comm->id->buf);
-    tr_free_name(comm->id);
-    return NULL;
+    tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.",
+             tr_comm_get_id(comm)->buf);
+    comm=NULL;
+    goto cleanup;
   }
 
-  comm->rp_realms = tr_cfg_parse_comm_rps(trc, jrps, rc);
+  tr_cfg_parse_comm_rps(trc, jrps, comm, rc);
   if (TR_CFG_SUCCESS != *rc) {
-    tr_debug("tr_cfg_parse_comm: Can't parse RP realms for comm %s .", comm->id->buf);
-    tr_free_name(comm->id);
-    return NULL;
+    tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .",
+             tr_comm_get_id(comm)->buf);
+    comm=NULL;
+    goto cleanup;
   }
 
   if (TR_COMM_APC == comm->type) {
@@ -1757,8 +1831,9 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
     comm->expiration_interval = 43200; /*30 days*/
     if (jexpire) {
        if (!json_is_integer(jexpire)) {
-         fprintf(stderr, "tr_parse_comm: expirae_interval is not an integer\n");
-         return NULL;
+         fprintf(stderr, "tr_parse_one_comm: expiration_interval is not an integer\n");
+    comm=NULL;
+    goto cleanup;
        }
        comm->expiration_interval = json_integer_value(jexpire);
        if (comm->expiration_interval <= 10)
@@ -1767,7 +1842,11 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
        comm->expiration_interval = 129600;
     }
   }
-  
+
+cleanup:
+  if (comm!=NULL)
+    talloc_steal(mem_ctx, comm);
+  talloc_free(tmp_ctx);
   return comm;
 }
 
@@ -1789,14 +1868,16 @@ static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg)
     }
 
     for (i = 0; i < json_array_size(jcomms); i++) {
-      if (NULL == (comm = tr_cfg_parse_one_comm(trc, 
-                                               json_array_get(jcomms, i), 
-                                               &rc))) {
-       return rc;
+      if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */
+                                                trc, 
+                                                json_array_get(jcomms, i), 
+                                               &rc))) {
+        return rc;
       }
-      tr_debug("tr_cfg_parse_comms: Community configured: %s.", comm->id->buf);
-      comm->next = trc->comms;
-      trc->comms = comm;
+      tr_debug("tr_cfg_parse_comms: Community configured: %s.",
+               tr_comm_get_id(comm)->buf);
+
+      tr_comm_table_add_comm(trc->ctable, comm);
     }
   }
   tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
@@ -1821,12 +1902,12 @@ TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
     rc = TR_CFG_ERROR;
   }
 
-  if (NULL == trc->comms) {
+  if (0==tr_comm_table_size(trc->ctable)) {
     tr_debug("tr_cfg_validate: Error: No Communities configured");
     rc = TR_CFG_ERROR;
   }
 
-  if ((NULL == trc->default_servers) && (NULL == trc->idp_realms)) {
+  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;
   }
@@ -1949,7 +2030,7 @@ TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
     return NULL;
   }
 
-  for (cfg_idp = tr_cfg->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
+  for (cfg_idp = tr_cfg->ctable->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;
index 44a60ba..a155566 100644 (file)
@@ -76,6 +76,29 @@ void tr_constraint_free(TR_CONSTRAINT *cons)
   talloc_free(cons);
 }
 
+TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons)
+{
+  TALLOC_CTX *tmp_ctx=NULL;
+  TR_CONSTRAINT *new=NULL;
+  int ii=0;
+
+  if (cons==NULL)
+    return NULL;
+
+  tmp_ctx=talloc_new(NULL);
+  new=tr_constraint_new(tmp_ctx);
+
+  if (new!=NULL) {
+    new->type=tr_dup_name(cons->type);
+    for (ii=0; ii<TR_MAX_CONST_MATCHES; ii++)
+      new->matches[ii]=tr_dup_name(cons->matches[ii]);
+    talloc_steal(mem_ctx, new);
+  }
+
+  talloc_free(tmp_ctx);
+  return new;
+}
+
 /* Returns TRUE (1) if the the string (str) matchs the wildcard string (wc_str), FALSE (0) if not.
  */
 int tr_prefix_wildcard_match (const char *str, const char *wc_str) {
index b1d1e17..031b404 100644 (file)
@@ -77,6 +77,11 @@ unsigned char tr_2048_dhprime[2048/8] = {
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 };
 
+DH *tr_dh_new(void)
+{
+  return DH_new();
+}
+
 DH *tr_create_dh_params(unsigned char *priv_key, 
                        size_t keylen) {
 
@@ -170,6 +175,66 @@ void tr_destroy_dh_params(DH *dh) {
   }
 }
 
+DH *tr_dh_dup(DH *in)
+{
+  DH *out=DH_new();
+
+  if (out==NULL)
+    return NULL;
+
+  if (in->g==NULL)
+    out->g=NULL;
+  else {
+    out->g=BN_dup(in->g);
+    if (out->g==NULL) {
+      DH_free(out);
+      return NULL;
+    }
+  }
+
+  if (in->p==NULL)
+    out->p=NULL;
+  else {
+    out->p=BN_dup(in->p);
+    if (out->p==NULL) {
+      DH_free(out);
+      return NULL;
+    }
+  }
+
+  if (in->q==NULL)
+    out->q=NULL;
+  else {
+    out->q=BN_dup(in->q);
+    if (out->q==NULL) {
+      DH_free(out);
+      return NULL;
+    }
+  }
+
+  if (in->priv_key==NULL)
+    out->priv_key=NULL;
+  else {
+    out->priv_key=BN_dup(in->priv_key);
+    if (out->priv_key==NULL) {
+      DH_free(out);
+      return NULL;
+    }
+  }
+
+  if (in->pub_key==NULL)
+    out->pub_key=NULL;
+  else {
+    out->pub_key=BN_dup(in->pub_key);
+    if (out->g==NULL) {
+      DH_free(out);
+      return NULL;
+    }
+  }
+
+  return out;
+}
+
 int tr_compute_dh_key(unsigned char **pbuf, 
                      BIGNUM *pub_key, 
                      DH *priv_dh) {
@@ -212,14 +277,21 @@ int tr_dh_pub_hash(TID_REQ *request,
   unsigned char *bn_bytes = talloc_zero_size(request, BN_num_bytes(pub));
   unsigned char *digest = talloc_zero_size(request, SHA_DIGEST_LENGTH+1);
   assert(bn_bytes && digest);
-                                   BN_bn2bin(pub, bn_bytes);
-                                   SHA1(bn_bytes, BN_num_bytes(pub), digest);
-                                   *out_digest = digest;
-                                   *out_len = SHA_DIGEST_LENGTH;
-                                   return 0;
+  BN_bn2bin(pub, bn_bytes);
+  SHA1(bn_bytes, BN_num_bytes(pub), digest);
+  *out_digest = digest;
+  *out_len = SHA_DIGEST_LENGTH;
+
+  talloc_free(bn_bytes);
+  return 0;
 }
 
 void tr_dh_free(unsigned char *dh_buf)
 {
   free(dh_buf);
 }
+
+void tr_dh_destroy(DH *dh)
+{
+  DH_free(dh);
+}
index 2ef9e93..10bb2c3 100644 (file)
  */
 
 #include <talloc.h>
+#include <time.h>
 
 #include <trust_router/tr_name.h>
 #include <tr_idp.h>
 #include <tr_config.h>
+#include <tr_debug.h>
 
 static int tr_aaa_server_destructor(void *obj)
 {
@@ -50,6 +52,7 @@ TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname)
 {
   TR_AAA_SERVER *aaa=talloc(mem_ctx, TR_AAA_SERVER);
   if (aaa!=NULL) {
+    aaa->next=NULL;
     aaa->hostname=hostname;
     talloc_set_destructor((void *)aaa, tr_aaa_server_destructor);
   }
@@ -61,7 +64,33 @@ void tr_aaa_server_free(TR_AAA_SERVER *aaa)
   talloc_free(aaa);
 }
 
-TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm)
+TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx)
+{
+  return talloc(mem_ctx, TR_AAA_SERVER_ITER);
+}
+
+void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa)
+{
+  iter->this=aaa;
+  return iter->this;
+}
+
+TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter)
+{
+  if (iter->this!=NULL) {
+    iter->this=iter->this->next;
+  }
+  return iter->this;
+}
+
+
+/* fills in shared if pointer not null */
+TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm, int *shared_out)
 {
   TR_IDP_REALM *idp = NULL;
 
@@ -71,9 +100,11 @@ TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_r
       break;
     }
   }
-  if (idp)
+  if (idp) {
+    if (shared_out!=NULL)
+      *shared_out=idp->shared_config;
     return idp->aaa_servers;
-  else 
+  else 
     return NULL;
 }
 
@@ -113,6 +144,64 @@ TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx)
   return idp;
 }
 
+void tr_idp_realm_free(TR_IDP_REALM *idp)
+{
+  talloc_free(idp);
+}
+
+TR_NAME *tr_idp_realm_get_id(TR_IDP_REALM *idp)
+{
+  if (idp==NULL)
+    return NULL;
+  
+  return idp->realm_id;
+}
+
+TR_NAME *tr_idp_realm_dup_id(TR_IDP_REALM *idp)
+{
+  if (idp==NULL)
+    return NULL;
+  
+  return tr_dup_name(tr_idp_realm_get_id(idp));
+}
+
+void tr_idp_realm_set_id(TR_IDP_REALM *idp, TR_NAME *id)
+{
+  if (idp->realm_id!=NULL)
+    tr_free_name(idp->realm_id);
+  idp->realm_id=id;
+}
+
+void tr_idp_realm_set_apcs(TR_IDP_REALM *idp, TR_APC *apc)
+{
+  if (idp->apcs!=NULL)
+    tr_apc_free(idp->apcs);
+  idp->apcs=apc;
+  talloc_steal(idp, apc);
+}
+
+TR_APC *tr_idp_realm_get_apcs(TR_IDP_REALM *idp)
+{
+  return idp->apcs;
+}
+
+TR_IDP_REALM *tr_idp_realm_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_name)
+{
+  TR_IDP_REALM *idp = NULL;
+
+  if (!idp_name) {
+    tr_debug("tr_idp_realm_lookup: Bad parameters.");
+    return NULL;
+  }
+
+  for (idp=idp_realms; NULL!=idp; idp=idp->next) {
+    if (0==tr_name_cmp(tr_idp_realm_get_id(idp), idp_name))
+      return idp;
+  } 
+  return NULL;
+}
+
+
 static TR_IDP_REALM *tr_idp_realm_tail(TR_IDP_REALM *idp)
 {
   if (idp==NULL)
@@ -123,8 +212,8 @@ static TR_IDP_REALM *tr_idp_realm_tail(TR_IDP_REALM *idp)
   return idp;
 }
 
-/* for correct behavior, call like: idp_realms=tr_idp_realm_add(idp_realms, new_realm); */
-TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new)
+/* do not call directly, use the tr_idp_realm_add() macro */
+TR_IDP_REALM *tr_idp_realm_add_func(TR_IDP_REALM *head, TR_IDP_REALM *new)
 {
   if (head==NULL)
     head=new;
@@ -138,6 +227,37 @@ TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new)
   return head;
 }
 
+/* use the macro */
+TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove)
+{
+  TALLOC_CTX *list_ctx=talloc_parent(head);
+  TR_IDP_REALM *this=NULL;
+
+  if (head==NULL)
+    return NULL;
+
+  if (head==remove) {
+    /* if we're removing the head, put the next element (if present) into the context
+     * the list head was in. */
+    head=head->next;
+    if (head!=NULL) {
+      talloc_steal(list_ctx, head);
+      /* now put all the other elements in the context of the list head */
+      for (this=head->next; this!=NULL; this=this->next)
+        talloc_steal(head, this);
+    }
+  } else {
+    /* not removing the head; no need to play with contexts */
+    for (this=head; this->next!=NULL; this=this->next) {
+      if (this->next==remove) {
+        this->next=remove->next;
+        break;
+      }
+    }
+  }
+  return head;
+}
+
 static int tr_idp_realm_apc_count(TR_IDP_REALM *idp)
 {
   int ii=0;
@@ -237,3 +357,46 @@ char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp)
   talloc_free(tmp_ctx);
   return result;
 }
+
+void tr_idp_realm_incref(TR_IDP_REALM *realm)
+{
+  realm->refcount++;
+}
+
+void tr_idp_realm_decref(TR_IDP_REALM *realm)
+{
+  if (realm->refcount>0)
+    realm->refcount--;
+}
+
+/* remove any with zero refcount 
+ * Call via macro. */
+TR_IDP_REALM *tr_idp_realm_sweep_func(TR_IDP_REALM *head)
+{
+  TR_IDP_REALM *idp=NULL;
+  TR_IDP_REALM *old_next=NULL;
+
+  if (head==NULL)
+    return NULL;
+
+  while ((head!=NULL) && (head->refcount==0)) {
+    idp=head; /* keep a pointer so we can remove it */
+    tr_idp_realm_remove(head, idp); /* use this to get talloc contexts right */
+    tr_idp_realm_free(idp);
+  }
+
+  if (head==NULL)
+    return NULL;
+
+  /* will not remove the head here, that has already been done */
+  for (idp=head; idp->next!=NULL; idp=idp->next) {
+    if (idp->next->refcount==0) {
+      old_next=idp->next;
+      tr_idp_realm_remove(head, idp->next); /* changes idp->next */
+      tr_idp_realm_free(old_next);
+    }
+  }
+
+  return head;
+}
+
index 60313c6..07080b6 100644 (file)
@@ -34,6 +34,8 @@
 
 #include <talloc.h>
 #include <pthread.h>
+#include <time.h>
+#include <errno.h>
 
 #include <tr_mq.h>
 #include <tr_debug.h>
@@ -107,11 +109,22 @@ static void tr_mq_msg_set_next(TR_MQ_MSG *msg, TR_MQ_MSG *next)
 TR_MQ *tr_mq_new(TALLOC_CTX *mem_ctx)
 {
   TR_MQ *mq=talloc(mem_ctx, TR_MQ);
+  pthread_condattr_t cattr;
+
   if (mq!=NULL) {
     pthread_mutex_init(&(mq->mutex), 0);
+    pthread_condattr_init(&cattr);
+
+    pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC); /* use the monotonic clock for timeouts */
+    pthread_cond_init(&(mq->have_msg_cond), &cattr);
+    pthread_condattr_destroy(&cattr);
+
     mq->head=NULL;
     mq->tail=NULL;
     mq->last_hi_prio=NULL;
+
+    mq->notify_cb=NULL;
+    mq->notify_cb_arg=NULL;
   }
   return mq;
 }
@@ -252,6 +265,12 @@ void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg)
   tr_mq_print(mq);
 #endif 
 
+  /* Before releasing the lock, signal any waiting threads that there's now
+   * something in the queue. Used for blocking tr_mq_pop() call. */
+
+  if (was_empty)
+    pthread_cond_broadcast(&(mq->have_msg_cond));
+
   tr_mq_unlock(mq);
 
   /* see if we need to tell someone we became non-empty */
@@ -259,12 +278,44 @@ void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg)
     notify_cb(mq, notify_cb_arg);
 }
 
-/* caller must free msg via tr_mq_msg_free */
-TR_MQ_MSG *tr_mq_pop(TR_MQ *mq)
+/* Compute an absolute time from a desired timeout interval for use with tr_mq_pop().
+ * Fills in *ts and returns 0 on success. */
+int tr_mq_pop_timeout(time_t seconds, struct timespec *ts)
 {
-  TR_MQ_MSG *popped=NULL;
+  if (0!=clock_gettime(CLOCK_MONOTONIC, ts))
+    return -1;
 
+  ts->tv_sec+=seconds;
+  return 0;
+}
+
+/* Caller must free msg via tr_mq_msg_free, waiting until absolute
+ * time ts_abort before giving up (using CLOCK_MONOTONIC). If ts_abort
+ * has passed, returns an existing message but will not wait if one is
+ * not already available. If ts_abort is null, no blocking.  Not
+ * guaranteed to wait if an error occurs - immediately returns without
+ * a message. Use tr_mq_pop_timeout() to get an absolute time that
+ * is guaranteed compatible with this function. */
+TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort)
+{
+  TR_MQ_MSG *popped=NULL;
+  int wait_err=0;
+  
   tr_mq_lock(mq);
+  if ((tr_mq_get_head(mq)==NULL) && (ts_abort!=NULL)) {
+    /* No msgs yet, and blocking was requested */
+    while ((wait_err==0) && (NULL==tr_mq_get_head(mq)))
+      wait_err=pthread_cond_timedwait(&(mq->have_msg_cond),
+                                     &(mq->mutex),
+                                     ts_abort);
+    
+    if ((wait_err!=0) && (wait_err!=ETIMEDOUT)) {
+      tr_notice("tr_mq_pop: error waiting for message.");
+      return NULL;
+    }
+    /* if it timed out, ok to go ahead and check once more for a message, so no special exit */
+  }
+
   if (tr_mq_get_head(mq)!=NULL) {
     popped=tr_mq_get_head(mq);
     tr_mq_set_head(mq, tr_mq_msg_get_next(popped)); /* popped is the old head */
index 9591cd4..9b205d3 100644 (file)
 #include <arpa/inet.h>
 #include <string.h>
 #include <openssl/dh.h>
+#include <openssl/crypto.h>
 #include <jansson.h>
 #include <assert.h>
 #include <talloc.h>
 
 
+#include <tr_apc.h>
+#include <tr_comm.h>
 #include <tr_msg.h>
 #include <trust_router/tr_name.h>
 #include <trp_internal.h>
 #include <trust_router/tr_constraint.h>
+#include <trust_router/tr_dh.h>
 #include <tr_debug.h>
 
 /* JSON helpers */
@@ -68,23 +72,23 @@ static int tr_msg_get_json_integer(json_t *jmsg, const char *attr, int *dest)
 
 /* Read attribute attr from msg as a string. Copies string into mem_ctx context so jmsg can
  * be destroyed safely. Returns nonzero on error. */
-static int tr_msg_get_json_string(json_t *jmsg, const char *attr, char **dest, TALLOC_CTX *mem_ctx)
+static TRP_RC tr_msg_get_json_string(json_t *jmsg, const char *attr, char **dest, TALLOC_CTX *mem_ctx)
 {
   json_t *obj;
 
   obj=json_object_get(jmsg, attr);
   if (obj == NULL)
-    return -1;
+    return TRP_ERROR;
 
   /* check type */
   if (!json_is_string(obj))
-    return -1;
+    return TRP_ERROR;
 
   *dest=talloc_strdup(mem_ctx, json_string_value(obj));
   if (*dest==NULL)
-    return -1;
+    return TRP_ERROR;
 
-  return 0;
+  return TRP_SUCCESS;
 }
 
 enum msg_type tr_msg_get_msg_type(TR_MSG *msg) 
@@ -133,6 +137,7 @@ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg)
 void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *update)
 {
   msg->msg_rep=update;
+  talloc_steal(NULL, update); /* should attach to msg, but TR_MSG not usually talloc'ed */
   msg->msg_type=TRP_UPDATE;
 }
 
@@ -153,19 +158,23 @@ static json_t *tr_msg_encode_dh(DH *dh)
 {
   json_t *jdh = NULL;
   json_t *jbn = NULL;
+  char *s=NULL;
 
   if ((!dh) || (!dh->p) || (!dh->g) || (!dh->pub_key))
     return NULL;
 
   jdh = json_object();
 
-  jbn = json_string(BN_bn2hex(dh->p));
+  jbn = json_string(s=BN_bn2hex(dh->p));
+  OPENSSL_free(s);
   json_object_set_new(jdh, "dh_p", jbn);
 
-  jbn = json_string(BN_bn2hex(dh->g));
+  jbn = json_string(s=BN_bn2hex(dh->g));
+  OPENSSL_free(s);
   json_object_set_new(jdh, "dh_g", jbn);
 
-  jbn = json_string(BN_bn2hex(dh->pub_key));
+  jbn = json_string(s=BN_bn2hex(dh->pub_key));
+  OPENSSL_free(s);
   json_object_set_new(jdh, "dh_pub_key", jbn);
 
   return jdh;
@@ -178,19 +187,17 @@ static DH *tr_msg_decode_dh(json_t *jdh)
   json_t *jg = NULL;
   json_t *jpub_key = NULL;
 
-  if (!(dh = malloc(sizeof(DH)))) {
+  if (!(dh=tr_dh_new())) {
     tr_crit("tr_msg_decode_dh(): Error allocating DH structure.");
     return NULL;
   }
  
-  memset(dh, 0, sizeof(DH));
-
   /* store required fields from dh object */
   if ((NULL == (jp = json_object_get(jdh, "dh_p"))) ||
       (NULL == (jg = json_object_get(jdh, "dh_g"))) ||
       (NULL == (jpub_key = json_object_get(jdh, "dh_pub_key")))) {
     tr_debug("tr_msg_decode_dh(): Error parsing dh_info.");
-    free(dh);
+    tr_dh_destroy(dh);
     return NULL;
   }
 
@@ -315,8 +322,7 @@ static json_t *tr_msg_encode_one_server(TID_SRVR_BLK *srvr)
 
   jsrvr = json_object();
 
-  /* Server IP Address -- TBD handle IPv6 */
-  jstr = json_string(inet_ntoa(srvr->aaa_server_addr));
+  jstr = json_string(srvr->aaa_server_addr);
   json_object_set_new(jsrvr, "server_addr", jstr);
 
   json_object_set_new(jsrvr,
@@ -328,7 +334,7 @@ static json_t *tr_msg_encode_one_server(TID_SRVR_BLK *srvr)
   json_object_set_new(jsrvr, "server_dh", tr_msg_encode_dh(srvr->aaa_server_dh));
   if (srvr->path)
     /* The path is owned by the srvr, so grab an extra ref*/
-    json_object_set(jsrvr, "path", srvr->path);
+    json_object_set(jsrvr, "path", (json_t *)(srvr->path));
   return jsrvr;
 }
 
@@ -349,12 +355,11 @@ static int tr_msg_decode_one_server(json_t *jsrvr, TID_SRVR_BLK *srvr)
     tr_notice("tr_msg_decode_one_server(): Error parsing required fields.");
     return -1;
   }
-  
-  /* TBD -- handle IPv6 Addresses */
-  inet_aton(json_string_value(jsrvr_addr), &(srvr->aaa_server_addr));
+
+  srvr->aaa_server_addr=talloc_strdup(srvr, json_string_value(jsrvr_addr));
   srvr->key_name = tr_new_name((char *)json_string_value(jsrvr_kn));
   srvr->aaa_server_dh = tr_msg_decode_dh(jsrvr_dh);
-  srvr->path = json_object_get(jsrvr, "path");
+  tid_srvr_blk_set_path(srvr, (TID_PATH *) json_object_get(jsrvr, "path"));
   jsrvr_expire = json_object_get(jsrvr, "key_expiration");
   if (jsrvr_expire && json_is_string(jsrvr_expire)) {
     if (!g_time_val_from_iso8601(json_string_value(jsrvr_expire),
@@ -386,9 +391,11 @@ static json_t *tr_msg_encode_servers(TID_RESP *resp)
   return jservers;
 }
 
-static TID_SRVR_BLK *tr_msg_decode_servers(void * ctx, json_t *jservers, size_t *out_len)
+static TID_SRVR_BLK *tr_msg_decode_servers(TALLOC_CTX *mem_ctx, json_t *jservers)
 {
-  TID_SRVR_BLK *servers = NULL;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TID_SRVR_BLK *servers=NULL;
+  TID_SRVR_BLK *new_srvr=NULL;
   json_t *jsrvr;
   size_t i, num_servers;
 
@@ -397,20 +404,30 @@ static TID_SRVR_BLK *tr_msg_decode_servers(void * ctx, json_t *jservers, size_t
   
   if (0 == num_servers) {
     tr_debug("tr_msg_decode_servers(): Server array is empty."); 
-    return NULL;
+    goto cleanup;
   }
-  servers = talloc_zero_array(ctx, TID_SRVR_BLK, num_servers);
 
   for (i = 0; i < num_servers; i++) {
     jsrvr = json_array_get(jservers, i);
-    if (0 != tr_msg_decode_one_server(jsrvr, &servers[i])) {
-      talloc_free(servers);
-      return NULL;
-    }
 
+    new_srvr=tid_srvr_blk_new(tmp_ctx);
+    if (new_srvr==NULL) {
+      servers=NULL; /* it's all in tmp_ctx, so we can just let go */
+      goto cleanup;
+    }
+    
+    if (0 != tr_msg_decode_one_server(jsrvr, new_srvr)) {
+      servers=NULL; /* it's all in tmp_ctx, so we can just let go */
+      goto cleanup;
+    }
 
+    tid_srvr_blk_add(servers, new_srvr);
   }
-  *out_len = num_servers;
+
+  talloc_steal(mem_ctx, servers);
+
+cleanup:
+  talloc_free(tmp_ctx);
   return servers;
 }
 
@@ -501,7 +518,7 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
     tr_debug("tr_msg_decode_tidresp(): Success! result = %s.", json_string_value(jresult));
     if ((NULL != (jservers = json_object_get(jresp, "servers"))) ||
        (!json_is_array(jservers))) {
-      tresp->servers = tr_msg_decode_servers(tresp, jservers, &tresp->num_servers); 
+      tresp->servers = tr_msg_decode_servers(tresp, jservers); 
     } 
     else {
       talloc_free(tresp);
@@ -514,18 +531,22 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
     tr_debug("tr_msg_decode_tidresp(): Error! result = %s.", json_string_value(jresult));
     if ((NULL != (jerr_msg = json_object_get(jresp, "err_msg"))) ||
        (!json_is_string(jerr_msg))) {
-      tresp->err_msg = tr_new_name((char *)json_string_value(jerr_msg));
-    }
+      tresp->err_msg = tr_new_name(json_string_value(jerr_msg));
+    } else
+      tresp->err_msg = tr_new_name("No error message set.");
+
+    if (NULL !=(tresp->error_path = json_object_get(jresp, "error_path")))
+      json_incref(tresp->error_path);
   }
 
-  tresp->rp_realm = tr_new_name((char *)json_string_value(jrp_realm));
-  tresp->realm = tr_new_name((char *)json_string_value(jrealm));
-  tresp->comm = tr_new_name((char *)json_string_value(jcomm));
+  tresp->rp_realm = tr_new_name(json_string_value(jrp_realm));
+  tresp->realm = tr_new_name(json_string_value(jrealm));
+  tresp->comm = tr_new_name(json_string_value(jcomm));
 
   /* store optional "orig_coi" field */
   if ((NULL != (jorig_coi = json_object_get(jresp, "orig_coi"))) &&
       (!json_is_object(jorig_coi))) {
-    tresp->orig_coi = tr_new_name((char *)json_string_value(jorig_coi));
+    tresp->orig_coi = tr_new_name(json_string_value(jorig_coi));
   }
      
   return tresp;
@@ -534,7 +555,7 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
 
 /* Information records for TRP update msg 
  * requires that jrec already be allocated */
-static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec )
+static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec)
 {
   json_t *jstr=NULL;
   json_t *jint=NULL;
@@ -543,43 +564,155 @@ static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec )
   if (rec==NULL)
     return TRP_BADTYPE;
 
-  if ((trp_inforec_get_comm(rec)==NULL)
-     || (trp_inforec_get_realm(rec)==NULL)
-     || (trp_inforec_get_trust_router(rec)==NULL)) {
+  if (trp_inforec_get_trust_router(rec)==NULL)
     return TRP_ERROR;
-  }
 
-  s=tr_name_strdup(trp_inforec_get_comm(rec));
+  s=tr_name_strdup(trp_inforec_get_trust_router(rec));
   if (s==NULL)
     return TRP_NOMEM;
   jstr=json_string(s);
   free(s);s=NULL;
   if(jstr==NULL)
     return TRP_ERROR;
-  json_object_set_new(jrec, "community", jstr);
+  json_object_set_new(jrec, "trust_router", jstr);
+
+  jint=json_integer(trp_inforec_get_metric(rec));
+  if(jint==NULL)
+    return TRP_ERROR;
+  json_object_set_new(jrec, "metric", jint);
 
-  s=tr_name_strdup(trp_inforec_get_realm(rec));
-  if (s==NULL)
-    return TRP_NOMEM;
-  jstr=json_string(s);
-  free(s);s=NULL;
+  jint=json_integer(trp_inforec_get_interval(rec));
+  if(jint==NULL)
+    return TRP_ERROR;
+  json_object_set_new(jrec, "interval", jint);
+
+  return TRP_SUCCESS;
+}
+
+/* returns a json array */
+static json_t *tr_msg_encode_apcs(TR_APC *apcs)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC_ITER *iter=tr_apc_iter_new(tmp_ctx);
+  TR_APC *apc=NULL;
+  json_t *jarray=NULL;
+  json_t *jid=NULL;
+
+  if (iter==NULL)
+    goto cleanup;
+
+  jarray=json_array();
+  if (jarray==NULL)
+    goto cleanup;
+
+  for (apc=tr_apc_iter_first(iter, apcs); apc!=NULL; apc=tr_apc_iter_next(iter)) {
+    jid=tr_name_to_json_string(tr_apc_get_id(apc));
+    if ((jid==NULL) || (json_array_append_new(jarray, jid)!=0)) {
+      json_decref(jarray);
+      jarray=NULL;
+      goto cleanup;
+    }
+  }
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return jarray;
+}
+
+static TR_APC *tr_msg_decode_apcs(TALLOC_CTX *mem_ctx, json_t *jarray, TRP_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  size_t ii=0;
+  TR_APC *apc_list=NULL;
+  TR_APC *new=NULL;
+  json_t *jstr=NULL;
+
+  *rc=TRP_ERROR;
+
+  for (ii=0; ii<json_array_size(jarray); ii++) {
+    jstr=json_array_get(jarray, ii);
+    new=tr_apc_new(tmp_ctx);
+    if ((jstr==NULL) || (new==NULL) || (!json_is_string(jstr))) {
+      apc_list=NULL; /* these are all in tmp_ctx, so they'll still get cleaned up */
+      goto cleanup;
+    }
+
+    tr_apc_set_id(new, tr_new_name(json_string_value(jstr)));
+    if (tr_apc_get_id(new)==NULL) {
+      apc_list=NULL; /* these are all in tmp_ctx, so they'll still get cleaned up */
+      goto cleanup;
+    }
+
+    tr_apc_add(apc_list, new);
+  }
+
+  *rc=TRP_SUCCESS;
+
+  if (apc_list!=NULL)
+    talloc_steal(mem_ctx, apc_list);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return apc_list;
+}
+
+static TRP_RC tr_msg_encode_inforec_comm(json_t *jrec, TRP_INFOREC *rec)
+{
+  json_t *jstr=NULL;
+  json_t *jint=NULL;
+  json_t *japcs=NULL;
+  const char *sconst=NULL;
+  TR_COMM_TYPE commtype=TR_COMM_UNKNOWN;
+
+  if (rec==NULL)
+    return TRP_BADTYPE;
+
+  commtype=trp_inforec_get_comm_type(rec);
+  if (commtype==TR_COMM_UNKNOWN) {
+    tr_notice("tr_msg_encode_inforec_comm: unknown community type.");
+    return TRP_ERROR;
+  }
+  sconst=tr_comm_type_to_str(commtype);
+  if (sconst==NULL)
+    return TRP_ERROR;
+  jstr=json_string(sconst);
   if(jstr==NULL)
     return TRP_ERROR;
-  json_object_set_new(jrec, "realm", jstr);
+  json_object_set_new(jrec, "type", jstr);
 
-  s=tr_name_strdup(trp_inforec_get_trust_router(rec));
-  if (s==NULL)
-    return TRP_NOMEM;
-  jstr=json_string(s);
-  free(s);s=NULL;
+  sconst=tr_realm_role_to_str(trp_inforec_get_role(rec));
+  if (sconst==NULL) {
+    tr_notice("tr_msg_encode_inforec_comm: unknown realm role.");
+    return TRP_ERROR;
+  }
+  jstr=json_string(sconst);
   if(jstr==NULL)
     return TRP_ERROR;
-  json_object_set_new(jrec, "trust_router", jstr);
+  json_object_set_new(jrec, "role", jstr);
 
-  jint=json_integer(trp_inforec_get_metric(rec));
-  if(jint==NULL)
+  japcs=tr_msg_encode_apcs(trp_inforec_get_apcs(rec));
+  if (japcs==NULL) {
+    tr_notice("tr_msg_encode_inforec_comm: error encoding APCs.");
     return TRP_ERROR;
-  json_object_set_new(jrec, "metric", jint);
+  }
+  json_object_set_new(jrec, "apcs", japcs);
+  
+
+  if (trp_inforec_get_owner_realm(rec)!=NULL) {
+    jstr=tr_name_to_json_string(trp_inforec_get_owner_realm(rec));
+    if(jstr==NULL)
+      return TRP_ERROR;
+    json_object_set_new(jrec, "owner_realm", jstr);
+  }  
+
+  if (trp_inforec_get_owner_contact(rec)!=NULL) {
+    jstr=tr_name_to_json_string(trp_inforec_get_owner_contact(rec));
+    if(jstr==NULL)
+      return TRP_ERROR;
+    json_object_set_new(jrec, "owner_contact", jstr);
+  }  
+
+  json_object_set(jrec, "provenance", trp_inforec_get_provenance(rec));
 
   jint=json_integer(trp_inforec_get_interval(rec));
   if(jint==NULL)
@@ -615,6 +748,12 @@ static json_t *tr_msg_encode_inforec(TRP_INFOREC *rec)
       return NULL;
     }
     break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (TRP_SUCCESS!=tr_msg_encode_inforec_comm(jrec, rec)) {
+      json_decref(jrec);
+      return NULL;
+    }
+    break;
   default:
     json_decref(jrec);
     return NULL;
@@ -622,66 +761,141 @@ static json_t *tr_msg_encode_inforec(TRP_INFOREC *rec)
   return jrec;
 }
 
-/* decode a single record */
-static TRP_INFOREC *tr_msg_decode_trp_inforec(TALLOC_CTX *mem_ctx, json_t *jrecord)
+static TRP_RC tr_msg_decode_trp_inforec_route(json_t *jrecord, TRP_INFOREC *rec)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TRP_INFOREC_TYPE rectype;
-  TRP_INFOREC *rec=NULL;
   TRP_RC rc=TRP_ERROR;
   char *s=NULL;
   int num=0;
-  
-  if (0!=tr_msg_get_json_string(jrecord, "record_type", &s, tmp_ctx))
-    goto cleanup;
 
-  rectype=trp_inforec_type_from_string(s);
+  rc=tr_msg_get_json_string(jrecord, "trust_router", &s, tmp_ctx);
+  if (rc != TRP_SUCCESS)
+    goto cleanup;
+  if (TRP_SUCCESS!=trp_inforec_set_trust_router(rec, tr_new_name(s))) {
+    rc=TRP_ERROR;
+    goto cleanup;
+  }
   talloc_free(s); s=NULL;
 
-  rec=trp_inforec_new(tmp_ctx, rectype);
-  if (rec==NULL) {
-    rc=TRP_NOMEM;
+  trp_inforec_set_next_hop(rec, NULL); /* make sure this is null (filled in later) */
+
+  rc=tr_msg_get_json_integer(jrecord, "metric", &num);
+  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_metric(rec,num)))
     goto cleanup;
-  }
 
-  /* We only support route_info records for now*/
-  if (trp_inforec_get_type(rec)!=TRP_INFOREC_TYPE_ROUTE) {
-    rc=TRP_UNSUPPORTED;
+  rc=tr_msg_get_json_integer(jrecord, "interval", &num);
+  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num)))
     goto cleanup;
-  }
 
-  tr_debug("tr_msg_decode_trp_inforec: '%s' record found.", trp_inforec_type_to_string(rec->type));
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static TRP_RC tr_msg_decode_trp_inforec_comm(json_t *jrecord, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_RC rc=TRP_ERROR;
+  char *s=NULL;
+  int num=0;
+  TR_APC *apcs=NULL;
 
-  rc=tr_msg_get_json_string(jrecord, "community", &s, tmp_ctx);
+  rc=tr_msg_get_json_string(jrecord, "type", &s, tmp_ctx);
   if (rc != TRP_SUCCESS)
     goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_comm(rec, tr_new_name(s)))
+  if (TRP_SUCCESS!=trp_inforec_set_comm_type(rec, tr_comm_type_from_str(s))) {
+    rc=TRP_ERROR;
     goto cleanup;
+  }
   talloc_free(s); s=NULL;
 
-  rc=tr_msg_get_json_string(jrecord, "realm", &s, tmp_ctx);
+  rc=tr_msg_get_json_string(jrecord, "role", &s, tmp_ctx);
   if (rc != TRP_SUCCESS)
     goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_realm(rec, tr_new_name(s)))
+  if (TRP_SUCCESS!=trp_inforec_set_role(rec, tr_realm_role_from_str(s))) {
+    rc=TRP_ERROR;
     goto cleanup;
+  }
   talloc_free(s); s=NULL;
 
-  rc=tr_msg_get_json_string(jrecord, "trust_router", &s, tmp_ctx);
-  if (rc != TRP_SUCCESS)
+  apcs=tr_msg_decode_apcs(rec, json_object_get(jrecord, "apcs"), &rc);
+  if (rc!=TRP_SUCCESS) {
+    rc=TRP_ERROR;
     goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_trust_router(rec, tr_new_name(s)))
+  }
+  trp_inforec_set_apcs(rec, apcs);
+
+  rc=tr_msg_get_json_integer(jrecord, "interval", &num);
+  tr_debug("tr_msg_decode_trp_inforec_comm: interval=%u", num);
+  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num)))
     goto cleanup;
-  talloc_free(s); s=NULL;
 
-  trp_inforec_set_next_hop(rec, NULL); /* make sure this is null (filled in later) */
+  trp_inforec_set_provenance(rec, json_object_get(jrecord, "provenance"));
 
-  rc=tr_msg_get_json_integer(jrecord, "metric", &num);
-  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_metric(rec,num)))
+  /* optional */
+  rc=tr_msg_get_json_string(jrecord, "owner_realm", &s, tmp_ctx);
+  if (rc == TRP_SUCCESS) {
+    if (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_new_name(s))) {
+      rc=TRP_ERROR;
+      goto cleanup;
+    }
+    if (s!=NULL) {
+      talloc_free(s);
+      s=NULL;
+    }
+  }
+
+  rc=tr_msg_get_json_string(jrecord, "owner_contact", &s, tmp_ctx);
+  if (rc == TRP_SUCCESS) {
+    if (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_new_name(s))) {
+      rc=TRP_ERROR;
+      goto cleanup;
+    }
+    if (s!=NULL) {
+      talloc_free(s);
+      s=NULL;
+    }
+  }
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* decode a single record */
+static TRP_INFOREC *tr_msg_decode_trp_inforec(TALLOC_CTX *mem_ctx, json_t *jrecord)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_INFOREC_TYPE rectype;
+  TRP_INFOREC *rec=NULL;
+  TRP_RC rc=TRP_ERROR;
+  char *s=NULL;
+  
+  if (TRP_SUCCESS!=tr_msg_get_json_string(jrecord, "record_type", &s, tmp_ctx))
     goto cleanup;
 
-  rc=tr_msg_get_json_integer(jrecord, "interval", &num);
-  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num)))
+  rectype=trp_inforec_type_from_string(s);
+  talloc_free(s); s=NULL;
+
+  rec=trp_inforec_new(tmp_ctx, rectype);
+  if (rec==NULL) {
+    rc=TRP_NOMEM;
     goto cleanup;
+  }
+
+  tr_debug("tr_msg_decode_trp_inforec: '%s' record found.", trp_inforec_type_to_string(rec->type));
+
+  switch(trp_inforec_get_type(rec)) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    rc=tr_msg_decode_trp_inforec_route(jrecord, rec);
+    break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    rc=tr_msg_decode_trp_inforec_comm(jrecord, rec);
+    break;
+  default:
+    rc=TRP_UNSUPPORTED;
+    goto cleanup;
+  }
 
   talloc_steal(mem_ctx, rec);
   rc=TRP_SUCCESS;
@@ -701,7 +915,9 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update)
   json_t *jupdate=NULL;
   json_t *jrecords=NULL;
   json_t *jrec=NULL;
+  json_t *jstr=NULL;
   TRP_INFOREC *rec;
+  char *s=NULL;
 
   if (update==NULL)
     return NULL;
@@ -710,6 +926,32 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update)
   if (jupdate==NULL)
     return NULL;
 
+  s=tr_name_strdup(trp_upd_get_comm(update));
+  if (s==NULL) {
+    json_decref(jupdate);
+    return NULL;
+  }
+  jstr=json_string(s);
+  free(s);s=NULL;
+  if(jstr==NULL) {
+    json_decref(jupdate);
+    return NULL;
+  }
+  json_object_set_new(jupdate, "community", jstr);
+
+  s=tr_name_strdup(trp_upd_get_realm(update));
+  if (s==NULL) {
+    json_decref(jupdate);
+    return NULL;
+  }
+  jstr=json_string(s);
+  free(s);s=NULL;
+  if(jstr==NULL) {
+    json_decref(jupdate);
+    return NULL;
+  }
+  json_object_set_new(jupdate, "realm", jstr);
+
   jrecords=json_array();
   if (jrecords==NULL) {
     json_decref(jupdate);
@@ -733,7 +975,7 @@ static json_t *tr_msg_encode_trp_upd(TRP_UPD *update)
   return jupdate;
 }
 
-/*Creates a linked list of records in the msg->body talloc context.
+/* Creates a linked list of records in the msg->body talloc context.
  * An error will be returned if any unparseable records are encountered. 
  */
 static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate)
@@ -744,6 +986,8 @@ static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate)
   TRP_UPD *update=NULL;
   TRP_INFOREC *new_rec=NULL;
   TRP_INFOREC *list_tail=NULL;
+  char *s=NULL;
+  TR_NAME *name;
   TRP_RC rc=TRP_ERROR;
 
   update=trp_upd_new(tmp_ctx);
@@ -752,6 +996,36 @@ static TRP_UPD *tr_msg_decode_trp_upd(TALLOC_CTX *mem_ctx, json_t *jupdate)
     goto cleanup;
   }
 
+  rc=tr_msg_get_json_string(jupdate, "community", &s, tmp_ctx);
+  if (rc != TRP_SUCCESS) {
+    tr_debug("tr_msg_decode_trp_upd: no community in TRP update message.");
+    rc=TRP_NOPARSE;
+    goto cleanup;
+  }
+  name=tr_new_name(s);
+  if (name==NULL) {
+    tr_debug("tr_msg_decode_trp_upd: could not allocate community name.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+  talloc_free(s); s=NULL;
+  trp_upd_set_comm(update, name);
+
+  rc=tr_msg_get_json_string(jupdate, "realm", &s, tmp_ctx);
+  if (rc != TRP_SUCCESS) {
+    tr_debug("tr_msg_decode_trp_upd: no realm in TRP update message.");
+    rc=TRP_NOPARSE;
+    goto cleanup;
+  }
+  name=tr_new_name(s);
+  if (name==NULL) {
+    tr_debug("tr_msg_decode_trp_upd: could not allocate realm name.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+  talloc_free(s); s=NULL;
+  trp_upd_set_realm(update, name);
+
   jrecords=json_object_get(jupdate, "records");
   if ((jrecords==NULL) || (!json_is_array(jrecords))) {
     rc=TRP_NOPARSE;
@@ -919,6 +1193,7 @@ char *tr_msg_encode(TR_MSG *msg)
     }
 
   encoded=json_dumps(jmsg, 0);
+  tr_debug("tr_msg_encode: outgoing msg=%s", encoded);
   json_decref(jmsg);
   return encoded;
 }
@@ -975,6 +1250,9 @@ TR_MSG *tr_msg_decode(char *jbuf, size_t buflen)
     msg->msg_type = TR_UNKNOWN;
     msg->msg_rep = NULL;
   }
+
+  json_decref(jmsg);
+
   return msg;
 }
 
index 4480a6a..eacef63 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <jansson.h>
 
 #include <trust_router/tr_name.h>
 
@@ -123,6 +124,15 @@ char * tr_name_strdup(TR_NAME *src)
   return s;
 }
 
+json_t *tr_name_to_json_string(TR_NAME *src)
+{
+  char *s=tr_name_strdup(src);
+  json_t *js=json_string(s);
+  if (s!=NULL)
+    free(s);
+  return js;
+}
+
 TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2)
 {
   char *s=malloc(n1->len+n2->len+1);
index 96c7254..94a6360 100644 (file)
@@ -75,7 +75,8 @@ static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client)
   return client;
 }
 
-TR_RP_CLIENT *tr_rp_client_add(TR_RP_CLIENT *clients, TR_RP_CLIENT *new)
+/* do not call directly, use the tr_rp_client_add() macro */
+TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new)
 {
   if (clients==NULL)
     clients=new;
@@ -120,6 +121,47 @@ TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name)
   return NULL;
 }
 
+TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name)
+{
+  TR_RP_REALM *rp = NULL;
+
+  if (!rp_name) {
+    tr_debug("tr_rp_realm_lookup: Bad parameters.");
+    return NULL;
+  }
+
+  for (rp=rp_realms; NULL!=rp; rp=rp->next) {
+    if (0==tr_name_cmp(tr_rp_realm_get_id(rp), rp_name))
+      return rp;
+  } 
+  return NULL;
+}
+
+static int tr_rp_realm_destructor(void *obj)
+{
+  TR_RP_REALM *rp=talloc_get_type_abort(obj, TR_RP_REALM);
+  if (rp->realm_id!=NULL)
+    tr_free_name(rp->realm_id);
+  return 0;
+}
+
+TR_RP_REALM *tr_rp_realm_new(TALLOC_CTX *mem_ctx)
+{
+  TR_RP_REALM *rp=talloc(mem_ctx, TR_RP_REALM);
+  if (rp!=NULL) {
+    rp->next=NULL;
+    rp->realm_id=NULL;
+    rp->refcount=0;
+    talloc_set_destructor((void *)rp, tr_rp_realm_destructor);
+  }
+  return rp;
+}
+
+void tr_rp_realm_free(TR_RP_REALM *rp)
+{
+  talloc_free(rp);
+}
+
 /* talloc note: lists of idp realms should be assembled using
  * tr_idp_realm_add(). This will put all of the elements in the
  * list, other than the head, as children of the head context.
@@ -127,13 +169,17 @@ TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name)
 
 static TR_RP_REALM *tr_rp_realm_tail(TR_RP_REALM *realm)
 {
-  while (realm!=NULL)
+  if (realm==NULL)
+    return NULL;
+
+  while (realm->next!=NULL)
     realm=realm->next;
   return realm;
 }
 
-/* for correct behavior, call like: rp_realms=tr_rp_realm_add(rp_realms, new_realm); */
-TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new)
+/* for correct behavior, call like: rp_realms=tr_rp_realm_add_func(rp_realms, new_realm);
+ * or better yet, use the macro */
+TR_RP_REALM *tr_rp_realm_add_func(TR_RP_REALM *head, TR_RP_REALM *new)
 {
   if (head==NULL)
     head=new;
@@ -146,3 +192,106 @@ TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new)
   }
   return head;
 }
+
+/* use the macro */
+TR_RP_REALM *tr_rp_realm_remove_func(TR_RP_REALM *head, TR_RP_REALM *remove)
+{
+  TALLOC_CTX *list_ctx=talloc_parent(head);
+  TR_RP_REALM *this=NULL;
+
+  if (head==NULL)
+    return NULL;
+
+  if (head==remove) {
+    /* if we're removing the head, put the next element (if present) into the context
+     * the list head was in. */
+    head=head->next;
+    if (head!=NULL) {
+      talloc_steal(list_ctx, head);
+      /* now put all the other elements in the context of the list head */
+      for (this=head->next; this!=NULL; this=this->next)
+        talloc_steal(head, this);
+    }
+  } else {
+    /* not removing the head; no need to play with contexts */
+    for (this=head; this->next!=NULL; this=this->next) {
+      if (this->next==remove) {
+        this->next=remove->next;
+        break;
+      }
+    }
+  }
+  return head;
+}
+
+void tr_rp_realm_incref(TR_RP_REALM *realm)
+{
+  realm->refcount++;
+}
+
+void tr_rp_realm_decref(TR_RP_REALM *realm)
+{
+  if (realm->refcount>0)
+    realm->refcount--;
+}
+
+/* remove any with zero refcount 
+ * Call via macro. */
+TR_RP_REALM *tr_rp_realm_sweep_func(TR_RP_REALM *head)
+{
+  TR_RP_REALM *rp=NULL;
+  TR_RP_REALM *old_next=NULL;
+
+  if (head==NULL)
+    return NULL;
+
+  while ((head!=NULL) && (head->refcount==0)) {
+    rp=head; /* keep a pointer so we can remove it */
+    tr_rp_realm_remove(head, rp); /* use this to get talloc contexts right */
+    tr_rp_realm_free(rp);
+  }
+
+  if (head==NULL)
+    return NULL;
+
+  /* will not remove the head here, that has already been done */
+  for (rp=head; rp->next!=NULL; rp=rp->next) {
+    if (rp->next->refcount==0) {
+      old_next=rp->next;
+      tr_rp_realm_remove(head, rp->next); /* changes rp->next */
+      tr_rp_realm_free(old_next);
+    }
+  }
+
+  return head;
+}
+
+TR_NAME *tr_rp_realm_get_id(TR_RP_REALM *rp)
+{
+  if (rp==NULL)
+    return NULL;
+
+  return rp->realm_id;
+}
+
+TR_NAME *tr_rp_realm_dup_id(TR_RP_REALM *rp)
+{
+  if (rp==NULL)
+    return NULL;
+
+  return tr_dup_name(tr_rp_realm_get_id(rp));
+}
+
+void tr_rp_realm_set_id(TR_RP_REALM *rp, TR_NAME *id)
+{
+  if (rp->realm_id!=NULL)
+    tr_free_name(rp->realm_id);
+  rp->realm_id=id;
+}
+
+char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp)
+{
+  return talloc_asprintf(mem_ctx,
+                         "RP realm: \"%.*s\"\n",
+                         rp->realm_id->len, rp->realm_id->buf);
+}
index 9562d9c..2ce3c82 100644 (file)
@@ -35,7 +35,9 @@
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
+#include <time.h>
 #include <trust_router/tr_dh.h>
+#include <tr_util.h>
 
 void tr_bin_to_hex(const unsigned char * bin, size_t bin_len,
                   char * hex_out, size_t hex_len)
@@ -49,3 +51,23 @@ void tr_bin_to_hex(const unsigned char * bin, size_t bin_len,
   }
 }
 
+/* Returns 0 if ts1==ts2, <0 if ts1<ts2, >= if ts1>ts2.
+ * Assumes that tv_nsec <= 1e9. */
+int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
+{
+  if (ts1->tv_sec > ts2->tv_sec)
+    return 1;
+
+  if (ts1->tv_sec < ts2->tv_sec)
+    return -1;
+
+  /* ts1->tv_sec==ts2->tv_sec */
+
+  if (ts1->tv_nsec > ts2->tv_nsec)
+    return 1;
+
+  if (ts1->tv_nsec < ts2->tv_nsec)
+    return -1;
+
+  return 0;
+}
index 2a89f00..dabfd1e 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.63)
-AC_INIT([trust_router],[1.5.2],
+AC_INIT([trust_router],[2.1.1],
 [bugs@project-moonshot.org])
 AC_CONFIG_MACRO_DIR(m4)
 AC_CONFIG_AUX_DIR(build-aux)
index 1f4d91f..1227c2b 100755 (executable)
 #include <gsscon.h>
 
 /* ---------------------------------------------------------------------------
-*/
+ */
 
 int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServiceName, int *outFD, gss_ctx_id_t *outGSSContext)
 {
-    int err = 0;
-    int fd = -1;
-    OM_uint32 majorStatus;
-    OM_uint32 minorStatus = 0, minorStatusToo = 0;
-    struct hostent *hp = NULL;
-    struct sockaddr_in saddr;
-    gss_name_t serviceName = NULL;
-    gss_name_t clientName = NULL;
-    gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL;
-    gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
-    OM_uint32 actualFlags = 0;
-    char *inputTokenBuffer = NULL;
-    size_t inputTokenBufferLength = 0;
-    gss_buffer_desc inputToken;  /* buffer received from the server */
-    gss_buffer_desc nameBuffer;
-    gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
-    char *name;
+  int err = 0;
+  int fd = -1;
+  OM_uint32 majorStatus;
+  OM_uint32 minorStatus = 0, minorStatusToo = 0;
+  struct addrinfo *ai=NULL;
+  struct addrinfo *ai_head=NULL;
+  struct addrinfo hints={.ai_family=AF_UNSPEC, .ai_socktype=SOCK_STREAM, .ai_protocol=IPPROTO_TCP};
+  struct sockaddr_in saddr;
+  char *port=NULL;
+  gss_name_t serviceName = NULL;
+  gss_name_t clientName = NULL;
+  gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL;
+  gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
+  OM_uint32 actualFlags = 0;
+  char *inputTokenBuffer = NULL;
+  size_t inputTokenBufferLength = 0;
+  gss_buffer_desc inputToken;  /* buffer received from the server */
+  gss_buffer_desc nameBuffer;
+  gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
+  char *name;
 
-    if (!inServiceName) { err = EINVAL; }
-    if (!outGSSContext) { err = EINVAL; }
-    
-    if (!err) {
-        hp = gethostbyname (inHost);
-        if (hp == NULL) { err = h_errno; }
-    }
+  if (!inServiceName) { err = EINVAL; }
+  if (!outGSSContext) { err = EINVAL; }
     
-    if (!err) {
-       saddr.sin_family = hp->h_addrtype;
-       memcpy ((char *) &saddr.sin_addr, hp->h_addr, sizeof (saddr.sin_addr));
-       saddr.sin_port = htons(inPort);
-        
-        fd = socket (AF_INET, SOCK_STREAM, 0);
-        if (fd < 0) { err = errno; }
-    }
+  if (!err) {
+    /* get a string for getaddrinfo */
+    if (asprintf(&port, "%d", inPort)>0) { 
+      err=getaddrinfo(inHost, port, &hints, &ai_head);
+      free(port);
+    } else
+      err=1;
+  }
     
-    if (!err) {
-        fprintf (stderr, "gss_connect: Connecting to host '%s' on port %d\n", inHost, inPort);
-        err = connect (fd, (struct sockaddr *) &saddr, sizeof (saddr));
-        if (err < 0) { err = errno; }
+  if (!err) {
+    /* try all options returned until one works */
+    for (ai=ai_head,fd=-1; (ai!=NULL) && (fd==-1); ai=ai->ai_next) {
+      fd=socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+      if (fd < 0) {
+        fd=-1;
+        continue;
+      }
+
+      fprintf(stderr, "gss_connect: Connecting to host '%s' on port %d\n", inHost, inPort);
+      err=connect(fd, ai->ai_addr, ai->ai_addrlen);
+      if (err!=0) {
+        close(fd);
+        fd=-1;
+        continue;
+      }
     }
+
+    if (fd==-1)
+      err=1;
+  }
     
-    if (!err) {
-        *outFD = fd;
-        fd = -1; /* takes ownership */
-    } else {
-        gsscon_print_error (err, "OpenConnection failed");
-    }
+  if (!err) {
+    *outFD = fd;
+    fd = -1; /* takes ownership */
+  } else {
+    gsscon_print_error (err, "OpenConnection failed");
+  }
     
-    if (fd >= 0) { close (fd); }
+  if (fd >= 0) { close (fd); }
 
-    if (!err) {
-      majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, 
-                                                GSS_C_INITIATE, &clientCredentials, NULL, NULL); 
-      if (majorStatus != GSS_S_COMPLETE) { 
-       gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus);
-       err = minorStatus ? minorStatus : majorStatus; 
-      }
+  if (!err) {
+    majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, 
+                                    GSS_C_INITIATE, &clientCredentials, NULL, NULL); 
+    if (majorStatus != GSS_S_COMPLETE) { 
+      gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus);
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
     
-    /*
-     * Here is where the client picks the service principal it will
-     * try to use to connect to the server.  In the case of the
-     * gssClientSample, the service principal is passed in on the
-     * command line, however, in a real world example, this would be
-     * unacceptable from a user interface standpoint since the user
-     * shouldn't need to know the server's service principal.
-     
-     * In traditional Kerberos setups, the service principal would be
-     * constructed from the type of the service (eg: "imap"), the DNS
-     * hostname of the server (eg: "mailserver.domain.com") and the
-     * client's local realm (eg: "DOMAIN.COM") to form a full
-     * principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM").
-     *
-     * Now that many sites do not have DNS, this setup is becoming
-     * less common.  However you decide to generate the service
-     * principal, you need to adhere to the following constraint: The
-     * service principal must be constructed by the client, typed in
-     * by the user or administrator, or transmitted to the client in a
-     * secure manner from a trusted third party -- such as through an
-     * encrypted connection to a directory server.  You should not
-     * have the server send the client the service principal name as
-     * part of the authentication negotiation.
-     *
-     * The reason you can't let the server tell the client which
-     * principal to use is that many machines at a site will have
-     * their own service principal and keytab which identifies the
-     * machine -- in a Windows Active Directory environment all
-     * machines have a service principal and keytab.  Some of these
-     * machines (such as a financial services server) will be more
-     * trustworthy than others (such as a random machine on a
-     * coworker's desk).  If the owner of one of these untrustworthy
-     * machines can trick the client into using the untrustworthy
-     * machine's principal instead of the financial services server's
-     * principal, then he can trick the client into authenticating and
-     * connecting to the untrustworthy machine.  The untrustworthy
-     * machine can then harvest any confidential information the
-     * client sends to it, such as credit card information or social
-     * security numbers.
-     *
-     * If your protocol already involves sending the service principal
-     * as part of your authentication negotiation, your client should
-     * cache the name it gets after the first successful
-     * authentication so that the problem above can only happen on the
-     * first connection attempt -- similar to what ssh does with host
-     * keys.
-     */
+  /*
+   * Here is where the client picks the service principal it will
+   * try to use to connect to the server.  In the case of the
+   * gssClientSample, the service principal is passed in on the
+   * command line, however, in a real world example, this would be
+   * unacceptable from a user interface standpoint since the user
+   * shouldn't need to know the server's service principal.
+   * 
+   * In traditional Kerberos setups, the service principal would be
+   * constructed from the type of the service (eg: "imap"), the DNS
+   * hostname of the server (eg: "mailserver.domain.com") and the
+   * client's local realm (eg: "DOMAIN.COM") to form a full
+   * principal string (eg: "imap/mailserver.domain.com@DOMAIN.COM").
+   *
+   * Now that many sites do not have DNS, this setup is becoming
+   * less common.  However you decide to generate the service
+   * principal, you need to adhere to the following constraint: The
+   * service principal must be constructed by the client, typed in
+   * by the user or administrator, or transmitted to the client in a
+   * secure manner from a trusted third party -- such as through an
+   * encrypted connection to a directory server.  You should not
+   * have the server send the client the service principal name as
+   * part of the authentication negotiation.
+   *
+   * The reason you can't let the server tell the client which
+   * principal to use is that many machines at a site will have
+   * their own service principal and keytab which identifies the
+   * machine -- in a Windows Active Directory environment all
+   * machines have a service principal and keytab.  Some of these
+   * machines (such as a financial services server) will be more
+   * trustworthy than others (such as a random machine on a
+   * coworker's desk).  If the owner of one of these untrustworthy
+   * machines can trick the client into using the untrustworthy
+   * machine's principal instead of the financial services server's
+   * principal, then he can trick the client into authenticating and
+   * connecting to the untrustworthy machine.  The untrustworthy
+   * machine can then harvest any confidential information the
+   * client sends to it, such as credit card information or social
+   * security numbers.
+   *
+   * If your protocol already involves sending the service principal
+   * as part of your authentication negotiation, your client should
+   * cache the name it gets after the first successful
+   * authentication so that the problem above can only happen on the
+   * first connection attempt -- similar to what ssh does with host
+   * keys.
+   */
     
-    if (!err) {
-      nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost);
-      nameBuffer.value = name;
+  if (!err) {
+    nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost);
+    nameBuffer.value = name;
 
-      majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
-      if (majorStatus != GSS_S_COMPLETE) { 
-       gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
-       err = minorStatus ? minorStatus : majorStatus; 
-      }
+    majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
+    if (majorStatus != GSS_S_COMPLETE) { 
+      gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
     
-    /* 
-     * The main authentication loop:
-     *
-     * GSS is a multimechanism API.  Because the number of packet
-     * exchanges required to authenticate can vary between mechanisms,
-     * we need to loop calling gss_init_sec_context, passing the
-     * "input tokens" received from the server and send the resulting
-     * "output tokens" back until we get GSS_S_COMPLETE or an error.
-     */
+  /* 
+   * The main authentication loop:
+   *
+   * GSS is a multimechanism API.  Because the number of packet
+   * exchanges required to authenticate can vary between mechanisms,
+   * we need to loop calling gss_init_sec_context, passing the
+   * "input tokens" received from the server and send the resulting
+   * "output tokens" back until we get GSS_S_COMPLETE or an error.
+   */
 
-    majorStatus = GSS_S_CONTINUE_NEEDED;
+  majorStatus = GSS_S_CONTINUE_NEEDED;
 
-    gss_OID_desc EAP_OID = { 9, "\x2B\x06\x01\x05\x05\x0F\x01\x01\x11" };
+  gss_OID_desc EAP_OID = { 9, "\x2B\x06\x01\x05\x05\x0F\x01\x01\x11" };
  
-    while (!err && (majorStatus != GSS_S_COMPLETE)) {
-        gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */
-        OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | 
-                                    GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
+  while (!err && (majorStatus != GSS_S_COMPLETE)) {
+    gss_buffer_desc outputToken = { 0, NULL }; /* buffer to send to the server */
+    OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | 
+                                GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
         
-        majorStatus = gss_init_sec_context (&minorStatus, 
-                                            clientCredentials, 
-                                            &gssContext, 
-                                            serviceName, 
-                                            &EAP_OID /* mech_type */,
-                                            requestedFlags, 
-                                            GSS_C_INDEFINITE, 
-                                            GSS_C_NO_CHANNEL_BINDINGS, 
-                                            inputTokenPtr,
-                                            NULL /* actual_mech_type */, 
-                                            &outputToken, 
-                                            &actualFlags, 
-                                            NULL /* time_rec */);
+    majorStatus = gss_init_sec_context (&minorStatus, 
+                                        clientCredentials, 
+                                       &gssContext, 
+                                        serviceName, 
+                                       &EAP_OID /* mech_type */,
+                                        requestedFlags, 
+                                        GSS_C_INDEFINITE, 
+                                        GSS_C_NO_CHANNEL_BINDINGS, 
+                                        inputTokenPtr,
+                                        NULL /* actual_mech_type */, 
+                                       &outputToken, 
+                                       &actualFlags, 
+                                        NULL /* time_rec */);
         
-        /* Send the output token to the server (even on error) */
-        if ((outputToken.length > 0) && (outputToken.value != NULL)) {
-            err = gsscon_write_token (*outFD, outputToken.value, outputToken.length);
+    /* Send the output token to the server (even on error) */
+    if ((outputToken.length > 0) && (outputToken.value != NULL)) {
+      err = gsscon_write_token (*outFD, outputToken.value, outputToken.length);
             
-            /* free the output token */
-            gss_release_buffer (&minorStatusToo, &outputToken);
-        }
+      /* free the output token */
+      gss_release_buffer (&minorStatusToo, &outputToken);
+    }
         
-        if (!err) {
-            if (majorStatus == GSS_S_CONTINUE_NEEDED) { 
-                /* Protocol requires another packet exchange */
+    if (!err) {
+      if (majorStatus == GSS_S_CONTINUE_NEEDED) { 
+        /* Protocol requires another packet exchange */
                 
-                /* Clean up old input buffer */
-                if (inputTokenBuffer) {
-                    free (inputTokenBuffer);
-                    inputTokenBuffer = NULL;  /* don't double-free */
-                }
+        /* Clean up old input buffer */
+        if (inputTokenBuffer) {
+          free (inputTokenBuffer);
+          inputTokenBuffer = NULL;  /* don't double-free */
+        }
                 
-                /* Read another input token from the server */
-                err = gsscon_read_token (*outFD, &inputTokenBuffer, &inputTokenBufferLength);
+        /* Read another input token from the server */
+        err = gsscon_read_token (*outFD, &inputTokenBuffer, &inputTokenBufferLength);
                 
-                if (!err) {
-                    /* Set up input buffers for the next run through the loop */
-                    inputToken.value = inputTokenBuffer;
-                    inputToken.length = inputTokenBufferLength;
-                    inputTokenPtr = &inputToken;
-                }
-            } else if (majorStatus != GSS_S_COMPLETE) {
-                gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus);
-                err = minorStatus ? minorStatus : majorStatus; 
-            }
+        if (!err) {
+          /* Set up input buffers for the next run through the loop */
+          inputToken.value = inputTokenBuffer;
+          inputToken.length = inputTokenBufferLength;
+          inputTokenPtr = &inputToken;
         }
+      } else if (majorStatus != GSS_S_COMPLETE) {
+        gsscon_print_gss_errors ("gss_init_sec_context", majorStatus, minorStatus);
+        err = minorStatus ? minorStatus : majorStatus; 
+      }
     }
+  }
     
-    if (!err) { 
-        *outGSSContext = gssContext;
-        gssContext = NULL;
-    } else {
-        gsscon_print_error (err, "AuthenticateToServer failed"); 
-    }
+  if (!err) { 
+    *outGSSContext = gssContext;
+    gssContext = NULL;
+  } else {
+    gsscon_print_error (err, "AuthenticateToServer failed"); 
+  }
 
-    if (inputTokenBuffer) { free (inputTokenBuffer); }
-    if (serviceName     ) { gss_release_name (&minorStatus, &serviceName); }
-    if (clientName      ) { gss_release_name (&minorStatus, &clientName); }
-    
-    if (clientCredentials != GSS_C_NO_CREDENTIAL) { 
-        gss_release_cred (&minorStatus, &clientCredentials); }
-    if (gssContext != GSS_C_NO_CONTEXT) { 
-        gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
+  if (inputTokenBuffer) { free (inputTokenBuffer); }
+  if (serviceName     ) { gss_release_name (&minorStatus, &serviceName); }
+  if (clientName      ) { gss_release_name (&minorStatus, &clientName); }
+  if (ai_head         ) { freeaddrinfo(ai_head); }
+
+  if (clientCredentials != GSS_C_NO_CREDENTIAL) { 
+    gss_release_cred (&minorStatus, &clientCredentials); }
+  if (gssContext != GSS_C_NO_CONTEXT) { 
+    gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
 
-    return err;
+  return err;
 }
 
index 1ede6d3..7cdff69 100755 (executable)
 
 const char *gServiceName = NULL;
 
-int gsscon_passive_authenticate (int               inSocket, 
-                                gss_buffer_desc    inNameBuffer,
-                                gss_ctx_id_t      *outGSSContext,
-                                client_cb_fn       clientCb,
-                                void              *clientCbData)
+int gsscon_passive_authenticate (int              inSocket, 
+                                 gss_buffer_desc    inNameBuffer,
+                                 gss_ctx_id_t      *outGSSContext,
+                                 client_cb_fn       clientCb,
+                                 void              *clientCbData)
 {
-    int err = 0;
-    OM_uint32 majorStatus;
-    OM_uint32 minorStatus = 0, minorStatusToo = 0;
-    gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
-    gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME;
-    gss_cred_id_t acceptorCredentials = NULL;
-    gss_buffer_desc clientDisplayName = {0, NULL};
-    char *inputTokenBuffer = NULL;
-    size_t inputTokenBufferLength = 0;
-    gss_buffer_desc inputToken;  /* buffer received from the server */
+  int err = 0;
+  OM_uint32 majorStatus;
+  OM_uint32 minorStatus = 0, minorStatusToo = 0;
+  gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
+  gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME;
+  gss_cred_id_t acceptorCredentials = NULL;
+  gss_buffer_desc clientDisplayName = {0, NULL};
+  char *inputTokenBuffer = NULL;
+  size_t inputTokenBufferLength = 0;
+  gss_buffer_desc inputToken;  /* buffer received from the server */
     
-    if (inSocket <  0 ) { err = EINVAL; }
-    if (!outGSSContext) { err = EINVAL; }
+  if (inSocket <  0 ) { err = EINVAL; }
+  if (!outGSSContext) { err = EINVAL; }
 
-    if (!err) {
-      majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
-      if (majorStatus != GSS_S_COMPLETE) {
-       gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus);
-       err = minorStatus ? minorStatus : majorStatus; 
-      }
+  if (!err) {
+    majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
+    if (majorStatus != GSS_S_COMPLETE) {
+      gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus);
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
 
-    if (!err) {
-      majorStatus = gss_acquire_cred ( &minorStatus, serviceName,
-                                      GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
-                                      GSS_C_ACCEPT, &acceptorCredentials,
-                                      NULL /*mechs out*/, NULL /*time out*/);
-      if (majorStatus != GSS_S_COMPLETE) { 
-       gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus);
-       err = minorStatus ? minorStatus : majorStatus; 
-      }
+  if (!err) {
+    majorStatus = gss_acquire_cred ( &minorStatus, serviceName,
+                                     GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
+                                     GSS_C_ACCEPT, &acceptorCredentials,
+                                     NULL /*mechs out*/, NULL /*time out*/);
+    if (majorStatus != GSS_S_COMPLETE) { 
+      gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus);
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
 
-    /* 
-     * The main authentication loop:
-     *
-     * GSS is a multimechanism API.  The number of packet exchanges required to  
-     * authenticatevaries between mechanisms.  As a result, we need to loop reading 
-     * input tokens from the client, calling gss_accept_sec_context on the input 
-     * tokens and send the resulting output tokens back to the client until we 
-     * get GSS_S_COMPLETE or an error.
-     *
-     * When we are done, save the client principal so we can make authorization 
-     * checks.
-     */
+  /* 
+   * The main authentication loop:
+   *
+   * GSS is a multimechanism API.  The number of packet exchanges required to  
+   * authenticatevaries between mechanisms.  As a result, we need to loop reading 
+   * input tokens from the client, calling gss_accept_sec_context on the input 
+   * tokens and send the resulting output tokens back to the client until we 
+   * get GSS_S_COMPLETE or an error.
+   *
+   * When we are done, save the client principal so we can make authorization 
+   * checks.
+   */
     
-    majorStatus = GSS_S_CONTINUE_NEEDED;
-    while (!err && (majorStatus != GSS_S_COMPLETE)) {
-        /* Clean up old input buffer */
-        if (inputTokenBuffer != NULL) {
-            free (inputTokenBuffer);
-            inputTokenBuffer = NULL;  /* don't double-free */
-        }
+  majorStatus = GSS_S_CONTINUE_NEEDED;
+  while (!err && (majorStatus != GSS_S_COMPLETE)) {
+    /* Clean up old input buffer */
+    if (inputTokenBuffer != NULL) {
+      free (inputTokenBuffer);
+      inputTokenBuffer = NULL;  /* don't double-free */
+    }
         
-        err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
+    err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength);
         
-        if (!err) {
-            /* Set up input buffers for the next run through the loop */
-            inputToken.value = inputTokenBuffer;
-            inputToken.length = inputTokenBufferLength;
-        }
+    if (!err) {
+      /* Set up input buffers for the next run through the loop */
+      inputToken.value = inputTokenBuffer;
+      inputToken.length = inputTokenBufferLength;
+    }
         
-        if (!err) {
-            /* buffer to send to the server */
-            gss_buffer_desc outputToken = { 0, NULL }; 
+    if (!err) {
+      /* buffer to send to the server */
+      gss_buffer_desc outputToken = { 0, NULL }; 
             
-            /*
-             * accept_sec_context does the actual work of taking the client's 
-             * request and generating an appropriate reply.              */
-            majorStatus = gss_accept_sec_context (&minorStatus, 
-                                                  &gssContext, 
-                                                 acceptorCredentials,
-                                                  &inputToken, 
-                                                  GSS_C_NO_CHANNEL_BINDINGS, 
-                                                  &clientName,
-                                                  NULL /* actual_mech_type */,
-                                                  &outputToken, 
-                                                  NULL /* req_flags */, 
-                                                  NULL /* time_rec */, 
-                                                  NULL /* delegated_cred_handle */);
+      /*
+       * accept_sec_context does the actual work of taking the client's 
+       * request and generating an appropriate reply.              */
+      majorStatus = gss_accept_sec_context (&minorStatus, 
+                                           &gssContext, 
+                                            acceptorCredentials,
+                                           &inputToken, 
+                                            GSS_C_NO_CHANNEL_BINDINGS, 
+                                           &clientName,
+                                            NULL /* actual_mech_type */,
+                                           &outputToken, 
+                                            NULL /* req_flags */, 
+                                            NULL /* time_rec */, 
+                                            NULL /* delegated_cred_handle */);
             
-            if ((outputToken.length > 0) && (outputToken.value != NULL)) {
-                /* Send the output token to the client (even on error) */
-                err = gsscon_write_token (inSocket, outputToken.value, outputToken.length);
+      if ((outputToken.length > 0) && (outputToken.value != NULL)) {
+        /* Send the output token to the client (even on error) */
+        err = gsscon_write_token (inSocket, outputToken.value, outputToken.length);
                 
-                /* free the output token */
-                gss_release_buffer (&minorStatusToo, &outputToken);
-            }
-        }
+        /* free the output token */
+        gss_release_buffer (&minorStatusToo, &outputToken);
+      }
+    }
         
-        if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) {
-            gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus);
-            err = minorStatus ? minorStatus : majorStatus; 
-        }            
+    if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) {
+      gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus);
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
 
-    if (!err) {
-      majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL);
-      if (GSS_ERROR(majorStatus)) {
-       gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus);
-       err = EINVAL;
-      }
-      if (!err)
-       err = clientCb(clientName, &clientDisplayName, clientCbData);
+  if (!err) {
+    majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL);
+    if (GSS_ERROR(majorStatus)) {
+      gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus);
+      err = EINVAL;
     }
+    if (!err)
+      err = clientCb(clientName, &clientDisplayName, clientCbData);
+  }
 
-    if (!err) { 
-        *outGSSContext = gssContext;
-        gssContext = NULL;
-    } else {
-        gsscon_print_error (err, "Authenticate failed");
-    }
+  if (!err) { 
+    *outGSSContext = gssContext;
+    gssContext = NULL;
+  } else {
+    gsscon_print_error (err, "Authenticate failed");
+  }
     
-    if (inputTokenBuffer) { free (inputTokenBuffer); }
-    if (gssContext != GSS_C_NO_CONTEXT) { 
-        gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
-if (clientName != GSS_C_NO_NAME)
-  gss_release_name(&minorStatus, &clientName);
-if (clientDisplayName.value != NULL)
-  gss_release_buffer(&minorStatus, &clientDisplayName);
- gss_release_name( &minorStatus, &serviceName);
- gss_release_cred( &minorStatus, &acceptorCredentials);
+  if (inputTokenBuffer) { free (inputTokenBuffer); }
+  if (gssContext != GSS_C_NO_CONTEXT) { 
+    gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); }
+  if (clientName != GSS_C_NO_NAME)
+    gss_release_name(&minorStatus, &clientName);
+  if (clientDisplayName.value != NULL)
+    gss_release_buffer(&minorStatus, &clientDisplayName);
 gss_release_name( &minorStatus, &serviceName);
 gss_release_cred( &minorStatus, &acceptorCredentials);
         
-    return err;
+  return err;
 }
 
     
@@ -195,18 +195,18 @@ if (clientDisplayName.value != NULL)
 
 static int ClientPrincipalIsAuthorizedForService (const char *inClientPrincipal)
 {
-    int err = 0;
-        /* 
-         * Here is where the server checks to see if the client principal should 
-         * be allowed to use your service. Typically it should check both the name 
-         * and the realm, since with cross-realm shared keys, a user at another 
-         * realm may be trying to contact your service.  
-         */
-        err = 0;
+  int err = 0;
+  /* 
+   * Here is where the server checks to see if the client principal should 
+   * be allowed to use your service. Typically it should check both the name 
+   * and the realm, since with cross-realm shared keys, a user at another 
+   * realm may be trying to contact your service.  
+   */
+  err = 0;
 
     
     
-    return err;
+  return err;
 }
 
 /* --------------------------------------------------------------------------- */
@@ -215,56 +215,56 @@ int gsscon_authorize (gss_ctx_id_t  inContext,
                       int          *outAuthorized, 
                       int          *outAuthorizationError)
 {
-    int err = 0;
-    OM_uint32 majorStatus;
-    OM_uint32 minorStatus = 0;
-    gss_name_t clientName = NULL;
-    gss_name_t serviceName = NULL;
-    char *clientPrincipal = NULL;
-    char *servicePrincipal = NULL;
+  int err = 0;
+  OM_uint32 majorStatus;
+  OM_uint32 minorStatus = 0;
+  gss_name_t clientName = NULL;
+  gss_name_t serviceName = NULL;
+  char *clientPrincipal = NULL;
+  char *servicePrincipal = NULL;
 
-    if (!inContext            ) { err = EINVAL; }
-    if (!outAuthorized        ) { err = EINVAL; }
-    if (!outAuthorizationError) { err = EINVAL; }
+  if (!inContext            ) { err = EINVAL; }
+  if (!outAuthorized        ) { err = EINVAL; }
+  if (!outAuthorizationError) { err = EINVAL; }
     
-    if (!err) {
-        /* Get the client and service principals used to authenticate */
-        majorStatus = gss_inquire_context (&minorStatus, 
-                                           inContext, 
-                                           &clientName, 
-                                           &serviceName, 
-                                           NULL, NULL, NULL, NULL, NULL);
-        if (majorStatus != GSS_S_COMPLETE) { 
-            err = minorStatus ? minorStatus : majorStatus; 
-        }
+  if (!err) {
+    /* Get the client and service principals used to authenticate */
+    majorStatus = gss_inquire_context (&minorStatus, 
+                                       inContext, 
+                                      &clientName, 
+                                      &serviceName, 
+                                       NULL, NULL, NULL, NULL, NULL);
+    if (majorStatus != GSS_S_COMPLETE) { 
+      err = minorStatus ? minorStatus : majorStatus; 
     }
+  }
     
-    if (!err) {
-        /* Pull the client principal string out of the gss name */
-        gss_buffer_desc nameToken;
+  if (!err) {
+    /* Pull the client principal string out of the gss name */
+    gss_buffer_desc nameToken;
         
-        majorStatus = gss_display_name (&minorStatus, 
-                                        clientName, 
-                                        &nameToken, 
-                                        NULL);
-        if (majorStatus != GSS_S_COMPLETE) { 
-            err = minorStatus ? minorStatus : majorStatus; 
-        }
+    majorStatus = gss_display_name (&minorStatus, 
+                                    clientName, 
+                                   &nameToken, 
+                                    NULL);
+    if (majorStatus != GSS_S_COMPLETE) { 
+      err = minorStatus ? minorStatus : majorStatus; 
+    }
         
-        if (!err) {
-            clientPrincipal = malloc (nameToken.length + 1);
-            if (clientPrincipal == NULL) { err = ENOMEM; }
-        }
+    if (!err) {
+      clientPrincipal = malloc (nameToken.length + 1);
+      if (clientPrincipal == NULL) { err = ENOMEM; }
+    }
         
-        if (!err) {
-            memcpy (clientPrincipal, nameToken.value, nameToken.length);
-            clientPrincipal[nameToken.length] = '\0';
-        }        
+    if (!err) {
+      memcpy (clientPrincipal, nameToken.value, nameToken.length);
+      clientPrincipal[nameToken.length] = '\0';
+    }        
 
-        if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); }
-    }
+    if (nameToken.value) { gss_release_buffer (&minorStatus, &nameToken); }
+  }
     
-        if (!err) {
+  if (!err) {
     //    /* Pull the service principal string out of the gss name */
     //    gss_buffer_desc nameToken;
     //    
@@ -290,24 +290,24 @@ int gsscon_authorize (gss_ctx_id_t  inContext,
     // }
     
 
-         int authorizationErr = 0;
-         authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal);
+    int authorizationErr = 0;
+    authorizationErr = ClientPrincipalIsAuthorizedForService (clientPrincipal);
 
 
         
 //        printf ("'%s' is%s authorized for service '%s'\n", 
 //                clientPrincipal, authorizationErr ? " NOT" : "", servicePrincipal);            
 //        
-         *outAuthorized = !authorizationErr;
-         *outAuthorizationError = authorizationErr;
-        }
+    *outAuthorized = !authorizationErr;
+    *outAuthorizationError = authorizationErr;
+  }
     
-    if (serviceName     ) { gss_release_name (&minorStatus, &serviceName); }
-    if (clientName      ) { gss_release_name (&minorStatus, &clientName); }
-    if (clientPrincipal ) { free (clientPrincipal); }
-    if (servicePrincipal) { free (servicePrincipal); }
+  if (serviceName     ) { gss_release_name (&minorStatus, &serviceName); }
+  if (clientName      ) { gss_release_name (&minorStatus, &clientName); }
+  if (clientPrincipal ) { free (clientPrincipal); }
+  if (servicePrincipal) { free (servicePrincipal); }
 
-    return err; 
+  return err; 
 }
 
 
index f415da6..10c6eac 100644 (file)
 #include <jansson.h>
 
 struct tid_srvr_blk {
-  struct in_addr aaa_server_addr;
+  TID_SRVR_BLK *next;
+  char *aaa_server_addr;
   TR_NAME *key_name;
   DH *aaa_server_dh;           /* AAA server's public dh information */
   GTimeVal key_expiration; /**< absolute time at which key expires*/
-  json_t *path;/**< Path of trust routers that the request traversed*/
+  TID_PATH *path;/**< Path of trust routers that the request traversed*/
 };
 
 struct tid_resp {
@@ -57,7 +58,6 @@ struct tid_resp {
   TR_CONSTRAINT_SET *cons;
   TR_NAME *orig_coi;
   TID_SRVR_BLK *servers;               /* array of servers */
-  size_t num_servers;
   json_t *error_path; /**< Path that a request generating an error traveled*/
 };
 
@@ -82,7 +82,7 @@ struct tid_req {
 };
 
 struct tidc_instance {
-  TID_REQ *req_list;
+  // TID_REQ *req_list;
   // TBD -- Do we still need a separate private key */
   // char *priv_key;
   // int priv_len;
@@ -95,17 +95,27 @@ struct tids_instance {
   char *ipaddr;
   const char *hostname;
   TIDS_REQ_FUNC *req_handler;
-  TIDS_AUTH_FUNC *auth_handler;
+  tids_auth_func *auth_handler;
   void *cookie;
   uint16_t tids_port;
   struct tr_rp_client *rp_gss;         /* Client matching GSS name */
 };
 
-
 /** Decrement a reference to #json when this tid_req is cleaned up. A
     new reference is not created; in effect the caller is handing a
     reference they already hold to the TID_REQ.*/
 void tid_req_cleanup_json(TID_REQ *, json_t *json);
 
 int tid_req_add_path(TID_REQ *, const char *this_system, unsigned port);
+
+TID_SRVR_BLK *tid_srvr_blk_new(TALLOC_CTX *mem_ctx);
+void tid_srvr_blk_free(TID_SRVR_BLK *srvr);
+TID_SRVR_BLK *tid_srvr_blk_dup(TALLOC_CTX *mem_ctx, TID_SRVR_BLK *srvr);
+TID_SRVR_BLK *tid_srvr_blk_add_func(TID_SRVR_BLK *head, TID_SRVR_BLK *new);
+#define tid_srvr_blk_add(head, new) ((head)=tid_srvr_blk_add_func((head),(new)))
+void tid_srvr_blk_set_path(TID_SRVR_BLK *block, TID_PATH *path);
+
+void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons);
+void tid_resp_set_error_path(TID_RESP *resp, json_t *ep);
+
 #endif
index 80b35b3..2919f86 100644 (file)
@@ -45,9 +45,14 @@ typedef struct tr_apc {
   TR_NAME *id;
 } TR_APC;
 
+/* iterator is just a pointer to a TR_APC */
+typedef TR_APC *TR_APC_ITER;
+
 TR_APC *tr_apc_new(TALLOC_CTX *mem_ctx);
 void tr_apc_free(TR_APC *apc);
-TR_APC *tr_apc_add(TR_APC *apcs, TR_APC *new);
+TR_APC *tr_apc_add_func(TR_APC *apcs, TR_APC *new);
+#define tr_apc_add(apcs,new) ((apcs)=tr_apc_add_func((apcs),(new)))
+TR_APC *tr_apc_dup_one(TALLOC_CTX *mem_ctx, TR_APC *apc);
 TR_APC *tr_apc_dup(TALLOC_CTX *mem_ctx, TR_APC *apc);
 
 void tr_apc_set_id(TR_APC *apc, TR_NAME *id);
@@ -56,6 +61,13 @@ TR_NAME *tr_apc_dup_id(TR_APC *apc);
 
 char *tr_apc_to_str(TALLOC_CTX *mem_ctx, TR_APC *apc);
 
+TR_APC_ITER *tr_apc_iter_new(TALLOC_CTX *mem_ctx);
+TR_APC *tr_apc_iter_first(TR_APC_ITER *iter, TR_APC *apc);
+TR_APC *tr_apc_iter_next(TR_APC_ITER *iter);
+void tr_apc_iter_free(TR_APC_ITER *iter);
+
+int tr_apc_in_common(TR_APC *one, TR_APC *two);
+
 #endif
 
 
index 2d753b6..5f4b4ae 100644 (file)
 #ifndef TR_COMM_H
 #define TR_COMM_H
 
+#include <stdio.h>
+#include <talloc.h>
+#include <time.h>
+
 #include <tr_idp.h>
 #include <tr_rp.h>
 #include <tr_apc.h>
 
+typedef struct tr_comm_table TR_COMM_TABLE;
+
 typedef enum tr_comm_type {
   TR_COMM_UNKNOWN,
   TR_COMM_APC,
@@ -50,18 +56,173 @@ typedef struct tr_comm {
   TR_NAME *id;
   TR_COMM_TYPE type;
   TR_APC *apcs;
-  TR_IDP_REALM *idp_realms;
-  TR_RP_REALM *rp_realms;
+  TR_NAME *owner_realm; /* what realm owns this community? */
+  TR_NAME *owner_contact; /* contact email */
   time_t expiration_interval; /*Minutes to key expiration; only valid for an APC*/
+  unsigned int refcount; /* how many TR_COMM_MEMBs refer to this community? */
 } TR_COMM;
 
+/* community membership - link realms to their communities */
+typedef struct tr_comm_memb {
+  struct tr_comm_memb *next;
+  struct tr_comm_memb *origin_next; /* for multiple copies from different origins */
+  TR_IDP_REALM *idp; /* only set one of idp and rp, other null */
+  TR_RP_REALM *rp; /* only set one of idp and rp, other null */
+  TR_COMM *comm;
+  TR_NAME *origin;
+  json_t *provenance; /* array of names of systems traversed */
+  unsigned int interval;
+  struct timespec *expiry;
+  unsigned int times_expired; /* how many times has this expired? */
+  int triggered; /* do we need to send this with triggered updates? */
+} TR_COMM_MEMB;
+
+/* table of communities/memberships */
+struct tr_comm_table {
+  TR_COMM *comms; /* all communities */
+  TR_IDP_REALM *idp_realms; /* all idp realms */
+  TR_RP_REALM *rp_realms; /* all rp realms */
+  TR_COMM_MEMB *memberships; /* head of the linked list of membership records */
+}; 
+
+typedef enum tr_realm_role {
+  TR_ROLE_UNKNOWN=0,
+  TR_ROLE_IDP,
+  TR_ROLE_RP
+} TR_REALM_ROLE;
+
+typedef struct tr_realm {
+  TR_REALM_ROLE role;
+  TR_RP_REALM *rp;
+  TR_IDP_REALM *idp;
+} TR_REALM;
+
+/* nb, not all iterator routines use all members */
+typedef struct tr_comm_iter {
+  TR_COMM *cur_comm;
+  TR_COMM_MEMB *cur_memb;
+  TR_COMM_MEMB *cur_orig_head; /* for iterating along orig_next list */
+  TR_NAME *match; /* realm or comm to match */
+  TR_REALM *realm; /* handle so caller does not have to manage memory, private */
+} TR_COMM_ITER;
+
+
+TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx);
+void tr_comm_table_free(TR_COMM_TABLE *ctab);
+
+TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx);
+void tr_comm_table_free(TR_COMM_TABLE *ctab);
+void tr_comm_table_sweep(TR_COMM_TABLE *ctab);
+void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new);
+void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm);
+TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id);
+void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new);
+void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm);
+TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id);
+void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new);
+void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm);
+void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new);
+void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb);
+TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin);
+TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm);
+TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *rp_realm, TR_NAME *comm, TR_NAME *origin);
+TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *rp_realm, TR_NAME *comm);
+TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *idp_realm, TR_NAME *comm, TR_NAME *origin);
+TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *idp_realm, TR_NAME *comm);
+TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id);
+size_t tr_comm_table_size(TR_COMM_TABLE *ctab);
+void tr_comm_table_print(FILE *f, TR_COMM_TABLE *ctab);
+
+TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx);
+void tr_comm_memb_free(TR_COMM_MEMB *memb);
+int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2);
+TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm);
+TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm);
+TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb);
+TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm);
+TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb);
+TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb);
+TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb);
+json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov);
+void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop);
+size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval);
+unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb);
+void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time);
+struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb);
+int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime);
+void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig);
+int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb);
+void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb);
+void tr_comm_memb_expire(TR_COMM_MEMB *memb);
+unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb);
+
 TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx);
 void tr_comm_free(TR_COMM *comm);
-TR_COMM *tr_comm_add(TR_COMM *comms, TR_COMM *new);
-void tr_comm_add_idp_realm(TR_COMM *comm, TR_IDP_REALM *realm);
-void tr_comm_add_rp_realm(TR_COMM *comm, TR_RP_REALM *realm);
-TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name);
-TR_RP_REALM *tr_find_comm_rp (TR_COMM *comm, TR_NAME *rp_realm);
-TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm);
+void tr_comm_set_id(TR_COMM *comm, TR_NAME *id);
+TR_NAME *tr_comm_get_id(TR_COMM *comm);
+TR_NAME *tr_comm_dup_id(TR_COMM *comm);
+void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc);
+TR_APC *tr_comm_get_apcs(TR_COMM *comm);
+void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type);
+TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm);
+void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm);
+TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm);
+TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm);
+void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact);
+TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm);
+TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm);
+void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_IDP_REALM *realm, unsigned int interval, json_t *provenance, struct timespec *expiry);
+void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_RP_REALM *realm, unsigned int interval, json_t *provenance, struct timespec *expiry);
+TR_RP_REALM *tr_comm_find_rp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm);
+TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm);
+const char *tr_comm_type_to_str(TR_COMM_TYPE type);
+TR_COMM_TYPE tr_comm_type_from_str(const char *s);
+void tr_comm_incref(TR_COMM *comm);
+void tr_comm_decref(TR_COMM *comm);
+unsigned int tr_comm_get_refcount(TR_COMM *comm);
+
+/* for iterating over communities within a realm or realms within a community */
+TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx);
+void tr_comm_iter_free(TR_COMM_ITER *iter);
+
+/* iterate over all communities in a table */
+TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab);
+TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *);
+
+/* these iterate over communities for a realm */
+TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm);
+TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter);
+TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm);
+TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter);
+TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm);
+TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter);
+
+/* iterate over realms for a community */
+TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm);
+TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter);
+TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm);
+TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter);
+TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm);
+TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter);
+
+/* iterate over members with different origins */
+TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb);
+TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter);
+
+/* iterate over all members */
+TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab);
+TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter);
+
+/* general realm stuff, should probably move */
+TR_NAME *tr_realm_get_id(TR_REALM *realm);
+TR_NAME *tr_realm_dup_id(TR_REALM *realm);
+
+const char *tr_realm_role_to_str(TR_REALM_ROLE role);
+TR_REALM_ROLE tr_realm_role_from_str(const char *s);
 
 #endif
index 8bd9e5b..62c5fa1 100644 (file)
 #include <trp_internal.h>
 
 #define TR_DEFAULT_MAX_TREE_DEPTH 12
-#define TR_DEFAULT_TR_PORT 12308
+#define TR_DEFAULT_TRPS_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_APC_EXPIRATION_INTERVAL 43200
 #define TR_DEFAULT_TRP_CONNECT_INTERVAL 10
-#define TR_DEFAULT_TRP_UPDATE_INTERVAL 120
+#define TR_DEFAULT_TRP_UPDATE_INTERVAL 30
 #define TR_DEFAULT_TRP_SWEEP_INTERVAL 30
+#define TR_DEFAULT_TID_REQ_TIMEOUT 5
+#define TR_DEFAULT_TID_RESP_NUMER 2
+#define TR_DEFAULT_TID_RESP_DENOM 3
 
 typedef enum tr_cfg_rc {
   TR_CFG_SUCCESS = 0,  /* No error */
@@ -79,14 +81,16 @@ typedef struct tr_cfg_internal {
   unsigned int trp_sweep_interval;
   unsigned int trp_update_interval;
   unsigned int trp_connect_interval;
+  unsigned int tid_req_timeout;
+  unsigned int tid_resp_numer; /* numerator of fraction of AAA servers to wait for in unshared mode */
+  unsigned int tid_resp_denom; /* denominator of fraction of AAA servers to wait for in unshared mode */
 } TR_CFG_INTERNAL;
 
 typedef struct tr_cfg {
   TR_CFG_INTERNAL *internal;           /* internal trust router config */
-  TR_IDP_REALM *idp_realms;            /* locally associated IDP Realms */
   TR_RP_CLIENT *rp_clients;            /* locally associated RP Clients */
   TRP_PTABLE *peers; /* TRP peer table */
-  TR_COMM *comms;                      /* locally-known communities */
+  TR_COMM_TABLE *ctable; /* communities/realms */
   TR_AAA_SERVER *default_servers;      /* default server list */
   /* TBD -- Global Filters */
 } TR_CFG;
@@ -108,9 +112,9 @@ void tr_cfg_free(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);
+void tr_print_comms(TR_COMM_TABLE *ctab);
+void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm);
+void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm);
 
 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);
index 16a1067..dc93860 100644 (file)
 
 #include <event2/event.h>
 
+#define TR_MAX_SOCKETS 10
+
 /* struct for hanging on to a socket listener event */
 struct tr_socket_event {
-  int sock_fd; /* the fd for the socket */
-  struct event *ev; /* its event */
+  size_t n_sock_fd; /* how many of those are filled in? */
+  int sock_fd[TR_MAX_SOCKETS]; /* the fd for the socket */
+  struct event *ev[TR_MAX_SOCKETS]; /* its events */
 };
 
 /* prototypes */
index 9af36d2..dbcb472 100644 (file)
@@ -36,6 +36,7 @@
 #define TR_IDP_H
 
 #include <talloc.h>
+#include <time.h>
 
 #include <trust_router/tr_name.h>
 #include <tr_apc.h>
@@ -45,6 +46,10 @@ typedef struct tr_aaa_server {
   TR_NAME *hostname;
 } TR_AAA_SERVER;
 
+typedef struct tr_aaa_server_iter {
+  TR_AAA_SERVER *this;
+} TR_AAA_SERVER_ITER;
+
 /* may also want to use in tr_rp.h */
 typedef enum tr_realm_origin {
   TR_REALM_LOCAL=0, /* realm we were configured to contact */
@@ -61,16 +66,36 @@ typedef struct tr_idp_realm {
   TR_AAA_SERVER *aaa_servers;
   TR_APC *apcs;
   TR_REALM_ORIGIN origin; /* how did we learn about this realm? */
+  unsigned int refcount; /* how many TR_COMM_MEMBs refer to this realm */
 } TR_IDP_REALM;
   
 TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx);
-TR_IDP_REALM *tr_idp_realm_add(TR_IDP_REALM *head, TR_IDP_REALM *new);
+void tr_idp_realm_free(TR_IDP_REALM *idp);
+TR_NAME *tr_idp_realm_get_id(TR_IDP_REALM *idp);
+TR_NAME *tr_idp_realm_dup_id(TR_IDP_REALM *idp);
+void tr_idp_realm_set_id(TR_IDP_REALM *idp, TR_NAME *id);
+void tr_idp_realm_set_apcs(TR_IDP_REALM *idp, TR_APC *apc);
+TR_APC *tr_idp_realm_get_apcs(TR_IDP_REALM *idp);
+TR_IDP_REALM *tr_idp_realm_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_name);
+TR_IDP_REALM *tr_idp_realm_add_func(TR_IDP_REALM *head, TR_IDP_REALM *new);
+#define tr_idp_realm_add(head,new) ((head)=tr_idp_realm_add_func((head),(new)))
+TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove);
+#define tr_idp_realm_remove(head,remove) ((head)=tr_idp_realm_remove_func((head),(remove)))
+TR_IDP_REALM *tr_idp_realm_sweep_func(TR_IDP_REALM *head);
+#define tr_idp_realm_sweep(head) ((head)=tr_idp_realm_sweep_func((head)))
 char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp);
+void tr_idp_realm_incref(TR_IDP_REALM *realm);
+void tr_idp_realm_decref(TR_IDP_REALM *realm);
 
 TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname);
 void tr_aaa_server_free(TR_AAA_SERVER *aaa);
 
-TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm);
+TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx);
+void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter);
+TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa);
+TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter);
+
+TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm, int *shared_out);
 TR_AAA_SERVER *tr_default_server_lookup(TR_AAA_SERVER *default_servers, TR_NAME *comm);
 
 #endif
index 81cb325..f60c7af 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <talloc.h>
 #include <pthread.h>
+#include <time.h>
 
 /* Note on mq priorities: High priority messages are guaranteed to be
  * processed before any normal priority messages. Otherwise, messages
@@ -63,6 +64,7 @@ typedef struct tr_mq TR_MQ;
 typedef void (*TR_MQ_NOTIFY_FN)(TR_MQ *, void *);
 struct tr_mq {
   pthread_mutex_t mutex;
+  pthread_cond_t have_msg_cond;
   TR_MQ_MSG *head;
   TR_MQ_MSG *tail;
   TR_MQ_MSG *last_hi_prio;
@@ -87,7 +89,8 @@ int tr_mq_lock(TR_MQ *mq);
 int tr_mq_unlock(TR_MQ *mq);
 void tr_mq_set_notify_cb(TR_MQ *mq, TR_MQ_NOTIFY_FN cb, void *arg);
 void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg);
-TR_MQ_MSG *tr_mq_pop(TR_MQ *mq);
+int tr_mq_pop_timeout(time_t seconds, struct timespec *ts);
+TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort);
 void tr_mq_clear(TR_MQ *mq);
  
 #endif /*_TR_MQ_H_ */
index 9db8c15..7e8f8f0 100644 (file)
@@ -50,16 +50,34 @@ typedef struct tr_rp_client {
 /* Structure to make a linked list of RP realms by name for community config */
 typedef struct tr_rp_realm {
   struct tr_rp_realm *next;
-  TR_NAME *realm_name;
+  TR_NAME *realm_id;
+  unsigned int refcount; /* how many TR_COMM_MEMBs refer to this realm */
 } TR_RP_REALM;
 
 /* prototypes */
 TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx);
 void tr_rp_client_free(TR_RP_CLIENT *client);
-TR_RP_CLIENT *tr_rp_client_add(TR_RP_CLIENT *clients, TR_RP_CLIENT *new);
+TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new);
+#define tr_rp_client_add(clients,new) ((clients)=tr_rp_client_add_func((clients),(new)))
 int tr_rp_client_add_gss_name(TR_RP_CLIENT *client, TR_NAME *name);
 int tr_rp_client_set_filter(TR_RP_CLIENT *client, TR_FILTER *filt);
-
 TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name);
-TR_RP_REALM *tr_rp_realm_add(TR_RP_REALM *head, TR_RP_REALM *new);
+
+TR_RP_REALM *tr_rp_realm_new(TALLOC_CTX *mem_ctx);
+void tr_rp_realm_free(TR_RP_REALM *rp);
+TR_NAME *tr_rp_realm_get_id(TR_RP_REALM *rp);
+TR_NAME *tr_rp_realm_dup_id(TR_RP_REALM *rp);
+void tr_rp_realm_set_id(TR_RP_REALM *rp, TR_NAME *id);
+TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name);
+TR_RP_REALM *tr_rp_realm_add_func(TR_RP_REALM *head, TR_RP_REALM *new);
+#define tr_rp_realm_add(head,new) ((head)=tr_rp_realm_add_func((head),(new)))
+TR_RP_REALM *tr_rp_realm_remove_func(TR_RP_REALM *head, TR_RP_REALM *remove);
+#define tr_rp_realm_remove(head,remove) ((head)=tr_rp_realm_remove_func((head),(remove)))
+TR_RP_REALM *tr_rp_realm_sweep_func(TR_RP_REALM *head);
+#define tr_rp_realm_sweep(head) ((head)=tr_rp_realm_sweep_func((head)))
+void tr_rp_realm_incref(TR_RP_REALM *realm);
+void tr_rp_realm_decref(TR_RP_REALM *realm);
+
+char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp);
+
 #endif
index 5db395e..924293d 100644 (file)
@@ -39,6 +39,8 @@
 #include <tr_event.h>
 #include <tr_config.h>
 
+#define TR_TID_MAX_AAA_SERVERS 10
+
 int tr_tids_event_init(struct event_base *base,
                        TIDS_INSTANCE *tids,
                        TR_CFG_MGR *cfg_mgr,
diff --git a/include/tr_util.h b/include/tr_util.h
new file mode 100644 (file)
index 0000000..3a3f7bc
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TR_UTIL_H
+#define TR_UTIL_H
+
+#include <trust_router/tr_versioning.h>
+
+TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2);
+
+#endif /* TR_UTIL_H */
index 09decc1..d6828d5 100644 (file)
 #ifndef TRP_INTERNAL_H
 #define TRP_INTERNAL_H
 
+#include <jansson.h>
 #include <pthread.h>
 #include <talloc.h>
+#include <time.h>
 
 #include <gsscon.h>
 #include <tr_mq.h>
 #include <tr_msg.h>
 #include <trp_ptable.h>
 #include <trp_rtable.h>
+#include <tr_apc.h>
+#include <tr_comm.h>
 #include <trust_router/trp.h>
 
+/* what clock do we use with clock_gettime() ? */
+#define TRP_CLOCK CLOCK_MONOTONIC
+
 /* info records */
 /* TRP update record types */
 typedef struct trp_inforec_route {
-  TR_NAME *comm;
-  TR_NAME *realm;
   TR_NAME *trust_router;
   TR_NAME *next_hop;
   unsigned int next_hop_port;
@@ -57,20 +62,31 @@ typedef struct trp_inforec_route {
   unsigned int interval;
 } TRP_INFOREC_ROUTE;
 
-/* TODO: define struct trp_msg_info_community */
+typedef struct trp_inforec_comm {
+  TR_COMM_TYPE comm_type;
+  TR_REALM_ROLE role;
+  TR_APC *apcs;
+  TR_NAME *owner_realm;
+  TR_NAME *owner_contact;
+  time_t expiration_interval; /* Minutes to key expiration; only valid for an APC */
+  json_t *provenance;
+  unsigned int interval;
+} TRP_INFOREC_COMM;
 
 typedef union trp_inforec_data {
   TRP_INFOREC_ROUTE *route;
-  /* TRP_INFOREC_COMM *comm; */
+  TRP_INFOREC_COMM *comm;
 } TRP_INFOREC_DATA;
 
 struct trp_inforec {
   TRP_INFOREC *next;
   TRP_INFOREC_TYPE type;
-  TRP_INFOREC_DATA data; /* contains pointer to one of the record types */
+  TRP_INFOREC_DATA *data; /* contains pointer to one of the record types */
 };
 
 struct trp_update {
+  TR_NAME *realm;
+  TR_NAME *comm;
   TRP_INFOREC *records;
   TR_NAME *peer; /* who did this update come from? */
 };
@@ -138,6 +154,7 @@ struct trps_instance {
   TR_MQ *mq; /* incoming message queue */
   TRP_PTABLE *ptable; /* peer table */
   TRP_RTABLE *rtable; /* route table */
+  TR_COMM_TABLE *ctable; /* community table */
   struct timeval connect_interval; /* interval between connection refreshes */
   struct timeval update_interval; /* interval between scheduled updates */
   struct timeval sweep_interval; /* interval between route table sweeps */
@@ -198,8 +215,10 @@ TRP_RC trpc_send_msg(TRPC_INSTANCE *trpc, const char *msg_content);
 
 TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx);
 void trps_free (TRPS_INSTANCE *trps);
+void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm);
 void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable);
 void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, void *), void *cookie);
+TR_NAME *trps_dup_label(TRPS_INSTANCE *trps);
 TRP_RC trps_init_rtable(TRPS_INSTANCE *trps);
 void trps_clear_rtable(TRPS_INSTANCE *trps);
 void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval);
@@ -219,7 +238,9 @@ int trps_get_listener(TRPS_INSTANCE *trps,
                       TRP_AUTH_FUNC auth_handler,
                       const char *hostname,
                       unsigned int port,
-                      void *cookie);
+                      void *cookie,
+                      int *fd_out,
+                      size_t max_fd);
 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps);
 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg);
 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn);
@@ -230,6 +251,7 @@ TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR
 TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm);
 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm);
 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps);
+TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps);
 TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route);
 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer);
 TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname);
@@ -237,4 +259,45 @@ TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename
 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE type);
 int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer);
 TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_gssname);
+
+TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type);
+void trp_inforec_free(TRP_INFOREC *rec);
+TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec);
+void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec);
+TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec);
+void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type);
+TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec);
+TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm);
+TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec);
+TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm);
+TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec);
+TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router);
+TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec);
+TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop);
+unsigned int trp_inforec_get_metric(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric);
+unsigned int trp_inforec_get_interval(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval);
+TR_NAME *trp_inforec_get_owner_realm(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_owner_realm(TRP_INFOREC *rec, TR_NAME *name);
+TR_NAME *trp_inforec_get_owner_contact(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_owner_contact(TRP_INFOREC *rec, TR_NAME *name);
+json_t *trp_inforec_get_provenance(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_provenance(TRP_INFOREC *rec, json_t *prov);
+TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s);
+const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype);
+time_t trp_inforec_get_exp_interval(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_exp_interval(TRP_INFOREC *rec, time_t expint);
+TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_comm_type(TRP_INFOREC *rec, TR_COMM_TYPE type);
+TR_REALM_ROLE trp_inforec_get_role(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_role(TRP_INFOREC *rec, TR_REALM_ROLE role);
+TR_APC *trp_inforec_get_apcs(TRP_INFOREC *rec);
+TRP_RC trp_inforec_set_apcs(TRP_INFOREC *rec, TR_APC *apcs);
+TR_NAME *trp_inforec_dup_origin(TRP_INFOREC *rec);
+
 #endif /* TRP_INTERNAL_H */
index 42c9688..a0d17b3 100644 (file)
@@ -50,6 +50,7 @@ typedef enum trp_peer_conn_status {
 typedef struct trp_peer TRP_PEER;
 struct trp_peer {
   TRP_PEER *next; /* for making a linked list */
+  TR_NAME *label; /* often null, set on first call to trp_peer_get_label or dup_label */
   char *server;
   TR_GSS_NAMES *gss_names;
   TR_NAME *servicename;
index 6aa3336..0475d4d 100644 (file)
@@ -71,7 +71,7 @@ typedef void (TIDC_RESP_FUNC)(TIDC_INSTANCE *, TID_REQ *, TID_RESP *, void *);
 
 
 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);
 
 
 
@@ -106,6 +106,7 @@ TR_EXPORT void tid_req_free( TID_REQ *req);
 
 TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx);
 void tid_resp_free(TID_RESP *resp);
+TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, 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);
@@ -146,16 +147,16 @@ TR_EXPORT int tidc_send_request (TIDC_INSTANCE *tidc, int conn, gss_ctx_id_t gss
 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);
+TR_EXPORT void tidc_destroy(TIDC_INSTANCE *tidc);
 
 /* TID Server functions, in tid/tids.c */
-TR_EXPORT TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx);
+TR_EXPORT TIDS_INSTANCE *tids_create (void);
 TR_EXPORT int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
-                         TIDS_AUTH_FUNC *auth_handler, const char *hostname, 
-                         unsigned int port, void *cookie);
+                          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);
+                                 tids_auth_func *auth_handler, const char *hostname, 
+                                 unsigned int port, void *cookie, int *fd_out, size_t max_fd);
 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);
index 58fbb67..9f76047 100644 (file)
@@ -51,6 +51,7 @@ typedef struct tr_constraint {
 
 TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx);
 void tr_constraint_free(TR_CONSTRAINT *cons);
+TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons);
 
 void TR_EXPORT tr_constraint_add_to_set (TR_CONSTRAINT_SET **cs, TR_CONSTRAINT *c);
 
index 7760155..f4ec7fb 100644 (file)
 #include <trust_router/tr_versioning.h>
 #include <trust_router/tid.h>
 
-
+TR_EXPORT DH *tr_dh_new(void);
+TR_EXPORT void tr_dh_destroy(DH *dh); /* called destroy because free is already used */
 TR_EXPORT DH *tr_create_dh_params(unsigned char *key, size_t len);
 TR_EXPORT DH *tr_create_matching_dh(unsigned char *key, size_t len, DH *in_dh);
 TR_EXPORT void tr_destroy_dh_params(DH *dh);
+TR_EXPORT DH *tr_dh_dup(DH *in);
 TR_EXPORT int tr_compute_dh_key(unsigned char **pbuf,  BIGNUM *pub_key, DH *priv_dh);
 
 TR_EXPORT void tr_dh_free(unsigned char *dh_buf);
@@ -51,8 +53,8 @@ int TR_EXPORT tr_dh_pub_hash(TID_REQ *request,
                             unsigned char **out_digest,
                             size_t *out_llen);
 
-
 TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen,
-                            char * hex_out, size_t hex_len);
+                             char * hex_out, size_t hex_len);
+
 
 #endif
index 5509f87..7e86003 100644 (file)
@@ -35,6 +35,7 @@
 #ifndef TR_NAME_H
 #define TR_NAME_H
 #include <string.h>
+#include <jansson.h>
 #include <trust_router/tr_versioning.h>
 
 typedef const char *tr_const_string;
@@ -50,6 +51,7 @@ TR_EXPORT void tr_free_name (TR_NAME *name);
 TR_EXPORT int tr_name_cmp (TR_NAME *one, TR_NAME *two);
 TR_EXPORT void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len);
 TR_EXPORT char *tr_name_strdup(TR_NAME *);
-TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2);
+TR_EXPORT json_t *tr_name_to_json_string(TR_NAME *src);
+TR_EXPORT TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2);
 
 #endif
index e683649..7e524af 100644 (file)
@@ -65,7 +65,7 @@ typedef enum trp_rc {
 typedef enum trp_inforec_type {
   TRP_INFOREC_TYPE_UNKNOWN=0, /* conveniently, JSON parser returns 0 if a non-integer number is specified */
   TRP_INFOREC_TYPE_ROUTE,
-  TRP_INFOREC_TYPE_COMMUNITY, /* not yet implemented (2016-06-14) */
+  TRP_INFOREC_TYPE_COMMUNITY
 } TRP_INFOREC_TYPE;
 
 typedef struct trp_inforec TRP_INFOREC;
@@ -79,34 +79,17 @@ void trp_upd_free(TRP_UPD *update);
 TR_EXPORT TRP_INFOREC *trp_upd_get_inforec(TRP_UPD *upd);
 void trp_upd_set_inforec(TRP_UPD *upd, TRP_INFOREC *rec);
 void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec);
+TR_EXPORT TR_NAME *trp_upd_get_realm(TRP_UPD *upd);
+TR_NAME *trp_upd_dup_realm(TRP_UPD *upd);
+void trp_upd_set_realm(TRP_UPD *upd, TR_NAME *realm);
+TR_EXPORT TR_NAME *trp_upd_get_comm(TRP_UPD *upd);
+TR_NAME *trp_upd_dup_comm(TRP_UPD *upd);
+void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm);
 TR_EXPORT TR_NAME *trp_upd_get_peer(TRP_UPD *upd);
 TR_NAME *trp_upd_dup_peer(TRP_UPD *upd);
 void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer);
 void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port);
-TR_EXPORT TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type);
-void trp_inforec_free(TRP_INFOREC *rec);
-TR_EXPORT TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec);
-void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec);
-TR_EXPORT TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec);
-void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type);
-TR_EXPORT TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec);
-TR_EXPORT TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm);
-TR_EXPORT TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec);
-TR_EXPORT TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm);
-TR_EXPORT TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec);
-TR_EXPORT TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router);
-TR_EXPORT TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec);
-TR_EXPORT TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop);
-TR_EXPORT unsigned int trp_inforec_get_metric(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric);
-TR_EXPORT unsigned int trp_inforec_get_interval(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval);
-TR_EXPORT TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s);
-TR_EXPORT const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype);
+void trp_upd_add_to_provenance(TRP_UPD *upd, TR_NAME *name);
 
 /* Functions for TRP_REQ structures */
 TR_EXPORT TRP_REQ *trp_req_new(TALLOC_CTX *mem_ctx);
diff --git a/redhat/default-internal.cfg b/redhat/default-internal.cfg
new file mode 100644 (file)
index 0000000..7bfe0f5
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "tr_internal": {
+    "max_tree_depth": 12,
+    "hostname":"beta.example.com",
+    "trps_port":25308,
+    "tids_port":25309,
+    "cfg_poll_interval": 1,
+    "cfg_settling_time": 5,
+    "trp_sweep_interval": 30,
+    "trp_update_interval": 30,
+    "trp_connect_interval": 10,
+    "tid_request_timeout": 5,
+    "tid_response_numerator": 2,
+    "tid_response_denominator": 3,
+    "logging": {
+      "log_threshold": "info",
+      "console_threshold":"notice"
+    }
+  }
+}
diff --git a/redhat/default-main.cfg b/redhat/default-main.cfg
deleted file mode 100644 (file)
index 1dca690..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-{"tr_internal": {"max_tree_depth": 4,
-                "hostname":"tr.moonshot.local",
-                "tids_port": 12309,
-
-                "logging": { "console_threshold": "debug",
-                             "log_threshold": "info"
-                           }
-               }
-}
diff --git a/redhat/organizations.cfg b/redhat/organizations.cfg
new file mode 100644 (file)
index 0000000..5c190b8
--- /dev/null
@@ -0,0 +1,84 @@
+{
+  "communities": [
+    {
+      "apcs": [],
+      "community_id": "apc.x",
+      "idp_realms": ["idp.x", "other.idp.x"],
+      "rp_realms": ["rp.x", "other.rp.x"],
+      "type": "apc",
+      "expiration_interval": 10
+    },
+    {
+      "apcs": ["apc."],
+      "community_id": "coi.x",
+      "idp_realms": ["idp.x"],
+      "rp_realms": ["rp.x"],
+      "type": "coi"
+    }
+  ],
+  "local_organizations": [
+    {
+      "organization_name": "Demo Organization",
+      "realms": [
+       {
+         "realm": "rp.x",
+         "gss_names": ["alpha-cred@apc.x",
+                       "beta-cred@apc.x",
+                       "gamma-cred@apc.x"],
+         "filters": {
+           "tid_inbound": [
+             {
+               "action": "accept",
+               "domain_constraints": [
+                 "*.local"
+               ],
+               "specs": [
+                 {
+                   "field": "rp_realm",
+                   "match": "rp.x"
+                 },
+                 {
+                   "field": "rp_realm",
+                   "match": "*.rp.x"
+                 }
+               ],
+               "realm_constraints": [
+                 "rp.x", "*.rp.x"
+               ]
+             }
+           ]
+         }
+       },
+        {
+          "realm": "other.rp.x",
+          "gss_names": ["something@apc.x"]
+        },
+       {
+         "realm": "idp.x",
+         "gss_names": ["alpha-cred@apc.x"],
+         "identity_provider": {
+           "aaa_servers": ["alpha.local"],
+           "apcs": ["apc.x"],
+           "shared_config": "no"
+         }
+        },
+       {
+         "realm": "other.idp.x",
+         "gss_names": ["beta-cred@apc.x"],
+         "identity_provider": {
+           "aaa_servers": ["alpha.local"],
+           "apcs": ["apc.x"],
+           "shared_config": "no"
+         }
+       }
+      ]
+    }
+  ],
+  "peer_organizations": [
+    {
+      "hostname": "gamma.local",
+      "port": 12310,
+      "gss_names": ["gamma-cred@apc.x"]
+    }
+  ]
+}
diff --git a/redhat/trusts.cfg b/redhat/trusts.cfg
deleted file mode 100644 (file)
index 0998e1f..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-{
-  "communities": [
-    {
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "community_id": "comm.offcenter.org",
-      "idp_realms": [
-        "idr2.offcenter.org"
-      ],
-      "rp_realms": [
-        "sr3.offcenter.org"
-      ],
-      "type": "coi"
-    },
-    {
-      "apcs": [
-
-      ],
-      "community_id": "pci-community.ja.net",
-      "idp_realms": [
-        "idr1.offcenter.org",
-        "idr2.offcenter.org",
-        "ja.net",
-        "no-longer-untitled.offcenter.org"
-      ],
-      "rp_realms": [
-        "exchange.ja.net",
-        "sr3.offcenter.org"
-      ],
-      "type": "apc"
-    }
-  ],
-  "idp_realms": [
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "idr1.offcenter.org",
-      "shared_config": "yes"
-    },
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "idr2.offcenter.org",
-      "shared_config": "no"
-    },
-    {
-      "aaa_servers": [
-        "10.1.10.90"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "ja.net",
-      "shared_config": "no"
-    },
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "no-longer-untitled.offcenter.org",
-      "shared_config": "yes"
-    }
-  ],
-  "rp_clients": [
-    {
-      "filter": {
-        "filter_lines": [
-          {
-            "action": "accept",
-            "domain_constraints": ["*.exchange.ja.net"],
-            "filter_specs": [
-              {
-                "field": "rp_realm",
-                "match": "exchange.ja.net"
-              },
-              {
-                "field": "rp_realm",
-                "match": "*.exchange.ja.net"
-              }
-            ],
-            "realm_constraints": ["*.exchange.ja.net", "a.com"]
-          }
-        ],
-        "type": "rp_permitted"
-      },
-      "gss_names": [
-        "01b80aa9-8753-4691-8f8a-f49f7793546f@portal-realm.ja.net"
-      ]
-    },
-    {
-      "filter": {
-        "filter_lines": [
-          {
-            "action": "accept",
-            "domain_constraints": ["*.bob.sr3.offcenter.org"],
-            "filter_specs": [
-              {
-                "field": "rp_realm",
-                "match": "sr3.offcenter.org"
-              },
-              {
-                "field": "rp_realm",
-                "match": "*.sr3.offcenter.org"
-              }
-            ],
-            "realm_constraints": ["*.sr3.offcenter.org" ]
-          }
-        ],
-        "type": "rp_permitted"
-      },
-      "gss_names": [
-        "895c308a-5624-4055-bb4f-ea24b77e6637@portal-realm.ja.net"
-      ]
-    }
-  ]
-}
index c334f41..7b58840 100644 (file)
@@ -41,6 +41,7 @@
 #include <poll.h>
 
 #include <tr_debug.h>
+#include <tr_util.h>
 #include <tid_internal.h>
 #include <trust_router/tr_constraint.h>
 #include <trust_router/tr_dh.h>
@@ -147,6 +148,7 @@ static int handle_authorizations(TID_REQ *req, const unsigned char *dh_hash,
       if (SQLITE_DONE != sqlite3_result)
        tr_crit("sqlite3: failed to write to database");
       sqlite3_reset(authorization_insert);
+      sqlite3_clear_bindings(authorization_insert);
     }
   return 0;
 }
@@ -160,7 +162,7 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   unsigned char *s_keybuf = NULL;
   int s_keylen = 0;
   char key_id[12];
-  unsigned char *pub_digest;
+  unsigned char *pub_digest=NULL;
   size_t pub_digest_len;
   
 
@@ -175,13 +177,12 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
 
 
   /* Allocate a new server block */
-  if (NULL == (resp->servers = talloc_zero(resp, TID_SRVR_BLK))){
-    tr_crit("tids_req_handler(): malloc failed.");
+  tid_srvr_blk_add(resp->servers, tid_srvr_blk_new(resp));
+  if (NULL==resp->servers) {
+    tr_crit("tids_req_handler(): unable to allocate server block.");
     return -1;
   }
 
-  resp->num_servers = 1;
-
   /* TBD -- Set up the server IP Address */
 
   if (!(req) || !(req->tidc_dh)) {
@@ -203,10 +204,7 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
     return -1;
   }
 
-  if (0 == inet_aton(tids->ipaddr, &(resp->servers->aaa_server_addr))) {
-    tr_debug("tids_req_handler: inet_aton() failed.");
-    return -1;
-  }
+  resp->servers->aaa_server_addr=talloc_strdup(resp->servers, tids->ipaddr);
 
   /* Set the key name */
   if (-1 == create_key_id(key_id, sizeof(key_id)))
@@ -229,7 +227,8 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   }
   if (0 != handle_authorizations(req, pub_digest, pub_digest_len))
     return -1;
-  resp->servers->path = req->path;
+  tid_srvr_blk_set_path(resp->servers, (TID_PATH *)(req->path));
+
   if (req->expiration_interval < 1)
     req->expiration_interval = 1;
   g_get_current_time(&resp->servers->key_expiration);
@@ -238,14 +237,16 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   if (NULL != insert_stmt) {
     int sqlite3_result;
     gchar *expiration_str = g_time_val_to_iso8601(&resp->servers->key_expiration);
-        sqlite3_bind_text(insert_stmt, 1, key_id, -1, SQLITE_TRANSIENT);
+    sqlite3_bind_text(insert_stmt, 1, key_id, -1, SQLITE_TRANSIENT);
     sqlite3_bind_blob(insert_stmt, 2, s_keybuf, s_keylen, SQLITE_TRANSIENT);
     sqlite3_bind_blob(insert_stmt, 3, pub_digest, pub_digest_len, SQLITE_TRANSIENT);
-        sqlite3_bind_text(insert_stmt, 4, expiration_str, -1, SQLITE_TRANSIENT);
+    sqlite3_bind_text(insert_stmt, 4, expiration_str, -1, SQLITE_TRANSIENT);
+    g_free(expiration_str); /* bind_text already made its own copy */
     sqlite3_result = sqlite3_step(insert_stmt);
     if (SQLITE_DONE != sqlite3_result)
       tr_crit("sqlite3: failed to write to database");
     sqlite3_reset(insert_stmt);
+    sqlite3_clear_bindings(insert_stmt);
   }
   
   /* Print out the key. */
@@ -255,6 +256,12 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   // }
   // fprintf(stderr, "\n");
 
+  if (s_keybuf!=NULL)
+    free(s_keybuf);
+
+  if (pub_digest!=NULL)
+    talloc_free(pub_digest);
+  
   return s_keylen;
 }
 
@@ -348,8 +355,6 @@ int main (int argc,
   TIDS_INSTANCE *tids;
   TR_NAME *gssname = NULL;
   struct cmdline_args opts={NULL};
-  int tids_socket=-1;
-  struct pollfd *poll_fds=NULL;
 
   /* parse the command line*/
   argp_parse(&argp, argc, argv, 0, 0, &opts);
@@ -375,38 +380,13 @@ int main (int argc,
                     -1, &authorization_insert, NULL);
 
   /* Create a TID server instance */
-  if (NULL == (tids = tids_create(NULL))) {
+  if (NULL == (tids = tids_create())) {
     tr_crit("Unable to create TIDS instance, exiting.");
     return 1;
   }
 
   tids->ipaddr = opts.ip_address;
-
-  /* get listener for tids port */
-  tids_socket = tids_get_listener(tids, &tids_req_handler , auth_handler, opts.hostname, TID_PORT, gssname);
-
-  poll_fds=malloc(sizeof(*poll_fds));
-  if (poll_fds == NULL) {
-    tr_crit("Could not allocate event polling list, exiting.");
-    return 1;
-  }
-
-  poll_fds[0].fd=tids_socket;
-  poll_fds[0].events=POLLIN; /* poll on ready for reading */
-  poll_fds[0].revents=0; 
-
-  /* main event loop */
-  while (1) {
-    /* wait up to 100 ms for an event, then handle any idle work */
-    if(poll(poll_fds, 1, 100) > 0) {
-      if (poll_fds[0].revents & POLLIN) {
-        if (0 != tids_accept(tids, tids_socket)) {
-          tr_err("Error handling tids request.");
-        }
-      }
-    }
-    /* idle loop stuff here */
-  }
+  (void) tids_start(tids, &tids_req_handler, auth_handler, opts.hostname, TID_PORT, gssname);
 
   /* Clean-up the TID server instance */
   tids_destroy(tids);
index a068c40..c3f1a27 100644 (file)
@@ -189,11 +189,12 @@ void tid_req_set_cookie(TID_REQ *req, void *cookie)
   req->cookie = cookie;
 }
 
+/* struct is allocated in talloc null context */
 TID_REQ *tid_dup_req (TID_REQ *orig_req) 
 {
   TID_REQ *new_req = NULL;
 
-  if (NULL == (new_req = talloc_zero(orig_req, TID_REQ))) {
+  if (NULL == (new_req = talloc_zero(NULL, TID_REQ))) {
     tr_crit("tid_dup_req: Can't allocated duplicate request.");
     return NULL;
   }
@@ -253,8 +254,8 @@ void tid_srvr_get_address(const TID_SRVR_BLK *blk,
     assert(blk);
     sa = talloc_zero(blk, struct sockaddr_in);
     sa->sin_family = AF_INET;
-    sa->sin_addr = blk->aaa_server_addr;
-    sa->sin_port = htons(2083);
+    inet_aton(blk->aaa_server_addr, &(sa->sin_addr));
+    sa->sin_port = htons(2083); /* radsec port */
     *out_addr = (struct sockaddr *) sa;
     *out_len = sizeof( struct sockaddr_in);
 }
index 338c972..c6ee2e8 100644 (file)
@@ -37,6 +37,7 @@
 #include <assert.h>
 #include <talloc.h>
 
+#include <trust_router/tr_dh.h>
 #include <tid_internal.h>
 
 static int tid_resp_destructor(void *obj)
@@ -67,7 +68,6 @@ TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx)
     resp->cons=NULL;
     resp->orig_coi=NULL;
     resp->servers=NULL;
-    resp->num_servers=0;
     resp->error_path=NULL;
     talloc_set_destructor((void *)resp, tid_resp_destructor);
   }
@@ -80,6 +80,29 @@ void tid_resp_free(TID_RESP *resp)
     talloc_free(resp);
 }
 
+TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp)
+{
+  TID_RESP *newresp=NULL;
+
+  if (resp==NULL)
+    return NULL;
+
+  newresp=tid_resp_new(mem_ctx);
+
+  if (NULL!=newresp) {
+    newresp->result=resp->result;
+    newresp->err_msg=tr_dup_name(resp->err_msg);
+    newresp->rp_realm=tr_dup_name(resp->rp_realm);
+    newresp->realm=tr_dup_name(resp->realm);
+    newresp->comm=tr_dup_name(resp->comm);
+    newresp->orig_coi=tr_dup_name(resp->orig_coi);
+    newresp->servers=tid_srvr_blk_dup(newresp, resp->servers);
+    tid_resp_set_cons(newresp, resp->cons);
+    tid_resp_set_error_path(newresp, resp->error_path);
+  }
+  return newresp;
+}
+
 TR_EXPORT int tid_resp_get_result(TID_RESP *resp)
 {
   return(resp->result);
@@ -97,6 +120,9 @@ TR_EXPORT TR_NAME *tid_resp_get_err_msg(TID_RESP *resp)
 
 void tid_resp_set_err_msg(TID_RESP *resp, TR_NAME *err_msg)
 {
+  if (resp->err_msg!=NULL)
+    tr_free_name(resp->err_msg);
+
   resp->err_msg = err_msg;
 }
 
@@ -143,33 +169,144 @@ void tid_resp_set_orig_coi(TID_RESP *resp, TR_NAME *orig_coi)
 TR_EXPORT TID_SRVR_BLK *tid_resp_get_server(TID_RESP *resp,
                                            size_t index)
 {
+  TID_SRVR_BLK *this=NULL;
   assert(resp);
-  assert(index < resp->num_servers);
-  return(&(resp->servers[index]));
+
+  for (this=resp->servers; index>0; index--, this=this->next) {}
+
+  return this;
 }
 
 size_t tid_resp_get_num_servers(const TID_RESP *resp)
 {
-  assert(resp);
-  return resp->num_servers;
+  size_t count=0;
+  TID_SRVR_BLK *this=NULL;
+
+  assert(resp!=NULL);
+  for (count=0, this=resp->servers; this!=NULL; count++, this=this->next) {}
+  return count;
+}
+
+static int tid_srvr_blk_destructor(void *obj)
+{
+  TID_SRVR_BLK *srvr=talloc_get_type_abort(obj, TID_SRVR_BLK);
+
+  if (srvr->key_name!=NULL)
+    tr_free_name(srvr->key_name);
+  if (srvr->aaa_server_dh!=NULL)
+    tr_destroy_dh_params(srvr->aaa_server_dh);
+  if (srvr->path!=NULL)
+    json_decref((json_t *)(srvr->path));
+  return 0;
+}
+
+TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_new(TALLOC_CTX *mem_ctx)
+{
+  TID_SRVR_BLK *srvr=talloc(mem_ctx, TID_SRVR_BLK);
+
+  if (srvr!=NULL) {
+    srvr->next=NULL;
+    srvr->aaa_server_addr=NULL;
+    srvr->key_name=NULL;
+    srvr->aaa_server_dh=NULL;
+    srvr->key_expiration=(GTimeVal){0};
+    srvr->path=NULL;
+    talloc_set_destructor((void *)srvr, tid_srvr_blk_destructor);
+  }
+  return srvr;
+}
+
+TR_EXPORT void tid_srvr_blk_free(TID_SRVR_BLK *srvr)
+{
+  talloc_free(srvr);
 }
 
+TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_dup(TALLOC_CTX *mem_ctx, TID_SRVR_BLK *srvr)
+{
+  TID_SRVR_BLK *new=NULL;
+
+  if (srvr==NULL)
+    return NULL;
+
+  new=tid_srvr_blk_new(mem_ctx);
+  if (new!=NULL) {
+    if (srvr->aaa_server_addr!=NULL)
+      new->aaa_server_addr=talloc_strdup(new, srvr->aaa_server_addr);
+    new->key_name=tr_dup_name(srvr->key_name);
+    new->aaa_server_dh=tr_dh_dup(srvr->aaa_server_dh);
+    new->key_expiration=srvr->key_expiration;
+    
+    tid_srvr_blk_set_path(new, srvr->path);
+
+    tid_srvr_blk_add(new->next, tid_srvr_blk_dup(mem_ctx, srvr->next));
+  }
+  return new;
+}
 
-const TID_PATH *tid_srvr_get_path( const TID_SRVR_BLK *block)
+/* use the macro */
+TR_EXPORT TID_SRVR_BLK *tid_srvr_blk_add_func(TID_SRVR_BLK *head, TID_SRVR_BLK *new)
+{
+  TID_SRVR_BLK *this=head;
+
+  if (head==NULL)
+    return new;
+
+  while (this->next!=NULL)
+    this=this->next;
+  
+  this->next=new;
+  while (this!=NULL) {
+    talloc_steal(head, this);
+    this=this->next;
+  }
+  return head;
+}
+
+TR_EXPORT void tid_srvr_blk_set_path(TID_SRVR_BLK *block, TID_PATH *path)
+{
+  if (block->path!=NULL)
+    json_decref((json_t *)(block->path));
+  block->path=path;
+  if (block->path!=NULL)
+    json_incref((json_t *)(block->path));
+}
+
+TR_EXPORT const TID_PATH *tid_srvr_get_path( const TID_SRVR_BLK *block)
 {
   if (!block)
     return NULL;
-  return (const TID_PATH *) block->path;
+  return block->path;
 }
 
-const TID_PATH *tid_resp_get_error_path( const TID_RESP *resp)
+TR_EXPORT void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons)
+{
+  json_t *jc=(json_t *)cons;
+
+  if (resp->cons!=NULL)
+    json_decref((json_t *) (resp->cons));
+
+  resp->cons=(TR_CONSTRAINT_SET *)jc;
+  if (jc!=NULL)
+    json_incref(jc);
+}
+
+TR_EXPORT void tid_resp_set_error_path(TID_RESP *resp, json_t *ep)
+{
+  if (resp->error_path!=NULL)
+    json_decref(resp->error_path);
+  resp->error_path=ep;
+  if (resp->error_path!=NULL)
+    json_incref(resp->error_path);
+}
+
+TR_EXPORT const TID_PATH *tid_resp_get_error_path(const TID_RESP *resp)
 {
   if (!resp)
     return NULL;
-  return (const TID_PATH *) resp->error_path;
+  return (const TID_PATH *)(resp->error_path);
 }
 
-const TID_PATH *tid_resp_get_a_path( const TID_RESP *const_resp)
+TR_EXPORT const TID_PATH *tid_resp_get_a_path(const TID_RESP *const_resp)
 {
   size_t index;
   TID_SRVR_BLK *server;
@@ -177,12 +314,11 @@ const TID_PATH *tid_resp_get_a_path( const TID_RESP *const_resp)
   if (!resp)
     return NULL;
 
-
   if (resp->error_path)
-    return (const TID_PATH *) resp->error_path;
+    return (const TID_PATH *)(resp->error_path);
   tid_resp_servers_foreach( resp, server, index) {
     if (server->path)
-      return (const TID_PATH *) server->path;
+      return server->path;
   }
   return NULL;
   
index 37a3712..d724322 100644 (file)
 
 int tmp_len = 32;
 
-TIDC_INSTANCE *tidc_create ()
+static int tidc_destructor(void *obj)
 {
-  TIDC_INSTANCE *tidc = NULL;
-
-  if (NULL == (tidc = talloc_zero(NULL, TIDC_INSTANCE)))
-    return NULL;
+  TIDC_INSTANCE *tidc=talloc_get_type_abort(obj, TIDC_INSTANCE);
+  if (NULL!=tidc) {
+    if (NULL!=tidc->client_dh)
+      tr_destroy_dh_params(tidc->client_dh);
+  }
+  return 0;
+}
 
+/* creates struct in talloc null context */
+TIDC_INSTANCE *tidc_create(void)
+{
+  TIDC_INSTANCE *tidc=talloc(NULL, TIDC_INSTANCE);
+  if (tidc!=NULL) {
+    tidc->client_dh=NULL;
+    talloc_set_destructor((void *)tidc, tidc_destructor);
+  }
   return tidc;
 }
 
-void tidc_destroy (TIDC_INSTANCE *tidc)
+void tidc_destroy(TIDC_INSTANCE *tidc)
 {
   talloc_free(tidc);
 }
@@ -109,7 +120,7 @@ int tidc_send_request (TIDC_INSTANCE *tidc,
     goto error;
   }
 
-  tid_req->tidc_dh = tidc->client_dh;
+  tid_req->tidc_dh = tr_dh_dup(tidc->client_dh);
 
   rc = tidc_fwd_request(tidc, tid_req, resp_handler, cookie);
   goto cleanup;
@@ -120,10 +131,10 @@ int tidc_send_request (TIDC_INSTANCE *tidc,
   return rc;
 }
 
-int tidc_fwd_request (TIDC_INSTANCE *tidc,
-                     TID_REQ *tid_req,
-                     TIDC_RESP_FUNC *resp_handler,
-                     void *cookie)
+int tidc_fwd_request(TIDC_INSTANCE *tidc,
+                     TID_REQ *tid_req,
+                    TIDC_RESP_FUNC *resp_handler,
+                     void *cookie)
 {
   char *req_buf = NULL;
   char *resp_buf = NULL;
@@ -186,10 +197,11 @@ int tidc_fwd_request (TIDC_INSTANCE *tidc,
   }
 
   if (resp_handler) {
-    /* Call the caller's response function */
+    /* Call the caller's response function. It must copy any data it needs before returning. */
     tr_debug("tidc_fwd_request: calling response callback function.");
     (*resp_handler)(tidc, tid_req, tr_msg_get_resp(resp_msg), cookie);
   }
+
   goto cleanup;
 
  error:
@@ -201,9 +213,8 @@ int tidc_fwd_request (TIDC_INSTANCE *tidc,
     free(req_buf);
   if (resp_buf)
     free(resp_buf);
-
-  /* TBD -- free the decoded response */
-
+  if (resp_msg)
+    tr_msg_free_decoded(resp_msg);
   return rc;
 }
 
index 47ecfcb..a08182d 100644 (file)
@@ -44,6 +44,7 @@
 #include <netinet/in.h>
 #include <jansson.h>
 #include <talloc.h>
+#include <poll.h>
 #include <tid_internal.h>
 #include <gsscon.h>
 #include <tr_debug.h>
@@ -54,7 +55,7 @@ static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req)
   TID_RESP *resp=NULL;
   int success=0;
 
-  if ((NULL == (resp = talloc_zero(req, TID_RESP)))) {
+  if (NULL == (resp = tid_resp_new(req))) {
     tr_crit("tids_create_response: Error allocating response structure.");
     return NULL;
   }
@@ -77,67 +78,87 @@ static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req)
 
 cleanup:
   if ((!success) && (resp!=NULL)) {
-    if (resp->rp_realm!=NULL)
-      tr_free_name(resp->rp_realm);
-    if (resp->realm!=NULL)
-      tr_free_name(resp->realm);
-    if (resp->comm!=NULL)
-      tr_free_name(resp->comm);
-    if (resp->orig_coi!=NULL)
-      tr_free_name(resp->orig_coi);
     talloc_free(resp);
     resp=NULL;
   }
   return resp;
 }
 
-static void tids_destroy_response(TIDS_INSTANCE *tids, TID_RESP *resp
+static int tids_listen(TIDS_INSTANCE *tids, int port, int *fd_out, size_t max_fd
 {
-  if (resp) {
-    if (resp->err_msg)
-      tr_free_name(resp->err_msg);
-    if (resp->rp_realm)
-      tr_free_name(resp->rp_realm);
-    if (resp->realm)
-      tr_free_name(resp->realm);
-    if (resp->comm)
-      tr_free_name(resp->comm);
-    if (resp->orig_coi)
-      tr_free_name(resp->orig_coi);
-    talloc_free(resp);
+  int rc = 0;
+  int conn = -1;
+  int optval = 1;
+  struct addrinfo *ai=NULL;
+  struct addrinfo *ai_head=NULL;
+  struct addrinfo hints={.ai_flags=AI_PASSIVE,
+                         .ai_family=AF_UNSPEC,
+                         .ai_socktype=SOCK_STREAM,
+                         .ai_protocol=IPPROTO_TCP};
+  char *port_str=NULL;
+  size_t n_opened=0;
+
+  tr_debug("tids_listen: started!");
+  port_str=talloc_asprintf(NULL, "%d", port);
+  if (port_str==NULL) {
+    tr_debug("tids_listen: unable to allocate port.");
+    return -1;
   }
-}
 
-static int tids_listen (TIDS_INSTANCE *tids, int port) 
-{
-    int rc = 0;
-    int conn = -1;
-    int optval = 1;
+  tr_debug("getaddrinfo()=%d", getaddrinfo(NULL, port_str, &hints, &ai_head));
+  talloc_free(port_str);
+  tr_debug("tids_listen: got address info");
 
-    union {
-      struct sockaddr_storage storage;
-      struct sockaddr_in in4;
-    } addr;
+  /* TODO: listen on all ports */
+  for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_opened<max_fd); ai=ai->ai_next) {
+    if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) {
+      tr_debug("tids_listen: unable to open socket.");
+      continue;
+    }
 
-    struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
+    optval=1;
+    if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
+      tr_debug("tids_listen: unable to set SO_REUSEADDR."); /* not fatal? */
+
+    if (ai->ai_family==AF_INET6) {
+      /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure
+       * if still relevant) */
+      if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) {
+        tr_debug("tids_listen: unable to set IPV6_V6ONLY. Skipping interface.");
+        close(conn);
+        continue;
+      }
+    }
 
-    saddr->sin_port = htons (port);
-    saddr->sin_family = AF_INET;
-    saddr->sin_addr.s_addr = INADDR_ANY;
+    rc=bind(conn, ai->ai_addr, ai->ai_addrlen);
+    if (rc<0) {
+      tr_debug("tids_listen: unable to bind to socket.");
+      close(conn);
+      continue;
+    }
 
-    if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
-      return conn;
+    if (0>listen(conn, 512)) {
+      tr_debug("tids_listen: unable to listen on bound socket.");
+      close(conn);
+      continue;
+    }
 
-    setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+    /* ok, this one worked. Save it */
+    fd_out[n_opened++]=conn;
+  }
+  freeaddrinfo(ai_head);
 
-    if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
-      return rc;
+  if (n_opened==0) {
+    tr_debug("tids_listen: no addresses available for listening.");
+    return -1;
+  }
 
-    if (0 > (rc = listen(conn, 512)))
-      return rc;
+  tr_debug("tids_listen: TRP Server listening on port %d on %d socket%s",
+           port,
+           n_opened,
+           (n_opened==1)?"":"s");
 
-    tr_debug("tids_listen: TID Server listening on port %d", port);
-    return conn;
+  return n_opened;
 }
 
 /* returns EACCES if authorization is denied */
@@ -174,8 +195,11 @@ static int tids_auth_connection (TIDS_INSTANCE *inst,
 
   if (rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tids_auth_cb, inst)) {
     tr_debug("tids_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
+    free(name);
     return -1;
   }
+  free(name);
+  nameBuffer.value=NULL; nameBuffer.length=0;
 
   if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
     tr_debug("tids_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", 
@@ -271,7 +295,6 @@ int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_m
     tr_crit("tids_send_err_response: Can't create response.");
     return -1;
   }
-
   
   /* mark this as an error response, and include the error message */
   resp->result = TID_ERROR;
@@ -280,7 +303,7 @@ int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_m
 
   rc = tids_send_response(tids, req, resp);
   
-  tids_destroy_response(tids, resp);
+  tid_resp_free(resp);
   return rc;
 }
 
@@ -378,55 +401,57 @@ static void tids_handle_connection (TIDS_INSTANCE *tids, int conn)
       tr_debug("tids_handle_connection: Error from tids_send_response(), rc = %d.", rc);
       /* if we didn't already send a response, try to send a generic error. */
       if (!tr_msg_get_req(mreq)->resp_sent)
-       tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response.");
+        tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response.");
       /* Fall through to free the response, either way. */
     }
     
-    tids_destroy_response(tids, resp);
-    tr_msg_free_decoded(mreq);
+    tr_msg_free_decoded(mreq); /* takes resp with it */
     return;
   } 
 }
 
-TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx)
+TIDS_INSTANCE *tids_create (void)
 {
-  return talloc_zero(mem_ctx, TIDS_INSTANCE);
+  return talloc_zero(NULL, TIDS_INSTANCE);
 }
 
 /* Get a listener for tids requests, returns its socket fd. Accept
  * connections with tids_accept() */
 int tids_get_listener(TIDS_INSTANCE *tids, 
                       TIDS_REQ_FUNC *req_handler,
-                      TIDS_AUTH_FUNC *auth_handler,
+                      tids_auth_func *auth_handler,
                       const char *hostname,
                       unsigned int port,
-                      void *cookie)
+                      void *cookie,
+                      int *fd_out,
+                      size_t max_fd)
 {
-  int listen = -1;
+  size_t n_fd=0;
+  size_t ii=0;
 
   tids->tids_port = port;
-  if (0 > (listen = tids_listen(tids, port))) {
-    char errbuf[256];
-    if (0 == strerror_r(errno, errbuf, 256)) {
-      tr_debug("tids_get_listener: Error opening port %d: %s.", port, errbuf);
-    } else {
-      tr_debug("tids_get_listener: Unknown error openining port %d.", port);
-    }
-  } 
-
-  if (listen > 0) {
+  n_fd=tids_listen(tids, port, fd_out, max_fd);
+  if (n_fd<=0)
+    tr_debug("tids_get_listener: Error opening port %d");
+  else {
     /* opening port succeeded */
     tr_debug("tids_get_listener: Opened port %d.", port);
     
     /* make this socket non-blocking */
-    if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
-      tr_debug("tids_get_listener: Error setting O_NONBLOCK.");
-      close(listen);
-      listen=-1;
+    for (ii=0; ii<n_fd; ii++) {
+      if (0 != fcntl(fd_out[ii], F_SETFL, O_NONBLOCK)) {
+        tr_debug("tids_get_listener: Error setting O_NONBLOCK.");
+        for (ii=0; ii<n_fd; ii++) {
+          close(fd_out[ii]);
+          fd_out[ii]=-1;
+        }
+        n_fd=0;
+        break;
+      }
     }
   }
 
-  if (listen > 0) {
+  if (n_fd>0) {
     /* store the caller's request handler & cookie */
     tids->req_handler = req_handler;
     tids->auth_handler = auth_handler;
@@ -434,7 +459,7 @@ int tids_get_listener(TIDS_INSTANCE *tids,
     tids->cookie = cookie;
   }
 
-  return listen;
+  return n_fd;
 }
 
 /* Accept and process a connection on a port opened with tids_get_listener() */
@@ -469,56 +494,64 @@ int tids_accept(TIDS_INSTANCE *tids, int listen)
 }
 
 /* Process tids requests forever. Should not return except on error. */
+#define MAX_SOCKETS 10
 int tids_start (TIDS_INSTANCE *tids, 
-               TIDS_REQ_FUNC *req_handler,
-               TIDS_AUTH_FUNC *auth_handler,
-               const char *hostname,
-               unsigned int port,
-               void *cookie)
+                TIDS_REQ_FUNC *req_handler,
+                tids_auth_func *auth_handler,
+                const char *hostname,
+                unsigned int port,
+                void *cookie)
 {
-  int listen = -1;
-  int conn = -1;
-  pid_t pid;
+  int fd[MAX_SOCKETS]={0};
+  size_t n_fd=0;
+  struct pollfd poll_fd[MAX_SOCKETS]={{0}};
+  int ii=0;
 
-  tids->tids_port = port;
-  if (0 > (listen = tids_listen(tids, port)))
+  n_fd=tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, MAX_SOCKETS);
+  if (n_fd <= 0) {
     perror ("Error from tids_listen()");
-
-  /* store the caller's request handler & cookie */
-  tids->req_handler = req_handler;
-  tids->auth_handler = auth_handler;
-  tids->hostname = hostname;
-  tids->cookie = cookie;
+    return 1;
+  }
 
   tr_info("Trust Path Query Server starting on host %s:%d.", hostname, port);
 
+  /* set up the poll structs */
+  for (ii=0; ii<n_fd; ii++) {
+    poll_fd[ii].fd=fd[ii];
+    poll_fd[ii].events=POLLIN;
+  }
+
   while(1) {   /* accept incoming conns until we are stopped */
+    /* clear out events from previous iteration */
+    for (ii=0; ii<n_fd; ii++)
+      poll_fd[ii].revents=0;
 
-    if (0 > (conn = accept(listen, NULL, NULL))) {
-      perror("Error from TIDS Server accept()");
+    /* wait indefinitely for a connection */
+    if (poll(poll_fd, n_fd, -1) < 0) {
+      perror("Error from poll()");
       return 1;
     }
 
-    if (0 > (pid = fork())) {
-      perror("Error on fork()");
-      return 1;
-    }
+    /* fork handlers for any sockets that have data */
+    for (ii=0; ii<n_fd; ii++) {
+      if (poll_fd[ii].revents == 0)
+        continue;
 
-    if (pid == 0) {
-      close(listen);
-      tids_handle_connection(tids, conn);
-      close(conn);
-      exit(0); /* exit to kill forked child process */
-    } else {
-      close(conn);
-    }
+      if ((poll_fd[ii].revents & POLLERR) || (poll_fd[ii].revents & POLLNVAL)) {
+        perror("Error polling fd");
+        continue;
+      }
 
-    /* clean up any processes that have completed */
-    while (waitpid(-1, 0, WNOHANG) > 0);
+      if (poll_fd[ii].revents & POLLIN) {
+        if (tids_accept(tids, poll_fd[ii].fd))
+          tr_err("tids_start: error in tids_accept().");
+      }
+    }
   }
 
   return 1;    /* should never get here, loops "forever" */
 }
+#undef MAX_SOCKETS
 
 void tids_destroy (TIDS_INSTANCE *tids)
 {
diff --git a/tr/internal.cfg b/tr/internal.cfg
new file mode 100644 (file)
index 0000000..7bfe0f5
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "tr_internal": {
+    "max_tree_depth": 12,
+    "hostname":"beta.example.com",
+    "trps_port":25308,
+    "tids_port":25309,
+    "cfg_poll_interval": 1,
+    "cfg_settling_time": 5,
+    "trp_sweep_interval": 30,
+    "trp_update_interval": 30,
+    "trp_connect_interval": 10,
+    "tid_request_timeout": 5,
+    "tid_response_numerator": 2,
+    "tid_response_denominator": 3,
+    "logging": {
+      "log_threshold": "info",
+      "console_threshold":"notice"
+    }
+  }
+}
diff --git a/tr/manual.cfg b/tr/manual.cfg
deleted file mode 100644 (file)
index 5d33638..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-{"tr_internal":{"max_tree_depth": 4,
-                "hostname":"margaret-moonshot3.local"}}
\ No newline at end of file
diff --git a/tr/organizations.cfg b/tr/organizations.cfg
new file mode 100644 (file)
index 0000000..5c190b8
--- /dev/null
@@ -0,0 +1,84 @@
+{
+  "communities": [
+    {
+      "apcs": [],
+      "community_id": "apc.x",
+      "idp_realms": ["idp.x", "other.idp.x"],
+      "rp_realms": ["rp.x", "other.rp.x"],
+      "type": "apc",
+      "expiration_interval": 10
+    },
+    {
+      "apcs": ["apc."],
+      "community_id": "coi.x",
+      "idp_realms": ["idp.x"],
+      "rp_realms": ["rp.x"],
+      "type": "coi"
+    }
+  ],
+  "local_organizations": [
+    {
+      "organization_name": "Demo Organization",
+      "realms": [
+       {
+         "realm": "rp.x",
+         "gss_names": ["alpha-cred@apc.x",
+                       "beta-cred@apc.x",
+                       "gamma-cred@apc.x"],
+         "filters": {
+           "tid_inbound": [
+             {
+               "action": "accept",
+               "domain_constraints": [
+                 "*.local"
+               ],
+               "specs": [
+                 {
+                   "field": "rp_realm",
+                   "match": "rp.x"
+                 },
+                 {
+                   "field": "rp_realm",
+                   "match": "*.rp.x"
+                 }
+               ],
+               "realm_constraints": [
+                 "rp.x", "*.rp.x"
+               ]
+             }
+           ]
+         }
+       },
+        {
+          "realm": "other.rp.x",
+          "gss_names": ["something@apc.x"]
+        },
+       {
+         "realm": "idp.x",
+         "gss_names": ["alpha-cred@apc.x"],
+         "identity_provider": {
+           "aaa_servers": ["alpha.local"],
+           "apcs": ["apc.x"],
+           "shared_config": "no"
+         }
+        },
+       {
+         "realm": "other.idp.x",
+         "gss_names": ["beta-cred@apc.x"],
+         "identity_provider": {
+           "aaa_servers": ["alpha.local"],
+           "apcs": ["apc.x"],
+           "shared_config": "no"
+         }
+       }
+      ]
+    }
+  ],
+  "peer_organizations": [
+    {
+      "hostname": "gamma.local",
+      "port": 12310,
+      "gss_names": ["gamma-cred@apc.x"]
+    }
+  ]
+}
diff --git a/tr/portal.cfg b/tr/portal.cfg
deleted file mode 100644 (file)
index 6ead29d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-{
-  "communities": [
-    {
-      "apcs": [
-      ],
-      "community_id": "apc.painless-security.com",
-      "idp_realms": [
-      ],
-      "rp_realms": [
-        "margaret-2.painless-security.com"
-      ],
-      "type": "apc"
-    }
-  ],
-  "idp_realms": [
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "idr1.offcenter.org",
-      "shared_config": "yes"
-    },
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "idr2.offcenter.org",
-      "shared_config": "no"
-    },
-    {
-      "aaa_servers": [
-        "10.1.10.90"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "ja.net",
-      "shared_config": "no"
-    },
-    {
-      "aaa_servers": [
-        "127.0.0.1"
-      ],
-      "apcs": [
-        "pci-community.ja.net"
-      ],
-      "realm_id": "no-longer-untitled.offcenter.org",
-      "shared_config": "yes"
-    }
-  ],
-  "rp_clients": [
-    {
-      "filter": {
-        "filter_lines": [
-          {
-            "action": "accept",
-            "domain_constraints": ["*.painless-security.com"],
-            "filter_specs": [
-              {
-                "field": "rp_realm",
-                "match": "margaret-2.painless-security.com"
-              },
-              {
-                "field": "rp_realm",
-                "match": "*.margaret-2.painless-security.com"
-              }
-            ],
-            "realm_constraints": ["*.painless-security.com", "a.com"]
-          }
-        ],
-        "type": "rp_permitted"
-      },
-      "gss_names": [
-        "4b95cd61-616f-48f2-9713-7d2cbe19ce69@apc.painless-security.com"
-      ]
-    }
-  ]
-}
index 5eb825e..1f2e2de 100644 (file)
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
   TR_INSTANCE *tr = NULL;
   struct cmdline_args opts;
   struct event_base *ev_base;
-  struct tr_socket_event tids_ev;
+  struct tr_socket_event tids_ev = {0};
   struct event *cfgwatch_ev;
 
   configure_signals();
@@ -174,10 +174,11 @@ int main(int argc, char *argv[])
   }
 
   /***** initialize the trust path query server instance *****/
-  if (NULL == (tr->tids = tids_create (tr))) {
+  if (NULL == (tr->tids = tids_create())) {
     tr_crit("Error initializing Trust Path Query Server instance.");
     return 1;
   }
+  talloc_steal(tr, tr->tids);
 
   /***** initialize the trust router protocol server instance *****/
   if (NULL == (tr->trps = trps_new(tr))) {
@@ -216,6 +217,7 @@ int main(int argc, char *argv[])
   /*tr_status_event_init();*/ /* install status reporting events */
 
   /* install TID server events */
+  tr_debug("Initializing TID server events.");
   if (0 != tr_tids_event_init(ev_base,
                               tr->tids,
                               tr->cfg_mgr,
@@ -226,11 +228,13 @@ int main(int argc, char *argv[])
   }
 
   /* install TRP handler events */
+  tr_debug("Initializing Dynamic Trust Router Protocol events.");
   if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) {
     tr_crit("Error initializing Trust Path Query Server instance.");
     return 1;
   }
 
+  tr_debug("Starting event loop.");
   tr_event_loop_run(ev_base); /* does not return until we are done */
 
   tr_destroy(tr); /* thanks to talloc, should destroy everything */
index 289ffba..64c0a16 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <talloc.h>
 
+#include <trust_router/tr_dh.h>
 #include <tid_internal.h>
 #include <tr_filter.h>
 #include <tr_comm.h>
 #include <gsscon.h>
 #include <trp_internal.h>
 #include <tr_config.h>
+#include <tr_mq.h>
+#include <tr_util.h>
 #include <tr_tid.h>
 
-/* Structure to hold TR instance and original request in one cookie */
+/* Structure to hold data for the tid response callback */
 typedef struct tr_resp_cookie {
-  TIDS_INSTANCE *tids;
-  TID_REQ *orig_req;
+  int thread_id;
+  TID_RESP *resp;
 } TR_RESP_COOKIE;
 
 /* hold a tids instance and a config manager */
@@ -59,43 +62,206 @@ struct tr_tids_event_cookie {
   TRPS_INSTANCE *trps;
 };
 
+static void tr_tidc_resp_handler(TIDC_INSTANCE *tidc, 
+                                 TID_REQ *req,
+                                 TID_RESP *resp, 
+                                 void *resp_cookie)
+{
+  TR_RESP_COOKIE *cookie=talloc_get_type_abort(resp_cookie, TR_RESP_COOKIE);
+
+  tr_debug("tr_tidc_resp_handler: Response received! Realm = %s, Community = %s, result = %s.",
+           resp->realm->buf,
+           resp->comm->buf,
+           (TID_SUCCESS==resp->result)?"success":"error");
+
+  if (resp->error_path!=NULL)
+    tr_debug("tr_tids_resp_handler: error_path is set.");
+  cookie->resp=tid_resp_dup(cookie, resp);
+}
+
+/* data for AAA req forwarding threads */
+struct tr_tids_fwd_cookie {
+  int thread_id;
+  pthread_mutex_t mutex; /* lock on the mq (separate from the locking within the mq, see below) */
+  TR_MQ *mq; /* messages from thread to main process; set to NULL to disable response */
+  TR_NAME *aaa_hostname;
+  DH *dh_params;
+  TID_REQ *fwd_req; /* the req to duplicate */
+};
 
-static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, 
-                                  TID_REQ *req,
-                                  TID_RESP *resp, 
-                                  void *resp_cookie)
+static int tr_tids_fwd_cookie_destructor(void *obj)
 {
-  tr_debug("tr_tidc_resp_handler: Response received (conn = %d)! Realm = %s, Community = %s.", ((TR_RESP_COOKIE *)resp_cookie)->orig_req->conn, resp->realm->buf, resp->comm->buf);
-  req->resp_rcvd = 1;
-
-  /* TBD -- handle concatentation of multiple responses to single req */
-  tids_send_response(((TR_RESP_COOKIE *)resp_cookie)->tids, 
-                    ((TR_RESP_COOKIE *)resp_cookie)->orig_req, 
-                    resp);
-  
-  return;
+  struct tr_tids_fwd_cookie *c=talloc_get_type_abort(obj, struct tr_tids_fwd_cookie);
+  if (c->aaa_hostname!=NULL)
+    tr_free_name(c->aaa_hostname);
+  if (c->dh_params!=NULL)
+    tr_destroy_dh_params(c->dh_params);
+  return 0;
 }
 
-static int tr_tids_req_handler (TIDS_INSTANCE *tids,
-                                TID_REQ *orig_req, 
-                                TID_RESP *resp,
-                                void *cookie_in)
+/* Block until we get the lock, returns 0 on success.
+ * The mutex is used to protect changes to the mq pointer in
+ * a thread's cookie. The master thread sets this to null to indicate
+ * that it has abandoned the thread and the message queue is no longer
+ * valid. This is unrelated to the locking in the message queue
+ * implementation itself. */
+static int tr_tids_fwd_get_mutex(struct tr_tids_fwd_cookie *cookie)
 {
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);;
-  TIDC_INSTANCE *tidc = NULL;
-  TR_RESP_COOKIE resp_cookie;
-  TR_AAA_SERVER *aaa_servers = NULL;
+  if (cookie==NULL)
+    return -1;
+
+  return (pthread_mutex_lock(&(cookie->mutex)));
+}
+
+static int tr_tids_fwd_release_mutex(struct tr_tids_fwd_cookie *cookie)
+{
+  if (cookie==NULL)
+    return -1;
+
+  return (pthread_mutex_unlock(&(cookie->mutex)));
+}
+
+/* values for messages */
+#define TR_TID_MQMSG_SUCCESS "tid success"
+#define TR_TID_MQMSG_FAILURE "tid failure"
+
+/* Thread main for sending and receiving a request to a single AAA server */
+static void *tr_tids_req_fwd_thread(void *arg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_tids_fwd_cookie *args=talloc_get_type_abort(arg, struct tr_tids_fwd_cookie);
+  TIDC_INSTANCE *tidc=tidc_create();
+  TR_MQ_MSG *msg=NULL;
+  TR_RESP_COOKIE *cookie=NULL;
+  int rc=0;
+  int success=0;
+
+  talloc_steal(tmp_ctx, args); /* take responsibility for the cookie */
+
+  if (tidc!=NULL)
+    talloc_steal(tmp_ctx, tidc);
+
+  /* create the cookie we will use for our response */
+  cookie=talloc(tmp_ctx, TR_RESP_COOKIE);
+  if (cookie==NULL) {
+    tr_notice("tr_tids_req_fwd_thread: unable to allocate response cookie.");
+    success=0;
+    goto cleanup;
+  }
+  cookie->thread_id=args->thread_id;
+  tr_debug("tr_tids_req_fwd_thread: thread %d started.", cookie->thread_id);
+
+  /* Create a TID client instance */
+  if (tidc==NULL) {
+    tr_crit("tr_tids_req_fwd_thread: Unable to allocate TIDC instance.");
+    /*tids_send_err_response(tids, orig_req, "Memory allocation failure");*/
+    /* TODO: encode reason for failure */
+    success=0;
+    goto cleanup;
+  }
+
+  /* Set-up TID connection */
+  if (-1==(args->fwd_req->conn = tidc_open_connection(tidc, 
+                                                      args->aaa_hostname->buf,
+                                                      TID_PORT, /* TODO: make this configurable */
+                                                     &(args->fwd_req->gssctx)))) {
+    tr_notice("tr_tids_req_fwd_thread: Error in tidc_open_connection.");
+    /* tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS"); */
+    /* TODO: encode reason for failure */
+    success=0;
+    goto cleanup;
+  };
+  tr_debug("tr_tids_req_fwd_thread: thread %d opened TID connection to %s.",
+           cookie->thread_id,
+           args->aaa_hostname->buf);
+
+  /* Send a TID request. */
+  if (0 > (rc = tidc_fwd_request(tidc, args->fwd_req, tr_tidc_resp_handler, (void *)cookie))) {
+    tr_notice("Error from tidc_fwd_request, rc = %d.", rc);
+    success=0;
+    goto cleanup;
+  }
+  /* cookie->resp should now contain our copy of the response */
+  success=1;
+  tr_debug("tr_tids_req_fwd_thread: thread %d received response.");
+
+cleanup:
+  /* Notify parent thread of the response, if it's still listening. */
+  if (0!=tr_tids_fwd_get_mutex(args)) {
+    tr_notice("tr_tids_req_fwd_thread: thread %d unable to acquire mutex.", cookie->thread_id);
+  } else if (NULL!=args->mq) {
+    /* mq is still valid, so we can queue our response */
+    tr_debug("tr_tids_req_fwd_thread: thread %d using valid msg queue.", cookie->thread_id);
+    if (success)
+      msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_SUCCESS, TR_MQ_PRIO_NORMAL);
+    else
+      msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_FAILURE, TR_MQ_PRIO_NORMAL);
+
+    if (msg==NULL)
+      tr_notice("tr_tids_req_fwd_thread: thread %d unable to allocate response msg.", cookie->thread_id);
+
+    tr_mq_msg_set_payload(msg, (void *)cookie, NULL);
+    if (NULL!=cookie)
+      talloc_steal(msg, cookie); /* attach this to the msg so we can forget about it */
+    tr_mq_add(args->mq, msg);
+    talloc_steal(NULL, args); /* take out of our tmp_ctx; master thread now responsible for freeing */
+    tr_debug("tr_tids_req_fwd_thread: thread %d queued response message.", cookie->thread_id);
+    if (0!=tr_tids_fwd_release_mutex(args))
+      tr_notice("tr_tids_req_fwd_thread: Error releasing mutex.");
+  }
+
+  talloc_free(tmp_ctx);
+  return NULL;
+}
+
+/* Merges r2 into r1 if they are compatible. */
+static TID_RC tr_tids_merge_resps(TID_RESP *r1, TID_RESP *r2)
+{
+  /* ensure these are compatible replies */
+  if ((r1->result!=TID_SUCCESS) || (r2->result!=TID_SUCCESS))
+    return TID_ERROR;
+
+  if ((0!=tr_name_cmp(r1->rp_realm, r2->rp_realm)) ||
+      (0!=tr_name_cmp(r1->realm, r2->realm)) ||
+      (0!=tr_name_cmp(r1->comm, r2->comm)))
+    return TID_ERROR;
+
+  tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers));
+  return TID_SUCCESS;
+}
+
+static int tr_tids_req_handler(TIDS_INSTANCE *tids,
+                               TID_REQ *orig_req, 
+                               TID_RESP *resp,
+                               void *cookie_in)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_AAA_SERVER *aaa_servers=NULL, *this_aaa=NULL;
+  int n_aaa=0;
+  int idp_shared=0;
+  TR_AAA_SERVER_ITER *aaa_iter=NULL;
+  pthread_t aaa_thread[TR_TID_MAX_AAA_SERVERS];
+  struct tr_tids_fwd_cookie *aaa_cookie[TR_TID_MAX_AAA_SERVERS]={NULL};
+  TID_RESP *aaa_resp[TR_TID_MAX_AAA_SERVERS]={NULL};
   TR_NAME *apc = NULL;
   TID_REQ *fwd_req = NULL;
   TR_COMM *cfg_comm = NULL;
   TR_COMM *cfg_apc = NULL;
   int oaction = TR_FILTER_ACTION_REJECT;
-  int rc = 0;
   time_t expiration_interval=0;
   struct tr_tids_event_cookie *cookie=talloc_get_type_abort(cookie_in, struct tr_tids_event_cookie);
   TR_CFG_MGR *cfg_mgr=cookie->cfg_mgr;
   TRPS_INSTANCE *trps=cookie->trps;
   TRP_ROUTE *route=NULL;
+  TR_MQ *mq=NULL;
+  TR_MQ_MSG *msg=NULL;
+  unsigned int n_responses=0;
+  unsigned int n_failed=0;
+  struct timespec ts_abort={0};
+  unsigned int resp_frac_numer=cfg_mgr->active->internal->tid_resp_numer;
+  unsigned int resp_frac_denom=cfg_mgr->active->internal->tid_resp_denom;
+  TR_RESP_COOKIE *payload=NULL;
+  int ii=0;
   int retval=-1;
 
   if ((!tids) || (!orig_req) || (!resp)) {
@@ -109,13 +275,14 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
   tids->req_count++;
 
   /* Duplicate the request, so we can modify and forward it */
-  if (NULL == (fwd_req = tid_dup_req(orig_req))) {
+  if (NULL == (fwd_req=tid_dup_req(orig_req))) {
     tr_debug("tr_tids_req_handler: Unable to duplicate request.");
     retval=-1;
     goto cleanup;
   }
+  talloc_steal(tmp_ctx, fwd_req);
 
-  if (NULL == (cfg_comm = tr_comm_lookup(cfg_mgr->active->comms, orig_req->comm))) {
+  if (NULL == (cfg_comm=tr_comm_table_find_comm(cfg_mgr->active->ctable, orig_req->comm))) {
     tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", orig_req->comm->buf);
     tids_send_err_response(tids, orig_req, "Unknown community");
     retval=-1;
@@ -123,7 +290,9 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
   }
 
   /* Check that the rp_realm matches the filter for the GSS name that 
-   * was received. */
+   * was received. N.B. that tids->rp_gss was pointed at the correct
+   * rp_client when we received its GSS name. It is only set within
+   * the TIDS handler subprocess. */
 
   if ((!tids->rp_gss) || 
       (!tids->rp_gss->filter)) {
@@ -145,7 +314,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
     goto cleanup;
   }
   /* Check that the rp_realm is a member of the community in the request */
-  if (NULL == (tr_find_comm_rp(cfg_comm, orig_req->rp_realm))) {
+  if (NULL == tr_comm_find_rp(cfg_mgr->active->ctable, cfg_comm, orig_req->rp_realm)) {
     tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
     tids_send_err_response(tids, orig_req, "RP COI membership error");
     retval=-1;
@@ -154,6 +323,14 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
 
   /* Map the comm in the request from a COI to an APC, if needed */
   if (TR_COMM_COI == cfg_comm->type) {
+    if (orig_req->orig_coi!=NULL) {
+      tr_notice("tr_tids_req_handler: community %s is COI but COI to APC mapping already occurred. Dropping request.",
+               orig_req->comm->buf);
+      tids_send_err_response(tids, orig_req, "Second COI to APC mapping would result, permitted only once.");
+      retval=-1;
+      goto cleanup;
+    }
+    
     tr_debug("tr_tids_req_handler: Community was a COI, switching.");
     /* TBD -- In theory there can be more than one?  How would that work? */
     if ((!cfg_comm->apcs) || (!cfg_comm->apcs->id)) {
@@ -165,7 +342,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
     apc = tr_dup_name(cfg_comm->apcs->id);
 
     /* Check that the APC is configured */
-    if (NULL == (cfg_apc = tr_comm_lookup(cfg_mgr->active->comms, apc))) {
+    if (NULL == (cfg_apc = tr_comm_table_find_comm(cfg_mgr->active->ctable, apc))) {
       tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf);
       tids_send_err_response(tids, orig_req, "Unknown APC");
       retval=-1;
@@ -176,7 +353,7 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
     fwd_req->orig_coi = orig_req->comm;
 
     /* Check that rp_realm is a  member of this APC */
-    if (NULL == (tr_find_comm_rp(cfg_apc, orig_req->rp_realm))) {
+    if (NULL == (tr_comm_find_rp(cfg_mgr->active->ctable, cfg_apc, orig_req->rp_realm))) {
       tr_notice("tr_tids_req_hander: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
       tids_send_err_response(tids, orig_req, "RP APC membership error");
       retval=-1;
@@ -196,13 +373,15 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
   }
   tr_debug("tr_tids_req_handler: found route.");
   if (trp_route_is_local(route)) {
-  tr_debug("tr_tids_req_handler: route is local.");
-    aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->idp_realms, 
+    tr_debug("tr_tids_req_handler: route is local.");
+    aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->ctable->idp_realms, 
                                            orig_req->realm, 
-                                           orig_req->comm);
+                                           orig_req->comm,
+                                          &idp_shared);
   } else {
     tr_debug("tr_tids_req_handler: route not local.");
     aaa_servers = tr_aaa_server_new(tmp_ctx, trp_route_get_next_hop(route));
+    idp_shared=0;
   }
 
   /* Find the AAA server(s) for this request */
@@ -215,15 +394,16 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
       retval=-1;
       goto cleanup;
     }
+    idp_shared=0;
   } else {
     /* if we aren't defaulting, check idp coi and apc membership */
-    if (NULL == (tr_find_comm_idp(cfg_comm, fwd_req->realm))) {
+    if (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_comm, fwd_req->realm))) {
       tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, orig_req->comm->buf);
       tids_send_err_response(tids, orig_req, "IDP community membership error");
       retval=-1;
       goto cleanup;
     }
-    if ( cfg_apc && (NULL == (tr_find_comm_idp(cfg_apc, fwd_req->realm)))) {
+    if ( cfg_apc && (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_apc, fwd_req->realm)))) {
       tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, orig_req->comm->buf);
       tids_send_err_response(tids, orig_req, "IDP APC membership error");
       retval=-1;
@@ -232,48 +412,174 @@ static int tr_tids_req_handler (TIDS_INSTANCE *tids,
   }
 
   /* send a TID request to the AAA server(s), and get the answer(s) */
-  /* TBD -- Handle multiple servers */
-
+  tr_debug("tr_tids_req_handler: sending TID request(s).");
   if (cfg_apc)
     expiration_interval = cfg_apc->expiration_interval;
   else expiration_interval = cfg_comm->expiration_interval;
   if (fwd_req->expiration_interval)
     fwd_req->expiration_interval =  (expiration_interval < fwd_req->expiration_interval) ? expiration_interval : fwd_req->expiration_interval;
   else fwd_req->expiration_interval = expiration_interval;
-  /* Create a TID client instance */
-  if (NULL == (tidc = tidc_create())) {
-    tr_crit("tr_tids_req_hander: Unable to allocate TIDC instance.");
-    tids_send_err_response(tids, orig_req, "Memory allocation failure");
+
+  /* Set up message queue for replies from req forwarding threads */
+  mq=tr_mq_new(tmp_ctx);
+  if (mq==NULL) {
+    tr_notice("tr_tids_req_handler: unable to allocate message queue.");
     retval=-1;
     goto cleanup;
   }
-  /* Use the DH parameters from the original request */
-  /* TBD -- this needs to be fixed when we handle more than one req per conn */
-  tidc->client_dh = orig_req->tidc_dh;
-
-  /* Save information about this request for the response */
-  resp_cookie.tids = tids;
-  resp_cookie.orig_req = orig_req;
+  tr_debug("tr_tids_req_handler: message queue allocated.");
 
-  /* Set-up TID connection */
-  if (-1 == (fwd_req->conn = tidc_open_connection(tidc, 
-                                                  aaa_servers->hostname->buf,
-                                                  TID_PORT,
-                                                 &(fwd_req->gssctx)))) {
-    tr_notice("tr_tids_req_handler: Error in tidc_open_connection.");
-    tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS");
+  /* start threads */
+  aaa_iter=tr_aaa_server_iter_new(tmp_ctx);
+  if (aaa_iter==NULL) {
+    tr_notice("tr_tids_req_handler: unable to allocate AAA server iterator.");
     retval=-1;
     goto cleanup;
-  };
+  }
+  for (n_aaa=0, this_aaa=tr_aaa_server_iter_first(aaa_iter, aaa_servers);
+       this_aaa!=NULL;
+       n_aaa++, this_aaa=tr_aaa_server_iter_next(aaa_iter)) {
+    tr_debug("tr_tids_req_handler: Preparing to start thread %d.", n_aaa);
+
+    aaa_cookie[n_aaa]=talloc(tmp_ctx, struct tr_tids_fwd_cookie);
+    if (aaa_cookie[n_aaa]==NULL) {
+      tr_notice("tr_tids_req_handler: unable to allocate cookie for AAA thread %d.", n_aaa);
+      retval=-1;
+      goto cleanup;
+    }
+    talloc_set_destructor((void *)(aaa_cookie[n_aaa]), tr_tids_fwd_cookie_destructor);
+    /* fill in the cookie. To ensure the thread has valid data even if we exit first and
+     * abandon it, duplicate anything pointed to (except the mq). */
+    aaa_cookie[n_aaa]->thread_id=n_aaa;
+    if (0!=pthread_mutex_init(&(aaa_cookie[n_aaa]->mutex), NULL)) {
+      tr_notice("tr_tids_req_handler: unable to init mutex for AAA thread %d.", n_aaa);
+      retval=-1;
+      goto cleanup;
+    }
+    aaa_cookie[n_aaa]->mq=mq;
+    aaa_cookie[n_aaa]->aaa_hostname=tr_dup_name(this_aaa->hostname);
+    aaa_cookie[n_aaa]->dh_params=tr_dh_dup(orig_req->tidc_dh);
+    aaa_cookie[n_aaa]->fwd_req=tid_dup_req(fwd_req);
+    talloc_steal(aaa_cookie[n_aaa], aaa_cookie[n_aaa]->fwd_req);
+    tr_debug("tr_tids_req_handler: cookie %d initialized.", n_aaa);
+
+    /* Take the cookie out of tmp_ctx before starting thread. If thread starts, it becomes
+     * responsible for freeing it until it queues a response. If we did not do this, the possibility
+     * exists that this function exits, freeing the cookie, before the thread takes the cookie
+     * out of our tmp_ctx. This would cause a segfault or talloc error in the thread. */
+    talloc_steal(NULL, aaa_cookie[n_aaa]);
+    if (0!=pthread_create(&(aaa_thread[n_aaa]), NULL, tr_tids_req_fwd_thread, aaa_cookie[n_aaa])) {
+      talloc_steal(tmp_ctx, aaa_cookie[n_aaa]); /* thread start failed; steal this back */
+      tr_notice("tr_tids_req_handler: unable to start AAA thread %d.", n_aaa);
+      retval=-1;
+      goto cleanup;
+    }
+    tr_debug("tr_tids_req_handler: thread %d started.", n_aaa);
+  }
 
-  /* Send a TID request */
-  if (0 > (rc = tidc_fwd_request(tidc, fwd_req, &tr_tidc_resp_handler, (void *)&resp_cookie))) {
-    tr_notice("Error from tidc_fwd_request, rc = %d.", rc);
-    tids_send_err_response(tids, orig_req, "Can't forward request to next hop TIDS");
+  /* determine expiration time */
+  if (0!=tr_mq_pop_timeout(cfg_mgr->active->internal->tid_req_timeout, &ts_abort)) {
+    tr_notice("tr_tids_req_handler: unable to read clock for timeout.");
     retval=-1;
     goto cleanup;
   }
 
+  /* wait for responses */
+  tr_debug("tr_tids_req_handler: waiting for response(s).");
+  n_responses=0;
+  n_failed=0;
+  while (((n_responses+n_failed)<n_aaa) &&
+         (NULL!=(msg=tr_mq_pop(mq, &ts_abort)))) {
+    /* process message */
+    if (0==strcmp(tr_mq_msg_get_message(msg), TR_TID_MQMSG_SUCCESS)) {
+      payload=talloc_get_type_abort(tr_mq_msg_get_payload(msg), TR_RESP_COOKIE);
+      talloc_steal(tmp_ctx, payload); /* put this back in our context */
+      aaa_resp[payload->thread_id]=payload->resp; /* save pointers to these */
+
+      if (payload->resp->result==TID_SUCCESS) {
+        tr_tids_merge_resps(resp, payload->resp);
+        n_responses++;
+      } else {
+        n_failed++;
+        tr_notice("tr_tids_req_handler: TID error received from AAA server %d: %.*s",
+                  payload->thread_id,
+                  payload->resp->err_msg->len,
+                  payload->resp->err_msg->buf);
+      }
+    } else if (0==strcmp(tr_mq_msg_get_message(msg), TR_TID_MQMSG_FAILURE)) {
+      /* failure */
+      n_failed++;
+      payload=talloc_get_type(tr_mq_msg_get_payload(msg), TR_RESP_COOKIE);
+      if (payload!=NULL) 
+        talloc_steal(tmp_ctx, payload); /* put this back in our context */
+      else {
+        /* this means the thread was unable to allocate a response cookie, and we thus cannot determine which thread it was. This is bad and should never happen in a working system.. Give up. */
+        tr_notice("tr_tids_req_handler: TID request thread sent invalid reply. Aborting!");
+        retval=-1;
+        goto cleanup;
+      }
+      tr_notice("tr_tids_req_handler: TID request for AAA server %d failed.",
+                payload->thread_id);
+    } else {
+      /* unexpected message */
+      tr_err("tr_tids_req_handler: Unexpected message received. Aborting!");
+      retval=-1;
+      goto cleanup;
+    }
+    
+    /* Set the cookie pointer to NULL so we know we've dealt with this one. The
+     * cookie itself is in our tmp_ctx, which we'll free before exiting. Let it hang
+     * around in case we are still using pointers to elements of the cookie. */
+    aaa_cookie[payload->thread_id]=NULL;
+
+    tr_mq_msg_free(msg);
+
+    /* check whether we've received enough responses to exit */
+    if ((idp_shared && (n_responses>0)) ||
+        (resp_frac_denom*n_responses>=resp_frac_numer*n_aaa))
+      break;
+  }
+
+  tr_debug("tr_tids_req_handler: done waiting for responses. %d responses, %d failures.",
+           n_responses, n_failed);
+  /* Inform any remaining threads that we will no longer handle their responses. */
+  for (ii=0; ii<n_aaa; ii++) {
+    if (aaa_cookie[ii]!=NULL) {
+      if (0!=tr_tids_fwd_get_mutex(aaa_cookie[ii]))
+        tr_notice("tr_tids_req_handler: unable to get mutex for AAA thread %d.", ii);
+
+      aaa_cookie[ii]->mq=NULL; /* threads will not try to respond through a null mq */
+
+      if (0!=tr_tids_fwd_release_mutex(aaa_cookie[ii]))
+        tr_notice("tr_tids_req_handler: unable to release mutex for AAA thread %d.", ii);
+    }
+  }
+
+  /* Now all threads have either replied (and aaa_cookie[ii] is null) or have been told not to
+   * reply (by setting their mq pointer to null). However, some may have responded by placing
+   * a message on the mq after we last checked but before we set their mq pointer to null. These
+   * will not know that we gave up on them, so we must free their cookies for them. We can just
+   * go through any remaining messages on the mq to identify these threads. By putting them in
+   * our context instead of freeing them directly, we ensure we don't accidentally invalidate
+   * any of our own pointers into the structure before this function exits. */
+  while (NULL!=(msg=tr_mq_pop(mq, NULL))) {
+    payload=(TR_RESP_COOKIE *)tr_mq_msg_get_payload(msg);
+    if (aaa_cookie[payload->thread_id]!=NULL)
+      talloc_steal(tmp_ctx, aaa_cookie[payload->thread_id]);
+
+    tr_mq_msg_free(msg);
+  }
+
+  if (n_responses==0) {
+    /* No requests succeeded. Forward an error if we got any error responses. */
+    for (ii=0; ii<n_aaa; ii++) {
+      if (aaa_resp[ii]!=NULL)
+        tids_send_response(tids, orig_req, aaa_resp[ii]);
+      else
+        tids_send_err_response(tids, orig_req, "Unable to contact AAA server(s).");
+    }
+  }
+
   /* success! */
   retval=0;
     
@@ -334,6 +640,7 @@ int tr_tids_event_init(struct event_base *base,
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   struct tr_tids_event_cookie *cookie=NULL;
   int retval=0;
+  size_t ii=0;
 
   if (tids_ev == NULL) {
     tr_debug("tr_tids_event_init: Null tids_ev.");
@@ -355,25 +662,29 @@ int tr_tids_event_init(struct event_base *base,
   talloc_steal(tids, cookie);
 
   /* get a tids listener */
-  tids_ev->sock_fd=tids_get_listener(tids,
-                                     tr_tids_req_handler,
-                                     tr_tids_gss_handler,
-                                     cfg_mgr->active->internal->hostname,
-                                     cfg_mgr->active->internal->tids_port,
-                                     (void *)cookie);
-  if (tids_ev->sock_fd < 0) {
+  tids_ev->n_sock_fd=tids_get_listener(tids,
+                                       tr_tids_req_handler,
+                                       tr_tids_gss_handler,
+                                       cfg_mgr->active->internal->hostname,
+                                       cfg_mgr->active->internal->tids_port,
+                                       (void *)cookie,
+                                       tids_ev->sock_fd,
+                                       TR_MAX_SOCKETS);
+  if (tids_ev->n_sock_fd==0) {
     tr_crit("Error opening TID server socket.");
     retval=1;
     goto cleanup;
   }
 
-  /* and its event */
-  tids_ev->ev=event_new(base,
-                        tids_ev->sock_fd,
-                        EV_READ|EV_PERSIST,
-                        tr_tids_event_cb,
-                        (void *)tids);
-  event_add(tids_ev->ev, NULL);
+  /* Set up events */
+  for (ii=0; ii<tids_ev->n_sock_fd; ii++) {
+    tids_ev->ev[ii]=event_new(base,
+                              tids_ev->sock_fd[ii],
+                              EV_READ|EV_PERSIST,
+                              tr_tids_event_cb,
+                              (void *)tids);
+    event_add(tids_ev->ev[ii], NULL);
+  }
 
 cleanup:
   talloc_free(tmp_ctx);
index ef44e89..2b93969 100644 (file)
@@ -214,7 +214,6 @@ static void tr_trps_cleanup_conn(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
   tr_debug("tr_trps_cleanup_conn: freeing %p", conn);
   pthread_join(*trp_connection_get_thread(conn), NULL);
   trps_remove_connection(trps, conn);
-  talloc_report_full(conn, stderr);
   trp_connection_free(conn);
   tr_debug("tr_trps_cleanup_conn: deleted connection");
 }
@@ -244,7 +243,6 @@ static void tr_trps_process_mq(int socket, short event, void *arg)
   TR_MQ_MSG *msg=NULL;
   const char *s=NULL;
 
-  talloc_report_full(trps->mq, stderr);
   msg=trps_mq_pop(trps);
   while (msg!=NULL) {
     s=tr_mq_msg_get_message(msg);
@@ -316,9 +314,10 @@ static void tr_trps_update(int listener, short event, void *arg)
   TRPS_INSTANCE *trps=cookie->trps;
   struct event *ev=cookie->ev;
 
-  tr_debug("tr_trps_update: sending scheduled route updates.");
+  tr_debug("tr_trps_update: sending scheduled route/community updates.");
   trps_update(trps, TRP_UPDATE_SCHEDULED);
   event_add(ev, &(trps->update_interval));
+  tr_debug("tr_trps_update: update interval=%d", trps->update_interval.tv_sec);
 }
 
 static void tr_trps_sweep(int listener, short event, void *arg)
@@ -329,6 +328,8 @@ static void tr_trps_sweep(int listener, short event, void *arg)
 
   tr_debug("tr_trps_sweep: sweeping routes.");
   trps_sweep_routes(trps);
+  tr_debug("tr_trps_sweep: sweeping communities.");
+  trps_sweep_ctable(trps);
   tr_trps_print_route_table(trps, stderr);
   /* schedule the event to run again */
   event_add(ev, &(trps->sweep_interval));
@@ -395,6 +396,7 @@ TRP_RC tr_trps_event_init(struct event_base *base, TR_INSTANCE *tr)
   struct tr_trps_event_cookie *sweep_cookie=NULL;
   struct timeval zero_time={0,0};
   TRP_RC retval=TRP_ERROR;
+  size_t ii=0;
 
   if (tr->events != NULL) {
     tr_notice("tr_trps_event_init: tr->events was not null. Freeing before reallocating..");
@@ -425,28 +427,31 @@ TRP_RC tr_trps_event_init(struct event_base *base, TR_INSTANCE *tr)
   trps_cookie->cfg_mgr=tr->cfg_mgr;
 
   /* get a trps listener */
-  listen_ev->sock_fd=trps_get_listener(tr->trps,
-                                       tr_trps_msg_handler,
-                                       tr_trps_gss_handler,
-                                       tr->cfg_mgr->active->internal->hostname,
-                                       tr->cfg_mgr->active->internal->trps_port,
-                                       (void *)trps_cookie);
-  if (listen_ev->sock_fd < 0) {
+  listen_ev->n_sock_fd=trps_get_listener(tr->trps,
+                                         tr_trps_msg_handler,
+                                         tr_trps_gss_handler,
+                                         tr->cfg_mgr->active->internal->hostname,
+                                         tr->cfg_mgr->active->internal->trps_port,
+                                         (void *)trps_cookie,
+                                         listen_ev->sock_fd,
+                                         TR_MAX_SOCKETS);
+  if (listen_ev->n_sock_fd==0) {
     tr_crit("Error opening TRP server socket.");
     retval=TRP_ERROR;
     tr_trps_events_free(tr->events);
     tr->events=NULL;
     goto cleanup;
   }
-  trps_cookie->ev=listen_ev->ev; /* in case it needs to frob the event */
-  
-  /* and its event */
-  listen_ev->ev=event_new(base,
-                          listen_ev->sock_fd,
-                          EV_READ|EV_PERSIST,
-                          tr_trps_event_cb,
-                          (void *)(tr->trps));
-  event_add(listen_ev->ev, NULL);
+
+  /* Set up events for the sockets */
+  for (ii=0; ii<listen_ev->n_sock_fd; ii++) {
+    listen_ev->ev[ii]=event_new(base,
+                                listen_ev->sock_fd[ii],
+                                EV_READ|EV_PERSIST,
+                                tr_trps_event_cb,
+                                (void *)(tr->trps));
+    event_add(listen_ev->ev[ii], NULL);
+  }
   
   /* now set up message queue processing event, only triggered by
    * tr_trps_mq_cb() */
@@ -545,6 +550,8 @@ static void *tr_trpc_thread(void *arg)
   const char *msg_type=NULL;
   char *encoded_msg=NULL;
   TR_NAME *peer_gssname=NULL;
+  int n_sent=0;
+  int exit_loop=0;
 
   struct trpc_notify_cb_data cb_data={0,
                                       PTHREAD_COND_INITIALIZER,
@@ -583,41 +590,40 @@ static void *tr_trpc_thread(void *arg)
     trps_mq_add(trps, msg); /* steals msg context */
     msg=NULL;
 
-    while(1) {
+    while(!exit_loop) {
       cb_data.msg_ready=0;
       pthread_cond_wait(&(cb_data.cond), &(cb_data.mutex));
       /* verify the condition */
       if (cb_data.msg_ready) {
-        msg=trpc_mq_pop(trpc);
-        if (msg==NULL) {
-          /* no message in the queue */
-          tr_err("tr_trpc_thread: notified of msg, but queue empty");
-          break;
-        }
-
-        msg_type=tr_mq_msg_get_message(msg);
+        for (msg=trpc_mq_pop(trpc),n_sent=0; msg!=NULL; msg=trpc_mq_pop(trpc),n_sent++) {
+          msg_type=tr_mq_msg_get_message(msg);
 
-        if (0==strcmp(msg_type, TR_MQMSG_ABORT)) {
-          tr_mq_msg_free(msg);
-          break; /* exit loop */
-        }
-        else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) {
-          encoded_msg=tr_mq_msg_get_payload(msg);
-          if (encoded_msg==NULL)
-            tr_notice("tr_trpc_thread: null outgoing TRP message.");
-          else {
-            rc = trpc_send_msg(trpc, encoded_msg);
-            if (rc!=TRP_SUCCESS) {
-              tr_notice("tr_trpc_thread: trpc_send_msg failed.");
-              tr_mq_msg_free(msg);
-              break;
+          if (0==strcmp(msg_type, TR_MQMSG_ABORT)) {
+            exit_loop=1;
+            break;
+          }
+          else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) {
+            encoded_msg=tr_mq_msg_get_payload(msg);
+            if (encoded_msg==NULL)
+              tr_notice("tr_trpc_thread: null outgoing TRP message.");
+            else {
+              rc = trpc_send_msg(trpc, encoded_msg);
+              if (rc!=TRP_SUCCESS) {
+                tr_notice("tr_trpc_thread: trpc_send_msg failed.");
+                exit_loop=1;
+                break;
+              }
             }
           }
-        }
-        else
-          tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
+          else
+            tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
 
-        tr_mq_msg_free(msg);
+          tr_mq_msg_free(msg);
+        }
+        if (n_sent==0)
+          tr_err("tr_trpc_thread: notified of msg, but queue empty");
+        else 
+          tr_debug("tr_trpc_thread: sent %d messages.", n_sent);
       }
     }
   }
@@ -755,7 +761,7 @@ TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg)
   if (trust_router_name==NULL)
     return TRP_NOMEM;
 
-  for (cur=cfg->idp_realms; cur!=NULL; cur=cur->next) {
+  for (cur=cfg->ctable->idp_realms; cur!=NULL; cur=cur->next) {
     local_routes=tr_make_local_routes(tmp_ctx, cur, trust_router_name, &n_routes);
     for (ii=0; ii<n_routes; ii++)
       trps_add_route(trps, local_routes[ii]);
@@ -834,6 +840,7 @@ void tr_config_changed(TR_CFG *new_cfg, void *cookie)
   trps_set_connect_interval(trps, new_cfg->internal->trp_connect_interval);
   trps_set_update_interval(trps, new_cfg->internal->trp_update_interval);
   trps_set_sweep_interval(trps, new_cfg->internal->trp_sweep_interval);
+  trps_set_ctable(trps, new_cfg->ctable);
   trps_set_ptable(trps, new_cfg->peers);
   trps_set_peer_status_callback(trps, tr_peer_status_change, (void *)trps);
   trps_clear_rtable(trps); /* should we do this every time??? */
index 8b16abd..f5ea1fb 100644 (file)
@@ -44,6 +44,8 @@
 static int trp_peer_destructor(void *object)
 {
   TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER);
+  if (peer->label!=NULL)
+    tr_free_name(peer->label);
   if (peer->servicename!=NULL)
     tr_free_name(peer->servicename);
   return 0;
@@ -53,6 +55,7 @@ TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
   TRP_PEER *peer=talloc(memctx, TRP_PEER);
   if (peer!=NULL) {
     peer->next=NULL;
+    peer->label=NULL;
     peer->server=NULL;
     peer->servicename=NULL;
     peer->gss_names=NULL;
@@ -86,15 +89,16 @@ static TRP_PEER *trp_peer_tail(TRP_PEER *peer)
  * Do not modify or free the label. */
 TR_NAME *trp_peer_get_label(TRP_PEER *peer)
 {
-  TR_GSS_NAMES_ITER *iter=tr_gss_names_iter_new(NULL);
-  TR_NAME *name=NULL;
+  char *s=NULL;
 
-  /* for now, use the first gss name */
-  if (iter!=NULL) {
-    name=tr_gss_names_iter_first(iter, peer->gss_names);
-    talloc_free(iter);
+  if (peer->label==NULL) {
+    s=talloc_asprintf(NULL, "%s:%u", peer->server, peer->port);
+    if (s!=NULL) {
+      peer->label=tr_new_name(s);
+      talloc_free(s);
+    }
   }
-  return name;
+  return peer->label;
 }
 
 /* Get a name that identifies this peer for display to the user, etc. 
index 927c993..98898ff 100644 (file)
 
 #include <trust_router/tr_name.h>
 #include <trp_internal.h>
+#include <tr_comm.h>
+#include <tr_apc.h>
 #include <tr_debug.h>
 
 
 /* static prototypes */
-static void *trp_inforec_route_new(TALLOC_CTX *mem_ctx);
-static void trp_inforec_route_print(TRP_INFOREC_DATA);
+static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx);
+static void trp_inforec_route_print(TRP_INFOREC_DATA *);
+static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx);
+static void trp_inforec_comm_print(TRP_INFOREC_DATA *);
 
 
 struct trp_inforec_type_entry {
   const char *name;
   TRP_INFOREC_TYPE type;
-  void *(*allocate)(TALLOC_CTX *);
-  void (*print)(TRP_INFOREC_DATA);
+  TRP_INFOREC_DATA *(*allocate)(TALLOC_CTX *);
+  void (*print)(TRP_INFOREC_DATA *);
 };
 static struct trp_inforec_type_entry trp_inforec_type_table[] = {
   { "route", TRP_INFOREC_TYPE_ROUTE, trp_inforec_route_new, trp_inforec_route_print },
-  { "comm", TRP_INFOREC_TYPE_COMMUNITY, NULL, NULL },
+  { "comm", TRP_INFOREC_TYPE_COMMUNITY, trp_inforec_comm_new, trp_inforec_comm_print },
   { NULL, TRP_INFOREC_TYPE_UNKNOWN, NULL, NULL } /* must be the last entry */
 };
 
@@ -95,45 +99,77 @@ static int trp_inforec_route_destructor(void *object)
   TRP_INFOREC_ROUTE *body=talloc_get_type_abort(object, TRP_INFOREC_ROUTE);
   
   /* clean up TR_NAME data, which are not managed by talloc */
-  if (body->comm != NULL) {
-    tr_free_name(body->comm);
-    body->comm=NULL;
-    tr_debug("trp_inforec_route_destructor: freed community");
-  }
-  if (body->realm != NULL) {
-    tr_free_name(body->realm);
-    body->realm=NULL;
-    tr_debug("trp_inforec_route_destructor: freed realm");
-  }
-  if (body->trust_router != NULL) {
+  if (body->trust_router != NULL)
     tr_free_name(body->trust_router);
-    body->trust_router=NULL;
-    tr_debug("trp_inforec_route_destructor: freed trust_router");
-  }
-  if (body->next_hop != NULL) {
+  if (body->next_hop != NULL)
     tr_free_name(body->next_hop);
-    body->next_hop=NULL;
-    tr_debug("trp_inforec_route_destructor: freed next_hop");
-  }
-
   return 0;
 }
 
-static void *trp_inforec_route_new(TALLOC_CTX *mem_ctx)
+static TRP_INFOREC_DATA *trp_inforec_route_new(TALLOC_CTX *mem_ctx)
 {
-  TRP_INFOREC_ROUTE *new_rec=talloc(mem_ctx, TRP_INFOREC_ROUTE);
+  TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA);
+  TRP_INFOREC_ROUTE *new_rec=NULL;
+
+  if (new_data==NULL)
+    return NULL;
 
-  if (new_rec != NULL) {
-    new_rec->comm=NULL;
-    new_rec->realm=NULL;
+  new_rec=talloc(new_data, TRP_INFOREC_ROUTE);
+  if (new_rec == NULL) {
+    talloc_free(new_data);
+    new_data=NULL;
+  } else {
     new_rec->trust_router=NULL;
     new_rec->next_hop=NULL;
     new_rec->next_hop_port=0;
     new_rec->metric=TRP_METRIC_INFINITY;
     new_rec->interval=0;
     talloc_set_destructor((void *)new_rec, trp_inforec_route_destructor);
+    new_data->route=new_rec;
   }
-  return new_rec;
+    
+  return new_data;
+}
+
+
+static int trp_inforec_comm_destructor(void *obj)
+{
+  TRP_INFOREC_COMM *rec=talloc_get_type_abort(obj, TRP_INFOREC_COMM);
+  if (rec->owner_realm!=NULL)
+    tr_free_name(rec->owner_realm);
+  if (rec->owner_contact!=NULL)
+    tr_free_name(rec->owner_contact);
+  if (rec->provenance!=NULL)
+    json_decref(rec->provenance);
+  return 0;
+}
+
+static TRP_INFOREC_DATA *trp_inforec_comm_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_INFOREC_DATA *new_data=talloc(mem_ctx, TRP_INFOREC_DATA);
+  TRP_INFOREC_COMM *new_rec=NULL;
+
+  if (new_data==NULL)
+    return NULL;
+
+  new_rec=talloc(new_data, TRP_INFOREC_COMM);
+  if (new_rec==NULL) {
+    talloc_free(new_data);
+    new_data=NULL;
+  } else {
+    new_rec->comm_type=TR_COMM_UNKNOWN;
+    new_rec->role=TR_ROLE_UNKNOWN;
+    new_rec->apcs=NULL;
+    new_rec->owner_realm=NULL;
+    new_rec->owner_contact=NULL;
+    new_rec->expiration_interval=0;
+    new_rec->provenance=NULL;
+    new_rec->interval=0;
+    talloc_set_destructor((void *)new_rec, trp_inforec_comm_destructor);
+    new_data->comm=new_rec;
+  }
+
+  return new_data;
 }
 
 TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec)
@@ -159,7 +195,7 @@ void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec)
 
 TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec)
 {
-  if (rec)
+  if (rec!=NULL)
     return rec->type;
   else
     return TRP_INFOREC_TYPE_UNKNOWN;
@@ -171,12 +207,12 @@ void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type)
     rec->type=type;
 }
 
-TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec)
+TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->comm;
+    if (rec->data->route!=NULL)
+      return rec->data->route->trust_router;
     break;
   default:
     break;
@@ -184,17 +220,17 @@ TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec)
   return NULL;
 }
 
-TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec)
+TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec)
 {
-  return tr_dup_name(trp_inforec_get_comm(rec));
+  return tr_dup_name(trp_inforec_get_trust_router(rec));
 }
 
-TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm)
+TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->comm=comm;
+    if (rec->data->route!=NULL) {
+      rec->data->route->trust_router=trust_router;
       return TRP_SUCCESS;
     }
     break;
@@ -204,12 +240,13 @@ TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm)
   return TRP_ERROR;
 }
 
-TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec)
+/* TODO: need to return hostname/port --jlr */
+TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->realm;
+    if (rec->data->route!=NULL)
+      return rec->data->route->next_hop;
     break;
   default:
     break;
@@ -217,84 +254,140 @@ TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec)
   return NULL;
 }
 
-TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec)
+TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec)
 {
-  return tr_dup_name(trp_inforec_get_realm(rec));
+  return tr_dup_name(trp_inforec_get_next_hop(rec));
 }
 
-TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm)
+TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->realm=realm;
-      return TRP_SUCCESS;
-    } 
+    if (rec->data->route==NULL)
+      return TRP_ERROR;
+    rec->data->route->next_hop=next_hop;
+    break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    /* next hop not used for community records */
     break;
   default:
     break;
   }
-  return TRP_ERROR;
+  return TRP_SUCCESS;
 }
 
-TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec)
+unsigned int trp_inforec_get_metric(TRP_INFOREC *rec)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->trust_router;
+    if (rec->data->route!=NULL)
+      return rec->data->route->metric;
     break;
   default:
     break;
   }
-  return NULL;
+  return TRP_METRIC_INVALID;
 }
 
-TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec)
+TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric)
 {
-  return tr_dup_name(trp_inforec_get_trust_router(rec));
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data->route!=NULL) {
+      rec->data->route->metric=metric;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
 }
 
-TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router)
+unsigned int trp_inforec_get_interval(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data->route!=NULL)
+      return rec->data->route->interval;
+    break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->interval;
+    break;
+  default:
+    break;
+  }
+  return TRP_INTERVAL_INVALID;
+}
+
+TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->trust_router=trust_router;
+    if (rec->data->route!=NULL) {
+      rec->data->route->interval=interval;
       return TRP_SUCCESS;
     }
     break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->interval=interval;
+      return TRP_SUCCESS;
+    }
   default:
     break;
   }
   return TRP_ERROR;
 }
 
-/* TODO: need to return hostname/port --jlr */
-TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec)
+time_t trp_inforec_get_exp_interval(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->next_hop;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->expiration_interval;
     break;
   default:
     break;
   }
-  return NULL;
+  return 0;
 }
 
-TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec)
+TRP_RC trp_inforec_set_exp_interval(TRP_INFOREC *rec, time_t expint)
 {
-  return tr_dup_name(trp_inforec_get_next_hop(rec));
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->expiration_interval=expint;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
 }
 
-TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop)
+TR_COMM_TYPE trp_inforec_get_comm_type(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->next_hop=next_hop;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->comm_type;
+    break;
+  default:
+    break;
+  }
+  return TR_COMM_UNKNOWN;
+}
+
+TRP_RC trp_inforec_set_comm_type(TRP_INFOREC *rec, TR_COMM_TYPE type)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->comm_type=type;
       return TRP_SUCCESS;
     }
     break;
@@ -304,53 +397,83 @@ TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop)
   return TRP_ERROR;
 }
 
-unsigned int trp_inforec_get_metric(TRP_INFOREC *rec)
+TR_REALM_ROLE trp_inforec_get_role(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->metric;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->role;
     break;
   default:
     break;
   }
-  return TRP_METRIC_INVALID;
+  return TR_ROLE_UNKNOWN;
 }
 
-TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric)
+TRP_RC trp_inforec_set_role(TRP_INFOREC *rec, TR_REALM_ROLE role)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->metric=metric;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->role=role;
       return TRP_SUCCESS;
+      break;
     }
+  default:
     break;
+  }
+  return TRP_ERROR;
+}
+
+TR_APC *trp_inforec_get_apcs(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->apcs;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TRP_RC trp_inforec_set_apcs(TRP_INFOREC *rec, TR_APC *apcs)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->apcs=apcs;
+      talloc_steal(rec, apcs);
+      return TRP_SUCCESS;
+    }
+    break;
+
   default:
     break;
   }
   return TRP_ERROR;
 }
 
-unsigned int trp_inforec_get_interval(TRP_INFOREC *rec)
+TR_NAME *trp_inforec_get_owner_realm(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL)
-      return rec->data.route->interval;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->owner_realm;
     break;
   default:
     break;
   }
-  return TRP_INTERVAL_INVALID;
+  return NULL;
 }
 
-TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval)
+TRP_RC trp_inforec_set_owner_realm(TRP_INFOREC *rec, TR_NAME *name)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data.route!=NULL) {
-      rec->data.route->interval=interval;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->owner_realm=name;
       return TRP_SUCCESS;
   default:
     break;
@@ -360,35 +483,132 @@ TRP_RC trp_inforec_set_interval(TRP_INFOREC *rec, unsigned int interval)
   return TRP_ERROR;
 }
 
-/* for internal use only; must set rec->type before calling this */
-static TRP_RC trp_inforec_set_data(TRP_INFOREC *rec, void *data)
+TR_NAME *trp_inforec_get_owner_contact(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->owner_contact;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TRP_RC trp_inforec_set_owner_contact(TRP_INFOREC *rec, TR_NAME *name)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      rec->data->comm->owner_contact=name;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+/* caller needs to incref the output if they're going to hang on to it */
+json_t *trp_inforec_get_provenance(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL)
+      return rec->data->comm->provenance;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+/* increments the reference count */
+TRP_RC trp_inforec_set_provenance(TRP_INFOREC *rec, json_t *prov)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    if (rec->data->comm!=NULL) {
+      if (rec->data->comm->provenance!=NULL)
+        json_decref(rec->data->comm->provenance);
+      rec->data->comm->provenance=prov;
+      json_incref(prov);
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+static TRP_RC trp_inforec_add_to_provenance(TRP_INFOREC *rec, TR_NAME *name)
 {
-  if (data==NULL)
-    return TRP_ERROR;
+  json_t *jname=NULL;
 
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
-    rec->data.route=talloc_get_type(data, TRP_INFOREC_ROUTE);
+    /* no provenance list */
+    break;
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    jname=tr_name_to_json_string(name);
+    if (jname==NULL)
+      return TRP_ERROR;
+    if (rec->data->comm->provenance==NULL) {
+      rec->data->comm->provenance=json_array();
+      if (rec->data->comm->provenance==NULL) {
+        json_decref(jname);
+        return TRP_ERROR;
+      }
+    }
+    if (0!=json_array_append_new(rec->data->comm->provenance, jname)) {
+      json_decref(jname);
+      return TRP_ERROR;
+    }
     break;
   default:
-    return TRP_BADTYPE;
+    break;
   }
   return TRP_SUCCESS;
 }
 
+TR_NAME *trp_inforec_dup_origin(TRP_INFOREC *rec)
+{
+  TR_NAME *origin=NULL;
+  json_t *prov=trp_inforec_get_provenance(rec);
+  const char *s=NULL;
+
+  if (prov==NULL)
+    return NULL;
+
+  s=json_string_value(json_array_get(prov, 0));
+  if (s==NULL) {
+    tr_debug("trp_inforec_dup_origin: empty origin in provenance list.");
+    return NULL;
+  }
+  origin=tr_new_name(s);
+  return origin;
+}
+
 /* generic record type */
 TRP_INFOREC *trp_inforec_new(TALLOC_CTX *mem_ctx, TRP_INFOREC_TYPE type)
 {
   TRP_INFOREC *new_rec=talloc(mem_ctx, TRP_INFOREC);
+  TRP_INFOREC_DATA *data=NULL;
   struct trp_inforec_type_entry *dtype=get_trp_inforec_type_entry(type);
 
   if ((new_rec != NULL) && (dtype->type != TRP_INFOREC_TYPE_UNKNOWN)) {
     trp_inforec_set_type(new_rec, type);
     trp_inforec_set_next(new_rec, NULL);
     if (dtype->allocate!=NULL) {
-      if (TRP_SUCCESS!=trp_inforec_set_data(new_rec, dtype->allocate(new_rec))) {
+      data=dtype->allocate(new_rec);
+      if (data!=NULL)
+        new_rec->data=data;
+      else {
         talloc_free(new_rec);
-        new_rec=NULL;
+        return NULL;
       }
     }
   }
@@ -404,6 +624,10 @@ void trp_inforec_free(TRP_INFOREC *rec)
 static int trp_upd_destructor(void *object)
 {
   TRP_UPD *upd=talloc_get_type_abort(object, TRP_UPD);
+  if (upd->realm!=NULL)
+    tr_free_name(upd->realm);
+  if (upd->comm!=NULL)
+    tr_free_name(upd->comm);
   if (upd->peer!=NULL)
     tr_free_name(upd->peer);
   return 0;
@@ -414,6 +638,8 @@ TRP_UPD *trp_upd_new(TALLOC_CTX *mem_ctx)
   TRP_UPD *new_body=talloc(mem_ctx, TRP_UPD);
 
   if (new_body!=NULL) {
+    new_body->realm=NULL;
+    new_body->comm=NULL;
     new_body->records=NULL;
     new_body->peer=NULL;
     talloc_set_destructor((void *)new_body, trp_upd_destructor);
@@ -451,6 +677,40 @@ void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec)
   talloc_steal(upd, rec);
 }
 
+TR_NAME *trp_upd_get_realm(TRP_UPD *upd)
+{
+  return upd->realm;
+}
+
+TR_NAME *trp_upd_dup_realm(TRP_UPD *upd)
+{
+  return tr_dup_name(upd->realm);
+}
+
+void trp_upd_set_realm(TRP_UPD *upd, TR_NAME *realm)
+{
+  if (upd->realm!=NULL)
+    tr_free_name(upd->realm);
+  upd->realm=realm;
+}
+
+TR_NAME *trp_upd_get_comm(TRP_UPD *upd)
+{
+  return upd->comm;
+}
+
+TR_NAME *trp_upd_dup_comm(TRP_UPD *upd)
+{
+  return tr_dup_name(upd->comm);
+}
+
+void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm)
+{
+  if (upd->comm!=NULL)
+    tr_free_name(upd->comm);
+  upd->comm=comm;
+}
+
 TR_NAME *trp_upd_get_peer(TRP_UPD *upd)
 {
   return upd->peer;
@@ -473,21 +733,41 @@ void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port)
   
   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
     if (trp_inforec_set_next_hop(rec, cpy=tr_new_name(hostname)) != TRP_SUCCESS) {
-      tr_err("trp_upd_set_peer: error setting peer.");
+      tr_err("trp_upd_set_next_hop: error setting next hop.");
       tr_free_name(cpy);
     }
   }
 }
 
+void trp_upd_add_to_provenance(TRP_UPD *upd, TR_NAME *name)
+{
+  TRP_INFOREC *rec=NULL;
+
+  /* add it to all inforecs */
+  for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
+    if (TRP_SUCCESS!=trp_inforec_add_to_provenance(rec, name))
+      tr_err("trp_upd_set_peer: error adding peer to provenance list.");
+  }
+}
+
 /* pretty print */
-static void trp_inforec_route_print(TRP_INFOREC_DATA data)
+static void trp_inforec_route_print(TRP_INFOREC_DATA *data)
 {
-  if (data.route!=NULL) {
-    printf("     community=%.*s\n     realm=%.*s\n     trust_router=%.*s\n     metric=%d\n     interval=%d]\n",
-           data.route->comm->len, data.route->comm->buf,
-           data.route->realm->len, data.route->realm->buf,
-           data.route->trust_router->len, data.route->trust_router->buf,
-           data.route->metric, data.route->interval);
+  if (data->route!=NULL) {
+    printf("     trust_router=%.*s\n     metric=%d\n     interval=%d]\n",
+           data->route->trust_router->len, data->route->trust_router->buf,
+           data->route->metric, data->route->interval);
   }
 }
 
+static void trp_inforec_comm_print(TRP_INFOREC_DATA *data)
+{
+  if (data->comm!=NULL) {
+    printf("     type=%s\n     role=%s\n     owner=%.*s\n     contact=%.*s]\n",
+           tr_comm_type_to_str(data->comm->comm_type),
+           tr_realm_role_to_str(data->comm->role),
+           data->comm->owner_realm->len, data->comm->owner_realm->buf,
+           data->comm->owner_contact->len, data->comm->owner_contact->buf);
+    /* TODO: print apcs */
+  }
+}
index 4f32584..b6a2cea 100644 (file)
@@ -191,7 +191,7 @@ void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg)
 
 TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc)
 {
-  return tr_mq_pop(trpc->mq);
+  return tr_mq_pop(trpc->mq, 0);
 }
 
 void trpc_mq_clear(TRPC_INSTANCE *trpc)
index 47479c7..c706f72 100644 (file)
 #include <errno.h>
 #include <unistd.h>
 #include <sys/time.h>
+#include <glib.h>
+#include <string.h>
 
 #include <gsscon.h>
+#include <tr_comm.h>
+#include <tr_apc.h>
 #include <tr_rp.h>
 #include <trust_router/tr_name.h>
 #include <trp_internal.h>
@@ -46,7 +50,7 @@
 #include <trp_ptable.h>
 #include <trp_rtable.h>
 #include <tr_debug.h>
-
+#include <tr_util.h>
 
 static int trps_destructor(void *object)
 {
@@ -116,7 +120,7 @@ void trps_free (TRPS_INSTANCE *trps)
 
 TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
 {
-  return tr_mq_pop(trps->mq);
+  return tr_mq_pop(trps->mq, 0);
 }
 
 void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
@@ -157,6 +161,11 @@ void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
   trps->sweep_interval.tv_usec=0;
 }
 
+void trps_set_ctable(TRPS_INSTANCE *trps, TR_COMM_TABLE *comm)
+{
+  trps->ctable=comm;
+}
+
 void trps_set_ptable(TRPS_INSTANCE *trps, TRP_PTABLE *ptable)
 {
   if (trps->ptable!=NULL)
@@ -177,6 +186,22 @@ void trps_set_peer_status_callback(TRPS_INSTANCE *trps, void (*cb)(TRP_PEER *, v
   trp_ptable_iter_free(iter);
 }
 
+/* Get the label peers will know us by - needs to match trp_peer_get_label() output.
+ * There is no get, only dup, because we don't store the label except when requested. */
+TR_NAME *trps_dup_label(TRPS_INSTANCE *trps)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_NAME *label=NULL;
+  char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->port);
+  if (s==NULL)
+    goto cleanup;
+  label=tr_new_name(s);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return label;
+}
+
 TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
 {
   TRPC_INSTANCE *cur=NULL;
@@ -251,36 +276,80 @@ TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
   return rc;
 }
 
-static int trps_listen (TRPS_INSTANCE *trps, int port) 
+/* Listens on all interfaces. Returns number of sockets opened. Their
+ * descriptors are stored in *fd_out, which should point to space for
+ * up to max_fd of them. */
+static size_t trps_listen(TRPS_INSTANCE *trps, int port, int *fd_out, size_t max_fd) 
 {
   int rc = 0;
   int conn = -1;
-  int optval = 1;
-
-  union {
-    struct sockaddr_storage storage;
-    struct sockaddr_in in4;
-  } addr;
+  int optval=0;
+  struct addrinfo *ai=NULL;
+  struct addrinfo *ai_head=NULL;
+  struct addrinfo hints={.ai_flags=AI_PASSIVE,
+                         .ai_family=AF_UNSPEC,
+                         .ai_socktype=SOCK_STREAM,
+                         .ai_protocol=IPPROTO_TCP};
+  char *port_str=NULL;
+  size_t n_opened=0;
+
+  port_str=talloc_asprintf(NULL, "%d", port);
+  if (port_str==NULL) {
+    tr_debug("trps_listen: unable to allocate port.");
+    return -1;
+  }
+  getaddrinfo(NULL, port_str, &hints, &ai_head);
+  talloc_free(port_str);
 
-  struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
+  for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_opened<max_fd); ai=ai->ai_next) {
+    if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) {
+      tr_debug("trps_listen: unable to open socket.");
+      continue;
+    }
 
-  saddr->sin_port = htons (port);
-  saddr->sin_family = AF_INET;
-  saddr->sin_addr.s_addr = INADDR_ANY;
+    optval=1;
+    if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
+      tr_debug("trps_listen: unable to set SO_REUSEADDR."); /* not fatal? */
+
+    if (ai->ai_family==AF_INET6) {
+      /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure
+       * if still relevant) */
+      if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) {
+        tr_debug("trps_listen: unable to set IPV6_V6ONLY. Skipping interface.");
+        close(conn);
+        continue;
+      }
+    }
 
-  if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
-    return conn;
+    rc=bind(conn, ai->ai_addr, ai->ai_addrlen);
+    if (rc<0) {
+      tr_debug("trps_listen: unable to bind to socket.");
+      close(conn);
+      continue;
+    }
 
-  setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+    if (0>listen(conn, 512)) {
+      tr_debug("trps_listen: unable to listen on bound socket.");
+      close(conn);
+      continue;
+    }
 
-  if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
-    return rc;
+    /* ok, this one worked. Save it */
+    fd_out[n_opened++]=conn;
+  }
+  freeaddrinfo(ai_head);
 
-  if (0 > (rc = listen(conn, 512)))
-    return rc;
+  if (n_opened==0) {
+    tr_debug("trps_listen: no addresses available for listening.");
+    return -1;
+  }
+  
+  tr_debug("trps_listen: TRP Server listening on port %d on %d socket%s",
+           port,
+           n_opened,
+           (n_opened==1)?"":"s");
 
-  tr_debug("trps_listen: TRP Server listening on port %d", port);
-  return conn;
+  return n_opened;
 }
 
 /* get the currently selected route if available */
@@ -363,6 +432,8 @@ static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MS
   case TRP_UPDATE:
     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer));
     trp_upd_set_next_hop(tr_msg_get_trp_upd(*msg), trp_peer_get_server(peer), 0); /* TODO: 0 should be the configured TID port */
+    /* update provenance if necessary */
+    trp_upd_add_to_provenance(tr_msg_get_trp_upd(*msg), trp_peer_get_label(peer));
     break;
 
   case TRP_REQUEST:
@@ -384,32 +455,35 @@ int trps_get_listener(TRPS_INSTANCE *trps,
                       TRP_AUTH_FUNC auth_handler,
                       const char *hostname,
                       unsigned int port,
-                      void *cookie)
+                      void *cookie,
+                      int *fd_out,
+                      size_t max_fd)
 {
-  int listen = -1;
-
-  if (0 > (listen = trps_listen(trps, port))) {
-    char errbuf[256];
-    if (0 == strerror_r(errno, errbuf, 256)) {
-      tr_debug("trps_get_listener: Error opening port %d: %s.", port, errbuf);
-    } else {
-      tr_debug("trps_get_listener: Unknown error openining port %d.", port);
-    }
-  } 
+  size_t n_fd=0;
+  size_t ii=0;
 
-  if (listen > 0) {
+  n_fd=trps_listen(trps, port, fd_out, max_fd);
+  if (n_fd==0)
+    tr_debug("trps_get_listener: Error opening port %d.");
+  else {
     /* opening port succeeded */
     tr_debug("trps_get_listener: Opened port %d.", port);
     
-    /* make this socket non-blocking */
-    if (0 != fcntl(listen, F_SETFL, O_NONBLOCK)) {
-      tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
-      close(listen);
-      listen=-1;
+    /* make the sockets non-blocking */
+    for (ii=0; ii<n_fd; ii++) {
+      if (0 != fcntl(fd_out[ii], F_SETFL, O_NONBLOCK)) {
+        tr_debug("trps_get_listener: Error setting O_NONBLOCK.");
+        for (ii=0; ii<n_fd; ii++) {
+          close(fd_out[ii]);
+          fd_out[ii]=-1;
+        }
+        n_fd=0;
+        break;
+      }
     }
   }
 
-  if (listen > 0) {
+  if (n_fd>0) {
     /* store the caller's request handler & cookie */
     trps->msg_handler = msg_handler;
     trps->auth_handler = auth_handler;
@@ -418,7 +492,7 @@ int trps_get_listener(TRPS_INSTANCE *trps,
     trps->cookie = cookie;
   }
 
-  return listen;
+  return n_fd;
 }
 
 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
@@ -458,6 +532,7 @@ void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
   tr_debug("trps_handle_connection: connection closed.");
 }
 
+/* TODO: check realm/comm, now part of the update instead of inforec */
 static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
 {
   if (upd==NULL) {
@@ -465,6 +540,16 @@ static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
     return TRP_BADARG;
   }
 
+  if (trp_upd_get_realm(upd)==NULL) {
+    tr_notice("trps_validate_update: received TRP update without realm.");
+    return TRP_ERROR;
+  }
+
+  if (trp_upd_get_comm(upd)==NULL) {
+    tr_notice("trps_validate_update: received TRP update without community.");
+    return TRP_ERROR;
+  }
+
   if (trp_upd_get_inforec(upd)==NULL) {
     tr_notice("trps_validate_update: received TRP update with no info records.");
     return TRP_ERROR;
@@ -474,6 +559,7 @@ static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
     tr_notice("trps_validate_update: received TRP update without origin peer information.");
     return TRP_ERROR;
   }
+
   
   return TRP_SUCCESS;
 }
@@ -483,9 +569,7 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
 {
   switch(trp_inforec_get_type(rec)) {
   case TRP_INFOREC_TYPE_ROUTE:
-    if ((trp_inforec_get_comm(rec)==NULL)
-       || (trp_inforec_get_realm(rec)==NULL)
-       || (trp_inforec_get_trust_router(rec)==NULL)
+    if ((trp_inforec_get_trust_router(rec)==NULL)
        || (trp_inforec_get_next_hop(rec)==NULL)) {
       tr_debug("trps_validate_inforec: missing record info.");
       return TRP_ERROR;
@@ -504,6 +588,10 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
     }
     break;
 
+  case TRP_INFOREC_TYPE_COMMUNITY:
+    /* TODO: validate community updates */
+    break;
+    
   default:
     tr_notice("trps_validate_inforec: unsupported record type.");
     return TRP_UNSUPPORTED;
@@ -526,7 +614,7 @@ static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, T
   return trp_route_get_metric(entry) + trps_cost(trps, peer);
 }
 
-static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
+static int trps_check_feasibility(TRPS_INSTANCE *trps, TR_NAME *realm, TR_NAME *comm, TRP_INFOREC *rec)
 {
   unsigned int rec_metric=trp_inforec_get_metric(rec);
   unsigned int new_metric=0;
@@ -542,9 +630,7 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
     return 1;
 
   /* updates from our current next hop are always feasible*/
-  next_hop=trps_get_next_hop(trps,
-                             trp_inforec_get_comm(rec),
-                             trp_inforec_get_realm(rec));;
+  next_hop=trps_get_next_hop(trps, comm, realm);
   if ((next_hop!=NULL)
      && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
     return 1;
@@ -553,10 +639,7 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
 
   /* compare the existing metric we advertise to what we would advertise
    * if we accept this update */
-  current_metric=trps_advertised_metric(trps,
-                                        trp_inforec_get_comm(rec),
-                                        trp_inforec_get_realm(rec),
-                                        trp_inforec_get_next_hop(rec));
+  current_metric=trps_advertised_metric(trps, comm, realm, trp_inforec_get_next_hop(rec));
   new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
   if (new_metric <= current_metric)
     return 1;
@@ -568,11 +651,12 @@ static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
 static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int interval, struct timespec *ts)
 {
   const unsigned int small_factor=3; /* how many intervals we wait before expiring */
-  if (0!=clock_gettime(CLOCK_REALTIME, ts)) {
+  if (0!=clock_gettime(TRP_CLOCK, ts)) {
     tr_err("trps_compute_expiry: could not read realtime clock.");
     ts->tv_sec=0;
     ts->tv_nsec=0;
   }
+  tr_debug("trps_compute_expiry: tv_sec=%u, interval=%u, small_factor*interval=%u", ts->tv_sec, interval, small_factor*interval);
   ts->tv_sec += small_factor*interval;
   return ts;
 }
@@ -582,8 +666,8 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
   TRP_ROUTE *entry=NULL;
 
   entry=trp_rtable_get_entry(trps->rtable,
-                             trp_inforec_get_comm(rec),
-                             trp_inforec_get_realm(rec),
+                             trp_upd_get_comm(upd),
+                             trp_upd_get_realm(upd),
                              trp_inforec_get_next_hop(rec));
   if (entry==NULL) {
     entry=trp_route_new(NULL);
@@ -592,8 +676,8 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
       return TRP_NOMEM;
     }
 
-    trp_route_set_comm(entry, trp_inforec_dup_comm(rec));
-    trp_route_set_realm(entry, trp_inforec_dup_realm(rec));
+    trp_route_set_comm(entry, trp_upd_dup_comm(upd));
+    trp_route_set_realm(entry, trp_upd_dup_realm(upd));
     trp_route_set_peer(entry, trp_upd_dup_peer(upd));
     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec));
     trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec));
@@ -636,11 +720,292 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
   return TRP_SUCCESS;
 }
 
-static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
+
+static TRP_RC trps_handle_inforec_route(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
 {
+  TRP_ROUTE *route=NULL;
   unsigned int feas=0;
+
+  /* determine feasibility */
+  feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec);
+  tr_debug("trps_handle_update: record feasibility=%d", feas);
+
+  /* do we have an existing route? */
+  route=trps_get_route(trps,
+                       trp_upd_get_comm(upd),
+                       trp_upd_get_realm(upd),
+                       trp_upd_get_peer(upd));
+  if (route!=NULL) {
+    /* there was a route table entry already */
+    tr_debug("trps_handle_updates: route entry already exists.");
+    if (feas) {
+      /* Update is feasible. Accept it. */
+      trps_accept_update(trps, upd, rec);
+    } else {
+      /* Update is infeasible. Ignore it unless the trust router has changed. */
+      if (0!=tr_name_cmp(trp_route_get_trust_router(route),
+                         trp_inforec_get_trust_router(rec))) {
+        /* the trust router associated with the route has changed, treat update as a retraction */
+        trps_retract_route(trps, route);
+      }
+    }
+  } else {
+    /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
+    tr_debug("trps_handle_update: no route entry exists yet.");
+    if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
+      trps_accept_update(trps, upd, rec);
+  }
+
+  return TRP_SUCCESS;
+}
+
+static int trps_name_in_provenance(TR_NAME *name, json_t *prov)
+{
+  size_t ii=0;
+  TR_NAME *this_name=NULL;
+  const char *s=NULL;
+
+  if (prov==NULL)
+    return 0; /* no provenance list, so it has no names in it */
+
+  /* now check to see if name is in the provenance */
+  for (ii=0; ii<json_array_size(prov); ii++) {
+    s=json_string_value(json_array_get(prov, ii));
+    if (s==NULL) {
+      tr_debug("trps_name_in_provenance: empty entry in provenance list.");
+      continue;
+    }
+
+    this_name=tr_new_name(s);
+    if (this_name==NULL) {
+      tr_debug("trps_name_in_provenance: unable to allocate name.");
+      return -1;
+    }
+    if (0==tr_name_cmp(name, this_name)) {
+      tr_free_name(this_name);
+      return 1;
+    }
+    tr_free_name(this_name);
+  }
+  return 0;
+}
+
+static TR_COMM *trps_create_new_comm(TALLOC_CTX *mem_ctx, TR_NAME *comm_id, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM *comm=tr_comm_new(tmp_ctx);
+  
+  if (comm==NULL) {
+    tr_debug("trps_create_new_comm: unable to allocate new community.");
+    goto cleanup;
+  }
+  /* fill in the community with info */
+  tr_comm_set_id(comm, tr_dup_name(comm_id));
+  if (tr_comm_get_id(comm)==NULL) {
+    tr_debug("trps_create_new_comm: unable to allocate community name.");
+    comm=NULL;
+    goto cleanup;
+  }
+  tr_comm_set_type(comm, trp_inforec_get_comm_type(rec));
+  if (trp_inforec_get_apcs(rec)!=NULL) {
+    tr_comm_set_apcs(comm, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
+    if (tr_comm_get_apcs(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate APC list.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  if (trp_inforec_get_owner_realm(rec)!=NULL) {
+    tr_comm_set_owner_realm(comm, tr_dup_name(trp_inforec_get_owner_realm(rec)));
+    if (tr_comm_get_owner_realm(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate owner realm name.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  if (trp_inforec_get_owner_contact(rec)!=NULL) {
+    tr_comm_set_owner_contact(comm, tr_dup_name(trp_inforec_get_owner_contact(rec)));
+    if (tr_comm_get_owner_contact(comm)==NULL) {
+      tr_debug("trps_create_new_comm: unable to allocate owner contact.");
+      comm=NULL;
+      goto cleanup;
+    }
+  }
+  comm->expiration_interval=trp_inforec_get_exp_interval(rec);
+  talloc_steal(mem_ctx, comm);
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return comm;
+}
+
+static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx);
+  
+  if (rp==NULL) {
+    tr_debug("trps_create_new_rp_realm: unable to allocate new realm.");
+    goto cleanup;
+  }
+  /* fill in the realm */
+  tr_rp_realm_set_id(rp, tr_dup_name(realm_id));
+  if (tr_rp_realm_get_id(rp)==NULL) {
+    tr_debug("trps_create_new_rp_realm: unable to allocate realm name.");
+    rp=NULL;
+    goto cleanup;
+  }
+  talloc_steal(mem_ctx, rp);
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return rp;
+}
+
+static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx);
+  
+  if (idp==NULL) {
+    tr_debug("trps_create_new_idp_realm: unable to allocate new realm.");
+    goto cleanup;
+  }
+  /* fill in the realm */
+  tr_idp_realm_set_id(idp, tr_dup_name(realm_id));
+  if (tr_idp_realm_get_id(idp)==NULL) {
+    tr_debug("trps_create_new_idp_realm: unable to allocate realm name.");
+    idp=NULL;
+    goto cleanup;
+  }
+  if (trp_inforec_get_apcs(rec)!=NULL) {
+    tr_idp_realm_set_apcs(idp, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
+    if (tr_idp_realm_get_apcs(idp)==NULL) {
+      tr_debug("trps_create_new_idp_realm: unable to allocate APC list.");
+      idp=NULL;
+      goto cleanup;
+    }
+  }
+  idp->origin=TR_REALM_DISCOVERED;
+  
+  talloc_steal(mem_ctx, idp);
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return idp;
+}
+
+static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_NAME *comm_id=trp_upd_get_comm(upd);
+  TR_NAME *realm_id=trp_upd_get_realm(upd);
+  TR_NAME *origin_id=NULL;
+  TR_NAME *our_peer_label=NULL;
+  TR_COMM *comm=NULL;
+  TR_RP_REALM *rp_realm=NULL;
+  TR_IDP_REALM *idp_realm=NULL;
+  struct timespec expiry={0,0};
+  TRP_RC rc=TRP_ERROR;
+
+  if ((comm_id==NULL) || (realm_id==NULL))
+    goto cleanup;
+
+  origin_id=trp_inforec_dup_origin(rec);
+  if (origin_id==NULL)
+    goto cleanup;
+    
+  /* see whether we want to add this */
+  our_peer_label=trps_dup_label(trps);
+  if (our_peer_label==NULL) {
+    tr_debug("trps_handle_inforec_comm: unable to allocate peer label.");
+    goto cleanup;
+  }
+
+  if (trps_name_in_provenance(our_peer_label, trp_inforec_get_provenance(rec)))
+    tr_debug("trps_handle_inforec_comm: rejecting community inforec to avoid provenance loop.");
+  else {
+    /* no loop occurring, accept the update */
+    comm=tr_comm_table_find_comm(trps->ctable, comm_id);
+    if (comm==NULL) {
+      tr_debug("trps_handle_inforec_comm: unknown community %.*s in inforec, creating it.",
+               comm_id->len, comm_id->buf);
+      comm=trps_create_new_comm(tmp_ctx, comm_id, rec);
+      if (comm==NULL) {
+        tr_debug("trps_handle_inforec_comm: unable to create new community.");
+        goto cleanup;
+      }
+      tr_comm_table_add_comm(trps->ctable, comm);
+    }
+    /* TODO: see if other comm data match the new inforec and update or complain */
+
+    trps_compute_expiry(trps, trp_inforec_get_interval(rec), &expiry);
+    if ((expiry.tv_sec==0)&&(expiry.tv_nsec==0))
+      goto cleanup;
+
+    switch (trp_inforec_get_role(rec)) {
+    case TR_ROLE_RP:
+      rp_realm=tr_rp_realm_lookup(trps->ctable->rp_realms, realm_id);
+      if (rp_realm==NULL) {
+        tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.",
+                 realm_id->len, realm_id->buf);
+        rp_realm=trps_create_new_rp_realm(tmp_ctx, realm_id, rec);
+        if (rp_realm==NULL) {
+          tr_debug("trps_handle_inforec_comm: unable to create new RP realm.");
+          /* we may leave an unused community in the table, but it will only last until
+           * the next table sweep if it does not get any realms before that happens */
+          goto cleanup;
+        }
+        tr_comm_table_add_rp_realm(trps->ctable, rp_realm);
+      }
+      /* TODO: if realm existed, see if data match the new inforec and update or complain */
+      tr_comm_add_rp_realm(trps->ctable, comm, rp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
+      tr_debug("trps_handle_inforec_comm: added RP realm %.*s to comm %.*s (origin %.*s).",
+               realm_id->len, realm_id->buf,
+               comm_id->len, comm_id->buf,
+               origin_id->len, origin_id->buf);
+      break;
+    case TR_ROLE_IDP:
+      idp_realm=tr_idp_realm_lookup(trps->ctable->idp_realms, realm_id);
+      if (idp_realm==NULL) {
+        tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.",
+                 realm_id->len, realm_id->buf);
+        idp_realm=trps_create_new_idp_realm(tmp_ctx, realm_id, rec);
+        if (idp_realm==NULL) {
+          tr_debug("trps_handle_inforec_comm: unable to create new IDP realm.");
+          /* we may leave an unused community in the table, but it will only last until
+           * the next table sweep if it does not get any realms before that happens */
+          goto cleanup;
+        }
+        tr_comm_table_add_idp_realm(trps->ctable, idp_realm);
+      }
+      /* TODO: if realm existed, see if data match the new inforec and update or complain */
+      tr_comm_add_idp_realm(trps->ctable, comm, idp_realm, trp_inforec_get_interval(rec), trp_inforec_get_provenance(rec), &expiry);
+      tr_debug("trps_handle_inforec_comm: added IDP realm %.*s to comm %.*s (origin %.*s).",
+               realm_id->len, realm_id->buf,
+               comm_id->len, comm_id->buf,
+               origin_id->len, origin_id->buf);
+      break;
+    default:
+      tr_debug("trps_handle_inforec_comm: unable to add realm.");
+      goto cleanup;
+    }
+  } 
+
+  rc=TRP_SUCCESS;
+
+cleanup:
+  if (our_peer_label!=NULL)
+    tr_free_name(our_peer_label);
+  if (origin_id!=NULL)
+    tr_free_name(origin_id);
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
+{
   TRP_INFOREC *rec=NULL;
-  TRP_ROUTE *route=NULL;
 
   if (trps_validate_update(trps, upd) != TRP_SUCCESS) {
     tr_notice("trps_handle_update: received invalid TRP update.");
@@ -650,40 +1015,27 @@ static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
     /* validate/sanity check the record update */
     if (trps_validate_inforec(trps, rec) != TRP_SUCCESS) {
-      tr_notice("trps_handle_update: invalid record in TRP update, discarding entire update.");
+      tr_notice("trps_handle_update: invalid inforec in TRP update, discarding entire update.");
       return TRP_ERROR;
     }
   }
 
   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
-    /* determine feasibility */
-    feas=trps_check_feasibility(trps, rec);
-    tr_debug("trps_handle_update: record feasibility=%d", feas);
-
-    /* do we have an existing route? */
-    route=trps_get_route(trps,
-                         trp_inforec_get_comm(rec),
-                         trp_inforec_get_realm(rec),
-                         trp_upd_get_peer(upd));
-    if (route!=NULL) {
-      /* there was a route table entry already */
-      tr_debug("trps_handle_updates: route entry already exists.");
-      if (feas) {
-        /* Update is feasible. Accept it. */
-        trps_accept_update(trps, upd, rec);
-      } else {
-        /* Update is infeasible. Ignore it unless the trust router has changed. */
-        if (0!=tr_name_cmp(trp_route_get_trust_router(route),
-                           trp_inforec_get_trust_router(rec))) {
-          /* the trust router associated with the route has changed, treat update as a retraction */
-          trps_retract_route(trps, route);
-        }
-      }
-    } else {
-      /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
-      tr_debug("trps_handle_update: no route entry exists yet.");
-      if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
-        trps_accept_update(trps, upd, rec);
+    switch (trp_inforec_get_type(rec)) {
+    case TRP_INFOREC_TYPE_ROUTE:
+      tr_debug("trps_handle_update: handling route inforec.");
+      if (TRP_SUCCESS!=trps_handle_inforec_route(trps, upd, rec))
+        tr_notice("trps_handle_update: error handling route inforec.");
+      break;
+    case TRP_INFOREC_TYPE_COMMUNITY:
+      tr_debug("trps_handle_update: handling community inforec.");
+      if (TRP_SUCCESS!=trps_handle_inforec_comm(trps, upd, rec))
+        tr_notice("trps_handle_update: error handling community inforec.");
+
+      break;
+    default:
+      tr_notice("trps_handle_update: unsupported inforec in TRP update.");
+      break;
     }
   }
   return TRP_SUCCESS;
@@ -791,9 +1143,7 @@ TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
 /* true if curtime >= expiry */
 static int trps_expired(struct timespec *expiry, struct timespec *curtime)
 {
-  return ((curtime->tv_sec > expiry->tv_sec)
-         || ((curtime->tv_sec == expiry->tv_sec)
-            &&(curtime->tv_nsec > expiry->tv_nsec)));
+  return (tr_cmp_timespec(curtime, expiry) >= 0);
 }
 
 /* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
@@ -806,7 +1156,7 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
   size_t ii=0;
 
   /* use a single time for the entire sweep */
-  if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
+  if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
     tr_err("trps_sweep_routes: could not read realtime clock.");
     sweep_time.tv_sec=0;
     sweep_time.tv_nsec=0;
@@ -839,6 +1189,173 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
   return TRP_SUCCESS;
 }
 
+
+static char *timespec_to_str(struct timespec *ts)
+{
+  struct tm tm;
+  char *s=NULL;
+
+  if (localtime_r(&(ts->tv_sec), &tm)==NULL)
+    return NULL;
+
+  s=malloc(40); /* long enough to contain strftime result */
+  if (s==NULL)
+    return NULL;
+
+  if (strftime(s, 40, "%F %T", &tm)==0) {
+    free(s);
+    return NULL;
+  }
+  return s;
+}
+
+
+/* Sweep for expired communities/realms/memberships. */
+TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct timespec sweep_time={0,0};
+  TR_COMM_MEMB *memb=NULL;
+  TR_COMM_ITER *iter=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  /* use a single time for the entire sweep */
+  if (0!=clock_gettime(TRP_CLOCK, &sweep_time)) {
+    tr_err("trps_sweep_ctable: could not read realtime clock.");
+    sweep_time.tv_sec=0;
+    sweep_time.tv_nsec=0;
+    goto cleanup;
+  }
+
+  /* iterate all memberships */
+  iter=tr_comm_iter_new(tmp_ctx);
+  if (iter==NULL) {
+    tr_err("trps_sweep_ctable: unable to allocate iterator.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+  for (memb=tr_comm_memb_iter_all_first(iter, trps->ctable);
+       memb!=NULL;
+       memb=tr_comm_memb_iter_all_next(iter)) {
+    if (tr_comm_memb_get_origin(memb)==NULL)
+      continue; /* do not expire local entries */
+
+    if (tr_comm_memb_is_expired(memb, &sweep_time)) {
+      if (tr_comm_memb_get_times_expired(memb)>0) {
+        /* Already expired once; flush. */
+        tr_debug("trps_sweep_ctable: flushing expired community membership (%.*s in %.*s, origin %.*s, expired %s).",
+                 tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
+                 tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
+                 tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf,
+                 timespec_to_str(tr_comm_memb_get_expiry(memb)));
+        tr_comm_table_remove_memb(trps->ctable, memb);
+        tr_comm_memb_free(memb);
+      } else {
+        /* This is the first expiration. Note this and reset the expiry time. */
+        tr_comm_memb_expire(memb);
+        trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb));
+        tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
+                 timespec_to_str(&sweep_time),
+                 timespec_to_str(tr_comm_memb_get_expiry(memb)),
+                 tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
+                 tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
+                 tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf);
+      }
+    }
+  }
+
+  /* get rid of any unreferenced realms, etc */
+  tr_comm_table_sweep(trps->ctable);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* add metrics */
+static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
+{
+  if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
+    return TRP_METRIC_INVALID;
+
+  if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
+    return TRP_METRIC_INFINITY;
+
+  if (trp_metric_is_finite(m1+m2))
+    return m1+m2;
+  else
+    return TRP_METRIC_INFINITY;
+}
+
+/* convert an rentry into a new trp update info record */
+static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
+{
+  TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
+  unsigned int linkcost=0;
+
+  if (rec!=NULL) {
+    if (trp_route_is_local(route))
+      linkcost=0;
+    else {
+      linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
+                                                              trp_route_get_peer(route)));
+    }
+
+    /* Note that we leave the next hop empty since the recipient fills that in.
+     * This is where we add the link cost (currently always 1) to the next peer. */
+    if ((trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
+       ||(trp_inforec_set_metric(rec,
+                                 trps_metric_add(trp_route_get_metric(route),
+                                                 linkcost)) != TRP_SUCCESS)
+       ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
+      tr_err("trps_route_to_inforec: error creating route update.");
+      talloc_free(rec);
+      rec=NULL;
+    }
+  }
+  return rec;
+}
+
+static TRP_UPD *trps_route_to_upd(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_UPD *upd=trp_upd_new(tmp_ctx);
+  TRP_INFOREC *rec=NULL;
+
+  if (upd==NULL) {
+    tr_err("trps_route_to_upd: could not create update message.");
+    goto cleanup;
+  }
+  trp_upd_set_realm(upd, trp_route_dup_realm(route));
+  if (trp_upd_get_realm(upd)==NULL) {
+    tr_err("trps_route_to_upd: could not copy realm.");
+    upd=NULL; /* it's still in tmp_ctx, so it will be freed */
+    goto cleanup;
+  }
+  trp_upd_set_comm(upd, trp_route_dup_comm(route));
+  if (trp_upd_get_comm(upd)==NULL) {
+    tr_err("trps_route_to_upd: could not copy comm.");
+    upd=NULL; /* it's still in tmp_ctx, so it will be freed */
+    goto cleanup;
+  }
+  rec=trps_route_to_inforec(tmp_ctx, trps, route);
+  if (rec==NULL) {
+    tr_err("trps_route_to_upd: could not create route info record for realm %.*s in comm %.*s.",
+           trp_route_get_realm(route)->len, trp_route_get_realm(route)->buf,
+           trp_route_get_comm(route)->len, trp_route_get_comm(route)->buf);
+    upd=NULL; /* it's till in tmp_ctx, so it will be freed */
+    goto cleanup;
+  }
+  trp_upd_add_inforec(upd, rec);
+
+  /* sucess */
+  talloc_steal(mem_ctx, upd);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return upd;
+}
+
 /* select the correct route to comm/realm to be announced to peer */
 static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
 {
@@ -864,14 +1381,12 @@ static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, T
   return route;
 }
 
-/* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
- * via n_update parameter. (The allocated space will generally be larger than required, see note in
- * the code.) If triggered is set, sends only triggered updates. */
-static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx,
+/* Add TRP_UPD msgs to the updates GPtrArray. Caller needs to arrange for these to be freed. */
+static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx,
+                                                 GPtrArray *updates,
                                                  TRPS_INSTANCE *trps,
                                                  TR_NAME *peer_gssname,
-                                                 int triggered,
-                                                 size_t *n_update)
+                                                 int triggered)
 {
   size_t n_comm=0;
   TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
@@ -879,88 +1394,230 @@ static TRP_ROUTE **trps_select_updates_for_peer(TALLOC_CTX *memctx,
   size_t n_realm=0;
   size_t ii=0, jj=0;
   TRP_ROUTE *best=NULL;
-  TRP_ROUTE **result=NULL;
-  size_t n_used=0;
-
-  /* Need to allocate space for the results. For simplicity, we just allocate a block
-   * with space for every route table entry to be returned. This is guaranteed to be large
-   * enough. If the routing table gets very large, this may be wasteful, but that seems
-   * unlikely to be significant in the near future. */
-  result=talloc_array(memctx, TRP_ROUTE *, trp_rtable_size(trps->rtable));
-  if (result==NULL) {
-    talloc_free(comm);
-    *n_update=0;
-    return NULL;
-  }
-  
+  TRP_UPD *upd=NULL;
+
+  if (updates==NULL)
+    return TRP_BADARG;
+
   for (ii=0; ii<n_comm; ii++) {
     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
     for (jj=0; jj<n_realm; jj++) {
       best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_gssname);
       /* If we found a route, add it to the list. If triggered!=0, then only
        * add triggered routes. */
-      if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best)))
-        result[n_used++]=best;
+      if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best))) {
+        upd=trps_route_to_upd(mem_ctx, trps, best);
+        if (upd==NULL) {
+          tr_err("trps_select_route_updates_for_peer: unable to create update message.");
+          continue;
+        }
+        g_ptr_array_add(updates, upd);
+      }
     }
+    
     if (realm!=NULL)
       talloc_free(realm);
     realm=NULL;
     n_realm=0;
   }
+
   if (comm!=NULL)
     talloc_free(comm);
+  
+  return TRP_SUCCESS;
+}
+
+static TRP_INFOREC *trps_memb_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_COMM_MEMB *memb)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_INFOREC *rec=NULL;
+  TR_COMM *comm=NULL;
+
+  if (memb==NULL)
+    goto cleanup;
 
-  *n_update=n_used;
-  return result;
+  comm=tr_comm_memb_get_comm(memb);
+  rec=trp_inforec_new(tmp_ctx, TRP_INFOREC_TYPE_COMMUNITY);
+  if (rec==NULL)
+    goto cleanup;
+  
+  if (TRP_SUCCESS!=trp_inforec_set_comm_type(rec, tr_comm_get_type(comm))) {
+    rec=NULL;
+    goto cleanup;
+  }
+  
+  if (TRP_SUCCESS!=trp_inforec_set_role(rec, tr_comm_memb_get_role(memb))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  if ((NULL!=tr_comm_get_apcs(comm)) &&
+      ( (TRP_SUCCESS!=trp_inforec_set_apcs(rec,
+                                           tr_apc_dup(rec, tr_comm_get_apcs(comm)))) ||
+        (NULL==trp_inforec_get_apcs(rec)))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  if ((NULL!=tr_comm_get_owner_realm(comm)) &&
+      ( (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_dup_name(tr_comm_get_owner_realm(comm)))) ||
+        (NULL==trp_inforec_get_owner_realm(rec)))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  if ((NULL!=tr_comm_get_owner_contact(comm)) &&
+      ( (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_dup_name(tr_comm_get_owner_contact(comm)))) ||
+        (NULL==trp_inforec_get_owner_contact(rec)))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  if ((NULL!=tr_comm_memb_get_provenance(memb)) &&
+      (TRP_SUCCESS!=trp_inforec_set_provenance(rec, tr_comm_memb_get_provenance(memb)))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  if (TRP_SUCCESS!=trp_inforec_set_interval(rec, trps_get_update_interval(trps))) {
+    rec=NULL;
+    goto cleanup;
+  }
+
+  /* success! */
+  talloc_steal(mem_ctx, rec);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rec;
 }
 
-/* add metrics */
-static unsigned int trps_metric_add(unsigned int m1, unsigned int m2)
+/* construct an update with all the inforecs for comm/realm/role to be sent to peer */
+static TRP_UPD *trps_comm_update(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, TR_COMM *comm, TR_REALM *realm)
 {
-  if (trp_metric_is_invalid(m1) || trp_metric_is_invalid(m2))
-    return TRP_METRIC_INVALID;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_UPD *upd=trp_upd_new(tmp_ctx);
+  TRP_INFOREC *rec=NULL;
+  TR_COMM_ITER *iter=NULL;
+  TR_COMM_MEMB *memb=NULL;
 
-  if (trp_metric_is_infinite(m1) || trp_metric_is_infinite(m2))
-    return TRP_METRIC_INFINITY;
+  if (upd==NULL)
+    goto cleanup;
+  
+  trp_upd_set_comm(upd, tr_comm_dup_id(comm));
+  trp_upd_set_realm(upd, tr_realm_dup_id(realm));
+  /* leave peer empty */
 
-  if (trp_metric_is_finite(m1+m2))
-    return m1+m2;
+  iter=tr_comm_iter_new(tmp_ctx);
+  if (iter==NULL) {
+    tr_err("trps_comm_update: unable to allocate iterator.");
+    upd=NULL;
+    goto cleanup;
+  }
+  
+  /* now add inforecs */
+  switch (realm->role) {
+  case TR_ROLE_IDP:
+    memb=tr_comm_table_find_idp_memb(trps->ctable,
+                                     tr_realm_get_id(realm),
+                                     tr_comm_get_id(comm));
+    break;
+  case TR_ROLE_RP:
+    memb=tr_comm_table_find_rp_memb(trps->ctable,
+                                    tr_realm_get_id(realm),
+                                    tr_comm_get_id(comm));
+    break;
+  default:
+    break;
+  }
+  if (memb!=NULL) {
+    for (memb=tr_comm_memb_iter_first(iter, memb);
+         memb!=NULL;
+         memb=tr_comm_memb_iter_next(iter)) {
+      rec=trps_memb_to_inforec(tmp_ctx, trps, memb);
+      if (rec==NULL) {
+        tr_err("trps_comm_update: unable to allocate inforec.");
+        upd=NULL;
+        goto cleanup;
+      }
+      trp_upd_add_inforec(upd, rec);
+    }
+  }
+
+  if (trp_upd_get_inforec(upd)==NULL)
+    upd=NULL; /* no inforecs, no reason to send the update */
   else
-    return TRP_METRIC_INFINITY;
+    talloc_steal(mem_ctx, upd); /* success! */
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return upd;
 }
 
-/* convert an rentry into a new trp update info record */
-static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TRP_ROUTE *route)
+/* Find all community updates to send to a peer and add these as TR_UPD records
+ * to the updates GPtrArray. */
+static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx,
+                                                GPtrArray *updates,
+                                                TRPS_INSTANCE *trps,
+                                                TR_NAME *peer_gssname,
+                                                int triggered)
 {
-  TRP_INFOREC *rec=trp_inforec_new(mem_ctx, TRP_INFOREC_TYPE_ROUTE);
-  unsigned int linkcost=0;
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM_ITER *comm_iter=NULL;
+  TR_COMM *comm=NULL;
+  TR_COMM_ITER *realm_iter=NULL;
+  TR_REALM *realm=NULL;
+  TRP_UPD *upd=NULL;
+  TRP_RC rc=TRP_ERROR;
 
-  if (rec!=NULL) {
-    if (trp_route_is_local(route))
-      linkcost=0;
-    else {
-      linkcost=trp_peer_get_linkcost(trps_get_peer_by_gssname(trps,
-                                                              trp_route_get_peer(route)));
-    }
+  /* currently do not send any communities on triggered updates */
+  if (triggered) {
+    rc=TRP_SUCCESS;
+    goto cleanup;
+  }
 
-    /* Note that we leave the next hop empty since the recipient fills that in.
-     * This is where we add the link cost (currently always 1) to the next peer. */
-    if ((trp_inforec_set_comm(rec, trp_route_dup_comm(route)) != TRP_SUCCESS)
-       ||(trp_inforec_set_realm(rec, trp_route_dup_realm(route)) != TRP_SUCCESS)
-       ||(trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
-       ||(trp_inforec_set_metric(rec,
-                                 trps_metric_add(trp_route_get_metric(route),
-                                                 linkcost)) != TRP_SUCCESS)
-       ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
-      tr_err("trps_route_to_inforec: error creating route update.");
-      talloc_free(rec);
-      rec=NULL;
+  comm_iter=tr_comm_iter_new(tmp_ctx);
+  realm_iter=tr_comm_iter_new(tmp_ctx);
+  if ((comm_iter==NULL) || (realm_iter==NULL)) {
+    tr_err("trps_select_comm_updates_for_peer: unable to allocate iterator.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  /* do every community */
+  for (comm=tr_comm_table_iter_first(comm_iter, trps->ctable);
+       comm!=NULL;
+       comm=tr_comm_table_iter_next(comm_iter)) {
+    /* do every realm in this community */
+    tr_debug("trps_select_comm_updates_for_peer: looking through community %.*s",
+             tr_comm_get_id(comm)->len,
+             tr_comm_get_id(comm)->buf);
+    for (realm=tr_realm_iter_first(realm_iter, trps->ctable, tr_comm_get_id(comm));
+         realm!=NULL;
+         realm=tr_realm_iter_next(realm_iter)) {
+      /* get the update for this comm/realm */
+      tr_debug("trps_select_comm_updates_for_peer: adding realm %.*s",
+               tr_realm_get_id(realm)->len,
+               tr_realm_get_id(realm)->buf);
+      upd=trps_comm_update(mem_ctx, trps, peer_gssname, comm, realm);
+      if (upd!=NULL)
+        g_ptr_array_add(updates, upd);
     }
   }
-  return rec;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
 }
 
-/* all routes to a single peer, unless comm/realm are specified (both or neither must be NULL) */
+
+/* helper for trps_update_one_peer. Frees the TRP_UPD pointed to by a GPtrArray element */
+static void trps_trp_upd_destroy(gpointer data)
+{
+  trp_upd_free((TRP_UPD *)data);
+}
+
+/* all routes/communities to a single peer, unless comm/realm are specified (both or neither must be NULL) */
 static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
                                    TRP_PEER *peer,
                                    TRP_UPDATE_TYPE update_type,
@@ -970,107 +1627,115 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_MSG msg; /* not a pointer! */
   TRP_UPD *upd=NULL;
-  TRP_ROUTE **update_list=NULL;
-  TRP_INFOREC *rec=NULL;
-  size_t n_updates=0, ii=0;
+  TRP_ROUTE *route=NULL;
+  size_t ii=0;
   char *encoded=NULL;
   TRP_RC rc=TRP_ERROR;
   TR_NAME *peer_label=trp_peer_get_label(peer);
+  GPtrArray *updates=g_ptr_array_new_with_free_func(trps_trp_upd_destroy);
+
+  if (updates==NULL) {
+    tr_err("trps_update_one_peer: unable to allocate updates array.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
 
   switch (update_type) {
   case TRP_UPDATE_TRIGGERED:
-    tr_debug("trps_update_one_peer: preparing triggered route update for %.*s",
+    tr_debug("trps_update_one_peer: preparing triggered update for %.*s",
              peer_label->len, peer_label->buf);
     break;
   case TRP_UPDATE_SCHEDULED:
-    tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s",
+    tr_debug("trps_update_one_peer: preparing scheduled update for %.*s",
              peer_label->len, peer_label->buf);
     break;
   case TRP_UPDATE_REQUESTED:
-    tr_debug("trps_update_one_peer: preparing requested route update for %.*s",
+    tr_debug("trps_update_one_peer: preparing requested update for %.*s",
              peer_label->len, peer_label->buf);
+    break;
+  default:
+    tr_err("trps_update_one_peer: invalid update type requested.");
+    rc=TRP_BADARG;
+    goto cleanup;
   }
 
-  /* do not fill in peer, recipient does that */
+  /* First, gather route updates. */
+  tr_debug("trps_update_one_peer: selecting route updates for %.*s.", peer_label->len, peer_label->buf);
   if ((comm==NULL) && (realm==NULL)) {
     /* do all realms */
-    update_list=trps_select_updates_for_peer(tmp_ctx,
-                                             trps,
-                                             peer_label,
-                                             update_type==TRP_UPDATE_TRIGGERED,
-                                            &n_updates);
+    rc=trps_select_route_updates_for_peer(tmp_ctx,
+                                          updates,
+                                          trps,
+                                          peer_label,
+                                          update_type==TRP_UPDATE_TRIGGERED);
   } else if ((comm!=NULL) && (realm!=NULL)) {
     /* a single community/realm was requested */
-    update_list=talloc(tmp_ctx, TRP_ROUTE *);
-    if (update_list==NULL) {
-      tr_err("trps_update_one_peer: could not allocate update_list.");
-      rc=TRP_NOMEM;
-      goto cleanup;
-    }
-    *update_list=trps_select_realm_update(trps, comm, realm, peer_label);
-    if (*update_list==NULL) {
+    route=trps_select_realm_update(trps, comm, realm, peer_label);
+    if (route==NULL) {
       /* we have no actual update to send back, MUST send a retraction */
       tr_debug("trps_update_one_peer: community/realm without route requested, sending mandatory retraction.");
-      *update_list=trp_route_new(update_list);
-      trp_route_set_comm(*update_list, tr_dup_name(comm));
-      trp_route_set_realm(*update_list, tr_dup_name(realm));
-      trp_route_set_peer(*update_list, tr_new_name(""));
-      trp_route_set_metric(*update_list, TRP_METRIC_INFINITY);
-      trp_route_set_trust_router(*update_list, tr_new_name(""));
-      trp_route_set_next_hop(*update_list, tr_new_name(""));
+      route=trp_route_new(tmp_ctx);
+      trp_route_set_comm(route, tr_dup_name(comm));
+      trp_route_set_realm(route, tr_dup_name(realm));
+      trp_route_set_peer(route, tr_new_name(""));
+      trp_route_set_metric(route, TRP_METRIC_INFINITY);
+      trp_route_set_trust_router(route, tr_new_name(""));
+      trp_route_set_next_hop(route, tr_new_name(""));
+    }
+    upd=trps_route_to_upd(tmp_ctx, trps, route);
+    if (upd==NULL) {
+      tr_err("trps_update_one_peer: unable to allocate route update.");
+      rc=TRP_NOMEM;
+      goto cleanup;
     }
-    n_updates=1;
+    g_ptr_array_add(updates, upd);
   } else {
-    tr_err("trps_update_one_peer: error: only comm or realm was specified.");
+    tr_err("trps_update_one_peer: error: only comm or realm was specified. Need both or neither.");
     rc=TRP_ERROR;
     goto cleanup;
   }
-  if ((n_updates>0) && (update_list!=NULL)) {
-    tr_debug("trps_update_one_peer: sending %u update records.", (unsigned int)n_updates);
-    upd=trp_upd_new(tmp_ctx);
 
-    for (ii=0; ii<n_updates; ii++) {
-      rec=trps_route_to_inforec(tmp_ctx, trps, update_list[ii]);
-      if (rec==NULL) {
-        tr_err("trps_update_one_peer: could not create all update records.");
+  /* Second, gather community updates */
+  tr_debug("trps_update_one_peer: selecting community updates for %.*s.", peer_label->len, peer_label->buf);
+  rc=trps_select_comm_updates_for_peer(tmp_ctx, updates, trps, peer_label, update_type==TRP_UPDATE_TRIGGERED);
+
+  /* see if we have anything to send */
+  if (updates->len<=0)
+    tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf);
+  else {
+    tr_debug("trps_update_one_peer: sending %d update messages.", updates->len);
+    for (ii=0; ii<updates->len; ii++) {
+      upd=(TRP_UPD *)g_ptr_array_index(updates, ii);
+      /* now encode the update message */
+      tr_msg_set_trp_upd(&msg, upd);
+      encoded=tr_msg_encode(&msg);
+      if (encoded==NULL) {
+        tr_err("trps_update_one_peer: error encoding update.");
         rc=TRP_ERROR;
         goto cleanup;
       }
-      trp_upd_add_inforec(upd, rec);
-    }
-    talloc_free(update_list);
-    update_list=NULL;
-
-    /* now encode the update message */
-    tr_msg_set_trp_upd(&msg, upd);
-    encoded=tr_msg_encode(&msg);
-    if (encoded==NULL) {
-      tr_err("trps_update_one_peer: error encoding update.");
-      rc=TRP_ERROR;
-      goto cleanup;
-    }
 
-    tr_debug("trps_update_one_peer: adding message to queue.");
-    if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
-      tr_err("trps_update_one_peer: error queueing update.");
-    else
-      tr_debug("trps_update_one_peer: update queued successfully.");
+      tr_debug("trps_update_one_peer: adding message to queue.");
+      if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS)
+        tr_err("trps_update_one_peer: error queueing update.");
+      else
+        tr_debug("trps_update_one_peer: update queued successfully.");
 
-    tr_msg_free_encoded(encoded);
-    encoded=NULL;
-    trp_upd_free(upd);
-    upd=NULL;
-  } else if (n_updates==0)
-    tr_debug("trps_update_one_peer: no updates for %.*s", peer_label->len, peer_label->buf);
+      tr_msg_free_encoded(encoded);
+      encoded=NULL;
+    }
+  }
 
   rc=TRP_SUCCESS;
 
 cleanup:
+  if (updates!=NULL)
+    g_ptr_array_free(updates, TRUE); /* frees any TRP_UPD records */
   talloc_free(tmp_ctx);
   return rc;
 }
 
-/* all routes to all peers */
+/* all routes/communities to all peers */
 TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
@@ -1088,7 +1753,7 @@ TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
   }
 
   for (peer=trp_ptable_iter_first(iter, trps->ptable);
-       peer!=NULL && rc==TRP_SUCCESS;
+       (peer!=NULL) && (rc==TRP_SUCCESS);
        peer=trp_ptable_iter_next(iter))
   {
     if (!trps_peer_connected(trps, peer)) {
index a71aa32..340e628 100644 (file)
@@ -1,6 +1,6 @@
 %global optflags %{optflags} -Wno-parentheses
 Name:           trust_router
-Version:        1.5.2
+Version:        2.1.1
 Release:        1%{?dist}
 Summary:        Moonshot Trust Router
 
@@ -12,7 +12,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildRequires:  krb5-devel, glib2-devel
 BuildRequires: jansson-devel >= 2.4
-BuildRequires: sqlite-devel, openssl-devel, libtalloc-devel
+BuildRequires: sqlite-devel, openssl-devel, libtalloc-devel, libevent-devel
 %{?el7:BuildRequires: systemd}
 Requires:       moonshot-gss-eap >= 0.9.3, sqlite
 
@@ -43,7 +43,7 @@ packages that wish trust_router functionality.
 
 %prep
 %setup -q
-
+autoreconf -f -i
 
 %build
 %configure --disable-static
@@ -57,16 +57,16 @@ find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';'
 
 # Install config files
 install -D -m 755 redhat/init $RPM_BUILD_ROOT/%{_initrddir}/trust_router
-install -D -m 640 redhat/trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/trusts.cfg
-install -D -m 640 redhat/default-main.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/main.cfg
-install -D -m 640 redhat/tr-test-main.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/main.cfg
+install -D -m 640 redhat/organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/organizations.cfg
+install -D -m 640 redhat/default-internal.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/internal.cfg
+install -D -m 640 redhat/tr-test-internal.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/internal.cfg
 install -D -m 640 redhat/sysconfig $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/trust_router
 install -D -m 640 redhat/sysconfig.tids $RPM_BUILD_ROOT/%{_sysconfdir}/sysconfig/tids
 install -D -m 755 redhat/tids.init $RPM_BUILD_ROOT/%{_initrddir}/tids
 
 # Link shared config
-ln -s ../../trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/trusts.cfg
-ln -s ../../trusts.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/trusts.cfg
+ln -s ../../organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/default/organizations.cfg
+ln -s ../../organizations.cfg $RPM_BUILD_ROOT/%{_sysconfdir}/trust_router/conf.d/tr-test/organizations.cfg
 
 # Install wrapper scripts
 install -D -m 755 redhat/tidc-wrapper $RPM_BUILD_ROOT/%{_bindir}/tidc-wrapper
@@ -113,6 +113,12 @@ chmod 770 /var/log/trust_router
 %files
 %defattr(-,root,root,-)
 %doc README
+#%{_bindir}/tidc
+#%{_bindir}/tidc-wrapper
+#%{_bindir}/tids
+#%{_bindir}/tids-wrapper
+#%{_bindir}/trust_router
+#%{_bindir}/trust_router-wrapper
 %{_bindir}/*
 %{_datadir}/trust_router/schema.sql
 
@@ -129,11 +135,11 @@ chmod 770 /var/log/trust_router
 %dir %attr(755,root,trustrouter) %{_sysconfdir}/trust_router/conf.d/default
 %dir %attr(755,root,trustrouter) %{_sysconfdir}/trust_router/conf.d/tr-test
 
-%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/trusts.cfg
-%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/main.cfg
-%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/main.cfg
-%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/trusts.cfg
-%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/trusts.cfg
+%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/organizations.cfg
+%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/internal.cfg
+%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/internal.cfg
+%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/default/organizations.cfg
+%attr(640,root,trustrouter) %config(noreplace) %{_sysconfdir}/trust_router/conf.d/tr-test/organizations.cfg
 
 %files libs
 %defattr(-,root,root,-)