Merge branch 'milestone/monitoring' into jennifer/request_id
authorJennifer Richards <jennifer@painless-security.com>
Thu, 3 May 2018 20:50:54 +0000 (16:50 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Thu, 3 May 2018 20:50:54 +0000 (16:50 -0400)
# Conflicts:
# include/trust_router/tid.h
# tid/tidc.c
# tr/tr_tid.c

97 files changed:
CMakeLists.txt [new file with mode: 0644]
Makefile.am
common/tests/filt_test.c
common/tr_comm.c
common/tr_comm_encoders.c [new file with mode: 0644]
common/tr_config.c
common/tr_config_comms.c [new file with mode: 0644]
common/tr_config_encoders.c [new file with mode: 0644]
common/tr_config_filters.c [new file with mode: 0644]
common/tr_config_internal.c [new file with mode: 0644]
common/tr_config_orgs.c [new file with mode: 0644]
common/tr_config_realms.c [new file with mode: 0644]
common/tr_config_rp_clients.c [new file with mode: 0644]
common/tr_filter_encoders.c [new file with mode: 0644]
common/tr_gss.c
common/tr_gss_client.c [new file with mode: 0644]
common/tr_gss_names.c [new file with mode: 0644]
common/tr_idp.c
common/tr_idp_encoders.c [new file with mode: 0644]
common/tr_msg.c
common/tr_rp.c
common/tr_rp_client.c [new file with mode: 0644]
common/tr_rp_client_encoders.c [new file with mode: 0644]
common/tr_socket.c [new file with mode: 0644]
common/tr_util.c
configure.ac
include/mon.h [new file with mode: 0644]
include/mon_internal.h [new file with mode: 0644]
include/mons_handlers.h [new file with mode: 0644]
include/tid_internal.h
include/tr_comm.h
include/tr_config.h
include/tr_event.h
include/tr_filter.h
include/tr_gss.h
include/tr_gss_client.h [new file with mode: 0644]
include/tr_gss_names.h [new file with mode: 0644]
include/tr_idp.h
include/tr_mon.h [new file with mode: 0644]
include/tr_msg.h
include/tr_rp.h
include/tr_rp_client.h [new file with mode: 0644]
include/tr_socket.h [new file with mode: 0644]
include/tr_tid.h
include/tr_trp.h
include/tr_util.h
include/trp_internal.h
include/trp_peer.h [new file with mode: 0644]
include/trp_ptable.h
include/trp_route.h [new file with mode: 0644]
include/trp_rtable.h
include/trust_router/tid.h
mon/mon_common.c [new file with mode: 0644]
mon/mon_req.c [new file with mode: 0644]
mon/mon_req_decode.c [new file with mode: 0644]
mon/mon_req_encode.c [new file with mode: 0644]
mon/mon_resp.c [new file with mode: 0644]
mon/mon_resp_decode.c [new file with mode: 0644]
mon/mon_resp_encode.c [new file with mode: 0644]
mon/monc.c [new file with mode: 0644]
mon/mons.c [new file with mode: 0644]
mon/mons_handlers.c [new file with mode: 0644]
mon/tests/req_reconfigure.test [new file with mode: 0644]
mon/tests/req_show_all_options.test [new file with mode: 0644]
mon/tests/req_show_no_options.test [new file with mode: 0644]
mon/tests/resp_reconfigure_error.test [new file with mode: 0644]
mon/tests/resp_reconfigure_success.test [new file with mode: 0644]
mon/tests/resp_show_success.test [new file with mode: 0644]
mon/tests/test_mon_req_decode.c [new file with mode: 0644]
mon/tests/test_mon_req_encode.c [new file with mode: 0644]
mon/tests/test_mon_resp_encode.c [new file with mode: 0644]
tid/example/tidc_main.c
tid/example/tids_main.c
tid/tid_req.c
tid/tid_resp.c
tid/tidc.c
tid/tids.c
tr/tr_main.c
tr/tr_mon.c [new file with mode: 0644]
tr/tr_tid.c
tr/tr_tid_mons.c [new file with mode: 0644]
tr/tr_trp.c
tr/tr_trp_mons.c [new file with mode: 0644]
tr/trmon_main.c [new file with mode: 0644]
trp/msgtst.c
trp/test/ptbl_test.c
trp/test/rtbl_test.c
trp/trp_peer.c [new file with mode: 0644]
trp/trp_peer_encoders.c [new file with mode: 0644]
trp/trp_ptable.c
trp/trp_ptable_encoders.c [new file with mode: 0644]
trp/trp_route.c [new file with mode: 0644]
trp/trp_route_encoders.c [new file with mode: 0644]
trp/trp_rtable.c
trp/trp_rtable_encoders.c [new file with mode: 0644]
trp/trps.c
trust_router.spec

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4ae8dba
--- /dev/null
@@ -0,0 +1,114 @@
+# Rudimentary CMakeLists.txt
+#
+# This is not used for real builds, it mostly exists to enable code navigation in
+# CLion. Real builds use autotools + make.
+#
+cmake_minimum_required(VERSION 3.6)
+project(trust_router)
+
+set(CMAKE_CXX_STANDARD 11)
+
+include(FindPkgConfig)
+pkg_check_modules(GLIB glib-2.0 REQUIRED)
+include_directories(${GLIB_INCLUDE_DIRS})
+
+include_directories(include)
+
+# Fill these in so CLion doesn't complain. The real versions are set in the Makefile
+add_definitions(-DPACKAGE_VERSION="built-with-cmake"
+                -DPACKAGE_NAME="Moonshot Trust Router"
+                -DPACKAGE_BUGREPORT="bugs@painless-security.com")
+
+set(SOURCE_FILES
+        common/tests/cfg_test.c
+        common/tests/commtest.c
+    common/tests/dh_test.c
+    common/tests/mq_test.c
+    common/tests/thread_test.c
+    common/jansson_iterators.h
+    common/t_constraint.c
+    common/tr_apc.c
+    common/tr_comm.c
+    common/tr_config.c
+    common/tr_constraint.c
+    common/tr_debug.c
+    common/tr_dh.c
+    common/tr_filter.c
+        common/tr_gss_names.c
+    common/tr_idp.c
+    common/tr_mq.c
+    common/tr_msg.c
+    common/tr_name.c
+    common/tr_rp.c
+    common/tr_util.c
+    gsscon/test/gsscon_client.c
+    gsscon/test/gsscon_server.c
+    gsscon/gsscon_active.c
+    gsscon/gsscon_common.c
+    gsscon/gsscon_passive.c
+    include/trust_router/tid.h
+    include/trust_router/tr_constraint.h
+    include/trust_router/tr_dh.h
+    include/trust_router/tr_name.h
+    include/trust_router/tr_versioning.h
+    include/trust_router/trp.h
+    include/gsscon.h
+    include/tid_internal.h
+    include/tr.h
+    include/tr_apc.h
+    include/tr_cfgwatch.h
+    include/tr_comm.h
+    include/tr_config.h
+    include/tr_debug.h
+    include/tr_event.h
+    include/tr_filter.h
+        include/tr_gss_names.h
+    include/tr_idp.h
+    include/tr_mq.h
+    include/tr_msg.h
+    include/tr_rp.h
+    include/tr_tid.h
+    include/tr_trp.h
+    include/tr_util.h
+    include/trp_internal.h
+    include/trp_ptable.h
+    include/trp_rtable.h
+    tid/example/tidc_main.c
+    tid/example/tids_main.c
+    tid/tid_req.c
+    tid/tid_resp.c
+    tid/tidc.c
+    tid/tids.c
+    tr/tr.c
+    tr/tr_cfgwatch.c
+    tr/tr_event.c
+    tr/tr_main.c
+    tr/tr_tid.c
+    tr/tr_trp.c
+    tr/trpc_main.c
+    trp/test/ptbl_test.c
+    trp/test/rtbl_test.c
+    trp/msgtst.c
+    trp/trp_conn.c
+    trp/trp_ptable.c
+    trp/trp_req.c
+    trp/trp_rtable.c
+    trp/trp_upd.c
+    trp/trpc.c
+    trp/trps.c include/tr_name_internal.h mon/mon_req.c mon/mon_req_encode.c mon/mon_req_decode.c
+        mon/mon_resp.c mon/mon_common.c mon/mon_resp_encode.c mon/mon_resp_decode.c tr/tr_mon.c mon/mons.c include/tr_socket.h common/tr_gss.c include/tr_gss.h common/tr_config_internal.c mon/mons_handlers.c include/mons_handlers.h tr/tr_tid_mons.c tr/tr_tid_mons.c trp/trp_route.c include/trp_route.h trp/trp_rtable_encoders.c trp/trp_route_encoders.c trp/trp_peer.c include/trp_peer.h trp/trp_peer_encoders.c trp/trp_ptable_encoders.c common/tr_idp_encoders.c common/tr_comm_encoders.c common/tr_rp_client.c include/tr_rp_client.h common/tr_rp_client_encoders.c common/tr_filter_encoders.c common/tr_config_encoders.c common/tr_config_filters.c common/tr_config_realms.c common/tr_config_rp_clients.c common/tr_config_orgs.c common/tr_config_comms.c)
+
+# Does not actually build!
+add_executable(trust_router ${SOURCE_FILES})
+
+add_executable(trmon mon/monc.c tr/trmon_main.c common/tr_gss_client.c include/tr_gss_client.h)
+
+# Test build targets - for debugging
+add_executable(test_mon_req_encode mon/mon_common.c mon/mon_req.c mon/tests/test_mon_req_encode.c mon/mon_req_encode.c)
+target_link_libraries(test_mon_req_encode jansson talloc glib-2.0)
+
+add_executable(test_mon_req_decode mon/mon_common.c mon/mon_req.c mon/tests/test_mon_req_decode.c mon/mon_req_decode.c)
+target_link_libraries(test_mon_req_decode jansson talloc glib-2.0)
+
+add_executable(test_mon_resp_encode mon/mon_common.c mon/mon_req.c mon/mon_resp.c mon/mon_resp_encode.c common/tr_name.c mon/tests/test_mon_resp_encode.c)
+target_link_libraries(test_mon_resp_encode jansson talloc glib-2.0)
index fbc763c..1231793 100644 (file)
@@ -1,8 +1,10 @@
+ACLOCAL_AMFLAGS = -I m4
 DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/tests/tr_dh_test common/tests/mq_test \
               common/tests/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test common/tests/cfg_test \
-              common/tests/commtest common/tests/name_test common/tests/filt_test
+              common/tests/commtest common/tests/name_test common/tests/filt_test mon/tests/test_mon_req_encode \
+              mon/tests/test_mon_req_decode mon/tests/test_mon_resp_encode tr/trmon
 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS)
 AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS)
 SUBDIRS = gsscon 
@@ -15,10 +17,17 @@ common_srcs = common/tr_name.c \
        common/tr_util.c \
        common/tr_apc.c \
        common/tr_comm.c \
+       common/tr_comm_encoders.c \
        common/tr_rp.c \
+       common/tr_rp_client.c \
+       common/tr_rp_client_encoders.c \
        common/tr_idp.c \
+       common/tr_idp_encoders.c \
        common/tr_filter.c \
-       common/tr_gss.c
+       common/tr_filter_encoders.c \
+       common/tr_gss_names.c \
+       common/tr_socket.c \
+       $(mon_srcs)
 
 tid_srcs = tid/tid_resp.c \
 tid/tid_req.c \
@@ -29,12 +38,44 @@ common/tr_rand_id.c
 trp_srcs = trp/trp_conn.c \
 trp/trps.c \
 trp/trpc.c \
+trp/trp_peer.c \
+trp/trp_peer_encoders.c \
 trp/trp_ptable.c \
+trp/trp_ptable_encoders.c \
+trp/trp_route.c \
+trp/trp_route_encoders.c \
 trp/trp_rtable.c \
+trp/trp_rtable_encoders.c \
 trp/trp_req.c \
 trp/trp_upd.c \
-common/tr_config.c \
-common/tr_mq.c
+common/tr_mq.c \
+$(config_srcs)
+
+# configuration parsing sources
+config_srcs = \
+    common/tr_config.c \
+    common/tr_config_comms.c \
+    common/tr_config_encoders.c \
+    common/tr_config_filters.c \
+    common/tr_config_internal.c \
+    common/tr_config_orgs.c \
+    common/tr_config_realms.c \
+    common/tr_config_rp_clients.c
+
+# general monitoring message sources
+mon_srcs =                   \
+    mon/mon_common.c         \
+    mon/mon_req.c            \
+    mon/mon_req_encode.c     \
+    mon/mon_req_decode.c     \
+    mon/mon_resp.c           \
+    mon/mon_resp_decode.c    \
+    mon/mon_resp_encode.c
+
+# monitoring server sources
+mons_srcs = \
+    mon/mons.c \
+    mon/mons_handlers.c
 
 check_PROGRAMS = common/t_constraint
 TESTS = common/t_constraint
@@ -68,15 +109,24 @@ tr/tr.c \
 tr/tr_event.c \
 tr/tr_cfgwatch.c \
 tr/tr_tid.c \
+tr/tr_tid_mons.c \
 tr/tr_trp.c \
+tr/tr_trp_mons.c \
+tr/tr_mon.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs) \
-$(common_srcs)
+$(common_srcs) \
+$(mons_srcs)
 
 tr_trust_router_LDFLAGS = $(AM_LDFLAGS) -levent_pthreads -pthread
 tr_trust_router_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
+
 tr_trpc_SOURCES =tr/trpc_main.c \
 tr/tr_trp.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(trp_srcs) \
 $(tid_srcs) \
 $(common_srcs)
@@ -84,6 +134,17 @@ $(common_srcs)
 tr_trpc_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 tr_trpc_LDFLAGS = $(AM_LDFLAGS) -pthread
 
+tr_trmon_SOURCES = tr/trmon_main.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
+$(tid_srcs) \
+$(trp_srcs) \
+$(common_srcs) \
+mon/monc.c
+
+tr_trmon_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+tr_trmon_LDFLAGS = $(AM_LDFLAGS) -pthread
+
 trp_msgtst_SOURCES = trp/msgtst.c \
 $(common_srcs) \
 common/tr_rand_id.c \
@@ -95,12 +156,18 @@ trp_msgtst_LDADD =  $(GLIB_LIBS)
 
 trp_test_rtbl_test_SOURCES = trp/test/rtbl_test.c \
 common/tr_name.c \
-common/tr_gss.c \
+common/tr_gss_names.c \
 common/tr_debug.c \
-trp/trp_rtable.c
+common/tr_util.c \
+trp/trp_route.c \
+trp/trp_route_encoders.c \
+trp/trp_rtable.c \
+trp/trp_rtable_encoders.c
 trp_test_rtbl_test_LDADD =  $(GLIB_LIBS)
 
 trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs) \
 $(common_srcs)
@@ -108,6 +175,8 @@ trp_test_ptbl_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 trp_test_ptbl_test_LDFLAGS = $(AM_LDFLAGS) -pthread
 
 tid_example_tidc_SOURCES = tid/example/tidc_main.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs) \
 $(common_srcs)
@@ -115,6 +184,8 @@ tid_example_tidc_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
 tid_example_tidc_LDFLAGS = $(AM_LDFLAGS) -pthread
 
 tid_example_tids_SOURCES = tid/example/tids_main.c \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs) \
 $(common_srcs)
@@ -133,6 +204,8 @@ common_tests_mq_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
 common_tests_cfg_test_SOURCES = common/tests/cfg_test.c \
 $(common_srcs) \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs)
 common_tests_cfg_test_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
@@ -144,6 +217,8 @@ common/tests/thread_test.c
 
 common_tests_commtest_SOURCES = common/tests/commtest.c \
 $(common_srcs) \
+common/tr_gss.c \
+common/tr_gss_client.c \
 $(tid_srcs) \
 $(trp_srcs)
 common_tests_commtest_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
@@ -153,6 +228,8 @@ common_tests_thread_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 
 common_tests_name_test_SOURCES = common/tests/name_test.c \
               $(common_srcs) \
+              common/tr_gss.c \
+              common/tr_gss_client.c \
               $(tid_srcs) \
               $(trp_srcs)
 common_tests_name_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
@@ -161,12 +238,32 @@ common_tests_name_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
 common_tests_filt_test_SOURCES = common/tests/filt_test.c \
               $(common_srcs) \
+              common/tr_gss.c \
+              common/tr_gss_client.c \
               $(tid_srcs) \
               $(trp_srcs)
 common_tests_filt_test_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 common_tests_filt_test_LDFLAGS = $(AM_LDFLAGS) -ltalloc -pthread
 common_tests_filt_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
 
+mon_tests_test_mon_req_encode_SOURCES = mon/tests/test_mon_req_encode.c \
+    $(mon_srcs) \
+    common/tr_name.c
+mon_tests_test_mon_req_encode_LDADD = $(GLIB_LIBS)
+mon_tests_test_mon_req_encode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+mon_tests_test_mon_req_decode_SOURCES = mon/tests/test_mon_req_decode.c \
+    $(mon_srcs) \
+    common/tr_name.c
+mon_tests_test_mon_req_decode_LDADD = $(GLIB_LIBS)
+mon_tests_test_mon_req_decode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+mon_tests_test_mon_resp_encode_SOURCES = mon/tests/test_mon_resp_encode.c \
+    $(mon_srcs) \
+    common/tr_name.c
+mon_tests_test_mon_resp_encode_LDADD = $(GLIB_LIBS)
+mon_tests_test_mon_resp_encode_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
 pkginclude_HEADERS = include/trust_router/tid.h include/trust_router/tr_name.h \
        include/tr_debug.h include/trust_router/trp.h \
        include/trust_router/tr_dh.h \
@@ -177,13 +274,13 @@ 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/tr_gss.h \
-       include/tid_internal.h include/trp_internal.h \
+       include/tr_tid.h include/tr_trp.h include/tr_mon.h \
+       include/tr_filter.h include/tr_gss_names.h \
+       include/tid_internal.h include/trp_internal.h include/trp_route.h \
        include/tr_cfgwatch.h include/tr_event.h \
-       include/tr_mq.h include/trp_ptable.h \
+       include/tr_mq.h include/trp_peer.h include/trp_ptable.h \
        include/trp_rtable.h include/tr_util.h \
-       include/tr_name_internal.h
+       include/tr_name_internal.h include/tr_gss.h
 
 pkgdata_DATA=schema.sql
 nobase_dist_pkgdata_DATA=redhat/init redhat/sysconfig redhat/organizations.cfg redhat/tidc-wrapper redhat/trust_router-wrapper redhat/tr-test-internal.cfg redhat/default-internal.cfg redhat/tids-wrapper redhat/sysconfig.tids
index d7dfe06..342c27b 100644 (file)
@@ -119,7 +119,7 @@ TRP_INFOREC *load_inforec(const char *fname)
   json_decref(decoded);
 
   assert(encoded);
-  assert(msg=tr_msg_decode(encoded, strlen(encoded)));
+  assert(msg= tr_msg_decode(NULL, encoded, strlen(encoded)));
   assert(upd=tr_msg_get_trp_upd(msg));
   assert(inforec=trp_upd_get_inforec(upd));
   /* now remove the inforec from the update context */
@@ -146,7 +146,7 @@ TID_REQ *load_tid_req(const char *fname)
   msglen=fread(msgbuf, 1, MAX_FILE_SIZE, f);
   assert(msglen);
   assert(feof(f));
-  msg=tr_msg_decode(msgbuf, msglen);
+  msg= tr_msg_decode(NULL, msgbuf, msglen);
   free(msgbuf);
   msgbuf=NULL;
 
index dd3c304..f8f91df 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, JANET(UK)
+ * Copyright (c) 2012-2018, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
diff --git a/common/tr_comm_encoders.c b/common/tr_comm_encoders.c
new file mode 100644 (file)
index 0000000..c33c604
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2012-2018, 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 <jansson.h>
+#include <tr_idp.h>
+#include <tr_comm.h>
+#include <tr_util.h>
+#include <tr_debug.h>
+
+static json_t *expiry_to_json_string(TR_COMM_MEMB *memb)
+{
+  struct timespec ts_zero = {0, 0};
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts_zero) == 0) {
+    s = strdup("");
+  } else {
+    s = timespec_to_str(tr_comm_memb_get_expiry(memb));
+  }
+
+  if (s) {
+    jstr = json_string(s);
+    free(s);
+  }
+
+  return jstr;
+}
+
+/**
+ * Get the provenance from the member, handling empty provenance safely
+ */
+static json_t *provenance_to_json(TR_COMM_MEMB *memb)
+{
+  json_t *prov = tr_comm_memb_get_provenance(memb);
+
+  if (prov) {
+    json_incref(prov);
+    return prov;
+  } else {
+    return json_array();
+  }
+}
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+static json_t *tr_comm_memb_to_json(TR_COMM_MEMB *memb)
+{
+  json_t *memb_json = NULL;
+  json_t *retval = NULL;
+
+  memb_json = json_object();
+  if (memb_json == NULL)
+    goto cleanup;
+
+  if (tr_comm_memb_get_origin(memb) == NULL) {
+    OBJECT_SET_OR_FAIL(memb_json, "origin", json_string("file"));
+  } else {
+    OBJECT_SET_OR_FAIL(memb_json, "origin",
+                       tr_name_to_json_string(tr_comm_memb_get_origin(memb)));
+    OBJECT_SET_OR_FAIL(memb_json, "provenance",
+                       provenance_to_json(memb));
+    OBJECT_SET_OR_FAIL(memb_json, "expires",
+                       expiry_to_json_string(memb));
+    OBJECT_SET_OR_FAIL(memb_json, "announce_interval",
+                       json_integer(tr_comm_memb_get_interval(memb)));
+    OBJECT_SET_OR_FAIL(memb_json, "times_expired",
+                       json_integer(tr_comm_memb_get_times_expired(memb)));
+  }
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = memb_json;
+  json_incref(retval);
+
+cleanup:
+  if (memb_json)
+    json_decref(memb_json);
+  return retval;
+}
+
+/**
+ * Summarize the different reasons we believe a realm belongs to a community
+ */
+static json_t *tr_comm_memb_sources_to_json(TR_COMM_MEMB *first_memb)
+{
+  json_t *jarray = NULL;
+  json_t *retval = NULL;
+  TR_COMM_ITER *iter = NULL;
+  TR_COMM_MEMB *memb = NULL;
+
+  jarray = json_array();
+  if (jarray == NULL)
+    goto cleanup;
+
+  iter = tr_comm_iter_new(NULL);
+  if (iter == NULL)
+    goto cleanup;
+
+  /* Iterate over all the memberships for this realm/comm pair that come from different origins */
+  memb = tr_comm_memb_iter_first(iter, first_memb);
+  while (memb) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_comm_memb_to_json(memb));
+    memb = tr_comm_memb_iter_next(iter);
+  }
+
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+  if (iter)
+    talloc_free(iter);
+  return retval;
+}
+
+static json_t *tr_comm_realms_to_json(TR_COMM_TABLE *ctable, TR_NAME *comm_name, TR_REALM_ROLE role)
+{
+  json_t *jarray = json_array();
+  json_t *realm_json = NULL;
+  json_t *retval = NULL;
+  TR_COMM_ITER *iter = NULL;
+  TR_REALM *realm = NULL;
+  TR_COMM_MEMB *memb = NULL;
+
+  iter = tr_comm_iter_new(NULL);
+  realm = tr_realm_iter_first(iter, ctable, comm_name);
+
+  /* Do not display the full realm json here, only the name and info relevant to the community listing */
+  while(realm) {
+    if (realm->role == role) {
+      realm_json = json_object();
+      OBJECT_SET_OR_FAIL(realm_json, "realm",
+                         tr_name_to_json_string(tr_realm_get_id(realm)));
+      memb = tr_comm_table_find_memb(ctable,
+                                     tr_realm_get_id(realm),
+                                     comm_name);
+      if (memb == NULL) {
+        /* This should not happen - there must be a matching membership if we
+         * believed the realm was in the community in the first place! */
+        goto cleanup;
+      }
+      OBJECT_SET_OR_FAIL(realm_json, "sources",
+                         tr_comm_memb_sources_to_json(memb));
+      json_array_append_new(jarray, realm_json);
+      realm_json = NULL; /* so we don't free this twice during cleanup */
+    }
+    realm = tr_realm_iter_next(iter);
+  }
+
+  /* Success - increment the reference count so return value survives */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  if (realm_json)
+    json_decref(realm_json);
+
+  if (iter)
+    tr_comm_iter_free(iter);
+
+  return retval;
+}
+
+static json_t *tr_comm_to_json(TR_COMM_TABLE *ctable, TR_COMM *comm)
+{
+  json_t *comm_json = NULL;
+  json_t *retval = NULL;
+
+  comm_json = json_object();
+  if (comm_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(comm_json, "type",
+                     json_string(tr_comm_type_to_str(tr_comm_get_type(comm))));
+  if (tr_comm_get_type(comm) == TR_COMM_APC) {
+    OBJECT_SET_OR_FAIL(comm_json, "expiration_interval",
+                       json_integer(comm->expiration_interval));
+  } else {
+    /* just get the first apc */
+    OBJECT_SET_OR_FAIL(comm_json, "apc",
+                       tr_name_to_json_string(
+                           tr_apc_get_id(
+                               tr_comm_get_apcs(comm))));
+  }
+  OBJECT_SET_OR_FAIL(comm_json, "name",
+                     tr_name_to_json_string(tr_comm_get_id(comm)));
+  if (tr_comm_get_owner_realm(comm)) {
+    OBJECT_SET_OR_FAIL(comm_json, "owner_realm",
+                     tr_name_to_json_string(tr_comm_get_owner_realm(comm)));
+  }
+  if (tr_comm_get_owner_contact(comm)) {
+    OBJECT_SET_OR_FAIL(comm_json, "owner_contact",
+                       tr_name_to_json_string(tr_comm_get_owner_contact(comm)));
+  }
+  OBJECT_SET_OR_FAIL(comm_json, "idp_realms",
+                     tr_comm_realms_to_json(ctable, tr_comm_get_id(comm), TR_ROLE_IDP));
+  OBJECT_SET_OR_FAIL(comm_json, "rp_realms",
+                     tr_comm_realms_to_json(ctable, tr_comm_get_id(comm), TR_ROLE_RP));
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = comm_json;
+  json_incref(retval);
+
+  cleanup:
+  if (comm_json)
+    json_decref(comm_json);
+  return retval;
+}
+
+json_t *tr_comm_table_to_json(TR_COMM_TABLE *ctable)
+{
+  json_t *ctable_json = NULL;
+  json_t *retval = NULL;
+  json_t *comm_json = NULL;
+  TR_COMM_ITER *iter = NULL;
+  TR_COMM *comm = NULL;
+
+  ctable_json = json_array();
+  if (ctable_json == NULL)
+    goto cleanup;
+
+  iter = tr_comm_iter_new(NULL);
+  if (iter == NULL)
+    goto cleanup;
+
+  /* Iterate over communities in the table */
+  comm = tr_comm_table_iter_first(iter, ctable);
+  while (comm) {
+    comm_json = tr_comm_to_json(ctable, comm);
+
+    if (comm_json == NULL)
+      goto cleanup;
+
+    json_array_append_new(ctable_json, comm_json);
+    comm = tr_comm_table_iter_next(iter);
+  }
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = ctable_json;
+  json_incref(retval);
+
+cleanup:
+  if (iter)
+    tr_comm_iter_free(iter);
+
+  if (ctable_json)
+    json_decref(ctable_json);
+
+  return retval;
+}
\ No newline at end of file
index c1fc6d9..8c0f577 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, JANET(UK)
+ * Copyright (c) 2012-2018, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,7 @@
 #include <tr_cfgwatch.h>
 #include <tr_comm.h>
 #include <tr_config.h>
-#include <tr_gss.h>
+#include <tr_gss_names.h>
 #include <tr_debug.h>
 #include <tr_filter.h>
 #include <trust_router/tr_constraint.h>
@@ -125,1814 +125,68 @@ void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm)
   talloc_free(tmp_ctx);
 }
 
-TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
-{
-  TR_CFG *cfg=talloc(mem_ctx, TR_CFG);
-  if (cfg!=NULL) {
-    cfg->internal=NULL;
-    cfg->rp_clients=NULL;
-    cfg->peers=NULL;
-    cfg->default_servers=NULL;
-    cfg->ctable=tr_comm_table_new(cfg);
-    if (cfg->ctable==NULL) {
-      talloc_free(cfg);
-      cfg=NULL;
-    }
-  }
-  return cfg;
-}
-
-void tr_cfg_free (TR_CFG *cfg)
-{
-  talloc_free(cfg);
-}
-
-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 (cfg_mgr->active != NULL)
-    tr_cfg_free(cfg_mgr->active);
-
-  cfg_mgr->active = cfg_mgr->new;
-  cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
-
-  tr_log_threshold(cfg_mgr->active->internal->log_threshold);
-  tr_console_threshold(cfg_mgr->active->internal->console_threshold);
-
-  return TR_CFG_SUCCESS;
-}
-
-static TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jcfg)
-{
-  json_t *jint = NULL;
-  json_t *jmtd = NULL;
-  json_t *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 *jtidreq_timeout = NULL;
-  json_t *jtidresp_numer = NULL;
-  json_t *jtidresp_denom = NULL;
-  json_t *jrouteconnect = NULL;
-
-  if ((!trc) || (!jcfg))
-    return TR_CFG_BAD_PARAMS;
-
-  if (NULL == trc->internal) {
-    if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
-      return TR_CFG_NOMEM;
-  }
-
-  if (NULL != (jint = json_object_get(jcfg, "tr_internal"))) {
-    if (NULL != (jmtd = json_object_get(jint, "max_tree_depth"))) {
-      if (json_is_number(jmtd)) {
-        trc->internal->max_tree_depth = json_integer_value(jmtd);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, max_tree_depth is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
-    }
-    if (NULL != (jtidsp = json_object_get(jint, "tids_port"))) {
-      if (json_is_number(jtidsp)) {
-        trc->internal->tids_port = json_integer_value(jtidsp);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, tids_port is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->tids_port = TR_DEFAULT_TIDS_PORT;
-    }
-    if (NULL != (jtrpsp = json_object_get(jint, "trps_port"))) {
-      if (json_is_number(jtrpsp)) {
-        trc->internal->trps_port = json_integer_value(jtrpsp);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trps_port is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* If not configured, use the default */
-      trc->internal->trps_port = TR_DEFAULT_TRPS_PORT;
-    }
-    if (NULL != (jhname = json_object_get(jint, "hostname"))) {
-      if (json_is_string(jhname)) {
-        trc->internal->hostname = talloc_strdup(trc->internal, json_string_value(jhname));
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, hostname is not a string.");
-        return TR_CFG_NOPARSE;
-      }
-    }
-    if (NULL != (jcfgpoll = json_object_get(jint, "cfg_poll_interval"))) {
-      if (json_is_number(jcfgpoll)) {
-        trc->internal->cfg_poll_interval = json_integer_value(jcfgpoll);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, cfg_poll_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      trc->internal->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
-    }
-
-    if (NULL != (jcfgsettle = json_object_get(jint, "cfg_settling_time"))) {
-      if (json_is_number(jcfgsettle)) {
-        trc->internal->cfg_settling_time = json_integer_value(jcfgsettle);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, cfg_settling_time is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      trc->internal->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
-    }
-
-    if (NULL != (jrouteconnect = json_object_get(jint, "trp_connect_interval"))) {
-      if (json_is_number(jrouteconnect)) {
-        trc->internal->trp_connect_interval = json_integer_value(jrouteconnect);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_connect_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_connect_interval=TR_DEFAULT_TRP_CONNECT_INTERVAL;
-    }
-
-    if (NULL != (jroutesweep = json_object_get(jint, "trp_sweep_interval"))) {
-      if (json_is_number(jroutesweep)) {
-        trc->internal->trp_sweep_interval = json_integer_value(jroutesweep);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_sweep_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_sweep_interval=TR_DEFAULT_TRP_SWEEP_INTERVAL;
-    }
-
-    if (NULL != (jrouteupdate = json_object_get(jint, "trp_update_interval"))) {
-      if (json_is_number(jrouteupdate)) {
-        trc->internal->trp_update_interval = json_integer_value(jrouteupdate);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, trp_update_interval is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->trp_update_interval=TR_DEFAULT_TRP_UPDATE_INTERVAL;
-    }
-
-    if (NULL != (jtidreq_timeout = json_object_get(jint, "tid_request_timeout"))) {
-      if (json_is_number(jtidreq_timeout)) {
-        trc->internal->tid_req_timeout = json_integer_value(jtidreq_timeout);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, tid_request_timeout is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->tid_req_timeout=TR_DEFAULT_TID_REQ_TIMEOUT;
-    }
-
-    if (NULL != (jtidresp_numer = json_object_get(jint, "tid_response_numerator"))) {
-      if (json_is_number(jtidresp_numer)) {
-        trc->internal->tid_resp_numer = json_integer_value(jtidresp_numer);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_numerator is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->tid_resp_numer=TR_DEFAULT_TID_RESP_NUMER;
-    }
-
-    if (NULL != (jtidresp_denom = json_object_get(jint, "tid_response_denominator"))) {
-      if (json_is_number(jtidresp_denom)) {
-        trc->internal->tid_resp_denom = json_integer_value(jtidresp_denom);
-      } else {
-        tr_debug("tr_cfg_parse_internal: Parsing error, tid_response_denominator is not a number.");
-        return TR_CFG_NOPARSE;
-      }
-    } else {
-      /* if not configured, use the default */
-      trc->internal->tid_resp_denom=TR_DEFAULT_TID_RESP_DENOM;
-    }
-
-    if (NULL != (jlog = json_object_get(jint, "logging"))) {
-      if (NULL != (jlogthres = json_object_get(jlog, "log_threshold"))) {
-        if (json_is_string(jlogthres)) {
-          trc->internal->log_threshold = str2sev(json_string_value(jlogthres));
-        } else {
-          tr_debug("tr_cfg_parse_internal: Parsing error, log_threshold is not a string.");
-          return TR_CFG_NOPARSE;
-        }
-      } else {
-        /* If not configured, use the default */
-        trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
-      }
-
-      if (NULL != (jconthres = json_object_get(jlog, "console_threshold"))) {
-        if (json_is_string(jconthres)) {
-            trc->internal->console_threshold = str2sev(json_string_value(jconthres));
-        } else {
-          tr_debug("tr_cfg_parse_internal: Parsing error, console_threshold is not a string.");
-          return TR_CFG_NOPARSE;
-        }
-      } else {
-        /* If not configured, use the default */
-        trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
-      }
-    } else {
-        /* If not configured, use the default */
-        trc->internal->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
-        trc->internal->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
-    }
-
-    tr_debug("tr_cfg_parse_internal: Internal config parsed.");
-    return TR_CFG_SUCCESS;
-  }
-  return TR_CFG_SUCCESS;
-}
-
-static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
-{
-  TR_CONSTRAINT *cons=NULL;
-  int i=0;
-
-  if ((!ctype) || (!jc) || (!rc) ||
-      (!json_is_array(jc)) ||
-      (0 >= json_array_size(jc)) ||
-      (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
-      (!json_is_string(json_array_get(jc, 0)))) {
-    tr_err("tr_cfg_parse_one_constraint: config error.");
-    *rc=TR_CFG_NOPARSE;
-    return NULL;
-  }
-
-  if (NULL==(cons=tr_constraint_new(mem_ctx))) {
-    tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
-    *rc=TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  if (NULL==(cons->type=tr_new_name(ctype))) {
-    tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
-    *rc=TR_CFG_NOMEM;
-    tr_constraint_free(cons);
-    return NULL;
-  }
-
-  for (i=0; i < json_array_size(jc); i++) {
-    cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
-    if (cons->matches[i]==NULL) {
-      tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
-      *rc=TR_CFG_NOMEM;
-      tr_constraint_free(cons);
-      return NULL;
-    }
-  }
-
-  return cons;
-}
-
-static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-  TR_FILTER *filt = NULL;
-  json_t *jfaction = NULL;
-  json_t *jfline = NULL;
-  json_t *jfspecs = NULL;
-  json_t *this_jfspec = NULL;
-  json_t *jfield = NULL;
-  json_t *jmatch = NULL;
-  json_t *jrc = NULL;
-  json_t *jdc = NULL;
-  json_t *this_jmatch = NULL;
-  TR_NAME *name = NULL;
-  size_t i = 0, j = 0, k = 0;
-
-  *rc = TR_CFG_ERROR;
-
-  if ((jfilt == NULL) || (rc == NULL)) {
-    tr_err("tr_cfg_parse_one_filter: null argument");
-    *rc = TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if (NULL == (filt = tr_filter_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_filter: Out of memory.");
-    *rc = TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  tr_filter_set_type(filt, ftype);
-
-  /* make sure we have space to represent the filter */
-  if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
-    tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
-    *rc = TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  /* For each entry in the filter... */
-  json_array_foreach(jfilt, i, jfline) {
-    if ((NULL == (jfaction = json_object_get(jfline, "action"))) ||
-        (!json_is_string(jfaction))) {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if ((NULL == (jfspecs = json_object_get(jfline, "specs"))) ||
-        (!json_is_array(jfspecs)) ||
-        (0 == json_array_size(jfspecs))) {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
-      tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if (NULL == (filt->lines[i] = tr_fline_new(filt))) {
-      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1);
-      *rc = TR_CFG_NOMEM;
-      goto cleanup;
-    }
-
-    if (!strcmp(json_string_value(jfaction), "accept")) {
-      filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
-    } else if (!strcmp(json_string_value(jfaction), "reject")) {
-      filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
-    } else {
-      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.",
-               json_string_value(jfaction));
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    if (NULL != (jrc = json_object_get(jfline, "realm_constraints"))) {
-      if (!json_is_array(jrc)) {
-        tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) {
-        tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
-               TR_MAX_CONST_MATCHES);
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      } else if (json_array_size(jrc) > 0) {
-        /* ok we actually have entries to process */
-        if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
-          tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
-          *rc = TR_CFG_NOPARSE;
-          goto cleanup;
-        }
-      }
-    }
-
-    if (NULL != (jdc = json_object_get(jfline, "domain_constraints"))) {
-      if (!json_is_array(jdc)) {
-        tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) {
-        tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
-               TR_MAX_CONST_MATCHES);
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      } else if (json_array_size(jdc) > 0) {
-        if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
-          tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
-          *rc = TR_CFG_NOPARSE;
-          goto cleanup;
-        }
-      }
-    }
-
-    /*For each filter spec within the filter line... */
-    json_array_foreach(jfspecs, j, this_jfspec) {
-      if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) ||
-          (!json_is_string(jfield))) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i,
-                 j);
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-
-      /* check that we have a match attribute */
-      if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) {
-        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i,
-                 j);
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-
-      /* check that match is a string or an array */
-      if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) {
-        tr_debug(
-            "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.",
-            i, j);
-        *rc = TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-
-      /* allocate the filter spec */
-      if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) {
-        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc = TR_CFG_NOMEM;
-        goto cleanup;
-      }
-
-      /* fill in the field */
-      if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) {
-        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-        *rc = TR_CFG_NOMEM;
-        goto cleanup;
-      }
-
-      /* fill in the matches */
-      if (json_is_string(jmatch)) {
-        if (NULL == (name = tr_new_name(json_string_value(jmatch)))) {
-          tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-          *rc = TR_CFG_NOMEM;
-          goto cleanup;
-        }
-        tr_fspec_add_match(filt->lines[i]->specs[j], name);
-      } else {
-        /* jmatch is an array (we checked earlier) */
-        json_array_foreach(jmatch, k, this_jmatch) {
-          if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) {
-            tr_debug("tr_cfg_parse_one_filter: Out of memory.");
-            *rc = TR_CFG_NOMEM;
-            goto cleanup;
-          }
-          tr_fspec_add_match(filt->lines[i]->specs[j], name);
-        }
-      }
-      if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){
-        tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.",
-                 filt->lines[i]->specs[j]->field->len,
-                 filt->lines[i]->specs[j]->field->buf,
-                 tr_filter_type_to_string(filt->type),
-                 i, j);
-        *rc = TR_CFG_ERROR;
-        goto cleanup;
-      }
-    }
-  }
-
-  /* check that the filter is valid */
-  if (!tr_filter_validate(filt)) {
-    *rc = TR_CFG_ERROR;
-  } else {
-    *rc = TR_CFG_SUCCESS;
-    talloc_steal(mem_ctx, filt);
-  }
-
- cleanup:
-  talloc_free(tmp_ctx);
-  if (*rc!=TR_CFG_SUCCESS)
-    filt=NULL;
-  return filt;
-}
-
-static TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  json_t *jfilt;
-  const char *filt_label=NULL;
-  TR_FILTER *filt=NULL;
-  TR_FILTER_SET *filt_set=NULL;
-  TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN;
-
-  *rc=TR_CFG_ERROR;
-
-  /* no filters */
-  if (jfilts==NULL) {
-    *rc=TR_CFG_SUCCESS;
-    goto cleanup;
-  }
-
-  filt_set=tr_filter_set_new(tmp_ctx);
-  if (filt_set==NULL) {
-    tr_debug("tr_cfg_parse_filters: Unable to allocate filter set.");
-    *rc = TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  json_object_foreach(jfilts, filt_label, jfilt) {
-    /* check that we got a filter */
-    if (jfilt == NULL) {
-      tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label);
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    /* check that we recognize the filter type */
-    filt_type=tr_filter_type_from_string(filt_label);
-    if (filt_type==TR_FILTER_TYPE_UNKNOWN) {
-      tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label);
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    /* finally, parse the filter */
-    tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label);
-    filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc);
-    tr_filter_set_add(filt_set, filt);
-    if (*rc != TR_CFG_SUCCESS) {
-      tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label);
-      *rc = TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-  }
-
-  *rc=TR_CFG_SUCCESS;
-
- cleanup:
-  if (*rc==TR_CFG_SUCCESS)
-    talloc_steal(mem_ctx, filt_set);
-  else if (filt_set!=NULL) {
-    talloc_free(filt_set);
-    filt_set=NULL;
-  }
-
-  talloc_free(tmp_ctx);
-  return filt_set;
-}
-
-static TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
-{
-  TR_AAA_SERVER *aaa = NULL;
-  TR_NAME *name=NULL;
-
-  if ((!jaddr) || (!json_is_string(jaddr))) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
-    *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  name=tr_new_name(json_string_value(jaddr));
-  if (name==NULL) {
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  aaa=tr_aaa_server_new(mem_ctx, name);
-  if (aaa==NULL) {
-    tr_free_name(name);
-    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  return aaa;
-}
-
-static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc) 
-{
-  TALLOC_CTX *tmp_ctx=NULL;
-  TR_AAA_SERVER *aaa = NULL;
-  TR_AAA_SERVER *temp_aaa = NULL;
-  int i = 0;
-
-  for (i = 0; i < json_array_size(jaaas); i++) {
-    /* rc gets set in here */
-    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
-      talloc_free(tmp_ctx);
-      return NULL;
-    }
-    /* TBD -- IPv6 addresses */
-    //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
-    temp_aaa->next = aaa;
-    aaa = temp_aaa;
-  }
-  tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
-
-  for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
-    talloc_steal(mem_ctx, temp_aaa);
-  talloc_free(tmp_ctx);
-  return aaa;
-}
-
-static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc)
-{
-  TR_APC *apc=NULL;
-  TR_NAME *name=NULL;
-  
-  *rc = TR_CFG_SUCCESS;         /* presume success */
-
-  if ((!japc) || (!rc) || (!json_is_string(japc))) {
-    tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
-    if (rc) 
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  apc=tr_apc_new(mem_ctx);
-  if (apc==NULL) {
-    tr_debug("tr_cfg_parse_one_apc: Out of memory.");
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-
-  name=tr_new_name(json_string_value(japc));
-  if (name==NULL) {
-    tr_debug("tr_cfg_parse_one_apc: No memory for APC name.");
-    tr_apc_free(apc);
-    *rc = TR_CFG_NOMEM;
-    return NULL;
-  }
-  tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
-
-  return apc;
-}
-
-static TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_APC *apcs=NULL;
-  TR_APC *new_apc=NULL;
-  int ii=0;
-  TR_CFG_RC call_rc=TR_CFG_ERROR;
-  
-  *rc = TR_CFG_SUCCESS;         /* presume success */
-
-  if ((!japcs) || (!rc) || (!json_is_array(japcs))) {
-    tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
-    if (rc) 
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  for (ii=0; ii<json_array_size(japcs); ii++) {
-    new_apc=tr_cfg_parse_one_apc(tmp_ctx, json_array_get(japcs, ii), &call_rc);
-    if ((call_rc!=TR_CFG_SUCCESS) || (new_apc==NULL)) {
-      tr_debug("tr_cfg_parse_apcs: Error parsing APC %d.", ii+1);
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-    tr_apc_add(apcs, new_apc);
-  }
-
-  talloc_steal(mem_ctx, apcs);
-  *rc=TR_CFG_SUCCESS;
-
- cleanup:
-  talloc_free(tmp_ctx);
-  return apcs;
-}
-
-static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
-{
-  TR_NAME *name=NULL;
-  *rc=TR_CFG_ERROR;
-
-  if ((jname==NULL) || (!json_is_string(jname))) {
-    tr_err("tr_cfg_parse_name: name missing or not a string");
-    *rc=TR_CFG_BAD_PARAMS;
-    name=NULL;
-  } else {
-    name=tr_new_name(json_string_value(jname));
-    if (name==NULL) {
-      tr_err("tr_cfg_parse_name: could not allocate name");
-      *rc=TR_CFG_NOMEM;
-    } else {
-      *rc=TR_CFG_SUCCESS;
-    }
-  }
-  return name;  
-}
-
-static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
-{
-  const char *shared=NULL;
-  *rc=TR_CFG_SUCCESS;
-  
-  if ((jsc==NULL) ||
-      (!json_is_string(jsc)) ||
-      (NULL==(shared=json_string_value(jsc)))) {
-    *rc=TR_CFG_BAD_PARAMS;
-    return -1;
-  }
-
-  if (0==strcmp(shared, "no"))
-    return 0;
-  else if (0==strcmp(shared, "yes"))
-    return 1;
-
-  /* any other value is an error */
-  tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
-           shared);
-  *rc=TR_CFG_NOPARSE;
-  return -1;
-}
-
-static TR_REALM_ORIGIN tr_cfg_realm_origin(json_t *jrealm)
-{
-  json_t *jremote=json_object_get(jrealm, "remote");
-  const char *s=NULL;
-
-  if (jremote==NULL)
-    return TR_REALM_LOCAL;
-  if (!json_is_string(jremote)) {
-    tr_warning("tr_cfg_realm_origin: \"remote\" is not a string, assuming this is a local realm.");
-    return TR_REALM_LOCAL;
-  }
-  s=json_string_value(jremote);
-  if (strcasecmp(s, "yes")==0)
-    return TR_REALM_REMOTE_INCOMPLETE;
-  else if (strcasecmp(s, "no")!=0)
-    tr_warning("tr_cfg_realm_origin: \"remote\" is neither 'yes' nor 'no', assuming this is a local realm.");
-
-  return TR_REALM_LOCAL;
-}
-
-/* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
-static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_APC *apcs=NULL;
-  TR_AAA_SERVER *aaa=NULL;
-  TR_CFG_RC rc=TR_CFG_ERROR;
-  
-  if (jidp==NULL)
-    goto cleanup;
-
-  idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
-  if (rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc);
-  if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
-    tr_err("tr_cfg_parse_idp: unable to parse APC");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  
-  aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
-  if (rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  
-  tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
-           apcs->id->len,
-           apcs->id->buf);
-  
-  /* done, fill in the idp structures */
-  idp->apcs=apcs;
-  talloc_steal(idp, apcs);
-  idp->aaa_servers=aaa;
-  rc=TR_CFG_SUCCESS;
-  
-cleanup:
-  if (rc!=TR_CFG_SUCCESS) {
-    if (apcs!=NULL)
-      tr_apc_free(apcs);
-    if (aaa!=NULL)
-      tr_aaa_server_free(aaa);
-  }
-  
-  talloc_free(tmp_ctx);
-  return rc;
-}
-
-/* parses idp realm */
-static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_IDP_REALM *realm=NULL;
-  TR_CFG_RC call_rc=TR_CFG_ERROR;
-
-  *rc=TR_CFG_ERROR; /* default to error if not set */
-
-  if ((!jrealm) || (!rc)) {
-    tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
-    if (rc)
-      *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  realm->origin=tr_cfg_realm_origin(jrealm);
-  if (realm->origin!=TR_REALM_LOCAL) {
-    tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info.");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  /* must have a name */
-  realm->realm_id=tr_cfg_parse_name(realm,
-                                    json_object_get(jrealm, "realm"),
-                                   &call_rc);
-  if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
-           realm->realm_id->len,
-           realm->realm_id->buf);
-        
-  call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
-  if (call_rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  
-  *rc=TR_CFG_SUCCESS;
-
-cleanup:
-  if (*rc==TR_CFG_SUCCESS)
-    talloc_steal(mem_ctx, realm);
-  else {
-    talloc_free(realm);
-    realm=NULL;
-  }
-  
-  talloc_free(tmp_ctx);
-  return realm;
-}
-
-  /* Determine whether the realm is an IDP realm */
-static int tr_cfg_is_idp_realm(json_t *jrealm)
-{
-  /* If a realm spec contains an identity_provider, it's an IDP realm. */
-  if (NULL != json_object_get(jrealm, "identity_provider"))
-    return 1;
-  else
-    return 0;
-}
-
-static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM);
-  
-  *rc=TR_CFG_ERROR; /* default to error if not set */
-
-  if ((!jrealm) || (!rc)) {
-    tr_err("tr_cfg_parse_one_remote_realm: Bad parameters.");
-    if (rc)
-      *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  /* must have a name */
-  realm->realm_id=tr_cfg_parse_name(realm,
-                                    json_object_get(jrealm, "realm"),
-                                    rc);
-  if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
-    tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"",
-           realm->realm_id->len,
-           realm->realm_id->buf);
-
-  realm->origin=tr_cfg_realm_origin(jrealm);
-  *rc=TR_CFG_SUCCESS;
-
-cleanup:
-  if (*rc==TR_CFG_SUCCESS)
-    talloc_steal(mem_ctx, realm);
-  else {
-    talloc_free(realm);
-    realm=NULL;
-  }
-  
-  talloc_free(tmp_ctx);
-  return realm;
-}
-
-static int tr_cfg_is_remote_realm(json_t *jrealm)
-{
-  return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL);
-}
-
-/* Parse any idp realms in the j_realms object. Ignores other realm types. */
-static TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_IDP_REALM *realms=NULL;
-  TR_IDP_REALM *new_realm=NULL;
-  json_t *this_jrealm=NULL;
-  int ii=0;
-
-  *rc=TR_CFG_ERROR;
-  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
-    tr_err("tr_cfg_parse_idp_realms: realms not an array");
-    *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  for (ii=0; ii<json_array_size(jrealms); ii++) {
-    this_jrealm=json_array_get(jrealms, ii);
-    if (tr_cfg_is_idp_realm(this_jrealm)) {
-      new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
-      if ((*rc)!=TR_CFG_SUCCESS) {
-        tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-      tr_idp_realm_add(realms, new_realm);
-    } else if (tr_cfg_is_remote_realm(this_jrealm)) {
-      new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
-      if ((*rc)!=TR_CFG_SUCCESS) {
-        tr_err("tr_cfg_parse_idp_realms: error decoding remote realm entry %d", ii+1);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-      tr_idp_realm_add(realms, new_realm);
-    }
-  }
-  
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, realms);
-
-cleanup:
-  talloc_free(tmp_ctx);
-  return realms;
-}
-
-static TR_GSS_NAMES *tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_GSS_NAMES *gn=NULL;
-  json_t *jname=NULL;
-  int ii=0;
-  TR_NAME *name=NULL;
-
-  if ((rc==NULL) || (jgss_names==NULL)) {
-    tr_err("tr_cfg_parse_gss_names: Bad parameters.");
-    *rc=TR_CFG_BAD_PARAMS;
-
-  }
-
-  if (!json_is_array(jgss_names)) {
-    tr_err("tr_cfg_parse_gss_names: gss_names not an array.");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  gn=tr_gss_names_new(tmp_ctx);
-  for (ii=0; ii<json_array_size(jgss_names); ii++) {
-    jname=json_array_get(jgss_names, ii);
-    if (!json_is_string(jname)) {
-      tr_err("tr_cfg_parse_gss_names: Encountered non-string gss name.");
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-
-    name=tr_new_name(json_string_value(jname));
-    if (name==NULL) {
-      tr_err("tr_cfg_parse_gss_names: Out of memory allocating gss name.");
-      *rc=TR_CFG_NOMEM;
-      goto cleanup;
-    }
-
-    if (tr_gss_names_add(gn, name)!=0) {
-      tr_free_name(name);
-      tr_err("tr_cfg_parse_gss_names: Unable to add gss name to RP client.");
-      *rc=TR_CFG_ERROR;
-      goto cleanup;
-    }
-  }
-
-  talloc_steal(mem_ctx, gn);
-  *rc=TR_CFG_SUCCESS;
-
- cleanup:
-  talloc_free(tmp_ctx);
-  if ((*rc!=TR_CFG_SUCCESS) && (gn!=NULL))
-    gn=NULL;
-  return gn;
-}
-
-/* default filter accepts realm and *.realm */
-static TR_FILTER_SET *tr_cfg_default_filters(TALLOC_CTX *mem_ctx, TR_NAME *realm, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_FILTER *filt=NULL;
-  TR_FILTER_SET *filt_set=NULL;
-  TR_CONSTRAINT *cons=NULL;
-  TR_NAME *name=NULL;
-  TR_NAME *n_prefix=tr_new_name("*.");
-  TR_NAME *n_rp_realm_1=tr_new_name("rp_realm");
-  TR_NAME *n_rp_realm_2=tr_new_name("rp_realm");
-  TR_NAME *n_domain=tr_new_name("domain");
-  TR_NAME *n_realm=tr_new_name("realm");
-  
-
-  if ((realm==NULL) || (rc==NULL)) {
-    tr_debug("tr_cfg_default_filters: invalid arguments.");
-    if (rc!=NULL)
-      *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if ((n_prefix==NULL) ||
-      (n_rp_realm_1==NULL) ||
-      (n_rp_realm_2==NULL) ||
-      (n_domain==NULL) ||
-      (n_realm==NULL)) {
-    tr_debug("tr_cfg_default_filters: unable to allocate names.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  filt=tr_filter_new(tmp_ctx);
-  if (filt==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate filter.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  tr_filter_set_type(filt, TR_FILTER_TYPE_TID_INBOUND);
-  filt->lines[0]=tr_fline_new(filt);
-  if (filt->lines[0]==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate filter line.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  filt->lines[0]->action=TR_FILTER_ACTION_ACCEPT;
-  filt->lines[0]->specs[0]=tr_fspec_new(filt->lines[0]);
-  filt->lines[0]->specs[0]->field=n_rp_realm_1;
-  n_rp_realm_1=NULL; /* we don't own this name any more */
-
-  name=tr_dup_name(realm);
-  if (name==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate realm name.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  tr_fspec_add_match(filt->lines[0]->specs[0], name);
-  name=NULL; /* we no longer own the name */
-
-  /* now do the wildcard name */
-  filt->lines[0]->specs[1]=tr_fspec_new(filt->lines[0]);
-  filt->lines[0]->specs[1]->field=n_rp_realm_2;
-  n_rp_realm_2=NULL; /* we don't own this name any more */
-
-  if (NULL==(name=tr_name_cat(n_prefix, realm))) {
-    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  tr_fspec_add_match(filt->lines[0]->specs[1], name);
-  name=NULL; /* we no longer own the name */
-
-  /* domain constraint */
-  if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
-    tr_debug("tr_cfg_default_filters: could not allocate domain constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  cons->type=n_domain;
-  n_domain=NULL; /* belongs to the constraint now */
-  name=tr_dup_name(realm);
-  if (name==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate realm name for domain constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  cons->matches[0]=name;
-  name=tr_name_cat(n_prefix, realm);
-  if (name==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for domain constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  cons->matches[1]=name;
-  name=NULL;
-  filt->lines[0]->domain_cons=cons;
-
-
-  /* realm constraint */
-  if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
-    tr_debug("tr_cfg_default_filters: could not allocate realm constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  cons->type=n_realm;
-  n_realm=NULL; /* belongs to the constraint now */
-  name=tr_dup_name(realm);
-  if (name==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate realm name for realm constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  cons->matches[0]=name;
-  name=tr_name_cat(n_prefix, realm);
-  if (name==NULL) {
-    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for realm constraint.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  cons->matches[1]=name;
-  name=NULL;
-  filt->lines[0]->realm_cons=cons;
-
-  /* put the filter in a set */
-  filt_set=tr_filter_set_new(tmp_ctx);
-  if ((filt_set==NULL)||(0!=tr_filter_set_add(filt_set, filt))) {
-    tr_debug("tr_cfg_default_filters: could not allocate filter set.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-  talloc_steal(mem_ctx, filt_set);
-
-cleanup:
-  talloc_free(tmp_ctx);
-
-  if (*rc!=TR_CFG_SUCCESS)
-    filt=NULL;
-
-  if (n_prefix!=NULL)
-    tr_free_name(n_prefix);
-  if (n_rp_realm_1!=NULL)
-    tr_free_name(n_rp_realm_1);
-  if (n_rp_realm_2!=NULL)
-    tr_free_name(n_rp_realm_2);
-  if (n_realm!=NULL)
-    tr_free_name(n_realm);
-  if (n_domain!=NULL)
-    tr_free_name(n_domain);
-  if (name!=NULL)
-    tr_free_name(name);
-
-  return filt_set;
-}
-
-/* parses rp client */
-static TR_RP_CLIENT *tr_cfg_parse_one_rp_client(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_RP_CLIENT *client=NULL;
-  TR_CFG_RC call_rc=TR_CFG_ERROR;
-  TR_FILTER_SET *new_filts=NULL;
-  TR_NAME *realm=NULL;
-  json_t *jfilt=NULL;
-  json_t *jrealm_id=NULL;
-
-  *rc=TR_CFG_ERROR; /* default to error if not set */
-
-  if ((!jrealm) || (!rc)) {
-    tr_err("tr_cfg_parse_one_rp_client: Bad parameters.");
-    if (rc)
-      *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  if ((NULL==(jrealm_id=json_object_get(jrealm, "realm"))) || (!json_is_string(jrealm_id))) {
-    tr_debug("tr_cfg_parse_one_rp_client: no realm ID found.");
-    *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  } 
-
-  tr_debug("tr_cfg_parse_one_rp_client: realm_id=\"%s\"", json_string_value(jrealm_id));
-  realm=tr_new_name(json_string_value(jrealm_id));
-  if (realm==NULL) {
-    tr_err("tr_cfg_parse_one_rp_client: could not allocate realm ID.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  if (NULL==(client=tr_rp_client_new(tmp_ctx))) {
-    tr_err("tr_cfg_parse_one_rp_client: could not allocate rp client.");
-    *rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  client->gss_names=tr_cfg_parse_gss_names(client, json_object_get(jrealm, "gss_names"), &call_rc);
-
-  if (call_rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_one_rp_client: could not parse gss_names.");
-    *rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  /* parse filters */
-  jfilt=json_object_get(jrealm, "filters");
-  if (jfilt!=NULL) {
-    new_filts=tr_cfg_parse_filters(tmp_ctx, jfilt, &call_rc);
-    if (call_rc!=TR_CFG_SUCCESS) {
-      tr_err("tr_cfg_parse_one_rp_client: could not parse filters.");
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-  } else {
-    tr_debug("tr_cfg_parse_one_rp_client: no filters specified, using default filters.");
-    new_filts= tr_cfg_default_filters(tmp_ctx, realm, &call_rc);
-    if (call_rc!=TR_CFG_SUCCESS) {
-      tr_err("tr_cfg_parse_one_rp_client: could not set default filters.");
-      *rc=TR_CFG_NOPARSE;
-      goto cleanup;
-    }
-  }
-
-  tr_rp_client_set_filters(client, new_filts);
-  *rc=TR_CFG_SUCCESS;
-
-  cleanup:
-  if (realm!=NULL)
-    tr_free_name(realm);
-
-  if (*rc==TR_CFG_SUCCESS)
-    talloc_steal(mem_ctx, client);
-  else {
-    talloc_free(client);
-    client=NULL;
-  }
-
-  talloc_free(tmp_ctx);
-  return client;
-}
-
-  /* Determine whether the realm is an RP realm */
-static int tr_cfg_is_rp_realm(json_t *jrealm)
-{
-  /* Check that we have a gss name. */
-  if (NULL != json_object_get(jrealm, "gss_names"))
-    return 1;
-  else
-    return 0;
-}
-
-/* Parse any rp clients in the j_realms object. Ignores other realms. */
-static TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_RP_CLIENT *clients=NULL;
-  TR_RP_CLIENT *new_client=NULL;
-  json_t *this_jrealm=NULL;
-  int ii=0;
-
-  *rc=TR_CFG_ERROR;
-  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
-    tr_err("tr_cfg_parse_rp_clients: realms not an array");
-    *rc=TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  for (ii=0; ii<json_array_size(jrealms); ii++) {
-    this_jrealm=json_array_get(jrealms, ii);
-    if (tr_cfg_is_rp_realm(this_jrealm)) {
-      new_client=tr_cfg_parse_one_rp_client(tmp_ctx, this_jrealm, rc);
-      if ((*rc)!=TR_CFG_SUCCESS) {
-        tr_err("tr_cfg_parse_rp_clients: error decoding realm entry %d", ii+1);
-        *rc=TR_CFG_NOPARSE;
-        goto cleanup;
-      }
-      tr_rp_client_add(clients, new_client);
-    }
-  }
-  
-  *rc=TR_CFG_SUCCESS;
-  talloc_steal(mem_ctx, clients);
-
-cleanup:
-  talloc_free(tmp_ctx);
-  return clients;
-}
-
-/* takes a talloc context, but currently does not use it */
-static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
+static int tr_cfg_destructor(void *object)
 {
-  TR_NAME *name=NULL;
-
-  if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
-    tr_debug("tr_cfg_parse_org_name: Bad parameters.");
-    if (rc!=NULL)
-      *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
-    return NULL;
-  }
-
-  name=tr_new_name(json_string_value(j_org));
-  if (name==NULL)
-    *rc=TR_CFG_NOMEM;
-  else
-    *rc=TR_CFG_SUCCESS;
-  return name;
-}
-
-static TR_CFG_RC tr_cfg_parse_one_local_org(TR_CFG *trc, json_t *jlorg)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
-  TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
-  TR_NAME *org_name=NULL;
-  json_t *j_org=NULL;
-  json_t *j_realms=NULL;
-  TR_IDP_REALM *new_idp_realms=NULL;
-  TR_RP_CLIENT *new_rp_clients=NULL;
-
-  tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
-
-  /* get organization_name (optional) */
-  if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
-    tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
-  } else {
-    org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
-    if (rc==TR_CFG_SUCCESS) {
-      tr_debug("tr_cfg_parse_one_local_org: organization_name=\"%.*s\"",
-               org_name->len,
-               org_name->buf);
-      /* we don't actually do anything with this, but we could */
-      tr_free_name(org_name);
-      org_name=NULL; 
-    }
-  }
-
-  /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
-   * it's harmless. Report a warning because that might be unintentional. */
-  if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
-    tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
-  } else {
-    /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
-    new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
-    if (rc!=TR_CFG_SUCCESS)
-      goto cleanup;
-
-    new_rp_clients=tr_cfg_parse_rp_clients(tmp_ctx, j_realms, &rc);
-    if (rc!=TR_CFG_SUCCESS)
-      goto cleanup;
-  }
-  retval=TR_CFG_SUCCESS;
-  
-cleanup:
-  /* if we succeeded, link things to the configuration and move out of tmp context */
-  if (retval==TR_CFG_SUCCESS) {
-    if (new_idp_realms!=NULL) {
-      tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/
-      talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */
-    }
-
-    if (new_rp_clients!=NULL) {
-      tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */
-      talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */
-    }
-  }
-
-  talloc_free(tmp_ctx);
-  return rc;
+  TR_CFG *cfg = talloc_get_type_abort(object, TR_CFG);
+  if (cfg->files)
+    g_array_unref(cfg->files);
+  return 0;
 }
-
-/* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */
-static TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg)
+TR_CFG *tr_cfg_new(TALLOC_CTX *mem_ctx)
 {
-  json_t *jlocorgs=NULL;
-  size_t ii=0;
-
-  jlocorgs=json_object_get(jcfg, "local_organizations");
-  if (jlocorgs==NULL)
-    return TR_CFG_SUCCESS;
-
-  if (!json_is_array(jlocorgs)) {
-    tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array.");
-    return TR_CFG_NOPARSE;
-  }
-
-  for (ii=0; ii<json_array_size(jlocorgs); ii++) {
-    if (tr_cfg_parse_one_local_org(trc, json_array_get(jlocorgs, ii))!=TR_CFG_SUCCESS) {
-      tr_err("tr_cfg_parse_local_orgs: error parsing local_organization %d.", ii+1);
-      return TR_CFG_NOPARSE;
+  TR_CFG *cfg=talloc(mem_ctx, TR_CFG);
+  if (cfg!=NULL) {
+    cfg->internal=NULL;
+    cfg->rp_clients=NULL;
+    cfg->peers=NULL;
+    cfg->default_servers=NULL;
+    cfg->ctable=tr_comm_table_new(cfg);
+    if (cfg->ctable==NULL) {
+      talloc_free(cfg);
+      return NULL;
     }
-  }
-
-  return TR_CFG_SUCCESS;
-}
-
-static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  json_t *jhost=NULL;
-  json_t *jport=NULL;
-  json_t *jgss=NULL;
-  json_t *jfilt=NULL;
-  TRP_PEER *new_peer=NULL;
-  TR_GSS_NAMES *names=NULL;
-  TR_FILTER_SET *filt_set=NULL;
-  TR_CFG_RC rc=TR_CFG_ERROR;
-
-  jhost=json_object_get(jporg, "hostname");
-  jport=json_object_get(jporg, "port");
-  jgss=json_object_get(jporg, "gss_names");
-  jfilt=json_object_get(jporg, "filters");
-
-  if ((jhost==NULL) || (!json_is_string(jhost))) {
-    tr_err("tr_cfg_parse_one_peer_org: hostname not specified or not a string.");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  if ((jport!=NULL) && (!json_is_number(jport))) {
-    /* note that not specifying the port is allowed, but if set it must be a number */
-    tr_err("tr_cfg_parse_one_peer_org: port is not a number.");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  if ((jgss==NULL) || (!json_is_array(jgss))) {
-    tr_err("tr_cfg_parse_one_peer_org: gss_names not specified or not an array.");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  if ((jfilt!=NULL) && (!json_is_object(jfilt))) {
-    tr_err("tr_cfg_parse_one_peer_org: filters is not an object.");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-
-  new_peer=trp_peer_new(tmp_ctx);
-  if (new_peer==NULL) {
-    tr_err("tr_cfg_parse_one_peer_org: could not allocate new peer.");
-    rc=TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-  trp_peer_set_server(new_peer, json_string_value(jhost)); /* string is strdup'ed in _set_server() */
-  if (jport==NULL)
-    trp_peer_set_port(new_peer, TRP_PORT);
-  else
-    trp_peer_set_port(new_peer, json_integer_value(jport));
-
-  names=tr_cfg_parse_gss_names(tmp_ctx, jgss, &rc);
-  if (rc!=TR_CFG_SUCCESS) {
-    tr_err("tr_cfg_parse_one_peer_org: unable to parse gss names.");
-    rc=TR_CFG_NOPARSE;
-    goto cleanup;
-  }
-  trp_peer_set_gss_names(new_peer, names);
-
-  if (jfilt) {
-    filt_set=tr_cfg_parse_filters(tmp_ctx, jfilt, &rc);
-    if (rc!=TR_CFG_SUCCESS) {
-      tr_err("tr_cfg_parse_one_peer_org: unable to parse filters.");
-      rc=TR_CFG_NOPARSE;
-      goto cleanup;
+    cfg->files = g_array_new(FALSE, FALSE, sizeof(TR_CFG_FILE));
+    if (cfg->files == NULL) {
+      talloc_free(cfg);
+      return NULL;
     }
-    trp_peer_set_filters(new_peer, filt_set);
+    talloc_set_destructor((void *)cfg, tr_cfg_destructor);
   }
-
-  /* success! */
-  trp_ptable_add(trc->peers, new_peer);
-  rc=TR_CFG_SUCCESS;
-
- cleanup:
-  talloc_free(tmp_ctx);
-  return rc;
+  return cfg;
 }
 
-/* Parse peer organizations, if present. Returns success if there are none. */
-static TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg)
-{
-  json_t *jpeerorgs=NULL;
-  int ii=0;
-
-  jpeerorgs=json_object_get(jcfg, "peer_organizations");
-  if (jpeerorgs==NULL)
-    return TR_CFG_SUCCESS;
-
-  if (!json_is_array(jpeerorgs)) {
-    tr_err("tr_cfg_parse_peer_orgs: peer_organizations is not an array.");
-    return TR_CFG_NOPARSE;
-  }
-
-  for (ii=0; ii<json_array_size(jpeerorgs); ii++) {
-    if (tr_cfg_parse_one_peer_org(trc, json_array_get(jpeerorgs, ii))!=TR_CFG_SUCCESS) {
-      tr_err("tr_cfg_parse_peer_orgs: error parsing peer_organization %d.", ii+1);
-      return TR_CFG_NOPARSE;
-    }
-  }
-
-  return TR_CFG_SUCCESS;
-}
-             
-static TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg) 
+void tr_cfg_free (TR_CFG *cfg)
 {
-  json_t *jdss = NULL;
-  TR_CFG_RC rc = TR_CFG_SUCCESS;
-  TR_AAA_SERVER *ds = NULL;
-  int i = 0;
-
-  if ((!trc) || (!jcfg))
-    return TR_CFG_BAD_PARAMS;
-
-  /* If there are default servers, store them */
-  if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) &&
-      (json_is_array(jdss)) &&
-      (0 < json_array_size(jdss))) {
-
-    for (i = 0; i < json_array_size(jdss); i++) {
-      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc,
-                                                    json_array_get(jdss, i), 
-                                                   &rc))) {
-       return rc;
-      }
-      tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf);
-      ds->next = trc->default_servers;
-      trc->default_servers = ds;
-    }
-  } 
-
-  tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
-  return rc;
+  talloc_free(cfg);
 }
 
-static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc)
+TR_CFG_MGR *tr_cfg_mgr_new(TALLOC_CTX *mem_ctx)
 {
-  TR_IDP_REALM *found_idp=NULL;
-  json_t *jidp_name=NULL;
-  TR_NAME *idp_name=NULL;
-  size_t ii = 0;
-
-  if ((!trc) ||
-      (!jidps) ||
-      (!json_is_array(jidps))) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return;
-  }
-
-  json_array_foreach(jidps, ii, jidp_name) {
-    idp_name=tr_new_name(json_string_value(jidp_name));
-    if (idp_name==NULL) {
-      *rc = TR_CFG_NOMEM;
-      return;
-    }
-    found_idp=tr_cfg_find_idp(trc, idp_name, rc);
-    tr_free_name(idp_name);
-
-    if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) {
-      tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", json_string_value(jidp_name));
-      *rc=TR_CFG_ERROR;
-      return;
-    }
-    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */
-  }
-
-  *rc=TR_CFG_SUCCESS;
-  return;
+  return talloc_zero(mem_ctx, TR_CFG_MGR);
 }
 
-static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc)
-{
-  TR_RP_REALM *found_rp=NULL;
-  TR_RP_REALM *new_rp=NULL;
-  TR_NAME *rp_name=NULL;
-  const char *s=NULL;
-  int ii=0;
-
-  if ((!trc) ||
-      (!jrps) ||
-      (!json_is_array(jrps))) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return;
-  }
-
-  for (ii=0; ii<json_array_size(jrps); ii++) {
-    /* get the RP name as a string */
-    s=json_string_value(json_array_get(jrps, ii));
-    if (s==NULL) {
-      tr_notice("tr_cfg_parse_comm_rps: null RP found in community %.*s, ignoring.",
-                tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-      continue;
-    }
-
-    /* convert string to TR_NAME */
-    rp_name=tr_new_name(s);
-    if (rp_name==NULL) {
-      tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.",
-             s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-    }
-
-    /* see if we already have this RP in this community */
-    found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name);
-    if (found_rp!=NULL) {
-      tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.",
-                s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-      tr_free_name(rp_name);
-      continue;
-    }
-
-    /* Add the RP to the community, first see if we have the RP in any community */
-    found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name);
-    if (found_rp!=NULL) {
-      tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s);
-      new_rp=found_rp; /* use it rather than creating a new realm record */
-    } else {
-      new_rp=tr_rp_realm_new(NULL);
-      if (new_rp==NULL) {
-        tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.",
-               s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-      }
-      tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf);
-      tr_rp_realm_set_id(new_rp, rp_name);
-      rp_name=NULL; /* rp_name no longer belongs to us */
-      tr_rp_realm_add(trc->ctable->rp_realms, new_rp);
-      talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */
-    }
-    tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL);
-  }
+void tr_cfg_mgr_free (TR_CFG_MGR *cfg_mgr) {
+  talloc_free(cfg_mgr);
 }
 
-static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc)
+TR_CFG_RC tr_apply_new_config (TR_CFG_MGR *cfg_mgr)
 {
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_COMM *comm = NULL;
-  json_t *jid = NULL;
-  json_t *jtype = NULL;
-  json_t *japcs = NULL;
-  json_t *jidps = NULL;
-  json_t *jrps = NULL;
-
-  if ((!trc) || (!jcomm) || (!rc)) {
-    tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    goto cleanup;
-  }
-
-  comm=tr_comm_new(tmp_ctx);
-  if (comm==NULL) {
-    tr_crit("tr_cfg_parse_one_comm: Out of memory.");
-    *rc = TR_CFG_NOMEM;
-    goto cleanup;
-  }
-
-
-  if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
-      (!json_is_string(jid)) ||
-      (NULL == (jtype = json_object_get(jcomm, "type"))) ||
-      (!json_is_string(jtype)) ||
-      (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
-      (!json_is_array(japcs)) ||
-      (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
-      (!json_is_array(jidps)) ||
-      (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
-      (!json_is_array(jrps))) {
-    tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
-    *rc = TR_CFG_NOPARSE;
-    comm=NULL;
-    goto cleanup;
-  }
-
-  tr_comm_set_id(comm, tr_new_name(json_string_value(jid)));
-  if (NULL == tr_comm_get_id(comm)) {
-    tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
-    *rc = TR_CFG_NOMEM;
-    comm=NULL;
-    goto cleanup;
-  }
-
-  if (0 == strcmp(json_string_value(jtype), "apc")) {
-    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))) {
-      tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.",
-               tr_comm_get_id(comm)->buf);
-      comm=NULL;
-      goto cleanup;
-    }
-  } else {
-    tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s",
-             tr_comm_get_id(comm)->buf, json_string_value(jtype));
-    *rc = TR_CFG_NOPARSE;
-    comm=NULL;
-    goto cleanup;
-  }
+  /* 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;
 
-  tr_cfg_parse_comm_idps(trc, jidps, comm, rc);
-  if (TR_CFG_SUCCESS != *rc) {
-    tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.",
-             tr_comm_get_id(comm)->buf);
-    comm=NULL;
-    goto cleanup;
-  }
+  if (cfg_mgr->active != NULL)
+    tr_cfg_free(cfg_mgr->active);
 
-  tr_cfg_parse_comm_rps(trc, jrps, comm, rc);
-  if (TR_CFG_SUCCESS != *rc) {
-    tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .",
-             tr_comm_get_id(comm)->buf);
-    comm=NULL;
-    goto cleanup;
-  }
+  cfg_mgr->active = cfg_mgr->new;
+  cfg_mgr->new=NULL; /* only keep a single handle on the new configuration */
 
-  if (TR_COMM_APC == comm->type) {
-    json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
-    comm->expiration_interval = 43200; /*30 days*/
-    if (jexpire) {
-      if (!json_is_integer(jexpire)) {
-        tr_err("tr_parse_one_comm: expiration_interval is not an integer for comm %.*s",
-                 tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-        comm=NULL;
-        goto cleanup;
-      }
-      comm->expiration_interval = json_integer_value(jexpire);
-      if (comm->expiration_interval <= 10) {
-        comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
-        tr_notice(
-            "tr_parse_one_comm: expiration interval for %.*s less than minimum of 11 minutes; using 11 minutes instead.",
-            tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-      }
-      if (comm->expiration_interval > 129600) {
-        /* > 90 days*/
-        comm->expiration_interval = 129600;
-        tr_notice(
-            "tr_parse_one_comm: expiration interval for %.*s exceeds maximum of 90 days; using 90 days instead.",
-            tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
-      }
-    }
-  }
+  tr_log_threshold(cfg_mgr->active->internal->log_threshold);
+  tr_console_threshold(cfg_mgr->active->internal->console_threshold);
 
-cleanup:
-  if (comm!=NULL)
-    talloc_steal(mem_ctx, comm);
-  talloc_free(tmp_ctx);
-  return comm;
+  return TR_CFG_SUCCESS;
 }
 
-static TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg) 
-{
-  json_t *jcomms = NULL;
-  TR_CFG_RC rc = TR_CFG_SUCCESS;
-  TR_COMM *comm = NULL;
-  int i = 0;
-
-  if ((!trc) || (!jcfg)) {
-    tr_debug("tr_cfg_parse_comms: Bad Parameters.");
-    return TR_CFG_BAD_PARAMS;
-  }
-
-  if (NULL != (jcomms = json_object_get(jcfg, "communities"))) {
-    if (!json_is_array(jcomms)) {
-      return TR_CFG_NOPARSE;
-    }
-
-    for (i = 0; i < json_array_size(jcomms); i++) {
-      if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */
-                                                trc, 
-                                                json_array_get(jcomms, i), 
-                                               &rc))) {
-        return rc;
-      }
-      tr_debug("tr_cfg_parse_comms: Community configured: %s.",
-               tr_comm_get_id(comm)->buf);
-
-      tr_comm_table_add_comm(trc->ctable, comm);
-    }
-  }
-  tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
-  return rc;
-}
 
 TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
 {
@@ -1983,7 +237,6 @@ static void tr_cfg_log_json_error(const char *label, json_error_t *rc)
 static json_t *tr_cfg_parse_one_config_file(const char *file_with_path)
 {
   json_t *jcfg=NULL;
-  json_t *jser=NULL;
   json_error_t rc;
 
   if (NULL==(jcfg=json_load_file(file_with_path, 
@@ -1994,16 +247,20 @@ static json_t *tr_cfg_parse_one_config_file(const char *file_with_path)
     return NULL;
   }
 
-  // Look for serial number and log it if it exists (borrowed reference, so no need to free it later)
-  if (NULL!=(jser=json_object_get(jcfg, "serial_number"))) {
-    if (json_is_number(jser)) {
-      tr_notice("tr_parse_one_config_file: Attempting to load revision %" JSON_INTEGER_FORMAT " of '%s'.",
-                json_integer_value(jser),
-                file_with_path);
+  return jcfg;
+}
+
+/* extract serial number */
+static json_int_t get_cfg_serial(json_t *jcfg)
+{
+  json_t *jser=NULL;
+
+  if (NULL != (jser = json_object_get(jcfg, "serial_number"))) {
+    if (json_is_integer(jser)) {
+      return json_integer_value(jser);
     }
   }
-
-  return jcfg;
+  return TR_CFG_INVALID_SERIAL;
 }
 
 /**
@@ -2028,11 +285,12 @@ static void tr_cfg_parse_free_jcfgs(unsigned int n_jcfgs, json_t **jcfgs)
  * @param cfg_files
  * @return
  */
-static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, char **files_with_paths)
+static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_files, GArray *files)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   unsigned int ii=0;
   json_t **jcfgs=NULL;
+  TR_CFG_FILE *this_file = NULL;
 
   /* first allocate the jcfgs */
   jcfgs=talloc_array(NULL, json_t *, n_files);
@@ -2041,14 +299,28 @@ static json_t **tr_cfg_parse_config_files(TALLOC_CTX *mem_ctx, unsigned int n_fi
     goto cleanup;
   }
   for (ii=0; ii<n_files; ii++) {
-    jcfgs[ii]=tr_cfg_parse_one_config_file(files_with_paths[ii]);
+    this_file = &g_array_index(files, TR_CFG_FILE, ii);
+    jcfgs[ii]=tr_cfg_parse_one_config_file(this_file->name);
     if (jcfgs[ii]==NULL) {
-      tr_err("tr_parse_config: Error parsing JSON in %s", files_with_paths[ii]);
+      tr_err("tr_parse_config: Error parsing JSON in %s", this_file->name);
       tr_cfg_parse_free_jcfgs(ii, jcfgs); /* frees the JSON objects and the jcfgs array */
       jcfgs=NULL;
       goto cleanup;
     }
+
+    this_file->serial = get_cfg_serial(jcfgs[ii]);
+    if (this_file->serial != TR_CFG_INVALID_SERIAL) {
+      tr_notice("tr_parse_one_config_file: Attempting to load revision %"
+                    JSON_INTEGER_FORMAT
+                    " of '%s'.",
+                this_file->serial,
+                this_file->name);
+    } else {
+      tr_notice("tr_parse_one_config_file: Attempting to load '%s'.",
+                this_file->name);
+    }
   }
+
 cleanup:
   if (jcfgs)
     talloc_steal(mem_ctx, jcfgs); /* give this to the caller's context if we succeeded */
@@ -2062,12 +334,17 @@ typedef TR_CFG_RC (TR_CFG_PARSE_FN)(TR_CFG *, json_t *);
  * Helper function to parse a collection of JSON structures using a generic parse function.
  *
  * @param cfg Config structure to receive results
- * @param jcfgs Pointer to an array of decoded JSON structures
- * @param n_jcfg Number of JSON structures in the array
  * @param parse_fn Function to apply
+ * @param n_jcfg Number of JSON structures in the array
+ * @param jcfgs Pointer to an array of decoded JSON structures
+ * @param key Key to extract from each jcfg before parsing, or NULL to use the object itself
  * @return TR_CFG_SUCCESS on success, _FAIL or an error code on failure
  */
-static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, unsigned int n_jcfg, json_t **jcfgs, TR_CFG_PARSE_FN parse_fn)
+static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg,
+                                     TR_CFG_PARSE_FN parse_fn,
+                                     unsigned int n_jcfg,
+                                     json_t **jcfgs,
+                                     const char *key)
 {
   size_t ii=0;
   json_t *this_jcfg=NULL;
@@ -2077,7 +354,15 @@ static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, unsigned int n_jcfg, json_t **
     return TR_CFG_ERROR;
 
   for (ii=0; ii<n_jcfg; ii++) {
-    this_jcfg=jcfgs[ii];
+    if (key)
+      this_jcfg = json_object_get(jcfgs[ii], key);
+    else
+      this_jcfg = jcfgs[ii];
+
+    /* do not try to parse a missing jcfg */
+    if (this_jcfg == NULL)
+      continue;
+
     ret=parse_fn(cfg, this_jcfg);
     if (ret!=TR_CFG_SUCCESS)
       break;
@@ -2085,9 +370,20 @@ static TR_CFG_RC tr_cfg_parse_helper(TR_CFG *cfg, unsigned int n_jcfg, json_t **
   return ret;
 }
 
+static void add_files(TR_CFG *cfg, unsigned int n, char **filenames)
+{
+  TR_CFG_FILE frec = {0};
+
+  while ((n--) > 0) {
+    frec.name = talloc_strdup(cfg, filenames[n]);
+    frec.serial = TR_CFG_INVALID_SERIAL;
+
+    g_array_append_val(cfg->files, frec);
+  }
+}
 
 /**
- *  Reads configuration files in config_dir ("" or "./" will use the current directory).
+ *  Read a list of configuration files
  *
  * @param cfg_mgr Configuration manager
  * @param n_files Number of entries in cfg_files
@@ -2114,8 +410,11 @@ TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **file
     goto cleanup;
   }
 
+  /* add the list of files to the config */
+  add_files(cfg_mgr->new, n_files, files_with_paths);
+
   /* first parse the json */
-  jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, files_with_paths);
+  jcfgs=tr_cfg_parse_config_files(tmp_ctx, n_files, cfg_mgr->new->files);
   if (jcfgs==NULL) {
     cfg_rc=TR_CFG_NOPARSE;
     goto cleanup;
@@ -2124,11 +423,12 @@ TR_CFG_RC tr_parse_config(TR_CFG_MGR *cfg_mgr, unsigned int n_files, char **file
   cfg_mgr->new->peers=trp_ptable_new(cfg_mgr); /* not sure why this isn't in cfg_mgr->new's context */
 
   /* now run through the parsers on the JSON */
-  if ((TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_internal))) ||
-      (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_local_orgs))) ||
-      (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_peer_orgs))) ||
-      (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_default_servers))) ||
-      (TR_CFG_SUCCESS != (cfg_rc=tr_cfg_parse_helper(cfg_mgr->new, n_files, jcfgs, tr_cfg_parse_comms))))
+  if ((TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_internal, n_files, jcfgs, "tr_internal"))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_local_orgs, n_files, jcfgs, NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_peer_orgs, n_files, jcfgs, NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_default_servers, n_files, jcfgs,
+                                                      NULL))) ||
+      (TR_CFG_SUCCESS != (cfg_rc= tr_cfg_parse_helper(cfg_mgr->new, tr_cfg_parse_comms, n_files, jcfgs, NULL))))
     goto cleanup; /* cfg_rc was set above */
 
   /* make sure we got a complete, consistent configuration */
@@ -2149,49 +449,8 @@ cleanup:
   return cfg_rc;
 }
 
-TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
-{
-
-  TR_IDP_REALM *cfg_idp;
-
-  if ((!tr_cfg) || (!idp_id)) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  for (cfg_idp = tr_cfg->ctable->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
-    if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
-      tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
-      return cfg_idp;
-    }
-  }
-  /* if we didn't find one, return NULL */ 
-  return NULL;
-}
-
-TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
-{
-  TR_RP_CLIENT *cfg_rp;
-
-  if ((!tr_cfg) || (!rp_gss)) {
-    if (rc)
-      *rc = TR_CFG_BAD_PARAMS;
-    return NULL;
-  }
-
-  for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
-    if (tr_gss_names_matches(cfg_rp->gss_names, rp_gss)) {
-      tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
-      return cfg_rp;
-    }
-  }
-  /* if we didn't find one, return NULL */ 
-  return NULL;
-}
-
 static int is_cfg_file(const struct dirent *dent) {
-  int n;
+  size_t n;
 
   /* Only accept filenames ending in ".cfg" and starting with a character
    * other than an ASCII '.' */
diff --git a/common/tr_config_comms.c b/common/tr_config_comms.c
new file mode 100644 (file)
index 0000000..60b5f93
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2012-2018, 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 <string.h>
+#include <jansson.h>
+#include <dirent.h>
+#include <talloc.h>
+
+#include <tr_cfgwatch.h>
+#include <tr_comm.h>
+#include <tr_config.h>
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+#include <tr_filter.h>
+#include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg)
+{
+  json_t *jdss = NULL;
+  TR_CFG_RC rc = TR_CFG_SUCCESS;
+  TR_AAA_SERVER *ds = NULL;
+  int i = 0;
+
+  if ((!trc) || (!jcfg))
+    return TR_CFG_BAD_PARAMS;
+
+  /* If there are default servers, store them */
+  if ((NULL != (jdss = json_object_get(jcfg, "default_servers"))) &&
+      (json_is_array(jdss)) &&
+      (0 < json_array_size(jdss))) {
+
+    for (i = 0; i < json_array_size(jdss); i++) {
+      if (NULL == (ds = tr_cfg_parse_one_aaa_server(trc,
+                                                    json_array_get(jdss, i),
+                                                   &rc))) {
+       return rc;
+      }
+      tr_debug("tr_cfg_parse_default_servers: Default server configured: %s", ds->hostname->buf);
+      ds->next = trc->default_servers;
+      trc->default_servers = ds;
+    }
+  }
+
+  tr_debug("tr_cfg_parse_default_servers: Finished (rc=%d)", rc);
+  return rc;
+}
+
+static void tr_cfg_parse_comm_idps(TR_CFG *trc, json_t *jidps, TR_COMM *comm, TR_CFG_RC *rc)
+{
+  TR_IDP_REALM *found_idp=NULL;
+  json_t *jidp_name=NULL;
+  TR_NAME *idp_name=NULL;
+  size_t ii = 0;
+
+  if ((!trc) ||
+      (!jidps) ||
+      (!json_is_array(jidps))) {
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return;
+  }
+
+  json_array_foreach(jidps, ii, jidp_name) {
+    idp_name=tr_new_name(json_string_value(jidp_name));
+    if (idp_name==NULL) {
+      *rc = TR_CFG_NOMEM;
+      return;
+    }
+    found_idp=tr_cfg_find_idp(trc, idp_name, rc);
+    tr_free_name(idp_name);
+
+    if ((found_idp==NULL) || (*rc!=TR_CFG_SUCCESS)) {
+      tr_debug("tr_cfg_parse_comm_idps: Unknown IDP %s.", json_string_value(jidp_name));
+      *rc=TR_CFG_ERROR;
+      return;
+    }
+    tr_comm_add_idp_realm(trc->ctable, comm, found_idp, 0, NULL, NULL); /* no provenance, never expires */
+  }
+
+  *rc=TR_CFG_SUCCESS;
+  return;
+}
+
+static void tr_cfg_parse_comm_rps(TR_CFG *trc, json_t *jrps, TR_COMM *comm, TR_CFG_RC *rc)
+{
+  TR_RP_REALM *found_rp=NULL;
+  TR_RP_REALM *new_rp=NULL;
+  TR_NAME *rp_name=NULL;
+  const char *s=NULL;
+  int ii=0;
+
+  if ((!trc) ||
+      (!jrps) ||
+      (!json_is_array(jrps))) {
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return;
+  }
+
+  for (ii=0; ii<json_array_size(jrps); ii++) {
+    /* get the RP name as a string */
+    s=json_string_value(json_array_get(jrps, ii));
+    if (s==NULL) {
+      tr_notice("tr_cfg_parse_comm_rps: null RP found in community %.*s, ignoring.",
+                tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      continue;
+    }
+
+    /* convert string to TR_NAME */
+    rp_name=tr_new_name(s);
+    if (rp_name==NULL) {
+      tr_err("tr_cfg_parse_comm_rps: unable to allocate RP name for %s in community %.*s.",
+             s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+    }
+
+    /* see if we already have this RP in this community */
+    found_rp=tr_comm_find_rp(trc->ctable, comm, rp_name);
+    if (found_rp!=NULL) {
+      tr_notice("tr_cfg_parse_comm_rps: RP %s repeated in community %.*s.",
+                s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      tr_free_name(rp_name);
+      continue;
+    }
+
+    /* Add the RP to the community, first see if we have the RP in any community */
+    found_rp=tr_rp_realm_lookup(trc->ctable->rp_realms, rp_name);
+    if (found_rp!=NULL) {
+      tr_debug("tr_cfg_parse_comm_rps: RP realm %s already exists.", s);
+      new_rp=found_rp; /* use it rather than creating a new realm record */
+    } else {
+      new_rp=tr_rp_realm_new(NULL);
+      if (new_rp==NULL) {
+        tr_err("tr_cfg_parse_comm_rps: unable to allocate RP record for %s in community %.*s.",
+               s, tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      }
+      tr_debug("tr_cfg_parse_comm_rps: setting name to %s", rp_name->buf);
+      tr_rp_realm_set_id(new_rp, rp_name);
+      rp_name=NULL; /* rp_name no longer belongs to us */
+      tr_rp_realm_add(trc->ctable->rp_realms, new_rp);
+      talloc_steal(trc->ctable, trc->ctable->rp_realms); /* make sure head is in the right context */
+    }
+    tr_comm_add_rp_realm(trc->ctable, comm, new_rp, 0, NULL, NULL);
+  }
+}
+
+static TR_COMM *tr_cfg_parse_one_comm (TALLOC_CTX *mem_ctx, TR_CFG *trc, json_t *jcomm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_COMM *comm = NULL;
+  json_t *jid = NULL;
+  json_t *jtype = NULL;
+  json_t *japcs = NULL;
+  json_t *jidps = NULL;
+  json_t *jrps = NULL;
+
+  if ((!trc) || (!jcomm) || (!rc)) {
+    tr_debug("tr_cfg_parse_one_comm: Bad parameters.");
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  comm=tr_comm_new(tmp_ctx);
+  if (comm==NULL) {
+    tr_crit("tr_cfg_parse_one_comm: Out of memory.");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+
+  if ((NULL == (jid = json_object_get(jcomm, "community_id"))) ||
+      (!json_is_string(jid)) ||
+      (NULL == (jtype = json_object_get(jcomm, "type"))) ||
+      (!json_is_string(jtype)) ||
+      (NULL == (japcs = json_object_get(jcomm, "apcs"))) ||
+      (!json_is_array(japcs)) ||
+      (NULL == (jidps = json_object_get(jcomm, "idp_realms"))) ||
+      (!json_is_array(jidps)) ||
+      (NULL == (jrps = json_object_get(jcomm, "rp_realms"))) ||
+      (!json_is_array(jrps))) {
+    tr_debug("tr_cfg_parse_one_comm: Error parsing Communities configuration.");
+    *rc = TR_CFG_NOPARSE;
+    comm=NULL;
+    goto cleanup;
+  }
+
+  tr_comm_set_id(comm, tr_new_name(json_string_value(jid)));
+  if (NULL == tr_comm_get_id(comm)) {
+    tr_debug("tr_cfg_parse_one_comm: No memory for community id.");
+    *rc = TR_CFG_NOMEM;
+    comm=NULL;
+    goto cleanup;
+  }
+
+  if (0 == strcmp(json_string_value(jtype), "apc")) {
+    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))) {
+      tr_debug("tr_cfg_parse_one_comm: Can't parse APCs for COI %s.",
+               tr_comm_get_id(comm)->buf);
+      comm=NULL;
+      goto cleanup;
+    }
+  } else {
+    tr_debug("tr_cfg_parse_one_comm: Invalid community type, comm = %s, type = %s",
+             tr_comm_get_id(comm)->buf, json_string_value(jtype));
+    *rc = TR_CFG_NOPARSE;
+    comm=NULL;
+    goto cleanup;
+  }
+
+  tr_cfg_parse_comm_idps(trc, jidps, comm, rc);
+  if (TR_CFG_SUCCESS != *rc) {
+    tr_debug("tr_cfg_parse_one_comm: Can't parse IDP realms for comm %s.",
+             tr_comm_get_id(comm)->buf);
+    comm=NULL;
+    goto cleanup;
+  }
+
+  tr_cfg_parse_comm_rps(trc, jrps, comm, rc);
+  if (TR_CFG_SUCCESS != *rc) {
+    tr_debug("tr_cfg_parse_one_comm: Can't parse RP realms for comm %s .",
+             tr_comm_get_id(comm)->buf);
+    comm=NULL;
+    goto cleanup;
+  }
+
+  if (TR_COMM_APC == comm->type) {
+    json_t *jexpire  = json_object_get(jcomm, "expiration_interval");
+    comm->expiration_interval = 43200; /*30 days*/
+    if (jexpire) {
+      if (!json_is_integer(jexpire)) {
+        tr_err("tr_parse_one_comm: expiration_interval is not an integer for comm %.*s",
+                 tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+        comm=NULL;
+        goto cleanup;
+      }
+      comm->expiration_interval = json_integer_value(jexpire);
+      if (comm->expiration_interval <= 10) {
+        comm->expiration_interval = 11; /* Freeradius waits 10 minutes between successful TR queries*/
+        tr_notice(
+            "tr_parse_one_comm: expiration interval for %.*s less than minimum of 11 minutes; using 11 minutes instead.",
+            tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      }
+      if (comm->expiration_interval > 129600) {
+        /* > 90 days*/
+        comm->expiration_interval = 129600;
+        tr_notice(
+            "tr_parse_one_comm: expiration interval for %.*s exceeds maximum of 90 days; using 90 days instead.",
+            tr_comm_get_id(comm)->len, tr_comm_get_id(comm)->buf);
+      }
+    }
+  }
+
+cleanup:
+  if (comm!=NULL)
+    talloc_steal(mem_ctx, comm);
+  talloc_free(tmp_ctx);
+  return comm;
+}
+
+TR_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg)
+{
+  json_t *jcomms = NULL;
+  TR_CFG_RC rc = TR_CFG_SUCCESS;
+  TR_COMM *comm = NULL;
+  int i = 0;
+
+  if ((!trc) || (!jcfg)) {
+    tr_debug("tr_cfg_parse_comms: Bad Parameters.");
+    return TR_CFG_BAD_PARAMS;
+  }
+
+  if (NULL != (jcomms = json_object_get(jcfg, "communities"))) {
+    if (!json_is_array(jcomms)) {
+      return TR_CFG_NOPARSE;
+    }
+
+    for (i = 0; i < json_array_size(jcomms); i++) {
+      if (NULL == (comm = tr_cfg_parse_one_comm(NULL, /* TODO: use a talloc context */
+                                                trc,
+                                                json_array_get(jcomms, i),
+                                               &rc))) {
+        return rc;
+      }
+      tr_debug("tr_cfg_parse_comms: Community configured: %s.",
+               tr_comm_get_id(comm)->buf);
+
+      tr_comm_table_add_comm(trc->ctable, comm);
+    }
+  }
+  tr_debug("tr_cfg_parse_comms: Finished (rc=%d)", rc);
+  return rc;
+}
+
+TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *tr_cfg, TR_NAME *idp_id, TR_CFG_RC *rc)
+{
+
+  TR_IDP_REALM *cfg_idp;
+
+  if ((!tr_cfg) || (!idp_id)) {
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return NULL;
+  }
+
+  for (cfg_idp = tr_cfg->ctable->idp_realms; NULL != cfg_idp; cfg_idp = cfg_idp->next) {
+    if (!tr_name_cmp (idp_id, cfg_idp->realm_id)) {
+      tr_debug("tr_cfg_find_idp: Found %s.", idp_id->buf);
+      return cfg_idp;
+    }
+  }
+  /* if we didn't find one, return NULL */
+  return NULL;
+}
+
+TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *tr_cfg, TR_NAME *rp_gss, TR_CFG_RC *rc)
+{
+  TR_RP_CLIENT *cfg_rp;
+
+  if ((!tr_cfg) || (!rp_gss)) {
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return NULL;
+  }
+
+  for (cfg_rp = tr_cfg->rp_clients; NULL != cfg_rp; cfg_rp = cfg_rp->next) {
+    if (tr_gss_names_matches(cfg_rp->gss_names, rp_gss)) {
+      tr_debug("tr_cfg_find_rp: Found %s.", rp_gss->buf);
+      return cfg_rp;
+    }
+  }
+  /* if we didn't find one, return NULL */
+  return NULL;
+}
diff --git a/common/tr_config_encoders.c b/common/tr_config_encoders.c
new file mode 100644 (file)
index 0000000..47331d9
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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 <jansson.h>
+#include <tr_config.h>
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+static json_t *tr_cfg_file_to_json(TR_CFG_FILE *cfg_file)
+{
+  json_t *file_json = NULL;
+  json_t *retval = NULL;
+
+  file_json = json_object();
+  if (file_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(file_json, "name", json_string(cfg_file->name));
+  if (cfg_file->serial != TR_CFG_INVALID_SERIAL)
+    OBJECT_SET_OR_FAIL(file_json, "serial", json_integer(cfg_file->serial));
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = file_json;
+  json_incref(retval);
+
+cleanup:
+  if (file_json)
+    json_decref(file_json);
+
+  return retval;
+}
+
+json_t *tr_cfg_files_to_json_array(TR_CFG *cfg)
+{
+  guint ii;
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+
+  if (jarray == NULL)
+    goto cleanup;
+
+  for (ii=0; ii<cfg->files->len; ii++) {
+    ARRAY_APPEND_OR_FAIL(jarray,
+                         tr_cfg_file_to_json(
+                             &g_array_index(cfg->files, TR_CFG_FILE, ii)));
+  }
+
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  return retval;
+}
diff --git a/common/tr_config_filters.c b/common/tr_config_filters.c
new file mode 100644 (file)
index 0000000..4035e68
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2012-2018, 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 <string.h>
+#include <jansson.h>
+#include <dirent.h>
+#include <talloc.h>
+
+#include <tr_cfgwatch.h>
+#include <tr_comm.h>
+#include <tr_config.h>
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+#include <tr_filter.h>
+#include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, char *ctype, json_t *jc, TR_CFG_RC *rc)
+{
+  TR_CONSTRAINT *cons=NULL;
+  int i=0;
+
+  if ((!ctype) || (!jc) || (!rc) ||
+      (!json_is_array(jc)) ||
+      (0 >= json_array_size(jc)) ||
+      (TR_MAX_CONST_MATCHES < json_array_size(jc)) ||
+      (!json_is_string(json_array_get(jc, 0)))) {
+    tr_err("tr_cfg_parse_one_constraint: config error.");
+    *rc=TR_CFG_NOPARSE;
+    return NULL;
+  }
+
+  if (NULL==(cons=tr_constraint_new(mem_ctx))) {
+    tr_debug("tr_cfg_parse_one_constraint: Out of memory (cons).");
+    *rc=TR_CFG_NOMEM;
+    return NULL;
+  }
+
+  if (NULL==(cons->type=tr_new_name(ctype))) {
+    tr_err("tr_cfg_parse_one_constraint: Out of memory (type).");
+    *rc=TR_CFG_NOMEM;
+    tr_constraint_free(cons);
+    return NULL;
+  }
+
+  for (i=0; i < json_array_size(jc); i++) {
+    cons->matches[i]=tr_new_name(json_string_value(json_array_get(jc, i)));
+    if (cons->matches[i]==NULL) {
+      tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i+1);
+      *rc=TR_CFG_NOMEM;
+      tr_constraint_free(cons);
+      return NULL;
+    }
+  }
+
+  return cons;
+}
+
+static TR_FILTER *tr_cfg_parse_one_filter(TALLOC_CTX *mem_ctx, json_t *jfilt, TR_FILTER_TYPE ftype, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_FILTER *filt = NULL;
+  json_t *jfaction = NULL;
+  json_t *jfline = NULL;
+  json_t *jfspecs = NULL;
+  json_t *this_jfspec = NULL;
+  json_t *jfield = NULL;
+  json_t *jmatch = NULL;
+  json_t *jrc = NULL;
+  json_t *jdc = NULL;
+  json_t *this_jmatch = NULL;
+  TR_NAME *name = NULL;
+  size_t i = 0, j = 0, k = 0;
+
+  *rc = TR_CFG_ERROR;
+
+  if ((jfilt == NULL) || (rc == NULL)) {
+    tr_err("tr_cfg_parse_one_filter: null argument");
+    *rc = TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if (NULL == (filt = tr_filter_new(tmp_ctx))) {
+    tr_err("tr_cfg_parse_one_filter: Out of memory.");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  tr_filter_set_type(filt, ftype);
+
+  /* make sure we have space to represent the filter */
+  if (json_array_size(jfilt) > TR_MAX_FILTER_LINES) {
+    tr_err("tr_cfg_parse_one_filter: Filter has too many lines, maximum of %d.", TR_MAX_FILTER_LINES);
+    *rc = TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  /* For each entry in the filter... */
+  json_array_foreach(jfilt, i, jfline) {
+    if ((NULL == (jfaction = json_object_get(jfline, "action"))) ||
+        (!json_is_string(jfaction))) {
+      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action.");
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    if ((NULL == (jfspecs = json_object_get(jfline, "specs"))) ||
+        (!json_is_array(jfspecs)) ||
+        (0 == json_array_size(jfspecs))) {
+      tr_debug("tr_cfg_parse_one_filter: Error parsing filter specs.");
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    if (TR_MAX_FILTER_SPECS < json_array_size(jfspecs)) {
+      tr_debug("tr_cfg_parse_one_filter: Filter has too many specs, maximimum of %d.", TR_MAX_FILTER_SPECS);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    if (NULL == (filt->lines[i] = tr_fline_new(filt))) {
+      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i + 1);
+      *rc = TR_CFG_NOMEM;
+      goto cleanup;
+    }
+
+    if (!strcmp(json_string_value(jfaction), "accept")) {
+      filt->lines[i]->action = TR_FILTER_ACTION_ACCEPT;
+    } else if (!strcmp(json_string_value(jfaction), "reject")) {
+      filt->lines[i]->action = TR_FILTER_ACTION_REJECT;
+    } else {
+      tr_debug("tr_cfg_parse_one_filter: Error parsing filter action, unknown action' %s'.",
+               json_string_value(jfaction));
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    if (NULL != (jrc = json_object_get(jfline, "realm_constraints"))) {
+      if (!json_is_array(jrc)) {
+        tr_err("tr_cfg_parse_one_filter: cannot parse realm_constraints, not an array.");
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      } else if (json_array_size(jrc) > TR_MAX_CONST_MATCHES) {
+        tr_err("tr_cfg_parse_one_filter: realm_constraints has too many entries, maximum of %d.",
+               TR_MAX_CONST_MATCHES);
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      } else if (json_array_size(jrc) > 0) {
+        /* ok we actually have entries to process */
+        if (NULL == (filt->lines[i]->realm_cons = tr_cfg_parse_one_constraint(filt->lines[i], "realm", jrc, rc))) {
+          tr_debug("tr_cfg_parse_one_filter: Error parsing realm constraint");
+          *rc = TR_CFG_NOPARSE;
+          goto cleanup;
+        }
+      }
+    }
+
+    if (NULL != (jdc = json_object_get(jfline, "domain_constraints"))) {
+      if (!json_is_array(jdc)) {
+        tr_err("tr_cfg_parse_one_filter: cannot parse domain_constraints, not an array.");
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      } else if (json_array_size(jdc) > TR_MAX_CONST_MATCHES) {
+        tr_err("tr_cfg_parse_one_filter: domain_constraints has too many entries, maximum of %d.",
+               TR_MAX_CONST_MATCHES);
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      } else if (json_array_size(jdc) > 0) {
+        if (NULL == (filt->lines[i]->domain_cons = tr_cfg_parse_one_constraint(filt->lines[i], "domain", jdc, rc))) {
+          tr_debug("tr_cfg_parse_one_filter: Error parsing domain constraint");
+          *rc = TR_CFG_NOPARSE;
+          goto cleanup;
+        }
+      }
+    }
+
+    /*For each filter spec within the filter line... */
+    json_array_foreach(jfspecs, j, this_jfspec) {
+      if ((NULL == (jfield = json_object_get(this_jfspec, "field"))) ||
+          (!json_is_string(jfield))) {
+        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing field for filer spec %d, filter line %d.", i,
+                 j);
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+
+      /* check that we have a match attribute */
+      if (NULL == (jmatch = json_object_get(this_jfspec, "match"))) {
+        tr_debug("tr_cfg_parse_one_filter: Error parsing filter: missing match for filer spec %d, filter line %d.", i,
+                 j);
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+
+      /* check that match is a string or an array */
+      if ((!json_is_string(jmatch)) && (!json_is_array(jmatch))) {
+        tr_debug(
+            "tr_cfg_parse_one_filter: Error parsing filter: match not a string or array for filter spec %d, filter line %d.",
+            i, j);
+        *rc = TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+
+      /* allocate the filter spec */
+      if (NULL == (filt->lines[i]->specs[j] = tr_fspec_new(filt->lines[i]))) {
+        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+        *rc = TR_CFG_NOMEM;
+        goto cleanup;
+      }
+
+      /* fill in the field */
+      if (NULL == (filt->lines[i]->specs[j]->field = tr_new_name(json_string_value(jfield)))) {
+        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+        *rc = TR_CFG_NOMEM;
+        goto cleanup;
+      }
+
+      /* fill in the matches */
+      if (json_is_string(jmatch)) {
+        if (NULL == (name = tr_new_name(json_string_value(jmatch)))) {
+          tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+          *rc = TR_CFG_NOMEM;
+          goto cleanup;
+        }
+        tr_fspec_add_match(filt->lines[i]->specs[j], name);
+      } else {
+        /* jmatch is an array (we checked earlier) */
+        json_array_foreach(jmatch, k, this_jmatch) {
+          if (NULL == (name = tr_new_name(json_string_value(this_jmatch)))) {
+            tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+            *rc = TR_CFG_NOMEM;
+            goto cleanup;
+          }
+          tr_fspec_add_match(filt->lines[i]->specs[j], name);
+        }
+      }
+      if (!tr_filter_validate_spec_field(ftype, filt->lines[i]->specs[j])){
+        tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.",
+                 filt->lines[i]->specs[j]->field->len,
+                 filt->lines[i]->specs[j]->field->buf,
+                 tr_filter_type_to_string(filt->type),
+                 i, j);
+        *rc = TR_CFG_ERROR;
+        goto cleanup;
+      }
+    }
+  }
+
+  /* check that the filter is valid */
+  if (!tr_filter_validate(filt)) {
+    *rc = TR_CFG_ERROR;
+  } else {
+    *rc = TR_CFG_SUCCESS;
+    talloc_steal(mem_ctx, filt);
+  }
+
+cleanup:
+  talloc_free(tmp_ctx);
+  if (*rc!=TR_CFG_SUCCESS)
+    filt=NULL;
+  return filt;
+}
+
+TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  json_t *jfilt;
+  const char *filt_label=NULL;
+  TR_FILTER *filt=NULL;
+  TR_FILTER_SET *filt_set=NULL;
+  TR_FILTER_TYPE filt_type=TR_FILTER_TYPE_UNKNOWN;
+
+  *rc=TR_CFG_ERROR;
+
+  /* no filters */
+  if (jfilts==NULL) {
+    *rc=TR_CFG_SUCCESS;
+    goto cleanup;
+  }
+
+  filt_set=tr_filter_set_new(tmp_ctx);
+  if (filt_set==NULL) {
+    tr_debug("tr_cfg_parse_filters: Unable to allocate filter set.");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  json_object_foreach(jfilts, filt_label, jfilt) {
+    /* check that we got a filter */
+    if (jfilt == NULL) {
+      tr_debug("tr_cfg_parse_filters: Definition for %s filter is missing.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    /* check that we recognize the filter type */
+    filt_type=tr_filter_type_from_string(filt_label);
+    if (filt_type==TR_FILTER_TYPE_UNKNOWN) {
+      tr_debug("tr_cfg_parse_filters: Unrecognized filter (%s) defined.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    /* finally, parse the filter */
+    tr_debug("tr_cfg_parse_filters: Found %s filter.", filt_label);
+    filt = tr_cfg_parse_one_filter(tmp_ctx, jfilt, filt_type, rc);
+    tr_filter_set_add(filt_set, filt);
+    if (*rc != TR_CFG_SUCCESS) {
+      tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+  }
+
+  *rc=TR_CFG_SUCCESS;
+
+cleanup:
+  if (*rc==TR_CFG_SUCCESS)
+    talloc_steal(mem_ctx, filt_set);
+  else if (filt_set!=NULL) {
+    talloc_free(filt_set);
+    filt_set=NULL;
+  }
+
+  talloc_free(tmp_ctx);
+  return filt_set;
+}
diff --git a/common/tr_config_internal.c b/common/tr_config_internal.c
new file mode 100644 (file)
index 0000000..d06dc51
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2012-2018, 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 <talloc.h>
+#include <jansson.h>
+#include <tr_debug.h>
+#include <tr_config.h>
+#include <tr_cfgwatch.h>
+
+/**
+ * Parse a boolean
+ *
+ * If the key does not exist in the src object, returns success but does fill in *dest.
+ *
+ * @param src JSON object to pull a value from
+ * @param key key to pull
+ * @param dest (output) pointer to an allocated integer
+ * @return TR_CFG_SUCCESS or an error code
+ */
+static TR_CFG_RC tr_cfg_parse_boolean(json_t *src, const char *key, int *dest)
+{
+  json_t *jtmp;
+
+  /* Validate parameters */
+  if ((src == NULL) || (key == NULL) || (dest == NULL))
+    return TR_CFG_BAD_PARAMS;
+
+  /* See if we have a value for this key; do nothing if not */
+  jtmp = json_object_get(src, key);
+  if (jtmp) {
+    if (json_is_boolean(jtmp)) {
+      *dest = json_boolean_value(jtmp);
+    } else {
+      tr_debug("tr_cfg_parse_unsigned: Parsing error, %s is not a boolean.", key);
+      return TR_CFG_NOPARSE;
+    }
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
+/**
+ * Parse an unsigned integer
+ *
+ * If the key does not exist in the src object, returns success but does fill in *dest.
+ *
+ * @param src JSON object to pull a value from
+ * @param key key to pull
+ * @param dest (output) pointer to an allocated unsigned integer
+ * @return TR_CFG_SUCCESS or an error code
+ */
+static TR_CFG_RC tr_cfg_parse_unsigned(json_t *src, const char *key, unsigned int *dest)
+{
+  json_t *jtmp;
+
+  /* Validate parameters */
+  if ((src == NULL) || (key == NULL) || (dest == NULL))
+    return TR_CFG_BAD_PARAMS;
+
+  /* See if we have a value for this key; do nothing if not */
+  jtmp = json_object_get(src, key);
+  if (jtmp) {
+    if (json_is_number(jtmp)) {
+      *dest = (unsigned int) json_integer_value(jtmp);
+    } else {
+      tr_debug("tr_cfg_parse_unsigned: Parsing error, %s is not a number.", key);
+      return TR_CFG_NOPARSE;
+    }
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
+/**
+ * Parse a string
+ *
+ * If the key does not exist in the src object, returns success but does not allocate
+ * a return value in dest. Nulls the destination pointer if there is no return value.
+ *
+ * Return value is allocated in talloc's NULL context and must be freed with talloc_free()
+ * or put into a non-NULL context with talloc_steal()
+ *
+ * @param src JSON object to pull a value from
+ * @param key key to pull
+ * @param dest (output) pointer to a pointer that will hold the newly allocated return value
+ * @return TR_CFG_SUCCESS or an error code
+ */
+static TR_CFG_RC tr_cfg_parse_string(json_t *src, const char *key, const char **dest)
+{
+  json_t *jtmp;
+
+  /* Validate parameters */
+  if ((src == NULL) || (key == NULL) || (dest == NULL))
+    return TR_CFG_BAD_PARAMS;
+
+  /* See if we have a value for this key; do nothing if not */
+  jtmp = json_object_get(src, key);
+  if (!jtmp) {
+    *dest = NULL; /* No return value, null this out */
+  } else {
+    if (json_is_string(jtmp)) {
+      *dest = talloc_strdup(NULL, json_string_value(jtmp));
+    } else {
+      tr_debug("tr_cfg_parse_string: Parsing error, %s is not a string.", key);
+      return TR_CFG_NOPARSE;
+    }
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
+/**
+ * Set default values for settings that have them
+ *
+ * @param cfg configuration structure to fill in, not null
+ */
+static void set_defaults(TR_CFG_INTERNAL *cfg)
+{
+  cfg->max_tree_depth = TR_DEFAULT_MAX_TREE_DEPTH;
+  cfg->tids_port = TR_DEFAULT_TIDS_PORT;
+  cfg->trps_port = TR_DEFAULT_TRPS_PORT;
+  cfg->monitoring_port = TR_DEFAULT_MONITORING_PORT;
+  cfg->cfg_poll_interval = TR_CFGWATCH_DEFAULT_POLL;
+  cfg->cfg_settling_time = TR_CFGWATCH_DEFAULT_SETTLE;
+  cfg->trp_connect_interval = TR_DEFAULT_TRP_CONNECT_INTERVAL;
+  cfg->trp_sweep_interval = TR_DEFAULT_TRP_SWEEP_INTERVAL;
+  cfg->trp_update_interval = TR_DEFAULT_TRP_UPDATE_INTERVAL;
+  cfg->tid_req_timeout = TR_DEFAULT_TID_REQ_TIMEOUT;
+  cfg->tid_resp_numer = TR_DEFAULT_TID_RESP_NUMER;
+  cfg->tid_resp_denom = TR_DEFAULT_TID_RESP_DENOM;
+  cfg->log_threshold = TR_DEFAULT_LOG_THRESHOLD;
+  cfg->console_threshold = TR_DEFAULT_CONSOLE_THRESHOLD;
+  cfg->monitoring_credentials = NULL;
+}
+
+/* Helper that checks return value of a parse fn and returns if it failed */
+#define NOPARSE_UNLESS(x)    \
+do {                         \
+  if ((x) != TR_CFG_SUCCESS) \
+    return TR_CFG_NOPARSE;   \
+} while(0)
+
+static TR_CFG_RC tr_cfg_parse_monitoring(TR_CFG *trc, json_t *jmon)
+{
+  int enabled = 1; /* assume we are enabled unless we are told not to be */
+
+  NOPARSE_UNLESS(tr_cfg_parse_boolean(jmon, "enabled", &enabled));
+  if (enabled) {
+    NOPARSE_UNLESS(tr_cfg_parse_unsigned(jmon, "port", &(trc->internal->monitoring_port)));
+    NOPARSE_UNLESS(tr_cfg_parse_gss_names(trc->internal,
+                                          json_object_get(jmon, "authorized_credentials"),
+                                          &(trc->internal->monitoring_credentials)));
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
+/**
+ * Parse internal configuration JSON
+ *
+ * @param trc configuration structure to fill in
+ * @param jint internal configuration JSON object
+ * @return TR_CFG_SUCCESS or an error code
+ */
+TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jint)
+{
+  json_t *jtmp = NULL;
+  const char *s = NULL;
+
+  if ((!trc) || (!jint))
+    return TR_CFG_BAD_PARAMS;
+
+  /* If we don't yet have an internal config, allocate one and set defaults. If it
+   * already exists, do not disturb existing settings. */
+  if (NULL == trc->internal) {
+    if (NULL == (trc->internal = talloc_zero(trc, TR_CFG_INTERNAL)))
+      return TR_CFG_NOMEM;
+    set_defaults(trc->internal); /* Install defaults for any unspecified settings */
+  }
+
+  NOPARSE_UNLESS(tr_cfg_parse_string(jint, "hostname", &(trc->internal->hostname)));
+  talloc_steal(trc->internal, trc->internal->hostname);
+
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "max_tree_depth",           &(trc->internal->max_tree_depth)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tids_port",                &(trc->internal->tids_port)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trps_port",                &(trc->internal->trps_port)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "cfg_poll_interval",        &(trc->internal->cfg_poll_interval)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "cfg_settling_time",        &(trc->internal->cfg_settling_time)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_connect_interval",     &(trc->internal->trp_connect_interval)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_sweep_interval",       &(trc->internal->trp_sweep_interval)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "trp_update_interval",      &(trc->internal->trp_update_interval)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_request_timeout",      &(trc->internal->tid_req_timeout)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_response_numerator",   &(trc->internal->tid_resp_numer)));
+  NOPARSE_UNLESS(tr_cfg_parse_unsigned(jint, "tid_response_denominator", &(trc->internal->tid_resp_denom)));
+
+  /* Parse the logging section */
+  if (NULL != (jtmp = json_object_get(jint, "logging"))) {
+    NOPARSE_UNLESS(tr_cfg_parse_string(jtmp, "log_threshold", &s));
+    if (s) {
+      trc->internal->log_threshold = str2sev(s);
+      talloc_free((void *) s);
+    }
+
+    NOPARSE_UNLESS(tr_cfg_parse_string(jtmp, "console_threshold", &s));
+    if (s) {
+      trc->internal->console_threshold = str2sev(s);
+      talloc_free((void *) s);
+    }
+  }
+
+  /* Parse the monitoring section */
+  if (NULL != (jtmp = json_object_get(jint, "monitoring"))) {
+    NOPARSE_UNLESS(tr_cfg_parse_monitoring(trc, jtmp));
+  }
+
+  tr_debug("tr_cfg_parse_internal: Internal config parsed.");
+  return TR_CFG_SUCCESS;
+}
diff --git a/common/tr_config_orgs.c b/common/tr_config_orgs.c
new file mode 100644 (file)
index 0000000..f6925ca
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2012-2018, 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 <string.h>
+#include <jansson.h>
+#include <dirent.h>
+#include <talloc.h>
+
+#include <tr_cfgwatch.h>
+#include <tr_comm.h>
+#include <tr_config.h>
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+#include <tr_filter.h>
+#include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+/* takes a talloc context, but currently does not use it */
+static TR_NAME *tr_cfg_parse_org_name(TALLOC_CTX *mem_ctx, json_t *j_org, TR_CFG_RC *rc)
+{
+  TR_NAME *name=NULL;
+
+  if ((j_org==NULL) || (rc==NULL) || (!json_is_string(j_org))) {
+    tr_debug("tr_cfg_parse_org_name: Bad parameters.");
+    if (rc!=NULL)
+      *rc = TR_CFG_BAD_PARAMS; /* fill in return value if we can */
+    return NULL;
+  }
+
+  name=tr_new_name(json_string_value(j_org));
+  if (name==NULL)
+    *rc=TR_CFG_NOMEM;
+  else
+    *rc=TR_CFG_SUCCESS;
+  return name;
+}
+
+static TR_CFG_RC tr_cfg_parse_one_local_org(TR_CFG *trc, json_t *jlorg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_CFG_RC retval=TR_CFG_ERROR; /* our return code */
+  TR_CFG_RC rc=TR_CFG_ERROR; /* return code from functions we call */
+  TR_NAME *org_name=NULL;
+  json_t *j_org=NULL;
+  json_t *j_realms=NULL;
+  TR_IDP_REALM *new_idp_realms=NULL;
+  TR_RP_CLIENT *new_rp_clients=NULL;
+
+  tr_debug("tr_cfg_parse_one_local_org: parsing local organization");
+
+  /* get organization_name (optional) */
+  if (NULL==(j_org=json_object_get(jlorg, "organization_name"))) {
+    tr_debug("tr_cfg_parse_one_local_org: organization_name unspecified");
+  } else {
+    org_name=tr_cfg_parse_org_name(tmp_ctx, j_org, &rc);
+    if (rc==TR_CFG_SUCCESS) {
+      tr_debug("tr_cfg_parse_one_local_org: organization_name=\"%.*s\"",
+               org_name->len,
+               org_name->buf);
+      /* we don't actually do anything with this, but we could */
+      tr_free_name(org_name);
+      org_name=NULL;
+    }
+  }
+
+  /* Now get realms. Allow this to be missing; even though that is a pointless organization entry,
+   * it's harmless. Report a warning because that might be unintentional. */
+  if (NULL==(j_realms=json_object_get(jlorg, "realms"))) {
+    tr_warning("tr_cfg_parse_one_local_org: warning - no realms in this local organization");
+  } else {
+    /* Allocate in the tmp_ctx so these will be cleaned up if we do not complete successfully. */
+    new_idp_realms=tr_cfg_parse_idp_realms(tmp_ctx, j_realms, &rc);
+    if (rc!=TR_CFG_SUCCESS)
+      goto cleanup;
+
+    new_rp_clients=tr_cfg_parse_rp_clients(tmp_ctx, j_realms, &rc);
+    if (rc!=TR_CFG_SUCCESS)
+      goto cleanup;
+  }
+  retval=TR_CFG_SUCCESS;
+
+cleanup:
+  /* if we succeeded, link things to the configuration and move out of tmp context */
+  if (retval==TR_CFG_SUCCESS) {
+    if (new_idp_realms!=NULL) {
+      tr_idp_realm_add(trc->ctable->idp_realms, new_idp_realms); /* fixes talloc contexts except for head*/
+      talloc_steal(trc, trc->ctable->idp_realms); /* make sure the head is in the right context */
+    }
+
+    if (new_rp_clients!=NULL) {
+      tr_rp_client_add(trc->rp_clients, new_rp_clients); /* fixes talloc contexts */
+      talloc_steal(trc, trc->rp_clients); /* make sure head is in the right context */
+    }
+  }
+
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* Parse local organizations if present. Returns success if there are none. On failure, the configuration is unreliable. */
+TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg)
+{
+  json_t *jlocorgs=NULL;
+  size_t ii=0;
+
+  jlocorgs=json_object_get(jcfg, "local_organizations");
+  if (jlocorgs==NULL)
+    return TR_CFG_SUCCESS;
+
+  if (!json_is_array(jlocorgs)) {
+    tr_err("tr_cfg_parse_local_orgs: local_organizations is not an array.");
+    return TR_CFG_NOPARSE;
+  }
+
+  for (ii=0; ii<json_array_size(jlocorgs); ii++) {
+    if (tr_cfg_parse_one_local_org(trc, json_array_get(jlocorgs, ii))!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_local_orgs: error parsing local_organization %d.", ii+1);
+      return TR_CFG_NOPARSE;
+    }
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
+static TR_CFG_RC tr_cfg_parse_one_peer_org(TR_CFG *trc, json_t *jporg)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  json_t *jhost=NULL;
+  json_t *jport=NULL;
+  json_t *jgss=NULL;
+  json_t *jfilt=NULL;
+  TRP_PEER *new_peer=NULL;
+  TR_GSS_NAMES *names=NULL;
+  TR_FILTER_SET *filt_set=NULL;
+  TR_CFG_RC rc=TR_CFG_ERROR;
+
+  jhost=json_object_get(jporg, "hostname");
+  jport=json_object_get(jporg, "port");
+  jgss=json_object_get(jporg, "gss_names");
+  jfilt=json_object_get(jporg, "filters");
+
+  if ((jhost==NULL) || (!json_is_string(jhost))) {
+    tr_err("tr_cfg_parse_one_peer_org: hostname not specified or not a string.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if ((jport!=NULL) && (!json_is_number(jport))) {
+    /* note that not specifying the port is allowed, but if set it must be a number */
+    tr_err("tr_cfg_parse_one_peer_org: port is not a number.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if ((jgss==NULL) || (!json_is_array(jgss))) {
+    tr_err("tr_cfg_parse_one_peer_org: gss_names not specified or not an array.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if ((jfilt!=NULL) && (!json_is_object(jfilt))) {
+    tr_err("tr_cfg_parse_one_peer_org: filters is not an object.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  new_peer=trp_peer_new(tmp_ctx);
+  if (new_peer==NULL) {
+    tr_err("tr_cfg_parse_one_peer_org: could not allocate new peer.");
+    rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  trp_peer_set_server(new_peer, json_string_value(jhost)); /* string is strdup'ed in _set_server() */
+  if (jport==NULL)
+    trp_peer_set_port(new_peer, TRP_PORT);
+  else
+    trp_peer_set_port(new_peer, json_integer_value(jport));
+
+  rc = tr_cfg_parse_gss_names(tmp_ctx, jgss, &names);
+  if (rc!=TR_CFG_SUCCESS) {
+    tr_err("tr_cfg_parse_one_peer_org: unable to parse gss names.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+  trp_peer_set_gss_names(new_peer, names);
+
+  if (jfilt) {
+    filt_set=tr_cfg_parse_filters(tmp_ctx, jfilt, &rc);
+    if (rc!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_one_peer_org: unable to parse filters.");
+      rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+    trp_peer_set_filters(new_peer, filt_set);
+  }
+
+  /* success! */
+  trp_ptable_add(trc->peers, new_peer);
+  rc=TR_CFG_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* Parse peer organizations, if present. Returns success if there are none. */
+TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg)
+{
+  json_t *jpeerorgs=NULL;
+  int ii=0;
+
+  jpeerorgs=json_object_get(jcfg, "peer_organizations");
+  if (jpeerorgs==NULL)
+    return TR_CFG_SUCCESS;
+
+  if (!json_is_array(jpeerorgs)) {
+    tr_err("tr_cfg_parse_peer_orgs: peer_organizations is not an array.");
+    return TR_CFG_NOPARSE;
+  }
+
+  for (ii=0; ii<json_array_size(jpeerorgs); ii++) {
+    if (tr_cfg_parse_one_peer_org(trc, json_array_get(jpeerorgs, ii))!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_peer_orgs: error parsing peer_organization %d.", ii+1);
+      return TR_CFG_NOPARSE;
+    }
+  }
+
+  return TR_CFG_SUCCESS;
+}
+
diff --git a/common/tr_config_realms.c b/common/tr_config_realms.c
new file mode 100644 (file)
index 0000000..2f6fa09
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2012-2018, 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 <string.h>
+#include <jansson.h>
+#include <dirent.h>
+#include <talloc.h>
+
+#include <tr_cfgwatch.h>
+#include <tr_comm.h>
+#include <tr_config.h>
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+#include <tr_filter.h>
+#include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc)
+{
+  TR_AAA_SERVER *aaa = NULL;
+  TR_NAME *name=NULL;
+
+  if ((!jaddr) || (!json_is_string(jaddr))) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
+    *rc = TR_CFG_BAD_PARAMS;
+    return NULL;
+  }
+
+  name=tr_new_name(json_string_value(jaddr));
+  if (name==NULL) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating hostname.");
+    *rc = TR_CFG_NOMEM;
+    return NULL;
+  }
+
+  aaa=tr_aaa_server_new(mem_ctx, name);
+  if (aaa==NULL) {
+    tr_free_name(name);
+    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
+    *rc = TR_CFG_NOMEM;
+    return NULL;
+  }
+
+  return aaa;
+}
+
+static TR_AAA_SERVER *tr_cfg_parse_aaa_servers(TALLOC_CTX *mem_ctx, json_t *jaaas, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=NULL;
+  TR_AAA_SERVER *aaa = NULL;
+  TR_AAA_SERVER *temp_aaa = NULL;
+  int i = 0;
+
+  for (i = 0; i < json_array_size(jaaas); i++) {
+    /* rc gets set in here */
+    if (NULL == (temp_aaa = tr_cfg_parse_one_aaa_server(tmp_ctx, json_array_get(jaaas, i), rc))) {
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+    /* TBD -- IPv6 addresses */
+    //    tr_debug("tr_cfg_parse_aaa_servers: Configuring AAA Server: ip_addr = %s.", inet_ntoa(temp_aaa->aaa_server_addr));
+    temp_aaa->next = aaa;
+    aaa = temp_aaa;
+  }
+  tr_debug("tr_cfg_parse_aaa_servers: Finished (rc=%d)", *rc);
+
+  for (temp_aaa=aaa; temp_aaa!=NULL; temp_aaa=temp_aaa->next)
+    talloc_steal(mem_ctx, temp_aaa);
+  talloc_free(tmp_ctx);
+  return aaa;
+}
+
+static TR_APC *tr_cfg_parse_one_apc(TALLOC_CTX *mem_ctx, json_t *japc, TR_CFG_RC *rc)
+{
+  TR_APC *apc=NULL;
+  TR_NAME *name=NULL;
+
+  *rc = TR_CFG_SUCCESS;         /* presume success */
+
+  if ((!japc) || (!rc) || (!json_is_string(japc))) {
+    tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return NULL;
+  }
+
+  apc=tr_apc_new(mem_ctx);
+  if (apc==NULL) {
+    tr_debug("tr_cfg_parse_one_apc: Out of memory.");
+    *rc = TR_CFG_NOMEM;
+    return NULL;
+  }
+
+  name=tr_new_name(json_string_value(japc));
+  if (name==NULL) {
+    tr_debug("tr_cfg_parse_one_apc: No memory for APC name.");
+    tr_apc_free(apc);
+    *rc = TR_CFG_NOMEM;
+    return NULL;
+  }
+  tr_apc_set_id(apc, name); /* apc is now responsible for freeing the name */
+
+  return apc;
+}
+
+TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC *apcs=NULL;
+  TR_APC *new_apc=NULL;
+  int ii=0;
+  TR_CFG_RC call_rc=TR_CFG_ERROR;
+
+  *rc = TR_CFG_SUCCESS;         /* presume success */
+
+  if ((!japcs) || (!rc) || (!json_is_array(japcs))) {
+    tr_debug("tr_cfg_parse_one_apc: Bad parameters.");
+    if (rc)
+      *rc = TR_CFG_BAD_PARAMS;
+    return NULL;
+  }
+
+  for (ii=0; ii<json_array_size(japcs); ii++) {
+    new_apc=tr_cfg_parse_one_apc(tmp_ctx, json_array_get(japcs, ii), &call_rc);
+    if ((call_rc!=TR_CFG_SUCCESS) || (new_apc==NULL)) {
+      tr_debug("tr_cfg_parse_apcs: Error parsing APC %d.", ii+1);
+      *rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+    tr_apc_add(apcs, new_apc);
+  }
+
+  talloc_steal(mem_ctx, apcs);
+  *rc=TR_CFG_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return apcs;
+}
+
+static TR_NAME *tr_cfg_parse_name(TALLOC_CTX *mem_ctx, json_t *jname, TR_CFG_RC *rc)
+{
+  TR_NAME *name=NULL;
+  *rc=TR_CFG_ERROR;
+
+  if ((jname==NULL) || (!json_is_string(jname))) {
+    tr_err("tr_cfg_parse_name: name missing or not a string");
+    *rc=TR_CFG_BAD_PARAMS;
+    name=NULL;
+  } else {
+    name=tr_new_name(json_string_value(jname));
+    if (name==NULL) {
+      tr_err("tr_cfg_parse_name: could not allocate name");
+      *rc=TR_CFG_NOMEM;
+    } else {
+      *rc=TR_CFG_SUCCESS;
+    }
+  }
+  return name;
+}
+
+static int tr_cfg_parse_shared_config(json_t *jsc, TR_CFG_RC *rc)
+{
+  const char *shared=NULL;
+  *rc=TR_CFG_SUCCESS;
+
+  if ((jsc==NULL) ||
+      (!json_is_string(jsc)) ||
+      (NULL==(shared=json_string_value(jsc)))) {
+    *rc=TR_CFG_BAD_PARAMS;
+    return -1;
+  }
+
+  if (0==strcmp(shared, "no"))
+    return 0;
+  else if (0==strcmp(shared, "yes"))
+    return 1;
+
+  /* any other value is an error */
+  tr_debug("tr_cfg_parse_shared_config: invalid shared_config value \"%s\" (should be \"yes\" or \"no\")",
+           shared);
+  *rc=TR_CFG_NOPARSE;
+  return -1;
+}
+
+static TR_REALM_ORIGIN tr_cfg_realm_origin(json_t *jrealm)
+{
+  json_t *jremote=json_object_get(jrealm, "remote");
+  const char *s=NULL;
+
+  if (jremote==NULL)
+    return TR_REALM_LOCAL;
+  if (!json_is_string(jremote)) {
+    tr_warning("tr_cfg_realm_origin: \"remote\" is not a string, assuming this is a local realm.");
+    return TR_REALM_LOCAL;
+  }
+  s=json_string_value(jremote);
+  if (strcasecmp(s, "yes")==0)
+    return TR_REALM_REMOTE_INCOMPLETE;
+  else if (strcasecmp(s, "no")!=0)
+    tr_warning("tr_cfg_realm_origin: \"remote\" is neither 'yes' nor 'no', assuming this is a local realm.");
+
+  return TR_REALM_LOCAL;
+}
+
+/* Parse the identity provider object from a realm and fill in the given TR_IDP_REALM. */
+static TR_CFG_RC tr_cfg_parse_idp(TR_IDP_REALM *idp, json_t *jidp)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_APC *apcs=NULL;
+  TR_AAA_SERVER *aaa=NULL;
+  TR_CFG_RC rc=TR_CFG_ERROR;
+
+  if (jidp==NULL)
+    goto cleanup;
+
+  idp->shared_config=tr_cfg_parse_shared_config(json_object_get(jidp, "shared_config"), &rc);
+  if (rc!=TR_CFG_SUCCESS) {
+    tr_err("tr_cfg_parse_idp: missing or malformed shared_config specification");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  apcs=tr_cfg_parse_apcs(tmp_ctx, json_object_get(jidp, "apcs"), &rc);
+  if ((rc!=TR_CFG_SUCCESS) || (apcs==NULL)) {
+    tr_err("tr_cfg_parse_idp: unable to parse APC");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  aaa=tr_cfg_parse_aaa_servers(idp, json_object_get(jidp, "aaa_servers"), &rc);
+  if (rc!=TR_CFG_SUCCESS) {
+    tr_err("tr_cfg_parse_idp: unable to parse AAA servers");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  tr_debug("tr_cfg_parse_idp: APC=\"%.*s\"",
+           apcs->id->len,
+           apcs->id->buf);
+
+  /* done, fill in the idp structures */
+  idp->apcs=apcs;
+  talloc_steal(idp, apcs);
+  idp->aaa_servers=aaa;
+  rc=TR_CFG_SUCCESS;
+
+cleanup:
+  if (rc!=TR_CFG_SUCCESS) {
+    if (apcs!=NULL)
+      tr_apc_free(apcs);
+    if (aaa!=NULL)
+      tr_aaa_server_free(aaa);
+  }
+
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* parses idp realm */
+static TR_IDP_REALM *tr_cfg_parse_one_idp_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *realm=NULL;
+  TR_CFG_RC call_rc=TR_CFG_ERROR;
+
+  *rc=TR_CFG_ERROR; /* default to error if not set */
+
+  if ((!jrealm) || (!rc)) {
+    tr_err("tr_cfg_parse_one_idp_realm: Bad parameters.");
+    if (rc)
+      *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
+    tr_err("tr_cfg_parse_one_idp_realm: could not allocate idp realm.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  realm->origin=tr_cfg_realm_origin(jrealm);
+  if (realm->origin!=TR_REALM_LOCAL) {
+    tr_debug("tr_cfg_parse_one_idp_realm: realm is remote, should not have full IdP info.");
+    *rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  /* must have a name */
+  realm->realm_id=tr_cfg_parse_name(realm,
+                                    json_object_get(jrealm, "realm"),
+                                    &call_rc);
+  if ((call_rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
+    tr_err("tr_cfg_parse_one_idp_realm: could not parse realm name");
+    *rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+  tr_debug("tr_cfg_parse_one_idp_realm: realm_id=\"%.*s\"",
+           realm->realm_id->len,
+           realm->realm_id->buf);
+
+  call_rc=tr_cfg_parse_idp(realm, json_object_get(jrealm, "identity_provider"));
+  if (call_rc!=TR_CFG_SUCCESS) {
+    tr_err("tr_cfg_parse_one_idp_realm: could not parse identity_provider.");
+    *rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  *rc=TR_CFG_SUCCESS;
+
+cleanup:
+  if (*rc==TR_CFG_SUCCESS)
+    talloc_steal(mem_ctx, realm);
+  else {
+    talloc_free(realm);
+    realm=NULL;
+  }
+
+  talloc_free(tmp_ctx);
+  return realm;
+}
+
+/* Determine whether the realm is an IDP realm */
+static int tr_cfg_is_idp_realm(json_t *jrealm)
+{
+  /* If a realm spec contains an identity_provider, it's an IDP realm. */
+  if (NULL != json_object_get(jrealm, "identity_provider"))
+    return 1;
+  else
+    return 0;
+}
+
+static TR_IDP_REALM *tr_cfg_parse_one_remote_realm(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *realm=talloc(mem_ctx, TR_IDP_REALM);
+
+  *rc=TR_CFG_ERROR; /* default to error if not set */
+
+  if ((!jrealm) || (!rc)) {
+    tr_err("tr_cfg_parse_one_remote_realm: Bad parameters.");
+    if (rc)
+      *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if (NULL==(realm=tr_idp_realm_new(tmp_ctx))) {
+    tr_err("tr_cfg_parse_one_remote_realm: could not allocate idp realm.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  /* must have a name */
+  realm->realm_id=tr_cfg_parse_name(realm,
+                                    json_object_get(jrealm, "realm"),
+                                    rc);
+  if ((*rc!=TR_CFG_SUCCESS) || (realm->realm_id==NULL)) {
+    tr_err("tr_cfg_parse_one_remote_realm: could not parse realm name");
+    *rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+  tr_debug("tr_cfg_parse_one_remote_realm: realm_id=\"%.*s\"",
+           realm->realm_id->len,
+           realm->realm_id->buf);
+
+  realm->origin=tr_cfg_realm_origin(jrealm);
+  *rc=TR_CFG_SUCCESS;
+
+cleanup:
+  if (*rc==TR_CFG_SUCCESS)
+    talloc_steal(mem_ctx, realm);
+  else {
+    talloc_free(realm);
+    realm=NULL;
+  }
+
+  talloc_free(tmp_ctx);
+  return realm;
+}
+
+static int tr_cfg_is_remote_realm(json_t *jrealm)
+{
+  return (tr_cfg_realm_origin(jrealm)!=TR_REALM_LOCAL);
+}
+
+/* Parse any idp realms in the j_realms object. Ignores other realm types. */
+TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_IDP_REALM *realms=NULL;
+  TR_IDP_REALM *new_realm=NULL;
+  json_t *this_jrealm=NULL;
+  int ii=0;
+
+  *rc=TR_CFG_ERROR;
+  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
+    tr_err("tr_cfg_parse_idp_realms: realms not an array");
+    *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  for (ii=0; ii<json_array_size(jrealms); ii++) {
+    this_jrealm=json_array_get(jrealms, ii);
+    if (tr_cfg_is_idp_realm(this_jrealm)) {
+      new_realm=tr_cfg_parse_one_idp_realm(tmp_ctx, this_jrealm, rc);
+      if ((*rc)!=TR_CFG_SUCCESS) {
+        tr_err("tr_cfg_parse_idp_realms: error decoding realm entry %d", ii+1);
+        *rc=TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+      tr_idp_realm_add(realms, new_realm);
+    } else if (tr_cfg_is_remote_realm(this_jrealm)) {
+      new_realm=tr_cfg_parse_one_remote_realm(tmp_ctx, this_jrealm, rc);
+      if ((*rc)!=TR_CFG_SUCCESS) {
+        tr_err("tr_cfg_parse_idp_realms: error decoding remote realm entry %d", ii+1);
+        *rc=TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+      tr_idp_realm_add(realms, new_realm);
+    }
+  }
+
+  *rc=TR_CFG_SUCCESS;
+  talloc_steal(mem_ctx, realms);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return realms;
+}
diff --git a/common/tr_config_rp_clients.c b/common/tr_config_rp_clients.c
new file mode 100644 (file)
index 0000000..960edce
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2012-2018, 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 <string.h>
+#include <jansson.h>
+#include <dirent.h>
+#include <talloc.h>
+
+#include <tr_cfgwatch.h>
+#include <tr_comm.h>
+#include <tr_config.h>
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+#include <tr_filter.h>
+#include <trust_router/tr_constraint.h>
+#include <tr_idp.h>
+#include <tr.h>
+#include <trust_router/trp.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+
+TR_CFG_RC tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_GSS_NAMES **gssn_out)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_GSS_NAMES *gn=NULL;
+  json_t *jname=NULL;
+  size_t ii=0;
+  TR_NAME *name=NULL;
+  TR_CFG_RC rc = TR_CFG_ERROR;
+
+  if (jgss_names==NULL) {
+    tr_err("tr_cfg_parse_gss_names: Bad parameters.");
+    rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if (!json_is_array(jgss_names)) {
+    tr_err("tr_cfg_parse_gss_names: gss_names not an array.");
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  gn=tr_gss_names_new(tmp_ctx);
+  for (ii=0; ii<json_array_size(jgss_names); ii++) {
+    jname=json_array_get(jgss_names, ii);
+    if (!json_is_string(jname)) {
+      tr_err("tr_cfg_parse_gss_names: Encountered non-string gss name.");
+      rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+
+    name=tr_new_name(json_string_value(jname));
+    if (name==NULL) {
+      tr_err("tr_cfg_parse_gss_names: Out of memory allocating gss name.");
+      rc=TR_CFG_NOMEM;
+      goto cleanup;
+    }
+
+    if (tr_gss_names_add(gn, name)!=0) {
+      tr_free_name(name);
+      tr_err("tr_cfg_parse_gss_names: Unable to add gss name to RP client.");
+      rc=TR_CFG_ERROR;
+      goto cleanup;
+    }
+  }
+
+  *gssn_out = gn;
+  talloc_steal(mem_ctx, *gssn_out);
+  rc=TR_CFG_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/* default filter accepts realm and *.realm */
+static TR_FILTER_SET *tr_cfg_default_filters(TALLOC_CTX *mem_ctx, TR_NAME *realm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_FILTER *filt=NULL;
+  TR_FILTER_SET *filt_set=NULL;
+  TR_CONSTRAINT *cons=NULL;
+  TR_NAME *name=NULL;
+  TR_NAME *n_prefix=tr_new_name("*.");
+  TR_NAME *n_rp_realm_1=tr_new_name("rp_realm");
+  TR_NAME *n_rp_realm_2=tr_new_name("rp_realm");
+  TR_NAME *n_domain=tr_new_name("domain");
+  TR_NAME *n_realm=tr_new_name("realm");
+
+
+  if ((realm==NULL) || (rc==NULL)) {
+    tr_debug("tr_cfg_default_filters: invalid arguments.");
+    if (rc!=NULL)
+      *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if ((n_prefix==NULL) ||
+      (n_rp_realm_1==NULL) ||
+      (n_rp_realm_2==NULL) ||
+      (n_domain==NULL) ||
+      (n_realm==NULL)) {
+    tr_debug("tr_cfg_default_filters: unable to allocate names.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  filt=tr_filter_new(tmp_ctx);
+  if (filt==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate filter.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  tr_filter_set_type(filt, TR_FILTER_TYPE_TID_INBOUND);
+  filt->lines[0]=tr_fline_new(filt);
+  if (filt->lines[0]==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate filter line.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  filt->lines[0]->action=TR_FILTER_ACTION_ACCEPT;
+  filt->lines[0]->specs[0]=tr_fspec_new(filt->lines[0]);
+  filt->lines[0]->specs[0]->field=n_rp_realm_1;
+  n_rp_realm_1=NULL; /* we don't own this name any more */
+
+  name=tr_dup_name(realm);
+  if (name==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate realm name.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  tr_fspec_add_match(filt->lines[0]->specs[0], name);
+  name=NULL; /* we no longer own the name */
+
+  /* now do the wildcard name */
+  filt->lines[0]->specs[1]=tr_fspec_new(filt->lines[0]);
+  filt->lines[0]->specs[1]->field=n_rp_realm_2;
+  n_rp_realm_2=NULL; /* we don't own this name any more */
+
+  if (NULL==(name=tr_name_cat(n_prefix, realm))) {
+    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  tr_fspec_add_match(filt->lines[0]->specs[1], name);
+  name=NULL; /* we no longer own the name */
+
+  /* domain constraint */
+  if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
+    tr_debug("tr_cfg_default_filters: could not allocate domain constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  cons->type=n_domain;
+  n_domain=NULL; /* belongs to the constraint now */
+  name=tr_dup_name(realm);
+  if (name==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate realm name for domain constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  cons->matches[0]=name;
+  name=tr_name_cat(n_prefix, realm);
+  if (name==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for domain constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  cons->matches[1]=name;
+  name=NULL;
+  filt->lines[0]->domain_cons=cons;
+
+
+  /* realm constraint */
+  if (NULL==(cons=tr_constraint_new(filt->lines[0]))) {
+    tr_debug("tr_cfg_default_filters: could not allocate realm constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  cons->type=n_realm;
+  n_realm=NULL; /* belongs to the constraint now */
+  name=tr_dup_name(realm);
+  if (name==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate realm name for realm constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  cons->matches[0]=name;
+  name=tr_name_cat(n_prefix, realm);
+  if (name==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate wildcard realm name for realm constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  cons->matches[1]=name;
+  name=NULL;
+  filt->lines[0]->realm_cons=cons;
+
+  /* put the filter in a set */
+  filt_set=tr_filter_set_new(tmp_ctx);
+  if ((filt_set==NULL)||(0!=tr_filter_set_add(filt_set, filt))) {
+    tr_debug("tr_cfg_default_filters: could not allocate filter set.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  talloc_steal(mem_ctx, filt_set);
+
+cleanup:
+  talloc_free(tmp_ctx);
+
+  if (*rc!=TR_CFG_SUCCESS)
+    filt=NULL;
+
+  if (n_prefix!=NULL)
+    tr_free_name(n_prefix);
+  if (n_rp_realm_1!=NULL)
+    tr_free_name(n_rp_realm_1);
+  if (n_rp_realm_2!=NULL)
+    tr_free_name(n_rp_realm_2);
+  if (n_realm!=NULL)
+    tr_free_name(n_realm);
+  if (n_domain!=NULL)
+    tr_free_name(n_domain);
+  if (name!=NULL)
+    tr_free_name(name);
+
+  return filt_set;
+}
+
+/* parses rp client */
+static TR_RP_CLIENT *tr_cfg_parse_one_rp_client(TALLOC_CTX *mem_ctx, json_t *jrealm, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_RP_CLIENT *client=NULL;
+  TR_CFG_RC call_rc=TR_CFG_ERROR;
+  TR_FILTER_SET *new_filts=NULL;
+  TR_NAME *realm=NULL;
+  json_t *jfilt=NULL;
+  json_t *jrealm_id=NULL;
+
+  *rc=TR_CFG_ERROR; /* default to error if not set */
+
+  if ((!jrealm) || (!rc)) {
+    tr_err("tr_cfg_parse_one_rp_client: Bad parameters.");
+    if (rc)
+      *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  if ((NULL==(jrealm_id=json_object_get(jrealm, "realm"))) || (!json_is_string(jrealm_id))) {
+    tr_debug("tr_cfg_parse_one_rp_client: no realm ID found.");
+    *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  tr_debug("tr_cfg_parse_one_rp_client: realm_id=\"%s\"", json_string_value(jrealm_id));
+  realm=tr_new_name(json_string_value(jrealm_id));
+  if (realm==NULL) {
+    tr_err("tr_cfg_parse_one_rp_client: could not allocate realm ID.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  if (NULL==(client=tr_rp_client_new(tmp_ctx))) {
+    tr_err("tr_cfg_parse_one_rp_client: could not allocate rp client.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  call_rc = tr_cfg_parse_gss_names(client, json_object_get(jrealm, "gss_names"), &(client->gss_names));
+
+  if (call_rc!=TR_CFG_SUCCESS) {
+    tr_err("tr_cfg_parse_one_rp_client: could not parse gss_names.");
+    *rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  /* parse filters */
+  jfilt=json_object_get(jrealm, "filters");
+  if (jfilt!=NULL) {
+    new_filts=tr_cfg_parse_filters(tmp_ctx, jfilt, &call_rc);
+    if (call_rc!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_one_rp_client: could not parse filters.");
+      *rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+  } else {
+    tr_debug("tr_cfg_parse_one_rp_client: no filters specified, using default filters.");
+    new_filts= tr_cfg_default_filters(tmp_ctx, realm, &call_rc);
+    if (call_rc!=TR_CFG_SUCCESS) {
+      tr_err("tr_cfg_parse_one_rp_client: could not set default filters.");
+      *rc=TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+  }
+
+  tr_rp_client_set_filters(client, new_filts);
+  *rc=TR_CFG_SUCCESS;
+
+cleanup:
+  if (realm!=NULL)
+    tr_free_name(realm);
+
+  if (*rc==TR_CFG_SUCCESS)
+    talloc_steal(mem_ctx, client);
+  else {
+    talloc_free(client);
+    client=NULL;
+  }
+
+  talloc_free(tmp_ctx);
+  return client;
+}
+
+/* Determine whether the realm is an RP realm */
+static int tr_cfg_is_rp_realm(json_t *jrealm)
+{
+  /* Check that we have a gss name. */
+  if (NULL != json_object_get(jrealm, "gss_names"))
+    return 1;
+  else
+    return 0;
+}
+
+/* Parse any rp clients in the j_realms object. Ignores other realms. */
+TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  TR_RP_CLIENT *clients=NULL;
+  TR_RP_CLIENT *new_client=NULL;
+  json_t *this_jrealm=NULL;
+  int ii=0;
+
+  *rc=TR_CFG_ERROR;
+  if ((jrealms==NULL) || (!json_is_array(jrealms))) {
+    tr_err("tr_cfg_parse_rp_clients: realms not an array");
+    *rc=TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  for (ii=0; ii<json_array_size(jrealms); ii++) {
+    this_jrealm=json_array_get(jrealms, ii);
+    if (tr_cfg_is_rp_realm(this_jrealm)) {
+      new_client=tr_cfg_parse_one_rp_client(tmp_ctx, this_jrealm, rc);
+      if ((*rc)!=TR_CFG_SUCCESS) {
+        tr_err("tr_cfg_parse_rp_clients: error decoding realm entry %d", ii+1);
+        *rc=TR_CFG_NOPARSE;
+        goto cleanup;
+      }
+      tr_rp_client_add(clients, new_client);
+    }
+  }
+
+  *rc=TR_CFG_SUCCESS;
+  talloc_steal(mem_ctx, clients);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return clients;
+}
diff --git a/common/tr_filter_encoders.c b/common/tr_filter_encoders.c
new file mode 100644 (file)
index 0000000..cc22b02
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018 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 <talloc.h>
+#include <jansson.h>
+
+#include <tr_filter.h>
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+
+typedef json_t *(ITEM_ENCODER_FUNC)(void *);
+
+static json_t *items_to_json_array(void *items[], ITEM_ENCODER_FUNC *item_encoder, size_t max_items)
+{
+  size_t ii;
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+
+  if (jarray == NULL)
+    goto cleanup;
+
+  for (ii=0; ii<max_items; ii++) {
+    if (items[ii] != NULL)
+      ARRAY_APPEND_OR_FAIL(jarray, item_encoder(items[ii]));
+  }
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  return retval;
+}
+
+static json_t *tr_fspec_to_json(TR_FSPEC *fspec)
+{
+  json_t *fspec_json = NULL;
+  json_t *retval = NULL;
+
+  fspec_json = json_object();
+  if (fspec_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(fspec_json, "field",
+                     tr_name_to_json_string(fspec->field));
+  OBJECT_SET_OR_FAIL(fspec_json, "matches",
+                     items_to_json_array((void **)fspec->match,
+                                         (ITEM_ENCODER_FUNC *) tr_name_to_json_string,
+                                         TR_MAX_FILTER_SPEC_MATCHES));
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = fspec_json;
+  json_incref(retval);
+
+cleanup:
+  if (fspec_json)
+    json_decref(fspec_json);
+  return retval;
+}
+
+static json_t *tr_fline_to_json(TR_FLINE *fline)
+{
+  json_t *fline_json = NULL;
+  json_t *retval = NULL;
+
+  fline_json = json_object();
+  if (fline_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(fline_json, "action",
+                     json_string( (fline->action == TR_FILTER_ACTION_ACCEPT) ? "accept" : "reject"));
+  OBJECT_SET_OR_FAIL(fline_json, "specs",
+                     items_to_json_array((void **)fline->specs,
+                                         (ITEM_ENCODER_FUNC *) tr_fspec_to_json,
+                                         TR_MAX_FILTER_SPECS));
+  if (fline->realm_cons) {
+    OBJECT_SET_OR_FAIL(fline_json, "realm_constraints",
+                       items_to_json_array((void **) fline->realm_cons->matches,
+                                           (ITEM_ENCODER_FUNC *) tr_name_to_json_string,
+                                           TR_MAX_CONST_MATCHES));
+  }
+  if (fline->domain_cons) {
+    OBJECT_SET_OR_FAIL(fline_json, "domain_constraints",
+                       items_to_json_array((void **) fline->domain_cons->matches,
+                                           (ITEM_ENCODER_FUNC *) tr_name_to_json_string,
+                                           TR_MAX_CONST_MATCHES));
+  }
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = fline_json;
+  json_incref(retval);
+
+cleanup:
+  if (fline_json)
+    json_decref(fline_json);
+  return retval;
+}
+
+json_t *tr_filter_set_to_json(TR_FILTER_SET *filter_set)
+{
+  json_t *fset_json = NULL;
+  json_t *retval = NULL;
+  TR_FILTER *filt = NULL;
+  TR_FILTER_TYPE *filt_type = NULL;
+  TR_FILTER_TYPE types[] = {
+      TR_FILTER_TYPE_TID_INBOUND,
+      TR_FILTER_TYPE_TRP_INBOUND,
+      TR_FILTER_TYPE_TRP_OUTBOUND,
+      TR_FILTER_TYPE_UNKNOWN /* list terminator */
+  };
+
+  fset_json = json_object();
+  if (fset_json == NULL)
+    goto cleanup;
+
+  for (filt_type = types; *filt_type != TR_FILTER_TYPE_UNKNOWN; filt_type++) {
+    filt = tr_filter_set_get(filter_set, *filt_type);
+    if (filt) {
+      OBJECT_SET_OR_FAIL(fset_json, tr_filter_type_to_string(*filt_type),
+                         items_to_json_array((void **)filt->lines,
+                                             (ITEM_ENCODER_FUNC *) tr_fline_to_json,
+                                             TR_MAX_FILTER_LINES));
+    }
+  }
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = fset_json;
+  json_incref(retval);
+
+cleanup:
+  if (fset_json)
+    json_decref(fset_json);
+  return retval;
+}
+
index 7fa6876..e7902ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, JANET(UK)
+ * Copyright (c) 2018, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 
 #include <talloc.h>
+#include <gssapi.h>
+#include <string.h>
 
+#include <tr_msg.h>
+#include <tr_debug.h>
+#include <gsscon.h>
 #include <tr_gss.h>
 
-static int tr_gss_names_destructor(void *obj)
+/**
+ * tr_gss.c - GSS connection handler
+ *
+ * The chief entry point to this module is tr_gss_handle_connection(). This
+ * function accepts an incoming socket connection, runs the GSS authorization
+ * and authentication process, accepts a request, processes it, then sends
+ * the reply and returns without closing the connection.
+ *
+ * Callers need to provide two callbacks, each with a cookie for passing
+ * custom data to the callback.
+ *
+ *   * TR_GSS_AUTH_FN auth_cb: Authorization callback
+ *     - This callback is used during the GSS auth process to determine whether
+ *       a credential should be authorized to connect.
+ *
+ *   * TR_GSS_HANDLE_REQ_FN req_cb: Request handler callback
+ *     - After auth, this callback is passed the string form of the incoming request.
+ *       It should process the request and return a string form of the outgoing
+ *       response, if any.
+ */
+
+typedef struct tr_gss_cookie {
+  TR_GSS_AUTH_FN *auth_cb;
+  void *auth_cookie;
+} TR_GSS_COOKIE;
+
+static int tr_gss_auth_cb(gss_name_t clientName, gss_buffer_t displayName, void *data)
 {
-  TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES);
-  int ii=0;
+  TR_GSS_COOKIE *cookie = talloc_get_type_abort(data, TR_GSS_COOKIE);
+  TR_NAME name ={(char *) displayName->value, (int) displayName->length};
+  int result=0;
 
-  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
-    if (gss_names->names[ii]!=NULL)
-      tr_free_name(gss_names->names[ii]);
+  tr_debug("tr_gss_auth_cb: Calling auth handler for %.*s.", name.len, name.buf);
+  if (cookie->auth_cb(clientName, &name, cookie->auth_cookie)) {
+    tr_debug("tr_gss_auth_cb: client '%.*s' denied authorization.", name.len, name.buf);
+    result=EACCES; /* denied */
   }
-  return 0;
+
+  return result;
 }
-TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx)
+
+
+/**
+ * Handle GSS authentication and authorization
+ *
+ * @param conn connection file descriptor
+ * @param acceptor_service name of acceptor to present to initiator
+ * @param acceptor_hostname hostname of acceptor to present to initiator
+ * @param gssctx GSS context
+ * @param auth_cb authorization callback
+ * @param auth_cookie generic data to pass to the authorization callback
+ * @return 0 on successful auth, 1 on disallowed auth, -1 on error
+ */
+static int tr_gss_auth_connection(int conn,
+                                  const char *acceptor_service,
+                                  const char *acceptor_hostname,
+                                  gss_ctx_id_t *gssctx,
+                                  TR_GSS_AUTH_FN auth_cb,
+                                  void *auth_cookie)
 {
-  TR_GSS_NAMES *gn=talloc(mem_ctx, TR_GSS_NAMES);
-  int ii=0;
+  int rc = 0;
+  int auth, autherr = 0;
+  gss_buffer_desc nameBuffer = {0, NULL};
+  TR_GSS_COOKIE *cookie = NULL;
 
-  if (gn!=NULL) {
-    for (ii=0; ii<TR_MAX_GSS_NAMES; ii++)
-      gn->names[ii]=NULL;
-    talloc_set_destructor((void *)gn, tr_gss_names_destructor);
+  nameBuffer.value = talloc_asprintf(NULL, "%s@%s", acceptor_service, acceptor_hostname);
+  if (nameBuffer.value == NULL) {
+    tr_err("tr_gss_auth_connection: Error allocating acceptor name.");
+    return -1;
   }
-  return gn;
-}
+  nameBuffer.length = strlen(nameBuffer.value);
 
-void tr_gss_names_free(TR_GSS_NAMES *gn)
-{
-  talloc_free(gn);
+  /* Set up for the auth callback. There are two layers of callbacks here: we
+   * use our own, which handles gsscon interfacing and calls the auth_cb parameter
+   * to do the actual auth. Store the auth_cb information in a metacookie. */
+  cookie = talloc(NULL, TR_GSS_COOKIE);
+  cookie->auth_cb=auth_cb;
+  cookie->auth_cookie=auth_cookie;
+
+  /* Now call gsscon with *our* auth callback and cookie */
+  tr_debug("tr_gss_auth_connection: Beginning passive authentication as %.*s",
+           nameBuffer.length, nameBuffer.value);
+  rc = gsscon_passive_authenticate(conn, nameBuffer, gssctx, tr_gss_auth_cb, cookie);
+  talloc_free(cookie);
+  talloc_free(nameBuffer.value);
+  if (rc) {
+    tr_debug("tr_gss_auth_connection: Error from gsscon_passive_authenticate(), rc = %d.", rc);
+    return -1;
+  }
+
+  tr_debug("tr_gss_auth_connection: Authentication succeeded, now authorizing.");
+  rc = gsscon_authorize(*gssctx, &auth, &autherr);
+  if (rc) {
+    tr_debug("tr_gss_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.",
+             rc, autherr);
+    return -1;
+  }
+
+  if (auth)
+    tr_debug("tr_gss_auth_connection: Connection authenticated, conn = %d.", conn);
+  else
+    tr_debug("tr_gss_auth_connection: Authentication failed, conn %d.", conn);
+
+  return !auth;
 }
 
-/* returns 0 on success */
-int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new)
+/**
+ * Read a request from the GSS connection
+ *
+ * @param mem_ctx talloc context for the result
+ * @param conn file descriptor for the connection
+ * @param gssctx GSS context
+ * @return talloc'ed string containing the request, or null on error
+ */
+static char *tr_gss_read_req(TALLOC_CTX *mem_ctx, int conn, gss_ctx_id_t gssctx)
 {
-  int ii=0;
+  int err;
+  char *retval = NULL;
+  char *buf = NULL;
+  size_t buflen = 0;
 
-  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
-    if (gn->names[ii]==NULL)
-      break;
+  err = gsscon_read_encrypted_token(conn, gssctx, &buf, &buflen);
+  if (err || (buf == NULL)) {
+    if (buf)
+      free(buf);
+    tr_debug("tr_gss_read_req: Error reading from connection, rc=%d", err);
+    return NULL;
   }
-  if (ii!=TR_MAX_GSS_NAMES) {
-    gn->names[ii]=new;
-    return 0;
-  } else
-    return -1;
+
+  tr_debug("tr_gss_read_req: Read %u bytes.", (unsigned) buflen);
+
+  // get a talloc'ed version, guaranteed to have a null termination
+  retval = talloc_asprintf(mem_ctx, "%.*s", (int) buflen, buf);
+  free(buf);
+
+  return retval;
 }
 
-int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name)
+/**
+ * Write a response to the GSS connection
+ *
+ * @param conn file descriptor for the connection
+ * @param gssctx GSS context
+ * @param resp encoded response string to send
+ * @return 0 on success, -1 on error
+ */
+static int tr_gss_write_resp(int conn, gss_ctx_id_t gssctx, const char *resp)
 {
-  int ii=0;
-
-  if (!gn)
-    return 0;
+  int err = 0;
 
-  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
-    if ((gn->names[ii]!=NULL) &&
-        (0==tr_name_cmp(gn->names[ii], name)))
-      return 1;
+  /* Send the response over the connection */
+  err = gsscon_write_encrypted_token (conn, gssctx, resp, strlen(resp) + 1);
+  if (err) {
+    tr_debug("tr_gss_send_response: Error sending response over connection, rc=%d.", err);
+    return -1;
   }
   return 0;
 }
 
-/* iterators */
-TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx)
+/**
+ * Handle a request/response connection
+ *
+ * Authorizes/authenticates the connection, then reads a response, passes that to a
+ * callback to get a response, sends that, then returns.
+ *
+ * @param conn connection file descriptor
+ * @param acceptor_service acceptor name to present
+ * @param acceptor_hostname acceptor hostname to present
+ * @param auth_cb callback for authorization
+ * @param auth_cookie cookie for the auth_cb
+ * @param req_cb callback to handle the request and produce the response
+ * @param req_cookie cookie for the req_cb
+ */
+void tr_gss_handle_connection(int conn,
+                              const char *acceptor_service,
+                              const char *acceptor_hostname,
+                              TR_GSS_AUTH_FN auth_cb,
+                              void *auth_cookie,
+                              TR_GSS_HANDLE_REQ_FN req_cb,
+                              void *req_cookie)
 {
-  TR_GSS_NAMES_ITER *iter=talloc(mem_ctx, TR_GSS_NAMES_ITER);
-  if (iter!=NULL) {
-    iter->gn=NULL;
-    iter->ii=0;
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
+  char *req_str = NULL;
+  size_t req_len = 0;
+  TR_MSG *req_msg = NULL;
+  TR_MSG *resp_msg = NULL;
+  char *resp_str = NULL;
+
+  tr_debug("tr_gss_handle_connection: Attempting to accept %s connection on fd %d.",
+           acceptor_service, conn);
+
+  if (tr_gss_auth_connection(conn,
+                             acceptor_service,
+                             acceptor_hostname,
+                             &gssctx,
+                             auth_cb,
+                             auth_cookie)) {
+    tr_notice("tr_gss_handle_connection: Error authorizing connection.");
+    goto cleanup;
   }
-  return iter;
-}
 
-TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn)
-{
-  iter->gn=gn;
-  iter->ii=-1;
-  return tr_gss_names_iter_next(iter);
-}
+  tr_debug("tr_gss_handle_connection: Connection authorized");
 
-TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter)
-{
-  for (iter->ii++;
-       (iter->ii < TR_MAX_GSS_NAMES) && (iter->gn->names[iter->ii]==NULL);
-       iter->ii++) { }
-
-  if (iter->ii<TR_MAX_GSS_NAMES)
-    return iter->gn->names[iter->ii];
-  
-  return NULL;
-}
+  // TODO: should there be a timeout on this?
+  do {
+    /* continue until an error breaks us out */
+    // try to read a request
+    req_str = tr_gss_read_req(tmp_ctx, conn, gssctx);
 
-void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter)
-{
-  talloc_free(iter);
+    if (req_str == NULL) {
+      // an error occurred, give up
+      tr_notice("tr_gss_handle_connection: Error reading request");
+      goto cleanup;
+    }
+
+    req_len = strlen(req_str);
+
+    /* If we got no characters, we will loop again. Free the empty response for the next loop. */
+    if (req_len == 0)
+      talloc_free(req_str);
+
+  } while (req_len == 0);
+
+  /* Decode the request */
+  req_msg = tr_msg_decode(tmp_ctx, req_str, req_len);
+  if (req_msg == NULL) {
+    tr_notice("tr_gss_handle_connection: Error decoding response");
+    goto cleanup;
+  }
+
+  /* Hand off the request for processing and get the response */
+  resp_msg = req_cb(tmp_ctx, req_msg, req_cookie);
+
+  if (resp_msg == NULL) {
+    // no response, clean up
+    goto cleanup;
+  }
+
+  /* Encode the response */
+  resp_str = tr_msg_encode(tmp_ctx, resp_msg);
+  if (resp_str == NULL) {
+    /* We apparently can't encode a response, so just return */
+    tr_err("tr_gss_handle_connection: Error encoding response");
+    goto cleanup;
+  }
+
+  // send the response
+  if (tr_gss_write_resp(conn, gssctx, resp_str)) {
+    tr_err("tr_gss_handle_connection: Error writing response");
+    goto cleanup;
+  }
+
+cleanup:
+  talloc_free(tmp_ctx);
 }
diff --git a/common/tr_gss_client.c b/common/tr_gss_client.c
new file mode 100644 (file)
index 0000000..db9f380
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2012, 2014-2018, 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 <talloc.h>
+
+#include <trust_router/tr_dh.h>
+#include <tr_msg.h>
+#include <gsscon.h>
+#include <tr_debug.h>
+#include <tr_gss_client.h>
+
+static int tr_gssc_destructor(void *obj)
+{
+  TR_GSSC_INSTANCE *tr_gssc=talloc_get_type_abort(obj, TR_GSSC_INSTANCE);
+  if (NULL!=tr_gssc) {
+    if (NULL!=tr_gssc->client_dh)
+      tr_destroy_dh_params(tr_gssc->client_dh);
+  }
+  return 0;
+}
+
+TR_GSSC_INSTANCE *tr_gssc_instance_new(TALLOC_CTX *mem_ctx)
+{
+  TR_GSSC_INSTANCE *gssc=talloc(NULL, TR_GSSC_INSTANCE);
+  if (gssc != NULL) {
+    gssc->service_name = NULL;
+    gssc->client_dh = NULL;
+    gssc->conn = -1;
+    gssc->gss_ctx = talloc(gssc, gss_ctx_id_t);
+    if (gssc->gss_ctx == NULL) {
+      talloc_free(gssc); /* before the destructor is set */
+      return NULL;
+    }
+    talloc_set_destructor((void *)gssc, tr_gssc_destructor);
+  }
+  return gssc;
+}
+
+void tr_gssc_instance_free(TR_GSSC_INSTANCE *tr_gssc)
+{
+  talloc_free(tr_gssc);
+}
+
+/**
+ * Open a connection to the requested server:port
+ *
+ * @param gssc client instance
+ * @param server server name/address
+ * @param port TCP port to connect
+ * @return 0 on success, -1 on failure
+ */
+int tr_gssc_open_connection(TR_GSSC_INSTANCE *gssc, const char *server, unsigned int port)
+{
+  tr_debug("tr_gssc_open_connection: opening connection to %s:%d", server, port);
+  if (0 != gsscon_connect(server, port, gssc->service_name, &(gssc->conn), gssc->gss_ctx))
+    return -1;
+
+  return 0; /* success */
+}
+
+/**
+ * Send a request message and retrieve a response message
+ *
+ * @param mem_ctx
+ * @param gssc
+ * @param req_msg
+ * @return decoded message, or null on error
+ */
+TR_MSG *tr_gssc_exchange_msgs(TALLOC_CTX *mem_ctx, TR_GSSC_INSTANCE *gssc, TR_MSG *req_msg)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  char *req_buf = NULL;
+  char *resp_buf = NULL;
+  size_t resp_buflen = 0;
+  TR_MSG *resp_msg = NULL; /* this is the return value */
+  int err;
+
+  /* Validate inputs */
+  if ((gssc == NULL) || (req_msg == NULL))
+    goto cleanup;
+
+  /* Encode the request into a json string */
+  if (!(req_buf = tr_msg_encode(tmp_ctx, req_msg))) {
+    tr_err("tr_gssc_exchange_msgs: Error encoding request message.\n");
+    goto cleanup;
+  }
+
+  tr_debug( "tr_gssc_exchange_msgs: Sending request message:\n%s\n", req_buf);
+
+  /* Send the request over the connection */
+  err = gsscon_write_encrypted_token(gssc->conn, *(gssc->gss_ctx), req_buf, strlen(req_buf));
+  if (err) {
+    tr_err( "tr_gssc_exchange_msgs: Error sending request.\n");
+    goto cleanup;
+  }
+
+  /* Read the response from the connection */
+  /* TBD -- timeout? */
+  if (gsscon_read_encrypted_token(gssc->conn, *(gssc->gss_ctx), &resp_buf, &resp_buflen))
+    goto cleanup;
+
+  tr_debug( "tr_gssc_exchange_msgs: Response Received (%u bytes).\n%s\n", (unsigned) resp_buflen, resp_buf);
+  resp_msg = tr_msg_decode(mem_ctx, resp_buf, resp_buflen);
+  free(resp_buf);
+
+  if (resp_msg == NULL) {
+    tr_err( "tr_gssc_exchange_msgs: Error decoding response.\n");
+    goto cleanup;
+  }
+
+  /* If we get here, then we decoded the message and resp_msg is not null. Nothing more to do. */
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return resp_msg;
+}
+
+DH * tr_gssc_get_dh(TR_GSSC_INSTANCE *inst)
+{
+  return inst->client_dh;
+}
+
+DH *tr_gssc_set_dh(TR_GSSC_INSTANCE *inst, DH *dh)
+{
+  inst->client_dh = dh;
+  return dh;
+}
diff --git a/common/tr_gss_names.c b/common/tr_gss_names.c
new file mode 100644 (file)
index 0000000..df15202
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * 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 <talloc.h>
+
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+
+static int tr_gss_names_destructor(void *obj)
+{
+  TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES);
+  int ii=0;
+
+  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
+    if (gss_names->names[ii]!=NULL)
+      tr_free_name(gss_names->names[ii]);
+  }
+  return 0;
+}
+TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx)
+{
+  TR_GSS_NAMES *gn=talloc(mem_ctx, TR_GSS_NAMES);
+  int ii=0;
+
+  if (gn!=NULL) {
+    for (ii=0; ii<TR_MAX_GSS_NAMES; ii++)
+      gn->names[ii]=NULL;
+    talloc_set_destructor((void *)gn, tr_gss_names_destructor);
+  }
+  return gn;
+}
+
+void tr_gss_names_free(TR_GSS_NAMES *gn)
+{
+  talloc_free(gn);
+}
+
+/* returns 0 on success */
+int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new)
+{
+  int ii=0;
+
+  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
+    if (gn->names[ii]==NULL)
+      break;
+  }
+  if (ii!=TR_MAX_GSS_NAMES) {
+    gn->names[ii]=new;
+    return 0;
+  } else
+    return -1;
+}
+
+/**
+ * Create a duplicate GSS names struct
+ *
+ * @param mem_ctx
+ * @param orig
+ * @return
+ */
+TR_GSS_NAMES *tr_gss_names_dup(TALLOC_CTX *mem_ctx, TR_GSS_NAMES *orig)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_GSS_NAMES *new = tr_gss_names_new(tmp_ctx);
+  TR_GSS_NAMES_ITER *iter = tr_gss_names_iter_new(tmp_ctx);
+  TR_NAME *this = NULL;
+
+  if ( !orig || !new || !iter ) {
+    talloc_free(tmp_ctx);
+    return NULL;
+  }
+  this = tr_gss_names_iter_first(iter, orig);
+  while (this) {
+    if (tr_gss_names_add(new, tr_dup_name(this)) != 0) {
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+    this = tr_gss_names_iter_next(iter);
+  }
+  /* success */
+  talloc_steal(mem_ctx, new);
+  return new;
+}
+int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name)
+{
+  int ii=0;
+
+  if (!gn)
+    return 0;
+
+  for (ii=0; ii<TR_MAX_GSS_NAMES; ii++) {
+    if ((gn->names[ii]!=NULL) &&
+        (0==tr_name_cmp(gn->names[ii], name)))
+      return 1;
+  }
+  return 0;
+}
+
+/* iterators */
+TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx)
+{
+  TR_GSS_NAMES_ITER *iter=talloc(mem_ctx, TR_GSS_NAMES_ITER);
+  if (iter!=NULL) {
+    iter->gn=NULL;
+    iter->ii=0;
+  }
+  return iter;
+}
+
+TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn)
+{
+  iter->gn=gn;
+  iter->ii=-1;
+  return tr_gss_names_iter_next(iter);
+}
+
+TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter)
+{
+  for (iter->ii++;
+       (iter->ii < TR_MAX_GSS_NAMES) && (iter->gn->names[iter->ii]==NULL);
+       iter->ii++) { }
+
+  if (iter->ii<TR_MAX_GSS_NAMES)
+    return iter->gn->names[iter->ii];
+  
+  return NULL;
+}
+
+void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+json_t *tr_gss_names_to_json_array(TR_GSS_NAMES *gss_names)
+{
+  TR_GSS_NAMES_ITER *iter = tr_gss_names_iter_new(NULL);
+  json_t *jarray = json_array();
+  TR_NAME *name = tr_gss_names_iter_first(iter, gss_names);
+  while (name) {
+    json_array_append_new(jarray, tr_name_to_json_string(name));
+    name = tr_gss_names_iter_next(iter);
+  }
+  tr_gss_names_iter_free(iter);
+  return jarray;
+}
+
index 747397b..50a9330 100644 (file)
@@ -259,7 +259,7 @@ TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove)
   return head;
 }
 
-static int tr_idp_realm_apc_count(TR_IDP_REALM *idp)
+int tr_idp_realm_apc_count(TR_IDP_REALM *idp)
 {
   int ii=0;
   TR_APC *apc=idp->apcs;
@@ -270,7 +270,7 @@ static int tr_idp_realm_apc_count(TR_IDP_REALM *idp)
   return ii;
 }
 
-static int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp)
+int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp)
 {
   int ii=0;
   TR_AAA_SERVER *aaa=idp->aaa_servers;
@@ -281,84 +281,6 @@ static int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp)
   return ii;
 }
 
-static char *tr_aaa_server_to_str(TALLOC_CTX *mem_ctx, TR_AAA_SERVER *aaa)
-{
-  return talloc_strndup(mem_ctx, aaa->hostname->buf, aaa->hostname->len);
-}
-
-char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp)
-{
-  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  char **s_aaa=NULL, *aaa_servers=NULL;
-  char **s_apc=NULL, *apcs=NULL;
-  int ii=0, aaa_servers_strlen=0, apcs_strlen=0;
-  int n_aaa_servers=tr_idp_realm_aaa_server_count(idp);
-  int n_apcs=tr_idp_realm_apc_count(idp);
-  TR_AAA_SERVER *aaa=NULL;
-  TR_APC *apc=NULL;
-  char *result=NULL;
-
-  /* get the AAA servers */
-  if (n_aaa_servers<=0)
-    aaa_servers=talloc_strdup(tmp_ctx, "");
-  else {
-    s_aaa=talloc_array(tmp_ctx, char *, n_aaa_servers);
-    for (aaa=idp->aaa_servers,ii=0; aaa!=NULL; aaa=aaa->next,ii++) {
-      s_aaa[ii]=tr_aaa_server_to_str(s_aaa, aaa);
-      aaa_servers_strlen+=strlen(s_aaa[ii]);
-    }
-
-    /* add space for comma-space separators */
-    aaa_servers_strlen+=2*(n_aaa_servers-1);
-
-    aaa_servers=talloc_array(tmp_ctx, char, aaa_servers_strlen+1);
-    aaa_servers[0]='\0';
-    for (ii=0; ii<n_aaa_servers; ii++) {
-      strcat(aaa_servers, s_aaa[ii]);
-      if (ii<(n_aaa_servers-1))
-        strcat(aaa_servers, ", ");
-    }
-    talloc_free(s_aaa);
-  }
-
-  /* get the APCs */
-  if (n_apcs<=0)
-    apcs=talloc_strdup(tmp_ctx, "");
-  else {
-    s_apc=talloc_array(tmp_ctx, char *, n_apcs);
-    for (apc=idp->apcs,ii=0; apc!=NULL; apc=apc->next,ii++) {
-      s_apc[ii]=tr_apc_to_str(s_apc, apc);
-      apcs_strlen+=strlen(s_apc[ii]);
-    }
-
-    /* add space for comma-space separators */
-    apcs_strlen+=2*(n_apcs-1);
-
-    apcs=talloc_array(tmp_ctx, char, apcs_strlen+1);
-    apcs[0]='\0';
-    for (ii=0; ii<n_apcs; ii++) {
-      strcat(apcs, s_apc[ii]);
-      if (ii<(n_apcs-1))
-        strcat(apcs, ", ");
-    }
-    talloc_free(s_apc);
-  }
-
-  result=talloc_asprintf(mem_ctx,
-                         "IDP realm: \"%.*s\""
-                         "  shared: %s"
-                         "  local: %s"
-                         "  AAA servers: %s"
-                         "  APCs: %s",
-                         idp->realm_id->len, idp->realm_id->buf,
-                         (idp->shared_config)?"yes":"no",
-                         (idp->origin==TR_REALM_LOCAL)?"yes":"no",
-                         aaa_servers,
-                         apcs);
-  talloc_free(tmp_ctx);
-  return result;
-}
-
 void tr_idp_realm_incref(TR_IDP_REALM *realm)
 {
   realm->refcount++;
diff --git a/common/tr_idp_encoders.c b/common/tr_idp_encoders.c
new file mode 100644 (file)
index 0000000..fec129a
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2012-2018, 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 <talloc.h>
+#include <time.h>
+#include <jansson.h>
+
+#include <tr_name_internal.h>
+#include <tr_idp.h>
+#include <tr_config.h>
+#include <tr_debug.h>
+
+static char *tr_aaa_server_to_str(TALLOC_CTX *mem_ctx, TR_AAA_SERVER *aaa)
+{
+  return talloc_strndup(mem_ctx, aaa->hostname->buf, aaa->hostname->len);
+}
+
+
+char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  char **s_aaa=NULL, *aaa_servers=NULL;
+  char **s_apc=NULL, *apcs=NULL;
+  int ii=0, aaa_servers_strlen=0, apcs_strlen=0;
+  int n_aaa_servers=tr_idp_realm_aaa_server_count(idp);
+  int n_apcs=tr_idp_realm_apc_count(idp);
+  TR_AAA_SERVER *aaa=NULL;
+  TR_APC *apc=NULL;
+  char *result=NULL;
+
+  /* get the AAA servers */
+  if (n_aaa_servers<=0)
+    aaa_servers=talloc_strdup(tmp_ctx, "");
+  else {
+    s_aaa=talloc_array(tmp_ctx, char *, n_aaa_servers);
+    for (aaa=idp->aaa_servers,ii=0; aaa!=NULL; aaa=aaa->next,ii++) {
+      s_aaa[ii]=tr_aaa_server_to_str(s_aaa, aaa);
+      aaa_servers_strlen+=strlen(s_aaa[ii]);
+    }
+
+    /* add space for comma-space separators */
+    aaa_servers_strlen+=2*(n_aaa_servers-1);
+
+    aaa_servers=talloc_array(tmp_ctx, char, aaa_servers_strlen+1);
+    aaa_servers[0]='\0';
+    for (ii=0; ii<n_aaa_servers; ii++) {
+      strcat(aaa_servers, s_aaa[ii]);
+      if (ii<(n_aaa_servers-1))
+        strcat(aaa_servers, ", ");
+    }
+    talloc_free(s_aaa);
+  }
+
+  /* get the APCs */
+  if (n_apcs<=0)
+    apcs=talloc_strdup(tmp_ctx, "");
+  else {
+    s_apc=talloc_array(tmp_ctx, char *, n_apcs);
+    for (apc=idp->apcs,ii=0; apc!=NULL; apc=apc->next,ii++) {
+      s_apc[ii]=tr_apc_to_str(s_apc, apc);
+      apcs_strlen+=strlen(s_apc[ii]);
+    }
+
+    /* add space for comma-space separators */
+    apcs_strlen+=2*(n_apcs-1);
+
+    apcs=talloc_array(tmp_ctx, char, apcs_strlen+1);
+    apcs[0]='\0';
+    for (ii=0; ii<n_apcs; ii++) {
+      strcat(apcs, s_apc[ii]);
+      if (ii<(n_apcs-1))
+        strcat(apcs, ", ");
+    }
+    talloc_free(s_apc);
+  }
+
+  result=talloc_asprintf(mem_ctx,
+                         "IDP realm: \"%.*s\""
+                         "  shared: %s"
+                         "  local: %s"
+                         "  AAA servers: %s"
+                         "  APCs: %s",
+                         idp->realm_id->len, idp->realm_id->buf,
+                         (idp->shared_config)?"yes":"no",
+                         (idp->origin==TR_REALM_LOCAL)?"yes":"no",
+                         aaa_servers,
+                         apcs);
+  talloc_free(tmp_ctx);
+  return result;
+}
+
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+static json_t *tr_apcs_to_json(TR_APC *apcs)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_APC_ITER *iter = tr_apc_iter_new(NULL);
+  TR_APC *apc = NULL;
+
+  if ((jarray == NULL) || (iter == NULL))
+    goto cleanup;
+
+  apc = tr_apc_iter_first(iter, apcs);
+  while (apc) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_name_to_json_string(tr_apc_get_id(apc)));
+    apc = tr_apc_iter_next(iter);
+  }
+
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  return retval;
+}
+
+static json_t *tr_aaa_server_to_json(TR_AAA_SERVER *aaa)
+{
+  char *hostname = tr_name_strdup(aaa->hostname);
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (hostname == NULL)
+    return NULL;
+
+  s = talloc_asprintf(NULL, "%s:%d", hostname, TID_PORT);
+  if (s) {
+    jstr = json_string(s);
+    talloc_free(s);
+  }
+  return jstr;
+}
+
+static json_t *tr_aaa_servers_to_json(TR_AAA_SERVER *aaas)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_AAA_SERVER_ITER *iter = tr_aaa_server_iter_new(NULL);
+  TR_AAA_SERVER *aaa = NULL;
+
+  if ((jarray == NULL) || (iter == NULL))
+    goto cleanup;
+
+  aaa = tr_aaa_server_iter_first(iter, aaas);
+  while (aaa) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_aaa_server_to_json(aaa));
+    aaa = tr_aaa_server_iter_next(iter);
+  }
+
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  return retval;
+}
+
+static json_t *tr_idp_realm_to_json(TR_IDP_REALM *idp)
+{
+  json_t *idp_json = json_object();
+  json_t *retval = NULL;
+
+  if (idp_json == NULL)
+    goto cleanup;
+
+
+  /* success */
+  retval = idp_json;
+  json_incref(retval);
+
+  OBJECT_SET_OR_FAIL(idp_json, "realm",
+                     tr_name_to_json_string(tr_idp_realm_get_id(idp)));
+  OBJECT_SET_OR_FAIL(idp_json, "discovered",
+                     json_boolean(idp->origin == TR_REALM_DISCOVERED));
+  OBJECT_SET_OR_FAIL(idp_json, "apcs",
+                     tr_apcs_to_json(tr_idp_realm_get_apcs(idp)));
+  OBJECT_SET_OR_FAIL(idp_json, "aaa_servers",
+                     tr_aaa_servers_to_json(idp->aaa_servers));
+  OBJECT_SET_OR_FAIL(idp_json, "shared_config",
+                     json_boolean(idp->shared_config));
+cleanup:
+  if (idp_json)
+    json_decref(idp_json);
+
+  return retval;
+}
+
+json_t *tr_idp_realms_to_json(TR_IDP_REALM *idps)
+{
+  {
+    json_t *jarray = json_array();
+    json_t *retval = NULL;
+    TR_IDP_REALM *this = NULL;
+
+    if (jarray == NULL)
+      goto cleanup;
+
+    for (this=idps; this != NULL; this=this->next)
+      ARRAY_APPEND_OR_FAIL(jarray, tr_idp_realm_to_json(this));
+
+    /* success */
+    retval = jarray;
+    json_incref(retval);
+
+  cleanup:
+    if (jarray)
+      json_decref(jarray);
+
+    return retval;
+  }
+
+}
\ No newline at end of file
index 66972e0..50e8bc9 100644 (file)
 #include <assert.h>
 #include <talloc.h>
 
-
 #include <tr_apc.h>
 #include <tr_comm.h>
+#include <trp_internal.h>
+#include <mon_internal.h>
 #include <tr_msg.h>
 #include <tr_name_internal.h>
-#include <trp_internal.h>
 #include <trust_router/tr_constraint.h>
 #include <trust_router/tr_dh.h>
 #include <tr_debug.h>
@@ -101,6 +101,17 @@ void tr_msg_set_msg_type(TR_MSG *msg, enum msg_type type)
   msg->msg_type = type;
 }
 
+/* NOTE: If you are manipulating messages with these getters/setters, the msg_rep
+ * objects are *not* put in the talloc context of the msg. If you are allocating
+ * the message directly with talloc, then you can talloc_steal() the rep into the
+ * message's context, but this is not handled automatically. */
+
+/**
+ * Get a TID_REQ message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a TID_REQUEST message
+ */
 TID_REQ *tr_msg_get_req(TR_MSG *msg)
 {
   if (msg->msg_type == TID_REQUEST)
@@ -108,12 +119,24 @@ TID_REQ *tr_msg_get_req(TR_MSG *msg)
   return NULL;
 }
 
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
 void tr_msg_set_req(TR_MSG *msg, TID_REQ *req)
 {
   msg->msg_rep = req;
   msg->msg_type = TID_REQUEST;
 }
 
+/**
+ * Get a TID_RESP message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a TID_RESPONSE message
+ */
 TID_RESP *tr_msg_get_resp(TR_MSG *msg)
 {
   if (msg->msg_type == TID_RESPONSE)
@@ -121,12 +144,74 @@ TID_RESP *tr_msg_get_resp(TR_MSG *msg)
   return NULL;
 }
 
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
 void tr_msg_set_resp(TR_MSG *msg, TID_RESP *resp)
 {
   msg->msg_rep = resp;
   msg->msg_type = TID_RESPONSE;
 }
 
+/**
+ * Get a MON_REQ message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a MON_REQUEST message
+ */
+MON_REQ *tr_msg_get_mon_req(TR_MSG *msg)
+{
+  if (msg->msg_type == MON_REQUEST)
+    return (MON_REQ *)msg->msg_rep;
+  return NULL;
+}
+
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
+void tr_msg_set_mon_req(TR_MSG *msg, MON_REQ *req)
+{
+  msg->msg_rep = req;
+  msg->msg_type = MON_REQUEST;
+}
+
+/**
+ * Get a MON_RESP message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a MON_RESPONSE message
+ */
+MON_RESP *tr_msg_get_mon_resp(TR_MSG *msg)
+{
+  if (msg->msg_type == MON_RESPONSE)
+    return (MON_RESP *)msg->msg_rep;
+  return NULL;
+}
+
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
+void tr_msg_set_mon_resp(TR_MSG *msg, MON_RESP *resp)
+{
+  msg->msg_rep = resp;
+  msg->msg_type = MON_RESPONSE;
+}
+
+/**
+ * Get a TRP_UPD message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a TRP_UPDATE message
+ */
 TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg)
 {
   if (msg->msg_type == TRP_UPDATE)
@@ -134,13 +219,24 @@ TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg)
   return NULL;
 }
 
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
 void tr_msg_set_trp_upd(TR_MSG *msg, TRP_UPD *update)
 {
   msg->msg_rep=update;
-  talloc_steal(NULL, update); /* should attach to msg, but TR_MSG not usually talloc'ed */
   msg->msg_type=TRP_UPDATE;
 }
 
+/**
+ * Get a TRP_REQ message payload
+ *
+ * @param msg
+ * @return the message payload, or null if it is not a TRP_REQUEST message
+ */
 TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg)
 {
   if (msg->msg_type == TRP_REQUEST)
@@ -148,6 +244,12 @@ TRP_REQ *tr_msg_get_trp_req(TR_MSG *msg)
   return NULL;
 }
 
+/**
+ * Set message's payload
+ *
+ * Does not manage talloc contexts, works with any means of allocating
+ * the objects.
+ */
 void tr_msg_set_trp_req(TR_MSG *msg, TRP_REQ *req)
 {
   msg->msg_rep=req;
@@ -216,7 +318,8 @@ static json_t * tr_msg_encode_tidreq(TID_REQ *req)
   if ((!req) || (!req->rp_realm) || (!req->realm) || !(req->comm))
     return NULL;
 
-  assert(jreq = json_object());
+  jreq = json_object();
+  assert(jreq);
 
   jstr = tr_name_to_json_string(req->rp_realm);
   json_object_set_new(jreq, "rp_realm", jstr);
@@ -251,7 +354,7 @@ static json_t * tr_msg_encode_tidreq(TID_REQ *req)
   return jreq;
 }
 
-static TID_REQ *tr_msg_decode_tidreq(json_t *jreq)
+static TID_REQ *tr_msg_decode_tidreq(TALLOC_CTX *mem_ctx, json_t *jreq)
 {
   TID_REQ *treq = NULL;
   json_t *jrp_realm = NULL;
@@ -267,7 +370,8 @@ static TID_REQ *tr_msg_decode_tidreq(json_t *jreq)
     tr_crit("tr_msg_decode_tidreq(): Error allocating TID_REQ structure.");
     return NULL;
   }
+  talloc_steal(mem_ctx, treq);
+
   /* store required fields from request */
   if ((NULL == (jrp_realm = json_object_get(jreq, "rp_realm"))) ||
       (NULL == (jrealm = json_object_get(jreq, "target_realm"))) ||
@@ -499,7 +603,7 @@ static json_t * tr_msg_encode_tidresp(TID_RESP *resp)
   return jresp;
 }
 
-static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
+static TID_RESP *tr_msg_decode_tidresp(TALLOC_CTX *mem_ctx, json_t *jresp)
 {
   TID_RESP *tresp = NULL;
   json_t *jresult = NULL;
@@ -511,11 +615,10 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
   json_t *jservers = NULL;
   json_t *jerr_msg = NULL;
 
-  if (!(tresp=tid_resp_new(NULL))) {
+  if (!(tresp=tid_resp_new(mem_ctx))) {
     tr_crit("tr_msg_decode_tidresp(): Error allocating TID_RESP structure.");
     return NULL;
   }
 
   /* store required fields from response */
   if ((NULL == (jresult = json_object_get(jresp, "result"))) ||
@@ -1167,21 +1270,23 @@ cleanup:
   return req;
 }
 
-char *tr_msg_encode(TR_MSG *msg) 
+char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg)
 {
   json_t *jmsg=NULL;
   json_t *jmsg_type=NULL;
+  char *encoded_tmp=NULL;
   char *encoded=NULL;
   TID_RESP *tidresp=NULL;
   TID_REQ *tidreq=NULL;
   TRP_UPD *trpupd=NULL;
   TRP_REQ *trpreq=NULL;
+  MON_REQ *monreq=NULL;
+  MON_RESP *monresp=NULL;
 
   /* TBD -- add error handling */
   jmsg = json_object();
 
-  switch (msg->msg_type) 
-    {
+  switch (msg->msg_type) {
     case TID_REQUEST:
       jmsg_type = json_string("tid_request");
       json_object_set_new(jmsg, "msg_type", jmsg_type);
@@ -1210,18 +1315,37 @@ char *tr_msg_encode(TR_MSG *msg)
       json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_req(trpreq));
       break;
 
+    case MON_REQUEST:
+      jmsg_type = json_string("mon_request");
+      json_object_set_new(jmsg, "msg_type", jmsg_type);
+      monreq=tr_msg_get_mon_req(msg);
+      json_object_set_new(jmsg, "msg_body", mon_req_encode(monreq));
+      break;
+
+    case MON_RESPONSE:
+      jmsg_type = json_string("mon_response");
+      json_object_set_new(jmsg, "msg_type", jmsg_type);
+      monresp=tr_msg_get_mon_resp(msg);
+      json_object_set_new(jmsg, "msg_body", mon_resp_encode(monresp));
+      break;
+
     default:
       json_decref(jmsg);
       return NULL;
-    }
+  }
+
+  /* We should perhaps use json_set_alloc_funcs to automatically use talloc, but for
+   * now, we'll encode to a malloc'ed buffer, then copy that to a talloc'ed buffer. */
+  encoded_tmp=json_dumps(jmsg, 0);                // malloc'ed version
+  json_decref(jmsg);                              // free the JSON structure
+  encoded = talloc_strdup(mem_ctx, encoded_tmp);  // get the talloc'ed version
+  free(encoded_tmp);                              // free the malloc'ed version
 
-  encoded=json_dumps(jmsg, 0);
   tr_debug("tr_msg_encode: outgoing msg=%s", encoded);
-  json_decref(jmsg);
   return encoded;
 }
 
-TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen)
+TR_MSG *tr_msg_decode(TALLOC_CTX *mem_ctx, const char *jbuf, size_t buflen)
 {
   TR_MSG *msg=NULL;
   json_t *jmsg = NULL;
@@ -1235,14 +1359,12 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen)
     return NULL;
   }
 
-  if (!(msg = malloc(sizeof(TR_MSG)))) {
+  if (!(msg = talloc_zero(mem_ctx, TR_MSG))) {
     tr_debug("tr_msg_decode(): Error allocating TR_MSG structure.");
     json_decref(jmsg);
     return NULL;
   }
  
-  memset(msg, 0, sizeof(TR_MSG));
-
   if ((NULL == (jtype = json_object_get(jmsg, "msg_type"))) ||
       (NULL == (jbody = json_object_get(jmsg, "msg_body")))) {
     tr_debug("tr_msg_decode(): Error parsing message header.");
@@ -1255,19 +1377,28 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen)
 
   if (0 == strcmp(mtype, "tid_request")) {
     msg->msg_type = TID_REQUEST;
-    tr_msg_set_req(msg, tr_msg_decode_tidreq(jbody));
+    tr_msg_set_req(msg, tr_msg_decode_tidreq(msg, jbody));
   }
   else if (0 == strcmp(mtype, "tid_response")) {
     msg->msg_type = TID_RESPONSE;
-    tr_msg_set_resp(msg, tr_msg_decode_tidresp(jbody));
+    tr_msg_set_resp(msg, tr_msg_decode_tidresp(msg, 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 */
+    tr_msg_set_trp_upd(msg, tr_msg_decode_trp_upd(msg, jbody));
   }
   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 */
+    tr_msg_set_trp_req(msg, tr_msg_decode_trp_req(msg, jbody));
+  }
+  else if (0 == strcmp(mtype, "mon_request")) {
+    msg->msg_type = MON_REQUEST;
+    tr_msg_set_mon_req(msg, mon_req_decode(msg, jbody));
+  }
+  /* We do not currently handle monitoring responses */
+  else if (0 == strcmp(mtype, "mon_response")) {
+    msg->msg_type = MON_RESPONSE;
+    tr_msg_set_mon_resp(msg, mon_resp_decode(msg, jbody));
   }
   else {
     msg->msg_type = TR_UNKNOWN;
@@ -1282,29 +1413,11 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen)
 void tr_msg_free_encoded(char *jmsg)
 {
   if (jmsg)
-    free (jmsg);
+    talloc_free(jmsg);
 }
 
 void tr_msg_free_decoded(TR_MSG *msg)
 {
-  if (msg) {
-    if (msg->msg_rep!=NULL) {
-      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);
-  }
+  if (msg)
+    talloc_free(msg);
 }
index 4acdc4b..0b7d08e 100644 (file)
 
 #include <tr.h>
 #include <tr_name_internal.h>
-#include <tr_gss.h>
+#include <tr_gss_names.h>
 #include <tr_config.h>
 #include <tr_rp.h>
 #include <tr_debug.h>
 
-static int tr_rp_client_destructor(void *obj)
-{
-  return 0;
-}
-
-TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx)
-{
-  TR_RP_CLIENT *client=talloc(mem_ctx, TR_RP_CLIENT);
-
-  if (client!=NULL) {
-    client->next=NULL;
-    client->comm_next=NULL;
-    client->gss_names=NULL;
-    client->filters=NULL;
-    talloc_set_destructor((void *)client, tr_rp_client_destructor);
-  }
-  return client;
-}
-
-void tr_rp_client_free(TR_RP_CLIENT *client)
-{
-  talloc_free(client);
-}
-
-static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client)
-{
-  if (client==NULL)
-    return NULL;
-
-  while (client->next!=NULL)
-    client=client->next;
-  return client;
-}
-
-/* do not call directly, use the tr_rp_client_add() macro */
-TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new)
-{
-  if (clients==NULL)
-    clients=new;
-  else {
-    tr_rp_client_tail(clients)->next=new;
-    while (new!=NULL) {
-      talloc_steal(clients, new); /* put it in the right context */
-      new=new->next;
-    }
-  }
-  return clients;
-}
-
-
-int tr_rp_client_add_gss_name(TR_RP_CLIENT *rp_client, TR_NAME *gss_name)
-{
-  return tr_gss_names_add(rp_client->gss_names, gss_name);
-}
-
-int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts)
-{
-  if (client->filters!=NULL)
-    tr_filter_set_free(client->filters);
-  client->filters=filts;
-  talloc_steal(client, filts);
-  return 0; /* success */
-}
-
-TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx)
-{
-  return talloc(memctx, TR_RP_CLIENT_ITER);
-}
-
-void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter)
-{
-  talloc_free(iter);
-}
-
-TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients)
-{
-  if (!iter) {
-    tr_err("tr_rp_client_iter_first: Iterator is null, failing.");
-    return NULL;
-  }
-  *iter=rp_clients;
-  return *iter;
-}
-
-TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter)
-{
-  if (*iter)
-    *iter=(*iter)->next;
-  return *iter;
-}
-
-/**
- * Find a client associated with a GSS name. It's possible there are other clients that match as well.
- *
- * @param rp_clients List of RP clients to search
- * @param gss_name GSS name to search for
- * @return Borrowed reference to an RP client linked to the GSS name
- */
-TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name)
-{
-  TR_RP_CLIENT_ITER *iter=tr_rp_client_iter_new(NULL);
-  TR_RP_CLIENT *client=NULL;
-
-  if (iter==NULL) {
-    tr_err("tr_rp_client_lookup: Unable to allocate iterator");
-    return NULL;
-  }
-  for (client=tr_rp_client_iter_first(iter, rp_clients); client != NULL; client=tr_rp_client_iter_next(iter)) {
-    if (tr_gss_names_matches(client->gss_names, gss_name))
-      break;
-  }
-  tr_rp_client_iter_free(iter);
-  return client;
-}
-
 TR_RP_REALM *tr_rp_realm_lookup(TR_RP_REALM *rp_realms, TR_NAME *rp_name)
 {
   TR_RP_REALM *rp = NULL;
@@ -330,3 +215,4 @@ char *tr_rp_realm_to_str(TALLOC_CTX *mem_ctx, TR_RP_REALM *rp)
                          "RP realm: \"%.*s\"\n",
                          rp->realm_id->len, rp->realm_id->buf);
 }
+
diff --git a/common/tr_rp_client.c b/common/tr_rp_client.c
new file mode 100644 (file)
index 0000000..b5e657e
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2012-2018, 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 <talloc.h>
+#include <tr_rp_client.h>
+#include <tr_debug.h>
+
+static int tr_rp_client_destructor(void *obj)
+{
+  return 0;
+}
+
+TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx)
+{
+  TR_RP_CLIENT *client=talloc(mem_ctx, TR_RP_CLIENT);
+
+  if (client!=NULL) {
+    client->next=NULL;
+    client->comm_next=NULL;
+    client->gss_names=NULL;
+    client->filters=NULL;
+    talloc_set_destructor((void *)client, tr_rp_client_destructor);
+  }
+  return client;
+}
+
+void tr_rp_client_free(TR_RP_CLIENT *client)
+{
+  talloc_free(client);
+}
+
+static TR_RP_CLIENT *tr_rp_client_tail(TR_RP_CLIENT *client)
+{
+  if (client==NULL)
+    return NULL;
+
+  while (client->next!=NULL)
+    client=client->next;
+  return client;
+}
+
+/* do not call directly, use the tr_rp_client_add() macro */
+TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new)
+{
+  if (clients==NULL)
+    clients=new;
+  else {
+    tr_rp_client_tail(clients)->next=new;
+    while (new!=NULL) {
+      talloc_steal(clients, new); /* put it in the right context */
+      new=new->next;
+    }
+  }
+  return clients;
+}
+
+
+int tr_rp_client_add_gss_name(TR_RP_CLIENT *rp_client, TR_NAME *gss_name)
+{
+  return tr_gss_names_add(rp_client->gss_names, gss_name);
+}
+
+int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts)
+{
+  if (client->filters!=NULL)
+    tr_filter_set_free(client->filters);
+  client->filters=filts;
+  talloc_steal(client, filts);
+  return 0; /* success */
+}
+
+TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx)
+{
+  return talloc(memctx, TR_RP_CLIENT_ITER);
+}
+
+void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients)
+{
+  if (!iter) {
+    tr_err("tr_rp_client_iter_first: Iterator is null, failing.");
+    return NULL;
+  }
+  *iter=rp_clients;
+  return *iter;
+}
+
+TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter)
+{
+  if (*iter)
+    *iter=(*iter)->next;
+  return *iter;
+}
+
+/**
+ * Find a client associated with a GSS name. It's possible there are other clients that match as well.
+ *
+ * @param rp_clients List of RP clients to search
+ * @param gss_name GSS name to search for
+ * @return Borrowed reference to an RP client linked to the GSS name
+ */
+TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name)
+{
+  TR_RP_CLIENT_ITER *iter=tr_rp_client_iter_new(NULL);
+  TR_RP_CLIENT *client=NULL;
+
+  if (iter==NULL) {
+    tr_err("tr_rp_client_lookup: Unable to allocate iterator");
+    return NULL;
+  }
+  for (client=tr_rp_client_iter_first(iter, rp_clients); client != NULL; client=tr_rp_client_iter_next(iter)) {
+    if (tr_gss_names_matches(client->gss_names, gss_name))
+      break;
+  }
+  tr_rp_client_iter_free(iter);
+  return client;
+}
+
diff --git a/common/tr_rp_client_encoders.c b/common/tr_rp_client_encoders.c
new file mode 100644 (file)
index 0000000..d0a5cd0
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <tr_gss_names.h>
+#include <tr_rp_client.h>
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+static json_t *tr_rp_client_to_json(TR_RP_CLIENT *rp_client)
+{
+  json_t *client_json = NULL;
+  json_t *retval = NULL;
+
+  client_json = json_object();
+  if (client_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(client_json, "gss_names", tr_gss_names_to_json_array(rp_client->gss_names));
+  OBJECT_SET_OR_FAIL(client_json, "filters", tr_filter_set_to_json(rp_client->filters));
+  
+  /* succeeded - set the return value and increment the reference count */
+  retval = client_json;
+  json_incref(retval);
+
+cleanup:
+  if (client_json)
+    json_decref(client_json);
+  return retval;
+}
+
+json_t *tr_rp_clients_to_json(TR_RP_CLIENT *rp_clients)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_RP_CLIENT_ITER *iter = tr_rp_client_iter_new(NULL);
+  TR_RP_CLIENT *rp_client = NULL;
+
+  if ((jarray == NULL) || (iter == NULL))
+    goto cleanup;
+
+  rp_client = tr_rp_client_iter_first(iter, rp_clients);
+  while (rp_client) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_rp_client_to_json(rp_client));
+    rp_client = tr_rp_client_iter_next(iter);
+  }
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+
+  if (iter)
+    tr_rp_client_iter_free(iter);
+
+  return retval;
+}
diff --git a/common/tr_socket.c b/common/tr_socket.c
new file mode 100644 (file)
index 0000000..7f1c917
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2016-2018, 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 <talloc.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <poll.h> // for nfds_t
+
+#include <tr_debug.h>
+#include <tr_socket.h>
+
+/**
+ * Open sockets on all interface addresses
+ *
+ * Uses getaddrinfo() to find all TCP addresses and opens sockets in
+ * non-blocking modes. Binds to most max_fd sockets and stores file descriptors
+ * in fd_out. Unused entries in fd_out are not modified. Returns the actual
+ * number of sockets opened.
+ *
+ * @param port port to listen on
+ * @param fd_out output array, at least max_fd long
+ * @param max_fd maximum number of file descriptors to write
+ * @return number of file descriptors written into the output array
+ */
+nfds_t tr_sock_listen_all(unsigned int port, int *fd_out, nfds_t max_fd)
+{
+  int rc = 0;
+  int conn = -1;
+  int optval = 1;
+  int gai_retval = 0;
+  struct addrinfo *ai=NULL;
+  struct addrinfo *ai_head=NULL;
+  struct addrinfo hints={
+      .ai_flags=AI_PASSIVE,
+      .ai_family=AF_UNSPEC,
+      .ai_socktype=SOCK_STREAM,
+      .ai_protocol=IPPROTO_TCP
+  };
+  char *port_str=NULL;
+  nfds_t n_opened=0;
+
+  port_str=talloc_asprintf(NULL, "%d", port);
+  if (port_str==NULL) {
+    tr_err("tr_sock_listen_all: unable to allocate port");
+    return 0;
+  }
+
+  gai_retval = getaddrinfo(NULL, port_str, &hints, &ai_head);
+  talloc_free(port_str);
+  if (gai_retval != 0) {
+    tr_err("tr_sock_listen_all: getaddrinfo() failed (%s)", gai_strerror(gai_retval));
+    return 0;
+  }
+  tr_debug("tr_sock_listen_all: got address info");
+
+  /* TODO: listen on all ports - I don't recall what this means (jlr, 4/11/2018) */
+  for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_opened<max_fd); ai=ai->ai_next) {
+    if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) {
+      tr_debug("tr_sock_listen_all: unable to open socket");
+      continue;
+    }
+
+    optval=1;
+    if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
+      tr_debug("tids_listen: unable to set SO_REUSEADDR"); /* not fatal? */
+
+    if (ai->ai_family==AF_INET6) {
+      /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure
+       * if still relevant) */
+      if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) {
+        tr_debug("tr_sock_listen_all: unable to set IPV6_V6ONLY, skipping interface");
+        close(conn);
+        continue;
+      }
+    }
+
+    rc=bind(conn, ai->ai_addr, ai->ai_addrlen);
+    if (rc<0) {
+      tr_debug("tr_sock_listen_all: unable to bind to socket");
+      close(conn);
+      continue;
+    }
+
+    if (0>listen(conn, 512)) {
+      tr_debug("tr_sock_listen_all: unable to listen on bound socket");
+      close(conn);
+      continue;
+    }
+
+    /* ok, this one worked. Save it */
+    fd_out[n_opened++]=conn;
+  }
+  freeaddrinfo(ai_head);
+
+  if (n_opened==0) {
+    tr_debug("tr_sock_listen_all: no addresses available for listening.");
+    return 0;
+  }
+
+  tr_debug("tr_sock_listen_all: listening on port %d on %d socket%s",
+           port,
+           n_opened,
+           (n_opened==1)?"":"s");
+
+  return n_opened;
+}
+
index 2ce3c82..ef85776 100644 (file)
@@ -36,8 +36,8 @@
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
-#include <trust_router/tr_dh.h>
 #include <tr_util.h>
+#include <stdlib.h>
 
 void tr_bin_to_hex(const unsigned char * bin, size_t bin_len,
                   char * hex_out, size_t hex_len)
@@ -71,3 +71,28 @@ int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
 
   return 0;
 }
+
+/**
+ * Convert a struct timespec to a string representation
+ * @param ts
+ * @return
+ */
+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;
+}
+
index 47c962b..6b4639c 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.63)
-AC_INIT([trust_router],[3.3.0],
+AC_INIT([trust_router],[3.4.0~1],
 [bugs@project-moonshot.org])
 AC_CONFIG_MACRO_DIR(m4)
 AC_CONFIG_AUX_DIR(build-aux)
diff --git a/include/mon.h b/include/mon.h
new file mode 100644 (file)
index 0000000..33b8f4b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TRUST_ROUTER_MON_H
+#define TRUST_ROUTER_MON_H
+
+#include <gssapi.h>
+#include <trust_router/tr_name.h>
+
+/* Typedefs */
+typedef struct mon_req MON_REQ;
+typedef struct mon_resp MON_RESP;
+
+typedef enum mon_cmd MON_CMD;
+typedef enum mon_resp_code MON_RESP_CODE;
+
+typedef struct mon_opt MON_OPT;
+typedef enum mon_opt_type MON_OPT_TYPE;
+
+typedef enum mon_rc MON_RC;
+
+typedef struct mons_instance MONS_INSTANCE;
+typedef struct monc_instance MONC_INSTANCE;
+
+typedef struct mons_dispatch_table_entry MONS_DISPATCH_TABLE_ENTRY;
+
+typedef int (MONS_REQ_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *);
+typedef int (MONS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie);
+typedef int (MONC_RESP_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *);
+
+#endif //TRUST_ROUTER_MON_H
diff --git a/include/mon_internal.h b/include/mon_internal.h
new file mode 100644 (file)
index 0000000..29ea514
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TRUST_ROUTER_MON_REQ_H
+#define TRUST_ROUTER_MON_REQ_H
+
+#include <talloc.h>
+#include <stdint.h>
+#include <jansson.h>
+#include <gmodule.h>
+#include <gssapi.h>
+#include <trust_router/tid.h>
+#include <trp_internal.h>
+#include <tr_gss_names.h>
+#include <tr_gss_client.h>
+#include <tr_name_internal.h>
+#include <trust_router/tr_dh.h>
+#include <mon.h>
+
+/* Typedefs */
+typedef struct mon_req MON_REQ;
+typedef struct mon_resp MON_RESP;
+
+typedef enum mon_cmd MON_CMD;
+typedef enum mon_resp_code MON_RESP_CODE;
+
+typedef struct mon_opt MON_OPT;
+typedef enum mon_opt_type MON_OPT_TYPE;
+
+typedef enum mon_rc MON_RC;
+
+typedef struct mons_instance MONS_INSTANCE;
+typedef struct monc_instance MONC_INSTANCE;
+
+typedef int (MONS_REQ_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *);
+typedef int (MONS_AUTH_FUNC)(gss_name_t client_name, TR_NAME *display_name, void *cookie);
+typedef int (MONC_RESP_FUNC)(MONS_INSTANCE *, MON_REQ *, MON_RESP *, void *);
+
+/* Struct and enum definitions */
+enum mon_rc {
+  MON_SUCCESS=0,
+  MON_ERROR, /* generic error */
+  MON_BADARG, /* problem with the arguments */
+  MON_NOMEM, /* out of memory */
+  MON_NOPARSE, /* parsing failed */
+};
+
+enum mon_cmd {
+  MON_CMD_UNKNOWN=0,
+  MON_CMD_RECONFIGURE,
+  MON_CMD_SHOW
+};
+
+/* These should be explicitly numbered because they form part of the public API */
+enum mon_resp_code {
+  MON_RESP_SUCCESS=0,
+  MON_RESP_ERROR=1, // generic error
+};
+
+enum mon_opt_type {
+  OPT_TYPE_UNKNOWN=0,
+  OPT_TYPE_ANY,
+
+  // System information
+  OPT_TYPE_SHOW_VERSION,
+  OPT_TYPE_SHOW_CONFIG_FILES,
+
+  // System statistics
+  OPT_TYPE_SHOW_UPTIME,
+  OPT_TYPE_SHOW_TID_REQ_COUNT,
+  OPT_TYPE_SHOW_TID_REQ_ERR_COUNT,
+  OPT_TYPE_SHOW_TID_REQ_PENDING,
+
+  // Dynamic trust router state
+  OPT_TYPE_SHOW_ROUTES,
+  OPT_TYPE_SHOW_PEERS,
+  OPT_TYPE_SHOW_COMMUNITIES,
+  OPT_TYPE_SHOW_REALMS,
+  OPT_TYPE_SHOW_RP_CLIENTS
+};
+
+struct mon_opt {
+  MON_OPT_TYPE type;
+};
+
+struct mon_req {
+  MON_CMD command;
+  GArray *options;
+};
+
+struct mon_resp {
+  MON_RESP_CODE code;
+  TR_NAME *message;
+  json_t *payload;
+};
+
+/* Monitoring server instance */
+struct mons_instance {
+  const char *hostname;
+  unsigned int port;
+  TR_GSS_NAMES *authorized_gss_names;
+  TIDS_INSTANCE *tids;
+  TRPS_INSTANCE *trps;
+  MONS_REQ_FUNC *req_handler;
+  MONS_AUTH_FUNC *auth_handler;
+  void *cookie;
+  GPtrArray *handlers;
+  GArray *pids; /* PIDs of active mons processes */
+};
+
+/* Client instance */
+struct monc_instance {
+  TR_GSSC_INSTANCE *gssc;
+};
+
+/* Prototypes */
+/* tr_mon.c */
+const char *mon_cmd_to_string(MON_CMD cmd);
+MON_CMD mon_cmd_from_string(const char *s);
+const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type);
+MON_OPT_TYPE mon_opt_type_from_string(const char *s);
+
+/* mon_req.c */
+MON_REQ *mon_req_new(TALLOC_CTX *mem_ctx, MON_CMD cmd);
+void mon_req_free(MON_REQ *req);
+MON_RC mon_req_add_option(MON_REQ *req, MON_OPT_TYPE opt_type);
+size_t mon_req_opt_count(MON_REQ *req);
+MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index);
+
+/* mon_req_encode.c */
+json_t *mon_req_encode(MON_REQ *req);
+
+/* mon_req_decode.c */
+MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json);
+MON_REQ *mon_req_parse(TALLOC_CTX *mem_ctx, const char *input);
+
+/* mon_resp.c */
+MON_RESP *mon_resp_new(TALLOC_CTX *mem_ctx, MON_RESP_CODE code, const char *msg, json_t *payload);
+void mon_resp_free(MON_RESP *resp);
+int mon_resp_set_message(MON_RESP *resp, const char *new_msg);
+void mon_resp_set_payload(MON_RESP *resp, json_t *new_payload);
+
+/* mon_resp_encode.c */
+json_t *mon_resp_encode(MON_RESP *resp);
+
+/* mon_resp_decode.c */
+MON_RESP * mon_resp_decode(TALLOC_CTX *mem_ctx, json_t *resp_json);
+
+/* mons.c */
+MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx);
+int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH_FUNC *auth_handler, const char *hostname,
+                      unsigned int port, void *cookie, int *fd_out, size_t max_fd);
+int mons_accept(MONS_INSTANCE *mons, int listen);
+
+/* monc.c */
+MONC_INSTANCE *monc_new(TALLOC_CTX *mem_ctx);
+void monc_free(MONC_INSTANCE *monc);
+DH *monc_get_dh(MONC_INSTANCE *inst);
+DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh);
+int monc_open_connection(MONC_INSTANCE *monc, const char *server, unsigned int port);
+MON_RESP *monc_send_request(TALLOC_CTX *mem_ctx, MONC_INSTANCE *monc, MON_REQ *req);
+
+#endif //TRUST_ROUTER_MON_REQ_H
diff --git a/include/mons_handlers.h b/include/mons_handlers.h
new file mode 100644 (file)
index 0000000..48a01b3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TRUST_ROUTER_MONS_HANDLERS_H
+#define TRUST_ROUTER_MONS_HANDLERS_H
+
+typedef MON_RC (MONS_HANDLER_FUNC)(void *cookie, json_t **result_ptr);
+
+struct mons_dispatch_table_entry {
+  MON_CMD command;
+  MON_OPT_TYPE opt_type;
+  MONS_HANDLER_FUNC *handler;
+  void *cookie;
+};
+
+/* mons_handlers.c */
+MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req);
+MON_RC mons_register_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type, MONS_HANDLER_FUNC *f, void *cookie);
+
+#endif //TRUST_ROUTER_MONS_HANDLERS_H
index 855df0a..8613eb8 100644 (file)
@@ -39,6 +39,7 @@
 #include <tr_rp.h>
 #include <trust_router/tid.h>
 #include <jansson.h>
+#include "tr_gss_client.h"
 
 struct tid_srvr_blk {
   TID_SRVR_BLK *next;
@@ -84,23 +85,26 @@ struct tid_req {
 };
 
 struct tidc_instance {
-  // TID_REQ *req_list;
-  // TBD -- Do we still need a separate private key */
-  // char *priv_key;
-  // int priv_len;
-  DH *client_dh;                       /* Client's DH struct with priv and pub keys */
+  TR_GSSC_INSTANCE *gssc;
+};
+
+struct tid_process {
+  pid_t pid;
+  int read_fd;
 };
 
 struct tids_instance {
   int req_count;
+  int error_count;
   char *priv_key;
   char *ipaddr;
   const char *hostname;
   TIDS_REQ_FUNC *req_handler;
   tids_auth_func *auth_handler;
   void *cookie;
-  uint16_t tids_port;
+  unsigned int tids_port;
   TR_NAME *gss_name;           /* GSS name client used for authentication */
+  GArray *pids; /* PIDs of active tids processes */
 };
 
 /** Decrement a reference to #json when this tid_req is cleaned up. A
@@ -117,7 +121,10 @@ TID_SRVR_BLK *tid_srvr_blk_add_func(TID_SRVR_BLK *head, TID_SRVR_BLK *new);
 #define tid_srvr_blk_add(head, new) ((head)=tid_srvr_blk_add_func((head),(new)))
 void tid_srvr_blk_set_path(TID_SRVR_BLK *block, TID_PATH *path);
 
+TID_RC tid_resp_cpy(TID_RESP *dst, TID_RESP *src);
 void tid_resp_set_cons(TID_RESP *resp, TR_CONSTRAINT_SET *cons);
 void tid_resp_set_error_path(TID_RESP *resp, json_t *ep);
 
+void tids_sweep_procs(TIDS_INSTANCE *tids);
+
 #endif
index 5a770b8..6228c1f 100644 (file)
@@ -226,4 +226,7 @@ TR_NAME *tr_realm_dup_id(TR_REALM *realm);
 const char *tr_realm_role_to_str(TR_REALM_ROLE role);
 TR_REALM_ROLE tr_realm_role_from_str(const char *s);
 
+/* tr_comm_encoders.c */
+json_t *tr_comm_table_to_json(TR_COMM_TABLE *ctable);
+
 #endif
index 89fd328..30317de 100644 (file)
 #include <syslog.h>
 #include <sys/time.h>
 #include <talloc.h>
+#include <gmodule.h>
 
 #include <tr_comm.h>
 #include <tr_rp.h>
+#include <tr_rp_client.h>
 #include <tr_idp.h>
 #include <trp_ptable.h>
 #include <trp_internal.h>
@@ -51,6 +53,7 @@
 #define TR_DEFAULT_MAX_TREE_DEPTH 12
 #define TR_DEFAULT_TRPS_PORT 12308
 #define TR_DEFAULT_TIDS_PORT 12309
+#define TR_DEFAULT_MONITORING_PORT 0 /* defaults to being turned off */
 #define TR_DEFAULT_LOG_THRESHOLD LOG_INFO
 #define TR_DEFAULT_CONSOLE_THRESHOLD LOG_NOTICE
 #define TR_DEFAULT_APC_EXPIRATION_INTERVAL 43200
@@ -61,6 +64,8 @@
 #define TR_DEFAULT_TID_RESP_NUMER 2
 #define TR_DEFAULT_TID_RESP_DENOM 3
 
+#define TR_CFG_INVALID_SERIAL -1
+
 typedef enum tr_cfg_rc {
   TR_CFG_SUCCESS = 0,  /* No error */
   TR_CFG_ERROR,                /* General processing error */
@@ -73,6 +78,7 @@ typedef struct tr_cfg_internal {
   unsigned int max_tree_depth;
   unsigned int tids_port;
   unsigned int trps_port;
+  unsigned int monitoring_port;
   const char *hostname;
   int log_threshold;
   int console_threshold;
@@ -84,15 +90,23 @@ typedef struct tr_cfg_internal {
   unsigned int tid_req_timeout;
   unsigned int tid_resp_numer; /* numerator of fraction of AAA servers to wait for in unshared mode */
   unsigned int tid_resp_denom; /* denominator of fraction of AAA servers to wait for in unshared mode */
+  TR_GSS_NAMES *monitoring_credentials;
 } TR_CFG_INTERNAL;
 
+/* record of files loaded for this configuration */
+typedef struct tr_cfg_file {
+  const char *name;
+  json_int_t serial;
+} TR_CFG_FILE;
+
 typedef struct tr_cfg {
   TR_CFG_INTERNAL *internal;           /* internal trust router config */
   TR_RP_CLIENT *rp_clients;            /* locally associated RP Clients */
   TRP_PTABLE *peers; /* TRP peer table */
   TR_COMM_TABLE *ctable; /* communities/realms */
   TR_AAA_SERVER *default_servers;      /* default server list */
-  /* TBD -- Global Filters */
+
+  GArray *files; /* files loaded to make this configuration */
 } TR_CFG;
 
 typedef struct tr_cfg_mgr {
@@ -115,7 +129,32 @@ void tr_print_comms(TR_COMM_TABLE *ctab);
 void tr_print_comm_idps(TR_COMM_TABLE *ctab, TR_COMM *comm);
 void tr_print_comm_rps(TR_COMM_TABLE *ctab, TR_COMM *comm);
 
-TR_IDP_REALM *tr_cfg_find_idp (TR_CFG *cfg, TR_NAME *idp_id, TR_CFG_RC *rc);
-TR_RP_CLIENT *tr_cfg_find_rp (TR_CFG *cfg, TR_NAME *rp_gss, TR_CFG_RC *rc);
+/* tr_config_internal.c */
+TR_CFG_RC tr_cfg_parse_internal(TR_CFG *trc, json_t *jint);
+
+/* tr_config_comms.c */
+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_CFG_RC tr_cfg_parse_comms (TR_CFG *trc, json_t *jcfg);
+TR_CFG_RC tr_cfg_parse_default_servers (TR_CFG *trc, json_t *jcfg);
+
+/* tr_config_filters.c */
+TR_FILTER_SET *tr_cfg_parse_filters(TALLOC_CTX *mem_ctx, json_t *jfilts, TR_CFG_RC *rc);
+
+/* tr_config_orgs.c */
+TR_CFG_RC tr_cfg_parse_local_orgs(TR_CFG *trc, json_t *jcfg);
+TR_CFG_RC tr_cfg_parse_peer_orgs(TR_CFG *trc, json_t *jcfg);
+
+/* tr_config_realms.c */
+TR_IDP_REALM *tr_cfg_parse_idp_realms(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc);
+TR_AAA_SERVER *tr_cfg_parse_one_aaa_server(TALLOC_CTX *mem_ctx, json_t *jaddr, TR_CFG_RC *rc);
+TR_APC *tr_cfg_parse_apcs(TALLOC_CTX *mem_ctx, json_t *japcs, TR_CFG_RC *rc);
+
+/* tr_config_rp_clients.c */
+TR_RP_CLIENT *tr_cfg_parse_rp_clients(TALLOC_CTX *mem_ctx, json_t *jrealms, TR_CFG_RC *rc);
+TR_CFG_RC tr_cfg_parse_gss_names(TALLOC_CTX *mem_ctx, json_t *jgss_names, TR_GSS_NAMES **gssn_out);
+
+/* tr_config_encoders.c */
+json_t *tr_cfg_files_to_json_array(TR_CFG *cfg);
 
 #endif
index dc93860..55cc272 100644 (file)
@@ -41,7 +41,7 @@
 
 /* struct for hanging on to a socket listener event */
 struct tr_socket_event {
-  size_t n_sock_fd; /* how many of those are filled in? */
+  int n_sock_fd; /* how many of those are filled in? */
   int sock_fd[TR_MAX_SOCKETS]; /* the fd for the socket */
   struct event *ev[TR_MAX_SOCKETS]; /* its events */
 };
index a7704d7..ece3650 100644 (file)
@@ -144,4 +144,7 @@ int tr_filter_validate_spec_field(TR_FILTER_TYPE ftype, TR_FSPEC *fspec);
 const char *tr_filter_type_to_string(TR_FILTER_TYPE ftype);
 TR_FILTER_TYPE tr_filter_type_from_string(const char *s);
 
+/* tr_filter_encoders.c */
+json_t *tr_filter_set_to_json(TR_FILTER_SET *filter_set);
+
 #endif
index 676c8e4..850f51b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, JANET(UK)
+ * Copyright (c) 2018, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  *
  */
 
-#ifndef __TR_GSS_H__
-#define __TR_GSS_H__
+#ifndef TRUST_ROUTER_TR_GSS_H
+#define TRUST_ROUTER_TR_GSS_H
 
-#include <talloc.h>
-#include <tr_name_internal.h>
+#include <tr_msg.h>
 
-#define TR_MAX_GSS_NAMES 5
+typedef int (TR_GSS_AUTH_FN)(gss_name_t, TR_NAME *, void *);
+typedef TR_MSG *(TR_GSS_HANDLE_REQ_FN)(TALLOC_CTX *, TR_MSG *, void *);
 
-typedef struct tr_gss_names {
-  TR_NAME *names[TR_MAX_GSS_NAMES];
-} TR_GSS_NAMES;
+void tr_gss_handle_connection(int conn, const char *acceptor_service, const char *acceptor_hostname, TR_GSS_AUTH_FN auth_cb,
+                              void *auth_cookie, TR_GSS_HANDLE_REQ_FN req_cb, void *req_cookie);
 
-typedef struct tr_gss_names_iter {
-  TR_GSS_NAMES *gn;
-  int ii; /* which entry did we last output? */
-} TR_GSS_NAMES_ITER;
-
-TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx);
-void tr_gss_names_free(TR_GSS_NAMES *gn);
-int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new);
-int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name);
-
-TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx);
-TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn);
-TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter);
-void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter);
-
-#endif /* __TR_GSS_H__ */
+#endif //TRUST_ROUTER_TR_GSS_H
diff --git a/include/tr_gss_client.h b/include/tr_gss_client.h
new file mode 100644 (file)
index 0000000..25024a6
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TRUST_ROUTER_TR_GSS_CLIENT_H
+#define TRUST_ROUTER_TR_GSS_CLIENT_H
+
+#include <gssapi.h>
+#include <trust_router/tr_dh.h>
+#include <tr_msg.h>
+
+typedef struct tr_gssc_instance TR_GSSC_INSTANCE;
+
+/* Client instance */
+struct tr_gssc_instance {
+  const char *service_name;
+  DH *client_dh;
+  gss_ctx_id_t *gss_ctx;
+  int conn;
+};
+
+/* tr_gss_client.c */
+TR_GSSC_INSTANCE *tr_gssc_instance_new(TALLOC_CTX *mem_ctx);
+void tr_gssc_instance_free(TR_GSSC_INSTANCE *tr_gssc);
+int tr_gssc_open_connection(TR_GSSC_INSTANCE *gssc, const char *server, unsigned int port);
+TR_MSG *tr_gssc_exchange_msgs(TALLOC_CTX *mem_ctx, TR_GSSC_INSTANCE *gssc, TR_MSG *req_msg);
+DH * tr_gssc_get_dh(TR_GSSC_INSTANCE *inst);
+DH *tr_gssc_set_dh(TR_GSSC_INSTANCE *inst, DH *dh);
+
+#endif //TRUST_ROUTER_TR_GSS_CLIENT_H
diff --git a/include/tr_gss_names.h b/include/tr_gss_names.h
new file mode 100644 (file)
index 0000000..f8f97a2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef __TR_GSS_H__
+#define __TR_GSS_H__
+
+#include <talloc.h>
+#include <tr_name_internal.h>
+
+#define TR_MAX_GSS_NAMES 5
+
+typedef struct tr_gss_names {
+  TR_NAME *names[TR_MAX_GSS_NAMES];
+} TR_GSS_NAMES;
+
+typedef struct tr_gss_names_iter {
+  TR_GSS_NAMES *gn;
+  int ii; /* which entry did we last output? */
+} TR_GSS_NAMES_ITER;
+
+TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx);
+void tr_gss_names_free(TR_GSS_NAMES *gn);
+int tr_gss_names_add(TR_GSS_NAMES *gn, TR_NAME *new);
+TR_GSS_NAMES *tr_gss_names_dup(TALLOC_CTX *mem_ctx, TR_GSS_NAMES *orig);
+int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name);
+
+TR_GSS_NAMES_ITER *tr_gss_names_iter_new(TALLOC_CTX *mem_ctx);
+TR_NAME *tr_gss_names_iter_first(TR_GSS_NAMES_ITER *iter, TR_GSS_NAMES *gn);
+TR_NAME *tr_gss_names_iter_next(TR_GSS_NAMES_ITER *iter);
+void tr_gss_names_iter_free(TR_GSS_NAMES_ITER *iter);
+
+json_t *tr_gss_names_to_json_array(TR_GSS_NAMES *gss_names);
+
+#endif /* __TR_GSS_H__ */
index 951f7d7..68d669e 100644 (file)
@@ -83,7 +83,8 @@ TR_IDP_REALM *tr_idp_realm_remove_func(TR_IDP_REALM *head, TR_IDP_REALM *remove)
 #define tr_idp_realm_remove(head,remove) ((head)=tr_idp_realm_remove_func((head),(remove)))
 TR_IDP_REALM *tr_idp_realm_sweep_func(TR_IDP_REALM *head);
 #define tr_idp_realm_sweep(head) ((head)=tr_idp_realm_sweep_func((head)))
-char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp);
+int tr_idp_realm_aaa_server_count(TR_IDP_REALM *idp);
+int tr_idp_realm_apc_count(TR_IDP_REALM *idp);
 void tr_idp_realm_incref(TR_IDP_REALM *realm);
 void tr_idp_realm_decref(TR_IDP_REALM *realm);
 
@@ -98,4 +99,8 @@ TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter);
 TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm, int *shared_out);
 TR_AAA_SERVER *tr_default_server_lookup(TR_AAA_SERVER *default_servers, TR_NAME *comm);
 
+/* tr_idp_encoders.c */
+char *tr_idp_realm_to_str(TALLOC_CTX *mem_ctx, TR_IDP_REALM *idp);
+json_t *tr_idp_realms_to_json(TR_IDP_REALM *idp);
+
 #endif
diff --git a/include/tr_mon.h b/include/tr_mon.h
new file mode 100644 (file)
index 0000000..15ab640
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TR_MON_H
+#define TR_MON_H
+
+#include <tr_event.h>
+#include <tr_config.h>
+#include <mon_internal.h>
+#include <mons_handlers.h>
+
+int tr_mons_event_init(struct event_base *base,
+                       MONS_INSTANCE *mons,
+                       TR_CFG_MGR *cfg_mgr,
+                       struct tr_socket_event *mons_ev);
+
+#endif /* TR_MON_H */
index e10fd1a..6605cbd 100644 (file)
@@ -38,6 +38,8 @@
 #include <jansson.h>
 #include <trust_router/tid.h>
 #include <trust_router/trp.h>
+#include <mon.h>
+
 typedef struct tr_msg TR_MSG;
 
 enum msg_type {
@@ -45,7 +47,9 @@ enum msg_type {
   TID_REQUEST,
   TID_RESPONSE,
   TRP_UPDATE,
-  TRP_REQUEST
+  TRP_REQUEST,
+  MON_REQUEST,
+  MON_RESPONSE
 };
 
 /* Union of TR message types to hold message of any type. */
@@ -65,11 +69,15 @@ 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);
+MON_REQ *tr_msg_get_mon_req(TR_MSG *msg);
+void tr_msg_set_mon_req(TR_MSG *msg, MON_REQ *req);
+MON_RESP *tr_msg_get_mon_resp(TR_MSG *msg);
+void tr_msg_set_mon_resp(TR_MSG *msg, MON_RESP *resp);
 
 
 /* Encoders/Decoders */
-char *tr_msg_encode(TR_MSG *msg);
-TR_MSG *tr_msg_decode(const char *jmsg, size_t len);
+char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg);
+TR_MSG *tr_msg_decode(TALLOC_CTX *mem_ctx, const char *jmsg, size_t len);
 void tr_msg_free_encoded(char *jmsg);
 void tr_msg_free_decoded(TR_MSG *msg);
 
index fa6b84e..4424c73 100644 (file)
 #define TR_RP_H
 
 #include <talloc.h>
-
-#include <tr_gss.h>
-#include <tr_filter.h>
-
-typedef struct tr_rp_client {
-  struct tr_rp_client *next;
-  struct tr_rp_client *comm_next;
-  TR_GSS_NAMES *gss_names;
-  TR_FILTER_SET *filters;
-} TR_RP_CLIENT;
-
-typedef struct tr_rp_client *TR_RP_CLIENT_ITER;
+#include <tr_name_internal.h>
 
 /* Structure to make a linked list of RP realms by name for community config */
 typedef struct tr_rp_realm {
@@ -57,18 +46,6 @@ typedef struct tr_rp_realm {
 } TR_RP_REALM;
 
 /* prototypes */
-TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx);
-void tr_rp_client_free(TR_RP_CLIENT *client);
-TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new);
-#define tr_rp_client_add(clients,new) ((clients)=tr_rp_client_add_func((clients),(new)))
-int tr_rp_client_add_gss_name(TR_RP_CLIENT *client, TR_NAME *name);
-int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts);
-TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx);
-void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter);
-TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients);
-TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter);
-TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name);
-
 TR_RP_REALM *tr_rp_realm_new(TALLOC_CTX *mem_ctx);
 void tr_rp_realm_free(TR_RP_REALM *rp);
 TR_NAME *tr_rp_realm_get_id(TR_RP_REALM *rp);
diff --git a/include/tr_rp_client.h b/include/tr_rp_client.h
new file mode 100644 (file)
index 0000000..1ff4b29
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012-2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TRUST_ROUTER_TR_RP_CLIENT_H
+#define TRUST_ROUTER_TR_RP_CLIENT_H
+
+#include <talloc.h>
+
+#include <tr_gss_names.h>
+#include <tr_filter.h>
+
+typedef struct tr_rp_client {
+  struct tr_rp_client *next;
+  struct tr_rp_client *comm_next;
+  TR_GSS_NAMES *gss_names;
+  TR_FILTER_SET *filters;
+} TR_RP_CLIENT;
+
+typedef struct tr_rp_client *TR_RP_CLIENT_ITER;
+
+/* tr_rp_client.c */
+TR_RP_CLIENT *tr_rp_client_new(TALLOC_CTX *mem_ctx);
+void tr_rp_client_free(TR_RP_CLIENT *client);
+TR_RP_CLIENT *tr_rp_client_add_func(TR_RP_CLIENT *clients, TR_RP_CLIENT *new);
+#define tr_rp_client_add(clients,new) ((clients)=tr_rp_client_add_func((clients),(new)))
+int tr_rp_client_add_gss_name(TR_RP_CLIENT *client, TR_NAME *name);
+int tr_rp_client_set_filters(TR_RP_CLIENT *client, TR_FILTER_SET *filts);
+TR_RP_CLIENT_ITER *tr_rp_client_iter_new(TALLOC_CTX *memctx);
+void tr_rp_client_iter_free(TR_RP_CLIENT_ITER *iter);
+TR_RP_CLIENT *tr_rp_client_iter_first(TR_RP_CLIENT_ITER *iter, TR_RP_CLIENT *rp_clients);
+TR_RP_CLIENT *tr_rp_client_iter_next(TR_RP_CLIENT_ITER *iter);
+TR_RP_CLIENT *tr_rp_client_lookup(TR_RP_CLIENT *rp_clients, TR_NAME *gss_name);
+
+/* tr_rp_client_encoders.c */
+json_t *tr_rp_clients_to_json(TR_RP_CLIENT *rp_clients);
+
+#endif //TRUST_ROUTER_TR_RP_CLIENT_H
diff --git a/include/tr_socket.h b/include/tr_socket.h
new file mode 100644 (file)
index 0000000..064c6fc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TRUST_ROUTER_TR_SOCKET_H
+#define TRUST_ROUTER_TR_SOCKET_H
+
+#include <stdlib.h>
+#include <poll.h> // for nfds_t
+
+nfds_t tr_sock_listen_all(unsigned int port, int *fd_out, nfds_t max_fd);
+
+#endif //TRUST_ROUTER_TR_SOCKET_H
index 924293d..d8285b0 100644 (file)
 #include <trp_internal.h>
 #include <tr_event.h>
 #include <tr_config.h>
+#include <mon.h>
 
 #define TR_TID_MAX_AAA_SERVERS 10
 
-int tr_tids_event_init(struct event_base *base,
-                       TIDS_INSTANCE *tids,
-                       TR_CFG_MGR *cfg_mgr,
-                       TRPS_INSTANCE *trps,
-                       struct tr_socket_event *tids_ev);
+int tr_tids_event_init(struct event_base *base, TIDS_INSTANCE *tids, TR_CFG_MGR *cfg_mgr, TRPS_INSTANCE *trps,
+                       struct tr_socket_event *tids_ev, struct event **sweep_ev);
+
+/* tr_tid_mons.c */
+void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons);
 
 #endif /* TR_TID_H */
index 297295f..dea20e4 100644 (file)
@@ -44,6 +44,7 @@
 #include <tr_config.h>
 #include <tr_cfgwatch.h>
 #include <tr_event.h>
+#include <mon_internal.h>
 
 typedef struct tr_trps_events {
   struct event *trps_ev;
@@ -59,6 +60,7 @@ struct tr_instance {
   TR_CFG_MGR *cfg_mgr;
   TIDS_INSTANCE *tids;
   TRPS_INSTANCE *trps;
+  MONS_INSTANCE *mons;
   TR_CFGWATCH *cfgwatch;
   TR_TRPS_EVENTS *events;
 };
@@ -78,4 +80,8 @@ 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);
+
+/* tr_trp_mons.h */
+void tr_trp_register_mons_handlers(TRPS_INSTANCE *trps, MONS_INSTANCE *mons);
+
 #endif /* TR_TRP_H */
index 3a3f7bc..bed0482 100644 (file)
 
 #include <trust_router/tr_versioning.h>
 
+/* NB, tr_bin_to_hex() is also prototyped in trust_router/tr_dh.h */
+TR_EXPORT void tr_bin_to_hex(const unsigned char * bin, size_t binlen,
+                             char * hex_out, size_t hex_len);
 TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2);
+char *timespec_to_str(struct timespec *ts);
 
 #endif /* TR_UTIL_H */
index d6828d5..a35a043 100644 (file)
@@ -43,7 +43,9 @@
 #include <gsscon.h>
 #include <tr_mq.h>
 #include <tr_msg.h>
+#include <trp_peer.h>
 #include <trp_ptable.h>
+#include <trp_route.h>
 #include <trp_rtable.h>
 #include <tr_apc.h>
 #include <tr_comm.h>
diff --git a/include/trp_peer.h b/include/trp_peer.h
new file mode 100644 (file)
index 0000000..8886bef
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016-2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef TRUST_ROUTER_TRP_PEER_H
+#define TRUST_ROUTER_TRP_PEER_H
+
+#include <tr_gss_names.h>
+#include <tr_filter.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 */
+  TR_NAME *label; /* often null, set on first call to trp_peer_get_label or dup_label */
+  char *server;
+  TR_GSS_NAMES *gss_names;
+  TR_NAME *servicename;
+  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;
+  TR_FILTER_SET *filters;
+};
+
+
+TRP_PEER *trp_peer_new(TALLOC_CTX *memctx);
+void trp_peer_free(TRP_PEER *peer);
+TRP_PEER *trp_peer_tail(TRP_PEER *peer);
+TR_NAME *trp_peer_get_label(TRP_PEER *peer);
+TR_NAME *trp_peer_dup_label(TRP_PEER *peer);
+char *trp_peer_get_server(TRP_PEER *peer);
+void trp_peer_set_server(TRP_PEER *peer, const char *server);
+void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gssname);
+void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names);
+TR_GSS_NAMES *trp_peer_get_gss_names(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);
+void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts);
+TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype);
+
+/* trp_peer_encoders.c */
+char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep);
+json_t *trp_peer_to_json(TRP_PEER *peer);
+
+#endif //TRUST_ROUTER_TRP_PEER_H
index 186f77a..b26fac1 100644 (file)
 #include <talloc.h>
 
 #include <tr_name_internal.h>
-#include <tr_gss.h>
+#include <tr_gss_names.h>
 #include <trust_router/trp.h>
 #include <tr_filter.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 */
-  TR_NAME *label; /* often null, set on first call to trp_peer_get_label or dup_label */
-  char *server;
-  TR_GSS_NAMES *gss_names;
-  TR_NAME *servicename;
-  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;
-  TR_FILTER_SET *filters;
-};
+#include <trp_peer.h>
 
 typedef struct trp_ptable {
   TRP_PEER *head; /* head of a peer table list */
@@ -78,38 +57,14 @@ 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_gss_name(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);
-TR_NAME *trp_peer_get_label(TRP_PEER *peer);
-TR_NAME *trp_peer_dup_label(TRP_PEER *peer);
-char *trp_peer_get_server(TRP_PEER *peer);
-void trp_peer_set_server(TRP_PEER *peer, const char *server);
-void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gssname);
-void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names);
-TR_GSS_NAMES *trp_peer_get_gss_names(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);
-void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts);
-TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype);
-char *trp_peer_to_str(TALLOC_CTX *memctx, TRP_PEER *peer, const char *sep);
+/* trp_ptable_encoders.c */
+char *trp_ptable_to_str(TALLOC_CTX *memctx, TRP_PTABLE *ptbl, const char *sep, const char *lineterm);
+json_t *trp_ptable_to_json(TRP_PTABLE *ptbl);
 
 #endif /* _TRP_PTABLE_H_ */
diff --git a/include/trp_route.h b/include/trp_route.h
new file mode 100644 (file)
index 0000000..d159861
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016-2018, JANET(UK)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of JANET(UK) nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TRUST_ROUTER_TRP_ROUTE_H
+#define TRUST_ROUTER_TRP_ROUTE_H
+
+typedef struct trp_route {
+  TR_NAME *comm;
+  TR_NAME *realm;
+  TR_NAME *peer;
+  unsigned int metric;
+  TR_NAME *trust_router; /* hostname */
+  unsigned int trp_port;
+  unsigned int tid_port;
+  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;
+
+/* trp_route.c */
+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);
+
+/* trp_route_encoders.c */
+char *trp_route_to_str(TALLOC_CTX *mem_ctx, TRP_ROUTE *entry, const char *sep);
+json_t *trp_route_to_json(TRP_ROUTE *route);
+
+#endif //TRUST_ROUTER_TRP_ROUTE_H
index 583b5a7..0e0639d 100644 (file)
 #include <talloc.h>
 #include <time.h>
 
+#include <trp_route.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; /* hostname */
-  unsigned int trp_port;
-  unsigned int tid_port;
-  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.c */
 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 */
@@ -67,7 +54,7 @@ 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);
+TRP_ROUTE **trp_rtable_get_entries(TALLOC_CTX *mem_ctx, 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);
@@ -76,37 +63,9 @@ TR_NAME **trp_rtable_get_comm_realm_peers(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NA
 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);
+/* trp_rtable_encoders.c */
+char *trp_rtable_to_str(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, const char *sep, const char *lineterm);
+json_t *trp_rtable_to_json(TRP_RTABLE *rtbl);
 
 #endif /* _TRP_RTABLE_H_ */
index 42b97f3..38833f1 100644 (file)
@@ -44,6 +44,7 @@
 #include <trust_router/tr_versioning.h>
 
 #include <gssapi.h>
+#include <poll.h>
 
 
 #define TID_PORT       12309
@@ -94,7 +95,7 @@ void tid_req_set_realm(TID_REQ *req, TR_NAME *realm);
 TR_EXPORT TR_NAME *tid_req_get_comm(TID_REQ *req);
 void tid_req_set_comm(TID_REQ *req, TR_NAME *comm);
 TR_EXPORT TR_NAME *tid_req_get_orig_coi(TID_REQ *req);
-void tid_req_set_rp_orig_coi(TID_REQ *req, TR_NAME *orig_coi);
+void tid_req_set_orig_coi(TID_REQ *req, TR_NAME *orig_coi);
 TR_EXPORT TR_NAME *tid_req_get_request_id(TID_REQ *req);
 void tid_req_set_request_id(TID_REQ *req, TR_NAME *request_id);
 TR_EXPORT TIDC_RESP_FUNC *tid_req_get_resp_func(TID_REQ *req);
@@ -140,9 +141,9 @@ TR_EXPORT const TID_PATH *tid_srvr_get_path(const TID_SRVR_BLK *);
 TR_EXPORT int tid_srvr_get_key_expiration(const TID_SRVR_BLK *, struct timeval *tv_out);
 
 #define tid_resp_servers_foreach(RESP, SERVER, INDEX) \
-  for (INDEX=0,SERVER=NULL;                                            \
-       ((INDEX < tid_resp_get_num_servers(RESP))&&(SERVER = tid_resp_get_server(resp, INDEX))); \
-       INDEX++)
+  for ((INDEX)=0,(SERVER)=NULL;                                                \
+       (((INDEX) < tid_resp_get_num_servers(RESP))&&((SERVER) = tid_resp_get_server(resp, (INDEX)))); \
+       (INDEX)++)
 
 
 /* TID Client functions, in tid/tidc.c */
@@ -155,13 +156,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 */
+TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx);
 TR_EXPORT TIDS_INSTANCE *tids_create (void);
 TR_EXPORT int tids_start (TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
                           tids_auth_func *auth_handler, const char *hostname,
                           unsigned int port, void *cookie);
-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, int *fd_out, size_t max_fd);
+TR_EXPORT nfds_t tids_get_listener(TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
+                                   tids_auth_func *auth_handler, const char *hostname,
+                                   unsigned int port, void *cookie, int *fd_out, size_t max_fd);
 TR_EXPORT int tids_accept(TIDS_INSTANCE *tids, int listen);
 TR_EXPORT int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp);
 TR_EXPORT int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg);
diff --git a/mon/mon_common.c b/mon/mon_common.c
new file mode 100644 (file)
index 0000000..a237b5d
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <gmodule.h>
+#include <string.h>
+
+#include <mon_internal.h>
+
+// Monitoring common code
+
+/**
+ * This method defines the command strings
+ */
+const char *mon_cmd_to_string(MON_CMD cmd)
+{
+  switch(cmd) {
+    case MON_CMD_UNKNOWN:
+      return NULL;
+
+    case MON_CMD_RECONFIGURE:
+      return "reconfigure";
+
+    case MON_CMD_SHOW:
+      return "show";
+  }
+  return NULL;
+}
+
+// Helper macro for the mon_cmd_from_string method
+#define return_if_matches(s, cmd)                \
+  do {                                           \
+    if (strcmp((s), mon_cmd_to_string(cmd))==0)  \
+      return (cmd);                              \
+  } while(0)
+
+MON_CMD mon_cmd_from_string(const char *s)
+{
+  return_if_matches(s, MON_CMD_RECONFIGURE);
+  return_if_matches(s, MON_CMD_SHOW);
+  return MON_CMD_UNKNOWN;
+}
+#undef return_if_matches
+
+/**
+ * This method defines the option type strings
+ */
+const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type)
+{
+  switch(opt_type) {
+    case OPT_TYPE_UNKNOWN:
+    case OPT_TYPE_ANY:
+      return NULL;
+
+    case OPT_TYPE_SHOW_VERSION:
+      return "version";
+
+    case OPT_TYPE_SHOW_CONFIG_FILES:
+      return "config_files";
+
+    case OPT_TYPE_SHOW_UPTIME:
+      return "uptime";
+
+    case OPT_TYPE_SHOW_TID_REQ_COUNT:
+      return "tid_req_count";
+
+    case OPT_TYPE_SHOW_TID_REQ_ERR_COUNT:
+      return "tid_req_error_count";
+
+    case OPT_TYPE_SHOW_TID_REQ_PENDING:
+      return "tid_req_pending";
+
+    case OPT_TYPE_SHOW_ROUTES:
+      return "routes";
+
+    case OPT_TYPE_SHOW_PEERS:
+      return "peers";
+
+    case OPT_TYPE_SHOW_COMMUNITIES:
+      return "communities";
+
+    case OPT_TYPE_SHOW_REALMS:
+      return "realms";
+
+    case OPT_TYPE_SHOW_RP_CLIENTS:
+      return "rp_clients";
+  }
+  return NULL;
+}
+
+// Helper macro for the mon_opt_type_from_string method
+#define return_if_matches(s, cmd)                     \
+  do {                                                \
+    if (strcmp((s), mon_opt_type_to_string(cmd))==0)  \
+      return (cmd);                                   \
+  } while(0)
+
+MON_OPT_TYPE mon_opt_type_from_string(const char *s)
+{
+  return_if_matches(s, OPT_TYPE_SHOW_VERSION);
+  return_if_matches(s, OPT_TYPE_SHOW_CONFIG_FILES);
+  return_if_matches(s, OPT_TYPE_SHOW_UPTIME);
+  return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_COUNT);
+  return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_ERR_COUNT);
+  return_if_matches(s, OPT_TYPE_SHOW_TID_REQ_PENDING);
+  return_if_matches(s, OPT_TYPE_SHOW_ROUTES);
+  return_if_matches(s, OPT_TYPE_SHOW_PEERS);
+  return_if_matches(s, OPT_TYPE_SHOW_COMMUNITIES);
+  return_if_matches(s, OPT_TYPE_SHOW_REALMS);
+  return_if_matches(s, OPT_TYPE_SHOW_RP_CLIENTS);
+  return OPT_TYPE_UNKNOWN;
+}
+#undef return_if_matches
diff --git a/mon/mon_req.c b/mon/mon_req.c
new file mode 100644 (file)
index 0000000..bffa92e
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <gmodule.h>
+
+#include <mon_internal.h>
+
+// Monitoring request message common code
+
+/**
+ * Destructor used by talloc to ensure proper cleanup
+ */
+static int mon_req_destructor(void *object)
+{
+  MON_REQ *req = talloc_get_type_abort(object, MON_REQ);
+  if (req->options) {
+    g_array_unref(req->options);
+  }
+  return 0;
+}
+
+/**
+ * Allocate a new monitoring request
+ *
+ * @param mem_ctx talloc context for the new request
+ * @param cmd command for the request
+ * @return newly allocated request, or null on error
+ */
+MON_REQ *mon_req_new(TALLOC_CTX *mem_ctx, MON_CMD cmd)
+{
+  MON_REQ *req=talloc(mem_ctx, MON_REQ);
+  if (req) {
+    req->command = cmd;
+    req->options = g_array_new(FALSE, FALSE, sizeof(MON_OPT));
+    talloc_set_destructor((void *)req, mon_req_destructor);
+  }
+  return req;
+}
+
+/**
+ * Free a monitoring request
+ *
+ * @param req request to free, must not be null
+ */
+void mon_req_free(MON_REQ *req)
+{
+  talloc_free(req);
+}
+
+/**
+ * Add an option to a MON_REQ
+ * @param req request to operate on, not null
+ * @param opt_type type of option
+ * @return MON_SUCCESS on success, error code on error
+ */
+MON_RC mon_req_add_option(MON_REQ *req, MON_OPT_TYPE opt_type)
+{
+  MON_OPT new_opt; // not a pointer
+
+  /* Validate parameters */
+  if ((req == NULL) || (opt_type == OPT_TYPE_UNKNOWN)) {
+    return MON_BADARG;
+  }
+
+  new_opt.type = opt_type;
+
+  /* Add the new option to the list */
+  g_array_append_val(req->options, new_opt);
+  return MON_SUCCESS;
+}
+
+size_t mon_req_opt_count(MON_REQ *req)
+{
+  return req->options->len;
+}
+
+MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index)
+{
+  MON_OPT *result = &g_array_index(req->options, MON_OPT, index);
+  return result;
+}
+
diff --git a/mon/mon_req_decode.c b/mon/mon_req_decode.c
new file mode 100644 (file)
index 0000000..21bb64b
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <mon_internal.h>
+
+// Monitoring request decoders
+
+/**
+ * Decode a single option
+ *
+ * Format:
+ * { "type": "some_tpye" }
+ *
+ * @param opt_json JSON object reference
+ * @param dest allocated memory for the result
+ * @return MON_SUCCESS on success, error on error
+ */
+static MON_RC mon_decode_one_opt(json_t *opt_json, MON_OPT *dest)
+{
+  json_t *jstr = NULL;
+  MON_OPT_TYPE opt_type = OPT_TYPE_UNKNOWN;
+
+  if ( (opt_json == NULL) || (dest == NULL))
+    return MON_BADARG;
+
+  if (! json_is_object(opt_json))
+    return MON_NOPARSE;
+
+  jstr = json_object_get(opt_json, "type");
+  if ( (jstr == NULL) || (! json_is_string(jstr)) )
+    return MON_NOPARSE;
+
+  opt_type = mon_opt_type_from_string(json_string_value(jstr));
+  if (opt_type == OPT_TYPE_UNKNOWN)
+    return MON_NOPARSE;
+
+  dest->type = opt_type;
+  return MON_SUCCESS;
+}
+
+/**
+ * Decode options array
+ *
+ * Format:
+ * [{option}, {option}, ...]
+ *
+ */
+static MON_RC mon_options_decode(json_t *opts_json, MON_REQ *req)
+{
+  MON_OPT opt; // not a pointer
+  size_t n_opts=0;
+  size_t ii=0;
+
+  if ( (opts_json == NULL) || (req == NULL))
+    return MON_BADARG;
+
+  if (! json_is_array(opts_json))
+    return MON_NOPARSE;
+
+  n_opts = json_array_size(opts_json);
+  for (ii=0; ii < n_opts; ii++) {
+    if (mon_decode_one_opt(json_array_get(opts_json, ii),
+                           &opt) != MON_SUCCESS) {
+      return MON_NOPARSE;
+    }
+    mon_req_add_option(req, opt.type);
+  }
+  return MON_SUCCESS;
+}
+
+/**
+ * Parse a JSON string into a request
+ */
+MON_REQ *mon_req_parse(TALLOC_CTX *mem_ctx, const char *input)
+{
+  json_t *parsed_json = NULL;
+  json_error_t json_error;
+
+  parsed_json = json_loads(input, JSON_REJECT_DUPLICATES, &json_error);
+  return mon_req_decode(mem_ctx, parsed_json);
+}
+
+/**
+ * Decode a JSON request
+ *
+ * Expected format:
+ * {
+ *   "command": "some_command_name",
+ *   "options": [{option1}, ...]
+ * }
+ *
+ * (options are optional)
+ *
+ * Caller must free the return value with mon_req_free().
+ *
+ * @param mem_ctx talloc context for the returned struct
+ * @param req_json reference to JSON request object
+ * @return decoded request struct or NULL on failure
+ */
+MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  MON_REQ *req = NULL;
+  json_t *jval = NULL;
+  json_t *opts_json = NULL;
+  MON_CMD cmd = MON_CMD_UNKNOWN;
+
+  if (! json_is_object(req_json))
+    goto cleanup;
+
+  // Get the command and verify that it is a string value
+  jval = json_object_get(req_json, "command");
+  if (! json_is_string(jval))
+    goto cleanup;
+
+  cmd = mon_cmd_from_string(json_string_value(jval));
+  if (cmd == MON_CMD_UNKNOWN)
+    goto cleanup;
+
+  /* Command is good. Allocate the request in the tmp context */
+  req = mon_req_new(tmp_ctx, cmd);
+  if (req == NULL)
+    goto cleanup;
+
+  /* Parse options if we have any */
+  opts_json = json_object_get(req_json, "options");
+  if (opts_json) {
+    if (mon_options_decode(opts_json, req) != MON_SUCCESS) {
+      req = NULL; // memory still in tmp_ctx, so it will be cleaned up
+      goto cleanup;
+    }
+  }
+
+  /* Success! Put the request in the caller's talloc context */
+  talloc_steal(mem_ctx, req);
+
+cleanup:
+  talloc_free(tmp_ctx);
+
+  return req;
+}
diff --git a/mon/mon_req_encode.c b/mon/mon_req_encode.c
new file mode 100644 (file)
index 0000000..5bcf8b2
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <jansson.h>
+#include <glib.h>
+
+#include <mon_internal.h>
+
+// Monitoring request encoders
+
+/**
+ * Encode options array as a JSON array
+ *
+ * Format:
+ * [
+ *   { "type": "first_type" },
+ *   { "type": "second_type"},
+ *   ...
+ * ]
+ *
+ * @param opts array of options
+ * @return reference to a JSON array of options
+ */
+static json_t *mon_opts_decode(GArray *opts)
+{
+  json_t *array_json = json_array(); // the array of options
+  json_t *opt_json = NULL; // individual option JSON object
+  json_t *type_json = NULL;
+  guint ii = 0;
+  MON_OPT this_opt;
+
+  if (array_json == NULL)
+    return NULL; // failed
+
+  /* Iterate over the options */
+  for (ii=0; ii < opts->len; ii++) {
+    this_opt = g_array_index(opts, MON_OPT, ii);
+
+    /* Create the JSON object for this option */
+    opt_json = json_object();
+    if (opt_json == NULL) {
+      json_decref(array_json);
+      return NULL;
+    }
+
+    /* Add to the array, making opt_json a borrowed ref if we succeed */
+    if (json_array_append_new(array_json, opt_json) == -1) {
+      json_decref(array_json);
+      json_decref(opt_json); // handle ourselves because the set failed
+    }
+
+    /* Create the type string for this option */
+    type_json = json_string(mon_opt_type_to_string(this_opt.type));
+    if (type_json == NULL) {
+      json_decref(array_json);
+      return NULL;
+    }
+
+    /* Add the type string to the JSON object, making type_json a borrowed ref */
+    if (json_object_set_new(opt_json, "type", type_json) == -1) {
+      json_decref(array_json);
+      json_decref(type_json); // must handle ourselves because the set failed
+      return NULL;
+    }
+  }
+
+  return array_json;
+}
+
+/**
+ * Encode a request as a JSON object
+ *
+ * Caller must free the return value using json_decref()
+ *
+ * Format:
+ * {
+ *   "command": "some_command",
+ *   "options": [...see mon_opts_to_json()...]
+ * }
+ *
+ * @param req request to encode
+ * @return reference to a JSON object
+ */
+json_t *mon_req_encode(MON_REQ *req)
+{
+  json_t *req_json = NULL;
+  json_t *cmd_json = NULL;
+  json_t *opts_json = NULL;
+
+  /* Allocate the base JSON object */
+  req_json = json_object();
+  if (req_json == NULL)
+    return NULL;
+
+  /* Allocate the JSON string for the command */
+  cmd_json = json_string(mon_cmd_to_string(req->command));
+  if (cmd_json == NULL) {
+    json_decref(req_json);
+    return NULL;
+  }
+
+  /* Add the command string to the base object. Steals the reference to
+   * the string if successful. */
+  if (json_object_set_new(req_json, "command", cmd_json) == -1) {
+    json_decref(cmd_json); // must clean this up ourselves because the set failed
+    json_decref(req_json);
+    return NULL;
+  }
+
+  /* If we have options, add them to the object */
+  if (req->options->len > 0) {
+    opts_json = mon_opts_decode(req->options);
+    if (opts_json == NULL) {
+      json_decref(req_json);
+      return NULL;
+    }
+
+    if (json_object_set_new(req_json, "options", opts_json) == -1) {
+      json_decref(req_json);
+      json_decref(opts_json); // must clean this up ourselves because set failed
+      return NULL;
+    }
+  }
+
+  /* That's it, we succeeded */
+  return req_json;
+}
+
diff --git a/mon/mon_resp.c b/mon/mon_resp.c
new file mode 100644 (file)
index 0000000..f0729ad
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <tr_name_internal.h>
+
+#include <mon_internal.h>
+
+// Monitoring request message common code
+
+/**
+ * Destructor used by talloc to ensure proper cleanup
+ */
+static int mon_resp_destructor(void *object)
+{
+  MON_RESP *resp = talloc_get_type_abort(object, MON_RESP);
+  /* free the message */
+  if (resp->message) {
+    tr_free_name(resp->message);
+  }
+  /* free the payload */
+  if (resp->payload) {
+    json_decref(resp->payload);
+  }
+  return 0;
+}
+
+/**
+ * Allocate a new monitoring response
+ *
+ * Caller must free using mon_resp_free().
+ *
+ * Makes its own copy of the message, so caller can dispose of
+ * that after allocating the response.
+ *
+ * Increments the reference count of the payload if it is not null.
+ *
+ * @param mem_ctx talloc context for allocation
+ * @param req MON_REQ this response corresponds to
+ * @param code numeric response code
+ * @param msg string description of response code
+ * @param payload JSON object to be send as payload, or null for no payload
+ * @return response allocated in the requested talloc context, null on failure
+ */
+MON_RESP *mon_resp_new(TALLOC_CTX *mem_ctx, MON_RESP_CODE code, const char *msg, json_t *payload)
+{
+  MON_RESP *resp = talloc(mem_ctx, MON_RESP);
+  if (resp) {
+    resp->code = code;
+    resp->message = tr_new_name(msg);
+
+    resp->payload = payload;
+    if (resp->payload)
+      json_incref(resp->payload);
+
+    talloc_set_destructor((void *)resp, mon_resp_destructor);
+    if (resp->message == NULL) {
+      talloc_free(resp); // destructor will be called
+      resp = NULL;
+    }
+  }
+  return resp;
+}
+
+/**
+ * Set or replace the response message
+ *
+ * Does not change the message if it fails
+ *
+ * @param resp
+ * @param new_msg
+ * @return 1 on success, 0 on error
+ */
+int mon_resp_set_message(MON_RESP *resp, const char *new_msg)
+{
+  TR_NAME *n = tr_new_name(new_msg);
+
+  if (n == NULL)
+    return 0; /* failed */
+
+  if (resp->message)
+    tr_free_name(resp->message);
+  resp->message = n;
+  return 1; /* succeeded */
+}
+
+/**
+ * Set or replace the payload
+ *
+ * Manages JSON reference counts
+ *
+ * @param resp
+ * @param new_payload
+ */
+void mon_resp_set_payload(MON_RESP *resp, json_t *new_payload)
+{
+  if (resp->payload)
+    json_decref(resp->payload);
+  resp->payload = new_payload;
+  if (resp->payload)
+    json_incref(new_payload);
+}
+
+/**
+ * Free a monitoring response
+ *
+ * @param resp request to free, must not be null
+ */
+void mon_resp_free(MON_RESP *resp)
+{
+  talloc_free(resp);
+}
diff --git a/mon/mon_resp_decode.c b/mon/mon_resp_decode.c
new file mode 100644 (file)
index 0000000..83b0ac3
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <mon_internal.h>
+
+// Monitoring response decoder
+
+/**
+ * Decode a JSON response
+ *
+ * Expected format:
+ * {
+ *   "code": 0,
+ *   "message": "success",
+ *   "payload": {
+ *     "serial": 12345,
+ *     ...
+ *   }
+ * }
+ *
+ * Caller must free the return value with MON_REQ_free().
+ *
+ * @param mem_ctx talloc context for the returned struct
+ * @param resp_json reference to JSON request object
+ * @return decoded request struct or NULL on failure
+ */
+MON_RESP *mon_resp_decode(TALLOC_CTX *mem_ctx, json_t *resp_json)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  MON_RESP *resp = NULL;
+  json_t *jcode = NULL;
+  json_t *jmessage = NULL;
+  json_t *jpayload = NULL;
+
+  if (! json_is_object(resp_json))
+    goto cleanup;
+
+  /* Get the response code, which is an integer */
+  jcode = json_object_get(resp_json, "code");
+  if (! json_is_integer(jcode))
+    goto cleanup;
+
+  /* Get the response message, which is a string */
+  jmessage = json_object_get(resp_json, "message");
+  if (! json_is_string(jmessage))
+    goto cleanup;
+
+  /* Get the payload if we have one */
+  jpayload = json_object_get(resp_json, "payload");
+
+  /* Get a response in the tmp_ctx context. The payload may be null. */
+  resp = mon_resp_new(tmp_ctx,
+                      (MON_RESP_CODE) json_integer_value(jcode),
+                      json_string_value(jmessage),
+                      jpayload);
+  if (resp == NULL)
+    goto cleanup;
+
+  /* Success! Put the request in the caller's talloc context */
+  talloc_steal(mem_ctx, resp);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  if (resp_json)
+    json_decref(resp_json);
+
+  return resp;
+}
diff --git a/mon/mon_resp_encode.c b/mon/mon_resp_encode.c
new file mode 100644 (file)
index 0000000..7e41c65
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <mon_internal.h>
+
+/* Helper for encoding. Adds a newly allocated JSON object to
+ * jobj. If the allocation or setting fails, returns NULL after
+ * cleaning up. */
+#define object_set_or_free_and_return(jobj, tmp_jval, key, jval)  \
+  do {                                                            \
+    (tmp_jval) = (jval);                                          \
+    if ( (tmp_jval) == NULL) {                                    \
+      json_decref(jobj);                                          \
+      return NULL;                                                \
+    }                                                             \
+    if (json_object_set_new((jobj), (key), (tmp_jval)) == -1) {   \
+      json_decref(tmp_jval);                                      \
+      json_decref(jobj);                                          \
+      return NULL;                                                \
+    }                                                             \
+  } while(0)
+
+/**
+ * Encode a monitoring response as a JSON object
+ *
+ * Caller must ensure json_decref() is used to free the return value.
+ *
+ * @param resp response to encode
+ * @return response as a newly allocated JSON object
+ */
+json_t *mon_resp_encode(MON_RESP *resp)
+{
+  json_t *resp_json = NULL;
+  json_t *jval = NULL;
+
+  /* Get a JSON object */
+  resp_json = json_object();
+  if (resp_json == NULL)
+    return NULL;
+
+  /* Add properties, cleaning up and returning NULL on failure */
+  object_set_or_free_and_return(resp_json, jval, "code",    json_integer(resp->code));
+  object_set_or_free_and_return(resp_json, jval, "message", tr_name_to_json_string(resp->message));
+
+  /* If we have a payload, add it */
+  if (resp->payload) {
+    object_set_or_free_and_return(resp_json, jval, "payload", resp->payload);
+    json_incref(resp->payload); /* we just created a second reference to the payload */
+  }
+
+  return resp_json;
+}
diff --git a/mon/monc.c b/mon/monc.c
new file mode 100644 (file)
index 0000000..426b447
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2012, 2014-2018, 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 <jansson.h>
+#include <talloc.h>
+
+#include <trust_router/tr_dh.h>
+#include <mon_internal.h>
+#include <tr_msg.h>
+#include <gsscon.h>
+#include <tr_debug.h>
+
+
+MONC_INSTANCE *monc_new(TALLOC_CTX *mem_ctx)
+{
+  MONC_INSTANCE *monc=talloc(mem_ctx, MONC_INSTANCE);
+  if (monc!=NULL) {
+    monc->gssc = tr_gssc_instance_new(monc);
+    if (monc->gssc == NULL) {
+      talloc_free(monc);
+      return NULL;
+    }
+
+    monc->gssc->service_name = "trustmonitor";
+  }
+  return monc;
+}
+
+void monc_free(MONC_INSTANCE *monc)
+{
+  talloc_free(monc);
+}
+
+int monc_open_connection(MONC_INSTANCE *monc,
+                         const char *server,
+                         unsigned int port)
+{
+  return tr_gssc_open_connection(monc->gssc, server, port);
+}
+
+MON_RESP *monc_send_request(TALLOC_CTX *mem_ctx, MONC_INSTANCE *monc, MON_REQ *req)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_MSG *msg = NULL;
+  TR_MSG *resp_msg = NULL;
+  MON_RESP *resp = NULL;
+
+  /* Create and populate a msg structure */
+  if (!(msg = talloc_zero(tmp_ctx, TR_MSG)))
+    goto cleanup;
+
+  msg->msg_type = MON_REQUEST;
+  tr_msg_set_mon_req(msg, req);
+
+  resp_msg = tr_gssc_exchange_msgs(tmp_ctx, monc->gssc, msg);
+  if (resp_msg == NULL)
+    goto cleanup;
+
+  resp = tr_msg_get_mon_resp(resp_msg);
+
+  /* if we got a response, steal it from resp_msg's context so we can return it */
+  if (resp)
+    talloc_steal(mem_ctx, resp);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return resp;
+}
+
+DH *monc_get_dh(MONC_INSTANCE *inst)
+{
+  return tr_gssc_get_dh(inst->gssc);
+}
+
+DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh)
+{
+  return tr_gssc_set_dh(inst->gssc, dh);
+}
diff --git a/mon/mons.c b/mon/mons.c
new file mode 100644 (file)
index 0000000..f2e69c5
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <tr.h>
+#include <tr_debug.h>
+#include <mon_internal.h>
+#include <tr_socket.h>
+#include <sys/wait.h>
+#include <tr_gss.h>
+
+#include "mons_handlers.h"
+
+static void mons_sweep_procs(MONS_INSTANCE *mons);
+
+static int mons_destructor(void *object)
+{
+  MONS_INSTANCE *mons = talloc_get_type_abort(object, MONS_INSTANCE);
+  if (mons->handlers)
+    g_ptr_array_unref(mons->handlers);
+
+  if (mons->pids)
+    g_array_unref(mons->pids);
+
+  return 0;
+}
+
+/**
+ * Allocate a new MONS_INSTANCE
+ *
+ * @param mem_ctx talloc context for allocation
+ * @return new MONS_INSTANCE or null on failure
+ */
+MONS_INSTANCE *mons_new(TALLOC_CTX *mem_ctx)
+{
+  MONS_INSTANCE *mons = talloc(mem_ctx, MONS_INSTANCE);
+
+  if (mons) {
+    mons->hostname = NULL;
+    mons->port = 0;
+    mons->tids = NULL;
+    mons->trps = NULL;
+    mons->req_handler = NULL;
+    mons->auth_handler = NULL;
+    mons->cookie = NULL;
+
+    /* Before any steps that may fail, install the destructor */
+    talloc_set_destructor((void *)mons, mons_destructor);
+
+    mons->authorized_gss_names = tr_gss_names_new(mons);
+    if (mons->authorized_gss_names == NULL) {
+      talloc_free(mons);
+      return NULL;
+    }
+
+    mons->handlers = g_ptr_array_new();
+    if (mons->handlers == NULL) {
+      talloc_free(mons);
+      return NULL;
+    }
+
+    mons->pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
+    if (mons->pids == NULL) {
+      talloc_free(mons);
+      return NULL;
+    }
+  }
+  return mons;
+}
+
+/**
+ * Callback to process a request and produce a response
+ *
+ * @param req_str JSON-encoded request
+ * @param data pointer to a MONS_INSTANCE
+ * @return pointer to the response string or null to send no response
+ */
+static TR_MSG *mons_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *req_msg, void *data)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  MONS_INSTANCE *mons = talloc_get_type_abort(data, MONS_INSTANCE);
+  MON_REQ *req = NULL;
+  MON_RESP *resp = NULL;
+  TR_MSG *resp_msg = NULL; /* This is the response value */
+
+  /* Validate inputs */
+  if (req_msg == NULL)
+    goto cleanup;
+
+  req = tr_msg_get_mon_req(req_msg);
+  if (req == NULL) {
+    /* this is an internal error */
+    tr_err("mons_req_cb: Received incorrect message type (was %d, expected %d)",
+           tr_msg_get_msg_type(req_msg),
+           MON_REQUEST);
+    /* TODO send an error response */
+    goto cleanup;
+  }
+
+  /* Allocate a response message */
+  resp_msg = talloc(tmp_ctx, TR_MSG);
+  if (resp_msg == NULL) {
+    /* can't return a message, just emit an error */
+    tr_crit("mons_req_cb: Error allocating response message.");
+    goto cleanup;
+  }
+
+  /* Handle the request */
+  resp = mons_handle_request(resp_msg, mons, req);
+  if (resp == NULL) {
+    /* error processing the request */
+    /* TODO send back an error */
+    goto cleanup;
+  }
+
+  /* Set the response message payload */
+  tr_msg_set_mon_resp(resp_msg, resp);
+
+  /* Put the response message in the caller's context so it does not get freed when we exit */
+  talloc_steal(mem_ctx, resp_msg);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return resp_msg;
+}
+
+/**
+ * Create a listener for monitoring requests
+ *
+ * Accept connections with mons_accept()
+ *
+ * @param mons monitoring server instance
+ * @param req_handler
+ * @param auth_handler
+ * @param hostname
+ * @param port
+ * @param cookie
+ * @param fd_out
+ * @param max_fd
+ * @return
+ */
+int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH_FUNC *auth_handler, const char *hostname,
+                      unsigned int port, void *cookie, int *fd_out, size_t max_fd)
+{
+  size_t n_fd=0;
+  size_t ii=0;
+
+  mons->port = port;
+  n_fd = tr_sock_listen_all(port, fd_out, max_fd);
+  if (n_fd<=0)
+    tr_err("mons_get_listener: Error opening port %d");
+  else {
+    /* opening port succeeded */
+    tr_info("mons_get_listener: Opened port %d.", port);
+
+    /* make this socket non-blocking */
+    for (ii=0; ii<n_fd; ii++) {
+      if (0 != fcntl(fd_out[ii], F_SETFL, O_NONBLOCK)) {
+        tr_err("mons_get_listener: Error setting O_NONBLOCK.");
+        for (ii=0; ii<n_fd; ii++) {
+          close(fd_out[ii]);
+          fd_out[ii]=-1;
+        }
+        n_fd=0;
+        break;
+      }
+    }
+  }
+
+  if (n_fd>0) {
+    /* store the caller's request handler & cookie */
+    mons->req_handler = req_handler;
+    mons->auth_handler = auth_handler;
+    mons->hostname = hostname;
+    mons->cookie = cookie;
+  }
+
+  return (int) n_fd;
+}
+
+/**
+ * Accept and process a connection on a port opened with mons_get_listener()
+ *
+ * @param mons monitoring interface instance
+ * @param listen FD of the connection socket
+ * @return 0 on success
+ */
+int mons_accept(MONS_INSTANCE *mons, int listen)
+{
+  int conn=-1;
+  int pid=-1;
+
+  if (0 > (conn = accept(listen, NULL, NULL))) {
+    perror("Error from monitoring interface accept()");
+    return 1;
+  }
+
+  if (0 > (pid = fork())) {
+    perror("Error on fork()");
+    return 1;
+  }
+
+  if (pid == 0) {
+    close(listen);
+    tr_gss_handle_connection(conn,
+                             "trustmonitor", mons->hostname, /* acceptor name */
+                             mons->auth_handler, mons->cookie, /* auth callback and cookie */
+                             mons_req_cb, mons /* req callback and cookie */
+    );
+    close(conn);
+    exit(0); /* exit to kill forked child process */
+  }
+
+  /* Only the parent process gets here */
+  close(conn);
+  g_array_append_val(mons->pids, pid);
+
+  /* clean up any processes that have completed */
+  mons_sweep_procs(mons);
+
+  return 0;
+}
+
+void mons_sweep_procs(MONS_INSTANCE *mons)
+{
+  guint ii;
+  pid_t pid;
+  int status;
+
+  /* loop backwards over the array so we can remove elements as we go */
+  for (ii=mons->pids->len; ii > 0; ii--) {
+    /* ii-1 is the current index */
+    pid = g_array_index(mons->pids, pid_t, ii-1);
+    if (waitpid(pid, &status, WNOHANG) > 0) {
+      /* the process exited */
+      tr_debug("mons_sweep_procs: monitoring process %d terminated.", pid);
+
+      g_array_remove_index_fast(mons->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */
+      if (WIFEXITED(status)) {
+        if (WEXITSTATUS(status) == 0)
+          tr_debug("mons_sweep_procs: monitoring process %d succeeded.", pid);
+        else
+          tr_debug("mons_sweep_procs: monitoring process %d exited with status %d.", pid, WTERMSIG(status));
+      } else if (WIFSIGNALED(status)) {
+        tr_debug("mons_sweep_procs: monitoring process %d terminated by signal %d.", pid, WTERMSIG(status));
+      }
+    }
+  }
+}
diff --git a/mon/mons_handlers.c b/mon/mons_handlers.c
new file mode 100644 (file)
index 0000000..5e19db0
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+/* Handlers for monitoring requests */
+
+#include <gmodule.h>
+
+#include <tr_debug.h>
+#include <mon_internal.h>
+#include <mons_handlers.h>
+
+
+/* Static Prototypes */
+static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e, MON_CMD command, MON_OPT_TYPE opt_type);
+static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type);
+static void request_helper(void *element, void *data);
+
+struct request_helper_data {
+  MON_CMD command;
+  MON_OPT_TYPE opt_type;
+  json_t *payload; /* json object to add responses to */
+  GArray *results;
+};
+
+struct handler_result {
+  MON_OPT_TYPE opt_type; /* what opt type set this? */
+  MON_RC rc;             /* what was its result code? */
+  json_t *json_data;        /* what data, if any, is it returning? */
+};
+
+/**
+ * Call the appropriate handler for a request
+ *
+ * TODO: report errors from handlers
+ *
+ * @return a MON_RESP structure or null if there was a processing error
+ */
+MON_RESP *mons_handle_request(TALLOC_CTX *mem_ctx, MONS_INSTANCE *mons, MON_REQ *req)
+{
+  MON_RESP *resp = NULL;
+  json_t *payload = NULL;
+  struct request_helper_data cookie = {0};
+  size_t ii = 0;
+
+  tr_debug("mons_handle_request: Handling a request");
+
+  /* Start off by allocating our response with a generic error message */
+  resp = mon_resp_new(mem_ctx,
+                      MON_RESP_ERROR,
+                      "error processing request",
+                      NULL);
+  if (resp == NULL) {
+    /* we can't respond, just return */
+    tr_crit("mons_handle_request: Error allocating response structure.");
+    goto cleanup;
+  }
+
+  /* Now get a JSON object for our return payload */
+  payload = json_object();
+  if (payload == NULL) {
+    tr_crit("mons_handle_request: Error allocating response payload.");
+    goto cleanup; /* This will return the generic error message set earlier */
+  }
+
+  /* Now call handlers */
+  cookie.command = req->command;
+  cookie.results = g_array_new(FALSE, TRUE, sizeof(struct handler_result));
+
+  if (mon_req_opt_count(req) == 0) {
+    /* call every handler that matches the command */
+    cookie.opt_type = OPT_TYPE_ANY;
+    g_ptr_array_foreach(mons->handlers, request_helper, &cookie);
+  } else {
+    /* call only those handlers that match an option */
+    for (ii=0; ii < mon_req_opt_count(req); ii++) {
+      cookie.opt_type = mon_req_opt_index(req, ii)->type;
+      /* Loop over all handlers - we know we can only have one match for each opt type */
+      g_ptr_array_foreach(mons->handlers, request_helper, &cookie);
+    }
+  }
+
+  /* We now have an array of results in cookie.results. If any of these failed, return an error. */
+  tr_debug("mons_handle_request: Examining %d handler results", cookie.results->len);
+  resp->code = MON_RESP_SUCCESS; /* tentatively set this to success */
+  for (ii=0; ii < cookie.results->len; ii++) {
+    struct handler_result *this = &g_array_index(cookie.results, struct handler_result, ii);
+    if (this->rc != MON_SUCCESS) {
+      tr_debug("mons_handle_request: Result %d was an error.", ii);
+      resp->code = MON_RESP_ERROR;
+    }
+
+    /* add the JSON response even if there was an error */
+    if (this->json_data) {
+      tr_debug("mons_handle_request: Result %d returned JSON data.", ii);
+      json_object_set_new(payload, mon_opt_type_to_string(this->opt_type), this->json_data);
+    }
+  }
+
+  if (resp->code == MON_RESP_SUCCESS) {
+    if (mon_resp_set_message(resp, "success") == 0) {
+      /* Failed to set the response message to success - fail ironically, don't send
+       * an inconsistent response. */
+      tr_crit("mons_handle_request: Error setting response message to 'success'.");
+      goto cleanup;
+    }
+  } else {
+    /* Failed - send a response indicating that the overall command succeeded */
+    if (mon_resp_set_message(resp, "request processed but an error occurred") == 0) {
+      tr_crit("mons_handle_request: Error setting response message after a handler error.");
+      goto cleanup;
+    }
+  }
+
+  /* Attach the accumulated payload to the response */
+  if (json_object_size(payload) > 0) {
+    tr_debug("mons_handle_request: Attaching payload to response.");
+    mon_resp_set_payload(resp, payload);
+  }
+
+  tr_debug("mons_handle_request: Successfully processed request.");
+
+cleanup:
+  if (payload)
+    json_decref(payload);
+  if (cookie.results)
+    g_array_free(cookie.results, TRUE);
+  return resp;
+}
+
+/**
+ * Register a handler for a command/option combination
+ *
+ * @param mons
+ * @param cmd
+ * @param opt_type
+ * @param f
+ * @param cookie
+ * @return
+ */
+MON_RC mons_register_handler(MONS_INSTANCE *mons,
+                             MON_CMD cmd,
+                             MON_OPT_TYPE opt_type,
+                             MONS_HANDLER_FUNC *f,
+                             void *cookie)
+{
+  MONS_DISPATCH_TABLE_ENTRY *entry = NULL;
+
+  if (mons_find_handler(mons, cmd, opt_type) != NULL) {
+    return MON_ERROR;
+  }
+
+  /* Put these in the mons talloc context so we don't have to muck about with
+   * a free function for the GPtrArray */
+  entry = talloc(mons, MONS_DISPATCH_TABLE_ENTRY);
+  if (entry == NULL) {
+    return MON_NOMEM;
+  }
+  entry->command = cmd;
+  entry->opt_type = opt_type;
+  entry->handler = f;
+  entry->cookie = cookie;
+
+  g_ptr_array_add(mons->handlers, entry);
+  return MON_SUCCESS;
+}
+
+/**
+ * Two table entries match if none of the commands or opt_types are unknown,
+ * if the commands match, and if the opt types either match or at least one is
+ * OPT_TYPE_ANY.
+ *
+ * No comparison of the handler pointer is included.
+ *
+ * @return 1 if the two match, 0 if not
+ */
+static int dispatch_entry_matches(MONS_DISPATCH_TABLE_ENTRY *e,
+                                  MON_CMD command,
+                                  MON_OPT_TYPE opt_type)
+{
+  if ((command == MON_CMD_UNKNOWN) || (opt_type == OPT_TYPE_UNKNOWN))
+    return 0; /* request is invalid */
+
+  if ((e->command == MON_CMD_UNKNOWN) || (e->opt_type == OPT_TYPE_UNKNOWN))
+    return 0; /* e1 is invalid */
+
+  if (e->command != command)
+    return 0; /* commands do not match */
+
+  if (e->opt_type == opt_type)
+    return 1; /* exact match */
+
+  if ( (e->opt_type == OPT_TYPE_ANY) || (opt_type == OPT_TYPE_ANY) )
+    return 1; /* one is a wildcard */
+
+  return 0; /* commands matched but opt_types did not */
+}
+
+static MONS_HANDLER_FUNC *mons_find_handler(MONS_INSTANCE *mons, MON_CMD cmd, MON_OPT_TYPE opt_type)
+{
+  guint index;
+
+  for (index=0; index < mons->handlers->len; index++) {
+    if (dispatch_entry_matches(g_ptr_array_index(mons->handlers, index), cmd, opt_type))
+      return g_ptr_array_index(mons->handlers, index);
+  }
+  return NULL;
+}
+
+/**
+ * This calls every request handler that matches a command/opt_type,
+ * gathering their results.
+ *
+ * @param element
+ * @param data
+ */
+static void request_helper(void *element, void *data)
+{
+  MONS_DISPATCH_TABLE_ENTRY *entry = talloc_get_type_abort(element, MONS_DISPATCH_TABLE_ENTRY);
+  struct request_helper_data *helper_data = data;
+  struct handler_result result = {0};
+
+  if (dispatch_entry_matches(entry, helper_data->command, helper_data->opt_type)) {
+    result.rc = entry->handler(entry->cookie, &(result.json_data));
+    result.opt_type = entry->opt_type;
+    g_array_append_val(helper_data->results, result);
+  }
+}
+
diff --git a/mon/tests/req_reconfigure.test b/mon/tests/req_reconfigure.test
new file mode 100644 (file)
index 0000000..75f1690
--- /dev/null
@@ -0,0 +1 @@
+{"command": "reconfigure"}
diff --git a/mon/tests/req_show_all_options.test b/mon/tests/req_show_all_options.test
new file mode 100644 (file)
index 0000000..799be44
--- /dev/null
@@ -0,0 +1 @@
+{"command": "show", "options": [{"type": "serial"}, {"type": "version"}, {"type": "uptime"}, {"type": "tid_req_count"}, {"type": "tid_req_pending"}, {"type": "routes"}, {"type": "communities"}]}
diff --git a/mon/tests/req_show_no_options.test b/mon/tests/req_show_no_options.test
new file mode 100644 (file)
index 0000000..af1cdce
--- /dev/null
@@ -0,0 +1 @@
+{"command": "show"}
diff --git a/mon/tests/resp_reconfigure_error.test b/mon/tests/resp_reconfigure_error.test
new file mode 100644 (file)
index 0000000..3344bb1
--- /dev/null
@@ -0,0 +1 @@
+{"code": 1, "message": "error"}
diff --git a/mon/tests/resp_reconfigure_success.test b/mon/tests/resp_reconfigure_success.test
new file mode 100644 (file)
index 0000000..392c2e0
--- /dev/null
@@ -0,0 +1 @@
+{"code": 0, "message": "success"}
diff --git a/mon/tests/resp_show_success.test b/mon/tests/resp_show_success.test
new file mode 100644 (file)
index 0000000..bfbec19
--- /dev/null
@@ -0,0 +1 @@
+{"code": 0, "message": "success", "show": {"version": "1.2.3-4", "serial": 86400, "tid_req_pending": 13, "tid_req_count": 1432}}
diff --git a/mon/tests/test_mon_req_decode.c b/mon/tests/test_mon_req_decode.c
new file mode 100644 (file)
index 0000000..19ad64d
--- /dev/null
@@ -0,0 +1,137 @@
+//
+// Created by jlr on 4/9/18.
+//
+
+#include <talloc.h>
+#include <jansson.h>
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+#include <mon_internal.h>
+
+/**
+ * @return reconfigure command
+ */
+static MON_REQ *reconfigure()
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_RECONFIGURE);
+  assert(req);
+  return req;
+}
+
+/**
+ * @return show command with no options
+ */
+static MON_REQ *show_plain()
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW);
+  assert(req);
+  return req;
+}
+
+/**
+ * @param opts array of option types, terminated with OPT_TYPE_UNKNOWN
+ * @return show command with the requested options, excluding the terminator
+ */
+static MON_REQ *show_options(const MON_OPT_TYPE *opts)
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW);
+  assert(req);
+
+  while (*opts != OPT_TYPE_UNKNOWN) {
+    assert(MON_SUCCESS == mon_req_add_option(req, *opts));
+    opts++;
+  }
+  return req;
+}
+
+/**
+ * @return show command with every option
+ */
+static MON_REQ *show_all_options()
+{
+  MON_OPT_TYPE opts[] = {
+      OPT_TYPE_SHOW_CONFIG_FILES,
+      OPT_TYPE_SHOW_VERSION,
+      OPT_TYPE_SHOW_UPTIME,
+      OPT_TYPE_SHOW_TID_REQ_COUNT,
+      OPT_TYPE_SHOW_TID_REQ_PENDING,
+      OPT_TYPE_SHOW_ROUTES,
+      OPT_TYPE_SHOW_COMMUNITIES,
+      OPT_TYPE_UNKNOWN // terminator
+  };
+
+  return show_options(opts);
+}
+
+static char *read_file(const char *filename)
+{
+  FILE *f = fopen(filename, "r");
+  char *s = NULL;
+  size_t nn = 0;
+  ssize_t n = getline(&s, &nn, f);
+
+  fclose(f);
+
+  if( (n > 0) && (s[n-1] == '\n'))
+    s[n-1] = 0;
+
+  return s;
+}
+
+static int equal(MON_REQ *r1, MON_REQ *r2)
+{
+  size_t ii;
+
+  if (r1->command != r2->command)
+    return 0;
+
+  if (mon_req_opt_count(r1) != mon_req_opt_count(r2))
+    return 0;
+
+  for (ii=0; ii < mon_req_opt_count(r1); ii++) {
+    if (mon_req_opt_index(r1, ii)->type != mon_req_opt_index(r2, ii)->type)
+      return 0;
+  }
+
+  return 1;
+}
+
+static int run_test(const char *filename, MON_REQ *(generator)())
+{
+  MON_REQ *req = NULL;
+  MON_REQ *expected = NULL;
+  char *req_json_str = NULL;
+
+  expected = generator();
+  assert(expected);
+
+  req_json_str = read_file(filename);
+  assert(req_json_str);
+
+  req = mon_req_parse(NULL, req_json_str);
+  assert(req);
+  assert(equal(req, expected));
+
+  free(req_json_str);
+  mon_req_free(req);
+  mon_req_free(expected);
+
+  return 1;
+}
+
+int main(void)
+{
+
+  // Test reconfigure command
+  assert(run_test("req_reconfigure.test", reconfigure));
+
+  // Test show command with no options
+  assert(run_test("req_show_no_options.test", show_plain));
+
+  // Test show command with all the options
+  assert(run_test("req_show_all_options.test", show_all_options));
+
+  return 0;
+}
\ No newline at end of file
diff --git a/mon/tests/test_mon_req_encode.c b/mon/tests/test_mon_req_encode.c
new file mode 100644 (file)
index 0000000..d3ae49c
--- /dev/null
@@ -0,0 +1,126 @@
+//
+// Created by jlr on 4/9/18.
+//
+
+#include <talloc.h>
+#include <jansson.h>
+#include <assert.h>
+#include <string.h>
+#include <glib.h>
+
+#include <mon_internal.h>
+
+#define JSON_DUMP_OPTS 0
+
+static char *reconfigure()
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_RECONFIGURE);
+  json_t *req_json = mon_req_encode(req);
+  char *result = json_dumps(req_json, JSON_DUMP_OPTS);
+  assert(req);
+  assert(req_json);
+  assert(result);
+  json_decref(req_json);
+  mon_req_free(req);
+  return result;
+}
+
+static char *show_plain()
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW);
+  json_t *req_json = mon_req_encode(req);
+  char *result = json_dumps(req_json, JSON_DUMP_OPTS);
+  assert(req);
+  assert(req_json);
+  assert(result);
+  json_decref(req_json);
+  mon_req_free(req);
+  return result;
+}
+
+static char *show_options(const MON_OPT_TYPE *opts)
+{
+  MON_REQ *req = mon_req_new(NULL, MON_CMD_SHOW);
+  json_t *req_json = NULL;
+  char *result = NULL;
+
+  assert(req);
+
+  while (*opts != OPT_TYPE_UNKNOWN) {
+    assert(MON_SUCCESS == mon_req_add_option(req, *opts));
+    opts++;
+  }
+
+  req_json = mon_req_encode(req);
+  assert(req_json);
+
+  result = json_dumps(req_json, JSON_DUMP_OPTS);
+  assert(result);
+
+  json_decref(req_json);
+  mon_req_free(req);
+  return result;
+}
+
+static char *read_file(const char *filename)
+{
+  FILE *f = fopen(filename, "r");
+  char *s = NULL;
+  size_t nn = 0;
+  ssize_t n = getline(&s, &nn, f);
+  fclose(f);
+
+  if( (n > 0) && (s[n-1] == '\n'))
+    s[n-1] = 0;
+
+  return s;
+}
+int main(void)
+{
+  char *s = NULL;
+  MON_OPT_TYPE opts[10];
+  char *expected = NULL;
+
+  // Test reconfigure command
+  s = reconfigure();
+  expected = read_file("req_reconfigure.test");
+  assert(expected);
+  assert(strcmp(expected, s) == 0);
+  free(s);
+  free(expected);
+
+  // Test show without options
+  s = show_plain();
+  expected = read_file("req_show_no_options.test");
+  assert(expected);
+  assert(strcmp(expected, s) == 0);
+  free(s);
+  free(expected);
+
+  // Test show with empty options (this mostly tests the test)
+  opts[0] = OPT_TYPE_UNKNOWN;
+  s = show_options(opts);
+  expected = read_file("req_show_no_options.test");
+  assert(expected);
+  assert(strcmp(expected, s) == 0);
+  free(s);
+  free(expected);
+
+  // Test show with many options
+  opts[0] = OPT_TYPE_SHOW_CONFIG_FILES;
+  opts[1] = OPT_TYPE_SHOW_VERSION;
+  opts[2] = OPT_TYPE_SHOW_UPTIME;
+  opts[3] = OPT_TYPE_SHOW_TID_REQ_COUNT;
+  opts[4] = OPT_TYPE_SHOW_TID_REQ_PENDING;
+  opts[5] = OPT_TYPE_SHOW_ROUTES;
+  opts[6] = OPT_TYPE_SHOW_COMMUNITIES;
+  opts[7] = OPT_TYPE_UNKNOWN;
+  s = show_options(opts);
+  expected = read_file("req_show_all_options.test");
+  assert(expected);
+  assert(strcmp(expected, s) == 0);
+  free(s);
+  free(expected);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/mon/tests/test_mon_resp_encode.c b/mon/tests/test_mon_resp_encode.c
new file mode 100644 (file)
index 0000000..6945b76
--- /dev/null
@@ -0,0 +1,130 @@
+//
+// Created by jlr on 4/9/18.
+//
+
+#include <talloc.h>
+#include <jansson.h>
+#include <assert.h>
+#include <string.h>
+
+#include <mon_internal.h>
+
+#define JSON_DUMP_OPTS 0
+
+static char *reconfigure(MON_RESP_CODE code, const char *message)
+{
+  MON_REQ *req = NULL;
+  MON_RESP *resp = NULL;
+  json_t *resp_json = NULL;
+  char *result = NULL;
+
+  req = mon_req_new(NULL, MON_CMD_RECONFIGURE);
+  assert(req);
+
+  resp = mon_resp_new(NULL, code, message, NULL);
+  assert(resp);
+
+  resp_json = mon_resp_encode(resp);
+  assert(resp_json);
+
+  result = json_dumps(resp_json, JSON_DUMP_OPTS);
+  assert(result);
+
+  json_decref(resp_json);
+  mon_resp_free(resp);
+  mon_req_free(req);
+  return result;
+}
+
+static char *reconfigure_success()
+{
+  return reconfigure(MON_RESP_SUCCESS, "success");
+}
+
+static char *reconfigure_error()
+{
+  return reconfigure(MON_RESP_ERROR, "error");
+}
+
+static char *show_success()
+{
+  MON_REQ *req = NULL;
+  MON_RESP *resp = NULL;
+  json_t *resp_json = NULL;
+  json_t *payload = NULL;
+  char *result = NULL;
+
+  req = mon_req_new(NULL, MON_CMD_SHOW);
+  // Only need the command to be set in req, don't actually need the options
+  assert(req);
+
+  payload = json_object();
+  assert(payload);
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_VERSION),
+                               json_string("1.2.3-4")));
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_CONFIG_FILES),
+                               json_integer(1234567890)));
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_CONFIG_FILES),
+                               json_integer(86400)));
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_TID_REQ_PENDING),
+                               json_integer(13)));
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_TID_REQ_COUNT),
+                               json_integer(1432)));
+
+  resp = mon_resp_new(NULL, MON_RESP_SUCCESS, "success", payload);
+  assert(resp);
+
+  resp_json = mon_resp_encode(resp);
+  assert(resp_json);
+
+  result = json_dumps(resp_json, JSON_DUMP_OPTS);
+  assert(result);
+
+  json_decref(resp_json);
+  mon_resp_free(resp);
+  mon_req_free(req);
+  return result;
+}
+
+static char *read_file(const char *filename)
+{
+  FILE *f = fopen(filename, "r");
+  char *s = NULL;
+  size_t nn = 0;
+  ssize_t n = getline(&s, &nn, f);
+  fclose(f);
+
+  if( (n > 0) && (s[n-1] == '\n'))
+    s[n-1] = 0;
+
+  return s;
+}
+
+int run_test(const char *filename, char *(generator)())
+{
+  char *s = NULL;
+  char *expected = NULL;
+
+  // Test reconfigure command
+  s = generator();
+  expected = read_file(filename);
+  assert(expected);
+  assert(strcmp(expected, s) == 0);
+  free(s);
+  free(expected);
+
+  return 1;
+}
+
+int main(void)
+{
+  assert(run_test("resp_reconfigure_success.test", reconfigure_success));
+  assert(run_test("resp_reconfigure_error.test", reconfigure_error));
+  assert(run_test("resp_show_success.test", show_success));
+  return 0;
+}
index 26fb994..02cd1db 100644 (file)
@@ -210,7 +210,8 @@ int main (int argc,
  
   /* Create a TID client instance & the client DH */
   tidc = tidc_create();
-  if (NULL == (tidc->client_dh = tr_create_dh_params(NULL, 0))) {
+  tidc_set_dh(tidc, tr_create_dh_params(NULL, 0));
+  if (tidc_get_dh(tidc) == NULL) {
     printf("Error creating client DH params.\n");
     return 1;
   }
index 8673cdd..1ecc494 100644 (file)
@@ -167,9 +167,6 @@ static int tids_req_handler (TIDS_INSTANCE *tids,
   
 
   tr_debug("tids_req_handler: Request received! target_realm = %s, community = %s", req->realm->buf, req->comm->buf);
-  if (tids)
-    tids->req_count++;
-
   if (!(resp) || !resp) {
     tr_debug("tids_req_handler: No response structure.");
     return -1;
index 967b1fc..002b720 100644 (file)
@@ -167,7 +167,7 @@ TR_NAME *tid_req_get_orig_coi(TID_REQ *req)
   return(req->orig_coi);
 }
 
-void tid_req_set_rp_orig_coi(TID_REQ *req, TR_NAME *orig_coi)
+void tid_req_set_orig_coi(TID_REQ *req, TR_NAME *orig_coi)
 {
   req->orig_coi = orig_coi;
 }
index 4f46b12..3ff3d02 100644 (file)
@@ -83,6 +83,13 @@ void tid_resp_free(TID_RESP *resp)
     talloc_free(resp);
 }
 
+/**
+ * Allocate a new copy of a TID_RESP
+ *
+ * @param mem_ctx
+ * @param resp
+ * @return
+ */
 TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp)
 {
   TID_RESP *newresp=NULL;
@@ -93,19 +100,38 @@ TID_RESP *tid_resp_dup(TALLOC_CTX *mem_ctx, TID_RESP *resp)
   newresp=tid_resp_new(mem_ctx);
 
   if (NULL!=newresp) {
-    newresp->result=resp->result;
-    newresp->err_msg=tr_dup_name(resp->err_msg);
-    newresp->rp_realm=tr_dup_name(resp->rp_realm);
-    newresp->realm=tr_dup_name(resp->realm);
-    newresp->comm=tr_dup_name(resp->comm);
-    newresp->orig_coi=tr_dup_name(resp->orig_coi);
-    newresp->servers=tid_srvr_blk_dup(newresp, resp->servers);
-    tid_resp_set_cons(newresp, resp->cons);
-    tid_resp_set_error_path(newresp, resp->error_path);
+    tid_resp_cpy(newresp, resp);
   }
   return newresp;
 }
 
+/**
+ * Copy contents of one TID_RESP to an existing TID_RESP
+ *
+ * @param dst
+ * @param src
+ * @return TID_SUCCESS on success, error code on error
+ */
+TID_RC tid_resp_cpy(TID_RESP *dst, TID_RESP *src)
+{
+  tid_resp_set_result(dst, tid_resp_get_result(src));
+  tid_resp_set_err_msg(dst,
+                       tr_dup_name(tid_resp_get_err_msg(src)));
+  tid_resp_set_rp_realm(dst,
+                        tr_dup_name(tid_resp_get_rp_realm(src)));
+  tid_resp_set_realm(dst,
+                     tr_dup_name(tid_resp_get_realm(src)));
+  tid_resp_set_comm(dst,
+                    tr_dup_name(tid_resp_get_comm(src)));
+  tid_resp_set_cons(dst, src->cons);
+  tid_resp_set_orig_coi(dst,
+                        tr_dup_name(tid_resp_get_orig_coi(src)));
+  dst->servers = tid_srvr_blk_dup(dst, src->servers);
+  tid_resp_set_error_path(dst, src->error_path);
+
+  return TID_SUCCESS;
+}
+
 TR_EXPORT int tid_resp_get_result(TID_RESP *resp)
 {
   return(resp->result);
index 320ded6..90335f0 100644 (file)
 #include <jansson.h>
 #include <talloc.h>
 
+#include <gsscon.h>
 #include <trust_router/tr_dh.h>
 #include <tid_internal.h>
 #include <tr_msg.h>
-#include <gsscon.h>
 #include <tr_debug.h>
 #include <tr_rand_id.h>
 
 
 int tmp_len = 32;
 
-static int tidc_destructor(void *obj)
-{
-  TIDC_INSTANCE *tidc=talloc_get_type_abort(obj, TIDC_INSTANCE);
-  if (NULL!=tidc) {
-    if (NULL!=tidc->client_dh)
-      tr_destroy_dh_params(tidc->client_dh);
-  }
-  return 0;
-}
-
 /* creates struct in talloc null context */
 TIDC_INSTANCE *tidc_create(void)
 {
   TIDC_INSTANCE *tidc=talloc(NULL, TIDC_INSTANCE);
   if (tidc!=NULL) {
-    tidc->client_dh=NULL;
-    talloc_set_destructor((void *)tidc, tidc_destructor);
+    tidc->gssc = tr_gssc_instance_new(tidc);
+    if (tidc->gssc == NULL) {
+      talloc_free(tidc);
+      return NULL;
+    }
+
+    tidc->gssc->service_name = "trustidentity";
   }
   return tidc;
 }
@@ -73,44 +68,56 @@ void tidc_destroy(TIDC_INSTANCE *tidc)
 }
 
 int tidc_open_connection (TIDC_INSTANCE *tidc, 
-                         const char *server,
-                         unsigned int port,
-                         gss_ctx_id_t *gssctx)
+                          const char *server,
+                          unsigned int port,
+                          gss_ctx_id_t *gssctx)
 {
-  int err = 0;
-  int conn = -1;
   unsigned int use_port = 0;
+  tidc->gssc->gss_ctx = gssctx;
 
   if (0 == port)
     use_port = TID_PORT;
   else
     use_port = port;
 
-  tr_debug("tidc_open_connection: opening tidc connection to %s:%d", server, port);
-  err = gsscon_connect(server, use_port, "trustidentity", &conn, gssctx);
-
-  if (!err)
-    return conn;
+  tr_debug("tidc_open_connection: opening tidc connection to %s:%d", server, use_port);
+  if (0 == tr_gssc_open_connection(tidc->gssc, server, use_port))
+    return tidc->gssc->conn;
   else
     return -1;
 }
 
 int tidc_send_request (TIDC_INSTANCE *tidc,
-                      int conn,
-                      gss_ctx_id_t gssctx,
-                      const char *rp_realm,
-                      const char *realm, 
-                      const char *comm,
-                      TIDC_RESP_FUNC *resp_handler,
-                      void *cookie)
+                       int conn,
+                       gss_ctx_id_t gssctx,
+                       const char *rp_realm,
+                       const char *realm,
+                       const char *comm,
+                       TIDC_RESP_FUNC *resp_handler,
+                       void *cookie)
 {
   TID_REQ *tid_req = NULL;
   char *request_id = NULL;
   int rc;
+  int orig_conn = 0;
+  gss_ctx_id_t *orig_gss_ctx = NULL;
+
+  /* For ABI compatibility, replace the generic GSS client parameters
+   * with the arguments we were passed. */
+  orig_conn = tidc->gssc->conn; /* save to restore later */
+  if (conn != tidc->gssc->conn) {
+    tr_warning("tidc_send_request: WARNING: socket connection FD does not match FD opened by tidc_open_connection()");
+    tidc->gssc->conn = conn;
+  }
+  orig_gss_ctx = tidc->gssc->gss_ctx; /* save to restore later */
+  if (gssctx != *(tidc->gssc->gss_ctx)) {
+    tr_warning("tidc_send_request: WARNING: sending request with different GSS context than used for tidc_open_connection()");
+    *tidc->gssc->gss_ctx = gssctx;
+  }
 
   /* Create and populate a TID req structure */
   if (!(tid_req = tid_req_new()))
-    return -1;
+    goto error;
 
   tid_req->conn = conn;
   tid_req->gssctx = gssctx;
@@ -122,7 +129,7 @@ int tidc_send_request (TIDC_INSTANCE *tidc,
     goto error;
   }
 
-  tid_req->tidc_dh = tr_dh_dup(tidc->client_dh);
+  tid_req->tidc_dh = tr_dh_dup(tidc->gssc->client_dh);
 
   /* generate an ID */
   request_id = tr_random_id(NULL);
@@ -140,7 +147,11 @@ int tidc_send_request (TIDC_INSTANCE *tidc,
  error:
   rc = -1;
  cleanup:
-  tid_req_free(tid_req);
+  if (tid_req)
+    tid_req_free(tid_req);
+
+  tidc->gssc->conn = orig_conn;
+  tidc->gssc->gss_ctx = orig_gss_ctx;
   return rc;
 }
 
@@ -149,60 +160,26 @@ int tidc_fwd_request(TIDC_INSTANCE *tidc,
                      TIDC_RESP_FUNC *resp_handler,
                      void *cookie)
 {
-  char *req_buf = NULL;
-  char *resp_buf = NULL;
-  size_t resp_buflen = 0;
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
   TR_MSG *msg = NULL;
   TR_MSG *resp_msg = NULL;
   TID_RESP *tid_resp = NULL;
-  int err;
   int rc = 0;
 
   /* Create and populate a TID msg structure */
-  if (!(msg = talloc_zero(tid_req, TR_MSG)))
+  if (!(msg = talloc_zero(tmp_ctx, TR_MSG)))
     goto error;
 
   msg->msg_type = TID_REQUEST;
   tr_msg_set_req(msg, tid_req);
 
-  /* store the response function and cookie */
-  // tid_req->resp_func = resp_handler;
-  // tid_req->cookie = cookie;
 
-
-  /* Encode the request into a json string */
-  if (!(req_buf = tr_msg_encode(msg))) {
-    tr_err("tidc_fwd_request: Error encoding TID request.\n");
-    goto error;
-  }
-
-  tr_debug( "tidc_fwd_request: Sending TID request:\n");
-  tr_debug( "%s\n", req_buf);
+  tr_debug( "tidc_fwd_request: Sending TID request\n");
 
   /* Send the request over the connection */
-  if (err = gsscon_write_encrypted_token (tid_req->conn, tid_req->gssctx, req_buf,
-                                         strlen(req_buf))) {
-    tr_err( "tidc_fwd_request: Error sending request over connection.\n");
+  resp_msg = tr_gssc_exchange_msgs(tmp_ctx, tidc->gssc, msg);
+  if (resp_msg == NULL)
     goto error;
-  }
-
-  /* TBD -- queue request on instance, read resps in separate thread */
-
-  /* Read the response from the connection */
-  /* TBD -- timeout? */
-  if (err = gsscon_read_encrypted_token(tid_req->conn, tid_req->gssctx, &resp_buf, &resp_buflen)) {
-    if (resp_buf)
-      free(resp_buf);
-    goto error;
-  }
-
-  tr_debug( "tidc_fwd_request: Response Received (%u bytes).\n", (unsigned) resp_buflen);
-  tr_debug( "%s\n", resp_buf);
-
-  if (NULL == (resp_msg = tr_msg_decode(resp_buf, resp_buflen))) {
-    tr_err( "tidc_fwd_request: Error decoding response.\n");
-    goto error;
-  }
 
   /* TBD -- Check if this is actually a valid response */
   tid_resp = tr_msg_get_resp(resp_msg);
@@ -237,25 +214,17 @@ int tidc_fwd_request(TIDC_INSTANCE *tidc,
  error:
   rc = -1;
  cleanup:
-  if (msg)
-    talloc_free(msg);
-  if (req_buf)
-    free(req_buf);
-  if (resp_buf)
-    free(resp_buf);
-  if (resp_msg)
-    tr_msg_free_decoded(resp_msg);
+  talloc_free(tmp_ctx);
   return rc;
 }
 
 
 DH * tidc_get_dh(TIDC_INSTANCE *inst)
 {
-  return inst->client_dh;
+  return tr_gssc_get_dh(inst->gssc);
 }
 
 DH *tidc_set_dh(TIDC_INSTANCE *inst, DH *dh)
 {
-  inst->client_dh = dh;
-  return dh;
+  return tr_gssc_set_dh(inst->gssc, dh);
 }
index 8d22749..9b9d51f 100644 (file)
  *
  */
 
-#include <assert.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdio.h>
-#include <errno.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
-#include <netinet/in.h>
 #include <jansson.h>
 #include <talloc.h>
 #include <poll.h>
 #include <gsscon.h>
 #include <tr_debug.h>
 #include <tr_msg.h>
+#include <tr_socket.h>
+#include <tr_gss.h>
+#include <tr_event.h>
 
-static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req) 
+/**
+ * Create a response with minimal fields filled in
+ *
+ * @param mem_ctx talloc context for the return value
+ * @param req request to respond to
+ * @return new response structure allocated in the mem_ctx context
+ */
+static TID_RESP *tids_create_response(TALLOC_CTX *mem_ctx, TID_REQ *req)
 {
   TID_RESP *resp=NULL;
   int success=0;
 
-  if (NULL == (resp = tid_resp_new(req))) {
+  if (NULL == (resp = tid_resp_new(mem_ctx))) {
     tr_crit("tids_create_response: Error allocating response structure.");
     return NULL;
   }
@@ -90,250 +97,137 @@ cleanup:
   return resp;
 }
 
-static int tids_listen(TIDS_INSTANCE *tids, int port, int *fd_out, size_t max_fd) 
-{
-  int rc = 0;
-  int conn = -1;
-  int optval = 1;
-  struct addrinfo *ai=NULL;
-  struct addrinfo *ai_head=NULL;
-  struct addrinfo hints={.ai_flags=AI_PASSIVE,
-                         .ai_family=AF_UNSPEC,
-                         .ai_socktype=SOCK_STREAM,
-                         .ai_protocol=IPPROTO_TCP};
-  char *port_str=NULL;
-  size_t n_opened=0;
-
-  tr_debug("tids_listen: started!");
-  port_str=talloc_asprintf(NULL, "%d", port);
-  if (port_str==NULL) {
-    tr_debug("tids_listen: unable to allocate port.");
-    return -1;
-  }
-
-  tr_debug("getaddrinfo()=%d", getaddrinfo(NULL, port_str, &hints, &ai_head));
-  talloc_free(port_str);
-  tr_debug("tids_listen: got address info");
-
-  /* TODO: listen on all ports */
-  for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_opened<max_fd); ai=ai->ai_next) {
-    if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) {
-      tr_debug("tids_listen: unable to open socket.");
-      continue;
-    }
-
-    optval=1;
-    if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
-      tr_debug("tids_listen: unable to set SO_REUSEADDR."); /* not fatal? */
-
-    if (ai->ai_family==AF_INET6) {
-      /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure
-       * if still relevant) */
-      if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) {
-        tr_debug("tids_listen: unable to set IPV6_V6ONLY. Skipping interface.");
-        close(conn);
-        continue;
-      }
-    }
-
-    rc=bind(conn, ai->ai_addr, ai->ai_addrlen);
-    if (rc<0) {
-      tr_debug("tids_listen: unable to bind to socket.");
-      close(conn);
-      continue;
-    }
-
-    if (0>listen(conn, 512)) {
-      tr_debug("tids_listen: unable to listen on bound socket.");
-      close(conn);
-      continue;
-    }
-
-    /* ok, this one worked. Save it */
-    fd_out[n_opened++]=conn;
-  }
-  freeaddrinfo(ai_head);
-
-  if (n_opened==0) {
-    tr_debug("tids_listen: no addresses available for listening.");
-    return -1;
-  }
-
-  tr_debug("tids_listen: TRP Server listening on port %d on %d socket%s",
-           port,
-           n_opened,
-           (n_opened==1)?"":"s");
-
-  return n_opened;
-}
-
-/* returns EACCES if authorization is denied */
-static int tids_auth_cb(gss_name_t clientName, gss_buffer_t displayName,
-                       void *data)
-{
-  struct tids_instance *inst = (struct tids_instance *) data;
-  TR_NAME name ={(char *) displayName->value,
-                displayName->length};
-  int result=0;
-
-  if (0!=inst->auth_handler(clientName, &name, inst->cookie)) {
-    tr_debug("tids_auth_cb: client '%.*s' denied authorization.", name.len, name.buf);
-    result=EACCES; /* denied */
-  }
-
-  return result;
-}
-
-/* returns 0 on authorization success, 1 on failure, or -1 in case of error */
-static int tids_auth_connection (TIDS_INSTANCE *inst,
-                                int conn,
-                                 gss_ctx_id_t *gssctx)
-{
-  int rc = 0;
-  int auth, autherr = 0;
-  gss_buffer_desc nameBuffer = {0, NULL};
-  char *name = 0;
-  int nameLen = 0;
-
-  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);
-    free(name);
-    return -1;
-  }
-  free(name);
-  nameBuffer.value=NULL; nameBuffer.length=0;
-
-  if (rc = gsscon_authorize(*gssctx, &auth, &autherr)) {
-    tr_debug("tids_auth_connection: Error from gsscon_authorize, rc = %d, autherr = %d.", 
-           rc, autherr);
-    return -1;
-  }
-
-  if (auth)
-    tr_debug("tids_auth_connection: Connection authenticated, conn = %d.", conn);
-  else
-    tr_debug("tids_auth_connection: Authentication failed, conn %d.", conn);
-
-  return !auth;
-}
-
-static int tids_read_request (TIDS_INSTANCE *tids, int conn, gss_ctx_id_t *gssctx, TR_MSG **mreq)
-{
-  int err;
-  char *buf;
-  size_t buflen = 0;
-
-  if (err = gsscon_read_encrypted_token(conn, *gssctx, &buf, &buflen)) {
-    if (buf)
-      free(buf);
-    return -1;
-  }
-
-  tr_debug("tids_read_request():Request Received, %u bytes.", (unsigned) buflen);
-
-  /* Parse request */
-  if (NULL == ((*mreq) = tr_msg_decode(buf, buflen))) {
-    tr_debug("tids_read_request():Error decoding request.");
-    free (buf);
-    return -1;
-  }
-
-  /* If this isn't a TID Request, just drop it. */
-  if (TID_REQUEST != (*mreq)->msg_type) {
-    tr_debug("tids_read_request(): Not a TID Request, dropped.");
-    return -1;
-  }
-
-  free (buf);
-  return buflen;
-}
-
-static int tids_handle_request (TIDS_INSTANCE *tids, TR_MSG *mreq, TID_RESP *resp) 
+static int tids_handle_request(TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp)
 {
   int rc=-1;
 
   /* Check that this is a valid TID Request.  If not, send an error return. */
-  if ((!tr_msg_get_req(mreq)) ||
-      (!tr_msg_get_req(mreq)->rp_realm) ||
-      (!tr_msg_get_req(mreq)->realm) ||
-      (!tr_msg_get_req(mreq)->comm)) {
+  if ((!req) ||
+      (!(req->rp_realm)) ||
+      (!(req->realm)) ||
+      (!(req->comm))) {
     tr_notice("tids_handle_request(): Not a valid TID Request.");
-    resp->result = TID_ERROR;
-    resp->err_msg = tr_new_name("Bad request format");
+    tid_resp_set_result(resp, TID_ERROR);
+    tid_resp_set_err_msg(resp, tr_new_name("Bad request format"));
     return -1;
   }
 
   tr_debug("tids_handle_request: adding self to req path.");
-  tid_req_add_path(tr_msg_get_req(mreq), tids->hostname, tids->tids_port);
+  tid_req_add_path(req, tids->hostname, tids->tids_port);
   
   /* Call the caller's request handler */
   /* TBD -- Handle different error returns/msgs */
-  if (0 > (rc = (*tids->req_handler)(tids, tr_msg_get_req(mreq), resp, tids->cookie))) {
+  if (0 > (rc = (*tids->req_handler)(tids, req, resp, tids->cookie))) {
     /* set-up an error response */
     tr_debug("tids_handle_request: req_handler returned error.");
-    resp->result = TID_ERROR;
-    if (!resp->err_msg)        /* Use msg set by handler, if any */
-      resp->err_msg = tr_new_name("Internal processing error");
-  }
-  else {
+    tid_resp_set_result(resp, TID_ERROR);
+    if (!tid_resp_get_err_msg(resp))   /* Use msg set by handler, if any */
+      tid_resp_set_err_msg(resp, tr_new_name("Internal processing error"));
+  } else {
     /* set-up a success response */
     tr_debug("tids_handle_request: req_handler returned success.");
-    resp->result = TID_SUCCESS;
+    tid_resp_set_result(resp, TID_SUCCESS);
     resp->err_msg = NULL;      /* No error msg on successful return */
   }
     
   return rc;
 }
 
+/**
+ * Produces a JSON-encoded msg containing the TID response
+ *
+ * @param mem_ctx talloc context for the return value
+ * @param resp outgoing response
+ * @return JSON-encoded message containing the TID response
+ */
+static char *tids_encode_response(TALLOC_CTX *mem_ctx, TID_RESP *resp)
+{
+  TR_MSG mresp;
+  char *resp_buf = NULL;
+
+  /* Construct the response message */
+  mresp.msg_type = TID_RESPONSE;
+  tr_msg_set_resp(&mresp, resp);
+
+  /* Encode the message to JSON */
+  resp_buf = tr_msg_encode(mem_ctx, &mresp);
+  if (resp_buf == NULL) {
+    tr_err("tids_encode_response: Error encoding json response.");
+    return NULL;
+  }
+  tr_debug("tids_encode_response: Encoded response: %s", resp_buf);
+
+  /* Success */
+  return resp_buf;
+}
+
+/**
+ * Encode/send an error response
+ *
+ * Part of the public interface
+ *
+ * @param tids
+ * @param req
+ * @param err_msg
+ * @return
+ */
 int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg) {
   TID_RESP *resp = NULL;
   int rc = 0;
 
+  if ((!tids) || (!req) || (!err_msg)) {
+    tr_debug("tids_send_err_response: Invalid parameters.");
+    return -1;
+  }
+
   /* If we already sent a response, don't send another no matter what. */
   if (req->resp_sent)
     return 0;
 
-  if (NULL == (resp = tids_create_response(tids, req))) {
+  if (NULL == (resp = tids_create_response(req, req))) {
     tr_crit("tids_send_err_response: Can't create response.");
     return -1;
   }
-  
+
   /* mark this as an error response, and include the error message */
   resp->result = TID_ERROR;
   resp->err_msg = tr_new_name((char *)err_msg);
   resp->error_path = req->path;
 
   rc = tids_send_response(tids, req, resp);
-  
+
   tid_resp_free(resp);
   return rc;
 }
 
+/**
+ * Encode/send a response
+ *
+ * Part of the public interface
+ *
+ * @param tids not actually used, but kept for ABI compatibility
+ * @param req
+ * @param resp
+ * @return
+ */
 int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp)
 {
   int err;
-  TR_MSG mresp;
   char *resp_buf;
 
-  if ((!tids) || (!req) || (!resp))
+  if ((!tids) || (!req) || (!resp)) {
     tr_debug("tids_send_response: Invalid parameters.");
+    return -1;
+  }
 
   /* Never send a second response if we already sent one. */
   if (req->resp_sent)
     return 0;
 
-  mresp.msg_type = TID_RESPONSE;
-  tr_msg_set_resp(&mresp, resp);
-
-  if (NULL == (resp_buf = tr_msg_encode(&mresp))) {
-
+  resp_buf = tids_encode_response(NULL, NULL);
+  if (resp_buf == NULL) {
     tr_err("tids_send_response: Error encoding json response.");
     tr_audit_req(req);
-
     return -1;
   }
 
@@ -344,12 +238,11 @@ int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp)
   tr_audit_resp(resp);
 
   /* Send the response over the connection */
-  if (err = gsscon_write_encrypted_token (req->conn, req->gssctx, resp_buf, 
-                                         strlen(resp_buf) + 1)) {
+  err = gsscon_write_encrypted_token (req->conn, req->gssctx, resp_buf,
+                                            strlen(resp_buf) + 1);
+  if (err) {
     tr_notice("tids_send_response: Error sending response over connection.");
-
     tr_audit_req(req);
-
     return -1;
   }
 
@@ -361,83 +254,119 @@ int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp)
   return 0;
 }
 
-static void tids_handle_connection (TIDS_INSTANCE *tids, int conn)
+/**
+ * Callback to process a request and produce a response
+ *
+ * @param req_str JSON-encoded request
+ * @param data pointer to a TIDS_INSTANCE
+ * @return pointer to the response string or null to send no response
+ */
+static TR_MSG *tids_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *mreq, void *data)
 {
-  TR_MSG *mreq = NULL;
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TIDS_INSTANCE *tids = talloc_get_type_abort(data, TIDS_INSTANCE);
+  TID_REQ *req = NULL;
   TID_RESP *resp = NULL;
+  TR_MSG *resp_msg = NULL; /* this is the return value */
   int rc = 0;
-  gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
 
-  if (tids_auth_connection(tids, conn, &gssctx)) {
-    tr_notice("tids_handle_connection: Error authorizing TID Server connection.");
-    close(conn);
-    return;
+  /* If this isn't a TID Request, just drop it. */
+  if (mreq->msg_type != TID_REQUEST) {
+    tr_debug("tids_req_cb: Not a TID request, dropped.");
+    goto cleanup;
   }
 
-  tr_debug("tids_handle_connection: Connection authorized!");
+  /* Get a handle on the request itself. Don't free req - it belongs to mreq */
+  req = tr_msg_get_req(mreq);
 
-  while (1) {  /* continue until an error breaks us out */
+  /* Allocate a response message */
+  resp_msg = talloc(tmp_ctx, TR_MSG);
+  if (resp_msg == NULL) {
+    /* We cannot create a response message, so all we can really do is emit
+     * an error message and return. */
+    tr_crit("tids_req_cb: Error allocating response message.");
+    goto cleanup;
+  }
 
-    if (0 > (rc = tids_read_request(tids, conn, &gssctx, &mreq))) {
-      tr_debug("tids_handle_connection: Error from tids_read_request(), rc = %d.", rc);
-      return;
-    } else if (0 == rc) {
-      continue;
-    }
+  /* Allocate a response structure and populate common fields. Put it in the
+   * response message's talloc context. */
+  resp = tids_create_response(resp_msg, req);
+  if (resp == NULL) {
+    /* If we were unable to create a response, we cannot reply. Log an
+     * error if we can, then drop the request. */
+    tr_crit("tids_req_cb: Error creating response structure.");
+    resp_msg = NULL; /* the contents are in tmp_ctx, so they will still be cleaned up */
+    goto cleanup;
+  }
+  /* Now officially assign the response to the message. */
+  tr_msg_set_resp(resp_msg, resp);
+
+  /* Handle the request and fill in resp */
+  rc = tids_handle_request(tids, req, resp);
+  if (rc < 0) {
+    tr_debug("tids_req_cb: Error from tids_handle_request(), rc = %d.", rc);
+    /* Fall through, to send the response, either way */
+  }
 
-    /* Put connection information into the request structure */
-    tr_msg_get_req(mreq)->conn = conn;
-    tr_msg_get_req(mreq)->gssctx = gssctx;
-
-    /* Allocate a response structure and populate common fields */
-    if (NULL == (resp = tids_create_response (tids, tr_msg_get_req(mreq)))) {
-      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;
-    }
+  /* put the response message in the caller's context */
+  talloc_steal(mem_ctx, resp_msg);
 
-    if (0 > (rc = tids_handle_request(tids, mreq, resp))) {
-      tr_debug("tids_handle_connection: Error from tids_handle_request(), rc = %d.", rc);
-      /* Fall through, to send the response, either way */
-    }
+cleanup:
+  talloc_free(tmp_ctx);
+  return resp_msg;
+}
 
-    if (0 > (rc = tids_send_response(tids, tr_msg_get_req(mreq), resp))) {
-      tr_debug("tids_handle_connection: Error from tids_send_response(), rc = %d.", rc);
-      /* if we didn't already send a response, try to send a generic error. */
-      if (!tr_msg_get_req(mreq)->resp_sent)
-        tids_send_err_response(tids, tr_msg_get_req(mreq), "Error sending response.");
-      /* Fall through to free the response, either way. */
+static int tids_destructor(void *object)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(object, TIDS_INSTANCE);
+  if (tids->pids)
+    g_array_unref(tids->pids);
+  return 0;
+}
+
+TIDS_INSTANCE *tids_new(TALLOC_CTX *mem_ctx)
+{
+  TIDS_INSTANCE *tids = talloc_zero(mem_ctx, TIDS_INSTANCE);
+  if (tids) {
+    tids->pids = g_array_new(FALSE, FALSE, sizeof(struct tid_process));
+    if (tids->pids == NULL) {
+      talloc_free(tids);
+      return NULL;
     }
-    
-    tr_msg_free_decoded(mreq); /* takes resp with it */
-    return;
-  } 
+    talloc_set_destructor((void *)tids, tids_destructor);
+  }
+  return tids;
 }
 
-TIDS_INSTANCE *tids_create (void)
+/**
+ * Create a new TIDS instance
+ *
+ * Deprecated: exists for ABI compatibility, but tids_new() should be used instead
+ *
+ */
+TIDS_INSTANCE *tids_create(void)
 {
-  return talloc_zero(NULL, TIDS_INSTANCE);
+  return tids_new(NULL);
 }
 
 /* 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,
-                      int *fd_out,
-                      size_t max_fd)
+nfds_t tids_get_listener(TIDS_INSTANCE *tids,
+                         TIDS_REQ_FUNC *req_handler,
+                         tids_auth_func *auth_handler,
+                         const char *hostname,
+                         unsigned int port,
+                         void *cookie,
+                         int *fd_out,
+                         size_t max_fd)
 {
-  size_t n_fd=0;
-  size_t ii=0;
+  nfds_t n_fd = 0;
+  nfds_t ii = 0;
 
   tids->tids_port = port;
-  n_fd=tids_listen(tids, port, fd_out, max_fd);
-  if (n_fd<=0)
+  n_fd = tr_sock_listen_all(port, fd_out, max_fd);
+
+  if (n_fd == 0)
     tr_err("tids_get_listener: Error opening port %d");
   else {
     /* opening port succeeded */
@@ -451,13 +380,13 @@ int tids_get_listener(TIDS_INSTANCE *tids,
           close(fd_out[ii]);
           fd_out[ii]=-1;
         }
-        n_fd=0;
+        n_fd = 0;
         break;
       }
     }
   }
 
-  if (n_fd>0) {
+  if (n_fd > 0) {
     /* store the caller's request handler & cookie */
     tids->req_handler = req_handler;
     tids->auth_handler = auth_handler;
@@ -465,7 +394,7 @@ int tids_get_listener(TIDS_INSTANCE *tids,
     tids->cookie = cookie;
   }
 
-  return n_fd;
+  return (int)n_fd;
 }
 
 /* Accept and process a connection on a port opened with tids_get_listener() */
@@ -473,47 +402,137 @@ int tids_accept(TIDS_INSTANCE *tids, int listen)
 {
   int conn=-1;
   int pid=-1;
+  int pipe_fd[2];
+  struct tid_process tp = {0};
 
   if (0 > (conn = accept(listen, NULL, NULL))) {
     perror("Error from TIDS Server accept()");
     return 1;
   }
 
+  if (0 > pipe(pipe_fd)) {
+    perror("Error on pipe()");
+    return 1;
+  }
+  /* pipe_fd[0] is for reading, pipe_fd[1] is for writing */
+
   if (0 > (pid = fork())) {
     perror("Error on fork()");
     return 1;
   }
 
   if (pid == 0) {
+    close(pipe_fd[0]); /* child only writes */
     close(listen);
-    tids_handle_connection(tids, conn);
+    tr_gss_handle_connection(conn,
+                             "trustidentity", tids->hostname, /* acceptor name */
+                             tids->auth_handler, tids->cookie, /* auth callback and cookie */
+                             tids_req_cb, tids /* req callback and cookie */
+    );
+    if (write(pipe_fd[1], "OK\0", 3) < 0)
+      tr_crit("tids_accept: child process unable to write to pipe");
+    close(pipe_fd[1]);
     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);
+  /* Only the parent process gets here */
+  close(pipe_fd[1]); /* parent only listens */
+  close(conn); /* connection belongs to the child */
+  tp.pid = pid;
+  tp.read_fd = pipe_fd[0];
+  g_array_append_val(tids->pids, tp); /* remember the PID of our child process */
 
+  /* clean up any processes that have completed */
+  tids_sweep_procs(tids);
   return 0;
 }
 
+/**
+ * Clean up any finished TID request processes
+ *
+ * This is called by the main process after forking each TID request. If you want to be
+ * sure finished processes are cleaned up promptly even during a lull in TID requests,
+ * this can be called from the main thread of the main process. It is not thread-safe,
+ * so should not be used from sub-threads. It should not be called by child processes -
+ * this would probably be harmless but ineffective.
+ *
+ * @param tids
+ */
+void tids_sweep_procs(TIDS_INSTANCE *tids)
+{
+  guint ii;
+  struct tid_process tp = {0};
+  char result[10] = {0};
+  ssize_t result_len;
+  int status;
+  int wait_rc;
+
+  /* loop backwards over the array so we can remove elements as we go */
+  for (ii=tids->pids->len; ii > 0; ii--) {
+    /* ii-1 is the current index - get our own copy, we may destroy the list's copy */
+    tp = g_array_index(tids->pids, struct tid_process, ii-1);
+
+    wait_rc = waitpid(tp.pid, &status, WNOHANG);
+    if (wait_rc == 0)
+      continue; /* process still running */
+
+    if (wait_rc < 0) {
+      /* invalid options will probably keep being invalid, report that condition */
+      if(errno == EINVAL)
+        tr_crit("tids_sweep_procs: waitpid called with invalid options");
+
+      /* If we got ECHILD, that means the PID was invalid; we'll assume the process was
+       * terminated and we missed it. For all other errors, move on
+       * to the next PID to check. */
+      if (errno != ECHILD)
+        continue;
+
+      tr_warning("tid_sweep_procs: TID process %d disappeared", tp.pid);
+    }
+
+    /* remove the item (we still have a copy of the data) */
+    g_array_remove_index_fast(tids->pids, ii-1); /* disturbs only indices >= ii-1 which we've already handled */
+
+    /* Report exit status unless we got ECHILD above or somehow waitpid returned the wrong pid */
+    if (wait_rc == tp.pid) {
+      if (WIFEXITED(status)) {
+        tr_debug("tids_sweep_procs: TID process %d exited with status %d.", tp.pid, WTERMSIG(status));
+      } else if (WIFSIGNALED(status)) {
+        tr_debug("tids_sweep_procs: TID process %d terminated by signal %d.", tp.pid, WTERMSIG(status));
+      }
+    } else if (wait_rc > 0) {
+      tr_err("tids_sweep_procs: waitpid returned pid %d, expected %d", wait_rc, tp.pid);
+    }
+
+    /* read the pipe - if the TID request worked, it will have written status before terminating */
+    result_len = read(tp.read_fd, result, sizeof(result)/sizeof(result[0]));
+    close(tp.read_fd);
+
+    if ((result_len > 0) && (strcmp(result, "OK") == 0)) {
+      tids->req_count++;
+      tr_debug("tids_sweep_procs: TID process %d succeeded", tp.pid);
+    } else {
+      tids->error_count++;
+      tr_debug("tids_sweep_procs: TID process %d failed", tp.pid);
+    }
+  }
+}
+
 /* Process tids requests forever. Should not return except on error. */
-#define MAX_SOCKETS 10
-int tids_start (TIDS_INSTANCE *tids, 
+int tids_start (TIDS_INSTANCE *tids,
                 TIDS_REQ_FUNC *req_handler,
                 tids_auth_func *auth_handler,
                 const char *hostname,
                 unsigned int port,
                 void *cookie)
 {
-  int fd[MAX_SOCKETS]={0};
-  size_t n_fd=0;
-  struct pollfd poll_fd[MAX_SOCKETS]={{0}};
+  int fd[TR_MAX_SOCKETS]={0};
+  nfds_t n_fd=0;
+  struct pollfd poll_fd[TR_MAX_SOCKETS]={{0}};
   int ii=0;
 
-  n_fd=tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, MAX_SOCKETS);
+  n_fd = tids_get_listener(tids, req_handler, auth_handler, hostname, port, cookie, fd, TR_MAX_SOCKETS);
   if (n_fd <= 0) {
     perror ("Error from tids_listen()");
     return 1;
@@ -557,7 +576,6 @@ int tids_start (TIDS_INSTANCE *tids,
 
   return 1;    /* should never get here, loops "forever" */
 }
-#undef MAX_SOCKETS
 
 void tids_destroy (TIDS_INSTANCE *tids)
 {
index 4f09d12..ba738c7 100644 (file)
 
 #include <stdio.h>
 #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 <time.h>
 
 #include <tid_internal.h>
+#include <mon_internal.h>
+#include <tr_mon.h>
 #include <tr_tid.h>
 #include <tr_trp.h>
 #include <tr_config.h>
@@ -151,6 +151,38 @@ static void configure_signals(void)
   pthread_sigmask(SIG_BLOCK, &signals, NULL);
 }
 
+/* Monitoring handlers */
+static MON_RC tr_handle_version(void *cookie, json_t **result_ptr)
+{
+  *result_ptr = json_string(PACKAGE_VERSION);
+  return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC tr_handle_uptime(void *cookie, json_t **result_ptr)
+{
+  time_t *start_time = cookie;
+  *result_ptr = json_integer(time(NULL) - (*start_time));
+  return (*result_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC tr_handle_show_rp_clients(void *cookie, json_t **response_ptr)
+{
+  TR_CFG_MGR *cfg_mgr = talloc_get_type_abort(cookie, TR_CFG_MGR);
+
+  *response_ptr = tr_rp_clients_to_json(cfg_mgr->active->rp_clients);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC tr_handle_show_cfg_serial(void *cookie, json_t **response_ptr)
+{
+  TR_CFG_MGR *cfg_mgr = talloc_get_type_abort(cookie, TR_CFG_MGR);
+
+  *response_ptr = tr_cfg_files_to_json_array(cfg_mgr->active);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+
+
 int main(int argc, char *argv[])
 {
   TALLOC_CTX *main_ctx=NULL;
@@ -159,8 +191,12 @@ int main(int argc, char *argv[])
   struct cmdline_args opts;
   struct event_base *ev_base;
   struct tr_socket_event tids_ev = {0};
+  struct event *tids_sweep_ev;
+  struct tr_socket_event mon_ev = {0};
   struct event *cfgwatch_ev;
 
+  time_t start_time = time(NULL); /* TODO move this? */
+
   configure_signals();
 
   /* we're going to be multithreaded, so disable null context tracking */
@@ -199,11 +235,10 @@ int main(int argc, char *argv[])
   }
 
   /***** initialize the trust path query server instance *****/
-  if (NULL == (tr->tids = tids_create())) {
+  if (NULL == (tr->tids = tids_new(tr))) {
     tr_crit("Error initializing Trust Path Query Server instance.");
     return 1;
   }
-  talloc_steal(tr, tr->tids);
 
   /***** initialize the trust router protocol server instance *****/
   if (NULL == (tr->trps = trps_new(tr))) {
@@ -211,6 +246,23 @@ int main(int argc, char *argv[])
     return 1;
   }
 
+  /***** initialize the monitoring interface instance *****/
+  if (NULL == (tr->mons = mons_new(tr))) {
+    tr_crit("Error initializing monitoring interface instance.");
+    return 1;
+  }
+  /* Monitor our tids/trps instances */
+  tr->mons->tids = tr->tids;
+  tr->mons->trps = tr->trps;
+
+  /* Register monitoring handlers */
+  mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_VERSION, tr_handle_version, NULL);
+  mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_CONFIG_FILES, tr_handle_show_cfg_serial, tr->cfg_mgr);
+  mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_UPTIME, tr_handle_uptime, &start_time);
+  mons_register_handler(tr->mons, MON_CMD_SHOW, OPT_TYPE_SHOW_RP_CLIENTS, tr_handle_show_rp_clients, tr->cfg_mgr);
+  tr_tid_register_mons_handlers(tr->tids, tr->mons);
+  tr_trp_register_mons_handlers(tr->trps, tr->mons);
+
   /***** process configuration *****/
   tr->cfgwatch=tr_cfgwatch_create(tr);
   if (tr->cfgwatch == NULL) {
@@ -244,15 +296,16 @@ int main(int argc, char *argv[])
     return 1;
   }
 
-  /*tr_status_event_init();*/ /* install status reporting events */
+  /* install monitoring interface events */
+  tr_debug("Initializing monitoring interface events.");
+  if (0 != tr_mons_event_init(ev_base, tr->mons, tr->cfg_mgr, &mon_ev)) {
+    tr_crit("Error initializing monitoring interface.");
+    return 1;
+  }
 
   /* install TID server events */
   tr_debug("Initializing TID server events.");
-  if (0 != tr_tids_event_init(ev_base,
-                              tr->tids,
-                              tr->cfg_mgr,
-                              tr->trps,
-                             &tids_ev)) {
+  if (0 != tr_tids_event_init(ev_base, tr->tids, tr->cfg_mgr, tr->trps, &tids_ev, &tids_sweep_ev)) {
     tr_crit("Error initializing Trust Path Query Server instance.");
     return 1;
   }
diff --git a/tr/tr_mon.c b/tr/tr_mon.c
new file mode 100644 (file)
index 0000000..84ab353
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2018, 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 <talloc.h>
+
+#include <tr_config.h>
+#include <tr_debug.h>
+#include <mon_internal.h>
+#include <tr_mon.h>
+
+/*
+ * Cookie for the event handling callback
+ */
+struct tr_mons_event_cookie {
+  MONS_INSTANCE *mons;
+  TR_CFG_MGR *cfg_mgr;
+};
+
+
+/**
+ * Callback to handle a triggered event
+ *
+ * @param listener file descriptor of the socket that triggered the event
+ * @param event libevent2 event
+ * @param arg pointer to our MONS_INSTANCE
+ */
+static void tr_mons_event_cb(int listener, short event, void *arg)
+{
+  MONS_INSTANCE *mons = talloc_get_type_abort(arg, MONS_INSTANCE);
+
+  // check that we were not accidentally triggered
+  if (0==(event & EV_READ))
+    tr_debug("tr_mons_event_cb: unexpected event on monitoring interface socket (event=0x%X)", event);
+  else
+    mons_accept(mons, listener);
+}
+
+
+/**
+ * Callback to handle an incoming monitoring request
+ *
+ * @param mons monitoring interface instance
+ * @param orig_req incoming request
+ * @param resp destination for outgoing response
+ * @param cookie_in cookie from the event handling system
+ * @return 0 on success
+ */
+static int tr_mons_req_handler(MONS_INSTANCE *mons,
+                               MON_REQ *orig_req,
+                               MON_RESP *resp,
+                               void *cookie_in)
+{
+  return -1;
+}
+
+/**
+ * Callback to authorize a GSS client
+ *
+ * @param client_name ?
+ * @param gss_name GSS name of credential attempting to authorize
+ * @param cookie_in event cookie
+ * @return 0 if authorization is successful, -1 if not
+ */
+static int tr_mons_auth_handler(gss_name_t client_name, TR_NAME *gss_name, void *cookie_in)
+{
+  struct tr_mons_event_cookie *cookie=talloc_get_type_abort(cookie_in, struct tr_mons_event_cookie);
+  MONS_INSTANCE *mons = cookie->mons;
+
+  if ((!client_name) || (!gss_name) || (!mons)) {
+    tr_debug("tr_mons_gss_handler: Bad parameters.");
+    return -1;
+  }
+
+  /* Ensure at least one client exists using this GSS name */
+  if (! tr_gss_names_matches(mons->authorized_gss_names, gss_name)) {
+    tr_info("tr_mons_gss_handler: Unauthorized request from %.*s", gss_name->len, gss_name->buf);
+    return -1;
+  }
+
+  /* Credential was valid, authorize it */
+  tr_info("tr_mons_gss_handler: Authorized request from %.*s", gss_name->len, gss_name->buf);
+  return 0;
+}
+
+
+/*
+ *
+ * Get a listener for monitoring requests, returns its socket fd. Accept
+ * connections with tids_accept() */
+
+/**
+ * Configure the monitoring service instance and set up its event handler
+ *
+ * @param base libevent2 event base
+ * @param mons MONS_INSTANCE for this monitoring interface
+ * @param cfg_mgr configuration manager instance
+ * @param mons_ev monitoring interface event instance
+ * @return 0 on success, nonzero on failure.
+ * */
+int tr_mons_event_init(struct event_base *base,
+                       MONS_INSTANCE *mons,
+                       TR_CFG_MGR *cfg_mgr,
+                       struct tr_socket_event *mons_ev)
+{
+  TALLOC_CTX *tmp_ctx=talloc_new(NULL);
+  struct tr_mons_event_cookie *cookie=NULL;
+  int retval=0;
+  int ii=0;
+
+  if (mons_ev == NULL) {
+    tr_debug("tr_mon_event_init: Null mons_ev.");
+    retval=1;
+    goto cleanup;
+  }
+
+  if (cfg_mgr->active->internal->monitoring_port == 0) {
+    tr_notice("tr_mons_event_init: monitoring is disabled, not enabling events or opening sockets");
+    retval = 0;
+    goto cleanup;
+  }
+
+  /* Create the cookie for callbacks. We'll put it in the mons context, so it will
+   * be cleaned up when mons is freed by talloc_free. */
+  cookie=talloc(tmp_ctx, struct tr_mons_event_cookie);
+  if (cookie == NULL) {
+    tr_debug("tr_mons_event_init: Unable to allocate cookie.");
+    retval=1;
+    goto cleanup;
+  }
+  cookie->mons=mons;
+  cookie->cfg_mgr=cfg_mgr;
+  talloc_steal(mons, cookie);
+
+  /* get a monitoring interface listener */
+  mons_ev->n_sock_fd = mons_get_listener(mons, tr_mons_req_handler,
+                                         tr_mons_auth_handler,
+                                         cfg_mgr->active->internal->hostname,
+                                         cfg_mgr->active->internal->monitoring_port,
+                                         (void *) cookie, mons_ev->sock_fd,
+                                         TR_MAX_SOCKETS);
+  if (mons_ev->n_sock_fd==0) {
+    tr_crit("Error opening monitoring interface socket.");
+    retval=1;
+    goto cleanup;
+  }
+
+  /* Set up events */
+  for (ii=0; ii<mons_ev->n_sock_fd; ii++) {
+    mons_ev->ev[ii]=event_new(base,
+                              mons_ev->sock_fd[ii],
+                              EV_READ|EV_PERSIST,
+                              tr_mons_event_cb,
+                              (void *)mons);
+    event_add(mons_ev->ev[ii], NULL);
+  }
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return retval;
+}
index c4ac9fb..62722e8 100644 (file)
 #include <tr_comm.h>
 #include <tr_idp.h>
 #include <tr_rp.h>
+#include <tr_rp_client.h>
 #include <tr_event.h>
 #include <tr_debug.h>
 #include <gsscon.h>
+#include <trp_route.h>
 #include <trp_internal.h>
 #include <tr_config.h>
 #include <tr_mq.h>
 #include <tr_util.h>
 #include <tr_tid.h>
+#include <tr_comm.h>
 
 /* Structure to hold data for the tid response callback */
 typedef struct tr_resp_cookie {
@@ -183,7 +186,7 @@ static void *tr_tids_req_fwd_thread(void *arg)
   }
   /* cookie->resp should now contain our copy of the response */
   success=1;
-  tr_debug("tr_tids_req_fwd_thread: thread %d received response.");
+  tr_debug("tr_tids_req_fwd_thread: thread %d received response.", cookie->thread_id);
 
 cleanup:
   /* Notify parent thread of the response, if it's still listening. */
@@ -221,15 +224,84 @@ static TID_RC tr_tids_merge_resps(TID_RESP *r1, TID_RESP *r2)
   if ((r1->result!=TID_SUCCESS) || (r2->result!=TID_SUCCESS))
     return TID_ERROR;
 
-  if ((0!=tr_name_cmp(r1->rp_realm, r2->rp_realm)) ||
-      (0!=tr_name_cmp(r1->realm, r2->realm)) ||
-      (0!=tr_name_cmp(r1->comm, r2->comm)))
-    return TID_ERROR;
+  if ((0 == tr_name_cmp(r1->rp_realm, r2->rp_realm))
+      && (0 == tr_name_cmp(r1->realm, r2->realm))
+      && ( (0 == tr_name_cmp(r1->comm, r2->comm))
+           || (0 == tr_name_cmp(r1->comm, r2->orig_coi))
+           || (0 == tr_name_cmp(r1->orig_coi, r2->comm)))) {
+
+    tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers));
+    return TID_SUCCESS;
+  }
 
-  tid_srvr_blk_add(r1->servers, tid_srvr_blk_dup(r1, r2->servers));
-  return TID_SUCCESS;
+  return TID_ERROR;
 }
 
+enum map_coi_result {
+  MAP_COI_SUCCESS = 0,
+  MAP_COI_MAP_NOT_REQUIRED,
+  MAP_COI_ALREADY_MAPPED,
+  MAP_COI_NO_APC,
+  MAP_COI_INVALID_APC,
+  MAP_COI_UNKNOWN_COMM,
+  MAP_COI_ERROR
+};
+
+static enum map_coi_result map_coi(TR_COMM_TABLE *ctable, TID_REQ *req)
+{
+  TR_COMM *orig_comm;
+  TR_NAME *apc_name;
+  TR_COMM *apc;
+  TR_APC *apcs;
+
+  if (tid_req_get_orig_coi(req) != NULL)
+    return MAP_COI_ALREADY_MAPPED;
+
+  /* look up the community */
+  orig_comm = tr_comm_table_find_comm(ctable, tid_req_get_comm(req));
+  if (orig_comm == NULL)
+    return MAP_COI_UNKNOWN_COMM;
+
+  if (tr_comm_get_type(orig_comm) == TR_COMM_APC)
+    return MAP_COI_MAP_NOT_REQUIRED; /* it was already an APC, no mapping to do */
+
+  /* use first (only) APC. These are just APC names  */
+  apcs = tr_comm_get_apcs(orig_comm);
+  if ((!apcs) || (!tr_apc_get_id(apcs)))
+    return MAP_COI_NO_APC;
+
+  /* get our own copy of the APC name */
+  apc_name = tr_dup_name(tr_apc_get_id(apcs));
+  if (apc_name == NULL) {
+    tr_err("map_coi: Error allocating apc_name");
+    return MAP_COI_ERROR;
+  }
+
+  /* Check that the APC is configured */
+  apc = tr_comm_table_find_comm(ctable, apc_name);
+  if (apc == NULL) {
+    tr_free_name(apc_name);
+    return MAP_COI_INVALID_APC;
+  }
+
+  tid_req_set_orig_coi(req, tid_req_get_comm(req)); /* was null, so no need to free anything */
+  tid_req_set_comm(req, apc_name); /* original contents will be freed via orig_coi */
+
+  return MAP_COI_SUCCESS; /* successfully mapped */
+}
+
+/**
+ * Process a TID request
+ *
+ * Return value of -1 means to send a TID_ERROR response. Fill in resp->err_msg or it will
+ * be returned as a generic error.
+ *
+ * @param tids
+ * @param orig_req
+ * @param resp
+ * @param cookie_in
+ * @return
+ */
 static int tr_tids_req_handler(TIDS_INSTANCE *tids,
                                TID_REQ *orig_req, 
                                TID_RESP *resp,
@@ -245,7 +317,6 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
   TID_RESP *aaa_resp[TR_TID_MAX_AAA_SERVERS]={NULL};
   TR_RP_CLIENT *rp_client=NULL;
   TR_RP_CLIENT_ITER *rpc_iter=NULL;
-  TR_NAME *apc = NULL;
   TID_REQ *fwd_req = NULL;
   TR_COMM *cfg_comm = NULL;
   TR_COMM *cfg_apc = NULL;
@@ -273,7 +344,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
     goto cleanup;
   }
 
-  tr_debug("tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s", orig_req->conn,
+  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 (orig_req->request_id)
     tr_debug("tr_tids_req_handler: TID request ID: %.*s", orig_req->request_id->len, orig_req->request_id->buf);
@@ -285,14 +356,15 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
   /* 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;
+    retval=-1; /* response will be a generic internal error */
     goto cleanup;
   }
   talloc_steal(tmp_ctx, fwd_req);
 
+  /* cfg_comm is now the community (APC or CoI) of the incoming request */
   if (NULL == (cfg_comm=tr_comm_table_find_comm(cfg_mgr->active->ctable, orig_req->comm))) {
     tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", orig_req->comm->buf);
-    tids_send_err_response(tids, orig_req, "Unknown community");
+    tid_resp_set_err_msg(resp, tr_new_name("Unknown community"));
     retval=-1;
     goto cleanup;
   }
@@ -305,7 +377,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
 
   if (!tids->gss_name) {
     tr_notice("tr_tids_req_handler: No GSS name for incoming request.");
-    tids_send_err_response(tids, orig_req, "No GSS name for request");
+    tid_resp_set_err_msg(resp, tr_new_name("No GSS name for request"));
     retval=-1;
     goto cleanup;
   }
@@ -317,7 +389,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
   target=tr_filter_target_tid_req(tmp_ctx, orig_req);
   if (target==NULL) {
     tr_crit("tid_req_handler: Unable to allocate filter target, cannot apply filter!");
-    tids_send_err_response(tids, orig_req, "Incoming TID request filter error");
+    tid_resp_set_err_msg(resp, tr_new_name("Incoming TID request filter error"));
     retval=-1;
     goto cleanup;
   }
@@ -346,82 +418,96 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
   /* We get here whether or not a filter matched. If tr_filter_apply() doesn't match, it returns
    * a default action of reject, so we don't have to check why we exited the loop. */
   if (oaction != TR_FILTER_ACTION_ACCEPT) {
-    tr_notice("tr_tids_req_handler: Incoming TID request rejected by filter for GSS name", orig_req->rp_realm->buf);
-    tids_send_err_response(tids, orig_req, "Incoming TID request filter error");
+    tr_notice("tr_tids_req_handler: Incoming TID request rejected by RP client filter for GSS name %.*s",
+              tids->gss_name->len, tids->gss_name->buf);
+    tid_resp_set_err_msg(resp, tr_new_name("Incoming TID request filter error"));
     retval = -1;
     goto cleanup;
   }
 
   /* Check that the rp_realm is a member of the community in the request */
   if (NULL == tr_comm_find_rp(cfg_mgr->active->ctable, cfg_comm, orig_req->rp_realm)) {
-    tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
-    tids_send_err_response(tids, orig_req, "RP COI membership error");
+    tr_notice("tr_tids_req_handler: RP Realm (%s) not member of community (%s).",
+              orig_req->rp_realm->buf, orig_req->comm->buf);
+    tid_resp_set_err_msg(resp, tr_new_name("RP community 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) {
-    if (orig_req->orig_coi!=NULL) {
-      tr_notice("tr_tids_req_handler: community %s is COI but COI to APC mapping already occurred. Dropping request.",
-               orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "Second COI to APC mapping would result, permitted only once.");
-      retval=-1;
-      goto cleanup;
-    }
-    
-    tr_debug("tr_tids_req_handler: Community was a COI, switching.");
-    /* TBD -- In theory there can be more than one?  How would that work? */
-    if ((!cfg_comm->apcs) || (!cfg_comm->apcs->id)) {
-      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;
+  switch(map_coi(cfg_mgr->active->ctable, fwd_req)) {
+    case MAP_COI_MAP_NOT_REQUIRED:
+      cfg_apc = cfg_comm;
+      break;
+
+    case MAP_COI_SUCCESS:
+      cfg_apc = tr_comm_table_find_comm(cfg_mgr->active->ctable, tid_req_get_comm(fwd_req));
+      tr_debug("tr_tids_req_handler: Community %.*s is a COI, mapping to APC %.*s.",
+               tid_req_get_orig_coi(fwd_req)->len, tid_req_get_orig_coi(fwd_req)->buf,
+               tr_comm_get_id(cfg_apc)->len, tr_comm_get_id(cfg_apc)->buf);
+      break;
+
+    case MAP_COI_ALREADY_MAPPED:
+      tr_notice("tr_tids_req_handler: community %.*s is COI but COI to APC mapping already occurred. Dropping request.",
+                tid_req_get_comm(orig_req)->len, tid_req_get_comm(orig_req)->buf);
+      tid_resp_set_err_msg(resp, tr_new_name("Second COI to APC mapping would result, permitted only once."));
+      retval = -1;
       goto cleanup;
-    }
-    apc = tr_dup_name(cfg_comm->apcs->id);
 
-    /* Check that the APC is configured */
-    if (NULL == (cfg_apc = tr_comm_table_find_comm(cfg_mgr->active->ctable, apc))) {
-      tr_notice("tr_tids_req_hander: Request for unknown comm: %s.", apc->buf);
-      tids_send_err_response(tids, orig_req, "Unknown APC");
-      retval=-1;
+    case MAP_COI_NO_APC:
+      tr_notice("No valid APC for COI %.*s.",
+                tid_req_get_comm(orig_req)->len, tid_req_get_comm(orig_req)->buf);
+      tid_resp_set_err_msg(resp, tr_new_name("No valid APC for community"));
+      retval = -1;
       goto cleanup;
-    }
 
-    fwd_req->comm = apc;
-    fwd_req->orig_coi = orig_req->comm;
+    case MAP_COI_INVALID_APC:
+      tr_notice("tr_tids_req_hander: Request for unknown APC.");
+      tid_resp_set_err_msg(resp, tr_new_name("Unknown APC"));
+      retval = -1;
+      goto cleanup;
 
-    /* Check that rp_realm is a  member of this APC */
-    if (NULL == (tr_comm_find_rp(cfg_mgr->active->ctable, cfg_apc, orig_req->rp_realm))) {
-      tr_notice("tr_tids_req_hander: RP Realm (%s) not member of community (%s).", orig_req->rp_realm->buf, orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "RP APC membership error");
-      retval=-1;
+    default:
+      tr_notice("tr_tids_req_hander: Unexpected error mapping COI to APC.");
+      retval = -1;
       goto cleanup;
-    }
   }
 
-  /* Look up the route for this community/realm. */
+  /* cfg_comm is now the original community, and cfg_apc is the APC it belongs to. These
+   * may both be the same. If not, check that rp_realm is a  member of the mapped APC */
+  if ((cfg_apc != cfg_comm)
+      && (NULL == tr_comm_find_rp(cfg_mgr->active->ctable,
+                                  cfg_apc,
+                                  tid_req_get_rp_realm(fwd_req)))) {
+    tr_notice("tr_tids_req_hander: RP Realm (%.*s) not member of mapped APC (%.*s).",
+              tid_req_get_rp_realm(fwd_req)->len, tid_req_get_rp_realm(fwd_req)->buf,
+              tr_comm_get_id(cfg_apc)->len, tr_comm_get_id(cfg_apc)->buf);
+    tid_resp_set_err_msg(resp, tr_new_name("RP community membership error"));
+    retval=-1;
+    goto cleanup;
+  }
+
+  /* Look up the route for forwarding request's community/realm. */
   tr_debug("tr_tids_req_handler: looking up route.");
-  route=trps_get_selected_route(trps, orig_req->comm, orig_req->realm);
+  route=trps_get_selected_route(trps, fwd_req->comm, fwd_req->realm);
   if (route==NULL) {
     /* No route. Use default AAA servers if we have them. */
-    tr_debug("tr_tids_req_handler: No route for realm %s, defaulting.", orig_req->realm->buf);
+    tr_debug("tr_tids_req_handler: No route for realm %s, defaulting.", fwd_req->realm->buf);
     if (NULL == (aaa_servers = tr_default_server_lookup(cfg_mgr->active->default_servers,
-                                                        orig_req->comm))) {
+                                                        fwd_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");
+      tid_resp_set_err_msg(resp, tr_new_name("No path to AAA Server(s) for realm"));
       retval = -1;
       goto cleanup;
     }
     idp_shared = 0;
   } else {
-    /* Found a route. Determine the AAA servers or next hop address. */
+    /* Found a route. Determine the AAA servers or next hop address for the request we are forwarding. */
     tr_debug("tr_tids_req_handler: found route.");
     if (trp_route_is_local(route)) {
       tr_debug("tr_tids_req_handler: route is local.");
       aaa_servers = tr_idp_aaa_server_lookup(cfg_mgr->active->ctable->idp_realms,
-                                             orig_req->realm,
-                                             orig_req->comm,
+                                             fwd_req->realm,
+                                             fwd_req->comm,
                                              &idp_shared);
     } else {
       tr_debug("tr_tids_req_handler: route not local.");
@@ -429,38 +515,39 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
       idp_shared = 0;
     }
 
-    /* Since we aren't defaulting, check idp coi and apc membership */
-    if (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_comm, fwd_req->realm))) {
-      tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "IDP community membership error");
+    /* Since we aren't defaulting, check idp coi and apc membership of the original request */
+    if (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_comm, orig_req->realm))) {
+      tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of community (%s).", orig_req->realm->buf, cfg_comm->id->buf);
+      tid_resp_set_err_msg(resp, tr_new_name("IDP community membership error"));
       retval=-1;
       goto cleanup;
     }
-    if ( cfg_apc && (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_apc, fwd_req->realm)))) {
-      tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "IDP APC membership error");
+    if ( cfg_apc && (NULL == (tr_comm_find_idp(cfg_mgr->active->ctable, cfg_apc, orig_req->realm)))) {
+      tr_notice("tr_tids_req_handler: IDP Realm (%s) not member of APC (%s).", orig_req->realm->buf, cfg_apc->id->buf);
+      tid_resp_set_err_msg(resp, tr_new_name("IDP APC membership error"));
       retval=-1;
       goto cleanup;
     }
   }
 
-  /* Make sure we came through with a AAA server. If not, we can't handle the request. */
+  /* Make sure we came through with a AAA server. If not, we can't handle the request.
+   * Report using the original request, not translated values. */
   if (NULL == aaa_servers) {
     tr_notice("tr_tids_req_handler: no route or AAA server 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");
+    tid_resp_set_err_msg(resp, tr_new_name("Missing trust route error"));
     retval = -1;
     goto cleanup;
   }
 
   /* send a TID request to the AAA server(s), and get the answer(s) */
   tr_debug("tr_tids_req_handler: sending TID request(s).");
-  if (cfg_apc)
-    expiration_interval = cfg_apc->expiration_interval;
-  else expiration_interval = cfg_comm->expiration_interval;
+  /* Use the smaller of the APC's expiration interval and the expiration interval of the incoming request */
+  expiration_interval = cfg_apc->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;
+  else
+    fwd_req->expiration_interval = expiration_interval;
 
   /* Set up message queue for replies from req forwarding threads */
   mq=tr_mq_new(tmp_ctx);
@@ -613,13 +700,19 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
   }
 
   if (n_responses==0) {
-    /* No requests succeeded. Forward an error if we got any error responses. */
+    /* No requests succeeded, so this will be an error */
+    retval = -1;
+
+    /* If we got any error responses, send an arbitrarily chosen one. */
     for (ii=0; ii<n_aaa; ii++) {
-      if (aaa_resp[ii]!=NULL)
-        tids_send_response(tids, orig_req, aaa_resp[ii]);
-      else
-        tids_send_err_response(tids, orig_req, "Unable to contact AAA server(s).");
+      if (aaa_resp[ii] != NULL) {
+        tid_resp_cpy(resp, aaa_resp[ii]);
+        goto cleanup;
+      }
     }
+    /* No error responses at all, so generate our own error. */
+    tid_resp_set_err_msg(resp, tr_new_name("Unable to contact AAA server(s)."));
+    goto cleanup;
   }
 
   /* success! */
@@ -661,7 +754,7 @@ static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
 /* 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;
+  TIDS_INSTANCE *tids = talloc_get_type_abort(arg, TIDS_INSTANCE);
 
   if (0==(event & EV_READ))
     tr_debug("tr_tids_event_cb: unexpected event on TIDS socket (event=0x%X)", event);
@@ -669,19 +762,28 @@ static void tr_tids_event_cb(int listener, short event, void *arg)
     tids_accept(tids, listener);
 }
 
-/* Configure the tids instance and set up its event handler.
+/* called when it's time to sweep for completed TID child processes */
+static void tr_tids_sweep_cb(int listener, short event, void *arg)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(arg, TIDS_INSTANCE);
+
+  if (0==(event & EV_TIMEOUT))
+    tr_debug("tr_tids_event_cb: unexpected event on TID process sweep timer (event=0x%X)", event);
+  else
+    tids_sweep_procs(tids);
+}
+
+/* Configure the tids instance and set up its event handlers.
  * 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,
-                       TRPS_INSTANCE *trps,
-                       struct tr_socket_event *tids_ev)
+int tr_tids_event_init(struct event_base *base, TIDS_INSTANCE *tids, TR_CFG_MGR *cfg_mgr, TRPS_INSTANCE *trps,
+                       struct tr_socket_event *tids_ev, struct event **sweep_ev)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   struct tr_tids_event_cookie *cookie=NULL;
+  struct timeval sweep_interval;
   int retval=0;
-  size_t ii=0;
+  int ii=0;
 
   if (tids_ev == NULL) {
     tr_debug("tr_tids_event_init: Null tids_ev.");
@@ -689,6 +791,12 @@ int tr_tids_event_init(struct event_base *base,
     goto cleanup;
   }
 
+  if (sweep_ev == NULL) {
+    tr_debug("tr_tids_event_init: Null sweep_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);
@@ -703,21 +811,21 @@ int tr_tids_event_init(struct event_base *base,
   talloc_steal(tids, cookie);
 
   /* get a tids listener */
-  tids_ev->n_sock_fd=tids_get_listener(tids,
-                                       tr_tids_req_handler,
-                                       tr_tids_gss_handler,
-                                       cfg_mgr->active->internal->hostname,
-                                       cfg_mgr->active->internal->tids_port,
-                                       (void *)cookie,
-                                       tids_ev->sock_fd,
-                                       TR_MAX_SOCKETS);
+  tids_ev->n_sock_fd = (int)tids_get_listener(tids,
+                                              tr_tids_req_handler,
+                                              tr_tids_gss_handler,
+                                              cfg_mgr->active->internal->hostname,
+                                              cfg_mgr->active->internal->tids_port,
+                                              (void *)cookie,
+                                              tids_ev->sock_fd,
+                                              TR_MAX_SOCKETS);
   if (tids_ev->n_sock_fd==0) {
     tr_crit("Error opening TID server socket.");
     retval=1;
     goto cleanup;
   }
 
-  /* Set up events */
+  /* Set up listener events */
   for (ii=0; ii<tids_ev->n_sock_fd; ii++) {
     tids_ev->ev[ii]=event_new(base,
                               tids_ev->sock_fd[ii],
@@ -727,6 +835,12 @@ int tr_tids_event_init(struct event_base *base,
     event_add(tids_ev->ev[ii], NULL);
   }
 
+  /* Set up a periodic check for completed TID handler processes */
+  *sweep_ev = event_new(base, -1, EV_TIMEOUT|EV_PERSIST, tr_tids_sweep_cb, tids);
+  sweep_interval.tv_sec = 10;
+  sweep_interval.tv_usec = 0;
+  event_add(*sweep_ev, &sweep_interval);
+
 cleanup:
   talloc_free(tmp_ctx);
   return retval;
diff --git a/tr/tr_tid_mons.c b/tr/tr_tid_mons.c
new file mode 100644 (file)
index 0000000..0a18d2d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+/* Monitoring handlers for trust router TID server */
+
+#include <jansson.h>
+#include <tid_internal.h>
+#include <tr_tid.h>
+#include <mon_internal.h>
+#include <mons_handlers.h>
+
+/**
+ * Get the count of completed TID requests
+ */
+static MON_RC handle_show_req_count(void *cookie, json_t **response_ptr)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+  *response_ptr = json_integer(tids->req_count);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_req_err_count(void *cookie, json_t **response_ptr)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+  *response_ptr = json_integer(tids->error_count);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_req_pending(void *cookie, json_t **response_ptr)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+  *response_ptr = json_integer(tids->pids->len);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+void tr_tid_register_mons_handlers(TIDS_INSTANCE *tids, MONS_INSTANCE *mons)
+{
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_COUNT,
+                        handle_show_req_count, tids);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_ERR_COUNT,
+                        handle_show_req_err_count, tids);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQ_PENDING,
+                        handle_show_req_pending, tids);
+}
index 298299e..e075d58 100644 (file)
@@ -47,7 +47,9 @@
 #include <tr.h>
 #include <tr_mq.h>
 #include <tr_rp.h>
+#include <trp_route.h>
 #include <trp_internal.h>
+#include <trp_peer.h>
 #include <trp_ptable.h>
 #include <trp_rtable.h>
 #include <tr_config.h>
@@ -870,6 +872,20 @@ void tr_config_changed(TR_CFG *new_cfg, void *cookie)
   tr->cfgwatch->settling_time.tv_sec=new_cfg->internal->cfg_settling_time;
   tr->cfgwatch->settling_time.tv_usec=0;
 
+  /* These need to be updated */
+  tr->tids->hostname = new_cfg->internal->hostname;
+  tr->mons->hostname = new_cfg->internal->hostname;
+
+  /* Update the authorized monitoring gss names */
+  if (tr->mons->authorized_gss_names) {
+    tr_debug("tr_config_changed: freeing tr->mons->authorized_gss_names");
+    tr_gss_names_free(tr->mons->authorized_gss_names);
+  }
+  tr->mons->authorized_gss_names = tr_gss_names_dup(tr->mons, new_cfg->internal->monitoring_credentials);
+  if (tr->mons->authorized_gss_names == NULL) {
+    tr_err("tr_config_changed: Error configuring monitoring credentials");
+  }
+
   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);
diff --git a/tr/tr_trp_mons.c b/tr/tr_trp_mons.c
new file mode 100644 (file)
index 0000000..354d1c8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+
+/* Monitoring handlers for trust router TID server */
+
+#include <jansson.h>
+#include <trp_internal.h>
+#include <tr_trp.h>
+#include <trp_rtable.h>
+#include <trp_ptable.h>
+#include <tr_comm.h>
+#include <tr_idp.h>
+#include <mon_internal.h>
+#include <mons_handlers.h>
+
+static MON_RC handle_show_routes(void *cookie, json_t **response_ptr)
+{
+  TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE);
+
+  *response_ptr = trp_rtable_to_json(trps->rtable);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_peers(void *cookie, json_t **response_ptr)
+{
+  TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE);
+
+  *response_ptr = trp_ptable_to_json(trps->ptable);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_communities(void *cookie, json_t **response_ptr)
+{
+  TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE);
+
+  *response_ptr = tr_comm_table_to_json(trps->ctable);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+static MON_RC handle_show_realms(void *cookie, json_t **response_ptr)
+{
+  TRPS_INSTANCE *trps = talloc_get_type_abort(cookie, TRPS_INSTANCE);
+
+  *response_ptr = tr_idp_realms_to_json(trps->ctable->idp_realms);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+void tr_trp_register_mons_handlers(TRPS_INSTANCE *trps, MONS_INSTANCE *mons)
+{
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_ROUTES,
+                        handle_show_routes, trps);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_PEERS,
+                        handle_show_peers, trps);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_COMMUNITIES,
+                        handle_show_communities, trps);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_REALMS,
+                        handle_show_realms, trps);
+}
diff --git a/tr/trmon_main.c b/tr/trmon_main.c
new file mode 100644 (file)
index 0000000..e6d4253
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2012-2018, 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 <mon_internal.h>
+#include <tr_debug.h>
+
+
+/* command-line option setup */
+static void print_version_info(void)
+{
+  printf("Moonshot TR Monitoring Client %s\n\n", PACKAGE_VERSION);
+}
+
+
+/* argp global parameters */
+const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
+
+/* doc strings */
+static const char doc[]=PACKAGE_NAME " - Moonshot TR Monitoring Client";
+static const char arg_doc[]="<server> <port> <command> [<option> ...]"; /* 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[] = {
+    { "version", 'v', NULL, 0, "Print version information and exit" },
+    {NULL}
+};
+
+#define MAX_OPTIONS 20
+/* structure for communicating with option parser */
+struct cmdline_args {
+  char *server;
+  unsigned int port;
+  MON_CMD command;
+  MON_OPT_TYPE options[MAX_OPTIONS];
+  unsigned int n_options;
+};
+
+/* parser for individual options - fills in a struct cmdline_args */
+static error_t parse_option(int key, char *arg, struct argp_state *state)
+{
+  long tmp_l = 0;
+
+  /* get a shorthand to the command line argument structure, part of state */
+  struct cmdline_args *arguments=state->input;
+
+  switch (key) {
+    case 'v':
+      print_version_info();
+      exit(0);
+      break;
+
+    case ARGP_KEY_ARG: /* handle argument (not option) */
+      switch (state->arg_num) {
+        case 0:
+          arguments->server = arg;
+          break;
+
+        case 1:
+          tmp_l = strtol(arg, NULL, 10);
+          if (errno || (tmp_l < 0) || (tmp_l > 65535)) /* max valid port */
+            argp_usage(state);
+
+          arguments->port=(unsigned int) tmp_l;
+          break;
+
+        case 2:
+          arguments->command=mon_cmd_from_string(arg);
+          if (arguments->command == MON_CMD_UNKNOWN) {
+            printf("\nUnknown command '%s'\n", arg);
+            argp_usage(state);
+          }
+          break;
+
+        default:
+          if (arguments->n_options >= MAX_OPTIONS) {
+            printf("\nToo many command options given, limit is %d\n", MAX_OPTIONS);
+            argp_usage(state);
+          }
+
+          arguments->options[arguments->n_options] = mon_opt_type_from_string(arg);
+          if (arguments->options[arguments->n_options] == OPT_TYPE_UNKNOWN) {
+            printf("\nUnknown command option '%s'\n", arg);
+            argp_usage(state);
+          }
+          arguments->n_options++;
+          break;
+      }
+      break;
+
+    case ARGP_KEY_END: /* no more arguments */
+      if (state->arg_num < 3) {
+        /* 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);
+  MONC_INSTANCE *monc = NULL;
+  MON_REQ *req = NULL;
+  MON_RESP *resp = NULL;
+  unsigned int ii;
+
+  struct cmdline_args opts;
+  int retval=1; /* exit with an error status unless this gets set to zero */
+
+  /* parse the command line*/
+  /* set defaults */
+  opts.server = NULL;
+  opts.port = TRP_PORT;
+  opts.command = MON_CMD_UNKNOWN;
+  opts.n_options = 0;
+
+  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_WARNING);
+
+  /* Create a MON client instance & the client DH */
+  monc = monc_new(main_ctx);
+  if (monc == NULL) {
+    printf("Error allocating client instance.\n");
+    goto cleanup;
+  }
+
+
+  /* fill in the DH parameters */
+  monc_set_dh(monc, tr_create_dh_params(NULL, 0));
+  if (monc_get_dh(monc) == NULL) {
+    printf("Error creating client DH params.\n");
+    goto cleanup;
+  }
+
+  /* Set-up MON connection */
+  if (0 != monc_open_connection(monc, opts.server, opts.port)) {
+    /* Handle error */
+    printf("Error opening connection to %s:%d.\n", opts.server, opts.port);
+    goto cleanup;
+  };
+
+  req = mon_req_new(main_ctx, opts.command);
+  for (ii=0; ii < opts.n_options; ii++)
+    mon_req_add_option(req, opts.options[ii]);
+
+  /* Send a MON request and get the response */
+  resp = monc_send_request(main_ctx, monc, req);
+
+  if (resp == NULL) {
+    /* Handle error */
+    printf("Error executing monitoring request.\n");
+    goto cleanup;
+  }
+
+  /* Print the JSON to stdout */
+  json_dumpf(mon_resp_encode(resp), stdout, JSON_INDENT(4));
+  printf("\n");
+
+  /* success */
+  retval = 0;
+
+  /* Clean-up the MON client instance, and exit */
+cleanup:
+  talloc_free(main_ctx);
+  return retval;
+}
+
index da39570..7a6a585 100644 (file)
@@ -81,14 +81,14 @@ int main(int argc, const char *argv[])
   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);
+  msg= tr_msg_decode(NULL, buf, buflen);
 
 /*  if (rc==TRP_SUCCESS)
     trp_msg_print(msg);*/
 
   printf("\nEncoding...\n");
 
-  printf("Result: \n%s\n\n", tr_msg_encode(msg));
+  printf("Result: \n%s\n\n", tr_msg_encode(NULL, msg));
 
   talloc_report_full(main_ctx, stdout);
 
index e4824ea..5853895 100644 (file)
 #include <talloc.h>
 #include <assert.h>
 
-#include <tr_gss.h>
+#include <tr_gss_names.h>
+#include <trp_route.h>
 #include <trp_internal.h>
+#include <trp_peer.h>
 #include <trp_ptable.h>
 
-
 /* Can't do the updates test because trps_select_updates_for_peer() is now static */
 #define VERIFY_UPDATES 0
 
index 0ec1ec0..e651941 100644 (file)
@@ -38,6 +38,7 @@
 #include <assert.h>
 
 #include <tr_name_internal.h>
+#include <trp_route.h>
 #include <trp_internal.h>
 #include <trp_rtable.h>
 
@@ -243,7 +244,7 @@ static void update_metric(TRP_RTABLE *table, unsigned int (*new_metric)(size_t,
   TRP_ROUTE **entries=NULL;
   size_t n=0, ii=0,jj=0,kk=0;
 
-  entries=trp_rtable_get_entries(table, &n);
+  entries= trp_rtable_get_entries(NULL, table, &n);
   while (n--) {
     ii=get_index(trp_route_get_comm(entries[n])->buf, apc, n_apc);
     jj=get_index(trp_route_get_realm(entries[n])->buf, realm, n_realm);
diff --git a/trp/trp_peer.c b/trp/trp_peer.c
new file mode 100644 (file)
index 0000000..c98cf47
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2016-2018, 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 <time.h>
+#include <talloc.h>
+
+#include <tr_name_internal.h>
+#include <trp_internal.h>
+#include <tr_gss_names.h>
+#include <trp_ptable.h>
+#include <tr_debug.h>
+#include <trp_peer.h>
+
+static int trp_peer_destructor(void *object)
+{
+  TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER);
+  if (peer->label!=NULL)
+    tr_free_name(peer->label);
+  if (peer->servicename!=NULL)
+    tr_free_name(peer->servicename);
+  return 0;
+}
+TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
+{
+  TRP_PEER *peer=talloc(memctx, TRP_PEER);
+  if (peer!=NULL) {
+    peer->next=NULL;
+    peer->label=NULL;
+    peer->server=NULL;
+    peer->servicename=NULL;
+    peer->gss_names=NULL;
+    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;
+    peer->filters=NULL;
+    talloc_set_destructor((void *)peer, trp_peer_destructor);
+  }
+  return peer;
+}
+
+void trp_peer_free(TRP_PEER *peer)
+{
+  talloc_free(peer);
+}
+
+TRP_PEER *trp_peer_tail(TRP_PEER *peer)
+{
+  while (peer->next!=NULL) {
+    peer=peer->next;
+  }
+  return peer;
+}
+
+
+/* Get a name that identifies this peer for display to the user, etc.
+ * Do not modify or free the label. */
+TR_NAME *trp_peer_get_label(TRP_PEER *peer)
+{
+  char *s=NULL;
+
+  if (peer->label==NULL) {
+    s=talloc_asprintf(NULL, "%s:%u", peer->server, peer->port);
+    if (s!=NULL) {
+      peer->label=tr_new_name(s);
+      talloc_free(s);
+    }
+  }
+  return peer->label;
+}
+
+/* Get a name that identifies this peer for display to the user, etc.
+ * Makes a copy, caller is responsible for freeing.  */
+TR_NAME *trp_peer_dup_label(TRP_PEER *peer)
+{
+  return tr_dup_name(trp_peer_get_label(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->servicename will be null */
+void trp_peer_set_server(TRP_PEER *peer, const char *server)
+{
+  peer->server=talloc_strdup(peer, server); /* will be null on error */
+  trp_peer_set_servicename(peer, server);
+}
+
+void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gss_name)
+{
+  if (peer->gss_names==NULL)
+    trp_peer_set_gss_names(peer, tr_gss_names_new(peer));
+  tr_gss_names_add(peer->gss_names, gss_name);
+}
+
+void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names)
+{
+  if (peer->gss_names!=NULL)
+    tr_gss_names_free(peer->gss_names);
+
+  peer->gss_names=gss_names;
+  talloc_steal(peer, gss_names);
+}
+
+/* get the peer gss_names, caller must not free the result */
+TR_GSS_NAMES *trp_peer_get_gss_names(TRP_PEER *peer)
+{
+  return peer->gss_names;
+}
+
+/* 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;
+}
+
+/**
+ * Set the filter associated with this peer. Any existing filter will be freed. Takes responsibility for
+ * freeing the new filter.
+ *
+ * @param peer Peer to modify
+ * @param filts New filter to attach to the peer
+ */
+void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts)
+{
+  if (peer->filters!=NULL)
+    tr_filter_set_free(peer->filters);
+
+  peer->filters=filts;
+  talloc_steal(peer, filts);
+}
+
+TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype)
+{
+  return tr_filter_set_get(peer->filters, ftype);
+}
+
+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;
+}
+
+void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status)
+{
+  TR_NAME *peer_label=trp_peer_get_label(peer);
+  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.",
+           peer_label->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)
+{
+  TR_NAME *peer_label=trp_peer_get_label(peer);
+  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.",
+           peer_label->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);
+}
diff --git a/trp/trp_peer_encoders.c b/trp/trp_peer_encoders.c
new file mode 100644 (file)
index 0000000..8aafece
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016-2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <tr_gss_names.h>
+#include <trp_peer.h>
+#include <tr_util.h>
+
+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);
+}
+
+/* helper for encoding to json */
+static json_t *server_to_json_string(const char *server, unsigned int port)
+{
+  char *s = talloc_asprintf(NULL, "%s:%u", server, port);
+  json_t *jstr = json_string(s);
+  talloc_free(s);
+  return jstr;
+}
+
+static json_t *last_attempt_to_json_string(TRP_PEER *peer)
+{
+  struct timespec ts_zero = {0, 0};
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (tr_cmp_timespec(trp_peer_get_last_conn_attempt(peer), &ts_zero) == 0) {
+    s = strdup("");
+  } else {
+    s = timespec_to_str(trp_peer_get_last_conn_attempt(peer));
+  }
+
+  if (s) {
+    jstr = json_string(s);
+    free(s);
+  }
+
+  return jstr;
+}
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+json_t *trp_peer_to_json(TRP_PEER *peer)
+{
+  json_t *peer_json = NULL;
+  json_t *retval = NULL;
+
+  peer_json = json_object();
+  if (peer_json == NULL)
+    goto cleanup;
+
+
+  OBJECT_SET_OR_FAIL(peer_json, "server",
+                     server_to_json_string(trp_peer_get_server(peer),
+                                           trp_peer_get_port(peer)));
+  OBJECT_SET_OR_FAIL(peer_json, "servicename",
+                     tr_name_to_json_string(trp_peer_get_servicename(peer)));
+  OBJECT_SET_OR_FAIL(peer_json, "linkcost",
+                     json_integer(trp_peer_get_linkcost(peer)));
+  OBJECT_SET_OR_FAIL(peer_json, "connected_to",
+                     json_boolean(trp_peer_get_outgoing_status(peer) == PEER_CONNECTED));
+  OBJECT_SET_OR_FAIL(peer_json, "connected_from",
+                     json_boolean(trp_peer_get_incoming_status(peer) == PEER_CONNECTED));
+  OBJECT_SET_OR_FAIL(peer_json, "last_connection_attempt",
+                     last_attempt_to_json_string(peer));
+  OBJECT_SET_OR_FAIL(peer_json, "allowed_credentials",
+                     tr_gss_names_to_json_array(trp_peer_get_gss_names(peer)));
+  OBJECT_SET_OR_FAIL(peer_json, "filters",
+                     tr_filter_set_to_json(peer->filters));
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = peer_json;
+  json_incref(retval);
+
+cleanup:
+  if (peer_json)
+    json_decref(peer_json);
+  return retval;
+}
index e47846e..fa0ca84 100644 (file)
 
 #include <tr_name_internal.h>
 #include <trp_internal.h>
-#include <tr_gss.h>
+#include <tr_gss_names.h>
 #include <trp_ptable.h>
 #include <tr_debug.h>
+#include <trp_peer.h>
 
-static int trp_peer_destructor(void *object)
-{
-  TRP_PEER *peer=talloc_get_type_abort(object, TRP_PEER);
-  if (peer->label!=NULL)
-    tr_free_name(peer->label);
-  if (peer->servicename!=NULL)
-    tr_free_name(peer->servicename);
-  return 0;
-}
-TRP_PEER *trp_peer_new(TALLOC_CTX *memctx)
-{
-  TRP_PEER *peer=talloc(memctx, TRP_PEER);
-  if (peer!=NULL) {
-    peer->next=NULL;
-    peer->label=NULL;
-    peer->server=NULL;
-    peer->servicename=NULL;
-    peer->gss_names=NULL;
-    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;
-    peer->filters=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;
-}
-
-
-/* Get a name that identifies this peer for display to the user, etc. 
- * Do not modify or free the label. */
-TR_NAME *trp_peer_get_label(TRP_PEER *peer)
-{
-  char *s=NULL;
-
-  if (peer->label==NULL) {
-    s=talloc_asprintf(NULL, "%s:%u", peer->server, peer->port);
-    if (s!=NULL) {
-      peer->label=tr_new_name(s);
-      talloc_free(s);
-    }
-  }
-  return peer->label;
-}
-
-/* Get a name that identifies this peer for display to the user, etc. 
- * Makes a copy, caller is responsible for freeing.  */
-TR_NAME *trp_peer_dup_label(TRP_PEER *peer)
-{
-  return tr_dup_name(trp_peer_get_label(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->servicename will be null */
-void trp_peer_set_server(TRP_PEER *peer, const char *server)
-{
-  peer->server=talloc_strdup(peer, server); /* will be null on error */
-  trp_peer_set_servicename(peer, server);
-}
-
-void trp_peer_add_gss_name(TRP_PEER *peer, TR_NAME *gss_name)
-{
-  if (peer->gss_names==NULL)
-    trp_peer_set_gss_names(peer, tr_gss_names_new(peer));
-  tr_gss_names_add(peer->gss_names, gss_name);
-}
-
-void trp_peer_set_gss_names(TRP_PEER *peer, TR_GSS_NAMES *gss_names)
-{
-  if (peer->gss_names!=NULL)
-    tr_gss_names_free(peer->gss_names);
-
-  peer->gss_names=gss_names;
-  talloc_steal(peer, gss_names);
-}
-
-/* get the peer gss_names, caller must not free the result */
-TR_GSS_NAMES *trp_peer_get_gss_names(TRP_PEER *peer)
-{
-  return peer->gss_names;
-}
-
-/* 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;
-}
-
-/**
- * Set the filter associated with this peer. Any existing filter will be freed. Takes responsibility for
- * freeing the new filter.
- *
- * @param peer Peer to modify
- * @param filts New filter to attach to the peer
- */
-void trp_peer_set_filters(TRP_PEER *peer, TR_FILTER_SET *filts)
-{
-  if (peer->filters!=NULL)
-    tr_filter_set_free(peer->filters);
-
-  peer->filters=filts;
-  talloc_steal(peer, filts);
-}
-
-TR_FILTER *trp_peer_get_filter(TRP_PEER *peer, TR_FILTER_TYPE ftype)
-{
-  return tr_filter_set_get(peer->filters, ftype);
-}
-
-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)
 {
@@ -247,43 +52,6 @@ TRP_PTABLE *trp_ptable_new(TALLOC_CTX *memctx)
   return ptbl;
 }
 
-void trp_peer_set_outgoing_status(TRP_PEER *peer, TRP_PEER_CONN_STATUS status)
-{
-  TR_NAME *peer_label=trp_peer_get_label(peer);
-  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.",
-           peer_label->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)
-{
-  TR_NAME *peer_label=trp_peer_get_label(peer);
-  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.",
-           peer_label->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);
@@ -341,36 +109,6 @@ TRP_PEER *trp_ptable_find_servicename(TRP_PTABLE *ptbl, TR_NAME *servicename)
   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);
diff --git a/trp/trp_ptable_encoders.c b/trp/trp_ptable_encoders.c
new file mode 100644 (file)
index 0000000..2ff8c9c
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016-2018, 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 <talloc.h>
+#include <trp_peer.h>
+#include <trp_ptable.h>
+
+/* 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;
+}
+
+json_t *trp_ptable_to_json(TRP_PTABLE *ptbl)
+{
+  TRP_PTABLE_ITER *iter = trp_ptable_iter_new(NULL);
+  json_t *ptbl_json = json_array();
+  TRP_PEER *peer = trp_ptable_iter_first(iter, ptbl);
+  while(peer) {
+    json_array_append_new(ptbl_json, trp_peer_to_json(peer));
+    peer = trp_ptable_iter_next(iter);
+  }
+  trp_ptable_iter_free(iter);
+  return ptbl_json;
+}
diff --git a/trp/trp_route.c b/trp/trp_route.c
new file mode 100644 (file)
index 0000000..1334032
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2016-2018, 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 <glib.h>
+#include <talloc.h>
+#include <time.h>
+
+#include <tr_name_internal.h>
+#include <trp_route.h>
+#include <trp_internal.h>
+#include <trp_rtable.h>
+#include <tr_debug.h>
+#include <trust_router/trp.h>
+#include <trust_router/tid.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->trp_port=TRP_PORT;
+    entry->tid_port=TID_PORT;
+    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;
+}
+
+/* TODO: set the hostname and port for the next hop. Currently assume default TID port. --jlr */
+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)
+{
+  tr_debug("trp_route_set_triggered: setting route to %.*s/%.*s through %.*s to %s",
+           entry->comm->len, entry->comm->buf,
+           entry->realm->len, entry->realm->buf,
+           entry->peer->len, entry->peer->buf,
+           trig ? "triggered" : "not triggered");
+  entry->triggered=trig;
+}
+
+int trp_route_is_triggered(TRP_ROUTE *entry)
+{
+  return entry->triggered;
+}
diff --git a/trp/trp_route_encoders.c b/trp/trp_route_encoders.c
new file mode 100644 (file)
index 0000000..8e1809b
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2016-2018, 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 <string.h>
+
+#include <talloc.h>
+#include <jansson.h>
+
+#include <tr_name_internal.h>
+#include <trp_route.h>
+#include <trp_internal.h>
+#include <trp_rtable.h>
+#include <trust_router/trp.h>
+#include <tr_util.h>
+
+/* 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;
+}
+
+/* helper */
+static json_t *expiry_to_json_string(TRP_ROUTE *route)
+{
+  struct timespec ts_zero = {0, 0};
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (tr_cmp_timespec(trp_route_get_expiry(route), &ts_zero) > 0) {
+    s = timespec_to_str(trp_route_get_expiry(route));
+
+    if (s) {
+      jstr = json_string(s);
+      free(s);
+    }
+  }
+
+  return jstr;
+}
+
+/* helper for below */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+#define OBJECT_SET_OR_SKIP(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+} while (0)
+
+json_t *trp_route_to_json(TRP_ROUTE *route)
+{
+  json_t *route_json = NULL;
+  json_t *retval = NULL;
+
+  route_json = json_object();
+  if (route_json == NULL)
+    goto cleanup;
+
+  OBJECT_SET_OR_FAIL(route_json, "community", tr_name_to_json_string(trp_route_get_comm(route)));
+  OBJECT_SET_OR_FAIL(route_json, "realm", tr_name_to_json_string(trp_route_get_realm(route)));
+  if (trp_route_get_peer(route)->len > 0)
+    OBJECT_SET_OR_FAIL(route_json, "peer", tr_name_to_json_string(trp_route_get_peer(route)));
+  OBJECT_SET_OR_FAIL(route_json, "metric", json_integer(trp_route_get_metric(route)));
+  OBJECT_SET_OR_FAIL(route_json, "trust_router", tr_name_to_json_string(trp_route_get_trust_router(route)));
+  if (trp_route_get_next_hop(route)->len > 0)
+    OBJECT_SET_OR_FAIL(route_json, "next_hop", tr_name_to_json_string(trp_route_get_next_hop(route)));
+  OBJECT_SET_OR_FAIL(route_json, "selected", json_boolean(trp_route_is_selected(route)));
+  OBJECT_SET_OR_FAIL(route_json, "local", json_boolean(trp_route_is_local(route)));
+  OBJECT_SET_OR_SKIP(route_json, "expires", expiry_to_json_string(route));
+
+  /* succeeded - set the return value and increment the reference count */
+  retval = route_json;
+  json_incref(retval);
+
+
+cleanup:
+  if (route_json)
+    json_decref(route_json);
+  return retval;
+}
\ No newline at end of file
index e2ea5bb..2926309 100644 (file)
 #include <time.h>
 
 #include <tr_name_internal.h>
+#include <trp_route.h>
 #include <trp_internal.h>
 #include <trp_rtable.h>
 #include <tr_debug.h>
 #include <trust_router/trp.h>
 #include <trust_router/tid.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->trp_port=TRP_PORT;
-    entry->tid_port=TID_PORT;
-    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;
-}
-
-/* TODO: set the hostname and port for the next hop. Currently assume default TID port. --jlr */
-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)
-{
-  tr_debug("trp_route_set_triggered: setting route to %.*s/%.*s through %.*s to %s",
-           entry->comm->len, entry->comm->buf,
-           entry->realm->len, entry->realm->buf,
-           entry->peer->len, entry->peer->buf,
-           trig ? "triggered" : "not triggered");
-  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)
@@ -431,9 +229,9 @@ size_t trp_rtable_realm_size(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *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
+ * Caller must free the array (in the mem_ctx context), but must
  * not free its contents. */
-TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
+TRP_ROUTE **trp_rtable_get_entries(TALLOC_CTX *mem_ctx, TRP_RTABLE *rtbl, size_t *n_out)
 {
   TRP_ROUTE **ret=NULL;
   TR_NAME **comm=NULL;
@@ -446,7 +244,7 @@ TRP_ROUTE **trp_rtable_get_entries(TRP_RTABLE *rtbl, size_t *n_out)
   if (*n_out==0)
     return NULL;
 
-  ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
+  ret=talloc_array(mem_ctx, TRP_ROUTE *, *n_out);
   if (ret==NULL) {
     tr_crit("trp_rtable_get_entries: unable to allocate return array.");
     *n_out=0;
@@ -632,25 +430,6 @@ TRP_ROUTE *trp_rtable_get_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm,
   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;
@@ -675,46 +454,10 @@ TRP_ROUTE *trp_rtable_get_selected_entry(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAM
   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);
+  TRP_ROUTE **entries= trp_rtable_get_entries(NULL, rtbl, &n_entries);
   size_t ii=0;
 
   if (entries!=NULL) {
@@ -723,84 +466,3 @@ void trp_rtable_clear_triggered(TRP_RTABLE *rtbl)
     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_rtable_encoders.c b/trp/trp_rtable_encoders.c
new file mode 100644 (file)
index 0000000..ca63d6b
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016-2018, 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 <talloc.h>
+#include <jansson.h>
+
+#include <tr_name_internal.h>
+#include <trp_route.h>
+#include <trp_internal.h>
+#include <trp_rtable.h>
+#include <trust_router/trp.h>
+
+
+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;
+}
+
+
+json_t *trp_rtable_to_json(TRP_RTABLE *rtbl)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  json_t *rtable_json = NULL;
+  json_t *route_json = NULL;
+  TRP_ROUTE **routes = NULL;
+  size_t n_routes = 0;
+  json_t *retval = NULL;
+
+  /* Get the JSON array to return */
+  rtable_json = json_array();
+  if (rtable_json == NULL)
+    goto cleanup;
+
+  /* Get the array of routes */
+  routes = trp_rtable_get_entries(tmp_ctx, rtbl, &n_routes);
+  if (routes == NULL)
+    goto cleanup;
+
+  /* Gather JSON for each route */
+  while (n_routes > 0) {
+    route_json = trp_route_to_json(routes[--n_routes]);
+    if (route_json == NULL)
+      goto cleanup;
+    json_array_append_new(rtable_json, route_json);
+  }
+
+  /* Success - set the return value and increment the reference count */
+  retval = rtable_json;
+  json_incref(retval);
+
+cleanup:
+  if (rtable_json)
+    json_decref(rtable_json);
+  talloc_free(tmp_ctx);
+  return retval;
+}
index ebc365d..573f0a3 100644 (file)
 #include <sys/time.h>
 #include <glib.h>
 #include <string.h>
+#include <poll.h> // for nfds_t
 
 #include <gsscon.h>
 #include <tr_comm.h>
 #include <tr_apc.h>
 #include <tr_rp.h>
 #include <tr_name_internal.h>
+#include <trp_route.h>
 #include <trp_internal.h>
-#include <tr_gss.h>
+#include <tr_gss_names.h>
+#include <trp_peer.h>
 #include <trp_ptable.h>
 #include <trp_rtable.h>
 #include <tr_debug.h>
 #include <tr_util.h>
+#include <tr_socket.h>
 
 static int trps_destructor(void *object)
 {
@@ -276,82 +280,6 @@ TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
   return rc;
 }
 
-/* Listens on all interfaces. Returns number of sockets opened. Their
- * descriptors are stored in *fd_out, which should point to space for
- * up to max_fd of them. */
-static size_t trps_listen(TRPS_INSTANCE *trps, int port, int *fd_out, size_t max_fd) 
-{
-  int rc = 0;
-  int conn = -1;
-  int optval=0;
-  struct addrinfo *ai=NULL;
-  struct addrinfo *ai_head=NULL;
-  struct addrinfo hints={.ai_flags=AI_PASSIVE,
-                         .ai_family=AF_UNSPEC,
-                         .ai_socktype=SOCK_STREAM,
-                         .ai_protocol=IPPROTO_TCP};
-  char *port_str=NULL;
-  size_t n_opened=0;
-
-  port_str=talloc_asprintf(NULL, "%d", port);
-  if (port_str==NULL) {
-    tr_debug("trps_listen: unable to allocate port.");
-    return -1;
-  }
-  getaddrinfo(NULL, port_str, &hints, &ai_head);
-  talloc_free(port_str);
-
-  for (ai=ai_head,n_opened=0; (ai!=NULL)&&(n_opened<max_fd); ai=ai->ai_next) {
-    if (0 > (conn = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))) {
-      tr_debug("trps_listen: unable to open socket.");
-      continue;
-    }
-
-    optval=1;
-    if (0!=setsockopt(conn, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)))
-      tr_debug("trps_listen: unable to set SO_REUSEADDR."); /* not fatal? */
-
-    if (ai->ai_family==AF_INET6) {
-      /* don't allow IPv4-mapped IPv6 addresses (per RFC4942, not sure
-       * if still relevant) */
-      if (0!=setsockopt(conn, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval))) {
-        tr_debug("trps_listen: unable to set IPV6_V6ONLY. Skipping interface.");
-        close(conn);
-        continue;
-      }
-    }
-
-    rc=bind(conn, ai->ai_addr, ai->ai_addrlen);
-    if (rc<0) {
-      tr_debug("trps_listen: unable to bind to socket.");
-      close(conn);
-      continue;
-    }
-
-    if (0>listen(conn, 512)) {
-      tr_debug("trps_listen: unable to listen on bound socket.");
-      close(conn);
-      continue;
-    }
-
-    /* ok, this one worked. Save it */
-    fd_out[n_opened++]=conn;
-  }
-  freeaddrinfo(ai_head);
-
-  if (n_opened==0) {
-    tr_debug("trps_listen: no addresses available for listening.");
-    return -1;
-  }
-  
-  tr_debug("trps_listen: TRP Server listening on port %d on %d socket%s",
-           port,
-           n_opened,
-           (n_opened==1)?"":"s");
-
-  return n_opened;
-}
-
 /* get the currently selected route if available */
 TRP_ROUTE *trps_get_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer)
 {
@@ -410,7 +338,7 @@ static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MS
   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);
+  *msg= tr_msg_decode(NULL, buf, buflen);
   free(buf);
   if (*msg==NULL)
     return TRP_NOPARSE;
@@ -459,11 +387,12 @@ int trps_get_listener(TRPS_INSTANCE *trps,
                       int *fd_out,
                       size_t max_fd)
 {
-  size_t n_fd=0;
-  size_t ii=0;
+  nfds_t n_fd=0;
+  nfds_t ii=0;
+
+  n_fd = tr_sock_listen_all(port, fd_out, max_fd);
 
-  n_fd=trps_listen(trps, port, fd_out, max_fd);
-  if (n_fd==0)
+  if (n_fd == 0)
     tr_err("trps_get_listener: Error opening port %d.");
   else {
     /* opening port succeeded */
@@ -477,13 +406,13 @@ int trps_get_listener(TRPS_INSTANCE *trps,
           close(fd_out[ii]);
           fd_out[ii]=-1;
         }
-        n_fd=0;
+        n_fd = 0;
         break;
       }
     }
   }
 
-  if (n_fd>0) {
+  if (n_fd > 0) {
     /* store the caller's request handler & cookie */
     trps->msg_handler = msg_handler;
     trps->auth_handler = auth_handler;
@@ -492,7 +421,7 @@ int trps_get_listener(TRPS_INSTANCE *trps,
     trps->cookie = cookie;
   }
 
-  return n_fd;
+  return (int) n_fd;
 }
 
 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
@@ -1218,7 +1147,7 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
     return TRP_ERROR;
   }
 
-  entry=trp_rtable_get_entries(trps->rtable, &n_entry); /* must talloc_free *entry */
+  entry= trp_rtable_get_entries(NULL, trps->rtable, &n_entry); /* must talloc_free *entry */
 
   /* loop over the entries */
   for (ii=0; ii<n_entry; ii++) {
@@ -1245,26 +1174,6 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
 }
 
 
-static char *timespec_to_str(struct timespec *ts)
-{
-  struct tm tm;
-  char *s=NULL;
-
-  if (localtime_r(&(ts->tv_sec), &tm)==NULL)
-    return NULL;
-
-  s=malloc(40); /* long enough to contain strftime result */
-  if (s==NULL)
-    return NULL;
-
-  if (strftime(s, 40, "%F %T", &tm)==0) {
-    free(s);
-    return NULL;
-  }
-  return s;
-}
-
-
 /* Sweep for expired communities/realms/memberships. */
 TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
 {
@@ -1821,7 +1730,7 @@ static TRP_RC trps_update_one_peer(TRPS_INSTANCE *trps,
         upd = (TRP_UPD *) g_ptr_array_index(updates, ii);
         /* now encode the update message */
         tr_msg_set_trp_upd(&msg, upd);
-        encoded = tr_msg_encode(&msg);
+        encoded = tr_msg_encode(NULL, &msg);
         if (encoded == NULL) {
           tr_err("trps_update_one_peer: error encoding update.");
           rc = TRP_ERROR;
@@ -2006,7 +1915,7 @@ TRP_RC trps_wildcard_route_req(TRPS_INSTANCE *trps, TR_NAME *peer_servicename)
   }
 
   tr_msg_set_trp_req(&msg, req);
-  encoded=tr_msg_encode(&msg);
+  encoded= tr_msg_encode(NULL, &msg);
   if (encoded==NULL) {
     tr_err("trps_wildcard_route_req: error encoding wildcard TRP request.");
     rc=TRP_ERROR;
index f0000ec..b962bcd 100644 (file)
@@ -1,6 +1,6 @@
 %global optflags %{optflags} -Wno-parentheses
 Name:           trust_router
-Version:        3.3.0
+Version:        3.4.0~1
 Release:        1%{?dist}
 Summary:        Moonshot Trust Router