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

55 files changed:
Makefile.am
common/mq_test/mq_test.c [new file with mode: 0644]
common/mq_test/thread_test.c [new file with mode: 0644]
common/tr_comm.c
common/tr_config.c
common/tr_debug.c
common/tr_idp.c
common/tr_mq.c [new file with mode: 0644]
common/tr_msg.c
common/tr_name.c
common/tr_rp.c
configure.ac
gsscon/gsscon_active.c
gsscon/gsscon_common.c
gsscon/gsscon_passive.c
include/tid_internal.h
include/tr.h
include/tr_apc.h
include/tr_cfgwatch.h [new file with mode: 0644]
include/tr_comm.h
include/tr_config.h
include/tr_debug.h
include/tr_event.h [new file with mode: 0644]
include/tr_idp.h
include/tr_mq.h [new file with mode: 0644]
include/tr_msg.h
include/tr_rp.h
include/tr_tid.h [new file with mode: 0644]
include/tr_trp.h [new file with mode: 0644]
include/trp_internal.h [new file with mode: 0644]
include/trp_ptable.h [new file with mode: 0644]
include/trp_rtable.h [new file with mode: 0644]
include/trust_router/tid.h
include/trust_router/trp.h [new file with mode: 0644]
tid/example/tids_main.c
tid/tid_req.c
tid/tid_resp.c
tid/tids.c
tr/tr.c
tr/tr_cfgwatch.c [new file with mode: 0644]
tr/tr_event.c [new file with mode: 0644]
tr/tr_main.c
tr/tr_tid.c [new file with mode: 0644]
tr/tr_trp.c [new file with mode: 0644]
tr/trpc_main.c [new file with mode: 0644]
trp/msgtst.c [new file with mode: 0644]
trp/test/ptbl_test.c [new file with mode: 0644]
trp/test/rtbl_test.c [new file with mode: 0644]
trp/trp_conn.c [new file with mode: 0644]
trp/trp_ptable.c [new file with mode: 0644]
trp/trp_req.c [new file with mode: 0644]
trp/trp_rtable.c [new file with mode: 0644]
trp/trp_upd.c [new file with mode: 0644]
trp/trpc.c [new file with mode: 0644]
trp/trps.c [new file with mode: 0644]

index 63d4e95..add053d 100644 (file)
@@ -1,6 +1,6 @@
 DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
-bin_PROGRAMS= tr/trust_router tid/example/tidc tid/example/tids common/dh_test/tr_dh_test
+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
 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS)
 AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS)
 SUBDIRS = gsscon 
@@ -12,43 +12,133 @@ common_srcs = common/tr_name.c \
         common/tr_debug.c \
        common/tr_util.c
 
+tid_srcs = trp/trpc.c \
+tid/tid_resp.c \
+tid/tid_req.c \
+tid/tids.c \
+tid/tidc.c
+
+trp_srcs = trp/trp_upd.c \
+trp/trp_req.c \
+trp/trp_conn.c \
+trp/trps.c \
+trp/trp_ptable.c \
+trp/trp_rtable.c
+
 check_PROGRAMS = common/t_constraint
 TESTS = common/t_constraint
 
-lib_LTLIBRARIES = libtr_tid.la
+#lib_LTLIBRARIES = libtr_tid.la libtr_trp.la
 
 common_t_constraint_SOURCES = common/t_constraint.c
 common_t_constraint_CPPFLAGS = $(AM_CPPFLAGS) -DTESTS=\"$(srcdir)/common/tests.json\"
 common_t_constraint_LDADD = gsscon/libgsscon.la libtr_tid.la
 
-tr_trust_router_SOURCES = tr/tr_main.c \
+tr_trust_router_SOURCES = $(common_srcs) \
+tr/tr_main.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_mq.c \
+tr/tr.c \
+tr/tr_event.c \
+tr/tr_cfgwatch.c \
+tr/tr_tid.c \
+tr/tr_trp.c \
+$(trp_srcs) \
+$(tid_srcs)
+
+tr_trust_router_CFLAGS = $(AM_CFLAGS) -pthread -v -da -Q
+tr_trust_router_LDFLAGS = $(AM_LDFLAGS) -levent_pthreads
+#tr_trust_router_LDADD = gsscon/libgsscon.la libtr_tid.la libtr_trp.la $(GLIB_LIBS)
+tr_trust_router_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+
+tr_trpc_SOURCES = $(common_srcs) \
+tr/trpc_main.c \
+common/tr_rp.c \
+common/tr_mq.c \
+tr/tr_trp.c \
+$(trp_srcs) \
+$(tid_srcs)
+
+#tr_trpc_LDADD = gsscon/libgsscon.la libtr_tid.la libtr_trp.la $(GLIB_LIBS)
+tr_trpc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+
+trp_msgtst_SOURCES = trp/msgtst.c \
+trp/trp_upd.c \
+trp/trp_req.c \
+tid/tid_req.c \
+tid/tid_resp.c \
+common/tr_msg.c \
+common/tr_name.c \
+common/tr_idp.c \
+common/tr_apc.c \
 common/tr_comm.c \
 common/tr_filter.c \
 common/tr_rp.c \
-tr/tr.c
+common/tr_config.c \
+common/tr_debug.c 
 
-tr_trust_router_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+trp_msgtst_LDADD =  $(GLIB_LIBS)
+
+trp_test_rtbl_test_SOURCES = trp/test/rtbl_test.c \
+common/tr_name.c \
+common/tr_debug.c \
+trp/trp_rtable.c
+
+trp_test_rtbl_test_LDADD =  $(GLIB_LIBS)
+
+trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \
+$(common_srcs) \
+$(trp_srcs) \
+$(tid_srcs) \
+common/tr_mq.c
+
+trp_test_ptbl_test_LDADD = gsscon/libgsscon.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_tidc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+tid_example_tidc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 
 tid_example_tids_SOURCES = tid/example/tids_main.c 
 
-tid_example_tids_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+#tid_example_tids_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
+tid_example_tids_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 
 common_dh_test_tr_dh_test_SOURCES = common/tr_dh.c \
 common/tr_debug.c \
 common/dh_test/dh_test.c
 
-libtr_tid_la_SOURCES = tid/tids.c tid/tidc.c tid/tid_req.c tid/tid_resp.c \
-$(common_srcs)
+common_mq_test_mq_test_SOURCES = common/tr_mq.c \
+common/mq_test/mq_test.c
+
+common_mq_test_mq_test_CFLAGS = -pthread
+common_mq_test_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
 
-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
+common_mq_test_thread_test_SOURCES = common/tr_mq.c \
+common/tr_debug.c \
+common/mq_test/thread_test.c
+
+common_mq_test_thread_test_CFLAGS = -pthread
+common_mq_test_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc
+
+# libtr_tid_la_SOURCES = tid/tids.c tid/tidc.c tid/tid_req.c tid/tid_resp.c \
+# $(common_srcs)
+# 
+# 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_trp_la_SOURCES = trp/trps.c trp/trpc.c trp/trp_req.c trp/trp_upd.c trp/trp_conn.c \
+# $(common_srcs)
+# 
+# libtr_trp_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+# libtr_trp_la_LIBADD = gsscon/libgsscon.la $(GLIB_LIBS)
+# libtr_trp_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2 -no-undefined
 
 pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \
        include/tr_debug.h \
@@ -60,6 +150,7 @@ noinst_HEADERS = include/gsscon.h include/tr_config.h \
        include/tr_msg.h include/tr.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
 
diff --git a/common/mq_test/mq_test.c b/common/mq_test/mq_test.c
new file mode 100644 (file)
index 0000000..a833510
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <tr_mq.h>
+
+static void notify_cb(TR_MQ *mq, void *arg)
+{
+  char *s=(char *)arg;
+
+  printf("MQ %s no longer empty.\n", s);
+}
+
+int main(void)
+{
+  TR_MQ *mq=NULL;
+  TR_MQ_MSG *msg=NULL;
+  TR_MQ_MSG *msg1=NULL;
+  TR_MQ_MSG *msg2=NULL;
+  TR_MQ_MSG *msg3=NULL;
+  TR_MQ_MSG *msg4=NULL;
+  char *mq_name="1";
+
+  mq=tr_mq_new(NULL);
+  mq->notify_cb=notify_cb;
+  mq->notify_cb_arg=mq_name;
+
+  msg1=tr_mq_msg_new(NULL);
+  asprintf((char **)&(msg1->p), "First message.\n");
+  msg1->p_free=free;
+  tr_mq_append(mq, msg1);
+  assert(mq->head==msg1);
+  assert(mq->tail==msg1);
+  assert(msg1->next==NULL);
+
+  msg2=tr_mq_msg_new(NULL);
+  asprintf((char **)&(msg2->p), "Second message.\n");
+  msg2->p_free=free;
+  tr_mq_append(mq, msg2);
+  assert(mq->head==msg1);
+  assert(msg1->next==msg2);
+  assert(mq->tail==msg2);
+  assert(msg2->next==NULL);
+
+  msg=tr_mq_pop(mq);
+  assert(msg==msg1);
+  assert(mq->head==msg2);
+  assert(mq->tail==msg2);
+  assert(msg2->next==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+  
+  msg3=tr_mq_msg_new(NULL);
+  asprintf((char **)&(msg3->p), "Third message.\n");
+  msg3->p_free=free;
+  tr_mq_append(mq, msg3);
+  assert(mq->head==msg2);
+  assert(mq->tail==msg3);
+  assert(msg2->next==msg3);
+  assert(msg3->next==NULL);
+
+  msg=tr_mq_pop(mq);
+  assert(msg==msg2);
+  assert(mq->head==msg3);
+  assert(mq->tail==msg3);
+  assert(msg3->next==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+  
+  msg=tr_mq_pop(mq);
+  assert(msg==msg3);
+  assert(mq->head==NULL);
+  assert(mq->tail==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+  
+  msg=tr_mq_pop(mq);
+  assert(msg==NULL);
+  assert(mq->head==NULL);
+  assert(mq->tail==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+
+  msg4=tr_mq_msg_new(NULL);
+  asprintf((char **)&(msg4->p), "Fourth message.\n");
+  msg4->p_free=free;
+  tr_mq_append(mq, msg4);
+  assert(mq->head==msg4);
+  assert(mq->tail==msg4);
+  assert(msg4->next==NULL);
+
+  msg=tr_mq_pop(mq);
+  assert(msg==msg4);
+  assert(mq->head==NULL);
+  assert(mq->tail==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+  
+  msg=tr_mq_pop(mq);
+  assert(msg==NULL);
+  assert(mq->head==NULL);
+  assert(mq->tail==NULL);
+  if ((msg!=NULL) && (msg->p!=NULL)) {
+    printf((char *)msg->p);
+    tr_mq_msg_free(msg);
+  } else
+    printf("no message to pop\n");
+
+  tr_mq_free(mq);
+
+  return 0;
+}
diff --git a/common/mq_test/thread_test.c b/common/mq_test/thread_test.c
new file mode 100644 (file)
index 0000000..7cb52e2
--- /dev/null
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <talloc.h>
+#include <time.h>
+#include <errno.h>
+
+#include <tr_mq.h>
+
+struct thread_data {
+  TR_MQ *mq;
+  useconds_t msg_dly;
+  int n_msgs;
+  char *label;
+};
+
+TR_MQ_MSG *make_msg(label, n)
+{
+  TR_MQ_MSG *msg=NULL;
+  msg=tr_mq_msg_new(NULL);
+  asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n);
+  msg->p_free=free;
+  return msg;
+}
+
+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;
+  
+  while (n_msgs>=0) {
+    usleep(msg_dly);
+    tr_mq_append(mq, make_msg(label, n_msgs));
+    n_msgs--;
+  }
+  tr_mq_append(mq, make_msg(label, -9999));
+  return NULL;
+}
+
+struct message_data {
+  pthread_mutex_t lock;
+  pthread_cond_t cond;
+  int ready;
+};
+
+void handle_messages(TR_MQ *mq, void *arg)
+{
+  struct message_data *status=(struct message_data *)arg;
+  pthread_mutex_lock(&(status->lock));
+  status->ready=1;
+  pthread_cond_signal(&(status->cond));
+  pthread_mutex_unlock(&(status->lock));
+}
+
+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)) {
+    printf("  > %s\n", (char *)msg->p);
+    tr_mq_msg_free(msg);
+  }
+  printf("* all messages handled\n");
+  fflush(stdout);
+}
+
+#define N_THREADS 2
+
+int main(void)
+{
+  TR_MQ *mq=NULL;
+  pthread_t thread[N_THREADS];
+  struct thread_data thread_data[N_THREADS];
+  useconds_t dly[N_THREADS]={100000, 1000000}; /* must be N_THREADS long */
+  int ii=0;
+  struct message_data status;
+  struct timespec timeout={0,0};
+  int wait_result=0;
+
+  mq=tr_mq_new(NULL);
+  mq->notify_cb=handle_messages;
+  mq->notify_cb_arg=(void *)&status;
+
+  pthread_cond_init(&(status.cond), 0);
+  pthread_mutex_init(&(status.lock), 0);
+  status.ready=0;
+
+  printf("Starting threads\n");
+  for (ii=0; ii<N_THREADS; ii++) {
+    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);
+    pthread_create(&(thread[ii]), NULL, thread_start, &thread_data[ii]);
+    printf("%s started.\n", thread_data[ii].label);
+  }
+
+  while (1) {
+    pthread_mutex_lock(&(status.lock));
+    while ((!status.ready) && (wait_result!=ETIMEDOUT)) {
+      clock_gettime(CLOCK_REALTIME, &timeout);
+      timeout.tv_sec+=1;
+      wait_result=pthread_cond_timedwait(&(status.cond), &(status.lock), &timeout);
+    }
+
+    if (wait_result==ETIMEDOUT)
+      break;
+
+    output_messages(mq);
+    status.ready=0;
+    pthread_mutex_unlock(&(status.lock));
+    usleep(2000000);
+  }
+  printf("\n*** Timeout expired with no new messages. Joining threads and terminating.\n");
+  for (ii=0; ii<N_THREADS; ii++)
+    pthread_join(thread[ii], NULL);
+
+  return 0;
+}
index 9b79850..2dda3a7 100644 (file)
  *
  */
 
+#include <tr_rp.h>
 #include <trust_router/tr_name.h>
-#include <tr_config.h>
-#include <tr.h>
 #include <tr_comm.h>
-#include <tr_rp.h>
 #include <tr_debug.h>
 
 TR_IDP_REALM *tr_find_comm_idp (TR_COMM *comm, TR_NAME *idp_realm)
@@ -75,13 +73,13 @@ TR_RP_REALM *tr_find_comm_rp (TR_COMM *comm, TR_NAME *rp_realm)
   return NULL;
 }
 
-TR_COMM *tr_comm_lookup(TR_INSTANCE *tr, TR_NAME *comm
+TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name
 {
   TR_COMM *cfg_comm = NULL;
 
-  for (cfg_comm = tr->active_cfg->comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
-    if ((cfg_comm->id->len == comm->len) &&
-       (!strncmp(cfg_comm->id->buf, comm->buf, comm->len)))
+  for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
+    if ((cfg_comm->id->len == comm_name->len) &&
+       (!strncmp(cfg_comm->id->buf, comm_name->buf, comm_name->len)))
       return cfg_comm;
   }
   return NULL;
index 433f726..83eddf7 100644 (file)
 #include <dirent.h>
 #include <talloc.h>
 
+#include <tr_cfgwatch.h>
 #include <tr_config.h>
 #include <tr_debug.h>
-#include <tr.h>
 #include <tr_filter.h>
 #include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
 
 void tr_print_config (TR_CFG *cfg) {
   tr_notice("tr_print_config: Logging running trust router configuration.");
@@ -79,22 +81,38 @@ void tr_print_comm_rps(TR_RP_REALM *rp_list) {
   }
 }
 
+TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
+{
+  return talloc_zero(mem_ctx, TR_CFG);
+}
+
 void tr_cfg_free (TR_CFG *cfg) {
   talloc_free(cfg);
-  return;
 }
 
-TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr) {
-  if (!tr)
+TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
+{
+  return talloc_zero(mem_ctx, TR_CFG_MGR);
+}
+
+void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
+  talloc_free(cfg_mgr);
+}
+
+TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
+{
+  /* cfg_mgr->active is allowed to be null, but new cannot be */
+  if ((cfg_mgr==NULL) || (cfg_mgr->new==NULL))
     return TR_CFG_BAD_PARAMS;
 
-  if (tr->active_cfg)
-    tr_cfg_free(tr->active_cfg);
+  if (cfg_mgr->active != NULL)
+    tr_cfg_free(cfg_mgr->active);
 
-  tr->active_cfg = tr->new_cfg;
+  cfg_mgr->active = cfg_mgr->new;
+  cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
 
-  tr_log_threshold(tr->active_cfg->internal->log_threshold);
-  tr_console_threshold(tr->active_cfg->internal->console_threshold);
+  tr_log_threshold(cfg_mgr->active->internal->log_threshold);
+  tr_console_threshold(cfg_mgr->active->internal->console_threshold);
 
   return TR_CFG_SUCCESS;
 }
@@ -102,20 +120,24 @@ TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr) {
 static TR_CFG_RC tr_cfg_parse_internal (TR_CFG *trc, json_t *jcfg) {
   json_t *jint = NULL;
   json_t *jmtd = NULL;
-  json_t *jtp = NULL;
+  json_t *jtidsp = NULL;
+  json_t *jtrpsp = NULL;
   json_t *jhname = NULL;
   json_t *jlog = NULL;
   json_t *jconthres = NULL;
   json_t *jlogthres = NULL;
+  json_t *jcfgpoll = NULL;
+  json_t *jcfgsettle = NULL;
+  json_t *jroutesweep = NULL;
+  json_t *jrouteupdate = NULL;
+  json_t *jrouteconnect = NULL;
 
   if ((!trc) || (!jcfg))
     return TR_CFG_BAD_PARAMS;
 
   if (NULL == trc->internal) {
-    if (NULL == (trc->internal = talloc(trc, TR_CFG_INTERNAL)))
+    if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
       return TR_CFG_NOMEM;
-
-    memset(trc->internal, 0, sizeof(TR_CFG_INTERNAL));
   }
 
   if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
@@ -130,17 +152,28 @@ static TR_CFG_RC tr_cfg_parse_internal (TR_CFG *trc, json_t *jcfg) {
       /* If not configured, use the default */
       trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
     }
-    if (NULL != (jtp = json_object_get(jint, "tids_port"))) {
-      if (json_is_number(jtp)) {
-       trc->internal->tids_port = json_integer_value(jtp);
+    if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
+      if (json_is_number(jtidsp)) {
+       trc->internal->tids_port = json_integer_value(jtidsp);
       } else {
-       tr_debug("tr_cfg_parse_internal: Parsing error, port is not a number.");
+       tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
        return TR_CFG_NOPARSE;
       }
     } else {
       /* If not configured, use the default */
       trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
     }
+    if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
+      if (json_is_number(jtrpsp)) {
+       trc->internal->trps_port = json_integer_value(jtrpsp);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* If not configured, use the default */
+      trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
+    }
     if (NULL != (jhname = json_object_get(jint, "hostname"))) {
       if (json_is_string(jhname)) {
        trc->internal->hostname = json_string_value(jhname);
@@ -149,6 +182,63 @@ static TR_CFG_RC tr_cfg_parse_internal (TR_CFG *trc, json_t *jcfg) {
        return TR_CFG_NOPARSE;
       }
     }
+    if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
+      if (json_is_number(jcfgpoll)) {
+       trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
+    }
+
+    if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
+      if (json_is_number(jcfgsettle)) {
+       trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
+    }
+
+    if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
+      if (json_is_number(jrouteconnect)) {
+       trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
+    }
+
+    if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
+      if (json_is_number(jroutesweep)) {
+       trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
+    }
+
+    if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
+      if (json_is_number(jrouteupdate)) {
+       trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
+      } else {
+       tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
+       return TR_CFG_NOPARSE;
+      }
+    } else {
+      /* if not configured, use the default */
+      trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
+    }
 
     if (NULL != (jlog = json_object_get(jint, "logging"))) {
       if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
@@ -201,14 +291,12 @@ static TR_CONSTRAINT *tr_cfg_parse_one_constraint (TR_CFG *trc, char *ctype, jso
     return NULL;
   }
 
-  if (NULL == (cons = talloc(trc, TR_CONSTRAINT))) {
+  if (NULL == (cons = talloc_zero(trc, TR_CONSTRAINT))) {
     tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
     *rc = TR_CFG_NOMEM;
     return NULL;
   }
 
-  memset(cons, 0, sizeof(TR_CONSTRAINT));
-
   if (NULL == (cons->type = tr_new_name(ctype))) {
     tr_debug("tr_cfg_parse_one_constraint: Out of memory (type).");
     *rc = TR_CFG_NOMEM;
@@ -255,14 +343,12 @@ static TR_FILTER *tr_cfg_parse_one_filter (TR_CFG *trc, json_t *jfilt, TR_CFG_RC
     return NULL;
   }
 
-  if (NULL == (filt = talloc(trc, TR_FILTER))) {
+  if (NULL == (filt = talloc_zero(trc, TR_FILTER))) {
     tr_debug("tr_cfg_parse_one_filter: Out of memory.");
     *rc = TR_CFG_NOMEM;
     return NULL;
   }
 
-  memset(filt, 0, sizeof(TR_FILTER));
-
   if (!strcmp(json_string_value(jftype), "rp_permitted")) {
     filt->type = TR_FILTER_TYPE_RP_PERMITTED;
   }
@@ -300,15 +386,13 @@ static TR_FILTER *tr_cfg_parse_one_filter (TR_CFG *trc, json_t *jfilt, TR_CFG_RC
       return NULL;
     }
 
-    if (NULL == (filt->lines[i] = talloc(trc, TR_FLINE))) {
+    if (NULL == (filt->lines[i] = talloc_zero(trc, TR_FLINE))) {
       tr_debug("tr_cfg_parse_one_filter: Out of memory (fline).");
       *rc = TR_CFG_NOMEM;
       tr_filter_free(filt);
       return NULL;
     }
 
-    memset(filt->lines[i], 0, sizeof(TR_FLINE));
-
     if (!strcmp(json_string_value(jfaction), "accept")) {
        filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
     }
@@ -359,15 +443,13 @@ static TR_FILTER *tr_cfg_parse_one_filter (TR_CFG *trc, json_t *jfilt, TR_CFG_RC
        return NULL;
       }
 
-      if (NULL == (filt->lines[i]->specs[j] = talloc(trc, TR_FSPEC))) {
+      if (NULL == (filt->lines[i]->specs[j] = talloc_zero(trc, TR_FSPEC))) {
        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
        *rc = TR_CFG_NOMEM;
        tr_filter_free(filt);
        return NULL;
       }
 
-      memset(filt->lines[i]->specs[j], 0, sizeof(TR_FSPEC));
-    
       if ((NULL == (filt->lines[i]->specs[j]->field = tr_new_name((char *)json_string_value(jffield)))) ||
          (NULL == (filt->lines[i]->specs[j]->match = tr_new_name((char *)json_string_value(jfmatch))))) {
        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
@@ -425,13 +507,11 @@ static TR_RP_CLIENT *tr_cfg_parse_one_rp_client (TR_CFG *trc, json_t *jrp, TR_CF
     return NULL;
   }
 
-  if (NULL == (rp = talloc(trc, TR_RP_CLIENT))) {
+  if (NULL == (rp = talloc_zero(trc, TR_RP_CLIENT))) {
     tr_debug("tr_cfg_parse_one_rp_realm: Out of memory.");
     *rc = TR_CFG_NOMEM;
     return NULL;
   }
-  
-  memset(rp, 0, sizeof(TR_RP_CLIENT));
 
   /* TBD -- support more than one filter entry per RP Client? */
   if (NULL == (rp->filter = tr_cfg_parse_one_filter(trc, jfilt, rc))) {
@@ -477,11 +557,13 @@ static TR_CFG_RC tr_cfg_parse_rp_clients (TR_CFG *trc, json_t *jcfg) {
       trc->rp_clients = rp;
     }
   }
+  tr_debug("tr_cfg_parse_rp_clients: Finished (rc=%d)", rc);
   return rc;
 }
 
-static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
+static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jaddr, TR_CFG_RC *rc) {
   TR_AAA_SERVER *aaa = NULL;
+  TR_NAME *name=NULL;
 
   if ((!trc) || (!jaddr) || (!json_is_string(jaddr))) {
     tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
@@ -489,27 +571,34 @@ static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server (TR_CFG *trc, json_t *jaddr, T
     return NULL;
   }
 
-  if (NULL == (aaa = talloc(trc, TR_AAA_SERVER))) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory.");
+  name=tr_new_name((char *)(json_string_value(jaddr)));
+  if (name==NULL) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
     *rc = TR_CFG_NOMEM;
     return NULL;
   }
 
-  memset(aaa, 0, sizeof(TR_AAA_SERVER));
-
-  aaa->hostname = tr_new_name((char *)(json_string_value(jaddr)));
+  aaa=tr_aaa_server_new(mem_ctx, name);
+  if (aaa==NULL) {
+    tr_free_name(name);
+    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
+    *rc = TR_CFG_NOMEM;
+    return NULL;
+  }
 
   return aaa;
 }
 
-static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TR_CFG *trc, json_t *jaaas, TR_CFG_RC *rc) 
+static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jaaas, TR_CFG_RC *rc) 
 {
+  TALLOC_CTX *tmp_ctx=NULL;
   TR_AAA_SERVER *aaa = NULL;
   TR_AAA_SERVER *temp_aaa = NULL;
   int i = 0;
 
   for (i = 0; i < json_array_size(jaaas); i++) {
-    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(trc, json_array_get(jaaas, i), rc))) {
+    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(mem_ctx, trc, json_array_get(jaaas, i), rc))) {
+      talloc_free(tmp_ctx);
       return NULL;
     }
     /* TBD -- IPv6 addresses */
@@ -517,10 +606,15 @@ static TR_AAA_SERVER *tr_cfg_parse_aaa_servers (TR_CFG *trc, json_t *jaaas, TR_C
     temp_aaa->next = aaa;
     aaa = temp_aaa;
   }
+  tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
+
+  for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
+    talloc_steal(mem_ctx, temp_aaa);
+  talloc_free(tmp_ctx);
   return aaa;
 }
 
-static TR_APC *tr_cfg_parse_apcs (TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
+static TR_APC *tr_cfg_parse_apcs (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
 {
   TR_APC *apc;
 
@@ -533,30 +627,35 @@ static TR_APC *tr_cfg_parse_apcs (TR_CFG *trc, json_t *japcs, TR_CFG_RC *rc)
     return NULL;
   }
 
-  if (NULL == (apc = talloc(trc, TR_APC))) {
+  apc=tr_apc_new(mem_ctx);
+  if (apc==NULL) {
     tr_debug("tr_cfg_parse_apcs: Out of memory.");
     *rc = TR_CFG_NOMEM;
     return NULL;
   }
 
-  memset(apc, 0, sizeof(TR_APC));
-
   /* TBD, deal with more than one APC.  In the meantime, though...                */
   /* Only parse the first APC, because we only know how to deal with one, anyway. */
-  if (0 == json_array_size(japcs))
+  if (0 == json_array_size(japcs)) {
+    talloc_free(apc);
     return NULL;
+  }
 
   if (NULL == (apc->id = tr_new_name((char *)json_string_value(json_array_get(japcs, 0))))) {
     tr_debug("tr_cfg_parse_apcs: No memory for APC name.");
     *rc = TR_CFG_NOMEM;
+    talloc_free(apc);
     return NULL;
   }
 
+  tr_debug("tr_cfg_parse_apcs: Finished (rc=%d)", *rc);
   return apc;
 }
 
 static TR_IDP_REALM *tr_cfg_parse_one_idp_realm (TR_CFG *trc, json_t *jidp, TR_CFG_RC *rc) {
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_IDP_REALM *idp = NULL;
+  json_t *jremote = NULL;
   json_t *jrid = NULL;
   json_t *jscfg = NULL;
   json_t *jsrvrs = NULL;
@@ -569,52 +668,76 @@ static TR_IDP_REALM *tr_cfg_parse_one_idp_realm (TR_CFG *trc, json_t *jidp, TR_C
     return NULL;
   }
 
-  if (NULL == (idp = talloc(trc, TR_IDP_REALM))) {
+  if (NULL == (idp = tr_idp_realm_new(tmp_ctx))) {
     tr_debug("tr_cfg_parse_one_idp_realm: Out of memory.");
     *rc = TR_CFG_NOMEM;
+    talloc_free(tmp_ctx);
     return NULL;
   }
 
-  memset(idp, 0, sizeof(TR_IDP_REALM));
-
-  if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
-      (!json_is_string(jrid)) ||
-      (NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
-      (!json_is_string(jscfg)) ||
-      (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
-      (!json_is_array(jsrvrs))) {
-    tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
-    *rc = TR_CFG_NOPARSE;
+  /* Assume local route unless specified as remote. */
+  jremote = json_object_get(jidp, "remote");
+  if ((jremote!=NULL) && (!json_is_number(jremote))) {
+    tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (remote is not a number).");
+    *rc=TR_CFG_NOPARSE;
+    talloc_free(tmp_ctx);
     return NULL;
   }
 
-  if (0 == strcmp(json_string_value(jscfg), "no")) {
-    idp->shared_config = 0;
-  } else {
-    idp->shared_config = 1;
+  if ((NULL == (jrid = json_object_get(jidp, "realm_id"))) ||
+      (!json_is_string(jrid))) {
+      tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration (realm_id missing or invalid).");
+      *rc = TR_CFG_NOPARSE;
+      talloc_free(tmp_ctx);
+      return NULL;
   }
+        
+  if ((jremote==NULL) || (0==json_integer_value(jremote))) {
+    idp->origin=TR_REALM_LOCAL;
+
+    if ((NULL == (jscfg = json_object_get(jidp, "shared_config"))) ||
+        (!json_is_string(jscfg)) ||
+        (NULL == (jsrvrs = json_object_get(jidp, "aaa_servers"))) ||
+        (!json_is_array(jsrvrs))) {
+      tr_debug("tr_cfg_parse_one_idp_realm: Error parsing IDP realm configuration.");
+      *rc = TR_CFG_NOPARSE;
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+
+    if (0 == strcmp(json_string_value(jscfg), "no")) {
+      idp->shared_config = 0;
+    } else {
+      idp->shared_config = 1;
+    }
+  } else
+    idp->origin=TR_REALM_REMOTE_INCOMPLETE;
 
   if (NULL == (idp->realm_id = tr_new_name((char *)json_string_value(jrid)))) {
     tr_debug("tr_cfg_parse_one_idp_realm: No memory for realm id.");
     *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  if (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(trc, jsrvrs, rc))) {
-    tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
-    tr_free_name(idp->realm_id);
+    talloc_free(tmp_ctx);
     return NULL;
   }
 
   if ((NULL != (japcs = json_object_get(jidp, "apcs"))) &&
       (json_is_array(japcs))) {
-    if (NULL == (idp->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
+    if (NULL == (idp->apcs = tr_cfg_parse_apcs(idp, trc, japcs, rc))) {
       tr_debug("tr_cfg_parse_one_idp_realm: Can't parse APCs for realm %s .", idp->realm_id->buf);
-      tr_free_name(idp->realm_id);
-      /* TBD -- free aaa_servers */;
+      talloc_free(tmp_ctx);
       return NULL;
     }
   } 
+
+  if ((idp->origin==TR_REALM_LOCAL) &&
+      (NULL == (idp->aaa_servers = tr_cfg_parse_aaa_servers(idp, trc, jsrvrs, rc)))) {
+    tr_debug("tr_cfg_parse_one_idp_realm: Can't parse AAA servers for realm %s.", idp->realm_id->buf);
+    talloc_free(tmp_ctx);
+    return NULL;
+  }
+
+  talloc_steal(trc, idp);
+  talloc_free(tmp_ctx);
   return idp;
 }
 
@@ -634,7 +757,7 @@ static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg)
       (0 < json_array_size(jdss))) {
 
     for (i = 0; i < json_array_size(jdss); i++) {
-      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, 
+      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc, trc, 
                                                  json_array_get(jdss, i), 
                                                  &rc))) {
        return rc;
@@ -645,6 +768,7 @@ static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg)
     }
   } 
 
+  tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
   return rc;
 }
 
@@ -673,6 +797,7 @@ static TR_CFG_RC tr_cfg_parse_idp_realms (TR_CFG *trc, json_t *jcfg)
     }
   }
 
+  tr_debug("tr_cfg_parse_idp_realms: Finished (rc=%d)", rc);
   return rc;
 }
 
@@ -733,13 +858,12 @@ static TR_RP_REALM *tr_cfg_parse_comm_rps (TR_CFG *trc, json_t *jrps, TR_CFG_RC
   }
 
   for (i = (json_array_size(jrps)-1); i >= 0; i--) {
-    if (NULL == (temp_rp = talloc(trc, TR_RP_REALM))) {
+    if (NULL == (temp_rp = talloc_zero(trc, TR_RP_REALM))) {
       tr_debug("tr_cfg_parse_comm_rps: Can't allocate memory for RP Realm.");
       if (rc)
        *rc = TR_CFG_NOMEM;
       return NULL;
     }
-    memset (temp_rp, 0, sizeof(TR_RP_REALM));
 
     if (NULL == (temp_rp->realm_name = tr_new_name((char *)json_string_value(json_array_get(jrps, i))))) {
       tr_debug("tr_cfg_parse_comm_rps: No memory for RP Realm Name.");
@@ -802,7 +926,7 @@ static TR_COMM *tr_cfg_parse_one_comm (TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc
     comm->type = TR_COMM_APC;
   } else if (0 == strcmp(json_string_value(jtype), "coi")) {
     comm->type = TR_COMM_COI;
-    if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, japcs, rc))) {
+    if (NULL == (comm->apcs = tr_cfg_parse_apcs(trc, trc, japcs, rc))) {
       tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.", comm->id->buf);
       tr_free_name(comm->id);
       return NULL;
@@ -875,6 +999,7 @@ static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg)
       trc->comms = comm;
     }
   }
+  tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
   return rc;
 }
 
@@ -910,43 +1035,49 @@ TR_CFG_RC tr_cfg_validate (TR_CFG *trc) {
 
 /* Join two paths and return a pointer to the result. This should be freed
  * via talloc_free. Returns NULL on failure. */
-static char *join_paths(const char *p1, const char *p2) {
-  return talloc_asprintf(NULL, "%s/%s", p1, p2); /* returns NULL on a failure */
+static char *join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2) {
+  return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
 }
 
-/* Reads configuration files in config_dir ("" or "./" will use the current directory) */
-TR_CFG_RC tr_parse_config (TR_INSTANCE *tr, const char *config_dir, int n, struct dirent **cfg_files) {
+/* Reads configuration files in config_dir ("" or "./" will use the current directory). */
+TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   json_t *jcfg;
   json_t *jser;
   json_error_t rc;
   char *file_with_path;
+  int ii;
+  TR_CFG_RC cfg_rc=TR_CFG_ERROR;
 
-  if ((!tr) || (!cfg_files) || (n<=0))
-    return TR_CFG_BAD_PARAMS;
-
-  /* If there is a partial/abandoned config lying around, free it */
-  if (tr->new_cfg) 
-    tr_cfg_free(tr->new_cfg);
-  
-  if (NULL == (tr->new_cfg = talloc(NULL, TR_CFG)))
-    return TR_CFG_NOMEM;
+  if ((!cfg_mgr) || (!cfg_files) || (n<=0)) {
+    cfg_rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
 
-  memset(tr->new_cfg, 0, sizeof(TR_CFG));
+  if (cfg_mgr->new != NULL)
+    tr_cfg_free(cfg_mgr->new);
+  cfg_mgr->new=tr_cfg_new(tmp_ctx); /* belongs to the temporary context for now */
+  if (cfg_mgr->new == NULL) {
+    cfg_rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
 
   /* Parse configuration information from each config file */
-  while (n--) {
-    file_with_path=join_paths(config_dir, cfg_files[n]->d_name); /* must free result with talloc_free */
+  for (ii=0; ii<n; ii++) {
+    file_with_path=join_paths(tmp_ctx, config_dir, cfg_files[ii]->d_name); /* must free result with talloc_free */
     if(file_with_path == NULL) {
       tr_crit("tr_parse_config: error joining path.");
-      return TR_CFG_NOMEM;
+      cfg_rc=TR_CFG_NOMEM;
+      goto cleanup;
     }
-    tr_debug("tr_parse_config: Parsing %s.", cfg_files[n]->d_name); /* print the filename without the path */
+    tr_debug("tr_parse_config: Parsing %s.", cfg_files[ii]->d_name); /* print the filename without the path */
     if (NULL == (jcfg = json_load_file(file_with_path, 
                                        JSON_DISABLE_EOF_CHECK, &rc))) {
       tr_debug("tr_parse_config: Error parsing config file %s.", 
-               cfg_files[n]->d_name);
-      talloc_free(file_with_path);
-      return TR_CFG_NOPARSE;
+               cfg_files[ii]->d_name);
+      cfg_rc=TR_CFG_NOPARSE;
+      goto cleanup;
     }
     talloc_free(file_with_path); /* done with filename */
 
@@ -959,23 +1090,30 @@ TR_CFG_RC tr_parse_config (TR_INSTANCE *tr, const char *config_dir, int n, struc
       }
     }
 
-    if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(tr->new_cfg, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(tr->new_cfg, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(tr->new_cfg, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(tr->new_cfg, jcfg)) ||
-        (TR_CFG_SUCCESS != tr_cfg_parse_comms(tr->new_cfg, jcfg))) {
-      tr_cfg_free(tr->new_cfg);
-      return TR_CFG_ERROR;
+    if ((TR_CFG_SUCCESS != tr_cfg_parse_internal(cfg_mgr->new, jcfg)) ||
+        (TR_CFG_SUCCESS != tr_cfg_parse_rp_clients(cfg_mgr->new, jcfg)) ||
+        (TR_CFG_SUCCESS != tr_cfg_parse_idp_realms(cfg_mgr->new, jcfg)) ||
+        (TR_CFG_SUCCESS != tr_cfg_parse_default_servers(cfg_mgr->new, jcfg)) ||
+        (TR_CFG_SUCCESS != tr_cfg_parse_comms(cfg_mgr->new, jcfg))) {
+      cfg_rc=TR_CFG_ERROR;
+      goto cleanup;
     }
   }
 
   /* make sure we got a complete, consistent configuration */
-  if (TR_CFG_SUCCESS != tr_cfg_validate(tr->new_cfg)) {
-    tr_debug("tr_parse_config: Error: INVALID CONFIGURATION, EXITING");
-    return TR_CFG_ERROR;
+  if (TR_CFG_SUCCESS != tr_cfg_validate(cfg_mgr->new)) {
+    tr_err("tr_parse_config: Error: INVALID CONFIGURATION");
+    cfg_rc=TR_CFG_ERROR;
+    goto cleanup;
   }
 
-  return TR_CFG_SUCCESS;
+  /* success! */
+  talloc_steal(cfg_mgr, cfg_mgr->new); /* hand this over to the cfg_mgr context */
+  cfg_rc=TR_CFG_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return cfg_rc;
 }
 
 TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
@@ -1056,21 +1194,14 @@ static int is_cfg_file(const struct dirent *dent) {
  * by scandir(). These can be freed with tr_free_config_file_list().
  */
 int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files) {
-  int n = 0, i = 0;
+  int n = 0;
   
-  n = scandir(config_dir, cfg_files, &is_cfg_file, 0);
+  n = scandir(config_dir, cfg_files, is_cfg_file, alphasort);
 
   if (n < 0) {
     perror("scandir");
     tr_debug("tr_find_config: scandir error trying to scan %s.", config_dir);
-  } else if (n == 0) {
-    tr_debug("tr_find_config: No config files found.");
-  } else {
-    i = n;
-    while(i--) {
-      tr_debug("tr_find_config: Config file found (%s).", (*cfg_files)[i]->d_name);
-    }
-  }
+  } 
 
   return n;
 }
index c921b8a..6981213 100644 (file)
@@ -162,6 +162,20 @@ static char *join_audit_msg(const int count, char *array[]) {
   return buf;
 }
 
+const char *sev2str(int sev)
+{
+  switch (sev) {
+  case LOG_DEBUG:   return "debug";
+  case LOG_INFO:    return "info";
+  case LOG_NOTICE:  return "notice";
+  case LOG_WARNING: return "warning";
+  case LOG_ERR:     return "err";
+  case LOG_CRIT:    return "crit";
+  case LOG_ALERT:   return "alert";
+  default:          return "invalid";
+  }
+}
+
 int str2sev(const char* sev) {
 
   if (strcmp(sev, "debug") ==0 ) {
index eb0c6fd..b041ffb 100644 (file)
  *
  */
 
+#include <talloc.h>
+
 #include <trust_router/tr_name.h>
 #include <tr_idp.h>
-#include <tr.h>
 #include <tr_config.h>
 
-TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_INSTANCE *tr, TR_NAME *idp_realm, TR_NAME *comm)
+static int tr_aaa_server_destructor(void *obj)
+{
+  TR_AAA_SERVER *aaa=talloc_get_type_abort(obj, TR_AAA_SERVER);
+  if (aaa->hostname!=NULL)
+    tr_free_name(aaa->hostname);
+  return 0;
+}
+
+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->hostname=hostname;
+    talloc_set_destructor((void *)aaa, tr_aaa_server_destructor);
+  }
+  return aaa;
+}
+
+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_IDP_REALM *idp = NULL;
 
-  for (idp = tr->active_cfg->idp_realms; idp != NULL; idp = idp->next) {
-    if (!tr_name_cmp(idp_realm, idp->realm_id)) {
+  for (idp = idp_realms; idp != NULL; idp = idp->next) {
+    if (!tr_name_cmp(idp_realm_name, idp->realm_id)) {
       /* TBD -- check that the community is one of the APCs for the IDP */
       break;
     }
@@ -53,10 +77,34 @@ TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_INSTANCE *tr, TR_NAME *idp_realm, TR_
     return NULL;
 }
 
-TR_AAA_SERVER *tr_default_server_lookup(TR_INSTANCE *tr, TR_NAME *comm)
+TR_AAA_SERVER *tr_default_server_lookup(TR_AAA_SERVER *default_servers, TR_NAME *comm)
 {
-  if ((!tr) || (!(tr->active_cfg)))
+  if (!default_servers)
     return NULL;
 
-  return(tr->active_cfg->default_servers);
+  return(default_servers);
+}
+
+static int tr_idp_realm_destructor(void *obj)
+{
+  TR_IDP_REALM *idp=talloc_get_type_abort(obj, TR_IDP_REALM);
+  if (idp->realm_id!=NULL)
+    tr_free_name(idp->realm_id);
+  return 0;
+}
+
+TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx)
+{
+  TR_IDP_REALM *idp=talloc(mem_ctx, TR_IDP_REALM);
+  if (idp!=NULL) {
+    idp->next=NULL;
+    idp->comm_next=NULL;
+    idp->realm_id=NULL;
+    idp->shared_config=0;
+    idp->aaa_servers=NULL;
+    idp->apcs=NULL;
+    idp->origin=TR_REALM_LOCAL;
+    talloc_set_destructor((void *)idp, tr_idp_realm_destructor);
+  }
+  return idp;
 }
diff --git a/common/tr_mq.c b/common/tr_mq.c
new file mode 100644 (file)
index 0000000..592e51c
--- /dev/null
@@ -0,0 +1,249 @@
+#include <talloc.h>
+#include <pthread.h>
+
+#include <tr_mq.h>
+#include <tr_debug.h>
+
+/* Messages */
+static int tr_mq_msg_destructor(void *object)
+{
+  TR_MQ_MSG *msg=talloc_get_type_abort(object, TR_MQ_MSG);
+  if ( (msg->p!=NULL) && (msg->p_free!=NULL))
+    msg->p_free(msg->p);
+  return 0;
+}
+
+TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *message, TR_MQ_PRIORITY prio)
+{
+  TR_MQ_MSG *msg=talloc(mem_ctx, TR_MQ_MSG);
+  if (msg!=NULL) {
+    msg->next=NULL;
+    msg->prio=prio;
+    msg->message=talloc_strdup(msg, message);
+    if (msg->message==NULL) {
+      talloc_free(msg);
+      return NULL;
+    }
+    msg->p=NULL;
+    talloc_set_destructor((void *)msg, tr_mq_msg_destructor);
+  }
+  return msg;
+}
+
+void tr_mq_msg_free(TR_MQ_MSG *msg)
+{
+  if (msg!=NULL)
+    talloc_free(msg);
+}
+
+TR_MQ_PRIORITY tr_mq_msg_get_prio(TR_MQ_MSG *msg)
+{
+  return msg->prio;
+}
+
+const char *tr_mq_msg_get_message(TR_MQ_MSG *msg)
+{
+  return msg->message;
+}
+
+void *tr_mq_msg_get_payload(TR_MQ_MSG *msg)
+{
+  return msg->p;
+}
+
+/* call with a pointer to the payload and a function to free it later */
+void tr_mq_msg_set_payload(TR_MQ_MSG *msg, void *p, void (*p_free)(void *))
+{
+  msg->p=p;
+  msg->p_free=p_free;
+}
+
+
+static TR_MQ_MSG *tr_mq_msg_get_next(TR_MQ_MSG *msg)
+{
+  return msg->next;
+}
+
+static void tr_mq_msg_set_next(TR_MQ_MSG *msg, TR_MQ_MSG *next)
+{
+  msg->next=next;
+}
+
+/* Message Queues */
+TR_MQ *tr_mq_new(TALLOC_CTX *mem_ctx)
+{
+  TR_MQ *mq=talloc(mem_ctx, TR_MQ);
+  if (mq!=NULL) {
+    pthread_mutex_init(&(mq->mutex), 0);
+    mq->head=NULL;
+    mq->tail=NULL;
+    mq->last_hi_prio=NULL;
+  }
+  return mq;
+}
+
+void tr_mq_free(TR_MQ *mq)
+{
+  if (mq!=NULL) {
+    tr_mq_lock(mq); /* don't pull the rug out from under someone */
+    talloc_free(mq);
+  }
+}
+
+int tr_mq_lock(TR_MQ *mq)
+{
+  return pthread_mutex_lock(&(mq->mutex));
+}
+
+int tr_mq_unlock(TR_MQ *mq)
+{
+  return pthread_mutex_unlock(&(mq->mutex));
+}
+
+static TR_MQ_MSG *tr_mq_get_head(TR_MQ *mq)
+{
+  return mq->head;
+}
+
+static void tr_mq_set_head(TR_MQ *mq, TR_MQ_MSG *msg)
+{
+  mq->head=msg;
+}
+
+static TR_MQ_MSG *tr_mq_get_tail(TR_MQ *mq)
+{
+  return mq->tail;
+}
+
+static void tr_mq_set_tail(TR_MQ *mq, TR_MQ_MSG *msg)
+{
+  mq->tail=msg;
+}
+
+void tr_mq_set_notify_cb(TR_MQ *mq, TR_MQ_NOTIFY_FN cb, void *arg)
+{
+  mq->notify_cb=cb;
+  mq->notify_cb_arg=arg;
+}
+
+void tr_mq_clear(TR_MQ *mq)
+{
+  TR_MQ_MSG *m=NULL;
+  TR_MQ_MSG *n=NULL;
+
+  tr_mq_lock(mq);
+  m=tr_mq_get_head(mq);
+  while (m!=NULL) {
+    n=tr_mq_msg_get_next(m);
+    tr_mq_msg_free(m);
+    m=n;
+  }
+  tr_mq_set_head(mq, NULL);
+  tr_mq_set_tail(mq, NULL);
+  tr_mq_unlock(mq);
+}
+
+static int tr_mq_empty(TR_MQ *mq)
+{
+  return tr_mq_get_head(mq)==NULL;
+}
+
+/* puts msg in mq's talloc context */
+static void tr_mq_append(TR_MQ *mq, TR_MQ_MSG *msg)
+{
+  if (tr_mq_get_head(mq)==NULL) {
+    tr_mq_set_head(mq, msg);
+    tr_mq_set_tail(mq, msg);
+  } else {
+    tr_mq_msg_set_next(tr_mq_get_tail(mq), msg); /* add to list */
+    tr_mq_set_tail(mq, msg); /* update tail of list */
+  }
+  talloc_steal(mq, msg);
+}
+
+static void tr_mq_append_high_prio(TR_MQ *mq, TR_MQ_MSG *new)
+{
+  if (tr_mq_get_head(mq)==NULL) {
+    tr_mq_set_head(mq, new);
+    tr_mq_set_tail(mq, new);
+  } else if (mq->last_hi_prio==NULL) {
+    tr_mq_msg_set_next(new, tr_mq_get_head(mq)); /* add to front of list */
+    tr_mq_set_head(mq, new); /* update head of list */
+  } else {
+    tr_mq_msg_set_next(new, tr_mq_msg_get_next(mq->last_hi_prio));
+    tr_mq_msg_set_next(mq->last_hi_prio, new); /* add to end of hi prio msgs */
+  }
+  mq->last_hi_prio=new; /* in any case, this is now the last high priority msg */
+  talloc_steal(mq,new);
+}
+
+#define DEBUG_TR_MQ 0
+#if DEBUG_TR_MQ
+static void tr_mq_print(TR_MQ *mq)
+{
+  TR_MQ_MSG *m=mq->head;
+  int ii=0;
+
+  tr_debug("tr_mq_print: mq contents:");
+  while(m!=NULL) {
+    ii++;
+    tr_debug("tr_mq_print: Entry %02d: %-15s (prio %d)",
+             ii, tr_mq_msg_get_message(m), tr_mq_msg_get_prio(m));
+    m=tr_mq_msg_get_next(m);
+  }
+}
+#endif
+void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg)
+{
+  int was_empty=0;
+  TR_MQ_NOTIFY_FN notify_cb=NULL;
+  void *notify_cb_arg=NULL;
+
+  tr_mq_lock(mq);
+  
+  was_empty=tr_mq_empty(mq);
+  switch (tr_mq_msg_get_prio(msg)) {
+  case TR_MQ_PRIO_HIGH:
+    tr_mq_append_high_prio(mq, msg);
+    break;
+  default:
+    tr_mq_append(mq, msg);
+    break;
+  }
+  /* before releasing the mutex, get notify_cb data out of mq */
+  notify_cb=mq->notify_cb;
+  notify_cb_arg=mq->notify_cb_arg;
+
+#if DEBUG_TR_MQ
+  tr_mq_print(mq);
+#endif 
+
+  tr_mq_unlock(mq);
+
+  /* see if we need to tell someone we became non-empty */
+  if (was_empty && (notify_cb!=NULL))
+    notify_cb(mq, notify_cb_arg);
+}
+
+/* caller must free msg via tr_mq_msg_free */
+TR_MQ_MSG *tr_mq_pop(TR_MQ *mq)
+{
+  TR_MQ_MSG *popped=NULL;
+
+  tr_mq_lock(mq);
+  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 */
+
+    if (popped==mq->last_hi_prio)
+      mq->last_hi_prio=NULL;
+
+    if (tr_mq_get_head(mq)==NULL)
+      tr_mq_set_tail(mq, NULL); /* just popped the last element */
+  }
+  tr_mq_unlock(mq);
+  if (popped!=NULL)
+    tr_mq_msg_set_next(popped, NULL); /* disconnect from list */
+  return popped;
+}
+
index f771fb8..b2964b6 100644 (file)
 
 #include <tr_msg.h>
 #include <trust_router/tr_name.h>
-#include <tid_internal.h>
+#include <trp_internal.h>
 #include <trust_router/tr_constraint.h>
 #include <tr_debug.h>
 
+/* JSON helpers */
+/* Read attribute attr from msg as an integer. Returns nonzero on error. */
+static int tr_msg_get_json_integer(json_t *jmsg, const char *attr, int *dest)
+{
+  json_t *obj;
+
+  obj=json_object_get(jmsg, attr);
+  if (obj == NULL) {
+    return -1;
+  }
+  /* check type */
+  if (!json_is_integer(obj)) {
+    return -1;
+  }
+
+  (*dest)=json_integer_value(obj);
+  return 0;
+}
+
+/* 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)
+{
+  json_t *obj;
+
+  obj=json_object_get(jmsg, attr);
+  if (obj == NULL)
+    return -1;
+
+  /* check type */
+  if (!json_is_string(obj))
+    return -1;
+
+  *dest=talloc_strdup(mem_ctx, json_string_value(obj));
+  if (*dest==NULL)
+    return -1;
+
+  return 0;
+}
+
 enum msg_type tr_msg_get_msg_type(TR_MSG *msg) 
 {
   return msg->msg_type;
@@ -83,6 +123,32 @@ void tr_msg_set_resp(TR_MSG *msg, TID_RESP *resp)
   msg->msg_type = TID_RESPONSE;
 }
 
+TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg)
+{
+  if (msg->msg_type == TRP_UPDATE)
+    return (TRP_UPD *)msg->msg_rep;
+  return NULL;
+}
+
+void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *update)
+{
+  msg->msg_rep=update;
+  msg->msg_type=TRP_UPDATE;
+}
+
+TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg)
+{
+  if (msg->msg_type == TRP_REQUEST)
+    return (TRP_REQ *)msg->msg_rep;
+  return NULL;
+}
+
+void tr_msg_set_trp_req(TR_MSG *msg, TRP_REQ *req)
+{
+  msg->msg_rep=req;
+  msg->msg_type=TRP_REQUEST;
+}
+
 static json_t *tr_msg_encode_dh(DH *dh)
 {
   json_t *jdh = NULL;
@@ -411,7 +477,7 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
   json_t *jservers = NULL;
   json_t *jerr_msg = NULL;
 
-  if (!(tresp = talloc_zero(NULL, TID_RESP))) {
+  if (!(tresp=tid_resp_new(NULL))) {
     tr_crit("tr_msg_decode_tidresp(): Error allocating TID_RESP structure.");
     return NULL;
   }
@@ -465,10 +531,350 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
   return tresp;
 }
 
+
+/* 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 )
+{
+  json_t *jstr=NULL;
+  json_t *jint=NULL;
+  char *s=NULL;
+
+  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)) {
+    return TRP_ERROR;
+  }
+
+  s=tr_name_strdup(trp_inforec_get_comm(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);
+
+  s=tr_name_strdup(trp_inforec_get_realm(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, "realm", 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;
+  if(jstr==NULL)
+    return TRP_ERROR;
+  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);
+
+  jint=json_integer(trp_inforec_get_interval(rec));
+  if(jint==NULL)
+    return TRP_ERROR;
+  json_object_set_new(jrec, "interval", jint);
+
+  return TRP_SUCCESS;
+}
+
+static json_t *tr_msg_encode_inforec(TRP_INFOREC *rec)
+{
+  json_t *jrec=NULL;
+  json_t *jstr=NULL;
+
+  if ((rec==NULL) || (trp_inforec_get_type(rec)==TRP_INFOREC_TYPE_UNKNOWN))
+    return NULL;
+
+  jrec=json_object();
+  if (jrec==NULL)
+    return NULL;
+
+  jstr=json_string(trp_inforec_type_to_string(trp_inforec_get_type(rec)));
+  if (jstr==NULL) {
+    json_decref(jrec);
+    return NULL;
+  }
+  json_object_set_new(jrec, "record_type", jstr);
+
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (TRP_SUCCESS!=tr_msg_encode_inforec_route(jrec, rec)) {
+      json_decref(jrec);
+      return NULL;
+    }
+    break;
+  default:
+    json_decref(jrec);
+    return NULL;
+  }
+  return jrec;
+}
+
+/* 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;
+  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);
+  talloc_free(s); s=NULL;
+
+  rec=trp_inforec_new(tmp_ctx, rectype);
+  if (rec==NULL) {
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  /* We only support route_info records for now*/
+  if (trp_inforec_get_type(rec)!=TRP_INFOREC_TYPE_ROUTE) {
+    rc=TRP_UNSUPPORTED;
+    goto cleanup;
+  }
+
+  tr_debug("tr_msg_decode_trp_inforec: '%s' record found.", trp_inforec_type_to_string(rec->type));
+
+  rc=tr_msg_get_json_string(jrecord, "community", &s, tmp_ctx);
+  if (rc != TRP_SUCCESS)
+    goto cleanup;
+  if (TRP_SUCCESS!=trp_inforec_set_comm(rec, tr_new_name(s)))
+    goto cleanup;
+  talloc_free(s); s=NULL;
+
+  rc=tr_msg_get_json_string(jrecord, "realm", &s, tmp_ctx);
+  if (rc != TRP_SUCCESS)
+    goto cleanup;
+  if (TRP_SUCCESS!=trp_inforec_set_realm(rec, tr_new_name(s)))
+    goto cleanup;
+  talloc_free(s); s=NULL;
+
+  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)))
+    goto cleanup;
+  talloc_free(s); s=NULL;
+
+  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;
+
+  rc=tr_msg_get_json_integer(jrecord, "interval", &num);
+  if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_interval(rec,num)))
+    goto cleanup;
+
+  talloc_steal(mem_ctx, rec);
+  rc=TRP_SUCCESS;
+
+cleanup:
+  if (rc != TRP_SUCCESS) {
+    trp_inforec_free(rec);
+    rec=NULL;
+  }
+  talloc_free(tmp_ctx);
+  return rec;
+}
+
+/* TRP update msg */
+static json_t *tr_msg_encode_trp_upd(TRP_UPD *update)
+{
+  json_t *jupdate=NULL;
+  json_t *jrecords=NULL;
+  json_t *jrec=NULL;
+  TRP_INFOREC *rec;
+
+  if (update==NULL)
+    return NULL;
+
+  jupdate=json_object();
+  if (jupdate==NULL)
+    return NULL;
+
+  jrecords=json_array();
+  if (jrecords==NULL) {
+    json_decref(jupdate);
+    return NULL;
+  }
+  json_object_set_new(jupdate, "records", jrecords); /* jrecords now a "borrowed" reference */
+  for (rec=trp_upd_get_inforec(update); rec!=NULL; rec=trp_inforec_get_next(rec)) {
+    tr_debug("tr_msg_encode_trp_upd: encoding inforec.");
+    jrec=tr_msg_encode_inforec(rec);
+    if (jrec==NULL) {
+      json_decref(jupdate); /* also decs jrecords and any elements */
+      return NULL;
+    }
+    if (0!=json_array_append_new(jrecords, jrec)) {
+      json_decref(jupdate); /* also decs jrecords and any elements */
+      json_decref(jrec); /* this one did not get added so dec explicitly */
+      return NULL;
+    }
+  }
+
+  return jupdate;
+}
+
+/*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)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  json_t *jrecords=NULL;
+  size_t ii=0;
+  TRP_UPD *update=NULL;
+  TRP_INFOREC *new_rec=NULL;
+  TRP_INFOREC *list_tail=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  update=trp_upd_new(tmp_ctx);
+  if (update==NULL) {
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  jrecords=json_object_get(jupdate, "records");
+  if ((jrecords==NULL) || (!json_is_array(jrecords))) {
+    rc=TRP_NOPARSE;
+    goto cleanup;
+  }
+
+  tr_debug("tr_msg_decode_trp_upd: found %d records", json_array_size(jrecords));
+  /* process the array */
+  for (ii=0; ii<json_array_size(jrecords); ii++) {
+    new_rec=tr_msg_decode_trp_inforec(update, json_array_get(jrecords, ii));
+    if (new_rec==NULL) {
+      rc=TRP_NOPARSE;
+      goto cleanup;
+    }
+
+    if (list_tail==NULL)
+      trp_upd_set_inforec(update, new_rec); /* first is a special case */
+    else
+      trp_inforec_set_next(list_tail, new_rec);
+
+    list_tail=new_rec;
+  }
+
+  /* Succeeded. Move new allocations into the correct talloc context */
+  talloc_steal(mem_ctx, update);
+  rc=TRP_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  if (rc!=TRP_SUCCESS)
+    return NULL;
+  return update;
+}
+
+static json_t *tr_msg_encode_trp_req(TRP_REQ *req)
+{
+  json_t *jbody=NULL;
+  json_t *jstr=NULL;
+  char *s=NULL;
+
+  if (req==NULL)
+    return NULL;
+
+  jbody=json_object();
+  if (jbody==NULL)
+    return NULL;
+
+  if ((NULL==trp_req_get_comm(req))
+     || (NULL==trp_req_get_realm(req))) {
+    json_decref(jbody);
+    return NULL;
+  }
+
+  s=tr_name_strdup(trp_req_get_comm(req)); /* ensures null termination */
+  if (s==NULL) {
+    json_decref(jbody);
+    return NULL;
+  }
+  jstr=json_string(s);
+  free(s); s=NULL;
+  if (jstr==NULL) {
+    json_decref(jbody);
+    return NULL;
+  }
+  json_object_set_new(jbody, "community", jstr);
+    
+  s=tr_name_strdup(trp_req_get_realm(req)); /* ensures null termination */
+  if (s==NULL) {
+    json_decref(jbody);
+    return NULL;
+  }
+  jstr=json_string(s);
+  free(s); s=NULL;
+  if (jstr==NULL) {
+    json_decref(jbody);
+    return NULL;
+  }
+  json_object_set_new(jbody, "realm", jstr);
+
+  return jbody;
+}
+
+static TRP_REQ *tr_msg_decode_trp_req(TALLOC_CTX *mem_ctx, json_t *jreq)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_REQ *req=NULL;
+  char *s=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  /* check message type and body type for agreement */
+  req=trp_req_new(tmp_ctx);
+  if (req==NULL) {
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  rc=tr_msg_get_json_string(jreq, "community", &s, tmp_ctx);
+  if (rc!=TRP_SUCCESS)
+    goto cleanup;
+  trp_req_set_comm(req, tr_new_name(s));
+  talloc_free(s); s=NULL;
+
+  rc=tr_msg_get_json_string(jreq, "realm", &s, tmp_ctx);
+  if (rc!=TRP_SUCCESS)
+    goto cleanup;
+  trp_req_set_realm(req, tr_new_name(s));
+  talloc_free(s); s=NULL;
+
+  rc=TRP_SUCCESS;
+  talloc_steal(mem_ctx, req);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  if (rc!=TRP_SUCCESS)
+    return NULL;
+  return req;
+}
+
 char *tr_msg_encode(TR_MSG *msg) 
 {
   json_t *jmsg;
   json_t *jmsg_type;
+  char *encoded;
 
   /* TBD -- add error handling */
   jmsg = json_object();
@@ -487,23 +893,35 @@ char *tr_msg_encode(TR_MSG *msg)
       json_object_set_new(jmsg, "msg_body", tr_msg_encode_tidresp(tr_msg_get_resp(msg)));
       break;
 
-      /* TBD -- Add TR message types */
+    case TRP_UPDATE:
+      jmsg_type = json_string("trp_update");
+      json_object_set_new(jmsg, "msg_type", jmsg_type);
+      json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_upd(tr_msg_get_trp_upd(msg)));
+      break;
+
+    case TRP_REQUEST:
+      jmsg_type = json_string("trp_request");
+      json_object_set_new(jmsg, "msg_type", jmsg_type);
+      json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_req(tr_msg_get_trp_req(msg)));
+      break;
 
     default:
       json_decref(jmsg);
       return NULL;
     }
-  
-  return(json_dumps(jmsg, 0));
+
+  encoded=json_dumps(jmsg, 0);
+  json_decref(jmsg);
+  return encoded;
 }
 
 TR_MSG *tr_msg_decode(char *jbuf, size_t buflen)
 {
-  TR_MSG *msg;
+  TR_MSG *msg=NULL;
   json_t *jmsg = NULL;
   json_error_t rc;
-  json_t *jtype;
-  json_t *jbody;
+  json_t *jtype=NULL;
+  json_t *jbody=NULL;
   const char *mtype = NULL;
 
   if (NULL == (jmsg = json_loadb(jbuf, buflen, JSON_DISABLE_EOF_CHECK, &rc))) {
@@ -537,6 +955,14 @@ TR_MSG *tr_msg_decode(char *jbuf, size_t buflen)
     msg->msg_type = TID_RESPONSE;
     tr_msg_set_resp(msg, tr_msg_decode_tidresp(jbody));
   }
+  else if (0 == strcmp(mtype, "trp_update")) {
+    msg->msg_type = TRP_UPDATE;
+    tr_msg_set_trp_upd(msg, tr_msg_decode_trp_upd(NULL, jbody)); /* null talloc context for now */
+  }
+  else if (0 == strcmp(mtype, "trp_request")) {
+    msg->msg_type = TRP_UPDATE;
+    tr_msg_set_trp_req(msg, tr_msg_decode_trp_req(NULL, jbody)); /* null talloc context for now */
+  }
   else {
     msg->msg_type = TR_UNKNOWN;
     msg->msg_rep = NULL;
@@ -552,8 +978,22 @@ void tr_msg_free_encoded(char *jmsg)
 
 void tr_msg_free_decoded(TR_MSG *msg)
 {
-  if (msg)
+  if (msg) {
+    switch (msg->msg_type) {
+    case TID_REQUEST:
+      tid_req_free(tr_msg_get_req(msg));
+      break;
+    case TID_RESPONSE:
+      tid_resp_free(tr_msg_get_resp(msg));
+      break;
+    case TRP_UPDATE:
+      trp_upd_free(tr_msg_get_trp_upd(msg));
+      break;
+    case TRP_REQUEST:
+      trp_req_free(tr_msg_get_trp_req(msg));
+    default:
+      break;
+    }
     free (msg);
+  }
 }
-
-
index dccf0d4..8837b02 100644 (file)
@@ -55,6 +55,9 @@ TR_NAME *tr_new_name (const char *name)
     new->len = strlen(name);
     if (new->buf = malloc((new->len)+1)) {
       strcpy(new->buf, name);
+    } else {
+      free(new);
+      new=NULL;
     }
   }
   return new;
@@ -80,12 +83,22 @@ TR_NAME *tr_dup_name (TR_NAME *from)
 
 int tr_name_cmp(TR_NAME *one, TR_NAME *two)
 {
-  if (one->len != two->len)
-    return 1;
-  else {
-    /* lengths equal */
-    return strncmp(one->buf, two->buf, one->len);
+  int len=one->len;
+  int cmp=0;
+
+  if (two->len<one->len)
+    len=two->len; /* len now min(one->len,two->len) */
+
+  cmp=strncmp(one->buf, two->buf, len);
+  if (cmp==0) {
+    if (one->len<two->len)
+      return -1;
+    else if (one->len==two->len)
+      return 0;
+    else
+      return 1;
   }
+  return cmp;
 }
 
 void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len)
@@ -109,5 +122,3 @@ char * tr_name_strdup(TR_NAME *src)
   }
   return s;
 }
-
-
index 7244cbd..b7d3562 100644 (file)
  *
  */
 
+#include <tr.h>
 #include <trust_router/tr_name.h>
-#include <tr_rp.h>
 #include <tr_config.h>
-#include <tr.h>
+#include <tr_rp.h>
 #include <tr_debug.h>
 
-TR_RP_CLIENT *tr_rp_client_lookup(TR_INSTANCE *tr, TR_NAME *gss_name) {
+TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name) {
   TR_RP_CLIENT *rp = NULL;
   int i = 0;
 
-  if ((!tr) || (!tr->active_cfg) || (!gss_name)) {
+  if ((!rp_clients) || (!gss_name)) {
     tr_debug("tr_rp_client_lookup: Bad parameters.");
     return NULL;
   }
 
-  for (rp = tr->active_cfg->rp_clients; NULL != rp; rp = rp->next) {
+  for (rp = rp_clients; NULL != rp; rp = rp->next) {
     for (i = 0; ((i < TR_MAX_GSS_NAMES) && (NULL != (rp->gss_names[i]))); i++) {
        if (!tr_name_cmp(gss_name, rp->gss_names[i])) {
        return rp;
index c8b1d47..2a89f00 100644 (file)
@@ -28,6 +28,7 @@ AC_CHECK_LIB([sqlite3], [sqlite3_open],,
     [AC_MSG_ERROR([Please install sqlite3 development])])
 AC_CHECK_LIB([jansson], [json_object])
 AC_CHECK_LIB([crypto], [DH_new])
-AC_CHECK_HEADERS(gssapi.h gssapi_ext.h jansson.h talloc.h openssl/dh.h openssl/bn.h syslog.h)
+AC_CHECK_LIB([event], [event_base_new])
+AC_CHECK_HEADERS(gssapi.h gssapi_ext.h jansson.h talloc.h openssl/dh.h openssl/bn.h syslog.h event2/event.h)
 AC_CONFIG_FILES([Makefile gsscon/Makefile])
 AC_OUTPUT
index 0b03926..f8dc166 100755 (executable)
@@ -199,7 +199,6 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
         OM_uint32 requestedFlags = (GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | 
                                     GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
         
-       // printf ("Calling gss_init_sec_context...\n");
         majorStatus = gss_init_sec_context (&minorStatus, 
                                             clientCredentials, 
                                             &gssContext, 
index c391698..b8d4fcf 100755 (executable)
@@ -52,8 +52,6 @@
  * or implied warranty.
  */
 
-#include <signal.h>
-
 #include <gsscon.h>
 
 /* --------------------------------------------------------------------------- */
@@ -135,11 +133,7 @@ static int WriteBuffer (int         inSocket,
         do {
             ssize_t count;
 
-            /* disable the SIGPIPE signal while we write so that we can handle a
-             * broken pipe error gracefully */
-            signal(SIGPIPE, SIG_IGN); /* temporarily disable */
             count = write (inSocket, ptr, inBufferLength - bytesWritten);
-            signal(SIGPIPE, SIG_DFL); /* reenable */
 
             if (count < 0) {
                 /* Try again on EINTR */
@@ -177,15 +171,16 @@ int gsscon_read_token (int      inSocket,
     if (!err) {
        tokenLength = ntohl (tokenLength);
        token = malloc (tokenLength);
-       memset (token, 0, tokenLength); 
+        if (token==NULL) {
+          err=EIO;
+        } else {
+          memset (token, 0, tokenLength); 
         
-       err = ReadBuffer (inSocket, tokenLength, token);
+          err = ReadBuffer (inSocket, tokenLength, token);
+        }
     }
     
     if (!err) {
-    //    printf ("Read token:\n");
-    //    PrintBuffer (token, tokenLength);
-        
        *outTokenLength = tokenLength;
         *outTokenValue = token;        
         token = NULL; /* only free on error */
index 9cf0695..1ede6d3 100755 (executable)
@@ -73,17 +73,16 @@ int gsscon_passive_authenticate (int                    inSocket,
     size_t inputTokenBufferLength = 0;
     gss_buffer_desc inputToken;  /* buffer received from the server */
     
-    printf("In gsscon_passive_authenticate(), inNameBuffer = %s\n", inNameBuffer.value);
-
     if (inSocket <  0 ) { err = EINVAL; }
     if (!outGSSContext) { err = EINVAL; }
 
-    if (!err)
+    if (!err) {
       majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
-    if (majorStatus != GSS_S_COMPLETE) {
+      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,
index 8502185..f415da6 100644 (file)
@@ -35,8 +35,9 @@
 #ifndef TID_INTERNAL_H
 #define TID_INTERNAL_H
 #include <glib.h>
-#include <trust_router/tid.h>
 
+#include <tr_rp.h>
+#include <trust_router/tid.h>
 #include <jansson.h>
 
 struct tid_srvr_blk {
@@ -94,9 +95,10 @@ 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 */
 };
 
 
index 61adc36..bd55176 100644 (file)
 #ifndef TR_H
 #define TR_H
 
+#include <talloc.h>
+
 #include <tid_internal.h>
 #include <trust_router/tr_name.h>
 #include <tr_msg.h>
 #include <tr_rp.h>
+#include <tr_trp.h>
+#include <tr_cfgwatch.h>
+#include <tr_config.h>
 
-typedef struct tr_instance {
-  struct tr_cfg *new_cfg;      /* unapplied configuration */
-  struct tr_cfg *active_cfg;
-  TIDS_INSTANCE *tids;
-  struct tr_rp_client *rp_gss;         /* Client matching GSS name, TBD -- FIX ME */
-} TR_INSTANCE;
+/* struct defined in tr_trp.h */
+typedef struct tr_instance TR_INSTANCE;
 
-TR_INSTANCE *tr_create(void);
+TR_INSTANCE *tr_create(TALLOC_CTX *mem_ctx);
 void tr_destroy(TR_INSTANCE *tr);
 
 #endif
index a417459..08f9c34 100644 (file)
 #ifndef TR_APC_H
 #define TR_APC_H
 
+#include <talloc.h>
+
+#include <trust_router/tr_name.h>
+
 /* Used to hold lists of APC names in cfg. */
 typedef struct tr_apc {
   struct tr_apc *next;
   TR_NAME *id;
 } TR_APC;
 
+TR_APC *tr_apc_new(TALLOC_CTX *mem_ctx);
+void tr_apc_free(TR_APC *apc);
+void tr_apc_set_id(TR_APC *apc, TR_NAME *id);
+TR_NAME *tr_apc_get_id(TR_APC *apc);
+
 #endif
 
 
diff --git a/include/tr_cfgwatch.h b/include/tr_cfgwatch.h
new file mode 100644 (file)
index 0000000..3d26048
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef TR_CFGWATCH_H
+#define TR_CFGWATCH_H
+
+#include <talloc.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <tr_config.h>
+#include <tr_event.h>
+/* interval in seconds */
+#define TR_CFGWATCH_DEFAULT_POLL 1
+#define TR_CFGWATCH_DEFAULT_SETTLE 5
+/* note: settling time is minimum - only checked on poll intervals */
+
+struct tr_fstat {
+  char *name;
+  struct timespec mtime;
+};
+
+typedef struct tr_cfgwatch_data {
+  struct timeval poll_interval; /* how often should we check for updates? */
+  struct timeval settling_time; /* how long should we wait for changes to settle before updating? */
+  char *config_dir; /* what directory are we watching? */
+  struct tr_fstat *fstat_list; /* file names and mtimes */
+  int n_files; /* number of files in fstat_list */
+  int change_detected; /* have we detected a change? */
+  struct timeval last_change_detected; /* when did we last note a changed mtime? */
+  TR_CFG_MGR *cfg_mgr; /* what trust router config are we updating? */
+  void (*update_cb)(TR_CFG *new_cfg, void *cookie); /* callback after config updated */
+  void *update_cookie; /* data for the update_cb() */
+} TR_CFGWATCH;
+
+
+/* prototypes */
+TR_CFGWATCH *tr_cfgwatch_create(TALLOC_CTX *mem_ctx);
+int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch);
+int tr_cfgwatch_event_init(struct event_base *base, TR_CFGWATCH *cfg_status, struct event **cfgwatch_ev);
+
+#endif /* TR_CFGWATCH_H */
index 374227b..46b9f8b 100644 (file)
@@ -55,7 +55,7 @@ typedef struct tr_comm {
   time_t expiration_interval; /*Minutes to key expiration; only valid for an APC*/
 } TR_COMM;
 
-TR_COMM *tr_comm_lookup(TR_INSTANCE *tr, TR_NAME *comm);
+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);
 
index 8a04415..89777fd 100644 (file)
 #include <dirent.h>
 #include <jansson.h>
 #include <syslog.h>
+#include <sys/time.h>
+#include <talloc.h>
 
-#include <tr.h>
+#include <tr_comm.h>
 #include <tr_rp.h>
 #include <tr_idp.h>
-#include <tr_comm.h>
+#include <trp_internal.h>
 
 #define TR_DEFAULT_MAX_TREE_DEPTH 12
 #define TR_DEFAULT_TR_PORT 12308
 #define TR_DEFAULT_TIDS_PORT 12309
+#define TR_DEFAULT_TRPS_PORT 12310
 #define TR_DEFAULT_LOG_THRESHOLD LOG_INFO
 #define TR_DEFAULT_CONSOLE_THRESHOLD LOG_NOTICE
-
+#define TR_DEFAULT_TRP_CONNECT_INTERVAL 10
+#define TR_DEFAULT_TRP_UPDATE_INTERVAL 120
+#define TR_DEFAULT_TRP_SWEEP_INTERVAL 30
 typedef enum tr_cfg_rc {
   TR_CFG_SUCCESS = 0,  /* No error */
   TR_CFG_ERROR,                /* General processing error */
   TR_CFG_BAD_PARAMS,   /* Bad parameters passed to tr_config function */
   TR_CFG_NOPARSE,      /* Parsing error */
-  TR_CFG_NOMEM         /* Memory allocation error */
+  TR_CFG_NOMEM,                /* Memory allocation error */
 } TR_CFG_RC;
 
 typedef struct tr_cfg_internal {
   unsigned int max_tree_depth;
   unsigned int tids_port;
+  unsigned int trps_port;
   const char *hostname;
   int log_threshold;
   int console_threshold;
+  unsigned int cfg_poll_interval;
+  unsigned int cfg_settling_time;
+  unsigned int trp_sweep_interval;
+  unsigned int trp_update_interval;
+  unsigned int trp_connect_interval;
 } TR_CFG_INTERNAL;
 
 typedef struct tr_cfg {
@@ -74,24 +85,29 @@ typedef struct tr_cfg {
   TR_COMM *comms;                      /* locally-known communities */
   TR_AAA_SERVER *default_servers;      /* default server list */
   /* TBD -- Global Filters */
-  /* TBD -- Trust Router Peers */
-  /* TBD -- Trust Links */
 } TR_CFG;
 
+typedef struct tr_cfg_mgr {
+  TR_CFG *active;
+  TR_CFG *new;
+} TR_CFG_MGR;
+
 int tr_find_config_files (const char *config_dir, struct dirent ***cfg_files);
 void tr_free_config_file_list(int n, struct dirent ***cfg_files);
-TR_CFG_RC tr_parse_config (TR_INSTANCE *tr, const char *config_dir, int n, struct dirent **cfg_files);
-TR_CFG_RC tr_apply_new_config (TR_INSTANCE *tr);
+TR_CFG_RC tr_parse_config (TR_CFG_MGR *cfg_mgr, const char *config_dir, int n, struct dirent **cfg_files);
+TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr);
 TR_CFG_RC tr_cfg_validate (TR_CFG *trc);
+TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx);
+TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx);
 void tr_cfg_free(TR_CFG *cfg);
+void tr_cfg_mgr_free(TR_CFG_MGR *cfg);
 
 void tr_print_config(TR_CFG *cfg);
 void tr_print_comms(TR_COMM *comm_list);
 void tr_print_comm_idps(TR_IDP_REALM *idp_list);
 void tr_print_comm_rps(TR_RP_REALM *rp_list);
 
-TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc);
-TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc);
-TR_RP_CLIENT *tr_rp_client_lookup(TR_INSTANCE *tr, TR_NAME *gss_name);
+TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *cfg, TR_NAME *idp_id, TR_CFG_RC *rc);
+TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *cfg, TR_NAME *rp_gss, TR_CFG_RC *rc);
 
 #endif
index c6de406..2400ba2 100644 (file)
@@ -50,6 +50,7 @@
 #define tr_info(...)    tr_log(LOG_INFO, __VA_ARGS__)
 #define tr_debug(...)   tr_log(LOG_DEBUG, __VA_ARGS__)
 
+TR_EXPORT const char *sev2str(int sev);
 TR_EXPORT int str2sev(const char *sev);
 TR_EXPORT void tr_log_threshold(const int sev);
 TR_EXPORT void tr_console_threshold(const int sev);
diff --git a/include/tr_event.h b/include/tr_event.h
new file mode 100644 (file)
index 0000000..5fab89c
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef TR_EVENT_H
+#define TR_EVENT_H
+
+#include <event2/event.h>
+
+/* 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 */
+};
+
+/* prototypes */
+struct event_base *tr_event_loop_init(void);
+int tr_event_loop_run(struct event_base *base);
+
+#endif /* TR_EVENT_H */
index 0eba315..9e56b98 100644 (file)
@@ -35,8 +35,9 @@
 #ifndef TR_IDP_H
 #define TR_IDP_H
 
+#include <talloc.h>
+
 #include <trust_router/tr_name.h>
-#include <tr.h>
 #include <tr_apc.h>
 
 typedef struct tr_aaa_server {
@@ -44,6 +45,14 @@ typedef struct tr_aaa_server {
   TR_NAME *hostname;
 } TR_AAA_SERVER;
 
+/* may also want to use in tr_rp.h */
+typedef enum tr_realm_origin {
+  TR_REALM_LOCAL=0, /* realm we were configured to contact */
+  TR_REALM_REMOTE_INCOMPLETE, /* realm we were configured to know about, without contact info yet */
+  TR_REALM_REMOTE, /* realm we were configured to know about, with discovered contact info */
+  TR_REALM_DISCOVERED /* realm we learned about from a peer */
+} TR_REALM_ORIGIN;
+
 typedef struct tr_idp_realm {
   struct tr_idp_realm *next;
   struct tr_idp_realm *comm_next; /* for linked list in comm config */
@@ -51,8 +60,14 @@ typedef struct tr_idp_realm {
   int shared_config;
   TR_AAA_SERVER *aaa_servers;
   TR_APC *apcs;
+  TR_REALM_ORIGIN origin; /* how did we learn about this realm? */
 } TR_IDP_REALM;
   
-TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_INSTANCE *tr, TR_NAME *idp_realm, TR_NAME *comm);
-TR_AAA_SERVER *tr_default_server_lookup(TR_INSTANCE *tr, TR_NAME *comm);
+TR_IDP_REALM *tr_idp_realm_new(TALLOC_CTX *mem_ctx);
+
+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 *tr_default_server_lookup(TR_AAA_SERVER *default_servers, TR_NAME *comm);
 #endif
diff --git a/include/tr_mq.h b/include/tr_mq.h
new file mode 100644 (file)
index 0000000..0dd7f38
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef _TR_MQ_H_
+#define _TR_MQ_H_
+
+#include <talloc.h>
+#include <pthread.h>
+
+/* Note on mq priorities: High priority messages are guaranteed to be
+ * processed before any normal priority messages. Otherwise, messages
+ * will be processed in the order they are added to the queue. */
+
+typedef enum tr_mq_priority {
+  TR_MQ_PRIO_NORMAL=0,
+  TR_MQ_PRIO_HIGH
+} TR_MQ_PRIORITY;
+
+/* msg for inter-thread messaging */
+typedef struct tr_mq_msg TR_MQ_MSG;
+struct tr_mq_msg {
+  TR_MQ_MSG *next;
+  TR_MQ_PRIORITY prio;
+  char *message;
+  void *p; /* payload */
+  void (*p_free)(void *); /* function to free payload */
+};
+
+/* message queue for inter-thread messaging */
+
+typedef struct tr_mq TR_MQ;
+typedef void (*TR_MQ_NOTIFY_FN)(TR_MQ *, void *);
+struct tr_mq {
+  pthread_mutex_t mutex;
+  TR_MQ_MSG *head;
+  TR_MQ_MSG *tail;
+  TR_MQ_MSG *last_hi_prio;
+  TR_MQ_NOTIFY_FN notify_cb; /* callback when queue becomes non-empty */
+  void *notify_cb_arg;
+};
+
+/* message string for sending trpc messages */
+#define TR_MQMSG_TRPC_SEND "trpc send msg"
+
+TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *msg, TR_MQ_PRIORITY prio);
+void tr_mq_msg_free(TR_MQ_MSG *msg);
+TR_MQ_PRIORITY tr_mq_msg_get_prio(TR_MQ_MSG *msg);
+const char *tr_mq_msg_get_message(TR_MQ_MSG *msg);
+void *tr_mq_msg_get_payload(TR_MQ_MSG *msg);
+void tr_mq_msg_set_payload(TR_MQ_MSG *msg, void *p, void (*p_free)(void *));
+
+
+TR_MQ *tr_mq_new(TALLOC_CTX *mem_ctx);
+void tr_mq_free(TR_MQ *mq);
+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);
+void tr_mq_clear(TR_MQ *mq);
+#endif /*_TR_MQ_H_ */
index 4f208e5..0a9096f 100644 (file)
 
 #include <jansson.h>
 #include <trust_router/tid.h>
-
+#include <trust_router/trp.h>
 typedef struct tr_msg TR_MSG;
 
 enum msg_type {
   TR_UNKNOWN = 0,
   TID_REQUEST,
-  TID_RESPONSE
+  TID_RESPONSE,
+  TRP_UPDATE,
+  TRP_REQUEST
 };
 
 /* Union of TR message types to hold message of any type. */
@@ -59,6 +61,11 @@ TID_REQ *tr_msg_get_req(TR_MSG *msg);
 void tr_msg_set_req(TR_MSG *msg, TID_REQ *req);
 TID_RESP *tr_msg_get_resp(TR_MSG *msg);
 void tr_msg_set_resp(TR_MSG *msg, TID_RESP *resp);
+TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg);
+void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *req);
+TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg);
+void tr_msg_set_trp_req(TR_MSG *msg, TRP_REQ *req);
+
 
 /* Encoders/Decoders */
 char *tr_msg_encode(TR_MSG *msg);
index 43a3b58..eec1d55 100644 (file)
@@ -35,7 +35,6 @@
 #ifndef TR_RP_H
 #define TR_RP_H
 
-#include <tr.h>
 #include <tr_filter.h>
 
 #define TR_MAX_GSS_NAMES 5
@@ -53,4 +52,7 @@ typedef struct tr_rp_realm {
   TR_NAME *realm_name;
 } TR_RP_REALM;
 
+/* prototypes */
+TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name);
+
 #endif
diff --git a/include/tr_tid.h b/include/tr_tid.h
new file mode 100644 (file)
index 0000000..fd01a10
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef TR_TID_H
+#define TR_TID_H
+
+#include <tr_event.h>
+#include <tr_config.h>
+
+int tr_tids_event_init(struct event_base *base,
+                       TIDS_INSTANCE *tids,
+                       TR_CFG_MGR *cfg_mgr,
+                       struct tr_socket_event *tids_ev);
+
+#endif /* TR_TID_H */
diff --git a/include/tr_trp.h b/include/tr_trp.h
new file mode 100644 (file)
index 0000000..d597cb0
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef TR_TRP_H
+#define TR_TRP_H
+
+#include <event2/event.h>
+#include <talloc.h>
+#include <pthread.h>
+
+#include <tr.h>
+#include <trp_internal.h>
+#include <tr_config.h>
+#include <tr_cfgwatch.h>
+#include <tr_event.h>
+
+typedef struct tr_trps_events {
+  struct event *trps_ev;
+  struct tr_socket_event *listen_ev;
+  struct event *mq_ev;
+  struct event *connect_ev;
+  struct event *update_ev;
+  struct event *sweep_ev;
+} TR_TRPS_EVENTS;
+
+/* typedef'ed as TR_INSTANCE in tr.h */
+struct tr_instance {
+  TR_CFG_MGR *cfg_mgr;
+  TIDS_INSTANCE *tids;
+  TRPS_INSTANCE *trps;
+  TR_CFGWATCH *cfgwatch;
+  TR_TRPS_EVENTS *events;
+};
+
+/* messages between threads */
+#define TR_MQMSG_MSG_RECEIVED "msg received"
+#define TR_MQMSG_TRPC_DISCONNECTED "trpc disconnected"
+#define TR_MQMSG_TRPC_CONNECTED "trpc connected"
+#define TR_MQMSG_TRPS_DISCONNECTED "trps disconnected"
+#define TR_MQMSG_TRPS_CONNECTED "trps connected"
+#define TR_MQMSG_ABORT "abort"
+
+/* prototypes */
+TRP_RC tr_trps_event_init(struct event_base *base, struct tr_instance *tr);
+TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg);
+TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer, struct event *ev);
+void tr_config_changed(TR_CFG *new_cfg, void *cookie);
+TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps, struct event *ev);
+void tr_peer_status_change(TRP_PEER *peer, void *cookie);
+#endif /* TR_TRP_H */
diff --git a/include/trp_internal.h b/include/trp_internal.h
new file mode 100644 (file)
index 0000000..ca870c1
--- /dev/null
@@ -0,0 +1,203 @@
+#ifndef TRP_INTERNAL_H
+#define TRP_INTERNAL_H
+
+#include <pthread.h>
+#include <talloc.h>
+
+#include <gsscon.h>
+#include <tr_mq.h>
+#include <tr_msg.h>
+#include <trp_ptable.h>
+#include <trp_rtable.h>
+#include <trust_router/trp.h>
+
+/* 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 metric;
+  unsigned int interval;
+} TRP_INFOREC_ROUTE;
+
+/* TODO: define struct trp_msg_info_community */
+
+typedef union trp_inforec_data {
+  TRP_INFOREC_ROUTE *route;
+  /* 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 */
+};
+
+struct trp_update {
+  TRP_INFOREC *records;
+  TR_NAME *peer; /* who did this update come from? */
+};
+
+struct trp_req {
+  TR_NAME *comm;
+  TR_NAME *realm;
+  TR_NAME *peer; /* who did this req come from? */
+};
+
+
+typedef struct trps_instance TRPS_INSTANCE;
+
+typedef enum trp_connection_status {
+  TRP_CONNECTION_CLOSED=0,
+  TRP_CONNECTION_DOWN,  
+  TRP_CONNECTION_AUTHORIZING,  
+  TRP_CONNECTION_UP,
+  TRP_CONNECTION_UNKNOWN,
+} TRP_CONNECTION_STATUS;
+
+typedef struct trp_connection TRP_CONNECTION;
+struct trp_connection {
+  pthread_mutex_t mutex; /* protects status attribute */
+  TRP_CONNECTION *next;
+  pthread_t *thread; /* thread servicing this connection */
+  int fd;
+  TR_NAME *gssname;
+  TR_NAME *peer; /* TODO: why is there a peer and a gssname? jlr */
+  gss_ctx_id_t *gssctx;
+  TRP_CONNECTION_STATUS status;
+  void (*status_change_cb)(TRP_CONNECTION *conn, void *cookie);
+  void *status_change_cookie;
+};
+
+typedef TRP_RC (*TRPS_MSG_FUNC)(TRPS_INSTANCE *, TRP_CONNECTION *, TR_MSG *);
+typedef void (*TRP_RESP_FUNC)();
+/*typedef int (*TRP_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie);*/
+typedef client_cb_fn TRP_AUTH_FUNC;
+
+/* function to look up comm/realm entries */
+typedef TRP_ROUTE *(TRP_LOOKUP_FUNC)(TR_NAME *, TR_NAME *, void *);
+
+
+/* TRP Client Instance Data */
+typedef struct trpc_instance TRPC_INSTANCE;
+struct trpc_instance {
+  TRPC_INSTANCE *next;
+  TR_NAME *gssname;
+  char *server;
+  unsigned int port;
+  TRP_CONNECTION *conn;
+  TR_MQ *mq; /* msgs from master to trpc */
+};
+
+/* TRP Server Instance Data */
+struct trps_instance {
+  char *hostname;
+  unsigned int port;
+  TRP_AUTH_FUNC auth_handler;
+  TRPS_MSG_FUNC msg_handler;
+  void *cookie;
+  TRP_CONNECTION *conn; /* connections from peers */
+  TRPC_INSTANCE *trpc; /* connections to peers */
+  TR_MQ *mq; /* incoming message queue */
+  TRP_PTABLE *ptable; /* peer table */
+  TRP_RTABLE *rtable; /* route 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 */
+};
+
+typedef enum trp_update_type {
+  TRP_UPDATE_SCHEDULED=0,
+  TRP_UPDATE_TRIGGERED,
+  TRP_UPDATE_REQUESTED
+} TRP_UPDATE_TYPE;
+
+TRP_CONNECTION *trp_connection_new(TALLOC_CTX *mem_ctx);
+void trp_connection_free(TRP_CONNECTION *conn);
+void trp_connection_close(TRP_CONNECTION *conn);
+int trp_connection_lock(TRP_CONNECTION *conn);
+int trp_connection_unlock(TRP_CONNECTION *conn);
+int trp_connection_get_fd(TRP_CONNECTION *conn);
+void trp_connection_set_fd(TRP_CONNECTION *conn, int fd);
+TR_NAME *trp_connection_get_peer(TRP_CONNECTION *conn);
+TR_NAME *trp_connection_get_gssname(TRP_CONNECTION *conn);
+void trp_connection_set_gssname(TRP_CONNECTION *conn, TR_NAME *gssname);
+gss_ctx_id_t *trp_connection_get_gssctx(TRP_CONNECTION *conn);
+void trp_connection_set_gssctx(TRP_CONNECTION *conn, gss_ctx_id_t *gssctx);
+TRP_CONNECTION_STATUS trp_connection_get_status(TRP_CONNECTION *conn);
+pthread_t *trp_connection_get_thread(TRP_CONNECTION *conn);
+void trp_connection_set_thread(TRP_CONNECTION *conn, pthread_t *thread);
+TRP_CONNECTION *trp_connection_get_next(TRP_CONNECTION *conn);
+TRP_CONNECTION *trp_connection_remove(TRP_CONNECTION *conn, TRP_CONNECTION *remove);
+void trp_connection_append(TRP_CONNECTION *conn, TRP_CONNECTION *new);
+int trp_connection_auth(TRP_CONNECTION *conn, TRP_AUTH_FUNC auth_callback, void *callback_data);
+TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gssname);
+TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int port);
+
+TRPC_INSTANCE *trpc_new (TALLOC_CTX *mem_ctx);
+void trpc_free (TRPC_INSTANCE *trpc);
+TRP_CONNECTION *trpc_get_conn(TRPC_INSTANCE *trpc);
+void trpc_set_conn(TRPC_INSTANCE *trpc, TRP_CONNECTION *conn);
+TRPC_INSTANCE *trpc_get_next(TRPC_INSTANCE *trpc);
+void trpc_set_next(TRPC_INSTANCE *trpc, TRPC_INSTANCE *next);
+TRPC_INSTANCE *trpc_remove(TRPC_INSTANCE *trpc, TRPC_INSTANCE *remove);
+void trpc_append(TRPC_INSTANCE *trpc, TRPC_INSTANCE *new);
+char *trpc_get_server(TRPC_INSTANCE *trpc);
+void trpc_set_server(TRPC_INSTANCE *trpc, char *server);
+TR_NAME *trpc_get_gssname(TRPC_INSTANCE *trpc);
+void trpc_set_gssname(TRPC_INSTANCE *trpc, TR_NAME *gssname);
+unsigned int trpc_get_port(TRPC_INSTANCE *trpc);
+void trpc_set_port(TRPC_INSTANCE *trpc, unsigned int port);
+TRP_CONNECTION_STATUS trpc_get_status(TRPC_INSTANCE *trpc);
+TR_MQ *trpc_get_mq(TRPC_INSTANCE *trpc);
+void trpc_set_mq(TRPC_INSTANCE *trpc, TR_MQ *mq);
+void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg);
+TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc);
+void trpc_mq_clear(TRPC_INSTANCE *trpc);
+void trpc_master_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg);
+TR_MQ_MSG *trpc_master_mq_pop(TRPC_INSTANCE *trpc);
+TRP_RC trpc_connect(TRPC_INSTANCE *trpc);
+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);
+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);
+unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps);
+void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval);
+unsigned int trps_get_update_interval(TRPS_INSTANCE *trps);
+void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval);
+unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps);
+TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer);
+TRP_RC trps_send_msg (TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg);
+void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new);
+void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove);
+void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc);
+void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove);
+int trps_get_listener(TRPS_INSTANCE *trps,
+                      TRPS_MSG_FUNC msg_handler,
+                      TRP_AUTH_FUNC auth_handler,
+                      const char *hostname,
+                      unsigned int port,
+                      void *cookie);
+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);
+void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn);
+TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps);
+TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg);
+TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer);
+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_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);
+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);
+#endif /* TRP_INTERNAL_H */
diff --git a/include/trp_ptable.h b/include/trp_ptable.h
new file mode 100644 (file)
index 0000000..6881ee8
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _TRP_PTABLE_H_
+#define _TRP_PTABLE_H_
+
+#include <time.h>
+#include <talloc.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+
+typedef enum trp_peer_conn_status {
+  PEER_DISCONNECTED=0,
+  PEER_CONNECTED
+} TRP_PEER_CONN_STATUS;
+
+typedef struct trp_peer TRP_PEER;
+struct trp_peer {
+  TRP_PEER *next; /* for making a linked list */
+  char *server;
+  TR_NAME *gssname;
+  TR_NAME *servicename;
+  unsigned int port;
+  unsigned int linkcost;
+  struct timespec last_conn_attempt;
+  TRP_PEER_CONN_STATUS outgoing_status;
+  TRP_PEER_CONN_STATUS incoming_status;
+  void (*conn_status_cb)(TRP_PEER *, void *); /* callback for connected status change */
+  void *conn_status_cookie;
+};
+
+typedef struct trp_ptable {
+  TRP_PEER *head; /* head of a peer table list */
+} TRP_PTABLE;
+
+/* iterator for the peer table */
+typedef TRP_PEER *TRP_PTABLE_ITER;
+
+TRP_PTABLE *trp_ptable_new(TALLOC_CTX *memctx);
+void trp_ptable_free(TRP_PTABLE *ptbl);
+TRP_RC trp_ptable_add(TRP_PTABLE *ptbl, TRP_PEER *newpeer);
+TRP_RC trp_ptable_remove(TRP_PTABLE *ptbl, TRP_PEER *peer);
+TRP_PEER *trp_ptable_find_gssname(TRP_PTABLE *ptbl, TR_NAME *gssname);
+TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename);
+char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm);
+
+TRP_PTABLE_ITER *trp_ptable_iter_new(TALLOC_CTX *mem_ctx);
+TRP_PEER *trp_ptable_iter_first(TRP_PTABLE_ITER *iter, TRP_PTABLE *ptbl);
+TRP_PEER *trp_ptable_iter_next(TRP_PTABLE_ITER *iter);
+void trp_ptable_iter_free(TRP_PTABLE_ITER *iter);
+
+TRP_PEER *trp_peer_new(TALLOC_CTX *memctx);
+void trp_peer_free(TRP_PEER *peer);
+char *trp_peer_get_server(TRP_PEER *peer);
+void trp_peer_set_server(TRP_PEER *peer, char *server);
+void trp_peer_set_gssname(TRP_PEER *peer, TR_NAME *gssname);
+TR_NAME *trp_peer_get_gssname(TRP_PEER *peer);
+TR_NAME *trp_peer_dup_gssname(TRP_PEER *peer);
+TR_NAME *trp_peer_get_servicename(TRP_PEER *peer);
+TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer);
+unsigned int trp_peer_get_port(TRP_PEER *peer);
+void trp_peer_set_port(TRP_PEER *peer, unsigned int port);
+unsigned int trp_peer_get_linkcost(TRP_PEER *peer);
+struct timespec *trp_peer_get_last_conn_attempt(TRP_PEER *peer);
+void trp_peer_set_last_conn_attempt(TRP_PEER *peer, struct timespec *time);
+TRP_PEER_CONN_STATUS trp_peer_get_outgoing_status(TRP_PEER *peer);
+void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status);
+TRP_PEER_CONN_STATUS trp_peer_get_incoming_status(TRP_PEER *peer);
+void trp_peer_set_incoming_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status);
+int trp_peer_is_connected(TRP_PEER *peer);
+void trp_peer_set_linkcost(TRP_PEER *peer, unsigned int linkcost);
+void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *), void *cookie);
+char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep);
+
+#endif /* _TRP_PTABLE_H_ */
diff --git a/include/trp_rtable.h b/include/trp_rtable.h
new file mode 100644 (file)
index 0000000..a8f66be
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef _TRP_RTABLE_H_
+#define _TRP_RTABLE_H_
+
+#include <glib.h>
+#include <talloc.h>
+#include <time.h>
+
+#include <trp_internal.h>
+
+typedef struct trp_route {
+  TR_NAME *comm;
+  TR_NAME *realm;
+  TR_NAME *peer;
+  unsigned int metric;
+  TR_NAME *trust_router;
+  TR_NAME *next_hop;
+  int selected;
+  unsigned int interval; /* interval from route update */
+  struct timespec *expiry;
+  int local; /* is this a local route? */
+  int triggered;
+} TRP_ROUTE;
+
+typedef GHashTable TRP_RTABLE;
+
+TRP_RTABLE *trp_rtable_new(void);
+void trp_rtable_free(TRP_RTABLE *rtbl);
+void trp_rtable_add(TRP_RTABLE *rtbl, TRP_ROUTE *entry); /* adds or updates */
+void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_ROUTE *entry);
+void trp_rtable_clear(TRP_RTABLE *rtbl);
+size_t trp_rtable_size(TRP_RTABLE *rtbl);
+size_t trp_rtable_comm_size(TRP_RTABLE *rtbl, TR_NAME *comm);
+size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm);
+TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out);
+TR_NAME **trp_rtable_get_comms(TRP_RTABLE *rtbl, size_t *n_out);
+TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out);
+TR_NAME **trp_rtable_get_comm_realms(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out);
+TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out);
+TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out);
+TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer);
+TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm);
+void trp_rtable_clear_triggered(TRP_RTABLE *rtbl);
+char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm);
+
+TRP_ROUTE *trp_route_new(TALLOC_CTX *mem_ctx);
+void trp_route_free(TRP_ROUTE *entry);
+void trp_route_set_comm(TRP_ROUTE *entry, TR_NAME *comm);
+TR_NAME *trp_route_get_comm(TRP_ROUTE *entry);
+TR_NAME *trp_route_dup_comm(TRP_ROUTE *entry);
+void trp_route_set_realm(TRP_ROUTE *entry, TR_NAME *realm);
+TR_NAME *trp_route_get_realm(TRP_ROUTE *entry);
+TR_NAME *trp_route_dup_realm(TRP_ROUTE *entry);
+void trp_route_set_trust_router(TRP_ROUTE *entry, TR_NAME *tr);
+TR_NAME *trp_route_get_trust_router(TRP_ROUTE *entry);
+TR_NAME *trp_route_dup_trust_router(TRP_ROUTE *entry);
+void trp_route_set_peer(TRP_ROUTE *entry, TR_NAME *peer);
+TR_NAME *trp_route_get_peer(TRP_ROUTE *entry);
+TR_NAME *trp_route_dup_peer(TRP_ROUTE *entry);
+void trp_route_set_metric(TRP_ROUTE *entry, unsigned int metric);
+unsigned int trp_route_get_metric(TRP_ROUTE *entry);
+void trp_route_set_next_hop(TRP_ROUTE *entry, TR_NAME *next_hop);
+TR_NAME *trp_route_get_next_hop(TRP_ROUTE *entry);
+TR_NAME *trp_route_dup_next_hop(TRP_ROUTE *entry);
+void trp_route_set_selected(TRP_ROUTE *entry, int sel);
+int trp_route_is_selected(TRP_ROUTE *entry);
+void trp_route_set_interval(TRP_ROUTE *entry, int interval);
+int trp_route_get_interval(TRP_ROUTE *entry);
+void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp);
+struct timespec *trp_route_get_expiry(TRP_ROUTE *entry);
+void trp_route_set_local(TRP_ROUTE *entry, int local);
+int trp_route_is_local(TRP_ROUTE *entry);
+void trp_route_set_triggered(TRP_ROUTE *entry, int trig);
+int trp_route_is_triggered(TRP_ROUTE *entry);
+char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep);
+
+#endif /* _TRP_RTABLE_H_ */
index 870d60c..6aa3336 100644 (file)
@@ -35,6 +35,8 @@
 #ifndef TID_H
 #define TID_H
 
+#include <talloc.h>
+
 #include <arpa/inet.h>
 #include <openssl/dh.h>
 
@@ -69,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);
 
 
 
@@ -98,9 +100,12 @@ void tid_req_set_resp_func(TID_REQ *req, TIDC_RESP_FUNC *resp_func);
 TR_EXPORT void *tid_req_get_cookie(TID_REQ *req);
 void tid_req_set_cookie(TID_REQ *req, void *cookie);
 TR_EXPORT TID_REQ *tid_dup_req (TID_REQ *orig_req);
-void TR_EXPORT tid_req_free( TID_REQ *req);
+TR_EXPORT void tid_req_free( TID_REQ *req);
 
 /* Utility functions for TID_RESP structure, in tid/tid_resp.c */
+
+TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx);
+void tid_resp_free(TID_RESP *resp);
 TR_EXPORT int tid_resp_get_result(TID_RESP *resp);
 void tid_resp_set_result(TID_RESP *resp, int result);
 TR_EXPORT TR_NAME *tid_resp_get_err_msg(TID_RESP *resp);
@@ -144,10 +149,14 @@ TR_EXPORT DH *tidc_set_dh(TIDC_INSTANCE *, DH *);
 TR_EXPORT void tidc_destroy (TIDC_INSTANCE *tidc);
 
 /* TID Server functions, in tid/tids.c */
-TR_EXPORT TIDS_INSTANCE *tids_create (void);
+TR_EXPORT TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx);
 TR_EXPORT int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
-                         tids_auth_func *auth_handler, const char *hostname,
+                         TIDS_AUTH_FUNC *auth_handler, const char *hostname, 
+                         unsigned int port, void *cookie);
+TR_EXPORT int tids_get_listener (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
+                         TIDS_AUTH_FUNC *auth_handler, const char *hostname, 
                          unsigned int port, void *cookie);
+TR_EXPORT int tids_accept(TIDS_INSTANCE *tids, int listen);
 TR_EXPORT int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp);
 TR_EXPORT int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg);
 TR_EXPORT void tids_destroy (TIDS_INSTANCE *tids);
diff --git a/include/trust_router/trp.h b/include/trust_router/trp.h
new file mode 100644 (file)
index 0000000..57f0a83
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef TRP_H
+#define TRP_H
+
+#include <talloc.h>
+
+#define TRP_PORT 12308
+#define TRP_METRIC_INFINITY 0xFFFF
+#define TRP_METRIC_INVALID 0xFFFFFFFF
+#define trp_metric_is_finite(x) (((x)<TRP_METRIC_INFINITY) && ((x)!=TRP_METRIC_INVALID))
+#define trp_metric_is_infinite(x) ((x)==TRP_METRIC_INFINITY)
+#define trp_metric_is_valid(x) (((x)<=TRP_METRIC_INFINITY) && ((x)!=TRP_METRIC_INVALID))
+#define trp_metric_is_invalid(x) (((x)>TRP_METRIC_INFINITY) || ((x)==TRP_METRIC_INVALID))
+#define TRP_INTERVAL_INVALID 0
+
+#define TRP_LINKCOST_DEFAULT 1
+
+typedef enum trp_rc {
+  TRP_SUCCESS=0,
+  TRP_ERROR, /* generic error */
+  TRP_NOPARSE, /* parse error */
+  TRP_NOMEM, /* allocation error */
+  TRP_BADTYPE, /* typing error */
+  TRP_UNSUPPORTED, /* unsupported feature */
+  TRP_BADARG, /* bad argument */
+  TRP_CLOCKERR, /* error reading time */
+} 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;
+
+typedef struct trp_inforec TRP_INFOREC;
+
+typedef struct trp_update TRP_UPD;
+typedef struct trp_req TRP_REQ;
+
+/* Functions for TRP_UPD structures */
+TR_EXPORT TRP_UPD *trp_upd_new(TALLOC_CTX *mem_ctx);
+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_peer(TRP_UPD *upd);
+TR_NAME *trp_upd_dup_peer(TRP_UPD *upd);
+void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer);
+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);
+
+/* Functions for TRP_REQ structures */
+TR_EXPORT TRP_REQ *trp_req_new(TALLOC_CTX *mem_ctx);
+TR_EXPORT void trp_req_free(TRP_REQ *req);
+TR_EXPORT TR_NAME *trp_req_get_comm(TRP_REQ *req);
+void trp_req_set_comm(TRP_REQ *req, TR_NAME *comm);
+TR_EXPORT TR_NAME *trp_req_get_realm(TRP_REQ *req);
+void trp_req_set_realm(TRP_REQ *req, TR_NAME *realm);
+TR_EXPORT TR_NAME *trp_req_get_peer(TRP_REQ *req);
+void trp_req_set_peer(TRP_REQ *req, TR_NAME *peer);
+int trp_req_is_wildcard(TRP_REQ *req);
+TRP_RC trp_req_make_wildcard(TRP_REQ *req);
+
+#endif /* TRP_H */
index 89b5d52..c334f41 100644 (file)
@@ -38,6 +38,7 @@
 #include <talloc.h>
 #include <sqlite3.h>
 #include <argp.h>
+#include <poll.h>
 
 #include <tr_debug.h>
 #include <tid_internal.h>
@@ -345,9 +346,10 @@ int main (int argc,
           char *argv[]) 
 {
   TIDS_INSTANCE *tids;
-  int rc = 0;
   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);
@@ -373,17 +375,38 @@ int main (int argc,
                     -1, &authorization_insert, NULL);
 
   /* Create a TID server instance */
-  if (NULL == (tids = tids_create())) {
+  if (NULL == (tids = tids_create(NULL))) {
     tr_crit("Unable to create TIDS instance, exiting.");
     return 1;
   }
 
   tids->ipaddr = opts.ip_address;
 
-  /* Start-up the server, won't return unless there is an error. */
-  rc = tids_start(tids, &tids_req_handler , auth_handler, opts.hostname, TID_PORT, gssname);
-  
-  tr_crit("Error in tids_start(), rc = %d. Exiting.", rc);
+  /* 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 */
+  }
 
   /* Clean-up the TID server instance */
   tids_destroy(tids);
index 1da8338..a068c40 100644 (file)
@@ -55,6 +55,14 @@ static int destroy_tid_req(TID_REQ *req)
       gss_delete_sec_context( &minor, &req->gssctx, NULL);
     }
   }
+  if (req->rp_realm!=NULL)
+    tr_free_name(req->rp_realm);
+  if (req->realm!=NULL)
+    tr_free_name(req->realm);
+  if (req->comm!=NULL)
+    tr_free_name(req->comm);
+  if (req->orig_coi!=NULL)
+    tr_free_name(req->orig_coi);
   return 0;
 }
 
index 2611b3b..3c40b3f 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
+#include <talloc.h>
 
 #include <tid_internal.h>
 
+static int tid_resp_destructor(void *obj)
+{
+  TID_RESP *resp=talloc_get_type_abort(obj, TID_RESP);
+  if (resp->err_msg!=NULL)
+    tr_free_name(resp->err_msg);
+  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);
+  return 0;
+}
+
+TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx)
+{
+  TID_RESP *resp=talloc(mem_ctx, TID_RESP);
+  if (resp!=NULL) {
+    talloc_set_destructor((void *)resp, tid_resp_destructor);
+  }
+  return resp;
+}
+
+void tid_resp_free(TID_RESP *resp)
+{
+  if (resp)
+    talloc_free(resp);
+}
+
 TR_EXPORT int tid_resp_get_result(TID_RESP *resp)
 {
   return(resp->result);
index eb081dd..984cb48 100644 (file)
@@ -35,6 +35,7 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -140,8 +141,9 @@ static int tids_auth_cb(gss_name_t clientName, gss_buffer_t displayName,
 }
 
 /* returns 0 on authorization success, 1 on failure, or -1 in case of error */
-static int tids_auth_connection (struct tids_instance *inst,
-                                int conn, gss_ctx_id_t *gssctx)
+static int tids_auth_connection (TIDS_INSTANCE *inst,
+                                int conn,
+                                 gss_ctx_id_t *gssctx)
 {
   int rc = 0;
   int auth, autherr = 0;
@@ -152,7 +154,7 @@ static int tids_auth_connection (struct tids_instance *inst,
   nameLen = asprintf(&name, "trustidentity@%s", inst->hostname);
   nameBuffer.length = nameLen;
   nameBuffer.value = name;
-  
+
   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);
     return -1;
@@ -343,6 +345,7 @@ static void tids_handle_connection (TIDS_INSTANCE *tids, int conn)
       tr_crit("tids_handle_connection: Error creating response structure.");
       /* try to send an error */
       tids_send_err_response(tids, tr_msg_get_req(mreq), "Error creating response.");
+      tr_msg_free_decoded(mreq);
       return;
     }
 
@@ -360,22 +363,95 @@ static void tids_handle_connection (TIDS_INSTANCE *tids, int conn)
     }
     
     tids_destroy_response(tids, resp);
+    tr_msg_free_decoded(mreq);
     return;
   } 
 }
 
-TIDS_INSTANCE *tids_create (void)
+TIDS_INSTANCE *tids_create (TALLOC_CTX *mem_ctx)
+{
+  return talloc_zero(mem_ctx, 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,
+                      const char *hostname,
+                      unsigned int port,
+                      void *cookie)
 {
-  TIDS_INSTANCE *tids = NULL;
-  if (tids = malloc(sizeof(TIDS_INSTANCE)))
-    memset(tids, 0, sizeof(TIDS_INSTANCE));
-  return tids;
+  int listen = -1;
+
+  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) {
+    /* 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;
+    }
+  }
+
+  if (listen > 0) {
+    /* store the caller's request handler & cookie */
+    tids->req_handler = req_handler;
+    tids->auth_handler = auth_handler;
+    tids->hostname = hostname;
+    tids->cookie = cookie;
+  }
+
+  return listen;
+}
+
+/* Accept and process a connection on a port opened with tids_get_listener() */
+int tids_accept(TIDS_INSTANCE *tids, int listen)
+{
+  int conn=-1;
+  int pid=-1;
+
+  if (0 > (conn = accept(listen, NULL, NULL))) {
+    perror("Error from TIDS Server accept()");
+    return 1;
+  }
+
+  if (0 > (pid = fork())) {
+    perror("Error on fork()");
+    return 1;
+  }
+
+  if (pid == 0) {
+    close(listen);
+    tids_handle_connection(tids, conn);
+    close(conn);
+    exit(0); /* exit to kill forked child process */
+  } else {
+    close(conn);
+  }
+
+  /* clean up any processes that have completed  (TBD: move to main loop?) */
+  while (waitpid(-1, 0, WNOHANG) > 0);
+
+  return 0;
 }
 
 /* Process tids requests forever. Should not return except on error. */
 int tids_start (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)
diff --git a/tr/tr.c b/tr/tr.c
index 09438f7..d79ca76 100644 (file)
--- a/tr/tr.c
+++ b/tr/tr.c
 
 #include <stdlib.h>
 #include <string.h>
+#include <talloc.h>
+#include <tr_config.h>
 #include <tr.h>
 
-TR_INSTANCE *tr_create() {
-  TR_INSTANCE *tr = NULL;
-  if (tr = malloc(sizeof(TR_INSTANCE)))
-    memset(tr, 0, sizeof(TR_INSTANCE));
+TR_INSTANCE *tr_create(TALLOC_CTX *mem_ctx) {
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_INSTANCE *tr=talloc_zero(tmp_ctx, TR_INSTANCE);
+  if (tr != NULL) {
+    tr->cfg_mgr=tr_cfg_mgr_new(tr);
+    if (tr->cfg_mgr != NULL) {
+      /* success, hand over ownership */
+      talloc_steal(mem_ctx, tr);
+    }
+  }
+  talloc_free(tmp_ctx);
   return tr;
 }
 
 void tr_destroy(TR_INSTANCE *tr) {
-  free (tr);
+  talloc_free (tr);
 }
diff --git a/tr/tr_cfgwatch.c b/tr/tr_cfgwatch.c
new file mode 100644 (file)
index 0000000..e6ec7ec
--- /dev/null
@@ -0,0 +1,278 @@
+/***** config file watching *****/
+
+#include <sys/stat.h>
+#include <talloc.h>
+
+#include <tr_config.h>
+#include <tr_debug.h>
+#include <tr_event.h>
+#include <tr_cfgwatch.h>
+
+/* Initialize a new tr_cfgwatch_data struct. Free this with talloc. */
+TR_CFGWATCH *tr_cfgwatch_create(TALLOC_CTX *mem_ctx)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_CFGWATCH *new_cfg;
+  
+  new_cfg=talloc_zero(tmp_ctx, TR_CFGWATCH);
+  if (new_cfg == NULL) {
+    tr_debug("tr_cfgwatch_create: Allocation failed.");
+  } 
+  talloc_steal(mem_ctx, new_cfg);
+  talloc_free(tmp_ctx);
+  return new_cfg;
+}
+
+/* Obtain the file modification time as seconds since epoch. Returns 0 on success. */
+static int tr_get_mtime(const char *path, struct timespec *ts)
+{
+  struct stat file_status;
+
+  if (stat(path, &file_status) != 0) {
+    return -1;
+  } else {
+    (*ts)=file_status.st_mtim;
+  }
+  return 0;
+}
+
+static char *tr_join_paths(TALLOC_CTX *mem_ctx, const char *p1, const char *p2)
+{
+  return talloc_asprintf(mem_ctx, "%s/%s", p1, p2); /* returns NULL on a failure */
+}
+
+static int tr_fstat_namecmp(const void *p1_arg, const void *p2_arg)
+{
+  struct tr_fstat *p1=(struct tr_fstat *) p1_arg;
+  struct tr_fstat *p2=(struct tr_fstat *) p2_arg;
+
+  return strcmp(p1->name, p2->name);
+}
+
+static int tr_fstat_mtimecmp(const void *p1_arg, const void *p2_arg)
+{
+  struct tr_fstat *p1=(struct tr_fstat *) p1_arg;
+  struct tr_fstat *p2=(struct tr_fstat *) p2_arg;
+
+  if (p1->mtime.tv_sec == p2->mtime.tv_sec)
+    return (p1->mtime.tv_nsec) - (p2->mtime.tv_nsec);
+  else
+    return (p1->mtime.tv_sec) - (p2->mtime.tv_sec);
+}
+
+/* Get status of all files in cfg_files. Returns list, or NULL on error.
+ * Files are sorted by filename. 
+ * After success, caller must eventually free result with talloc_free. */
+static struct tr_fstat *tr_fstat_get_all(TALLOC_CTX *mem_ctx,
+                                         const char *config_path,
+                                         struct dirent **cfg_files,
+                                         int n_files)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_fstat *fstat_list=NULL;
+  int ii=0;
+
+  /* create a new fstat list (may be discarded) */
+  fstat_list=talloc_array(tmp_ctx, struct tr_fstat, n_files);
+  if (fstat_list==NULL) {
+    tr_err("tr_fstat_get_all: Could not allocate fstat list.");
+    goto cleanup;
+  }
+
+  for (ii=0; ii<n_files; ii++) {
+    fstat_list[ii].name=talloc_strdup(fstat_list, cfg_files[ii]->d_name);
+    if (0 != tr_get_mtime(tr_join_paths(tmp_ctx, config_path, fstat_list[ii].name),
+                         &(fstat_list[ii].mtime))) {
+      tr_warning("tr_fstat_get_all: Could not obtain mtime for file %s", fstat_list[ii].name);
+    }
+  }
+
+  /* sort the list */
+  qsort(fstat_list, n_files, sizeof(struct tr_fstat), tr_fstat_namecmp);
+
+  /* put list in the caller's context and return it */
+  talloc_steal(mem_ctx, fstat_list);
+ cleanup:
+  talloc_free(tmp_ctx);
+  return fstat_list;
+}
+
+/* Checks whether any config files have appeared/disappeared/modified.
+ * Returns 1 if so, 0 otherwise. */
+static int tr_cfgwatch_update_needed(TR_CFGWATCH *cfg_status)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_fstat *fstat_list=NULL;
+  int n_files=0;
+  int ii=0;
+  struct dirent **cfg_files=NULL;
+  int update_needed=0; /* return value */
+
+  /* get the list, must free cfg_files later with tr_free_cfg_file_list */
+  n_files = tr_find_config_files(cfg_status->config_dir, &cfg_files);
+  if (n_files <= 0) {
+    tr_warning("tr_cfgwatch_update: configuration files disappeared, skipping update.");
+    goto cleanup;
+  }
+
+  /* create a new fstat list (will be discarded) */
+  fstat_list=tr_fstat_get_all(tmp_ctx, cfg_status->config_dir, cfg_files, n_files);
+  if (fstat_list==NULL) {
+    tr_err("tr_cfgwatch_update: Error getting fstat list.");
+    goto cleanup;
+  }
+
+  /* see if the number of files change, if so need to update */
+  if (n_files != cfg_status->n_files) {
+    tr_debug("tr_cfgwatch_update: Changed number of config files (was %d, now %d).",
+             cfg_status->n_files,
+             n_files);
+    update_needed=1;
+    talloc_free(cfg_status->fstat_list);
+    cfg_status->n_files=n_files;
+    cfg_status->fstat_list=fstat_list;
+    talloc_steal(cfg_status, fstat_list);
+    goto cleanup;
+  }
+
+  /* See if any files have a changed mtime. Both are sorted by name so this is easy. */
+  for (ii=0; ii<n_files; ii++) {
+    if ((0 != tr_fstat_mtimecmp(&fstat_list[ii], &cfg_status->fstat_list[ii]))
+       || (0 != tr_fstat_namecmp(&fstat_list[ii], &cfg_status->fstat_list[ii]))){
+      update_needed=1;
+      talloc_free(cfg_status->fstat_list);
+      cfg_status->n_files=n_files;
+      cfg_status->fstat_list=fstat_list;
+      talloc_steal(cfg_status, fstat_list);
+      goto cleanup;
+    }
+  }
+
+ cleanup:
+  tr_free_config_file_list(n_files, &cfg_files);
+  talloc_free(tmp_ctx);
+  return update_needed;
+}
+
+/* must specify the ctx and tr in cfgwatch! */
+int tr_read_and_apply_config(TR_CFGWATCH *cfgwatch)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  char *config_dir=cfgwatch->config_dir;
+  int n_files = 0;
+  struct dirent **cfg_files=NULL;
+  TR_CFG_RC rc = TR_CFG_SUCCESS;       /* presume success */
+  struct tr_fstat *new_fstat_list=NULL;
+  int retval=0;
+
+  /* find the configuration files -- n.b., tr_find_config_files()
+   * allocates memory to cfg_files which we must later free */
+  tr_debug("Reading configuration files from %s/", config_dir);
+  n_files = tr_find_config_files(config_dir, &cfg_files);
+  if (n_files <= 0) {
+    tr_debug("tr_read_and_apply_config: No configuration files.");
+    retval=1; goto cleanup;
+  }
+
+  /* Get the list of update times.
+   * Do this before loading in case they change between obtaining their timestamp
+   * and reading the file---this way they will immediately reload if this happens. */
+  new_fstat_list=tr_fstat_get_all(tmp_ctx, config_dir, cfg_files, n_files);
+  if (new_fstat_list==NULL) {
+    tr_debug("tr_read_and_apply_config: Could not allocate config file status list.");
+    retval=1; goto cleanup;
+  }
+  
+  /* now fill it in (tr_parse_config allocates space for new config) */
+  if (TR_CFG_SUCCESS != (rc = tr_parse_config(cfgwatch->cfg_mgr, config_dir, n_files, cfg_files))) {
+    tr_debug("tr_read_and_apply_config: Error parsing configuration information, rc=%d.", rc);
+    retval=1; goto cleanup;
+  }
+
+  /* apply new configuration (nulls new, manages context ownership) */
+  if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(cfgwatch->cfg_mgr))) {
+    tr_debug("tr_read_and_apply_config: Error applying configuration, rc = %d.", rc);
+    retval=1; goto cleanup;
+  }
+
+  /* call callback to notify system of new configuration */
+  tr_debug("tr_read_and_apply_config: calling update callback function.");
+  if (cfgwatch->update_cb!=NULL)
+    cfgwatch->update_cb(cfgwatch->cfg_mgr->active, cfgwatch->update_cookie);
+
+  /* give ownership of the new_fstat_list to caller's context */
+  if (cfgwatch->fstat_list != NULL) {
+    /* free the old one */
+    talloc_free(cfgwatch->fstat_list);
+  }
+  cfgwatch->n_files=n_files;
+  cfgwatch->fstat_list=new_fstat_list;
+  talloc_steal(cfgwatch, new_fstat_list);
+  new_fstat_list=NULL;
+
+ cleanup:
+  tr_free_config_file_list(n_files, &cfg_files);
+  talloc_free(tmp_ctx);
+  cfgwatch->cfg_mgr->new=NULL; /* this has been freed, either explicitly or with tmp_ctx */
+  return retval;
+}
+
+
+static void tr_cfgwatch_event_cb(int listener, short event, void *arg)
+{
+  TR_CFGWATCH *cfg_status=(TR_CFGWATCH *) arg;
+  struct timeval now, diff;;
+
+  if (tr_cfgwatch_update_needed(cfg_status)) {
+    tr_notice("Configuration file change detected, waiting for changes to settle.");
+    cfg_status->change_detected=1;
+
+    if (0 != gettimeofday(&cfg_status->last_change_detected, NULL)) {
+      tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (1).");
+    }
+  }
+
+  if (cfg_status->change_detected) {
+    if (0 != gettimeofday(&now, NULL)) {
+      tr_err("tr_cfgwatch_event_cb: gettimeofday() failed (2).");
+    }
+    timersub(&now, &cfg_status->last_change_detected, &diff);
+    if (!timercmp(&diff, &cfg_status->settling_time, <)) {
+      tr_notice("Configuration file change settled, attempting to update configuration.");
+      if (0 != tr_read_and_apply_config(cfg_status))
+        tr_warning("Configuration file update failed. Using previous configuration.");
+      else
+        tr_notice("Configuration updated successfully.");
+      cfg_status->change_detected=0;
+    }
+  }
+}
+
+
+/* Configure the cfgwatch instance and set up its event handler.
+ * Returns 0 on success, nonzero on failure. Points
+ * *cfgwatch_ev to the event struct. */
+int tr_cfgwatch_event_init(struct event_base *base,
+                           TR_CFGWATCH *cfg_status,
+                           struct event **cfgwatch_ev)
+{
+  if (cfgwatch_ev == NULL) {
+    tr_debug("tr_cfgwatch_event_init: Null cfgwatch_ev.");
+    return 1;
+  }
+
+  /* zero out the change detection fields */
+  cfg_status->change_detected=0;
+  cfg_status->last_change_detected.tv_sec=0;
+  cfg_status->last_change_detected.tv_usec=0;
+
+  /* create the event and enable it */
+  *cfgwatch_ev=event_new(base, -1, EV_TIMEOUT|EV_PERSIST, tr_cfgwatch_event_cb, (void *)cfg_status);
+  event_add(*cfgwatch_ev, &(cfg_status->poll_interval));
+
+  tr_info("tr_cfgwatch_event_init: Added configuration file watcher with %0d.%06d second poll interval.",
+           cfg_status->poll_interval.tv_sec,
+           cfg_status->poll_interval.tv_usec);
+  return 0;
+}
+
diff --git a/tr/tr_event.c b/tr/tr_event.c
new file mode 100644 (file)
index 0000000..452dea9
--- /dev/null
@@ -0,0 +1,25 @@
+#include <event2/event.h>
+#include <event2/thread.h>
+
+#include <tr.h>
+#include <tid_internal.h>
+#include <tr_debug.h>
+#include <tr_event.h>
+
+/* Allocate and set up the event base, return a pointer
+ * to the new event_base or NULL on failure.
+ * Enables thread-safe mode. */
+struct event_base *tr_event_loop_init(void)
+{
+  struct event_base *base=NULL;
+
+  evthread_use_pthreads(); /* enable pthreads support */
+  base=event_base_new();
+  return base;
+}
+
+/* run the loop, does not normally return */
+int tr_event_loop_run(struct event_base *base)
+{
+  return event_base_dispatch(base);
+}
index 2590474..2810edc 100644 (file)
 #include <stdlib.h>
 #include <jansson.h>
 #include <argp.h>
+#include <event2/event.h>
+#include <talloc.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <pthread.h>
 
-#include <tr.h>
-#include <tr_filter.h>
 #include <tid_internal.h>
+#include <tr_tid.h>
+#include <tr_trp.h>
 #include <tr_config.h>
-#include <tr_comm.h>
-#include <tr_idp.h>
-#include <tr_rp.h>
+#include <tr_event.h>
+#include <tr_cfgwatch.h>
+#include <tr.h>
 #include <tr_debug.h>
 
-/* Structure to hold TR instance and original request in one cookie */
-typedef struct tr_resp_cookie {
-  TR_INSTANCE *tr;
-  TID_REQ *orig_req;
-} TR_RESP_COOKIE;
-
-
-static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, 
-                       TID_REQ *req,
-                       TID_RESP *resp, 
-                       void *resp_cookie) 
-{
-  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)->tr->tids, 
-                    ((TR_RESP_COOKIE *)resp_cookie)->orig_req, 
-                    resp);
-  
-  return;
-}
-
-static int tr_tids_req_handler (TIDS_INSTANCE *tids,
-                     TID_REQ *orig_req, 
-                     TID_RESP *resp,
-                     void *tr_in)
-{
-  TIDC_INSTANCE *tidc = NULL;
-  TR_RESP_COOKIE resp_cookie;
-  TR_AAA_SERVER *aaa_servers = NULL;
-  TR_NAME *apc = NULL;
-  TID_REQ *fwd_req = NULL;
-  TR_COMM *cfg_comm = NULL;
-  TR_COMM *cfg_apc = NULL;
-  TR_INSTANCE *tr = (TR_INSTANCE *) tr_in;
-  int oaction = TR_FILTER_ACTION_REJECT;
-  int rc = 0;
-  time_t expiration_interval;
-
-  if ((!tids) || (!orig_req) || (!resp) ||  (!tr)) {
-    tr_debug("tr_tids_req_handler: Bad parameters");
-    return -1;
-  }
-
-  tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn, 
-        orig_req->realm->buf, orig_req->comm->buf);
-  if (tids)
-    tids->req_count++;
-
-  /* Duplicate the request, so we can modify and forward it */
-  if (NULL == (fwd_req = tid_dup_req(orig_req))) {
-    tr_debug("tr_tids_req_handler: Unable to duplicate request.");
-    return -1;
-  }
-
-  if (NULL == (cfg_comm = tr_comm_lookup(tids->cookie, 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");
-    return -1;
-  }
-
-  /* Check that the rp_realm matches the filter for the GSS name that 
-   * was received. */
-
-  if ((!(tr)->rp_gss) || 
-      (!(tr)->rp_gss->filter)) {
-    tr_notice("tr_tids_req_handler: No GSS name for incoming request.");
-    tids_send_err_response(tids, orig_req, "No GSS name for request");
-    return -1;
-  }
-
-  if ((TR_FILTER_NO_MATCH == tr_filter_process_rp_permitted(orig_req->rp_realm, (tr)->rp_gss->filter, orig_req->cons, &fwd_req->cons, &oaction)) ||
-      (TR_FILTER_ACTION_REJECT == oaction)) {
-    tr_notice("tr_tids_req_handler: RP realm (%s) does not match RP Realm filter for GSS name", orig_req->rp_realm->buf);
-    tids_send_err_response(tids, orig_req, "RP Realm filter error");
-    return -1;
-  }
-  /* 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))) {
-    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");
-    return -1;
-  }
-
-  /* Map the comm in the request from a COI to an APC, if needed */
-  if (TR_COMM_COI == cfg_comm->type) {
-    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)) {
-      tr_notice("No valid APC for COI %s.", orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "No valid APC for community");
-      return -1;
-    }
-    apc = tr_dup_name(cfg_comm->apcs->id);
-
-    /* Check that the APC is configured */
-    if (NULL == (cfg_apc = tr_comm_lookup(tids->cookie, apc))) {
-      tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf);
-      tids_send_err_response(tids, orig_req, "Unknown APC");
-      return -1;
-    }
+#define TALLOC_DEBUG_ENABLE 1
+#define DEBUG_HARDCODED_PEER_TABLE 1
+#define DEBUG_PING_SELF 0
 
-    fwd_req->comm = apc;
-    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))) {
-      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");
-      return -1;
-    }
-  }
-
-  /* Find the AAA server(s) for this request */
-  if (NULL == (aaa_servers = tr_idp_aaa_server_lookup((TR_INSTANCE *)tids->cookie, 
-                                                     orig_req->realm, 
-                                                     orig_req->comm))) {
-      tr_debug("tr_tids_req_handler: No AAA Servers for realm %s, defaulting.", orig_req->realm->buf);
-      if (NULL == (aaa_servers = tr_default_server_lookup ((TR_INSTANCE *)tids->cookie,
-                                                          orig_req->comm))) {
-       tr_notice("tr_tids_req_handler: No default AAA servers, discarded.");
-        tids_send_err_response(tids, orig_req, "No path to AAA Server(s) for realm");
-        return -1;
-      }
-  } else {
-    /* if we aren't defaulting, check idp coi and apc membership */
-    if (NULL == (tr_find_comm_idp(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");
-      return -1;
-    }
-    if ( cfg_apc && (NULL == (tr_find_comm_idp(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");
-      return -1;
-    }
-  }
-
-  /* send a TID request to the AAA server(s), and get the answer(s) */
-  /* TBD -- Handle multiple servers */
-
-  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");
-    return -1;
-  }
-  /* 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.tr = tr;
-  resp_cookie.orig_req = orig_req;
-
-  /* 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");
-    return -1;
-  };
-
-  /* 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");
-    tid_req_free(orig_req);
-    return -1;
-  }
-    
-  tid_req_free(orig_req);
-  return 0;
-}
-
-static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
-                       void *tr_in)
-{
-  TR_RP_CLIENT *rp;
-  TR_INSTANCE *tr = (TR_INSTANCE *) tr_in;
-
-  if ((!client_name) || (!gss_name) || (!tr)) {
-    tr_debug("tr_tidc_gss_handler: Bad parameters.");
-    return -1;
-  }
-  
-  /* look up the RP client matching the GSS name */
-  if ((NULL == (rp = tr_rp_client_lookup(tr, gss_name)))) {
-    tr_debug("tr_tids_gss_handler: Unknown GSS name %s", gss_name->buf);
-    return -1;
-  }
-
-  /* Store the rp client in the TR_INSTANCE structure for now... 
-   * TBD -- fix me for new tasking model. */
-  (tr)->rp_gss = rp;
-  tr_debug("Client's GSS Name: %s", gss_name->buf);
-
-  return 0;
-}
+/***** command-line option handling / setup *****/
 
 /* Strip trailing / from a path name.*/
 static void remove_trailing_slash(char *s) {
@@ -265,8 +67,6 @@ static void remove_trailing_slash(char *s) {
   }
 }
 
-/* command-line option setup */
-
 /* argp global parameters */
 const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
 
@@ -312,18 +112,103 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
 static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
 
 
-int main (int argc, char *argv[])
+/***** talloc error handling *****/
+/* called when talloc tries to abort */
+static void tr_abort(const char *reason)
 {
+  tr_crit("tr_abort: Critical error, talloc aborted. Reason: %s", reason);
+  abort();
+}
+
+#if TALLOC_DEBUG_ENABLE
+static void tr_talloc_log(const char *msg)
+{
+  tr_debug("talloc: %s", msg);
+}
+#endif /* TALLOC_DEBUG_ENABLE */
+
+
+#if DEBUG_PING_SELF
+struct thingy {
+  TRPS_INSTANCE *trps;
+  struct event *ev;
+};
+
+static void debug_ping(evutil_socket_t fd, short what, void *arg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct thingy *thingy=(struct thingy *)arg;
+  TRPS_INSTANCE *trps=thingy->trps;
+  TRP_REQ *req=NULL;
+  TR_MSG msg;
+  char *encoded=NULL;
+  struct timeval interval={1, 0};
+  static int count=10;
+  TR_NAME *name=NULL;
+
+  tr_debug("debug_ping entered");
+  if (trps->trpc==NULL)
+    tr_trpc_initiate(trps, trps->hostname, trps->port);
+
+  /* create a TRP route request msg */
+  req=trp_req_new(tmp_ctx);
+  name=tr_new_name("community");
+  trp_req_set_comm(req, name);
+  name=tr_new_name("realm");
+  trp_req_set_realm(req, name);
+  tr_msg_set_trp_req(&msg, req);
+  encoded=tr_msg_encode(&msg);
+  if (encoded==NULL)
+    tr_err("debug_ping: error encoding TRP message.");
+  else {
+    tr_debug("debug_ping: sending message");
+    trps_send_msg(trps, NULL, encoded);
+    tr_msg_free_encoded(encoded);
+  }
+  if (count-- > 0)
+    evtimer_add(thingy->ev, &interval);
+}
+#endif /* DEBUG_PING_SELF */
+
+static void configure_signals(void)
+{
+  sigset_t signals;
+  /* ignore SIGPIPE */
+  sigemptyset(&signals);
+  sigaddset(&signals, SIGPIPE);
+  pthread_sigmask(SIG_BLOCK, &signals, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+  TALLOC_CTX *main_ctx=NULL;
+
   TR_INSTANCE *tr = NULL;
-  struct dirent **cfg_files = NULL;
-  TR_CFG_RC rc = TR_CFG_SUCCESS;       /* presume success */
-  int err = 0, n = 0;
   struct cmdline_args opts;
+  struct event_base *ev_base;
+  struct tr_socket_event tids_ev;
+  struct event *cfgwatch_ev;
+
+#if DEBUG_PING_SELF
+  struct event *debug_ping_ev;
+  struct timeval notime={0, 0};
+  struct thingy thingy={NULL};
+#endif /* DEBUG_PING_SELF */
+
+  configure_signals();
+
+  /* we're going to be multithreaded, so disable null context tracking */
+  talloc_set_abort_fn(tr_abort);
+  talloc_disable_null_tracking();
+#if TALLOC_DEBUG_ENABLE
+  talloc_set_log_fn(tr_talloc_log);
+#endif /* TALLOC_DEBUG_ENABLE */
+  main_ctx=talloc_new(NULL);
 
   /* Use standalone logging */
   tr_log_open();
 
-  /* parse command-line arguments */
+  /***** parse command-line arguments *****/
   /* set defaults */
   opts.config_dir=".";
 
@@ -333,53 +218,130 @@ int main (int argc, char *argv[])
   /* process options */
   remove_trailing_slash(opts.config_dir);
 
-  /* create a Trust Router instance */
-  if (NULL == (tr = tr_create())) {
+  /***** create a Trust Router instance *****/
+  if (NULL == (tr = tr_create(main_ctx))) {
     tr_crit("Unable to create Trust Router instance, exiting.");
     return 1;
   }
 
-  /* find the configuration files -- n.b., tr_find_config_files()
-   * allocates memory to cfg_files which we must later free */
-  tr_debug("Reading configuration files from %s/", opts.config_dir);
-  n = tr_find_config_files(opts.config_dir, &cfg_files);
-  if (n <= 0) {
-    tr_crit("Can't locate configuration files, exiting.");
-    tr_free_config_file_list(n, &cfg_files);
-    exit(1);
+  /***** initialize the trust path query server instance *****/
+  if (NULL == (tr->tids = tids_create (tr))) {
+    tr_crit("Error initializing Trust Path Query Server instance.");
+    return 1;
+  }
+
+  /***** initialize the trust router protocol server instance *****/
+  if (NULL == (tr->trps = trps_new(tr))) {
+    tr_crit("Error initializing Trust Router Protocol Server instance.");
+    return 1;
   }
 
-  if (TR_CFG_SUCCESS != tr_parse_config(tr, opts.config_dir, n, cfg_files)) {
-    tr_crit("Error decoding configuration information, exiting.");
-    tr_free_config_file_list(n, &cfg_files);
-    exit(1);
+  /***** process configuration *****/
+  tr->cfgwatch=tr_cfgwatch_create(tr);
+  if (tr->cfgwatch == NULL) {
+    tr_crit("Unable to create configuration watcher object, exiting.");
+    return 1;
   }
-  
-  /* we are now done with the config filenames, free those */
-  tr_free_config_file_list(n, &cfg_files);
-
-  /* apply initial configuration */
-  if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(tr))) {
-    tr_crit("Error applying configuration, rc = %d.", rc);
-    exit(1);
+  tr->cfgwatch->config_dir=opts.config_dir;
+  tr->cfgwatch->cfg_mgr=tr->cfg_mgr;
+  tr->cfgwatch->update_cb=tr_config_changed; /* handle configuration changes */
+  tr->cfgwatch->update_cookie=(void *)tr;
+  if (0 != tr_read_and_apply_config(tr->cfgwatch)) {
+    tr_crit("Error reading configuration, exiting.");
+    return 1;
   }
 
-  /* print the loaded configuration */
-  tr_print_config(tr->active_cfg);
+  /***** Set up the event loop *****/
+  ev_base=tr_event_loop_init(); /* Set up the event loop */
+  if (ev_base==NULL) {
+    tr_crit("Error initializing event loop.");
+    return 1;
+  }
+
+  /* already set config_dir, fstat_list and n_files earlier */
+  if (0 != tr_cfgwatch_event_init(ev_base, tr->cfgwatch, &cfgwatch_ev)) {
+    tr_crit("Error initializing configuration file watcher.");
+    return 1;
+  }
+
+  /*tr_status_event_init();*/ /* install status reporting events */
 
-  /* initialize the trust path query server instance */
-  if (0 == (tr->tids = tids_create ())) {
+  /* install TID server events */
+  if (0 != tr_tids_event_init(ev_base,
+                              tr->tids,
+                              tr->cfg_mgr,
+                             &tids_ev)) {
     tr_crit("Error initializing Trust Path Query Server instance.");
-    exit(1);
+    return 1;
   }
 
-  /* start the trust path query server, won't return unless fatal error. */
-  if (0 != (err = tids_start(tr->tids, &tr_tids_req_handler, &tr_tids_gss_handler, tr->active_cfg->internal->hostname, tr->active_cfg->internal->tids_port, (void *)tr))) {
-    tr_crit("Error from Trust Path Query Server, err = %d.", err);
-    exit(err);
+  /* install TRP handler events */
+  if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) {
+    tr_crit("Error initializing Trust Path Query Server instance.");
+    return 1;
   }
 
-  tids_destroy(tr->tids);
-  tr_destroy(tr);
-  exit(0);
+#if DEBUG_HARDCODED_PEER_TABLE
+  {
+    TRP_PEER *hc_peer=NULL;
+    char *s=NULL;
+
+    hc_peer=trp_peer_new(main_ctx); /* will later be stolen by ptable context */
+    if (hc_peer==NULL) {
+      tr_crit("Unable to allocate new peer. Aborting.");
+      return 1;
+    }
+    trp_peer_set_server(hc_peer, "epsilon.vmnet");
+    trp_peer_set_gssname(hc_peer, tr_new_name("tr-epsilon-vmnet@apc.painless-security.com"));
+    trp_peer_set_conn_status_cb(hc_peer, tr_peer_status_change, (void *)(tr->trps));
+    switch (tr->trps->port) {
+    case 10000:
+      trp_peer_set_port(hc_peer, 10001);
+      break;
+    case 10001:
+      trp_peer_set_port(hc_peer, 10000);
+      break;
+    default:
+      tr_crit("Cannot use hardcoded peer table with port other than 10000 or 10001.");
+      return 1;
+    }
+    if (TRP_SUCCESS != trps_add_peer(tr->trps, hc_peer)) {
+      tr_crit("Unable to add peer.");
+      return 1;
+    }
+
+    hc_peer=trp_peer_new(main_ctx); /* will later be stolen by ptable context */
+    if (hc_peer==NULL) {
+      tr_crit("Unable to allocate new peer. Aborting.");
+      return 1;
+    }
+    trp_peer_set_server(hc_peer, "epsilon-trpc.vmnet");
+    trp_peer_set_gssname(hc_peer, tr_new_name("trpc@apc.painless-security.com"));
+    trp_peer_set_port(hc_peer, 10002); /* not really used */
+    if (TRP_SUCCESS != trps_add_peer(tr->trps, hc_peer)) {
+      tr_crit("Unable to add peer.");
+      return 1;
+    }
+    
+    s=trp_ptable_to_str(main_ctx, tr->trps->ptable, NULL, NULL);
+    tr_debug("Peer Table:\n%s\n", s);
+    talloc_free(s);
+  }
+#endif /* DEBUG_HARDCODED_PEER_TABLE */
+
+#if DEBUG_PING_SELF
+  /* for debugging, send a message to peers on a timer */
+  debug_ping_ev=evtimer_new(ev_base, debug_ping, (void *)&thingy);
+  thingy.trps=tr->trps;
+  thingy.ev=debug_ping_ev;
+  evtimer_add(debug_ping_ev, &notime);
+#endif /* DEBUG_PING_SELF */
+
+  tr_event_loop_run(ev_base); /* does not return until we are done */
+
+  /* TODO: ensure talloc is properly used so this actually works */
+  tr_destroy(tr); /* thanks to talloc, should destroy everything */
+
+  talloc_free(main_ctx);
+  return 0;
 }
diff --git a/tr/tr_tid.c b/tr/tr_tid.c
new file mode 100644 (file)
index 0000000..e740703
--- /dev/null
@@ -0,0 +1,338 @@
+#include <talloc.h>
+
+#include <tid_internal.h>
+#include <tr_filter.h>
+#include <tr_comm.h>
+#include <tr_idp.h>
+#include <tr_rp.h>
+#include <tr_event.h>
+#include <tr_debug.h>
+#include <gsscon.h>
+#include <trp_internal.h>
+#include <tr_config.h>
+#include <tr_tid.h>
+
+/* Structure to hold TR instance and original request in one cookie */
+typedef struct tr_resp_cookie {
+  TIDS_INSTANCE *tids;
+  TID_REQ *orig_req;
+} TR_RESP_COOKIE;
+
+/* hold a tids instance and a config manager */
+struct tr_tids_event_cookie {
+  TIDS_INSTANCE *tids;
+  TR_CFG_MGR *cfg_mgr;
+  TRPS_INSTANCE *trps;
+};
+
+
+static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, 
+                                  TID_REQ *req,
+                                  TID_RESP *resp, 
+                                  void *resp_cookie)
+{
+  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;
+}
+
+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);;
+  TIDC_INSTANCE *tidc = NULL;
+  TR_RESP_COOKIE resp_cookie;
+  TR_AAA_SERVER *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=(struct tr_tids_event_cookie *)cookie_in;
+  TR_CFG_MGR *cfg_mgr=talloc_get_type_abort(cookie, TR_CFG_MGR);
+  TRPS_INSTANCE *trps=talloc_get_type_abort(cookie, TRPS_INSTANCE);
+  TRP_ROUTE *route=NULL;
+  int retval=-1;
+
+  if ((!tids) || (!orig_req) || (!resp)) {
+    tr_debug("tr_tids_req_handler: Bad parameters");
+    retval=-1;
+    goto cleanup;
+  }
+
+  tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn, 
+           orig_req->realm->buf, orig_req->comm->buf);
+  tids->req_count++;
+
+  /* Duplicate the request, so we can modify and forward it */
+  if (NULL == (fwd_req = tid_dup_req(orig_req))) {
+    tr_debug("tr_tids_req_handler: Unable to duplicate request.");
+    retval=-1;
+    goto cleanup;
+  }
+
+  if (NULL == (cfg_comm = tr_comm_lookup(cfg_mgr->active->comms, 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;
+    goto cleanup;
+  }
+
+  /* Check that the rp_realm matches the filter for the GSS name that 
+   * was received. */
+
+  if ((!tids->rp_gss) || 
+      (!tids->rp_gss->filter)) {
+    tr_notice("tr_tids_req_handler: No GSS name for incoming request.");
+    tids_send_err_response(tids, orig_req, "No GSS name for request");
+    retval=-1;
+    goto cleanup;
+  }
+
+  if ((TR_FILTER_NO_MATCH == tr_filter_process_rp_permitted(orig_req->rp_realm,
+                                                            tids->rp_gss->filter,
+                                                            orig_req->cons,
+                                                           &fwd_req->cons,
+                                                           &oaction)) ||
+      (TR_FILTER_ACTION_REJECT == oaction)) {
+    tr_notice("tr_tids_req_handler: RP realm (%s) does not match RP Realm filter for GSS name", orig_req->rp_realm->buf);
+    tids_send_err_response(tids, orig_req, "RP Realm filter error");
+    retval=-1;
+    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))) {
+    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;
+    goto cleanup;
+  }
+
+  /* Map the comm in the request from a COI to an APC, if needed */
+  if (TR_COMM_COI == cfg_comm->type) {
+    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)) {
+      tr_notice("No valid APC for COI %s.", orig_req->comm->buf);
+      tids_send_err_response(tids, orig_req, "No valid APC for community");
+      retval=-1;
+      goto cleanup;
+    }
+    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))) {
+      tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf);
+      tids_send_err_response(tids, orig_req, "Unknown APC");
+      retval=-1;
+      goto cleanup;
+    }
+
+    fwd_req->comm = apc;
+    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))) {
+      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;
+      goto cleanup;
+    }
+  }
+
+  /* Look up the route for this community/realm. */
+  route=trps_get_selected_route(trps, orig_req->comm, orig_req->realm);
+  if (route==NULL) {
+    tr_notice("tr_tids_req_handler: no route table entry found for realm (%s) in community (%s).",
+              orig_req->realm->buf, orig_req->comm->buf);
+    tids_send_err_response(tids, orig_req, "Missing trust route error");
+    retval=-1;
+    goto cleanup;
+  }
+  if (trp_route_is_local(route)) {
+    aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->idp_realms, 
+                                           orig_req->realm, 
+                                           orig_req->comm);
+  } else {
+    aaa_servers = tr_aaa_server_new(tmp_ctx, trp_route_get_next_hop(route));
+  }
+
+  /* Find the AAA server(s) for this request */
+  if (NULL == aaa_servers) {
+    tr_debug("tr_tids_req_handler: No AAA Servers for realm %s, defaulting.", orig_req->realm->buf);
+    if (NULL == (aaa_servers = tr_default_server_lookup (cfg_mgr->active->default_servers,
+                                                         orig_req->comm))) {
+      tr_notice("tr_tids_req_handler: No default AAA servers, discarded.");
+      tids_send_err_response(tids, orig_req, "No path to AAA Server(s) for realm");
+      retval=-1;
+      goto cleanup;
+    }
+  } else {
+    /* if we aren't defaulting, check idp coi and apc membership */
+    if (NULL == (tr_find_comm_idp(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)))) {
+      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;
+      goto cleanup;
+    }
+  }
+
+  /* send a TID request to the AAA server(s), and get the answer(s) */
+  /* TBD -- Handle multiple servers */
+
+  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");
+    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;
+
+  /* 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");
+    retval=-1;
+    goto cleanup;
+  };
+
+  /* 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");
+    retval=-1;
+    goto cleanup;
+  }
+    
+cleanup:
+  talloc_free(tmp_ctx);
+  return retval;
+}
+
+static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
+                               void *data)
+{
+  TR_RP_CLIENT *rp;
+  struct tr_tids_event_cookie *cookie=(struct tr_tids_event_cookie *)data;
+  TIDS_INSTANCE *tids = cookie->tids;
+  TR_CFG_MGR *cfg_mgr = cookie->cfg_mgr;
+
+  if ((!client_name) || (!gss_name) || (!tids) || (!cfg_mgr)) {
+    tr_debug("tr_tidc_gss_handler: Bad parameters.");
+    return -1;
+  }
+
+  /* look up the RP client matching the GSS name */
+  if ((NULL == (rp = tr_rp_client_lookup(cfg_mgr->active->rp_clients, gss_name)))) {
+    tr_debug("tr_tids_gss_handler: Unknown GSS name %s", gss_name->buf);
+    return -1;
+  }
+
+  /* Store the rp client */
+  tids->rp_gss = rp;
+  tr_debug("Client's GSS Name: %s", gss_name->buf);
+
+  return 0;
+}
+
+
+/***** TIDS event handling *****/
+
+/* called when a connection to the TIDS port is received */
+static void tr_tids_event_cb(int listener, short event, void *arg)
+{
+  TIDS_INSTANCE *tids = (TIDS_INSTANCE *)arg;
+
+  if (0==(event & EV_READ))
+    tr_debug("tr_tids_event_cb: unexpected event on TIDS socket (event=0x%X)", event);
+  else 
+    tids_accept(tids, listener);
+}
+
+/* Configure the tids instance and set up its event handler.
+ * Returns 0 on success, nonzero on failure. Fills in
+ * *tids_event (which should be allocated by caller). */
+int tr_tids_event_init(struct event_base *base,
+                       TIDS_INSTANCE *tids,
+                       TR_CFG_MGR *cfg_mgr,
+                       struct tr_socket_event *tids_ev)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_tids_event_cookie *cookie=NULL;
+  int retval=0;
+
+  if (tids_ev == NULL) {
+    tr_debug("tr_tids_event_init: Null tids_ev.");
+    retval=1;
+    goto cleanup;
+  }
+
+  /* Create the cookie for callbacks. We'll put it in the tids context, so it will
+   * be cleaned up when tids is freed by talloc_free. */
+  cookie=talloc(tmp_ctx, struct tr_tids_event_cookie);
+  if (cookie == NULL) {
+    tr_debug("tr_tids_event_init: Unable to allocate cookie.");
+    retval=1;
+    goto cleanup;
+  }
+  cookie->tids=tids;
+  cookie->cfg_mgr=cfg_mgr;
+  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) {
+    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);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return retval;
+}
diff --git a/tr/tr_trp.c b/tr/tr_trp.c
new file mode 100644 (file)
index 0000000..0d87439
--- /dev/null
@@ -0,0 +1,804 @@
+#include <stdio.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <event2/event.h>
+#include <talloc.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <gsscon.h>
+#include <tr.h>
+#include <tr_mq.h>
+#include <tr_rp.h>
+#include <trp_internal.h>
+#include <trp_ptable.h>
+#include <trp_rtable.h>
+#include <tr_config.h>
+#include <tr_event.h>
+#include <tr_msg.h>
+#include <tr_trp.h>
+#include <tr_debug.h>
+
+/* data for event callbacks */
+struct tr_trps_event_cookie {
+  TRPS_INSTANCE *trps;
+  TR_CFG_MGR *cfg_mgr;
+  struct event *ev;
+};
+
+/* callback to schedule event to process messages */
+static void tr_trps_mq_cb(TR_MQ *mq, void *arg)
+{
+  struct event *mq_ev=(struct event *)arg;
+  event_active(mq_ev, 0, 0);
+}
+
+static void msg_free_helper(void *p)
+{
+  tr_msg_free_decoded((TR_MSG *)p);
+}
+
+static void tr_free_name_helper(void *arg)
+{
+  tr_free_name((TR_NAME *)arg);
+}
+
+/* takes a TR_MSG and puts it in a TR_MQ_MSG for processing by the main thread */
+static TRP_RC tr_trps_msg_handler(TRPS_INSTANCE *trps,
+                                  TRP_CONNECTION *conn,
+                                  TR_MSG *tr_msg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_MQ_MSG *mq_msg=NULL;
+
+  /* n.b., conn is available here, but do not hold onto the reference
+   * because it may be cleaned up if the originating connection goes
+   * down before the message is processed */
+  mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_MSG_RECEIVED, TR_MQ_PRIO_NORMAL);
+  if (mq_msg==NULL) {
+    return TRP_NOMEM;
+  }
+  tr_mq_msg_set_payload(mq_msg, (void *)tr_msg, msg_free_helper);
+  trps_mq_add(trps, mq_msg);
+  talloc_free(tmp_ctx); /* cleans up the message if it did not get appended correctly */
+  return TRP_SUCCESS;
+}
+
+
+static int tr_trps_gss_handler(gss_name_t client_name, gss_buffer_t gss_name,
+                               void *cookie_in)
+{
+  struct tr_trps_event_cookie *cookie=(struct tr_trps_event_cookie *)cookie_in;
+  TRPS_INSTANCE *trps = cookie->trps;
+  TR_CFG_MGR *cfg_mgr = cookie->cfg_mgr;
+  TR_NAME name={gss_name->value, gss_name->length};
+
+  tr_debug("tr_trps_gss_handler()");
+
+  if ((!client_name) || (!gss_name) || (!trps) || (!cfg_mgr)) {
+    tr_debug("tr_trps_gss_handler: Bad parameters.");
+    return -1;
+  }
+  
+  /* look up the TRPS peer matching the GSS name */
+  if (NULL==trps_get_peer_by_gssname(trps, &name)) {
+    tr_warning("tr_trps_gss_handler: Connection attempt from unknown peer (GSS name: %.*s).", name.len, name.buf);
+    return -1;
+  }
+
+  tr_debug("Client's GSS Name: %.*s", name.len, name.buf);
+  return 0;
+}
+
+/* data passed to thread */
+struct trps_thread_data {
+  TRP_CONNECTION *conn;
+  TRPS_INSTANCE *trps;
+};
+/* thread to handle GSS connections from peers */
+static void *tr_trps_thread(void *arg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct trps_thread_data *thread_data=talloc_get_type_abort(arg, struct trps_thread_data);
+  TRP_CONNECTION *conn=thread_data->conn;
+  TRPS_INSTANCE *trps=thread_data->trps;
+  TR_MQ_MSG *msg=NULL;
+
+  tr_debug("tr_trps_thread: started");
+  if (trps_authorize_connection(trps, conn)!=TRP_SUCCESS)
+    goto cleanup;
+
+  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_CONNECTED, TR_MQ_PRIO_HIGH);
+  tr_mq_msg_set_payload(msg, (void *)tr_dup_name(trp_connection_get_peer(conn)), tr_free_name_helper);
+  if (msg==NULL) {
+    tr_err("tr_trps_thread: error allocating TR_MQ_MSG");
+    goto cleanup;
+  } 
+  trps_mq_add(trps, msg); /* steals msg context */
+  msg=NULL;
+
+  trps_handle_connection(trps, conn);
+
+cleanup:
+  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_DISCONNECTED, TR_MQ_PRIO_HIGH);
+  tr_mq_msg_set_payload(msg, (void *)conn, NULL); /* do not pass a free routine */
+  if (msg==NULL)
+    tr_err("tr_trps_thread: error allocating TR_MQ_MSG");
+  else
+    trps_mq_add(trps, msg);
+  tr_debug("tr_trps_thread: exit");
+  talloc_free(tmp_ctx);
+  return NULL;
+}
+
+/* called when a connection to the TRPS port is received */
+static void tr_trps_event_cb(int listener, short event, void *arg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRPS_INSTANCE *trps = talloc_get_type_abort(arg, TRPS_INSTANCE); /* aborts on wrong type */
+  TRP_CONNECTION *conn=NULL;
+  TR_NAME *gssname=NULL;
+  char *name=NULL;
+  struct trps_thread_data *thread_data=NULL;
+
+  if (0==(event & EV_READ)) {
+    tr_debug("tr_trps_event_cb: unexpected event on TRPS socket (event=0x%X)", event);
+  } else {
+    /* create a thread to handle this connection */
+    asprintf(&name, "trustrouter@%s", trps->hostname);
+    gssname=tr_new_name(name);
+    free(name); name=NULL;
+    conn=trp_connection_accept(tmp_ctx, listener, gssname);
+    if (conn!=NULL) {
+      /* need to monitor this fd and trigger events when read becomes possible */
+      thread_data=talloc(conn, struct trps_thread_data);
+      if (thread_data==NULL) {
+        tr_err("tr_trps_event_cb: unable to allocate trps_thread_data");
+        talloc_free(tmp_ctx);
+        return;
+      }
+      thread_data->conn=conn;
+      thread_data->trps=trps;
+      trps_add_connection(trps, conn); /* remember the connection */
+      pthread_create(trp_connection_get_thread(conn), NULL, tr_trps_thread, thread_data);
+    }
+  }
+  talloc_free(tmp_ctx);
+}
+
+static void tr_trps_cleanup_conn(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
+{
+  /* everything belonging to the thread is in the TRP_CONNECTION
+   * associated with it */
+  tr_debug("tr_trps_cleanup_conn: freeing %p", conn);
+/*  pthread_join(*trp_connection_get_thread(conn), NULL); -- removed while debugging, put back!!! --jlr */
+  trps_remove_connection(trps, conn);
+  talloc_report_full(conn, stderr);
+  trp_connection_free(conn);
+  tr_debug("tr_trps_cleanup_conn: deleted connection");
+}
+
+static void tr_trps_cleanup_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
+{
+  pthread_join(*trp_connection_get_thread(trpc_get_conn(trpc)), NULL);
+  trps_remove_trpc(trps, trpc);
+  trpc_free(trpc);
+  tr_debug("tr_trps_cleanup_trpc: deleted connection");
+}
+
+static void tr_trps_print_route_table(TRPS_INSTANCE *trps, FILE *f)
+{
+  char *table=trp_rtable_to_str(NULL, trps->rtable, " | ", NULL);
+  if (table==NULL)
+    fprintf(f, "Unable to print route table.\n");
+  else {
+    fprintf(f, "%s\n", table);
+    talloc_free(table);
+  }
+}
+
+static void tr_trps_process_mq(int socket, short event, void *arg)
+{
+  TRPS_INSTANCE *trps=talloc_get_type_abort(arg, TRPS_INSTANCE);
+  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);
+    if (0==strcmp(s, TR_MQMSG_TRPS_CONNECTED)) {
+      TR_NAME *gssname=(TR_NAME *)tr_mq_msg_get_payload(msg);
+      TRP_PEER *peer=trps_get_peer_by_gssname(trps, gssname);
+      if (peer==NULL)
+        tr_err("tr_trps_process_mq: incoming connection from unknown peer (%s) reported.", gssname->buf);
+      else {
+        trp_peer_set_incoming_status(peer, PEER_CONNECTED);
+        tr_err("tr_trps_process_mq: incoming connection from %s established.", gssname->buf);
+      }
+    }
+    else if (0==strcmp(s, TR_MQMSG_TRPS_DISCONNECTED)) {
+      TRP_CONNECTION *conn=talloc_get_type_abort(tr_mq_msg_get_payload(msg), TRP_CONNECTION);
+      TR_NAME *gssname=trp_connection_get_gssname(conn);
+      TRP_PEER *peer=trps_get_peer_by_gssname(trps, gssname);
+      if (peer==NULL) {
+        tr_err("tr_trps_process_mq: incoming connection from unknown peer (%s) lost.",
+               trp_connection_get_gssname(conn)->buf);
+      } else {
+        trp_peer_set_incoming_status(peer, PEER_DISCONNECTED);
+        tr_trps_cleanup_conn(trps, conn);
+        tr_err("tr_trps_process_mq: incoming connection from %s lost.", gssname->buf);
+      }
+    }
+    else if (0==strcmp(s, TR_MQMSG_TRPC_CONNECTED)) {
+      TR_NAME *svcname=(TR_NAME *)tr_mq_msg_get_payload(msg);
+      TRP_PEER *peer=trps_get_peer_by_servicename(trps, svcname);
+      if (peer==NULL)
+        tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) reported.", svcname->buf);
+      else {
+        trp_peer_set_outgoing_status(peer, PEER_CONNECTED);
+        tr_err("tr_trps_process_mq: outgoing connection to %s established.", svcname->buf);
+      }
+    }
+    else if (0==strcmp(s, TR_MQMSG_TRPC_DISCONNECTED)) {
+      /* trpc connection died */
+      TRPC_INSTANCE *trpc=talloc_get_type_abort(tr_mq_msg_get_payload(msg), TRPC_INSTANCE);
+      TR_NAME *gssname=trpc_get_gssname(trpc);
+      TRP_PEER *peer=trps_get_peer_by_servicename(trps, gssname);
+      if (peer==NULL)
+        tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) lost.", gssname->buf);
+      else {
+        trp_peer_set_outgoing_status(peer, PEER_DISCONNECTED);
+        tr_err("tr_trps_process_mq: outgoing connection to %s lost.", gssname->buf);
+        tr_trps_cleanup_trpc(trps, trpc);
+      }
+    }
+
+    else if (0==strcmp(s, TR_MQMSG_MSG_RECEIVED)) {
+      if (trps_handle_tr_msg(trps, tr_mq_msg_get_payload(msg))!=TRP_SUCCESS)
+        tr_notice("tr_trps_process_mq: error handling message.");
+      else {
+        tr_trps_print_route_table(trps, stderr);
+      }
+    }
+    else
+      tr_notice("tr_trps_process_mq: unknown message '%s' received.", tr_mq_msg_get_message(msg));
+
+    tr_mq_msg_free(msg);
+    msg=trps_mq_pop(trps);
+  }
+}
+
+static void tr_trps_update(int listener, short event, void *arg)
+{
+  struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
+  TRPS_INSTANCE *trps=cookie->trps;
+  struct event *ev=cookie->ev;
+
+  tr_debug("tr_trps_update: sending scheduled route updates.");
+  trps_update(trps, TRP_UPDATE_SCHEDULED);
+  event_add(ev, &(trps->update_interval));
+}
+
+static void tr_trps_sweep(int listener, short event, void *arg)
+{
+  struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
+  TRPS_INSTANCE *trps=cookie->trps;
+  struct event *ev=cookie->ev;
+
+  tr_debug("tr_trps_sweep: sweeping routes.");
+  trps_sweep_routes(trps);
+  tr_trps_print_route_table(trps, stderr);
+  /* schedule the event to run again */
+  event_add(ev, &(trps->sweep_interval));
+}
+
+static void tr_connection_update(int listener, short event, void *arg)
+{
+  struct tr_trps_event_cookie *cookie=talloc_get_type_abort(arg, struct tr_trps_event_cookie);
+  TRPS_INSTANCE *trps=cookie->trps;
+  struct event *ev=cookie->ev;
+
+  tr_debug("tr_connection_update: checking peer connections.");
+  tr_connect_to_peers(trps, ev);
+  /* schedule the event to run again */
+  event_add(ev, &(trps->connect_interval));
+}
+
+static int tr_trps_events_destructor(void *obj)
+{
+  TR_TRPS_EVENTS *ev=talloc_get_type_abort(obj, TR_TRPS_EVENTS);
+  if (ev->mq_ev!=NULL)
+    event_free(ev->mq_ev);
+  if (ev->connect_ev!=NULL)
+    event_free(ev->connect_ev);
+  if (ev->update_ev!=NULL)
+    event_free(ev->update_ev);
+  if (ev->sweep_ev!=NULL)
+    event_free(ev->sweep_ev);
+  return 0;
+}
+static TR_TRPS_EVENTS *tr_trps_events_new(TALLOC_CTX *mem_ctx)
+{
+  TR_TRPS_EVENTS *ev=talloc(mem_ctx, TR_TRPS_EVENTS);
+  if (ev!=NULL) {
+    ev->listen_ev=talloc(ev, struct tr_socket_event);
+    ev->mq_ev=NULL;
+    ev->connect_ev=NULL;
+    ev->update_ev=NULL;
+    ev->sweep_ev=NULL;
+    if (ev->listen_ev==NULL) {
+      talloc_free(ev);
+      ev=NULL;
+    } else {
+      talloc_set_destructor((void *)ev, tr_trps_events_destructor);
+    }
+  }
+  return ev;
+}
+
+static void tr_trps_events_free(TR_TRPS_EVENTS *ev)
+{
+  talloc_free(ev);
+}
+
+/* Configure the trps instance and set up its event handler.
+ * Fills in trps_ev, which should be allocated by caller. */
+TRP_RC tr_trps_event_init(struct event_base *base, TR_INSTANCE *tr)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_socket_event *listen_ev=NULL;
+  struct tr_trps_event_cookie *trps_cookie=NULL;
+  struct tr_trps_event_cookie *connection_cookie=NULL;
+  struct tr_trps_event_cookie *update_cookie=NULL;
+  struct tr_trps_event_cookie *sweep_cookie=NULL;
+  struct timeval zero_time={0,0};
+  TRP_RC retval=TRP_ERROR;
+
+  if (tr->events != NULL) {
+    tr_notice("tr_trps_event_init: tr->events was not null. Freeing before reallocating..");
+    tr_trps_events_free(tr->events);
+  }
+
+  tr->events=tr_trps_events_new(tmp_ctx);
+  if (tr->events == NULL) {
+    tr_debug("tr_trps_event_init: unable to allocate event handles.");
+    retval=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  /* get convenient handles */
+  listen_ev=tr->events->listen_ev;
+
+  /* Create the cookie for callbacks. It will end up part of the trps context, so it will
+   * be cleaned up when trps is freed by talloc_free. */
+  trps_cookie=talloc(tr->events, struct tr_trps_event_cookie);
+  if (trps_cookie == NULL) {
+    tr_debug("tr_trps_event_init: Unable to allocate trps_cookie.");
+    retval=TRP_NOMEM;
+    tr_trps_events_free(tr->events);
+    tr->events=NULL;
+    goto cleanup;
+  }
+  trps_cookie->trps=tr->trps;
+  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) {
+    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);
+  
+  /* now set up message queue processing event, only triggered by
+   * tr_trps_mq_cb() */
+  tr->events->mq_ev=event_new(base,
+                              0,
+                              EV_PERSIST,
+                              tr_trps_process_mq,
+                              (void *)(tr->trps));
+  tr_mq_set_notify_cb(tr->trps->mq, tr_trps_mq_cb, tr->events->mq_ev);
+
+  /* now set up the peer connection timer event */
+  connection_cookie=talloc(tr->events, struct tr_trps_event_cookie);
+  if (connection_cookie == NULL) {
+    tr_debug("tr_trps_event_init: Unable to allocate connection_cookie.");
+    retval=TRP_NOMEM;
+    tr_trps_events_free(tr->events);
+    tr->events=NULL;
+    goto cleanup;
+  }
+  connection_cookie->trps=tr->trps;
+  connection_cookie->cfg_mgr=tr->cfg_mgr;
+  tr->events->connect_ev=event_new(base, -1, EV_TIMEOUT, tr_connection_update, (void *)connection_cookie);
+  connection_cookie->ev=tr->events->connect_ev; /* in case it needs to frob the event */
+  /* The first time, do this immediately. Thereafter, it will retrigger every trps->connect_interval */
+  event_add(tr->events->connect_ev, &zero_time);
+
+  /* now set up the route update timer event */
+  update_cookie=talloc(tr->events, struct tr_trps_event_cookie);
+  if (update_cookie == NULL) {
+    tr_debug("tr_trps_event_init: Unable to allocate update_cookie.");
+    retval=TRP_NOMEM;
+    tr_trps_events_free(tr->events);
+    tr->events=NULL;
+    goto cleanup;
+  }
+  update_cookie->trps=tr->trps;
+  update_cookie->cfg_mgr=tr->cfg_mgr;
+  tr->events->update_ev=event_new(base, -1, EV_TIMEOUT, tr_trps_update, (void *)update_cookie);
+  update_cookie->ev=tr->events->update_ev; /* in case it needs to frob the event */
+  event_add(tr->events->update_ev, &(tr->trps->update_interval));
+
+  /* now set up the route table sweep timer event */
+  sweep_cookie=talloc(tr->events, struct tr_trps_event_cookie);
+  if (sweep_cookie == NULL) {
+    tr_debug("tr_trps_event_init: Unable to allocate sweep_cookie.");
+    retval=TRP_NOMEM;
+    tr_trps_events_free(tr->events);
+    tr->events=NULL;
+    goto cleanup;
+  }
+  sweep_cookie->trps=tr->trps;
+  sweep_cookie->cfg_mgr=tr->cfg_mgr;
+  tr->events->sweep_ev=event_new(base, -1, EV_TIMEOUT, tr_trps_sweep, (void *)sweep_cookie);
+  sweep_cookie->ev=tr->events->sweep_ev; /* in case it needs to frob the event */
+  event_add(tr->events->sweep_ev, &(tr->trps->sweep_interval));
+
+  talloc_steal(tr, tr->events);
+  retval=TRP_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return retval;
+}
+
+
+struct trpc_notify_cb_data {
+  int msg_ready;
+  pthread_cond_t cond;
+  pthread_mutex_t mutex;
+};
+
+static void tr_trpc_mq_cb(TR_MQ *mq, void *arg)
+{
+  struct trpc_notify_cb_data *cb_data=(struct trpc_notify_cb_data *) arg;
+  pthread_mutex_lock(&(cb_data->mutex));
+  if (!cb_data->msg_ready) {
+    cb_data->msg_ready=1;
+    pthread_cond_signal(&(cb_data->cond));
+  }
+  pthread_mutex_unlock(&(cb_data->mutex));
+}
+
+/* data passed to thread */
+struct trpc_thread_data {
+  TRPC_INSTANCE *trpc;
+  TRPS_INSTANCE *trps;
+};
+static void *tr_trpc_thread(void *arg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct trpc_thread_data *thread_data=talloc_get_type_abort(arg, struct trpc_thread_data);
+  TRPC_INSTANCE *trpc=thread_data->trpc;
+  TRPS_INSTANCE *trps=thread_data->trps;
+  TRP_RC rc=TRP_ERROR;
+  TR_MQ_MSG *msg=NULL;
+  const char *msg_type=NULL;
+  char *encoded_msg=NULL;
+  TR_NAME *peer_gssname=NULL;
+
+  struct trpc_notify_cb_data cb_data={0,
+                                      PTHREAD_COND_INITIALIZER,
+                                      PTHREAD_MUTEX_INITIALIZER};
+
+  tr_debug("tr_trpc_thread: started");
+
+  /* set up the mq for receiving */
+  pthread_mutex_lock(&(cb_data.mutex)); /* hold this lock until we enter the main loop */
+
+  tr_mq_lock(trpc->mq);
+  tr_mq_set_notify_cb(trpc->mq, tr_trpc_mq_cb, (void *) &cb_data);
+  tr_mq_unlock(trpc->mq);
+
+  rc=trpc_connect(trpc);
+  if (rc!=TRP_SUCCESS) {
+    tr_notice("tr_trpc_thread: failed to initiate connection to %s:%d.",
+              trpc_get_server(trpc),
+              trpc_get_port(trpc));
+  } else {
+    peer_gssname=trp_connection_get_peer(trpc_get_conn(trpc));
+    if (peer_gssname==NULL) {
+      tr_err("tr_trpc_thread: could not duplicate peer_gssname.");
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+    tr_debug("tr_trpc_thread: connected to peer %s", peer_gssname->buf);
+
+    msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_CONNECTED, TR_MQ_PRIO_HIGH);
+    tr_mq_msg_set_payload(msg, (void *)tr_dup_name(peer_gssname), tr_free_name_helper);
+    if (msg==NULL) {
+      tr_err("tr_trpc_thread: error allocating TR_MQ_MSG");
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+    trps_mq_add(trps, msg); /* steals msg context */
+    msg=NULL;
+
+    while(1) {
+      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);
+
+        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;
+            }
+          }
+        }
+        else
+          tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
+
+        tr_mq_msg_free(msg);
+      }
+    }
+  }
+
+  tr_debug("tr_trpc_thread: exiting.");
+  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_DISCONNECTED, TR_MQ_PRIO_HIGH);
+  tr_mq_msg_set_payload(msg, (void *)trpc, NULL); /* do not pass a free routine */
+  if (msg==NULL)
+    tr_err("tr_trpc_thread: error allocating TR_MQ_MSG");
+  else
+    trps_mq_add(trps, msg);
+
+  trpc_mq_clear(trpc); /* clear any queued messages */
+
+  talloc_free(tmp_ctx);
+  return NULL;
+}
+
+/* convert an IDP realm into routing table entries. Outputs number in *n_routes */
+static TRP_ROUTE **tr_make_local_routes(TALLOC_CTX *mem_ctx,
+                                         TR_IDP_REALM *realm,
+                                         char *trust_router,
+                                         size_t *n_routes)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC *comm=NULL;
+  TRP_ROUTE *new_entry=NULL;
+  TRP_ROUTE **entries=NULL;
+  size_t n_comms=0, ii=0;
+
+  *n_routes=0;
+
+  if ((realm==NULL) || (realm->origin!=TR_REALM_LOCAL))
+    goto cleanup;
+
+  /* count comms */
+  for (comm=realm->apcs, n_comms=0; comm!=NULL; comm=comm->next,n_comms++) {}
+
+  entries=talloc_array(tmp_ctx, TRP_ROUTE *, n_comms);
+  for (comm=realm->apcs,ii=0; comm!=NULL; comm=comm->next, ii++) {
+    new_entry=trp_route_new(entries);
+    if (new_entry==NULL) {
+      tr_crit("tr_make_local_routes: unable to allocate entry.");
+      talloc_free(entries);
+      goto cleanup;
+    }
+    trp_route_set_comm(new_entry, tr_dup_name(comm->id));
+    trp_route_set_realm(new_entry, tr_dup_name(realm->realm_id));
+    trp_route_set_peer(new_entry, tr_new_name("")); /* no peer, it's us */
+    trp_route_set_metric(new_entry, 0);
+    trp_route_set_trust_router(new_entry, tr_new_name(trust_router));
+    trp_route_set_next_hop(new_entry, tr_new_name(""));
+    trp_route_set_local(new_entry, 1);
+    entries[ii]=new_entry;
+  }
+
+  talloc_steal(mem_ctx, entries);
+  *n_routes=n_comms;
+ cleanup:
+  talloc_free(tmp_ctx);
+  return entries;
+}
+
+void tr_peer_status_change(TRP_PEER *peer, void *cookie)
+{
+  TRPS_INSTANCE *trps=talloc_get_type_abort(cookie, TRPS_INSTANCE);
+
+  if (TRP_SUCCESS!=trps_wildcard_route_req(trps, trp_peer_get_servicename(peer)))
+    tr_err("tr_send_wildcard: error sending wildcard route request.");
+}
+
+/* starts a trpc thread to connect to server:port */
+TRP_RC tr_trpc_initiate(TRPS_INSTANCE *trps, TRP_PEER *peer, struct event *ev)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRPC_INSTANCE *trpc=NULL;
+  TRP_CONNECTION *conn=NULL;
+  struct trpc_thread_data *thread_data=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  tr_debug("tr_trpc_initiate entered");
+  trpc=trpc_new(tmp_ctx);
+  if (trpc==NULL) {
+    tr_crit("tr_trpc_initiate: could not allocate TRPC_INSTANCE.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+  tr_debug("tr_trpc_initiate: allocated trpc");
+
+  conn=trp_connection_new(trpc);
+  if (conn==NULL) {
+    tr_crit("tr_trpc_initiate: could not allocate TRP_CONNECTION.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  trpc_set_conn(trpc, conn);
+  trpc_set_server(trpc, talloc_strdup(trpc, trp_peer_get_server(peer)));
+  trpc_set_port(trpc, trp_peer_get_port(peer));
+  trpc_set_gssname(trpc, trp_peer_dup_servicename(peer));
+  tr_debug("tr_trpc_initiate: allocated connection");
+  
+  /* start thread */
+  thread_data=talloc(trpc, struct trpc_thread_data);
+  if (thread_data==NULL) {
+    tr_crit("tr_trpc_initiate: could not allocate struct trpc_thread_data.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+  thread_data->trpc=trpc;
+  thread_data->trps=trps;
+
+  trps_add_trpc(trps, trpc); /* must add before starting thread */
+  pthread_create(trp_connection_get_thread(conn), NULL, tr_trpc_thread, thread_data);
+
+  tr_debug("tr_trpc_initiate: started trpc thread");
+  rc=TRP_SUCCESS;
+
+ cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* Add local routes to the route table. */
+TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *cur=NULL;
+  TRP_ROUTE **local_routes=NULL;
+  size_t n_routes=0;
+  size_t ii=0;
+  char *trust_router_name=talloc_asprintf(tmp_ctx, "%s:%d", cfg->internal->hostname, cfg->internal->trps_port);
+
+  /* determine our trust router name */
+  if (trust_router_name==NULL)
+    return TRP_NOMEM;
+
+  for (cur=cfg->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]);
+
+    talloc_free(local_routes);
+    local_routes=NULL;
+    n_routes=0;
+  }
+
+  talloc_free(tmp_ctx);
+  return TRP_SUCCESS;
+}
+
+/* decide how often to attempt to connect to a peer */
+static int tr_conn_attempt_due(TRPS_INSTANCE *trps, TRP_PEER *peer, struct timespec *when)
+{
+  return 1; /* currently make an attempt every cycle */
+}
+
+/* open missing connections to peers */
+TRP_RC tr_connect_to_peers(TRPS_INSTANCE *trps, struct event *ev)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
+  TRP_PEER *peer=NULL;
+  struct timespec curtime={0,0};
+  TRP_RC rc=TRP_ERROR;
+
+  if (clock_gettime(CLOCK_REALTIME, &curtime)) {
+    tr_err("tr_connect_to_peers: failed to read time.");
+    rc=TRP_CLOCKERR;
+    goto cleanup;
+  }
+
+  for (peer=trp_ptable_iter_first(iter, trps->ptable);
+       peer!=NULL;
+       peer=trp_ptable_iter_next(iter))
+  {
+    if (trps_find_trpc(trps, peer)==NULL) {
+      tr_debug("tr_connect_to_peers: %.*s missing connection.",
+               trp_peer_get_gssname(peer)->len, trp_peer_get_gssname(peer)->buf);
+      /* has it been long enough since we last tried? */
+      if (tr_conn_attempt_due(trps, peer, &curtime)) {
+        trp_peer_set_last_conn_attempt(peer, &curtime); /* we are trying again now */
+        if (tr_trpc_initiate(trps, peer, ev)!=TRP_SUCCESS) {
+          tr_err("tr_connect_to_peers: unable to initiate TRP connection to %s:%u.",
+                 trp_peer_get_server(peer),
+                 trp_peer_get_port(peer));
+        } 
+      }
+    }
+  }
+  rc=TRP_SUCCESS;
+    
+cleanup:
+  trp_ptable_iter_free(iter);
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+
+/* Called by the config manager after a change to the active configuration.
+ * Updates configuration of objects that do not know about the config manager. */
+void tr_config_changed(TR_CFG *new_cfg, void *cookie)
+{
+  TR_INSTANCE *tr=talloc_get_type_abort(cookie, TR_INSTANCE);
+  TRPS_INSTANCE *trps=tr->trps;
+
+  tr->cfgwatch->poll_interval.tv_sec=new_cfg->internal->cfg_poll_interval;
+  tr->cfgwatch->poll_interval.tv_usec=0;
+
+  tr->cfgwatch->settling_time.tv_sec=new_cfg->internal->cfg_settling_time;
+  tr->cfgwatch->settling_time.tv_usec=0;
+
+  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_clear_rtable(trps); /* should we do this every time??? */
+  tr_add_local_routes(trps, new_cfg); /* should we do this every time??? */
+  trps_update_active_routes(trps); /* find new routes */
+  trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
+  tr_trps_print_route_table(trps, stderr);
+}
+
diff --git a/tr/trpc_main.c b/tr/trpc_main.c
new file mode 100644 (file)
index 0000000..f04b4dd
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <talloc.h>
+#include <argp.h>
+#include <unistd.h>
+
+#include <gsscon.h>
+#include <tr_debug.h>
+#include <tr_trp.h>
+
+
+/* command-line option setup */
+
+/* argp global parameters */
+const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
+
+/* doc strings */
+static const char doc[]=PACKAGE_NAME " - TRP Client";
+static const char arg_doc[]="<message> <server> [<port>]"; /* string describing arguments, if any */
+
+/* define the options here. Fields are:
+ * { long-name, short-name, variable name, options, help description } */
+static const struct argp_option cmdline_options[] = {
+  { "repeat", 'r', "N", OPTION_ARG_OPTIONAL, "Repeat message until terminated, or N times." },
+  {NULL}
+};
+
+/* structure for communicating with option parser */
+struct cmdline_args {
+  char *msg;
+  char *server;
+  int port; /* optional */
+  int repeat; /* how many times to repeat, or -1 for infinite */
+};
+
+/* parser for individual options - fills in a struct cmdline_args */
+static error_t parse_option(int key, char *arg, struct argp_state *state)
+{
+  /* get a shorthand to the command line argument structure, part of state */
+  struct cmdline_args *arguments=state->input;
+
+  switch (key) {
+  case 'r':
+    if (arg==NULL)
+      arguments->repeat=-1;
+    else
+      arguments->repeat=strtol(arg, NULL, 10);
+    break;
+
+  case ARGP_KEY_ARG: /* handle argument (not option) */
+    switch (state->arg_num) {
+    case 0:
+      arguments->msg=arg;
+      break;
+
+    case 1:
+      arguments->server=arg;
+      break;
+
+    case 2:
+      arguments->port=strtol(arg, NULL, 10); /* optional */
+      break;
+
+    default:
+      /* too many arguments */
+      argp_usage(state);
+    }
+    break;
+
+  case ARGP_KEY_END: /* no more arguments */
+    if (state->arg_num < 2) {
+      /* not enough arguments encountered */
+      argp_usage(state);
+    }
+    break;
+
+  default:
+    return ARGP_ERR_UNKNOWN;
+  }
+
+  return 0; /* success */
+}
+
+/* assemble the argp parser */
+static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
+
+int main (int argc, 
+          char *argv[]) 
+{
+  TALLOC_CTX *main_ctx=talloc_new(NULL);
+  TRPC_INSTANCE *trpc=NULL;
+  TRP_CONNECTION *conn=NULL;
+  struct cmdline_args opts;
+
+  /* parse the command line*/
+  /* set defaults */
+  opts.msg=NULL;
+  opts.server=NULL;
+  opts.port=TRP_PORT;
+  opts.repeat=1;
+
+  argp_parse(&argp, argc, argv, 0, 0, &opts);
+
+  /* Use standalone logging */
+  tr_log_open();
+
+  /* set logging levels */
+  talloc_set_log_stderr();
+  tr_log_threshold(LOG_CRIT);
+  tr_console_threshold(LOG_DEBUG);
+
+  printf("TRPC Client:\nServer = %s, port = %i\n", opts.server, opts.port);
+  conn=trp_connection_new(trpc);
+  if (conn==NULL) {
+    printf("Could not allocate TRP_CONNECTION.\n");
+    return 1;
+  }
+  trpc = trpc_new(main_ctx);
+  trpc_set_server(trpc, opts.server);
+  trpc_set_port(trpc, opts.port);
+  trpc_set_conn(trpc, conn);
+  /* Set-up TRP connection */
+  if (TRP_SUCCESS != trpc_connect(trpc)) {
+    /* Handle error */
+    printf("Error in trpc_connect.\n");
+    return 1;
+  }
+
+  /* Send a TRP message */
+  while ((opts.repeat==-1) || (opts.repeat-->0)) {
+    if (TRP_SUCCESS != trpc_send_msg(trpc, opts.msg)) {
+      /* Handle error */
+      printf("Error in trpc_send_request.");
+      return 1;
+    }
+    usleep(1000000);
+  }
+    
+  /* Clean-up the TRP client instance, and exit */
+  trpc_free(trpc);
+
+  return 0;
+}
+
diff --git a/trp/msgtst.c b/trp/msgtst.c
new file mode 100644 (file)
index 0000000..aef0c19
--- /dev/null
@@ -0,0 +1,62 @@
+/* Testing trp message encoding / decoding */
+
+/* compiles with: gcc -o msgtst -I../include msgtst.c trp_msg.c $(pkg-config --cflags --libs glib-2.0) ../common/tr_debug.c  ../common/tr_name.c ../common/tr_msg.c -ltalloc -ljansson */
+
+#include <stdio.h>
+#include <talloc.h>
+
+#include <trust_router/trp.h>
+#include <tr_msg.h>
+#include <tr_debug.h>
+
+#define MAX_MSG_LEN 8192
+
+int main(int argc, const char *argv[])
+{
+  TALLOC_CTX *main_ctx=talloc_new(NULL);
+  FILE *f;
+  char *buf;
+  size_t buflen;
+  TR_MSG *msg;
+  
+  if (argc != 2) {
+    printf("Usage: %s <input file>\n\n", argv[0]);
+    exit(-1);
+  }
+
+  buf=malloc(MAX_MSG_LEN);
+  if (!buf) {
+    printf("Allocation error.\n\n");
+    exit(-1);
+  }
+
+  f=fopen(argv[1], "r");
+  if (!f) {
+    printf("Error opening %s for reading.\n\n", argv[1]);
+    exit(-1);
+  }
+
+  printf("Reading from %s...\n", argv[1]);
+
+  buflen=fread(buf, sizeof(char), MAX_MSG_LEN, f);
+  if (buflen==0) {
+    printf("File empty.\n\n");
+    exit(0);
+  }
+
+  if (buflen>=MAX_MSG_LEN)
+    printf("Warning: file may exceed maximum message length (%d bytes).\n", MAX_MSG_LEN);
+
+  msg=tr_msg_decode(buf, buflen);
+
+/*  if (rc==TRP_SUCCESS)
+    trp_msg_print(msg);*/
+
+  printf("\nEncoding...\n");
+
+  printf("Result: \n%s\n\n", tr_msg_encode(msg));
+
+  talloc_report_full(main_ctx, stdout);
+
+  return 0;
+}
diff --git a/trp/test/ptbl_test.c b/trp/test/ptbl_test.c
new file mode 100644 (file)
index 0000000..15d6d15
--- /dev/null
@@ -0,0 +1,255 @@
+#include <stdio.h>
+#include <talloc.h>
+#include <assert.h>
+
+#include <trp_internal.h>
+#include <trp_ptable.h>
+
+struct peer_entry {
+  char *server;
+  unsigned int port;
+  unsigned int linkcost;
+};
+
+static struct peer_entry peer_data[]={
+  {"peer0", 10000, 0x0001},
+  {"peer1", 15000, 0x0002},
+  {"peer2", 20000, 0x0004},
+  {"peer3", 25000, 0x0008},
+  {"peer4", 30000, 0x0010}
+};
+static size_t n_peers=sizeof(peer_data)/sizeof(peer_data[0]);
+
+static void populate_ptable(TRPS_INSTANCE *trps)
+{
+  TRP_PEER *new_peer;
+  int i;
+
+  for (i=0; i<n_peers; i++) {
+    new_peer=trp_peer_new(NULL);
+    assert(new_peer!=NULL);
+    trp_peer_set_server(new_peer, peer_data[i].server);
+    assert(trp_peer_get_server(new_peer)!=NULL);
+    trp_peer_set_port(new_peer, peer_data[i].port);
+    trp_peer_set_linkcost(new_peer, peer_data[i].linkcost);
+    assert(trps_add_peer(trps, new_peer)==TRP_SUCCESS);
+  }
+}
+
+static struct peer_entry *find_peer_entry(char *server)
+{
+  int i;
+  for (i=0; i<n_peers; i++) {
+    if (0==strcmp(server, peer_data[i].server)) {
+      return (peer_data+i);
+    }
+  }
+  return NULL;
+}
+
+static void verify_ptable(TRPS_INSTANCE *trps)
+{
+  struct peer_entry *peer_entry=NULL;
+  TRP_PEER *peer;
+  char *s;
+  TR_NAME *gssname;
+
+  peer=trps->ptable->head;
+  while (peer!=NULL) {
+    peer_entry=find_peer_entry(trp_peer_get_server(peer));
+    assert(peer_entry!=NULL);
+    assert(!strcmp(trp_peer_get_server(peer), peer_entry->server));
+    assert(trp_peer_get_port(peer)==peer_entry->port);
+    assert(trp_peer_get_linkcost(peer)==peer_entry->linkcost);
+    assert(0<asprintf(&s, "trustrouter@%s", peer_entry->server));
+    gssname=tr_new_name(s);
+    free(s);
+    assert(gssname!=NULL);
+    assert(!tr_name_cmp(trp_peer_get_gssname(peer), gssname));
+    tr_free_name(gssname);
+    peer=peer->next;
+  }
+}
+
+struct route_data {
+  char *apc;
+  char *realm;
+  char *peer;
+  unsigned int metric;
+  char *trust_router;
+  char *next_hop;
+  int selected;
+  unsigned int interval;
+  int verified; /* for testing */
+};
+static struct route_data route_table[]={
+  {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+  {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer0", 1, "tr.r0.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer0", 0, "tr.r1.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer0", 1, "tr.r3.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer0", 2, "tr.r4.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer1", 0, "tr.r0.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer1", 1, "tr.r1.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer1", 1, "tr.r2.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer1", 2, "tr.r4.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer2", 0, "tr.r0.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer2", 2, "tr.r1.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer2", 2, "tr.r2.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer2", 1, "tr.r3.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+};
+static size_t n_routes=sizeof(route_table)/sizeof(route_table[0]);
+
+/* These are the correct updates to select from the above route table for each peer.
+ * The rule is: send selected route unless it is through that peer, otherwise send
+ * the best (lowest metric) alternative route. 
+ *
+ * In a few cases there are multiple valid options (when a two non-selected routes
+ * exist). If these tests are failing, it may be that the trps code is selecting another
+ * valid option, so check that. Probably ought to tweak metrics to avoid that ambiguity. */
+static struct route_data update_table[][10]={
+  { /* peer0 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer1", 1, "tr.r2.apc0", "trustrouter@peer1", 0, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer1 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer2", 1, "tr.r3.apc0", "trustrouter@peer2", 0, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer2 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer1", 2, "tr.r4.apc0", "trustrouter@peer1", 0, 60, 0},
+    {NULL}
+  },
+  { /* peer3 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer4 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  }
+};
+
+static void populate_rtable(TRPS_INSTANCE *trps)
+{
+  int i;
+  TRP_RENTRY *new;
+
+  for (i=0; i<n_routes; i++) {
+    new=trp_rentry_new(NULL);
+    assert(new!=NULL);
+    trp_rentry_set_apc(new, tr_new_name(route_table[i].apc));
+    trp_rentry_set_realm(new, tr_new_name(route_table[i].realm));
+    trp_rentry_set_peer(new, tr_new_name(route_table[i].peer));
+    trp_rentry_set_metric(new, route_table[i].metric);
+    trp_rentry_set_trust_router(new, tr_new_name(route_table[i].trust_router));
+    trp_rentry_set_next_hop(new, tr_new_name(route_table[i].next_hop));
+    trp_rentry_set_selected(new, route_table[i].selected);
+    trp_rentry_set_interval(new, route_table[i].interval);
+    /* do not set expiry */
+    trp_rtable_add(trps->rtable, new);
+    new=NULL;
+  }
+}
+
+static void verify_update(TRP_RENTRY **updates, size_t n_updates, struct route_data *expected)
+{
+  int ii,jj;
+  int found;
+
+  for(jj=0; jj<n_updates; jj++) {
+    found=0;
+    for (ii=0; expected[ii].apc!=NULL; ii++) {
+      if ((0==strcmp(expected[ii].apc, updates[jj]->apc->buf))
+         &&(0==strcmp(expected[ii].realm, updates[jj]->realm->buf))
+         &&(0==strcmp(expected[ii].peer, updates[jj]->peer->buf))
+         &&(expected[ii].metric==updates[jj]->metric)
+         &&(0==strcmp(expected[ii].trust_router, updates[jj]->trust_router->buf))
+         &&(0==strcmp(expected[ii].next_hop, updates[jj]->next_hop->buf))
+         &&(expected[ii].selected==updates[jj]->selected)
+         &&(expected[ii].interval==updates[jj]->interval)) {
+        assert(expected[ii].verified==0); /* should only encounter each entry once */
+        expected[ii].verified=1;
+        found=1;
+        continue;
+      }
+    }
+    if (!found) {
+      printf("missing:\n%s\n", trp_rentry_to_str(NULL,updates[jj], " | "));
+      assert(0);
+    }
+  }
+  for(ii=0; expected[ii].apc!=NULL; ii++)
+    assert(expected[ii].verified==1);
+}
+
+static void verify_update_selection(TRPS_INSTANCE *trps)
+{
+  int ii;
+  TRP_RENTRY **updates=NULL;
+  size_t n_updates;
+  TR_NAME *gssname=NULL;
+  char *s;
+
+  for (ii=0; ii<n_peers; ii++) {
+    assert(0<asprintf(&s, "trustrouter@%s", peer_data[ii].server));
+    assert(NULL!=(gssname=tr_new_name(s)));
+    free(s);
+    updates=trps_select_updates_for_peer(NULL, trps, gssname, &n_updates);
+    tr_free_name(gssname);
+    verify_update(updates, n_updates, update_table[ii]);
+    talloc_free(updates);
+  }
+}
+
+int main(void)
+{
+  TALLOC_CTX *main_ctx=talloc_new(NULL);
+  TRPS_INSTANCE *trps;
+  char *s;
+
+  trps=trps_new(main_ctx);
+
+  printf("\nPopulating peer table...\n");
+  populate_ptable(trps);
+
+  printf("\nVerifying peer table...\n");
+  verify_ptable(trps);
+
+  printf("\nPopulating route table...\n");
+  populate_rtable(trps);
+  s=trp_rtable_to_str(main_ctx, trps->rtable, " | ", NULL);
+  printf("Route Table:\n%s---\n", s);
+
+  printf("\nVerifying route update selection...\n");
+  verify_update_selection(trps);
+
+  printf("\nDone\n\n");
+  talloc_report_full(main_ctx, stderr);
+  talloc_free(main_ctx);
+  talloc_report_full(NULL, stderr);
+  return 0;
+}
diff --git a/trp/test/rtbl_test.c b/trp/test/rtbl_test.c
new file mode 100644 (file)
index 0000000..7f5d575
--- /dev/null
@@ -0,0 +1,306 @@
+#include <stdio.h>
+#include <talloc.h>
+#include <string.h>
+#include <assert.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+#include <trp_rtable.h>
+
+char *apc[]={"apc1", "apc2", "apc3"};
+size_t n_apc=sizeof(apc)/sizeof(apc[0]);
+char *realm[]={"realm1", "realm2", "realm3"};
+size_t n_realm=sizeof(realm)/sizeof(realm[0]);
+char *peer[]={"peer1", "peer2", "peer3"};
+size_t n_peer=sizeof(peer)/sizeof(peer[0]);
+
+static unsigned int metric1(size_t a, size_t b, size_t c)
+{
+  return a+b+c;
+}
+
+static unsigned int metric2(size_t a, size_t b, size_t c)
+{
+  return a+b+c+25;
+}
+
+static unsigned int metric3(size_t a, size_t b, size_t c)
+{
+  return a*(b+c)+b*c;
+}
+
+static void populate_rtable(TRP_RTABLE *table, unsigned int (*metric)(size_t, size_t, size_t))
+{
+  TRP_RENTRY *entry=NULL;
+  size_t ii=0, jj=0, kk=0;
+  struct timespec ts={0,0};
+
+  for (ii=0; ii<n_apc; ii++) {
+    for (jj=0; jj<n_realm; jj++) {
+      for (kk=0; kk<n_peer; kk++) {
+        entry=trp_rentry_new(NULL);
+        trp_rentry_set_apc(entry, tr_new_name(apc[ii]));
+        trp_rentry_set_realm(entry, tr_new_name(realm[jj]));
+        trp_rentry_set_trust_router(entry, tr_new_name(realm[jj]));
+        trp_rentry_set_peer(entry, tr_new_name(peer[kk]));
+        trp_rentry_set_metric(entry, metric(ii,jj,kk));
+        trp_rentry_set_next_hop(entry, tr_new_name(peer[kk]));
+        ts=(struct timespec){jj+1,ii+kk+1};
+        trp_rentry_set_expiry(entry, &ts);
+        trp_rtable_add(table, entry);
+        entry=NULL; /* entry belongs to the table now */
+      }
+    }
+  }
+}
+
+static void verify_rtable(TRP_RTABLE *table, unsigned int (*metric)(size_t, size_t, size_t))
+{
+  TRP_RENTRY *entry=NULL;
+  size_t ii=0, jj=0, kk=0;
+  size_t len=0;
+  TR_NAME *apc_n, *realm_n, *peer_n;
+
+  for (ii=0; ii<n_apc; ii++) {
+    for (jj=0; jj<n_realm; jj++) {
+      for (kk=0; kk<n_peer; kk++) {
+        apc_n=tr_new_name(apc[ii]);
+        realm_n=tr_new_name(realm[jj]);
+        peer_n=tr_new_name(peer[kk]);
+        entry=trp_rtable_get_entry(table, apc_n, realm_n, peer_n);
+        tr_free_name(apc_n);
+        tr_free_name(realm_n);
+        tr_free_name(peer_n);
+
+        assert(entry!=NULL);
+
+        len=trp_rentry_get_apc(entry)->len;
+        assert(len==strlen(apc[ii]));
+        assert(0==strncmp(trp_rentry_get_apc(entry)->buf, apc[ii], len));
+
+        len=trp_rentry_get_realm(entry)->len;
+        assert(len==strlen(realm[jj]));
+        assert(0==strncmp(trp_rentry_get_realm(entry)->buf, realm[jj], len));
+        
+        len=trp_rentry_get_peer(entry)->len;
+        assert(len==strlen(peer[kk]));
+        assert(0==strncmp(trp_rentry_get_peer(entry)->buf, peer[kk], len));
+        
+        len=trp_rentry_get_trust_router(entry)->len;
+        assert(len==strlen(realm[jj]));
+        assert(0==strncmp(trp_rentry_get_trust_router(entry)->buf, realm[jj], len));
+
+        assert(trp_rentry_get_metric(entry)==metric(ii,jj,kk));
+
+        len=trp_rentry_get_next_hop(entry)->len;
+        assert(len==strlen(peer[kk]));
+        assert(0==strncmp(trp_rentry_get_next_hop(entry)->buf, peer[kk], len));
+
+        assert(trp_rentry_get_selected(entry)==0);
+        assert(trp_rentry_get_expiry(entry)->tv_sec==jj+1);
+        assert(trp_rentry_get_expiry(entry)->tv_nsec==ii+kk+1);
+
+        printf("{%s %s %s} entry OK!\n", apc[ii], realm[jj], peer[kk]);
+      }
+    }
+  }
+}
+
+static int is_in(char *a, char *b[], size_t n_b)
+{
+  size_t count=0;
+
+  while (n_b--) {
+    if (0==strcmp(a, b[n_b]))
+      count++;
+  }
+  return count;
+}
+static void verify_apc_list(TRP_RTABLE *table)
+{
+  size_t n=0;
+  TR_NAME **apcs_found=trp_rtable_get_apcs(table, &n);
+  assert(n==n_apc);
+  while(n--)
+    assert(1==is_in(apcs_found[n]->buf, apc, n_apc));
+}
+
+static void verify_apc_realm_lists(TRP_RTABLE *table)
+{
+  size_t n=0, ii=0;
+  TR_NAME *apc_n=NULL, **realms_found=NULL;
+
+  for (ii=0; ii<n_apc; ii++) {
+    apc_n=tr_new_name(apc[ii]);
+    realms_found=trp_rtable_get_apc_realms(table, apc_n, &n);
+    tr_free_name(apc_n);
+    assert(n==n_realm);
+    while (n--)
+      assert(1==is_in(realms_found[n]->buf, realm, n_realm));
+    talloc_free(realms_found);
+    printf("APC %s ok!\n", apc[ii]);
+  }
+}
+
+static void verify_get_apc_entries(TRP_RTABLE *table)
+{
+  size_t n=0, ii=0;
+  TRP_RENTRY **apc_entries=NULL;
+  TR_NAME *apc_n=NULL;
+
+  for (ii=0; ii<n_apc; ii++) {
+    apc_n=tr_new_name(apc[ii]);
+    apc_entries=trp_rtable_get_apc_entries(table, apc_n, &n);
+    tr_free_name(apc_n);
+    assert(n==n_realm*n_peer);
+    while (n--) {
+      assert(0==strncmp(trp_rentry_get_apc(apc_entries[n])->buf,
+                        apc[ii],
+                        trp_rentry_get_apc(apc_entries[n])->len));
+      assert(1==is_in(trp_rentry_get_realm(apc_entries[n])->buf, realm, n_realm));
+      assert(1==is_in(trp_rentry_get_peer(apc_entries[n])->buf, peer, n_peer));
+    }
+    printf("APC %s ok!\n", apc[ii]);
+    talloc_free(apc_entries);
+  }
+}
+
+static void verify_get_realm_entries(TRP_RTABLE *table)
+{
+  size_t n=0, ii=0, jj=0;
+  TRP_RENTRY **realm_entries=NULL;
+  TR_NAME *apc_n=NULL, *realm_n=NULL;
+
+  for (ii=0; ii<n_apc; ii++) {
+    for (jj=0; jj<n_realm; jj++) {
+      apc_n=tr_new_name(apc[ii]);
+      realm_n=tr_new_name(realm[jj]);
+      realm_entries=trp_rtable_get_realm_entries(table, apc_n, realm_n, &n);
+      tr_free_name(apc_n);
+      tr_free_name(realm_n);
+      assert(n==n_peer);
+      while (n--) {
+        assert(0==strncmp(trp_rentry_get_apc(realm_entries[n])->buf,
+                          apc[ii],
+                          trp_rentry_get_apc(realm_entries[n])->len));
+        assert(0==strncmp(trp_rentry_get_realm(realm_entries[n])->buf,
+                          realm[jj],
+                          trp_rentry_get_realm(realm_entries[n])->len));
+        assert(1==is_in(trp_rentry_get_peer(realm_entries[n])->buf, peer, n_peer));
+      }
+      printf("APC %s realm %s ok!\n", apc[ii], realm[jj]);
+      talloc_free(realm_entries);
+    }
+  }
+}
+
+/* doesn't work if c not in a */
+static size_t get_index(char *c, char **a, size_t n_a)
+{
+  while(n_a--) {
+    if (0==strcmp(c, a[n_a]))
+      return n_a;
+  }
+  return 0;
+}
+
+static void update_metric(TRP_RTABLE *table, unsigned int (*new_metric)(size_t, size_t, size_t))
+{
+  TRP_RENTRY **entries=NULL;
+  size_t n=0, ii=0,jj=0,kk=0;
+
+  entries=trp_rtable_get_entries(table, &n);
+  while (n--) {
+    ii=get_index(trp_rentry_get_apc(entries[n])->buf, apc, n_apc);
+    jj=get_index(trp_rentry_get_realm(entries[n])->buf, realm, n_realm);
+    kk=get_index(trp_rentry_get_peer(entries[n])->buf, peer, n_peer);
+    trp_rentry_set_metric(entries[n],
+                          new_metric(ii,jj,kk));
+  }
+  talloc_free(entries);
+}
+
+static void remove_entries(TRP_RTABLE *table)
+{
+  size_t n=trp_rtable_size(table);
+  size_t ii,jj,kk;
+  TR_NAME *apc_n, *realm_n, *peer_n;
+  TRP_RENTRY *entry=NULL;
+
+  for (ii=0; ii<n_apc; ii++) {
+    for (jj=0; jj<n_realm; jj++) {
+      for (kk=0; kk<n_realm; kk++) {
+        apc_n=tr_new_name(apc[ii]);
+        realm_n=tr_new_name(realm[jj]);
+        peer_n=tr_new_name(peer[kk]);
+        entry=trp_rtable_get_entry(table, apc_n, realm_n, peer_n);
+        assert(entry !=NULL);
+        tr_free_name(apc_n);
+        tr_free_name(realm_n);
+        tr_free_name(peer_n);
+        trp_rtable_remove(table, entry);
+        entry=NULL;
+        assert(trp_rtable_size(table)==--n);
+      }
+    }
+  }
+}
+
+
+static void print_rtable(TRP_RTABLE *table)
+{
+  char *s=trp_rtable_to_str(NULL, table, NULL, NULL);
+  printf(s);
+  talloc_free(s);
+}
+
+int main(void)
+{
+  TRP_RTABLE *table=NULL;
+  table=trp_rtable_new();
+  populate_rtable(table, metric1);
+  print_rtable(table);
+
+  printf("\nVerifying routing table...\n");
+  verify_rtable(table, metric1);
+  printf("                         ...success!\n");
+
+  printf("\nVerifying APC list...\n");
+  verify_apc_list(table);
+  printf("                    ...success!\n");
+
+  printf("\nVerifying APC realm lists...\n");
+  verify_apc_realm_lists(table);
+  printf("                    ...success!\n");
+
+  printf("\nVerifying APC entry lists...\n");
+  verify_get_apc_entries(table);
+  printf("                           ...success!\n");
+
+  printf("\nVerifying realm entry lists...\n");
+  verify_get_realm_entries(table);
+  printf("                              ...success!\n");
+
+  printf("\nVerifying table value update...\n");
+  update_metric(table, metric2); /* changes the metric value in each element in-place */
+  verify_rtable(table, metric2);
+  printf("                              ...success!\n");
+
+  printf("\nVerifying element replacement...\n");
+  populate_rtable(table, metric3); /* replaces all the elements with new ones */
+  verify_rtable(table, metric3);
+  printf("                               ...success!\n");
+
+  printf("\nVerifying element removal...\n");
+  remove_entries(table);
+  print_rtable(table);
+  printf("                           ...success!\n");
+
+  printf("\nRepopulating table...\n");
+  populate_rtable(table, metric3);
+  verify_rtable(table, metric3);
+  printf("                               ...success!\n");
+
+  trp_rtable_free(table);
+  return 0;
+}
diff --git a/trp/trp_conn.c b/trp/trp_conn.c
new file mode 100644 (file)
index 0000000..cd45bc5
--- /dev/null
@@ -0,0 +1,365 @@
+#include <gsscon.h>
+#include <gssapi.h>
+#include <fcntl.h>
+#include <talloc.h>
+#include <unistd.h>
+
+#include <tr_debug.h>
+#include <trp_internal.h>
+
+/* Threading note: mutex lock is only used for protecting get_status() and set_status().
+ * If needed, locking for other operations (notably adding/removing connections) must be managed
+ * by whomever is holding on to the connection list. */
+
+int trp_connection_lock(TRP_CONNECTION *conn)
+{
+  return pthread_mutex_lock(&(conn->mutex));
+}
+
+int trp_connection_unlock(TRP_CONNECTION *conn)
+{
+  return pthread_mutex_unlock(&(conn->mutex));
+}
+
+int trp_connection_get_fd(TRP_CONNECTION *conn)
+{
+  return conn->fd;
+}
+
+void trp_connection_set_fd(TRP_CONNECTION *conn, int fd)
+{
+  conn->fd=fd;
+}
+
+/* we use the gss name of the peer to identify it */
+static TRP_RC trp_connection_set_peer(TRP_CONNECTION *conn)
+{
+  OM_uint32 major_status=0;
+  OM_uint32 minor_status=0;
+  gss_name_t source_name=GSS_C_NO_NAME;
+  gss_name_t target_name=GSS_C_NO_NAME;
+  gss_buffer_desc peer_display_name={0,NULL};
+  int local=0;
+
+  major_status=gss_inquire_context(&minor_status,
+                                   *trp_connection_get_gssctx(conn),
+                                  &source_name,
+                                  &target_name,
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                  &local,
+                                   NULL);
+
+  if (major_status != GSS_S_COMPLETE) {
+    tr_err("trp_connection_set_peer: unable to identify GSS peer.");
+    if (source_name!=GSS_C_NO_NAME)
+      gss_release_name(&minor_status, &source_name);
+    if (target_name!=GSS_C_NO_NAME)
+      gss_release_name(&minor_status, &target_name);
+    return TRP_ERROR;
+  }
+
+  if (local) {
+    /* we are the source, peer is the target */
+    major_status=gss_display_name(&minor_status, target_name, &peer_display_name, NULL);
+  } else {
+    /* we are the target, peer is the source */
+    major_status=gss_display_name(&minor_status, source_name, &peer_display_name, NULL);
+  }
+  gss_release_name(&minor_status, &source_name);
+  gss_release_name(&minor_status, &target_name);
+
+  conn->peer=tr_new_name(peer_display_name.value);
+  if (conn->peer==NULL)
+    tr_err("trp_connection_set_peer: unable to allocate peer name.");
+  else {
+    if (conn->peer->len != peer_display_name.length) {
+      tr_err("trp_connection_set_peer: error converting GSS display name to TR_NAME.");
+      tr_free_name(conn->peer);
+      conn->peer=NULL;
+    }
+  }
+  gss_release_buffer(&minor_status, &peer_display_name);
+
+  if (conn->peer==NULL)
+    return TRP_ERROR;
+
+  tr_debug("trp_connection_set_peer: set peer for %p to %.*s (%p).", conn, conn->peer->len, conn->peer->buf, conn->peer);
+  return TRP_SUCCESS;
+}
+
+TR_NAME *trp_connection_get_peer(TRP_CONNECTION *conn)
+{
+  return conn->peer;
+}
+
+TR_NAME *trp_connection_get_gssname(TRP_CONNECTION *conn)
+{
+  return conn->gssname;
+}
+
+void trp_connection_set_gssname(TRP_CONNECTION *conn, TR_NAME *gssname)
+{
+  conn->gssname=gssname;
+}
+
+gss_ctx_id_t *trp_connection_get_gssctx(TRP_CONNECTION *conn)
+{
+  return conn->gssctx;
+}
+
+void trp_connection_set_gssctx(TRP_CONNECTION *conn, gss_ctx_id_t *gssctx)
+{
+  conn->gssctx=gssctx;
+}
+
+TRP_CONNECTION_STATUS trp_connection_get_status(TRP_CONNECTION *conn)
+{
+  TRP_CONNECTION_STATUS status=TRP_CONNECTION_UNKNOWN;
+  trp_connection_lock(conn);
+  status=conn->status;
+  trp_connection_unlock(conn);
+  return status;
+}
+
+static void trp_connection_set_status(TRP_CONNECTION *conn, TRP_CONNECTION_STATUS status)
+{
+  TRP_CONNECTION_STATUS old_status=TRP_CONNECTION_UNKNOWN;
+  trp_connection_lock(conn);
+  old_status=conn->status;
+  conn->status=status;
+  trp_connection_unlock(conn);
+  if ((status!=old_status) && (conn->status_change_cb!=NULL))
+      conn->status_change_cb(conn, conn->status_change_cookie);
+}
+
+pthread_t *trp_connection_get_thread(TRP_CONNECTION *conn)
+{
+  return conn->thread;
+}
+
+void trp_connection_set_thread(TRP_CONNECTION *conn, pthread_t *thread)
+{
+  conn->thread=thread;
+}
+
+TRP_CONNECTION *trp_connection_get_next(TRP_CONNECTION *conn)
+{
+  return conn->next;
+}
+
+static void trp_connection_set_next(TRP_CONNECTION *conn, TRP_CONNECTION *next)
+{
+  conn->next=next;
+}
+
+/* Ok to call more than once; guarantees connection no longer in the list. Does not free removed element.
+ * Returns handle to new list, you must replace your old handle on the list with this.  */
+TRP_CONNECTION *trp_connection_remove(TRP_CONNECTION *conn, TRP_CONNECTION *remove)
+{
+  TRP_CONNECTION *cur=conn;
+  TRP_CONNECTION *last=NULL;
+
+  if (cur==NULL)
+    return NULL;
+
+  /* first element is a special case */
+  if (cur==remove) {
+    conn=trp_connection_get_next(cur); /* advance list head */
+  } else {
+    /* it was not the first element */
+    last=cur;
+    cur=trp_connection_get_next(cur);
+    while (cur!=NULL) {
+      if (cur==remove) {
+        trp_connection_set_next(last, trp_connection_get_next(cur));
+        break;
+      }
+      last=cur;
+      cur=trp_connection_get_next(cur);
+    }
+  }
+  return conn;
+}
+
+static TRP_CONNECTION *trp_connection_get_tail(TRP_CONNECTION *conn)
+{
+  while((conn!=NULL)&&(trp_connection_get_next(conn)!=NULL))
+    conn=trp_connection_get_next(conn);
+  return conn;
+}
+
+void trp_connection_append(TRP_CONNECTION *conn, TRP_CONNECTION *new)
+{
+  trp_connection_set_next(trp_connection_get_tail(conn), new);
+}
+
+static void trp_connection_mutex_init(TRP_CONNECTION *conn)
+{
+  pthread_mutex_init(&(conn->mutex), NULL);
+}
+
+/* talloc destructor for a connection: ensures connection is closed, memory freed */
+static int trp_connection_destructor(void *object)
+{
+  TRP_CONNECTION *conn=talloc_get_type_abort(object, TRP_CONNECTION); /* aborts on wrong type */
+  if ((trp_connection_get_status(conn)!=TRP_CONNECTION_CLOSED)
+     && (trp_connection_get_fd(conn)!=-1))
+    close(trp_connection_get_fd(conn));
+  if (conn->peer!=NULL)
+    tr_free_name(conn->peer);
+  if (conn->gssname!=NULL)
+    tr_free_name(conn->gssname);
+  return 0;
+}
+
+TRP_CONNECTION *trp_connection_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_CONNECTION *new_conn=talloc(mem_ctx, TRP_CONNECTION);
+  gss_ctx_id_t *gssctx=NULL;
+  pthread_t *thread=NULL;
+  
+
+  if (new_conn != NULL) {
+    trp_connection_set_next(new_conn, NULL);
+    trp_connection_set_fd(new_conn, -1);
+    trp_connection_set_gssname(new_conn, NULL);
+    trp_connection_mutex_init(new_conn);
+    new_conn->peer=NULL; /* no true set function for this */
+    new_conn->status_change_cb=NULL;
+    new_conn->status_change_cookie=NULL;
+    new_conn->status=TRP_CONNECTION_CLOSED;
+
+    thread=talloc(new_conn, pthread_t);
+    if (thread==NULL) {
+      talloc_free(new_conn);
+      return NULL;
+    }
+    trp_connection_set_thread(new_conn, thread);
+
+    gssctx=talloc(new_conn, gss_ctx_id_t);
+    if (gssctx==NULL) {
+      talloc_free(new_conn);
+      return NULL;
+    }
+    trp_connection_set_gssctx(new_conn, gssctx);
+    talloc_set_destructor((void *)new_conn, trp_connection_destructor);
+  }
+  return new_conn;
+}
+
+void trp_connection_free(TRP_CONNECTION *conn)
+{
+  talloc_free(conn);
+}
+
+void trp_connection_close(TRP_CONNECTION *conn)
+{
+  if ((conn->status!=TRP_CONNECTION_DOWN) && (conn->fd>0))
+    close(trp_connection_get_fd(conn));
+  trp_connection_set_fd(conn, -1);
+  trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
+}
+
+/* returns 0 on authorization success, 1 on failure, or -1 in case of error */
+int trp_connection_auth(TRP_CONNECTION *conn, TRP_AUTH_FUNC auth_callback, void *callback_data)
+{
+  int rc = 0;
+  int auth, autherr = 0;
+  gss_buffer_desc nameBuffer = {0, NULL};
+  gss_ctx_id_t *gssctx=trp_connection_get_gssctx(conn);
+
+  nameBuffer.length = trp_connection_get_gssname(conn)->len;
+  nameBuffer.value = tr_name_strdup(trp_connection_get_gssname(conn));
+
+  tr_debug("trp_connection_auth: beginning passive authentication");
+  if (trp_connection_get_status(conn)!=TRP_CONNECTION_AUTHORIZING)
+    tr_warning("trp_connection_auth: warning: connection was not in TRP_CONNECTION_AUTHORIZING state.");
+
+  rc = gsscon_passive_authenticate(trp_connection_get_fd(conn), nameBuffer, gssctx, auth_callback, callback_data);
+  gss_release_buffer(NULL, &nameBuffer);
+  if (rc!=0) {
+    tr_debug("trp_connection_auth: Error from gsscon_passive_authenticate(), rc = 0x%08X.", rc);
+    trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
+    return -1;
+  }
+
+  tr_debug("trp_connection_auth: beginning second stage authentication");
+  if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
+    tr_debug("trp_connection_auth: Error from gsscon_authorize, rc = %d, autherr = %d.", 
+             rc, autherr);
+    trp_connection_set_status(conn, TRP_CONNECTION_DOWN);
+    return -1;
+  }
+
+  trp_connection_set_peer(conn);
+  trp_connection_set_status(conn, TRP_CONNECTION_UP);
+
+  if (auth)
+    tr_debug("trp_connection_auth: Connection authenticated, fd = %d.", trp_connection_get_fd(conn));
+  else
+    tr_debug("trp_connection_auth: Authentication failed, fd = %d.", trp_connection_get_fd(conn));
+
+  return !auth;
+}
+
+/* Accept connection */
+TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gssname)
+{
+  int conn_fd=-1;
+  TRP_CONNECTION *conn=NULL;
+
+  conn_fd = accept(listen, NULL, NULL);
+
+  if (0 > conn_fd) {
+    tr_notice("trp_connection_accept: accept() returned error.");
+    return NULL;
+  }
+  conn=trp_connection_new(mem_ctx);
+  trp_connection_set_fd(conn, conn_fd);
+  trp_connection_set_gssname(conn, gssname);
+  trp_connection_set_status(conn, TRP_CONNECTION_AUTHORIZING);
+  return conn;
+}
+
+/* Initiate connection */
+TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int port)
+{
+  int err = 0;
+  int fd=-1;
+  unsigned int use_port=0;
+
+  if (0 == port)
+    use_port = TRP_PORT;
+  else 
+    use_port = port;
+
+  if (conn==NULL) {
+    tr_err("trp_connection_initiate: null TRP_CONNECTION");
+    return TRP_BADARG;
+  }
+
+  tr_debug("trp_connection_initiate: opening GSS connection to %s:%d",
+           server,
+           use_port);
+  err = gsscon_connect(server,
+                       use_port,
+                       "trustrouter",
+                      &fd,
+                       trp_connection_get_gssctx(conn));
+  if (err) {
+    tr_err("trp_connection_initiate: connection failed.");
+    return TRP_ERROR;
+  } else {
+    tr_debug("trp_connection_initiate: connected.");
+    trp_connection_set_fd(conn, fd);
+    if (trp_connection_set_peer(conn)!=TRP_SUCCESS) {
+      tr_err("trp_connection_initiate: error setting peer gssname.");
+      trp_connection_close(conn);
+      return TRP_ERROR;
+    }
+    trp_connection_set_status(conn, TRP_CONNECTION_UP);
+    return TRP_SUCCESS;
+  }
+}
diff --git a/trp/trp_ptable.c b/trp/trp_ptable.c
new file mode 100644 (file)
index 0000000..14a2509
--- /dev/null
@@ -0,0 +1,308 @@
+#include <time.h>
+#include <talloc.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+#include <trp_ptable.h>
+#include <tr_debug.h>
+
+static int trp_peer_destructor(void *object)
+{
+  TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER);
+  if (peer->servicename!=NULL)
+    tr_free_name(peer->servicename);
+  if (peer->gssname!=NULL)
+    tr_free_name(peer->gssname);
+  return 0;
+}
+TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
+{
+  TRP_PEER *peer=talloc(memctx, TRP_PEER);
+  if (peer!=NULL) {
+    peer->next=NULL;
+    peer->server=NULL;
+    peer->servicename=NULL;
+    peer->gssname=NULL;
+    peer->port=0;
+    peer->linkcost=TRP_LINKCOST_DEFAULT;
+    peer->last_conn_attempt=(struct timespec){0,0};
+    peer->outgoing_status=PEER_DISCONNECTED;
+    peer->incoming_status=PEER_DISCONNECTED;
+    peer->conn_status_cb=NULL;
+    peer->conn_status_cookie=NULL;
+    talloc_set_destructor((void *)peer, trp_peer_destructor);
+  }
+  return peer;
+}
+
+void trp_peer_free(TRP_PEER *peer)
+{
+  talloc_free(peer);
+}
+
+static TRP_PEER *trp_peer_tail(TRP_PEER *peer)
+{
+  while (peer->next!=NULL) {
+    peer=peer->next;
+  }
+  return peer;
+}
+
+char *trp_peer_get_server(TRP_PEER *peer)
+{
+  return peer->server;
+}
+
+static void trp_peer_set_servicename(TRP_PEER *peer, const char *server)
+{
+  char *name=NULL;
+  if (peer->servicename !=NULL)
+    tr_free_name(peer->servicename);
+
+  if (server!=NULL)
+    name=talloc_asprintf(NULL, "trustrouter/%s", server);
+
+  if (name!=NULL) {
+    peer->servicename=tr_new_name(name);
+    talloc_free(name);
+  } else {
+    peer->servicename=NULL;
+  }
+}
+
+/* copies input; on error, peer->gssname will be null */
+void trp_peer_set_server(TRP_PEER *peer, char *server)
+{
+  peer->server=talloc_strdup(peer, server); /* will be null on error */
+  trp_peer_set_servicename(peer, server);
+}
+
+void trp_peer_set_gssname(TRP_PEER *peer, TR_NAME *gssname)
+{
+  peer->gssname=gssname;
+}
+
+/* get the peer gssname, caller must not free the result */
+TR_NAME *trp_peer_get_gssname(TRP_PEER *peer)
+{
+  return peer->gssname;
+}
+
+/* get a copy of the peer gssname, caller must free via tr_free_name() */
+TR_NAME *trp_peer_dup_gssname(TRP_PEER *peer)
+{
+  return tr_dup_name(peer->gssname);
+}
+
+/* get the service name (i.e., gssname we see when we connect to this peer) */
+TR_NAME *trp_peer_get_servicename(TRP_PEER *peer)
+{
+  return peer->servicename;
+}
+
+/* get a copy of the servicename, caller must free via tr_free_name */
+TR_NAME *trp_peer_dup_servicename(TRP_PEER *peer)
+{
+  return tr_dup_name(peer->servicename);
+}
+
+unsigned int trp_peer_get_port(TRP_PEER *peer)
+{
+  return peer->port;
+}
+
+void trp_peer_set_port(TRP_PEER *peer, unsigned int port)
+{
+  peer->port=port;
+}
+
+unsigned int trp_peer_get_linkcost(TRP_PEER *peer)
+{
+  if (peer!=NULL)
+    return peer->linkcost;
+  else
+    return 1;
+}
+
+void trp_peer_set_linkcost(TRP_PEER *peer, unsigned int linkcost)
+{
+  if ((linkcost>TRP_METRIC_INFINITY) && (linkcost!=TRP_METRIC_INVALID)) {
+    /* This indicates a programming error, but probably means an already infinite metric
+     * was (incorrectly) incremented. Issue a warning and proceed with an infinite metric. */
+    tr_warning("trp_peer_set_linkcost: link cost > infinity encountered, setting to infinity");
+    linkcost=TRP_METRIC_INFINITY;
+  }
+  peer->linkcost=linkcost;
+}
+
+void trp_peer_set_conn_status_cb(TRP_PEER *peer, void (*cb)(TRP_PEER *, void *), void *cookie)
+{
+  peer->conn_status_cb=cb;
+  peer->conn_status_cookie=cookie;
+}
+
+struct timespec *trp_peer_get_last_conn_attempt(TRP_PEER *peer)
+{
+  return &(peer->last_conn_attempt);
+}
+
+void trp_peer_set_last_conn_attempt(TRP_PEER *peer, struct timespec *time)
+{
+  peer->last_conn_attempt=*time;
+}
+
+TRP_PTABLE *trp_ptable_new(TALLOC_CTX *memctx)
+{
+  TRP_PTABLE *ptbl=talloc(memctx, TRP_PTABLE);
+  if (ptbl!=NULL) {
+    ptbl->head=NULL;
+  }
+  return ptbl;
+}
+
+void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status)
+{
+  int was_connected=trp_peer_is_connected(peer);
+  peer->outgoing_status=status;
+  tr_debug("trp_peer_set_outgoing_status: %s: status=%d peer connected was %d now %d.",
+           trp_peer_get_gssname(peer)->buf, status, was_connected, trp_peer_is_connected(peer));
+  if ((trp_peer_is_connected(peer) != was_connected) && (peer->conn_status_cb!=NULL))
+    peer->conn_status_cb(peer, peer->conn_status_cookie);
+}
+
+TRP_PEER_CONN_STATUS trp_peer_get_outgoing_status(TRP_PEER *peer)
+{
+  return peer->outgoing_status;
+}
+
+void trp_peer_set_incoming_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status)
+{
+  int was_connected=trp_peer_is_connected(peer);
+  peer->incoming_status=status;
+  tr_debug("trp_peer_set_incoming_status: %s: status=%d peer connected was %d now %d.",
+           trp_peer_get_gssname(peer)->buf, status, was_connected, trp_peer_is_connected(peer));
+  if ((trp_peer_is_connected(peer) != was_connected) && (peer->conn_status_cb!=NULL))
+    peer->conn_status_cb(peer, peer->conn_status_cookie);
+}
+
+TRP_PEER_CONN_STATUS trp_peer_get_incoming_status(TRP_PEER *peer)
+{
+  return peer->incoming_status;
+}
+
+int trp_peer_is_connected(TRP_PEER *peer)
+{
+  return (peer->outgoing_status==PEER_CONNECTED) && (peer->incoming_status==PEER_CONNECTED);
+}
+
+void trp_ptable_free(TRP_PTABLE *ptbl)
+{
+  talloc_free(ptbl);
+}
+
+TRP_RC trp_ptable_add(TRP_PTABLE *ptbl, TRP_PEER *newpeer)
+{
+  if (ptbl->head==NULL) {
+    ptbl->head=newpeer;
+  } else {
+    trp_peer_tail(ptbl->head)->next=newpeer;
+    talloc_steal(ptbl, newpeer);
+  }
+  return TRP_SUCCESS;
+}
+
+/* peer pointer is invalid after successful removal. Does nothing and returns
+ * TRP_ERROR if peer is not in the list. */
+TRP_RC trp_ptable_remove(TRP_PTABLE *ptbl, TRP_PEER *peer)
+{
+  TRP_PEER *cur=NULL;
+  TRP_PEER *last=NULL;
+  if (ptbl->head!=NULL) {
+    if (ptbl->head==peer) {
+      /* special case for removing head of list */
+      cur=ptbl->head;
+      ptbl->head=ptbl->head->next; /* advance the head */
+      trp_peer_free(cur);
+    }
+    for (cur=ptbl->head->next; cur!=NULL; last=cur,cur=cur->next) {
+      if (cur==peer) {
+        if (last!=NULL)
+          last->next=cur->next;
+        trp_peer_free(cur);
+        return TRP_SUCCESS;
+      }
+    }
+  }
+  return TRP_ERROR;
+}
+
+TRP_PEER *trp_ptable_find_gssname(TRP_PTABLE *ptbl, TR_NAME *gssname)
+{
+  TRP_PEER *cur=ptbl->head;
+  while ((cur!=NULL) && (0 != tr_name_cmp(trp_peer_get_gssname(cur), gssname)))
+    cur=cur->next;
+  return cur;
+}
+
+TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename)
+{
+  TRP_PEER *cur=ptbl->head;
+  while ((cur!=NULL) && (0 != tr_name_cmp(trp_peer_get_servicename(cur), servicename)))
+    cur=cur->next;
+  return cur;
+}
+
+char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep)
+{
+  if (sep==NULL)
+    sep=", ";
+  return talloc_asprintf(memctx,
+                         "%s:%u%s0x%04X",
+                         peer->server, peer->port, sep,
+                         peer->linkcost);
+}
+
+/* this is horribly inefficient but should be ok for small peer tables */
+char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm)
+{
+  TALLOC_CTX *tmpctx=talloc_new(NULL);
+  TRP_PEER *peer=NULL;
+  char *result=talloc_strdup(tmpctx, "");
+
+  if (lineterm==NULL)
+    lineterm="\n";
+
+  /* this leaves intermediate result strings in the tmpctx context, we'll free these when
+   * we're done */
+  for (peer=ptbl->head; peer!=NULL; peer=peer->next)
+    result=talloc_asprintf(tmpctx, "%s%s%s", result, lineterm, trp_peer_to_str(tmpctx, peer, sep));
+
+  talloc_steal(memctx, result); /* hand result over to caller */
+  talloc_free(tmpctx); /* free detritus */
+  return result;
+}
+
+TRP_PTABLE_ITER *trp_ptable_iter_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_PTABLE_ITER *iter=talloc(mem_ctx, TRP_PTABLE_ITER);
+  *iter=NULL;
+  return iter;
+}
+
+TRP_PEER *trp_ptable_iter_first(TRP_PTABLE_ITER *iter, TRP_PTABLE *ptbl)
+{
+  *iter=ptbl->head;
+  return *iter;
+}
+
+TRP_PEER *trp_ptable_iter_next(TRP_PTABLE_ITER *iter)
+{
+  *iter=(*iter)->next;
+  return *iter;
+}
+
+void trp_ptable_iter_free(TRP_PTABLE_ITER *iter)
+{
+  talloc_free(iter);
+}
+
diff --git a/trp/trp_req.c b/trp/trp_req.c
new file mode 100644 (file)
index 0000000..497acd6
--- /dev/null
@@ -0,0 +1,119 @@
+#include <jansson.h>
+#include <talloc.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+#include <tr_debug.h>
+
+static int trp_req_destructor(void *object)
+{
+  TRP_REQ *req=talloc_get_type_abort(object, TRP_REQ);
+  
+  /* clean up TR_NAME data, which are not managed by talloc */
+  if (req->comm != NULL)
+    tr_free_name(req->comm);
+
+  if (req->realm != NULL)
+    tr_free_name(req->realm);
+
+  if (req->peer != NULL)
+    tr_free_name(req->peer);
+
+  return 0;
+}
+
+TRP_REQ *trp_req_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_REQ *new_req=talloc(mem_ctx, TRP_REQ);
+
+  if (new_req != NULL) {
+    new_req->comm=NULL;
+    new_req->realm=NULL;
+    new_req->peer=NULL;
+  }
+
+  talloc_set_destructor((void *)new_req, trp_req_destructor);
+  return new_req;
+}
+
+void trp_req_free(TRP_REQ *req)
+{
+  if (req!=NULL)
+    talloc_free(req);
+}
+
+TR_NAME *trp_req_get_comm(TRP_REQ *req)
+{
+  if (req!=NULL)
+    return req->comm;
+  else
+    return NULL;
+}
+
+void trp_req_set_comm(TRP_REQ *req, TR_NAME *comm)
+{
+  if (req)
+    req->comm=comm;
+}
+
+TR_NAME *trp_req_get_realm(TRP_REQ *req)
+{
+  if (req!=NULL)
+    return req->realm;
+  else
+    return NULL;
+}
+
+
+void trp_req_set_realm(TRP_REQ *req, TR_NAME *realm)
+{
+  if (req)
+    req->realm=realm;
+}
+
+TR_NAME *trp_req_get_peer(TRP_REQ *req)
+{
+  if (req!=NULL)
+    return req->peer;
+  else
+    return NULL;
+}
+
+
+void trp_req_set_peer(TRP_REQ *req, TR_NAME *peer)
+{
+  if (req)
+    req->peer=peer;
+}
+
+/* Defines what we use as a wildcard for realm or community name.
+ * Must not be a valid name for either of those. Currently, we
+ * use the empty string. */
+static int trp_req_name_is_wildcard(TR_NAME *name)
+{
+  return (name!=NULL) && (name->len==0);
+}
+
+int trp_req_is_wildcard(TRP_REQ *req)
+{
+  return (req!=NULL) && trp_req_name_is_wildcard(req->comm) && trp_req_name_is_wildcard(req->realm);
+}
+
+TRP_RC trp_req_make_wildcard(TRP_REQ *req)
+{
+  if (req==NULL)
+    return TRP_BADARG;
+
+  req->comm=tr_new_name("");
+  if (req->comm==NULL)
+    return TRP_NOMEM;
+
+  req->realm=tr_new_name("");
+  if (req->realm==NULL) {
+    tr_free_name(req->comm);
+    req->comm=NULL;
+    return TRP_NOMEM;
+  }
+
+  return TRP_SUCCESS;
+}
diff --git a/trp/trp_rtable.c b/trp/trp_rtable.c
new file mode 100644 (file)
index 0000000..fc11506
--- /dev/null
@@ -0,0 +1,752 @@
+#include <stdlib.h>
+
+#include <glib.h>
+#include <talloc.h>
+#include <time.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+#include <trp_rtable.h>
+#include <tr_debug.h>
+
+/* Note: be careful mixing talloc with glib. */
+
+static int trp_route_destructor(void *obj)
+{
+  TRP_ROUTE *entry=talloc_get_type_abort(obj, TRP_ROUTE);
+  if (entry->comm!=NULL)
+    tr_free_name(entry->comm);
+  if (entry->realm!=NULL)
+    tr_free_name(entry->realm);
+  if (entry->trust_router!=NULL)
+    tr_free_name(entry->trust_router);
+  if (entry->peer!=NULL)
+    tr_free_name(entry->peer);
+  if (entry->next_hop!=NULL)
+    tr_free_name(entry->next_hop);
+  return 0;
+}
+
+TRP_ROUTE *trp_route_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_ROUTE *entry=talloc(mem_ctx, TRP_ROUTE);
+  if (entry!=NULL) {
+    entry->comm=NULL;
+    entry->realm=NULL;
+    entry->trust_router=NULL;
+    entry->peer=NULL;
+    entry->next_hop=NULL;
+    entry->selected=0;
+    entry->interval=0;
+    entry->expiry=talloc(entry, struct timespec);
+    if (entry->expiry==NULL) {
+      talloc_free(entry);
+      return NULL;
+    }
+    *(entry->expiry)=(struct timespec){0,0};
+    entry->local=0;
+    entry->triggered=0;
+    talloc_set_destructor((void *)entry, trp_route_destructor);
+  }
+  return entry;
+}
+
+void trp_route_free(TRP_ROUTE *entry)
+{
+  if (entry!=NULL)
+    talloc_free(entry);
+}
+
+void trp_route_set_comm(TRP_ROUTE *entry, TR_NAME *comm)
+{
+  if (entry->comm!=NULL)
+    tr_free_name(entry->comm);
+  entry->comm=comm;
+}
+
+TR_NAME *trp_route_get_comm(TRP_ROUTE *entry)
+{
+  return entry->comm;
+}
+
+TR_NAME *trp_route_dup_comm(TRP_ROUTE *entry)
+{
+  return tr_dup_name(trp_route_get_comm(entry));
+}
+
+void trp_route_set_realm(TRP_ROUTE *entry, TR_NAME *realm)
+{
+  if (entry->realm!=NULL)
+    tr_free_name(entry->realm);
+  entry->realm=realm;
+}
+
+TR_NAME *trp_route_get_realm(TRP_ROUTE *entry)
+{
+  return entry->realm;
+}
+
+TR_NAME *trp_route_dup_realm(TRP_ROUTE *entry)
+{
+  return tr_dup_name(trp_route_get_realm(entry));
+}
+
+void trp_route_set_trust_router(TRP_ROUTE *entry, TR_NAME *tr)
+{
+  if (entry->trust_router!=NULL)
+    tr_free_name(entry->trust_router);
+  entry->trust_router=tr;
+}
+
+TR_NAME *trp_route_get_trust_router(TRP_ROUTE *entry)
+{
+  return entry->trust_router;
+}
+
+TR_NAME *trp_route_dup_trust_router(TRP_ROUTE *entry)
+{
+  return tr_dup_name(trp_route_get_trust_router(entry));
+}
+
+void trp_route_set_peer(TRP_ROUTE *entry, TR_NAME *peer)
+{
+  if (entry->peer!=NULL)
+    tr_free_name(entry->peer);
+  entry->peer=peer;
+}
+
+TR_NAME *trp_route_get_peer(TRP_ROUTE *entry)
+{
+  return entry->peer;
+}
+
+TR_NAME *trp_route_dup_peer(TRP_ROUTE *entry)
+{
+  return tr_dup_name(trp_route_get_peer(entry));
+}
+
+void trp_route_set_metric(TRP_ROUTE *entry, unsigned int metric)
+{
+  entry->metric=metric;
+}
+
+unsigned int trp_route_get_metric(TRP_ROUTE *entry)
+{
+  return entry->metric;
+}
+
+void trp_route_set_next_hop(TRP_ROUTE *entry, TR_NAME *next_hop)
+{
+  if (entry->next_hop!=NULL)
+    tr_free_name(entry->next_hop);
+  entry->next_hop=next_hop;
+}
+
+TR_NAME *trp_route_get_next_hop(TRP_ROUTE *entry)
+{
+  return entry->next_hop;
+}
+
+TR_NAME *trp_route_dup_next_hop(TRP_ROUTE *entry)
+{
+  return tr_dup_name(trp_route_get_next_hop(entry));
+}
+
+void trp_route_set_selected(TRP_ROUTE *entry, int sel)
+{
+  entry->selected=sel;
+}
+
+int trp_route_is_selected(TRP_ROUTE *entry)
+{
+  return entry->selected;
+}
+
+void trp_route_set_interval(TRP_ROUTE *entry, int interval)
+{
+  entry->interval=interval;
+}
+
+int trp_route_get_interval(TRP_ROUTE *entry)
+{
+  return entry->interval;
+}
+
+/* copies incoming value, does not assume responsibility for freeing */
+void trp_route_set_expiry(TRP_ROUTE *entry, struct timespec *exp)
+{
+  entry->expiry->tv_sec=exp->tv_sec;
+  entry->expiry->tv_nsec=exp->tv_nsec;
+}
+
+struct timespec *trp_route_get_expiry(TRP_ROUTE *entry)
+{
+  return entry->expiry;
+}
+
+void trp_route_set_local(TRP_ROUTE *entry, int local)
+{
+  entry->local=local;
+}
+
+int trp_route_is_local(TRP_ROUTE *entry)
+{
+  return entry->local;
+}
+
+void trp_route_set_triggered(TRP_ROUTE *entry, int trig)
+{
+  entry->triggered=trig;
+}
+
+int trp_route_is_triggered(TRP_ROUTE *entry)
+{
+  return entry->triggered;
+}
+
+
+/* result must be freed with g_free */
+static gchar *tr_name_to_g_str(const TR_NAME *n)
+{
+  gchar *s=g_strndup(n->buf, n->len);
+  if (s==NULL)
+    tr_debug("tr_name_to_g_str: allocation failure.");
+  return s;
+}
+
+/* hash function for TR_NAME keys */
+static guint trp_tr_name_hash(gconstpointer key)
+{
+  const TR_NAME *name=(TR_NAME *)key;
+  gchar *s=tr_name_to_g_str(name);
+  guint hash=g_str_hash(s);
+  g_free(s);
+  return hash;
+}
+
+/* hash equality function for TR_NAME keys */
+static gboolean trp_tr_name_equal(gconstpointer key1, gconstpointer key2)
+{
+  const TR_NAME *n1=(TR_NAME *)key1;
+  const TR_NAME *n2=(TR_NAME *)key2;
+  gchar *s1=tr_name_to_g_str(n1);
+  gchar *s2=tr_name_to_g_str(n2);
+  gboolean equal=g_str_equal(s1, s2);
+  g_free(s1);
+  g_free(s2);
+  return equal;
+}
+
+/* free a value to the top level rtable (a hash of all entries in the comm) */
+static void trp_rtable_destroy_table(gpointer data)
+{
+  g_hash_table_destroy(data);
+}
+
+static void trp_rtable_destroy_rentry(gpointer data)
+{
+  trp_route_free(data);
+}
+
+static void trp_rtable_destroy_tr_name(gpointer data)
+{
+  tr_free_name(data);
+}
+
+TRP_RTABLE *trp_rtable_new(void)
+{
+  GHashTable *new=g_hash_table_new_full(trp_tr_name_hash,
+                                        trp_tr_name_equal,
+                                        trp_rtable_destroy_tr_name,
+                                        trp_rtable_destroy_table);
+  return new;
+}
+
+void trp_rtable_free(TRP_RTABLE *rtbl)
+{
+  g_hash_table_destroy(rtbl);
+}
+
+static GHashTable *trp_rtbl_get_or_add_table(GHashTable *tbl, TR_NAME *key, GDestroyNotify destroy)
+{
+  GHashTable *val_tbl=NULL;
+
+  val_tbl=g_hash_table_lookup(tbl, key);
+  if (val_tbl==NULL) {
+    val_tbl=g_hash_table_new_full(trp_tr_name_hash,
+                                  trp_tr_name_equal,
+                                  trp_rtable_destroy_tr_name,
+                                  destroy);
+    g_hash_table_insert(tbl, tr_dup_name(key), val_tbl);
+  }
+  return val_tbl;
+}
+
+void trp_rtable_add(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
+{
+  GHashTable *comm_tbl=NULL;
+  GHashTable *realm_tbl=NULL;
+
+  comm_tbl=trp_rtbl_get_or_add_table(rtbl, entry->comm, trp_rtable_destroy_table);
+  realm_tbl=trp_rtbl_get_or_add_table(comm_tbl, entry->realm, trp_rtable_destroy_rentry);
+  g_hash_table_insert(realm_tbl, tr_dup_name(entry->peer), entry); /* destroys and replaces a duplicate */
+  /* the route entry should not belong to any context, we will manage it ourselves */
+  talloc_steal(NULL, entry);
+}
+
+/* note: the entry pointer passed in is invalid after calling this because the entry is freed */
+void trp_rtable_remove(TRP_RTABLE *rtbl, TRP_ROUTE *entry)
+{
+  GHashTable *comm_tbl=NULL;
+  GHashTable *realm_tbl=NULL;
+
+  comm_tbl=g_hash_table_lookup(rtbl, entry->comm);
+  if (comm_tbl==NULL)
+    return;
+
+  realm_tbl=g_hash_table_lookup(comm_tbl, entry->realm);
+  if (realm_tbl==NULL)
+    return;
+
+  /* remove the element */
+  g_hash_table_remove(realm_tbl, entry->peer);
+  /* if that was the last entry in the realm, remove the realm table */
+  if (g_hash_table_size(realm_tbl)==0)
+    g_hash_table_remove(comm_tbl, entry->realm);
+  /* if that was the last realm in the comm, remove the comm table */
+  if (g_hash_table_size(comm_tbl)==0)
+    g_hash_table_remove(rtbl, entry->comm);
+}
+
+void trp_rtable_clear(TRP_RTABLE *rtbl)
+{
+  g_hash_table_remove_all(rtbl); /* destructors should do all the cleanup */
+}
+
+/* gets the actual hash table, for internal use only */
+static GHashTable *trp_rtable_get_comm_table(TRP_RTABLE *rtbl, TR_NAME *comm)
+{
+  return g_hash_table_lookup(rtbl, comm);
+}
+
+/* gets the actual hash table, for internal use only */
+static GHashTable *trp_rtable_get_realm_table(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
+{
+  GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
+  if (comm_tbl==NULL)
+    return NULL;
+  else
+    return g_hash_table_lookup(comm_tbl, realm);
+}
+
+struct table_size_cookie {
+  TRP_RTABLE *rtbl;
+  size_t size;
+};
+static void trp_rtable_size_helper(gpointer key, gpointer value, gpointer user_data)
+{
+  struct table_size_cookie *data=(struct table_size_cookie *)user_data;
+  data->size += trp_rtable_comm_size(data->rtbl, (TR_NAME *)key);
+};
+size_t trp_rtable_size(TRP_RTABLE *rtbl)
+{
+  struct table_size_cookie data={rtbl, 0};
+  g_hash_table_foreach(rtbl, trp_rtable_size_helper, &data);
+  return data.size;
+}
+
+struct table_comm_size_cookie {
+  TR_NAME *comm;
+  TRP_RTABLE *rtbl;
+  size_t size;
+};
+static void table_comm_size_helper(gpointer key, gpointer value, gpointer user_data)
+{
+  struct table_comm_size_cookie *data=(struct table_comm_size_cookie *)user_data;
+  data->size += trp_rtable_realm_size(data->rtbl, data->comm, (TR_NAME *)key);
+}
+size_t trp_rtable_comm_size(TRP_RTABLE *rtbl, TR_NAME *comm)
+{
+  struct table_comm_size_cookie data={comm, rtbl, 0};
+  GHashTable *comm_tbl=trp_rtable_get_comm_table(rtbl, comm);
+  if (comm_tbl==NULL)
+    return 0;;
+  g_hash_table_foreach(comm_tbl, table_comm_size_helper, &data);
+  return data.size;
+}
+
+size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
+{
+  GHashTable *realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
+  if (realm_tbl==NULL)
+    return 0;
+  else
+    return g_hash_table_size(g_hash_table_lookup(
+                               g_hash_table_lookup(rtbl, comm),
+                               realm));
+}
+
+/* Returns an array of pointers to TRP_ROUTE, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context), but must
+ * not free its contents. */
+TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
+{
+  TRP_ROUTE **ret=NULL;
+  TR_NAME **comm=NULL;
+  size_t n_comm=0;
+  TRP_ROUTE **comm_entries=NULL;
+  size_t n_entries=0;
+  size_t ii_ret=0;
+
+  *n_out=trp_rtable_size(rtbl);
+  if (*n_out==0)
+    return NULL;
+
+  ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_entries: unable to allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+
+  ii_ret=0; /* counts output entries */
+  comm=trp_rtable_get_comms(rtbl, &n_comm);
+  while(n_comm--) {
+    comm_entries=trp_rtable_get_comm_entries(rtbl, comm[n_comm], &n_entries);
+    while (n_entries--)
+      ret[ii_ret++]=comm_entries[n_entries];
+    talloc_free(comm_entries);
+  }
+  talloc_free(comm);
+
+  if (ii_ret!=*n_out) {
+    tr_crit("trp_rtable_get_entries: found incorrect number of entries.");
+    talloc_free(ret);
+    *n_out=0;
+    return NULL;
+  }
+  return ret;
+}
+
+/* Returns an array of pointers to TR_NAME, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context). */
+TR_NAME **trp_rtable_get_comms(TRP_RTABLE *rtbl, size_t *n_out)
+{
+  size_t len=g_hash_table_size(rtbl); /* known comms are keys in top level hash table */
+  size_t ii=0;
+  GList *comms=NULL;;
+  GList *p=NULL;
+  TR_NAME **ret=NULL;
+
+  if (len==0) {
+    *n_out=0;
+    return NULL;
+  }
+    
+  ret=talloc_array(NULL, TR_NAME *, len);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_comms: unable to allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  comms=g_hash_table_get_keys(rtbl);
+  for (ii=0,p=comms; p!=NULL; ii++,p=g_list_next(p))
+    ret[ii]=(TR_NAME *)p->data;
+
+  g_list_free(comms);
+
+  *n_out=len;
+  return ret;
+}
+
+/* Returns an array of pointers to TR_NAME, length of array in n_out.
+ * Caller must free the array (in the talloc NULL context). */
+TR_NAME **trp_rtable_get_comm_realms(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
+{
+  size_t ii=0;
+  TRP_RTABLE *comm_tbl=g_hash_table_lookup(rtbl, comm);;
+  GList *entries=NULL;
+  GList *p=NULL;
+  TR_NAME **ret=NULL;
+
+  if (comm_tbl==NULL) {
+    *n_out=0;
+    return NULL;
+  }
+  *n_out=g_hash_table_size(comm_tbl); /* set output length */
+  ret=talloc_array(NULL, TR_NAME *, *n_out);
+  entries=g_hash_table_get_keys(comm_tbl);
+  for (ii=0,p=entries; p!=NULL; ii++,p=g_list_next(p))
+    ret[ii]=(TR_NAME *)p->data;
+
+  g_list_free(entries);
+  return ret;
+}
+
+/* Get all entries in an comm. Returns an array of pointers in NULL talloc context.
+ * Caller must free this list with talloc_free, but must not free the entries in the
+ * list.. */
+TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t *n_out)
+{
+  size_t ii=0, jj=0;
+  TR_NAME **realm=NULL;
+  size_t n_realms=0;
+  TRP_ROUTE **realm_entries=NULL;
+  size_t n_entries=0;
+  TRP_ROUTE **ret=NULL;
+  size_t ii_ret=0;
+
+  *n_out=trp_rtable_comm_size(rtbl, comm);
+  if (*n_out==0)
+    return NULL;
+
+  ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_comm_entries: could not allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  
+  ii_ret=0; /* counts entries in the output array */
+  realm=trp_rtable_get_comm_realms(rtbl, comm, &n_realms);
+  for (ii=0; ii<n_realms; ii++) {
+    realm_entries=trp_rtable_get_realm_entries(rtbl, comm, realm[ii], &n_entries);
+    for (jj=0; jj<n_entries; jj++)
+      ret[ii_ret++]=realm_entries[jj];
+    talloc_free(realm_entries);
+  }
+  talloc_free(realm);
+
+  if (ii_ret!=*n_out) {
+    tr_crit("trp_rtable_get_comm_entries: found incorrect number of entries.");
+    talloc_free(ret);
+    *n_out=0;
+    return NULL;
+  }
+
+  return ret;
+}
+
+/* Get all entries in an comm/realm. Returns an array of pointers in NULL talloc context.
+ * Caller must free this list with talloc_free, but must not free the entries in the
+ * list.. */
+TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
+{
+  size_t ii=0;
+  TRP_ROUTE **ret=NULL;
+  TR_NAME **peer=NULL;
+
+  peer=trp_rtable_get_comm_realm_peers(rtbl, comm, realm, n_out);
+  ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
+    talloc_free(peer);
+    n_out=0;
+    return NULL;
+  }
+  for (ii=0; ii<*n_out; ii++)
+    ret[ii]=trp_rtable_get_entry(rtbl, comm, realm, peer[ii]);
+  talloc_free(peer);
+  return ret;
+}
+
+TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
+{
+  TR_NAME **ret=NULL;
+  GHashTable *realm_tbl=NULL;
+  GList *keys=NULL;
+  GList *p=NULL;
+  size_t ii=0;
+
+  *n_out=trp_rtable_realm_size(rtbl, comm, realm);
+  if (*n_out==0)
+    return NULL;
+  realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
+  ret=talloc_array(NULL, TR_NAME *, *n_out);
+  if (ret==NULL) {
+    tr_crit("trp_rtable_get_comm_realm_peers: could not allocate return array.");
+    *n_out=0;
+    return NULL;
+  }
+  keys=g_hash_table_get_keys(realm_tbl);
+  for (ii=0,p=keys; p!=NULL; ii++,p=g_list_next(p))
+    ret[ii]=(TR_NAME *)p->data;
+  g_list_free(keys);
+  return ret;
+}
+
+/* Gets a single entry. Do not free it. */
+TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
+{
+  GHashTable *realm_tbl=NULL;
+
+  realm_tbl=trp_rtable_get_realm_table(rtbl, comm, realm);
+  if (realm_tbl==NULL)
+    return NULL;
+
+  return g_hash_table_lookup(realm_tbl, peer); /* does not copy or increment ref count */
+}
+
+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;
+}
+
+TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm)
+{
+  size_t n=0;
+  TRP_ROUTE **entry=trp_rtable_get_realm_entries(rtbl, comm, realm, &n);
+  TRP_ROUTE *selected=NULL;
+
+  if (n==0)
+    return NULL;
+
+  while(n-- && !trp_route_is_selected(entry[n])) { }
+  selected=entry[n];
+  talloc_free(entry);
+  return selected;
+}
+
+/* Pretty print a route table entry to a newly allocated string. If sep is NULL,
+ * returns comma+space separated string. */
+char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep)
+{
+  char *comm=tr_name_strdup(entry->comm);
+  char *realm=tr_name_strdup(entry->realm);
+  char *peer=tr_name_strdup(entry->peer);
+  char *trust_router=tr_name_strdup(entry->trust_router);
+  char *next_hop=tr_name_strdup(entry->next_hop);
+  char *expiry=timespec_to_str(entry->expiry);
+  char *result=NULL;
+
+  if (sep==NULL)
+    sep=", ";
+
+  result=talloc_asprintf(mem_ctx,
+                         "%s%s%s%s%s%s%u%s%s%s%s%s%u%s%u%s%s%s%u",
+                         comm, sep,
+                         realm, sep,
+                         peer, sep,
+                         entry->metric, sep,
+                         trust_router, sep,
+                         next_hop, sep,
+                         entry->selected, sep,
+                         entry->local, sep,
+                         expiry, sep,
+                         entry->triggered);
+  free(comm);
+  free(realm);
+  free(peer);
+  free(trust_router);
+  free(next_hop);
+  free(expiry);
+  return result;
+}
+
+void trp_rtable_clear_triggered(TRP_RTABLE *rtbl)
+{
+  size_t n_entries=0;
+  TRP_ROUTE **entries=trp_rtable_get_entries(rtbl, &n_entries);
+  size_t ii=0;
+
+  if (entries!=NULL) {
+    for (ii=0; ii<n_entries; ii++)
+      trp_route_set_triggered(entries[ii], 0);
+    talloc_free(entries);
+  }
+}
+
+static int sort_tr_names_cmp(const void *a, const void *b)
+{
+  TR_NAME **n1=(TR_NAME **)a;
+  TR_NAME **n2=(TR_NAME **)b;
+  return tr_name_cmp(*n1, *n2);
+}
+
+static void sort_tr_names(TR_NAME **names, size_t n_names)
+{
+  qsort(names, n_names, sizeof(TR_NAME *), sort_tr_names_cmp);
+}
+
+char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_NAME **comms=NULL;
+  size_t n_comms=0;
+  TR_NAME **realms=NULL;
+  size_t n_realms=0;
+  TRP_ROUTE **entries=NULL;
+  size_t n_entries=0;
+  char **tbl_strings=NULL;
+  size_t ii_tbl=0; /* counts tbl_strings */
+  size_t tbl_size=0;
+  size_t len=0;
+  size_t ii=0, jj=0, kk=0;
+  char *p=NULL;
+  char *result=NULL;
+
+  if (lineterm==NULL)
+    lineterm="\n";
+
+  tbl_size=trp_rtable_size(rtbl);
+  if (tbl_size==0) {
+    result=talloc_strdup(mem_ctx, lineterm);
+    goto cleanup;
+  }
+
+  tbl_strings=talloc_array(tmp_ctx, char *, tbl_size);
+  if (tbl_strings==NULL) {
+    result=talloc_strdup(mem_ctx, "error");
+    goto cleanup;
+  }
+  
+  comms=trp_rtable_get_comms(rtbl, &n_comms);
+  talloc_steal(tmp_ctx, comms);
+  sort_tr_names(comms, n_comms);
+  ii_tbl=0;
+  len=0;
+  for (ii=0; ii<n_comms; ii++) {
+    realms=trp_rtable_get_comm_realms(rtbl, comms[ii], &n_realms);
+    talloc_steal(tmp_ctx, realms);
+    sort_tr_names(realms, n_realms);
+    for (jj=0; jj<n_realms; jj++) {
+      entries=trp_rtable_get_realm_entries(rtbl, comms[ii], realms[jj], &n_entries);
+      talloc_steal(tmp_ctx, entries);
+      for (kk=0; kk<n_entries; kk++) {
+        tbl_strings[ii_tbl]=trp_route_to_str(tmp_ctx, entries[kk], sep);
+        len+=strlen(tbl_strings[ii_tbl]);
+        ii_tbl++;
+      }
+      talloc_free(entries);
+    }
+    talloc_free(realms);
+  }
+  talloc_free(comms);
+
+  /* now combine all the strings */
+  len += tbl_size*strlen(lineterm); /* space for line terminations*/
+  len += 1; /* nul terminator */
+  result=(char *)talloc_size(tmp_ctx, len);
+  for (p=result,ii=0; ii < tbl_size; ii++) {
+    p+=sprintf(p, "%s%s", tbl_strings[ii], lineterm);
+  }
+  talloc_steal(mem_ctx, result);
+  
+cleanup:
+  talloc_free(tmp_ctx);
+  return result;
+}
diff --git a/trp/trp_upd.c b/trp/trp_upd.c
new file mode 100644 (file)
index 0000000..245ac1e
--- /dev/null
@@ -0,0 +1,453 @@
+#include <jansson.h>
+#include <talloc.h>
+
+#include <trust_router/tr_name.h>
+#include <trp_internal.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);
+
+
+struct trp_inforec_type_entry {
+  const char *name;
+  TRP_INFOREC_TYPE type;
+  void *(*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 },
+  { NULL, TRP_INFOREC_TYPE_UNKNOWN, NULL, NULL } /* must be the last entry */
+};
+
+
+/* look up an entry in the trp_inforec_type_table */
+static struct trp_inforec_type_entry *get_trp_inforec_type_entry(TRP_INFOREC_TYPE msgtype)
+{
+  struct trp_inforec_type_entry *entry=trp_inforec_type_table;
+
+  while ((entry->type != TRP_INFOREC_TYPE_UNKNOWN)
+        && (entry->type != msgtype)) {
+    entry ++;
+  }
+  return entry;
+}
+
+/* translate strings to codes */
+TRP_INFOREC_TYPE trp_inforec_type_from_string(const char *s)
+{
+  struct trp_inforec_type_entry *entry=trp_inforec_type_table;
+
+  while ((entry->type != TRP_INFOREC_TYPE_UNKNOWN)
+        && (strcmp(s, entry->name)!=0)) {
+    entry++;
+  }
+  return entry->type;
+}
+/* translate codes to strings (do not need to be freed) 
+ * Returns NULL on an unknown code */
+const char *trp_inforec_type_to_string(TRP_INFOREC_TYPE msgtype)
+{
+  struct trp_inforec_type_entry *entry=get_trp_inforec_type_entry(msgtype);
+  return entry->name;
+}
+
+/* called by talloc when destroying an update message body */
+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) {
+    tr_free_name(body->trust_router);
+    body->trust_router=NULL;
+    tr_debug("trp_inforec_route_destructor: freed trust_router");
+  }
+  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)
+{
+  TRP_INFOREC_ROUTE *new_rec=talloc(mem_ctx, TRP_INFOREC_ROUTE);
+
+  if (new_rec != NULL) {
+    new_rec->comm=NULL;
+    new_rec->realm=NULL;
+    new_rec->trust_router=NULL;
+    new_rec->next_hop=NULL;
+    new_rec->metric=TRP_METRIC_INFINITY;
+    new_rec->interval=0;
+    talloc_set_destructor((void *)new_rec, trp_inforec_route_destructor);
+  }
+  return new_rec;
+}
+
+TRP_INFOREC *trp_inforec_get_next(TRP_INFOREC *rec)
+{
+  if (rec!=NULL)
+    return rec->next;
+  else
+    return NULL;
+}
+
+static TRP_INFOREC *trp_inforec_get_tail(TRP_INFOREC *rec)
+{
+  while ((rec->next)!=NULL)
+    rec=trp_inforec_get_next(rec);
+  return rec;
+}
+
+void trp_inforec_set_next(TRP_INFOREC *rec, TRP_INFOREC *next_rec)
+{
+  if (rec !=NULL)
+    rec->next=next_rec;
+}
+
+TRP_INFOREC_TYPE trp_inforec_get_type(TRP_INFOREC *rec)
+{
+  if (rec)
+    return rec->type;
+  else
+    return TRP_INFOREC_TYPE_UNKNOWN;
+}
+
+void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type)
+{
+  if (rec!=NULL)
+    rec->type=type;
+}
+
+TR_NAME *trp_inforec_get_comm(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data.route!=NULL)
+      return rec->data.route->comm;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TR_NAME *trp_inforec_dup_comm(TRP_INFOREC *rec)
+{
+  return tr_dup_name(trp_inforec_get_comm(rec));
+}
+
+TRP_RC trp_inforec_set_comm(TRP_INFOREC *rec, TR_NAME *comm)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data.route!=NULL) {
+      rec->data.route->comm=comm;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data.route!=NULL)
+      return rec->data.route->realm;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec)
+{
+  return tr_dup_name(trp_inforec_get_realm(rec));
+}
+
+TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm)
+{
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    if (rec->data.route!=NULL) {
+      rec->data.route->realm=realm;
+      return TRP_SUCCESS;
+    } 
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+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->trust_router;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec)
+{
+  return tr_dup_name(trp_inforec_get_trust_router(rec));
+}
+
+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->trust_router=trust_router;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+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->next_hop;
+    break;
+  default:
+    break;
+  }
+  return NULL;
+}
+
+TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec)
+{
+  return tr_dup_name(trp_inforec_get_next_hop(rec));
+}
+
+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->next_hop=next_hop;
+      return TRP_SUCCESS;
+    }
+    break;
+  default:
+    break;
+  }
+  return TRP_ERROR;
+}
+
+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->metric;
+    break;
+  default:
+    break;
+  }
+  return TRP_METRIC_INVALID;
+}
+
+TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric)
+{
+  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;
+}
+
+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;
+  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->interval=interval;
+      return TRP_SUCCESS;
+  default:
+    break;
+    }
+    break;
+  }
+  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)
+{
+  if (data==NULL)
+    return TRP_ERROR;
+
+  switch (rec->type) {
+  case TRP_INFOREC_TYPE_ROUTE:
+    rec->data.route=talloc_get_type(data, TRP_INFOREC_ROUTE);
+    break;
+  default:
+    return TRP_BADTYPE;
+  }
+  return TRP_SUCCESS;
+}
+
+/* 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);
+  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))) {
+        talloc_free(new_rec);
+        new_rec=NULL;
+      }
+    }
+  }
+  return new_rec;
+}
+
+void trp_inforec_free(TRP_INFOREC *rec)
+{
+  if (rec!=NULL)
+    talloc_free(rec);
+}
+
+static int trp_upd_destructor(void *object)
+{
+  TRP_UPD *upd=talloc_get_type_abort(object, TRP_UPD);
+  if (upd->peer!=NULL)
+    tr_free_name(upd->peer);
+  return 0;
+}
+
+TRP_UPD *trp_upd_new(TALLOC_CTX *mem_ctx)
+{
+  TRP_UPD *new_body=talloc(mem_ctx, TRP_UPD);
+
+  if (new_body!=NULL) {
+    new_body->records=NULL;
+    new_body->peer=NULL;
+    talloc_set_destructor((void *)new_body, trp_upd_destructor);
+  }
+  return new_body;
+}
+
+void trp_upd_free(TRP_UPD *update)
+{
+  if (update!=NULL)
+    talloc_free(update);
+}
+
+TRP_INFOREC *trp_upd_get_inforec(TRP_UPD *upd)
+{
+  if (upd!=NULL)
+    return upd->records;
+  else
+    return NULL;
+}
+
+void trp_upd_set_inforec(TRP_UPD *upd, TRP_INFOREC *rec)
+{
+  if (upd!=NULL)
+    upd->records=rec;
+}
+
+void trp_upd_add_inforec(TRP_UPD *upd, TRP_INFOREC *rec)
+{
+  tr_debug("trp_upd_add_inforec: adding record.");
+  if (upd->records==NULL)
+    upd->records=rec;
+  else
+    trp_inforec_set_next(trp_inforec_get_tail(upd->records), rec);
+  talloc_steal(upd, rec);
+}
+
+TR_NAME *trp_upd_get_peer(TRP_UPD *upd)
+{
+  return upd->peer;
+}
+
+TR_NAME *trp_upd_dup_peer(TRP_UPD *upd)
+{
+  return tr_dup_name(upd->peer);
+}
+
+void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer)
+{
+  TRP_INFOREC *rec=NULL;
+  TR_NAME *cpy=NULL;
+
+  upd->peer=peer;
+  for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
+    if (trp_inforec_set_next_hop(rec, cpy=tr_dup_name(peer)) != TRP_SUCCESS) {
+      tr_err("trp_upd_set_peer: error setting peer.");
+      tr_free_name(cpy);
+    }
+  }
+}
+
+/* pretty print */
+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);
+  }
+}
+
diff --git a/trp/trpc.c b/trp/trpc.c
new file mode 100644 (file)
index 0000000..4e02fea
--- /dev/null
@@ -0,0 +1,189 @@
+#include <fcntl.h>
+#include <talloc.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <gsscon.h>
+#include <tr_rp.h>
+#include <tr_debug.h>
+#include <trp_internal.h>
+
+static int trpc_destructor(void *object)
+{
+  TRPC_INSTANCE *trpc=talloc_get_type_abort(object, TRPC_INSTANCE);
+  if (trpc->gssname!=NULL)
+    tr_free_name(trpc->gssname);
+  return 0;
+}
+
+/* also allocates the incoming mq */
+TRPC_INSTANCE *trpc_new (TALLOC_CTX *mem_ctx)
+{
+  TRPC_INSTANCE *trpc=talloc(mem_ctx, TRPC_INSTANCE);
+  if (trpc!=NULL) {
+    trpc->next=NULL;
+    trpc->server=NULL;
+    trpc->port=0;
+    trpc->conn=NULL;
+    trpc->mq=tr_mq_new(trpc);
+    if (trpc->mq==NULL) {
+      talloc_free(trpc);
+      trpc=NULL;
+    } else
+      talloc_set_destructor((void *)trpc, trpc_destructor);
+    
+  }
+  return trpc;
+}
+
+void trpc_free (TRPC_INSTANCE *trpc)
+{
+  if (trpc)
+    talloc_free(trpc);
+}
+
+TRPC_INSTANCE *trpc_get_next(TRPC_INSTANCE *trpc)
+{
+  return trpc->next;
+}
+
+void trpc_set_next(TRPC_INSTANCE *trpc, TRPC_INSTANCE *next)
+{
+  trpc->next=next;
+}
+
+/* Ok to call more than once; guarantees trpc no longer in the list. Does not free removed element.
+ * Returns handle to new list, you must replace your old handle on the list with this.  */
+TRPC_INSTANCE *trpc_remove(TRPC_INSTANCE *trpc, TRPC_INSTANCE *remove)
+{
+  TRPC_INSTANCE *cur=trpc;
+  TRPC_INSTANCE *last=NULL;
+
+  if (cur==NULL)
+    return NULL;
+
+  /* first element is a special case */
+  if (cur==remove) {
+    trpc=trpc_get_next(cur); /* advance list head */
+  } else {
+    /* it was not the first element */
+    last=cur;
+    cur=trpc_get_next(cur);
+    while (cur!=NULL) {
+      if (cur==remove) {
+        trpc_set_next(last, trpc_get_next(cur));
+        break;
+      }
+      last=cur;
+      cur=trpc_get_next(cur);
+    }
+  }
+  return trpc;
+}
+
+static TRPC_INSTANCE *trpc_get_tail(TRPC_INSTANCE *trpc)
+{
+  while((trpc!=NULL)&&(trpc_get_next(trpc)!=NULL))
+    trpc=trpc_get_next(trpc);
+  return trpc;
+}
+
+void trpc_append(TRPC_INSTANCE *trpc, TRPC_INSTANCE *new)
+{
+  trpc_set_next(trpc_get_tail(trpc), new);
+}
+
+char *trpc_get_server(TRPC_INSTANCE *trpc)
+{
+  return trpc->server;
+}
+
+void trpc_set_server(TRPC_INSTANCE *trpc, char *server)
+{
+  trpc->server=server;
+}
+
+TR_NAME *trpc_get_gssname(TRPC_INSTANCE *trpc)
+{
+  return trpc->gssname;
+}
+
+/* takes responsibility for freeing gssname */
+void trpc_set_gssname(TRPC_INSTANCE *trpc, TR_NAME *gssname)
+{
+  trpc->gssname=gssname;
+}
+
+unsigned int trpc_get_port(TRPC_INSTANCE *trpc)
+{
+  return trpc->port;
+}
+
+void trpc_set_port(TRPC_INSTANCE *trpc, unsigned int port)
+{
+  trpc->port=port;
+}
+
+TRP_CONNECTION *trpc_get_conn(TRPC_INSTANCE *trpc)
+{
+  return trpc->conn;
+}
+
+void trpc_set_conn(TRPC_INSTANCE *trpc, TRP_CONNECTION *conn)
+{
+  trpc->conn=conn;
+}
+
+TRP_CONNECTION_STATUS trpc_get_status(TRPC_INSTANCE *trpc)
+{
+  return trp_connection_get_status(trpc_get_conn(trpc));
+}
+
+TR_MQ *trpc_get_mq(TRPC_INSTANCE *trpc)
+{
+  return trpc->mq;
+}
+
+void trpc_set_mq(TRPC_INSTANCE *trpc, TR_MQ *mq)
+{
+  trpc->mq=mq;
+}
+
+/* submit msg to trpc for transmission */
+void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg)
+{
+  tr_mq_add(trpc->mq, msg);
+}
+
+TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc)
+{
+  return tr_mq_pop(trpc->mq);
+}
+
+void trpc_mq_clear(TRPC_INSTANCE *trpc)
+{
+  tr_mq_clear(trpc->mq);
+}
+
+TRP_RC trpc_connect(TRPC_INSTANCE *trpc)
+{
+  return trp_connection_initiate(trpc_get_conn(trpc), trpc_get_server(trpc), trpc_get_port(trpc));
+}
+
+/* simple function, based on tidc_send_req */
+TRP_RC trpc_send_msg (TRPC_INSTANCE *trpc, 
+                      const char *msg_content)
+{
+  int err=0;
+  TRP_RC rc=TRP_SUCCESS;
+
+  /* Send the request over the connection */
+  if (err = gsscon_write_encrypted_token(trp_connection_get_fd(trpc_get_conn(trpc)),
+                                         *trp_connection_get_gssctx(trpc_get_conn(trpc)),
+                                         msg_content, 
+                                         strlen(msg_content))) {
+    tr_err( "trpc_send_msg: Error sending message over connection.\n");
+    rc=TRP_ERROR;
+  }
+  return rc;
+}
diff --git a/trp/trps.c b/trp/trps.c
new file mode 100644 (file)
index 0000000..1997c54
--- /dev/null
@@ -0,0 +1,1168 @@
+#include <fcntl.h>
+#include <talloc.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <gsscon.h>
+#include <tr_rp.h>
+#include <trust_router/tr_name.h>
+#include <trp_internal.h>
+#include <trp_ptable.h>
+#include <trp_rtable.h>
+#include <tr_debug.h>
+
+
+static int trps_destructor(void *object)
+{
+  TRPS_INSTANCE *trps=talloc_get_type_abort(object, TRPS_INSTANCE);
+  if (trps->rtable!=NULL)
+    trp_rtable_free(trps->rtable);
+  return 0;
+}
+
+TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
+{
+  TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
+  if (trps!=NULL)  {
+    trps->hostname=NULL;
+    trps->port=0;
+    trps->cookie=NULL;
+    trps->conn=NULL;
+    trps->trpc=NULL;
+    trps->update_interval=(struct timeval){0,0};
+    trps->sweep_interval=(struct timeval){0,0};
+
+    trps->mq=tr_mq_new(trps);
+    if (trps->mq==NULL) {
+      /* failed to allocate mq */
+      talloc_free(trps);
+      return NULL;
+    }
+
+    trps->ptable=trp_ptable_new(trps);
+    if (trps->ptable==NULL) {
+      /* failed to allocate ptable */
+      talloc_free(trps);
+      return NULL;
+    }
+
+    trps->rtable=NULL;
+    if (trps_init_rtable(trps) != TRP_SUCCESS) {
+      /* failed to allocate rtable */
+      talloc_free(trps);
+      return NULL;
+    }
+
+    talloc_set_destructor((void *)trps, trps_destructor);
+  }
+  return trps;
+}
+
+/* create a new route table, first discarding an old one if necessary */
+TRP_RC trps_init_rtable(TRPS_INSTANCE *trps)
+{
+  if (trps->rtable != NULL) {
+    trp_rtable_free(trps->rtable);
+    trps->rtable=NULL;
+  }
+
+  trps->rtable=trp_rtable_new();
+  if (trps->rtable==NULL) {
+    return TRP_NOMEM;
+  }
+  return TRP_SUCCESS;
+}
+
+void trps_clear_rtable(TRPS_INSTANCE *trps)
+{
+  trp_rtable_clear(trps->rtable);
+}
+
+void trps_free (TRPS_INSTANCE *trps)
+{
+  if (trps!=NULL)
+    talloc_free(trps);
+}
+
+TR_MQ_MSG *trps_mq_pop(TRPS_INSTANCE *trps)
+{
+  return tr_mq_pop(trps->mq);
+}
+
+void trps_mq_add(TRPS_INSTANCE *trps, TR_MQ_MSG *msg)
+{
+  tr_mq_add(trps->mq, msg);
+}
+
+unsigned int trps_get_connect_interval(TRPS_INSTANCE *trps)
+{
+  return trps->connect_interval.tv_sec;
+}
+
+void trps_set_connect_interval(TRPS_INSTANCE *trps, unsigned int interval)
+{
+  trps->connect_interval.tv_sec=interval;
+  trps->connect_interval.tv_usec=0;
+}
+
+unsigned int trps_get_update_interval(TRPS_INSTANCE *trps)
+{
+  return trps->update_interval.tv_sec;
+}
+
+void trps_set_update_interval(TRPS_INSTANCE *trps, unsigned int interval)
+{
+  trps->update_interval.tv_sec=interval;
+  trps->update_interval.tv_usec=0;
+}
+
+unsigned int trps_get_sweep_interval(TRPS_INSTANCE *trps)
+{
+  return trps->sweep_interval.tv_sec;
+}
+
+void trps_set_sweep_interval(TRPS_INSTANCE *trps, unsigned int interval)
+{
+  trps->sweep_interval.tv_sec=interval;
+  trps->sweep_interval.tv_usec=0;
+}
+
+TRPC_INSTANCE *trps_find_trpc(TRPS_INSTANCE *trps, TRP_PEER *peer)
+{
+  TRPC_INSTANCE *cur=NULL;
+  TR_NAME *name=NULL;
+  TR_NAME *peer_gssname=trp_peer_get_gssname(peer);
+
+  for (cur=trps->trpc; cur!=NULL; cur=trpc_get_next(cur)) {
+    name=trpc_get_gssname(cur);
+    if ((name!=NULL) && (0==tr_name_cmp(peer_gssname, name))) {
+      break;
+    }
+  }
+  return cur;
+}
+
+void trps_add_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *new)
+{
+  if (trps->conn==NULL)
+    trps->conn=new;
+  else
+    trp_connection_append(trps->conn, new);
+
+  talloc_steal(trps, new);
+}
+
+/* ok to call more than once; guarantees connection no longer in the list.
+ * Caller is responsible for freeing the removed element afterwards.  */
+void trps_remove_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *remove)
+{
+  trps->conn=trp_connection_remove(trps->conn, remove);
+}
+
+void trps_add_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *trpc)
+{
+  if (trps->trpc==NULL)
+    trps->trpc=trpc;
+  else
+    trpc_append(trps->trpc, trpc);
+
+  talloc_steal(trps, trpc);
+}
+
+/* ok to call more than once; guarantees trpc no longer in the list.
+ * Caller is responsible for freeing the removed element afterwards.  */
+void trps_remove_trpc(TRPS_INSTANCE *trps, TRPC_INSTANCE *remove)
+{
+  trps->trpc=trpc_remove(trps->trpc, remove);
+}
+
+TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_MQ_MSG *mq_msg=NULL;
+  char *msg_dup=NULL;
+  TRP_RC rc=TRP_ERROR;
+  TRPC_INSTANCE *trpc=NULL;
+
+  /* get the connection for this peer */
+  trpc=trps_find_trpc(trps, peer);
+  /* instead, let's let that happen and then clear the queue when an attempt to
+   * connect fails */
+  if (trpc==NULL) {
+    tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
+  } else {
+    mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND, TR_MQ_PRIO_NORMAL);
+    msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
+    tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
+    trpc_mq_add(trpc, mq_msg);
+    rc=TRP_SUCCESS;
+  }
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+static int trps_listen (TRPS_INSTANCE *trps, int port) 
+{
+  int rc = 0;
+  int conn = -1;
+  int optval = 1;
+
+  union {
+    struct sockaddr_storage storage;
+    struct sockaddr_in in4;
+  } addr;
+
+  struct sockaddr_in *saddr = (struct sockaddr_in *) &addr.in4;
+
+  saddr->sin_port = htons (port);
+  saddr->sin_family = AF_INET;
+  saddr->sin_addr.s_addr = INADDR_ANY;
+
+  if (0 > (conn = socket (AF_INET, SOCK_STREAM, 0)))
+    return conn;
+
+  setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+
+  if (0 > (rc = bind (conn, (struct sockaddr *) saddr, sizeof(struct sockaddr_in))))
+    return rc;
+
+  if (0 > (rc = listen(conn, 512)))
+    return rc;
+
+  tr_debug("trps_listen: TRP Server listening on port %d", port);
+  return conn;
+}
+
+/* get the currently selected route if available */
+TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
+{
+  return trp_rtable_get_entry(trps->rtable, comm, realm, peer);
+}
+
+TRP_ROUTE *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
+{
+  return trp_rtable_get_selected_entry(trps->rtable, comm, realm);
+}
+
+/* copy the result if you want to keep it */
+TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm)
+{
+  TRP_ROUTE *route=trps_get_selected_route(trps, comm, realm);
+  if (route==NULL)
+    return NULL;
+
+  return trp_route_get_next_hop(route);
+}
+
+
+/* mark a route as retracted */
+static void trps_retract_route(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
+{
+  trp_route_set_metric(entry, TRP_METRIC_INFINITY);
+  trp_route_set_triggered(entry, 1);
+}
+
+/* is this route retracted? */
+static int trps_route_retracted(TRPS_INSTANCE *trps, TRP_ROUTE *entry)
+{
+  return (trp_metric_is_infinite(trp_route_get_metric(entry)));
+}
+
+static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MSG **msg)
+{
+  int err=0;
+  char *buf=NULL;
+  size_t buflen = 0;
+  TR_NAME *peer=NULL;
+
+  tr_debug("trps_read_message: started");
+  if (err = gsscon_read_encrypted_token(trp_connection_get_fd(conn),
+                                       *(trp_connection_get_gssctx(conn)), 
+                                       &buf,
+                                       &buflen)) {
+    tr_debug("trps_read_message: error");
+    if (buf)
+      free(buf);
+    return TRP_ERROR;
+  }
+
+  tr_debug("trps_read_message(): message received, %u bytes.", (unsigned) buflen);
+  tr_debug("trps_read_message(): %.*s", buflen, buf);
+
+  *msg=tr_msg_decode(buf, buflen);
+  free(buf);
+  if (*msg==NULL)
+    return TRP_NOPARSE;
+
+  peer=trp_connection_get_peer(conn);
+  /* verify we received a message we support, otherwise drop it now */
+  switch (tr_msg_get_msg_type(*msg)) {
+  case TRP_UPDATE:
+    trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(peer));
+    break;
+
+  case TRP_REQUEST:
+    trp_req_set_peer(tr_msg_get_trp_req(*msg), tr_dup_name(peer));
+    break;
+
+  default:
+    tr_debug("trps_read_message: received unsupported message from %.*s", peer->len, peer->buf);
+    tr_msg_free_decoded(*msg);
+    *msg=NULL;
+    return TRP_UNSUPPORTED;
+  }
+  
+  return TRP_SUCCESS;
+}
+
+int trps_get_listener(TRPS_INSTANCE *trps,
+                      TRPS_MSG_FUNC msg_handler,
+                      TRP_AUTH_FUNC auth_handler,
+                      const char *hostname,
+                      unsigned int port,
+                      void *cookie)
+{
+  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);
+    }
+  } 
+
+  if (listen > 0) {
+    /* 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;
+    }
+  }
+
+  if (listen > 0) {
+    /* store the caller's request handler & cookie */
+    trps->msg_handler = msg_handler;
+    trps->auth_handler = auth_handler;
+    trps->hostname = talloc_strdup(trps, hostname);
+    trps->port = port;
+    trps->cookie = cookie;
+  }
+
+  return listen;
+}
+
+TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
+{
+  /* try to establish a GSS context */
+  if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
+    tr_notice("trps_authorize_connection: failed to authorize connection");
+    trp_connection_close(conn);
+    return TRP_ERROR;
+  }
+  tr_notice("trps_authorize_connection: authorized connection");
+  return TRP_SUCCESS;
+}
+
+void trps_handle_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
+{
+  TR_MSG *msg=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  /* loop as long as the connection exists */
+  while (trp_connection_get_status(conn)==TRP_CONNECTION_UP) {
+    rc=trps_read_message(trps, conn, &msg);
+    switch(rc) {
+    case TRP_SUCCESS:
+      trps->msg_handler(trps, conn, msg); /* send the TR_MSG off to the callback */
+      break;
+
+    case TRP_ERROR:
+      trp_connection_close(conn);
+      break;
+
+    default:
+      tr_debug("trps_handle_connection: trps_read_message failed (%d)", rc);
+    }
+  }
+
+  tr_debug("trps_handle_connection: connection closed.");
+}
+
+static TRP_RC trps_validate_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
+{
+  if (upd==NULL) {
+    tr_notice("trps_validate_update: null TRP update.");
+    return TRP_BADARG;
+  }
+
+  if (trp_upd_get_inforec(upd)==NULL) {
+    tr_notice("trps_validate_update: received TRP update with no info records.");
+    return TRP_ERROR;
+  }
+
+  if (trp_upd_get_peer(upd)==NULL) {
+    tr_notice("trps_validate_update: received TRP update without origin peer information.");
+    return TRP_ERROR;
+  }
+  
+  return TRP_SUCCESS;
+}
+
+/* ensure that the update could be accepted if feasible */
+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)
+       || (trp_inforec_get_next_hop(rec)==NULL)) {
+      tr_debug("trps_validate_inforec: missing record info.");
+      return TRP_ERROR;
+    }
+
+    /* check for valid metric */
+    if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
+      tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
+      return TRP_ERROR;
+    }
+
+    /* check for valid interval */
+    if (trp_inforec_get_interval(rec)==TRP_INTERVAL_INVALID) {
+      tr_debug("trps_validate_inforec: invalid interval.");
+      return TRP_ERROR;
+    }
+    break;
+
+  default:
+    tr_notice("trps_validate_inforec: unsupported record type.");
+    return TRP_UNSUPPORTED;
+  }
+
+  return TRP_SUCCESS;
+}
+
+/* link cost to a peer */
+static unsigned int trps_cost(TRPS_INSTANCE *trps, TR_NAME *peer)
+{
+  return 1;
+}
+
+static unsigned int trps_advertised_metric(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
+{
+  TRP_ROUTE *entry=trp_rtable_get_entry(trps->rtable, comm, realm, peer);
+  if (entry==NULL)
+    return TRP_METRIC_INFINITY;
+  return trp_route_get_metric(entry) + trps_cost(trps, peer);
+}
+
+static int trps_check_feasibility(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
+{
+  unsigned int rec_metric=trp_inforec_get_metric(rec);
+  unsigned int new_metric=0;
+  unsigned int current_metric=0;
+  TR_NAME *next_hop=NULL;
+
+  /* we check these in the validation stage, but just in case... */
+  if (trp_metric_is_invalid(rec_metric))
+    return 0;
+
+  /* retractions (aka infinite metrics) are always feasible */
+  if (trp_metric_is_infinite(rec_metric))
+    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));;
+  if ((next_hop!=NULL)
+     && (0==tr_name_cmp(next_hop,trp_inforec_get_next_hop(rec)))) {
+    return 1;
+  }
+    
+
+  /* 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));
+  new_metric=rec_metric + trps_cost(trps, trp_inforec_get_next_hop(rec));
+  if (new_metric <= current_metric)
+    return 1;
+  else
+    return 0;
+}
+
+/* uses memory pointed to by *ts, also returns that value. On error, its contents are {0,0} */
+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)) {
+    tr_err("trps_compute_expiry: could not read realtime clock.");
+    ts->tv_sec=0;
+    ts->tv_nsec=0;
+  }
+  ts->tv_sec += small_factor*interval;
+  return ts;
+}
+
+static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
+{
+  TRP_ROUTE *entry=NULL;
+
+  entry=trp_rtable_get_entry(trps->rtable,
+                             trp_inforec_get_comm(rec),
+                             trp_inforec_get_realm(rec),
+                             trp_inforec_get_next_hop(rec));
+  if (entry==NULL) {
+    entry=trp_route_new(NULL);
+    if (entry==NULL) {
+      tr_err("trps_accept_update: unable to allocate new entry.");
+      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_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));
+    if ((trp_route_get_comm(entry)==NULL)
+       ||(trp_route_get_realm(entry)==NULL)
+       ||(trp_route_get_peer(entry)==NULL)
+       ||(trp_route_get_trust_router(entry)==NULL)
+       ||(trp_route_get_next_hop(entry)==NULL)) {
+      /* at least one field could not be allocated */
+      tr_err("trps_accept_update: unable to allocate all fields for entry.");
+      trp_route_free(entry);
+      return TRP_NOMEM;
+    }
+    trp_rtable_add(trps->rtable, entry);
+  }
+
+  /* We now have an entry in the table, whether it's new or not. Update metric and expiry, unless
+   * the metric is infinity. An infinite metric can only occur here if we just retracted an existing
+   * route (we never accept retractions as new routes), so there is no risk of leaving the expiry
+   * time unset on a new route entry. */
+  tr_debug("trps_accept_update: accepting route update.");
+  trp_route_set_metric(entry, trp_inforec_get_metric(rec));
+  trp_route_set_interval(entry, trp_inforec_get_interval(rec));
+
+  /* check whether the trust router has changed */
+  if (0!=tr_name_cmp(trp_route_get_trust_router(entry),
+                     trp_inforec_get_trust_router(rec))) {
+    /* The name changed. Set this route as triggered. */
+    tr_debug("trps_accept_update: trust router for route changed.");
+    trp_route_set_triggered(entry, 1);
+    trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); /* frees old name */
+  }
+  if (!trps_route_retracted(trps, entry)) {
+    tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
+    trp_route_set_expiry(entry, trps_compute_expiry(trps,
+                                                     trp_route_get_interval(entry),
+                                                     trp_route_get_expiry(entry)));
+  }
+  return TRP_SUCCESS;
+}
+
+static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
+{
+  unsigned int feas=0;
+  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.");
+    return TRP_ERROR;
+  }
+
+  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.");
+      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);
+    }
+  }
+  return TRP_SUCCESS;
+}
+
+static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
+{
+  if (req==NULL) {
+    tr_notice("trps_validate_request: null TRP request.");
+    return TRP_BADARG;
+  }
+
+  if (trp_req_get_comm(req)==NULL) {
+    tr_notice("trps_validate_request: received TRP request with null community.");
+    return TRP_ERROR;
+  }
+  
+  if (trp_req_get_realm(req)==NULL) {
+    tr_notice("trps_validate_request: received TRP request with null realm.");
+    return TRP_ERROR;
+  }
+  
+  if (trp_req_get_peer(req)==NULL) {
+    tr_notice("trps_validate_request: received TRP request without origin peer information.");
+    return TRP_ERROR;
+  }
+  
+  return TRP_SUCCESS;
+}
+
+/* choose the best route to comm/realm, optionally excluding routes to a particular peer */
+static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
+                                        TR_NAME *comm,
+                                        TR_NAME *realm,
+                                        TR_NAME *exclude_peer)
+{
+  TRP_ROUTE **entry=NULL;
+  TRP_ROUTE *best=NULL;
+  size_t n_entry=0;
+  unsigned int kk=0;
+  unsigned int kk_min=0;
+  unsigned int min_metric=TRP_METRIC_INFINITY;
+
+  entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
+  for (kk=0; kk<n_entry; kk++) {
+    if (trp_route_get_metric(entry[kk]) < min_metric) {
+      if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_route_get_peer(entry[kk]),
+                                                  exclude_peer))) {
+        kk_min=kk;
+        min_metric=trp_route_get_metric(entry[kk]);
+      } 
+    }
+  }
+  if (trp_metric_is_finite(min_metric))
+    best=entry[kk_min];
+  
+  talloc_free(entry);
+  return best;
+}
+
+/* TODO: think this through more carefully. At least ought to add hysteresis
+ * to avoid flapping between routers or routes. */
+TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
+{
+  size_t n_comm=0, ii=0;
+  TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
+  size_t n_realm=0, jj=0;
+  TR_NAME **realm=NULL;
+  TRP_ROUTE *best_route=NULL, *cur_route=NULL;
+  unsigned int best_metric=0, cur_metric=0;
+
+  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_route=trps_find_best_route(trps, comm[ii], realm[jj], NULL);
+      if (best_route==NULL)
+        best_metric=TRP_METRIC_INFINITY;
+      else
+        best_metric=trp_route_get_metric(best_route);
+
+      cur_route=trps_get_selected_route(trps, comm[ii], realm[jj]);
+      if (cur_route!=NULL) {
+        cur_metric=trp_route_get_metric(cur_route);
+        if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
+          /* The new route has a lower metric than the previous, and is finite. Accept. */
+          trp_route_set_selected(cur_route, 0);
+          trp_route_set_selected(best_route, 1);
+        } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
+          trp_route_set_selected(cur_route, 0);
+      } else if (trp_metric_is_finite(best_metric)) {
+        trp_route_set_selected(best_route, 1);
+      }
+    }
+    if (realm!=NULL)
+      talloc_free(realm);
+    realm=NULL; n_realm=0;
+  }
+  if (comm!=NULL)
+    talloc_free(comm);
+  comm=NULL; n_comm=0;
+
+  return TRP_SUCCESS;
+}
+
+/* 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)));
+}
+
+/* Sweep for expired routes. For each expired route, if its metric is infinite, the route is flushed.
+ * If its metric is finite, the metric is set to infinite and the route's expiration time is updated. */
+TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
+{
+  struct timespec sweep_time={0,0};
+  TRP_ROUTE **entry=NULL;
+  size_t n_entry=0;
+  size_t ii=0;
+
+  /* use a single time for the entire sweep */
+  if (0!=clock_gettime(CLOCK_REALTIME, &sweep_time)) {
+    tr_err("trps_sweep_routes: could not read realtime clock.");
+    sweep_time.tv_sec=0;
+    sweep_time.tv_nsec=0;
+    return TRP_ERROR;
+  }
+
+  entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
+
+  /* loop over the entries */
+  for (ii=0; ii<n_entry; ii++) {
+    if (!trp_route_is_local(entry[ii]) && trps_expired(trp_route_get_expiry(entry[ii]), &sweep_time)) {
+      tr_debug("trps_sweep_routes: route expired.");
+      if (!trp_metric_is_finite(trp_route_get_metric(entry[ii]))) {
+        /* flush route */
+        tr_debug("trps_sweep_routes: metric was infinity, flushing route.");
+        trp_rtable_remove(trps->rtable, entry[ii]); /* entry[ii] is no longer valid */
+        entry[ii]=NULL;
+      } else {
+        /* set metric to infinity and reset timer */
+        tr_debug("trps_sweep_routes: setting metric to infinity and resetting expiry.");
+        trp_route_set_metric(entry[ii], TRP_METRIC_INFINITY);
+        trp_route_set_expiry(entry[ii], trps_compute_expiry(trps,
+                                                             trp_route_get_interval(entry[ii]),
+                                                             trp_route_get_expiry(entry[ii])));
+      }
+    }
+  }
+
+  talloc_free(entry);
+  return TRP_SUCCESS;
+}
+
+/* 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)
+{
+  TRP_ROUTE *route;
+
+  /* Take the currently selected route unless it is through the peer we're sending the update to.
+   * I.e., enforce the split horizon rule. */
+  route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
+  if (route==NULL) {
+    /* No selected route, this should only happen if the only route has been retracted,
+     * in which case we do not want to advertise it. */
+    return NULL;
+  }
+  tr_debug("trps_select_realm_update: %s vs %s", peer_gssname->buf,
+           trp_route_get_peer(route)->buf);
+  if (0==tr_name_cmp(peer_gssname, trp_route_get_peer(route))) {
+    tr_debug("trps_select_realm_update: matched, finding alternate route");
+    /* the selected entry goes through the peer we're reporting to, choose an alternate */
+    route=trps_find_best_route(trps, comm, realm, peer_gssname);
+    if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route))))
+      return NULL; /* don't advertise a nonexistent or retracted route */
+  }
+  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,
+                                                 TRPS_INSTANCE *trps,
+                                                 TR_NAME *peer_gssname,
+                                                 int triggered,
+                                                 size_t *n_update)
+{
+  size_t n_comm=0;
+  TR_NAME **comm=trp_rtable_get_comms(trps->rtable, &n_comm);
+  TR_NAME **realm=NULL;
+  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;
+  }
+  
+  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 (realm!=NULL)
+      talloc_free(realm);
+    realm=NULL;
+    n_realm=0;
+  }
+  if (comm!=NULL)
+    talloc_free(comm);
+
+  *n_update=n_used;
+  return result;
+}
+
+/* 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_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;
+    }
+  }
+  return rec;
+}
+
+/* all routes 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,
+                                   TR_NAME *peer_gssname,
+                                   TRP_UPDATE_TYPE update_type,
+                                   TR_NAME *comm,
+                                   TR_NAME *realm)
+{
+  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;
+  char *encoded=NULL;
+  TRP_RC rc=TRP_ERROR;
+  TRP_PEER *peer=trps_get_peer_by_gssname(trps, peer_gssname);
+
+  switch (update_type) {
+  case TRP_UPDATE_TRIGGERED:
+    tr_debug("trps_update_one_peer: preparing triggered route update for %.*s",
+             peer_gssname->len, peer_gssname->buf);
+    break;
+  case TRP_UPDATE_SCHEDULED:
+    tr_debug("trps_update_one_peer: preparing scheduled route update for %.*s",
+             peer_gssname->len, peer_gssname->buf);
+    break;
+  case TRP_UPDATE_REQUESTED:
+    tr_debug("trps_update_one_peer: preparing requested route update for %.*s",
+             peer_gssname->len, peer_gssname->buf);
+  }
+
+  /* do not fill in peer, recipient does that */
+  if ((comm==NULL) && (realm==NULL)) {
+    /* do all realms */
+    update_list=trps_select_updates_for_peer(tmp_ctx,
+                                             trps,
+                                             peer_gssname,
+                                             update_type==TRP_UPDATE_TRIGGERED,
+                                            &n_updates);
+  } 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_gssname);
+    if (*update_list==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(""));
+    }
+    n_updates=1;
+  } else {
+    tr_err("trps_update_one_peer: error: only comm or realm was specified.");
+    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.");
+        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_msg_free_encoded(encoded);
+    encoded=NULL;
+    trp_upd_free(upd);
+    upd=NULL;
+  }
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* all routes to all peers */
+TRP_RC trps_update(TRPS_INSTANCE *trps, TRP_UPDATE_TYPE update_type)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_PTABLE_ITER *iter=trp_ptable_iter_new(tmp_ctx);
+  TRP_PEER *peer=NULL;
+  TRP_RC rc=TRP_SUCCESS;
+
+  if (iter==NULL) {
+    tr_err("trps_update: failed to allocate peer table iterator.");
+    talloc_free(tmp_ctx);
+    return TRP_NOMEM;
+  }
+
+  for (peer=trp_ptable_iter_first(iter, trps->ptable);
+       peer!=NULL && rc==TRP_SUCCESS;
+       peer=trp_ptable_iter_next(iter))
+  {
+    if (!trps_peer_connected(trps, peer)) {
+      TR_NAME *peer_gssname=trp_peer_get_gssname(peer);
+      tr_debug("trps_update: no TRP connection to %.*s, skipping.",
+               peer_gssname->len, peer_gssname->buf);
+      continue;
+    }
+    rc=trps_update_one_peer(trps, trp_peer_get_gssname(peer), update_type, NULL, NULL);
+  }
+  
+  trp_ptable_iter_free(iter);
+  trp_rtable_clear_triggered(trps->rtable); /* don't re-send triggered updates */
+  talloc_free(tmp_ctx);
+  return rc;
+}        
+
+TRP_RC trps_add_route(TRPS_INSTANCE *trps, TRP_ROUTE *route)
+{
+  trp_rtable_add(trps->rtable, route); /* should return status */
+  return TRP_SUCCESS; 
+}
+
+/* steals the peer object */
+TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer)
+{
+  return trp_ptable_add(trps->ptable, peer);
+}
+
+TRP_PEER *trps_get_peer_by_gssname(TRPS_INSTANCE *trps, TR_NAME *gssname)
+{
+  return trp_ptable_find_gssname(trps->ptable, gssname);
+}
+
+TRP_PEER *trps_get_peer_by_servicename(TRPS_INSTANCE *trps, TR_NAME *servicename)
+{
+  return trp_ptable_find_servicename(trps->ptable, servicename);
+}
+
+int trps_peer_connected(TRPS_INSTANCE *trps, TRP_PEER *peer)
+{
+  TRPC_INSTANCE *trpc=trps_find_trpc(trps, peer);
+  if (trpc==NULL)
+    return 0;
+
+  if (trpc_get_status(trpc)==TRP_CONNECTION_UP)
+    return 1;
+  else
+    return 0;
+}
+
+
+static TRP_RC trps_handle_request(TRPS_INSTANCE *trps, TRP_REQ *req)
+{
+  TR_NAME *comm=NULL;
+  TR_NAME *realm=NULL;
+
+  tr_debug("trps_handle_request: handling TRP request.");
+
+  if (trps_validate_request(trps, req) != TRP_SUCCESS) {
+    tr_notice("trps_handle_request: received invalid TRP request.");
+    return TRP_ERROR;
+  }
+
+  if (!trp_req_is_wildcard(req)) {
+    comm=trp_req_get_comm(req);
+    realm=trp_req_get_realm(req);
+    tr_debug("trps_handle_request: route for %.*s/%.*s requested.",
+             comm->len, comm->buf, realm->len, realm->buf);
+  } else {
+    tr_debug("trps_handle_request: all routes requested.");
+    /* leave comm/realm NULL */
+  }
+  return trps_update_one_peer(trps, trp_req_get_peer(req), TRP_UPDATE_REQUESTED, comm, realm);
+}
+
+
+TRP_RC trps_handle_tr_msg(TRPS_INSTANCE *trps, TR_MSG *tr_msg)
+{
+  TRP_RC rc=TRP_ERROR;
+
+  switch (tr_msg_get_msg_type(tr_msg)) {
+  case TRP_UPDATE:
+    rc=trps_handle_update(trps, tr_msg_get_trp_upd(tr_msg));
+    if (rc==TRP_SUCCESS) {
+      rc=trps_update_active_routes(trps);
+      trps_update(trps, TRP_UPDATE_TRIGGERED); /* send any triggered routes */
+    }
+    return rc;
+
+  case TRP_REQUEST:
+    rc=trps_handle_request(trps, tr_msg_get_trp_req(tr_msg));
+    return rc;
+
+  default:
+    /* unknown error or one we don't care about (e.g., TID messages) */
+    return TRP_ERROR;
+  }
+}
+
+/* send wildcard route request to a peer */
+TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TRP_PEER *peer=trps_get_peer_by_servicename(trps, peer_servicename);
+  TR_MSG msg; /* not a pointer */
+  TRP_REQ *req=trp_req_new(tmp_ctx);
+  char *encoded=NULL;
+  TRP_RC rc=TRP_ERROR;
+
+  if (peer==NULL) {
+    tr_err("trps_wildcard_route_req: unknown peer (%.*s).", peer_servicename->len, peer_servicename->buf);
+    rc=TRP_BADARG;
+    goto cleanup;
+  }
+  if ((req==NULL) || (trp_req_make_wildcard(req)!=TRP_SUCCESS)) {
+    tr_err("trps_wildcard_route_req: unable to create wildcard TRP request.");
+    rc=TRP_NOMEM;
+    goto cleanup;
+  }
+
+  tr_msg_set_trp_req(&msg, req);
+  encoded=tr_msg_encode(&msg);
+  if (encoded==NULL) {
+    tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
+    rc=TRP_ERROR;
+    goto cleanup;
+  }
+
+  tr_debug("trps_wildcard_route_req: adding message to queue.");
+  if (trps_send_msg(trps, peer, encoded) != TRP_SUCCESS) {
+    tr_err("trps_wildcard_route_req: error queueing request.");
+    rc=TRP_ERROR;
+  } else {
+    tr_debug("trps_wildcard_route_req: request queued successfully.");
+    rc=TRP_SUCCESS;
+  }
+
+cleanup:
+  if (encoded!=NULL)
+    tr_msg_free_encoded(encoded);
+  if (req!=NULL)
+    trp_req_free(req);
+
+  talloc_free(tmp_ctx);
+  return rc;
+}