Fix community table sweep / removal. Trust router now stable.
authorJennifer Richards <jennifer@painless-security.com>
Mon, 7 Nov 2016 17:22:21 +0000 (12:22 -0500)
committerJennifer Richards <jennifer@painless-security.com>
Mon, 7 Nov 2016 17:22:21 +0000 (12:22 -0500)
18 files changed:
Makefile.am
common/tests/cfg_test.c [moved from common/cfg_test/cfg_test.c with 100% 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 [moved from common/cfg_test/idp.cfg with 100% similarity]
common/tests/mq_test.c [moved from common/mq_test/mq_test.c with 100% similarity]
common/tests/thread_test.c [moved from common/mq_test/thread_test.c with 100% similarity]
common/tr_comm.c
common/tr_config.c
common/tr_idp.c
common/tr_msg.c
common/tr_rp.c
include/tr_comm.h
include/tr_idp.h
include/tr_rp.h
include/trp_internal.h
tr/tr_trp.c
trp/trps.c

index 664a5d6..ebd0e8f 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 
@@ -112,29 +112,35 @@ 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 \
+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_mq_test_mq_test_CFLAGS = -pthread
-common_mq_test_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+common_tests_mq_test_CFLAGS = -pthread
+common_tests_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
 
-common_cfg_test_cfg_test_SOURCES = common/cfg_test/cfg_test.c \
+common_tests_cfg_test_SOURCES = common/tests/cfg_test.c \
 $(trp_srcs)
 
-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_tests_cfg_test_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+common_tests_cfg_test_CFLAGS = -pthread
+common_tests_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_mq_test_thread_test_CFLAGS = -pthread
-common_mq_test_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+common_tests_thread_test_CFLAGS = -pthread
+common_tests_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+
+common_tests_commtest_SOURCES = common/tests/commtest.c \
+$(trp_srcs)
+common_tests_commtest_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+common_tests_commtest_CFLAGS = -pthread
+common_test_commtest_LDFLAGS = $(AM_LDFLAGS) -ltalloc
 
 pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \
        include/tr_debug.h \
diff --git a/common/tests/commtest.c b/common/tests/commtest.c
new file mode 100644 (file)
index 0000000..9c9c008
--- /dev/null
@@ -0,0 +1,837 @@
+#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 %d.\n", 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==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);
+}
+
+
+/**********************************************************************/
+/* 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;
+}
similarity index 100%
rename from common/cfg_test/idp.cfg
rename to common/tests/idp.cfg
index 52d93f1..bd5c0bf 100644 (file)
@@ -32,6 +32,7 @@
  *
  */
 
+#include <stdio.h>
 #include <jansson.h>
 #include <talloc.h>
 #include <sys/time.h>
 #include <tr_debug.h>
 
 
-/* static prototypes */
-static TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb);
-
-
 static int tr_comm_destructor(void *obj)
 {
   TR_COMM *comm=talloc_get_type_abort(obj, TR_COMM);
@@ -171,20 +168,59 @@ unsigned int tr_comm_get_refcount(TR_COMM *comm)
   return comm->refcount;
 }
 
-/* add to the table if it's a new membership or has a shorter
- * provenance list than our existing membership */
+/* 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 {
-    /* Had an entry. Replace if we have shorter provenance. */
-    if (tr_comm_memb_provenance_len(newmemb) < tr_comm_memb_provenance_len(existing)) {
+    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);
-    } 
+    }
   }
 }
 
@@ -192,6 +228,7 @@ static void tr_comm_add_if_shorter(TR_COMM_TABLE *ctab, TR_COMM_MEMB *existing,
 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)
 {
@@ -207,14 +244,15 @@ void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab,
 
   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);
 }
 
@@ -222,6 +260,7 @@ void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab,
 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)
 {
@@ -237,8 +276,8 @@ void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab,
 
   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),
@@ -293,7 +332,7 @@ static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
      * the list head was in. */
     comms=comms->next;
     if (comms!=NULL) {
-      talloc_steal(list_ctx, comms->next);
+      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);
@@ -301,13 +340,47 @@ static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
   } else {
     /* not removing the head; no need to play with contexts */
     for (this=comms; this->next!=NULL; this=this->next) {
-      if (this->next==remove)
+      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_COMM *comm=NULL;
+  TR_COMM *old_next=NULL;
+
+  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);
+  }
+
+  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);
+    }
+  }
+
+  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);
@@ -377,6 +450,7 @@ TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx)
   if (iter!=NULL) {
     iter->cur_comm=NULL;
     iter->cur_memb=NULL;
+    iter->cur_orig_head=NULL;
     iter->match=NULL;
     iter->realm=NULL;
   }
@@ -670,6 +744,7 @@ const char *tr_comm_type_to_str(TR_COMM_TYPE type)
   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;
@@ -683,6 +758,32 @@ TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
   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)
@@ -723,6 +824,7 @@ TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
     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);
@@ -739,6 +841,19 @@ 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)
@@ -826,6 +941,8 @@ json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb)
 
 void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
 {
+  const char *s=NULL;
+
   if (memb->provenance)
     json_decref(memb->provenance);
 
@@ -835,7 +952,11 @@ void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
 
     /* next line sets origin to NULL if provenance is empty because jansson
      * routines return NULL on error */
-    memb->origin=tr_new_name(json_string_value(json_array_get(prov, 0)));
+    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);
   }
@@ -901,16 +1022,33 @@ int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
             &&(curtime->tv_nsec >= memb->expiry->tv_nsec)));
 }
 
-void tr_comm_set_triggered(TR_COMM_MEMB *memb, int trig)
+void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig)
 {
   memb->triggered=trig;
 }
 
-int tr_comm_is_triggered(TR_COMM_MEMB *memb)
+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)
 {
@@ -943,7 +1081,10 @@ void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
 {
   TR_COMM_MEMB *cur=NULL;
 
-  /* TODO: validate the member (must have valid comm and realm) */
+  /* 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) {
@@ -997,7 +1138,7 @@ void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
   /* see if it's the first member */
   if (ctab->memberships==memb) {
     if (memb->origin_next!=NULL) {
-      memb->origin_next->next=ctab->memberships->next;
+      memb->origin_next->next=memb->next;
       ctab->memberships=memb->origin_next;
     } else
       ctab->memberships=memb->next;
@@ -1006,10 +1147,10 @@ void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
   }
 
   /* see if it's in first member's origin list */
-  for (orig_cur=ctab->memberships->origin_next;
-       orig_cur!=NULL;
-       orig_cur=ctab->memberships->origin_next) {
-    if (orig_cur==memb) {
+  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;
     }
@@ -1019,26 +1160,34 @@ void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
   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) {
+      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;
-      } else
-        cur->next=memb->next; /* no origin list, just drop memb */
+      }
       return;
     } else {
       /* it was not on the main list, walk the origin list */
-      for (orig_cur=cur; orig_cur->next!=NULL; orig_cur=orig_cur->next) {
-        if (orig_cur->next==memb) {
-          orig_cur->next=memb->next;
+      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 */
+    }
+  }
 }
 
-static TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
+TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
 {
   if (memb->rp!=NULL)
     return tr_rp_realm_get_id(memb->rp);
@@ -1169,6 +1318,8 @@ TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *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)
@@ -1176,6 +1327,41 @@ 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)
 {
@@ -1188,6 +1374,14 @@ size_t tr_comm_table_size(TR_COMM_TABLE *ctab)
   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)
 {
@@ -1210,3 +1404,24 @@ TR_REALM_ROLE tr_realm_role_from_str(const char *s)
   return TR_ROLE_UNKNOWN;
 }
 
+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)\n",
+            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);
+    for (p2=p1->origin_next; p2!=NULL; p2=p2->origin_next) {
+      fprintf(f, "  %s (%p)\n",
+              (tr_comm_memb_get_origin(p2)==NULL)?"null origin":(tr_comm_memb_get_origin(p2)->buf),
+              p2);
+    }
+    fprintf(f, "\n");
+  }
+}
index b07c8dd..bbbda1a 100644 (file)
@@ -1635,7 +1635,7 @@ static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR
       *rc=TR_CFG_ERROR;
       return;
     }
-    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, NULL, NULL); /* no provenance, never expires */
+    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */
   }
 
   *rc=TR_CFG_SUCCESS;
@@ -1698,8 +1698,9 @@ static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_C
       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, NULL, NULL);
+    tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL);
   }
 }
 
index 3019ef6..bcbbd8d 100644 (file)
@@ -198,6 +198,37 @@ TR_IDP_REALM *tr_idp_realm_add_func(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;
@@ -308,3 +339,35 @@ 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 48965e5..ac57ebb 100644 (file)
@@ -807,29 +807,37 @@ static TRP_RC tr_msg_decode_trp_inforec_comm(json_t *jrecord, TRP_INFOREC *rec)
   }
   trp_inforec_set_apcs(rec, apcs);
 
-  rc=tr_msg_get_json_string(jrecord, "owner_realm", &s, tmp_ctx);
-  if (rc != TRP_SUCCESS)
-    goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_owner_realm(rec, tr_new_name(s))) {
-    rc=TRP_ERROR;
+  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;
+
+  trp_inforec_set_provenance(rec, json_object_get(jrecord, "provenance"));
+
+  /* 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;
+    }
   }
-  talloc_free(s); s=NULL;
 
   rc=tr_msg_get_json_string(jrecord, "owner_contact", &s, tmp_ctx);
-  if (rc != TRP_SUCCESS)
-    goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_owner_contact(rec, tr_new_name(s))) {
-    rc=TRP_ERROR;
-    goto cleanup;
+  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;
+    }
   }
-  talloc_free(s); s=NULL;
-
-  trp_inforec_set_provenance(rec, json_object_get(jrecord, "provenance"));
-
-  rc=tr_msg_get_json_integer(jrecord, "interval", &num);
-  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num)))
-    goto cleanup;
 
 cleanup:
   talloc_free(tmp_ctx);
index 9a056c0..94a6360 100644 (file)
@@ -193,6 +193,37 @@ TR_RP_REALM *tr_rp_realm_add_func(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++;
@@ -204,6 +235,37 @@ void tr_rp_realm_decref(TR_RP_REALM *realm)
     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)
index 4d47eaf..5f4b4ae 100644 (file)
@@ -35,6 +35,7 @@
 #ifndef TR_COMM_H
 #define TR_COMM_H
 
+#include <stdio.h>
 #include <talloc.h>
 #include <time.h>
 
@@ -72,6 +73,7 @@ typedef struct tr_comm_memb {
   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;
 
@@ -99,6 +101,7 @@ typedef struct tr_realm {
 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;
@@ -109,8 +112,15 @@ 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);
@@ -121,14 +131,17 @@ TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *i
 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);
@@ -142,8 +155,11 @@ 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_set_triggered(TR_COMM_MEMB *memb, int trig);
-int tr_comm_is_triggered(TR_COMM_MEMB *memb);
+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);
@@ -160,8 +176,8 @@ 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, json_t *provenance, struct timespec *expiry);
-void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_RP_REALM *realm, json_t *provenance, struct timespec *expiry);
+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);
@@ -198,6 +214,10 @@ TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter);
 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);
index aa407b8..361439c 100644 (file)
@@ -75,6 +75,10 @@ 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);
index 1fa48d7..7e8f8f0 100644 (file)
@@ -71,6 +71,10 @@ 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);
 
index ac7e3ee..eb89692 100644 (file)
@@ -245,6 +245,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);
index b2b570f..a3b32b4 100644 (file)
@@ -328,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));
index e7703e3..9b1bfa4 100644 (file)
@@ -40,6 +40,7 @@
 #include <glib.h>
 
 #include <gsscon.h>
+#include <tr_comm.h>
 #include <tr_apc.h>
 #include <tr_rp.h>
 #include <trust_router/tr_name.h>
@@ -906,10 +907,10 @@ static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_IN
            * the next table sweep if it does not get any realms before that happens */
           goto cleanup;
         }
-        tr_rp_realm_add(trps->ctable->rp_realms, rp_realm);
+        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_provenance(rec), &expiry);
+      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,
@@ -927,10 +928,10 @@ static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_IN
            * the next table sweep if it does not get any realms before that happens */
           goto cleanup;
         }
-        tr_idp_realm_add(trps->ctable->idp_realms, idp_realm);
+        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_provenance(rec), &expiry);
+      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,
@@ -1140,6 +1141,88 @@ 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(CLOCK_REALTIME, &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, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
+                 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)
 {
@@ -1347,7 +1430,7 @@ static TRP_INFOREC *trps_memb_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trp
     goto cleanup;
   }
 
-  if (TRP_SUCCESS!=trp_inforec_set_interval(rec, tr_comm_memb_get_interval(memb))) {
+  if (TRP_SUCCESS!=trp_inforec_set_interval(rec, trps_get_update_interval(trps))) {
     rec=NULL;
     goto cleanup;
   }