Merge pull request #100 from painless-security/milestone/monitoring
authorJennifer Richards <jennifer@painless-security.com>
Sat, 2 Jun 2018 02:23:01 +0000 (22:23 -0400)
committerGitHub <noreply@github.com>
Sat, 2 Jun 2018 02:23:01 +0000 (22:23 -0400)
Merge milestone/monitoring into master in preparation for release

129 files changed:
.gitignore
CMakeLists.txt [new file with mode: 0644]
Makefile.am
common/tests/cfg_test.c
common/tests/commtest.c
common/tests/filt_test.c
common/tests/mq_test.c
common/tests/thread_test.c
common/tr_aaa_server.c [new file with mode: 0644]
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_constraint.c
common/tr_filter.c
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_inet_util.c [new file with mode: 0644]
common/tr_list.c [new file with mode: 0644]
common/tr_mq.c
common/tr_msg.c
common/tr_name.c
common/tr_rand_id.c [new file with mode: 0644]
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
gsscon/gsscon_active.c
gsscon/gsscon_common.c
gsscon/test/gsscon_client.c
gsscon/test/gsscon_server.c
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_aaa_server.h [new file with mode: 0644]
include/tr_comm.h
include/tr_config.h
include/tr_constraint_internal.h [new file with mode: 0644]
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_inet_util.h [new file with mode: 0644]
include/tr_json_util.h [new file with mode: 0644]
include/tr_list.h [new file with mode: 0644]
include/tr_mon.h [new file with mode: 0644]
include/tr_mq.h
include/tr_msg.h
include/tr_name_internal.h
include/tr_rand_id.h [new file with mode: 0644]
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
include/trust_router/tr_constraint.h
include/trust_router/tr_name.h
include/trust_router/trp.h
m4/.ignore-this-file [new file with mode: 0644]
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_show_all_options.test [new file with mode: 0644]
mon/tests/req_show_no_options.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]
redhat/default-internal.cfg
redhat/organizations.cfg
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]
tr/trpc_main.c
trp/msgtst.c
trp/test/ptbl_test.c
trp/test/rtbl_test.c
trp/trp_conn.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/trp_upd.c
trp/trpc.c
trp/trps.c
trust_router.spec

index 7159568..6dab25d 100644 (file)
@@ -13,7 +13,8 @@ configure
 Makefile.in
 Makefile
 autom4te.cache
-m4
+m4/*
+!m4/.ignore-this-file
 ltmain.sh
 libtool
 config.*
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0761bfc
--- /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 common/tr_list.c include/tr_list.h include/tr_constraint_internal.h include/tr_json_util.h common/tr_aaa_server.c include/tr_aaa_server.h common/tr_inet_util.c include/tr_inet_util.h)
+
+# 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 include/tr_json_util.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 include/tr_json_util.h)
+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 include/tr_json_util.h)
+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 include/tr_json_util.h)
+target_link_libraries(test_mon_resp_encode jansson talloc glib-2.0)
index ae745ef..c08e9df 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 
@@ -13,27 +15,70 @@ common_srcs = common/tr_name.c \
        common/tr_dh.c \
     common/tr_debug.c \
        common/tr_util.c \
+       common/tr_inet_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_aaa_server.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 \
+       common/tr_list.c \
+       $(mon_srcs)
 
 tid_srcs = tid/tid_resp.c \
 tid/tid_req.c \
 tid/tids.c \
-tid/tidc.c
+tid/tidc.c \
+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
@@ -53,28 +98,39 @@ libtr_tid_la_LDFLAGS = $(AM_LDFLAGS) -version-info 4:1:2 -no-undefined
 common_t_constraint_SOURCES = common/t_constraint.c \
 common/tr_debug.c \
 common/tr_name.c \
+common/tr_list.c \
 common/tr_constraint.c \
 common/tr_dh.c \
+common/tr_rand_id.c \
 tid/tid_req.c \
 tid/tid_resp.c
 
 common_t_constraint_CPPFLAGS = $(AM_CPPFLAGS) -DTESTS=\"$(srcdir)/common/tests.json\"
-common_t_constraint_LDADD = gsscon/libgsscon.la 
+common_t_constraint_LDADD = gsscon/libgsscon.la $(GLIB_LIBS)
 
 tr_trust_router_SOURCES =tr/tr_main.c \
 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)
@@ -82,8 +138,20 @@ $(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 \
 trp/trp_req.c \
 trp/trp_upd.c \
 tid/tid_resp.c \
@@ -92,12 +160,20 @@ 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 \
+common/tr_inet_util.c \
+common/tr_list.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)
@@ -105,6 +181,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)
@@ -112,6 +190,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)
@@ -130,6 +210,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)
@@ -141,6 +223,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)
@@ -150,6 +234,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)
@@ -158,29 +244,62 @@ 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 \
        include/trust_router/tr_constraint.h \
        include/trust_router/tr_versioning.h 
 
-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_cfgwatch.h include/tr_event.h \
-       include/tr_mq.h include/trp_ptable.h \
-       include/trp_rtable.h include/tr_util.h \
-       include/tr_name_internal.h
+noinst_HEADERS = include/gsscon.h \
+    include/tr_config.h include/tr_cfgwatch.h \
+       include/tr.h \
+       include/tr_msg.h \
+       include/tr_idp.h \
+       include/tr_aaa_server.h \
+       include/tr_rp.h include/tr_rp_client.h \
+       include/tr_comm.h \
+       include/tr_apc.h \
+       include/tr_tid.h include/tid_internal.h \
+       include/tr_trp.h include/trp_internal.h \
+    include/tr_mon.h include/mon.h include/mon_internal.h include/mons_handlers.h \
+       include/tr_filter.h \
+       include/tr_gss.h include/tr_gss_client.h \
+    include/tr_gss_names.h \
+       include/tr_event.h \
+       include/tr_mq.h \
+       include/trp_peer.h include/trp_ptable.h \
+       include/trp_route.h include/trp_rtable.h \
+       include/tr_list.h \
+       include/tr_name_internal.h \
+       include/tr_util.h include/tr_json_util.h include/tr_inet_util.h\
+       include/tr_rand_id.h include/tr_socket.h \
+       include/tr_constraint_internal.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 97cd03c..29a949d 100644 (file)
@@ -41,6 +41,7 @@
 #include <tr_idp.h>
 #include <tr_config.h>
 #include <tr_debug.h>
+#include <tr_gss_names.h>
 
 static void tr_talloc_log(const char *msg)
 {
@@ -98,7 +99,6 @@ static int verify_idp_cfg(TR_CFG *cfg)
 
 static int verify_rp_cfg(TR_CFG *cfg)
 {
-  int ii=0;
   TR_NAME *name=NULL;
 
   assert(cfg!=NULL);
@@ -107,11 +107,9 @@ static int verify_rp_cfg(TR_CFG *cfg)
   assert(cfg->rp_clients->comm_next==NULL);
 
   assert(cfg->rp_clients->gss_names!=NULL);
-  for (ii=1; ii<TR_MAX_GSS_NAMES; ii++)
-    assert(cfg->rp_clients->gss_names->names[ii]==NULL);
-  assert(cfg->rp_clients->gss_names->names[0]!=NULL);
+  assert(tr_gss_names_length(cfg->rp_clients->gss_names) == 1);
   name=tr_new_name("gss@example.com");
-  assert(tr_name_cmp(name, cfg->rp_clients->gss_names->names[0])==0);
+  assert(tr_name_cmp(name, tr_gss_names_index(cfg->rp_clients->gss_names, 0))==0);
   return 0;
 }
 
index 72a2844..d2becc0 100644 (file)
@@ -239,11 +239,9 @@ struct aaa_entry {
 static TR_AAA_SERVER *aaa_entry_to_aaa_server(TALLOC_CTX *mem_ctx, struct aaa_entry *ae)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
-  TR_AAA_SERVER *aaa=tr_aaa_server_new(tmp_ctx, tr_new_name(ae->hostname));
+  TR_AAA_SERVER *aaa=tr_aaa_server_from_string(tmp_ctx, ae->hostname);
 
-  if ((aaa==NULL) || (aaa->hostname==NULL))
-    aaa=NULL;
-  else
+  if (aaa)
     talloc_steal(mem_ctx, aaa);
 
   talloc_free(tmp_ctx); 
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 5c04b37..b309e25 100644 (file)
@@ -59,7 +59,7 @@ int main(void)
   mq->notify_cb=notify_cb;
   mq->notify_cb_arg=mq_name;
 
-  msg1=tr_mq_msg_new(NULL,"Message 1", TR_MQ_PRIO_NORMAL);
+  msg1= tr_mq_msg_new(NULL, "Message 1");
   assert(asprintf((char **)&(msg1->p), "First message.\n")!=-1);
   msg1->p_free=free;
   tr_mq_add(mq, msg1);
@@ -67,7 +67,7 @@ int main(void)
   assert(mq->tail==msg1);
   assert(msg1->next==NULL);
 
-  msg2=tr_mq_msg_new(NULL, "Message 2", TR_MQ_PRIO_NORMAL);
+  msg2= tr_mq_msg_new(NULL, "Message 2");
   assert(asprintf((char **)&(msg2->p), "Second message.\n")!=-1);
   msg2->p_free=free;
   tr_mq_add(mq, msg2);
@@ -87,7 +87,7 @@ int main(void)
   } else
     printf("no message to pop\n");
   
-  msg3=tr_mq_msg_new(NULL, "Message 3", TR_MQ_PRIO_NORMAL);
+  msg3= tr_mq_msg_new(NULL, "Message 3");
   assert(asprintf((char **)&(msg3->p), "%s", "Third message.\n")!=-1);
   msg3->p_free=free;
   tr_mq_add(mq, msg3);
@@ -127,7 +127,7 @@ int main(void)
   } else
     printf("no message to pop\n");
 
-  msg4=tr_mq_msg_new(NULL, "Message 4", TR_MQ_PRIO_NORMAL);
+  msg4= tr_mq_msg_new(NULL, "Message 4");
   assert(asprintf((char **)&(msg4->p), "%s", "Fourth message.\n")!=-1);
   msg4->p_free=free;
   tr_mq_add(mq, msg4);
index 14aac6f..35d7882 100644 (file)
@@ -53,7 +53,7 @@ struct thread_data {
 static TR_MQ_MSG *make_msg(char *label, int n)
 {
   TR_MQ_MSG *msg=NULL;
-  msg=tr_mq_msg_new(NULL, "Message", TR_MQ_PRIO_NORMAL);
+  msg= tr_mq_msg_new(NULL, "Message");
   assert(-1!=asprintf((char **)&(msg->p), "%s: %d messages to go...", label, n));
   msg->p_free=free;
   return msg;
diff --git a/common/tr_aaa_server.c b/common/tr_aaa_server.c
new file mode 100644 (file)
index 0000000..dedf054
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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_name_internal.h>
+#include <tr_aaa_server.h>
+#include <trust_router/tid.h>
+#include <tr_util.h>
+#include <tr_inet_util.h>
+
+static int tr_aaa_server_destructor(void *obj)
+{
+  TR_AAA_SERVER *aaa=talloc_get_type_abort(obj, TR_AAA_SERVER);
+  if (aaa->hostname!=NULL)
+    tr_free_name(aaa->hostname);
+  return 0;
+}
+
+TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx)
+{
+  TR_AAA_SERVER *aaa=talloc(mem_ctx, TR_AAA_SERVER);
+  if (aaa!=NULL) {
+    aaa->next=NULL;
+    aaa->hostname = NULL;
+    tr_aaa_server_set_port(aaa, 0); /* go through setter to guarantee consistent default */
+    talloc_set_destructor((void *)aaa, tr_aaa_server_destructor);
+  }
+  return aaa;
+}
+
+void tr_aaa_server_free(TR_AAA_SERVER *aaa)
+{
+  talloc_free(aaa);
+}
+
+TR_NAME *tr_aaa_server_get_hostname(TR_AAA_SERVER *aaa)
+{
+  return aaa->hostname;
+}
+
+/**
+ * Set the hostname for a AAA server
+ *
+ * Takes ownership of the TR_NAME. Does nothing if aaa is null.
+ *
+ * @param aaa
+ * @param hostname
+ */
+void tr_aaa_server_set_hostname(TR_AAA_SERVER *aaa, TR_NAME *hostname)
+{
+  if (aaa == NULL)
+    return;
+
+  if (aaa->hostname != NULL) {
+    tr_free_name(aaa->hostname);
+  }
+
+  aaa->hostname = hostname;
+}
+
+int tr_aaa_server_get_port(TR_AAA_SERVER *aaa)
+{
+  return aaa->port;
+}
+
+/**
+ * Set the port for a AAA server
+ *
+ * If port is 0, uses the standard TID port (12309). Other invalid values are stored
+ * as-is.
+ *
+ * Does nothing if aaa is null.
+ *
+ * @param aaa
+ * @param port
+ */
+void tr_aaa_server_set_port(TR_AAA_SERVER *aaa, int port)
+{
+  if (aaa == NULL)
+    return;
+
+  if (port == 0)
+    port = TID_PORT;
+
+  aaa->port = port;
+}
+
+/**
+ * Allocate a AAA server record and fill it in by parsing a hostname:port string
+ *
+ * If hostname or port are invalid, hostname will be empty and port will be -1.
+ *
+ * @return newly allocated TR_AAA_SERVER in the mem_ctx context, or NULL on error
+ */
+TR_AAA_SERVER *tr_aaa_server_from_string(TALLOC_CTX *mem_ctx, const char *s)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_AAA_SERVER *aaa = tr_aaa_server_new(tmp_ctx);
+  char *hostname;
+  int port;
+
+  if (aaa == NULL)
+    goto failed;
+
+  hostname = tr_parse_host(tmp_ctx, s, &port);
+  if (NULL == hostname) {
+    hostname = "";
+    port = -1;
+  }
+
+  tr_aaa_server_set_hostname(aaa, tr_new_name(hostname));
+  if (tr_aaa_server_get_hostname(aaa) == NULL)
+    goto failed;
+
+  tr_aaa_server_set_port(aaa, port); /* port = 0 uses default TID port */
+  talloc_steal(mem_ctx, aaa); /*put this in the caller's context */
+  goto succeeded;
+
+failed:
+  aaa = NULL; /* talloc will free the memory if it was allocated */
+
+succeeded:
+  talloc_free(tmp_ctx);
+  return aaa;
+}
+
+TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx)
+{
+  return talloc(mem_ctx, TR_AAA_SERVER_ITER);
+}
+
+void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa)
+{
+  iter->this=aaa;
+  return iter->this;
+}
+
+TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter)
+{
+  if (iter->this!=NULL) {
+    iter->this=iter->this->next;
+  }
+  return iter->this;
+}
index dd3c304..725fcac 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
 #include <tr_rp.h>
 #include <tr_idp.h>
 #include <tr_name_internal.h>
+#include <trp_internal.h>
 #include <tr_comm.h>
 #include <tr_debug.h>
-
+#include <tr_util.h>
 
 static int tr_comm_destructor(void *obj)
 {
@@ -761,9 +762,53 @@ TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
 }
 
 
-/* iterate over all memberships in the table */
+/* iterate over all memberships in the table
+ *
+ * The table is structured as a vertical list of memberships, each for a
+ * different community/realm. Each element in this list has a horizontal "origin list,"
+ * each for the same community/realm but with a different origin for the membership.
+ * Only the first element in the origin list has a vertical link ("next" pointer).
+ * Any element may have a horizontal link ("origin_next" pointer).
+ *
+ *  (A) - (B) - (C) - X
+ *   |
+ *  (D) - (E) - X
+ *   |
+ *  (F) - X
+ *   |
+ *  (G) - (H) - X
+ *   |
+ *   X
+ *
+ *  A, B, and C are all community/realm pair membership 1, with different origins
+ *  D, E are a second community/realm pair, with different origins
+ *  F is a third...
+ *  G, H are a fourth pair, with different origins
+ *
+ * This iterator will return every element in the grid.
+ *
+ * Algorithm:
+ *   The iterator struct stores the current member (cur_memb) and the origin head (cur_orig_head).
+ *   The latter is a pointer to the head of the current origin list (i.e., first element in a row).
+ *   The former can point to any element in the list. Both start at the root of the list (A in the
+ *   diagram above).
+ *
+ *   After each call to _first() or _next(), cur_memb points to the element just returned.
+ *
+ *   0. _first() just returns the first element. The rest of the steps are in _next()
+ *
+ *   1. If cur_memb has an origin_next element, walk the origin list. Move cur_memb to
+ *      the origin_list element (next one in this row) and return it.
+ *   2. If cur_memb does not have an origin_next element, we've finished the current origin
+ *      list. Move cur_memb to cur_orig_head's next element (the start of the next column),
+ *      move cur_orig_head to that same place, and return it.
+ *   3. If neither cur_memb has an origin_next element nor cur_orig_head has a next element,
+ *      then we have already reached the end of the list and there's nothing more to do.
+ *      Return NULL.
+ */
 TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
 {
+  /* step 0: return the root of the list */
   iter->cur_memb=ctab->memberships;
   iter->cur_orig_head=ctab->memberships;
   return iter->cur_memb;
@@ -771,16 +816,15 @@ TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *cta
 
 TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter)
 {
-  if (iter->cur_memb->next==NULL) {
-    if (iter->cur_orig_head->next==NULL) {
-      /* we're done */
-      return NULL;
-    } else {
-      iter->cur_memb=iter->cur_orig_head->next;
-      iter->cur_orig_head=iter->cur_orig_head->next;
-    }
+  if (iter->cur_memb->origin_next) {
+    /* step 1: return the next element in the current origin list */
+    iter->cur_memb = iter->cur_memb->origin_next;
+  } else if (iter->cur_orig_head->next) {
+    /* step 2: move to the start of the next row and return the first element */
+    iter->cur_orig_head = iter->cur_memb = iter->cur_orig_head->next;
   } else {
-    iter->cur_memb=iter->cur_memb->origin_next;
+    /* step 3: both cur_memb->origin_next and cur_orig_head->next are null */
+    iter->cur_orig_head = iter->cur_memb = NULL;
   }
   return iter->cur_memb;
 }
@@ -1017,6 +1061,18 @@ struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
   return memb->expiry;
 }
 
+/**
+ * Get the expiration according to the realtime clock
+ *
+ * @param memb
+ * @param result space to store the result
+ * @return pointer to the result, or null on error
+ */
+struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result)
+{
+  return tr_clock_convert(TRP_CLOCK, memb->expiry, CLOCK_REALTIME, result);
+}
+
 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
 {
   tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s",
@@ -1322,11 +1378,24 @@ TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id)
   return tr_comm_lookup(ctab->comms, comm_id);
 }
 
-void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
+/**
+ * Add a community to the table.
+ *
+ * Does not allow duplicate community ids.
+ *
+ * @param ctab
+ * @param new
+ * @return 0 on success, -1 on failure
+ */
+int tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
 {
+  if (tr_comm_table_find_comm(ctab, tr_comm_get_id(new)) != NULL)
+    return -1;
+
   tr_comm_add(ctab->comms, new);
   if (ctab->comms!=NULL)
     talloc_steal(ctab, ctab->comms); /* make sure it's in the right context */
+  return 0;
 }
 
 void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm)
diff --git a/common/tr_comm_encoders.c b/common/tr_comm_encoders.c
new file mode 100644 (file)
index 0000000..9f53008
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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 = {0}; /* initialization to zero is important */
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (tr_cmp_timespec(tr_comm_memb_get_expiry(memb), &ts) > 0) {
+    if (tr_comm_memb_get_expiry_realtime(memb, &ts) == NULL)
+      s = strdup("error");
+    else
+      s = timespec_to_str(&ts);
+
+    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 */
+  for (memb = tr_comm_memb_iter_first(iter, first_memb);
+       memb != NULL;
+       memb = tr_comm_memb_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_comm_memb_to_json(memb));
+  }
+
+  /* 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 = NULL;
+
+  /* Do not display the full realm json here, only the name and info relevant to the community listing */
+  for (realm = tr_realm_iter_first(iter, ctable, comm_name);
+       realm != NULL;
+       realm = tr_realm_iter_next(iter)) {
+    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 */
+    }
+  }
+
+  /* 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 */
+  for (comm = tr_comm_table_iter_first(iter, ctable);
+       comm != NULL;
+       comm = tr_comm_table_iter_next(iter)) {
+    comm_json = tr_comm_to_json(ctable, comm);
+
+    if (comm_json == NULL)
+      goto cleanup;
+
+    json_array_append_new(ctable_json, comm_json);
+  }
+
+  /* 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..e0becec 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)
-{
-  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)
+static int tr_cfg_destructor(void *object)
 {
-  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)
 {
@@ -1941,11 +195,10 @@ TR_CFG_RC tr_cfg_validate(TR_CFG *trc)
   if (!trc)
     return TR_CFG_BAD_PARAMS;
 
-  if ((NULL == trc->internal)||
-      (NULL == trc->internal->hostname)) {
-    tr_debug("tr_cfg_validate: Error: No internal configuration, or no hostname.");
+  /* validate the internal config - error messages will be generated there, so don't genreate
+   * our own */
+  if (tr_cfg_validate_internal(trc->internal) != TR_CFG_SUCCESS)
     rc = TR_CFG_ERROR;
-  }
 
   if (NULL == trc->rp_clients) {
     tr_debug("tr_cfg_validate: Error: No RP Clients configured");
@@ -1983,7 +236,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 +246,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 +284,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 +298,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_config_files: Attempting to load revision %"
+                    JSON_INTEGER_FORMAT
+                    " of '%s'.",
+                this_file->serial,
+                this_file->name);
+    } else {
+      tr_notice("tr_parse_config_files: 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 +333,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 +353,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 +369,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 +409,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 +422,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 +448,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..99e2070
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * 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;
+      }
+      if (tr_comm_table_add_comm(trc->ctable, comm) != 0) {
+        tr_debug("tr_cfg_parse_comms: Duplicate community %s.", tr_comm_get_id(comm)->buf);
+        return TR_CFG_NOPARSE;
+      }
+
+      tr_debug("tr_cfg_parse_comms: Community configured: %s.",
+               tr_comm_get_id(comm)->buf);
+    }
+  }
+  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..a6307cd
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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 <jansson.h>
+#include <tr_config.h>
+#include <tr_json_util.h>
+
+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..7d4c615
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * 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 <talloc.h>
+
+#include <tr_constraint_internal.h>
+#include <tr_cfgwatch.h>
+#include <tr_debug.h>
+
+#if JANSSON_VERSION_HEX < 0x020500
+#include "jansson_iterators.h"
+#endif
+
+static TR_CONSTRAINT *tr_cfg_parse_one_constraint(TALLOC_CTX *mem_ctx, const char *ctype, json_t *jc, TR_CFG_RC *rc)
+{
+  TR_CONSTRAINT *cons=NULL;
+  size_t i=0;
+
+  if (!rc) {
+    tr_err("tr_cfg_parse_one_constraint: rc is null, cannot process constraint.");
+    return NULL;
+  }
+
+  if ((!ctype) || (!jc) ||
+      (!json_is_array(jc)) ||
+      (0 >= 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++) {
+    if (NULL == tr_constraint_add_match(cons, tr_new_name(json_string_value(json_array_get(jc, i))))) {
+      tr_err("tr_cfg_parse_one_constraint: Out of memory (match %d).", i);
+      *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;
+  TR_FLINE *fline = NULL;
+  TR_FSPEC *fspec = 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);
+
+  /* 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;
+    }
+
+    fline = tr_fline_new(tmp_ctx);
+    if (fline == NULL) {
+      tr_debug("tr_cfg_parse_one_filter: Out of memory allocating filter line %d.", i);
+      *rc = TR_CFG_NOMEM;
+      goto cleanup;
+    }
+    if (!strcmp(json_string_value(jfaction), "accept")) {
+      fline->action = TR_FILTER_ACTION_ACCEPT;
+      tr_debug("tr_cfg_parse_one_filter: Filter action is 'accept'");
+
+    } else if (!strcmp(json_string_value(jfaction), "reject")) {
+      fline->action = TR_FILTER_ACTION_REJECT;
+      tr_debug("tr_cfg_parse_one_filter: Filter action is '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) > 0) {
+        /* ok we actually have entries to process */
+        if (NULL == (fline->realm_cons = tr_cfg_parse_one_constraint(fline, "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) > 0) {
+        if (NULL == (fline->domain_cons = tr_cfg_parse_one_constraint(fline, "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... */
+    tr_debug("tr_cfg_parse_one_filter: Filter line has %d spec(s)", json_array_size(jfspecs));
+    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 == (fspec = tr_fspec_new(fline))) {
+        tr_debug("tr_cfg_parse_one_filter: Out of memory.");
+        *rc = TR_CFG_NOMEM;
+        goto cleanup;
+      }
+
+      /* fill in the field */
+      if (NULL == (fspec->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(fspec, 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(fspec, name);
+        }
+      }
+      if (!tr_filter_validate_spec_field(ftype, fspec)) {
+        tr_debug("tr_cfg_parse_one_filter: Invalid filter field \"%.*s\" for %s filter, spec %d, filter %d.",
+                 fspec->field->len,
+                 fspec->field->buf,
+                 tr_filter_type_to_string(filt->type),
+                 i, j);
+        *rc = TR_CFG_ERROR;
+        goto cleanup;
+      }
+
+      if (tr_fline_add_spec(fline, fspec) == NULL) {
+        tr_debug("tr_cfg_parse_one_filter: Unable to add spec %d to line %d of %s filter.",
+                 j, i, tr_filter_type_to_string(filt->type));
+      }
+    }
+
+    if (NULL == tr_filter_add_line(filt, fline)) {
+      tr_debug("tr_cfg_parse_one_filter: Error adding line %d for %s filter",
+               i, tr_filter_type_to_string(filt->type));
+      *rc = TR_CFG_NOMEM;
+      goto cleanup;
+    }
+    tr_debug("tr_cfg_parse_one_filter: Added line %d to %s filter", i, tr_filter_type_to_string(filt->type));
+  }
+
+  /* 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);
+    if (*rc != TR_CFG_SUCCESS) {
+      tr_debug("tr_cfg_parse_filters: Error parsing %s filter.", filt_label);
+      *rc = TR_CFG_NOPARSE;
+      goto cleanup;
+    }
+    if (tr_filter_set_add(filt_set, filt) != 0) {
+      tr_debug("tr_cfg_parse_filters: Error adding %s filter to filter set.", 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..f6bd011
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * 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 a signed 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 integer
+ * @return TR_CFG_SUCCESS or an error code
+ */
+static TR_CFG_RC tr_cfg_parse_integer(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_number(jtmp)) {
+      *dest = (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 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)) {
+      tr_debug("tr_cfg_parse_unsigned: Parsing error, %s is not a number.", key);
+      return TR_CFG_NOPARSE;
+    } else if (json_integer_value(jtmp) < 0) {
+      tr_debug("tr_cfg_parse_unsigned: Value %d < 0.", json_integer_value(jtmp));
+      return TR_CFG_NOPARSE;
+    } else {
+      *dest = (unsigned int) json_integer_value(jtmp);
+    }
+  }
+
+  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->mons_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_integer(jmon, "port", &(trc->internal->mons_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_integer(jint, "tids_port",                &(trc->internal->tids_port)));
+  NOPARSE_UNLESS(tr_cfg_parse_integer(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;
+}
+
+static int invalid_port(int port)
+{
+  return ((port <= 0) || (port > 65536));
+}
+
+/**
+ * Validate the internal configuration of the trust router
+ *
+ * Validates fields, emitting errors if there are any. Safe to call with
+ * a null int_cfg, but this results in an error being returned.
+ *
+ * @param int_cfg pointer to an internal configuration (NULL is safe)
+ * @return success or error
+ */
+TR_CFG_RC tr_cfg_validate_internal(TR_CFG_INTERNAL *int_cfg)
+{
+  TR_CFG_RC rc;
+
+  /* ensure we have an internal configuration and exit if not  */
+  if (NULL == int_cfg) {
+    tr_debug("tr_cfg_validate_internal: No internal configuration present.");
+    return TR_CFG_BAD_PARAMS;
+  }
+
+  /* Assume we are going to succeed. If any errors are encountered, emit a message
+   * and set the return code to an error. Don't exit early, emit all the errors
+   * at once if we can. */
+  rc = TR_CFG_SUCCESS;
+
+  /*** Validate hostname ***/
+  if (NULL == int_cfg->hostname) {
+    tr_debug("tr_cfg_validate_internal: No hostname specified.");
+    rc = TR_CFG_ERROR;
+  }
+
+  /*** Validate various intervals ***/
+  if (TR_MIN_TRP_CONNECT_INTERVAL > int_cfg->trp_connect_interval) {
+    tr_debug(
+        "tr_cfg_validate_internal: Error: trp_connect_interval must be at least %d (currently %d).",
+        TR_MIN_TRP_CONNECT_INTERVAL, int_cfg->trp_connect_interval);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (TR_MIN_TRP_SWEEP_INTERVAL > int_cfg->trp_sweep_interval) {
+    tr_debug(
+        "tr_cfg_validate_internal: Error: trp_sweep_interval must be at least %d (currently %d).",
+        TR_MIN_TRP_SWEEP_INTERVAL, int_cfg->trp_sweep_interval);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (TR_MIN_TRP_UPDATE_INTERVAL > int_cfg->trp_update_interval) {
+    tr_debug(
+        "tr_cfg_validate_internal: Error: trp_update_interval must be at least %d (currently %d).",
+        TR_MIN_TRP_UPDATE_INTERVAL, int_cfg->trp_update_interval);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (TR_MIN_CFG_POLL_INTERVAL > int_cfg->cfg_poll_interval) {
+    tr_debug(
+        "tr_cfg_validate_internal: Error: cfg_poll_interval must be at least %d (currently %d).",
+        TR_MIN_CFG_POLL_INTERVAL, int_cfg->cfg_poll_interval);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (TR_MIN_CFG_SETTLING_TIME > int_cfg->cfg_settling_time) {
+    tr_debug(
+        "tr_cfg_validate_internal: Error: cfg_settling_time must be at least %d (currently %d).",
+        TR_MIN_CFG_SETTLING_TIME, int_cfg->cfg_settling_time);
+    rc = TR_CFG_ERROR;
+  }
+
+  /*** Validate ports ***/
+  if (invalid_port(int_cfg->tids_port)) {
+    tr_debug("tr_cfg_validate_internal: Error: invalid tids_port (%d).", int_cfg->tids_port);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (invalid_port(int_cfg->trps_port)) {
+    tr_debug("tr_cfg_validate_internal: Error: invalid trps_port (%d).", int_cfg->trps_port);
+    rc = TR_CFG_ERROR;
+  }
+
+  if (invalid_port(int_cfg->mons_port)) {
+    tr_debug("tr_cfg_validate_internal: Error: invalid monitoring port (%d).", int_cfg->mons_port);
+    rc = TR_CFG_ERROR;
+  }
+
+  /*** Validate tid request timeout ***/
+  if (TR_MIN_TID_REQ_TIMEOUT > int_cfg->tid_req_timeout) {
+    tr_debug("tr_cfg_validate_internal: Error: tid_request_timeout must be at least %d (currently %d).",
+             TR_MIN_TID_REQ_TIMEOUT, int_cfg->tid_req_timeout);
+    rc = TR_CFG_ERROR;
+  }
+
+  /*** Validate tid response parameters ***/
+  if ((int_cfg->tid_resp_numer <= 0)
+      || (int_cfg->tid_resp_denom <= 0)
+      || (int_cfg->tid_resp_numer > int_cfg->tid_resp_denom)) {
+    tr_debug("tr_cfg_validate_internal: Error: invalid tid_response_numerator / tid_response_denominator. Both must be positive and the numerator/denominator ratio must be <= 1 (currently %d/%d).",
+             int_cfg->tid_resp_numer, int_cfg->tid_resp_denom);
+    rc = TR_CFG_ERROR;
+  }
+  return rc;
+}
\ No newline at end of file
diff --git a/common/tr_config_orgs.c b/common/tr_config_orgs.c
new file mode 100644 (file)
index 0000000..e9b2c3c
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * 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>
+#include <tr_util.h>
+#include <tr_inet_util.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 *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;
+  char *hostname=NULL;
+  int port;
+
+  jhost=json_object_get(jporg, "hostname");
+  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 ((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;
+  }
+
+  /* parse / validate the hostname and port */
+  hostname = tr_parse_host(tmp_ctx, json_string_value(jhost), &port);
+  if (NULL == hostname) {
+    tr_err("tr_cfg_parse_one_peer_org: error parsing hostname (%s)", json_string_value(jhost));
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if (port < 0) {
+    tr_err("tr_cfg_parse_one_peer_org: invalid port (%s)", json_string_value(jhost));
+    rc=TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if (port == 0)
+    port = TRP_PORT;
+  trp_peer_set_port(new_peer, port);
+
+  trp_peer_set_server(new_peer, hostname); /* string is strdup'ed in _set_server() */
+  if (trp_peer_get_server(new_peer) == NULL) {
+    tr_err("tr_cfg_parse_one_peer: could not set server hostname for new peer");
+    rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  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..3c189ee
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * 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)
+{
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_AAA_SERVER *aaa = NULL;
+
+  if ((!jaddr) || (!json_is_string(jaddr))) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Bad parameters.");
+    *rc = TR_CFG_BAD_PARAMS;
+    goto cleanup;
+  }
+
+  aaa = tr_aaa_server_from_string(mem_ctx, json_string_value(jaddr));
+  if (aaa == NULL) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Out of memory allocating AAA server.");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  if (tr_aaa_server_get_hostname(aaa)->len == 0) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Invalid hostname for AAA server (%s)",
+             json_string_value(jaddr));
+    *rc = TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  if ((tr_aaa_server_get_port(aaa) <= 0)
+      || (tr_aaa_server_get_port(aaa) > 65535)) {
+    tr_debug("tr_cfg_parse_one_aaa_server: Invalid AAA server port (%s)",
+             json_string_value(jaddr));
+    *rc = TR_CFG_NOPARSE;
+    goto cleanup;
+  }
+
+  /* success ! */
+  *rc = TR_CFG_SUCCESS;
+  talloc_steal(mem_ctx, aaa);
+
+cleanup:
+  if (*rc != TR_CFG_SUCCESS)
+    aaa = NULL;
+  talloc_free(tmp_ctx);
+  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:
+  if (*rc != TR_CFG_SUCCESS)
+    apcs = NULL;
+
+  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..b263c02
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * 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 <tr_constraint_internal.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_FLINE *fline = NULL;
+  TR_FSPEC *fspec = 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);
+
+  fline = tr_fline_new(tmp_ctx);
+  if (fline==NULL) {
+    tr_debug("tr_cfg_default_filters: could not allocate filter line.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  fline->action=TR_FILTER_ACTION_ACCEPT;
+  
+  fspec=tr_fspec_new(tmp_ctx);
+  fspec->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(fspec, name);
+  name=NULL; /* we no longer own the name */
+
+  if (tr_fline_add_spec(fline, fspec) == NULL) {
+    tr_debug("tr_cfg_default_filters: could not add first spec to filter line");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  /* now do the wildcard name */
+  fspec=tr_fspec_new(tmp_ctx);
+  fspec->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(fspec, name);
+  name=NULL; /* we no longer own the name */
+
+  if (tr_fline_add_spec(fline, fspec) == NULL) {
+    tr_debug("tr_cfg_default_filters: could not add second spec to filter line");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  /* domain constraint */
+  if (NULL==(cons=tr_constraint_new(fline))) {
+    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;
+  }
+  if (NULL == tr_constraint_add_match(cons, name)) {
+    tr_debug("tr_cfg_default_filters: could not add realm name for domain constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  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;
+  }
+  if (NULL == tr_constraint_add_match(cons, name)) {
+    tr_debug("tr_cfg_default_filters: could not add wildcard realm name for domain constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  name=NULL;
+  fline->domain_cons=cons;
+
+
+  /* realm constraint */
+  if (NULL==(cons=tr_constraint_new(fline))) {
+    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;
+  }
+  if (NULL == tr_constraint_add_match(cons, name)) {
+    tr_debug("tr_cfg_default_filters: could not add realm name for realm constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  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;
+  }
+  if (NULL == tr_constraint_add_match(cons, name)) {
+    tr_debug("tr_cfg_default_filters: could not add wildcard realm name for realm constraint.");
+    *rc=TR_CFG_NOMEM;
+    goto cleanup;
+  }
+  name=NULL;
+  fline->realm_cons=cons;
+
+  /* put the fline in the filter */
+  if (NULL == tr_filter_add_line(filt, fline)) {
+    tr_debug("tr_cfg_default_filters: could not add line to filter.");
+    *rc = TR_CFG_NOMEM;
+    goto cleanup;
+  }
+
+  /* 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;
+}
index da5f42e..f9fcf61 100644 (file)
 #include <assert.h>
 #include <talloc.h>
 
-#include <trust_router/tr_constraint.h>
 #include <tr_filter.h>
 #include <tid_internal.h>
 #include <tr_debug.h>
+#include <tr_constraint_internal.h>
 
-
+/**
+ * Helper for tr_constraint_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void constraint_destruct_helper(void *item, void *cookie)
+{
+  TR_NAME *name = (TR_NAME *) item;
+  tr_free_name(name);
+}
 static int tr_constraint_destructor(void *obj)
 {
   TR_CONSTRAINT *cons = talloc_get_type_abort(obj, TR_CONSTRAINT);
-  int ii = 0;
 
-  if (cons->type != NULL)
+  if (cons->type)
     tr_free_name(cons->type);
-  for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++) {
-    if (cons->matches[ii] != NULL)
-      tr_free_name(cons->matches[ii]);
-  }
+
+  if (cons->matches)
+    tr_list_foreach(cons->matches, constraint_destruct_helper, NULL);
+
   return 0;
 }
 
 TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx)
 {
   TR_CONSTRAINT *cons = talloc(mem_ctx, TR_CONSTRAINT);
-  int ii = 0;
 
   if (cons != NULL) {
     cons->type = NULL;
-    for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++)
-      cons->matches[ii] = NULL;
+    cons->matches = tr_list_new(cons);
+    if (cons->matches == NULL) {
+      talloc_free(cons);
+      return NULL;
+    }
     talloc_set_destructor((void *) cons, tr_constraint_destructor);
   }
   return cons;
@@ -77,25 +88,63 @@ void tr_constraint_free(TR_CONSTRAINT *cons)
   talloc_free(cons);
 }
 
+/**
+ * Helper for tr_constraint_dup - duplicates a TR_NAME and adds it as a TR_CONSTRAINT match
+ *
+ * No return value. If this succeeds, it will have added a new entry to the TR_CONSTRAINT
+ * match list. Check the length of that - you won't be able to tell whether the allocation
+ * of the duplicate TR_NAME or the addition to the list failed, but either of those is probably
+ * due to a memory allocation failure, in which case the system is probably crashing anyway.
+ *
+ * @param item void pointer to a TR_NAME to add as a match
+ * @param cookie void pointer to a TR_CONSTRAINT to add the match to
+ */
+static void cons_dup_helper(void *item, void *cookie)
+{
+  TR_CONSTRAINT *new_cons = talloc_get_type_abort(cookie, TR_CONSTRAINT);
+  TR_NAME *new_name = tr_dup_name((TR_NAME *) item);
+  if (new_name) {
+    /* check that new_name is added, free if it fails */
+    if (tr_constraint_add_match(new_cons, new_name) == NULL)
+      tr_free_name(new_name);
+  }
+}
+/**
+ * Duplicate a TR_CONSTRAINT
+ *
+ * @param mem_ctx talloc context for the result
+ * @param cons TR_CONSTRAINT to duplicate
+ * @return pointer to the new TR_CONSTRAINT, or NULL on error
+ */
 TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons)
 {
-  TALLOC_CTX *tmp_ctx = NULL;
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
   TR_CONSTRAINT *new = NULL;
-  int ii = 0;
 
   if (cons == NULL)
-    return NULL;
+    goto cleanup;
 
-  tmp_ctx = talloc_new(NULL);
   new = tr_constraint_new(tmp_ctx);
+  if (new == NULL)
+    goto cleanup;
+
+  new->type = tr_dup_name(cons->type);
+  if (new->type == NULL) {
+    new = NULL;
+    goto cleanup;
+  }
 
-  if (new != NULL) {
-    new->type = tr_dup_name(cons->type);
-    for (ii = 0; ii < TR_MAX_CONST_MATCHES; ii++)
-      new->matches[ii] = tr_dup_name(cons->matches[ii]);
-    talloc_steal(mem_ctx, new);
+  tr_list_foreach(cons->matches, cons_dup_helper, new); /* copies matches to new->matches */
+  /* check that we were successful - if we were, then the lists will be the same length */
+  if (tr_list_length(new->matches) != tr_list_length(cons->matches)) {
+    new = NULL;
+    goto cleanup; /* at least one dup or add failed */
   }
 
+  /* success */
+  talloc_steal(mem_ctx, new);
+
+cleanup:
   talloc_free(tmp_ctx);
   return new;
 }
@@ -165,7 +214,8 @@ void tr_constraint_add_to_set(TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
 {
   json_t *jcons = NULL;
   json_t *jmatches = NULL;
-  int i = 0;
+  TR_NAME *this_match = NULL;
+  TR_CONSTRAINT_ITER iter = {0};
 
   if ((!cset) || (!cons))
     return;
@@ -178,8 +228,11 @@ void tr_constraint_add_to_set(TR_CONSTRAINT_SET **cset, TR_CONSTRAINT *cons)
   jmatches = json_array();
   jcons = json_object();
 
-  for (i = 0; ((i < TR_MAX_CONST_MATCHES) && (NULL != cons->matches[i])); i++) {
-    json_array_append_new(jmatches, json_string(cons->matches[i]->buf));
+  for (this_match = tr_constraint_iter_first(&iter, cons);
+       this_match != NULL;
+       this_match = tr_constraint_iter_next(&iter))
+  {
+    json_array_append_new(jmatches, tr_name_to_json_string(this_match));
   }
 
   json_object_set_new(jcons, cons->type->buf, jmatches);
@@ -226,7 +279,6 @@ int tr_constraint_set_validate(TR_CONSTRAINT_SET *cset) {
   return 1;
 }
 
-
 /**
  * Create a new constraint set containing all constraints from #orig
  * with constraint_type #constraint_type and no others.  This constraint set is
@@ -244,7 +296,8 @@ TR_CONSTRAINT_SET *tr_constraint_set_filter(TID_REQ *request,
     tr_debug ("tr_constraint_set_filter: not a valid constraint set\n");
     return NULL;
   }
-  assert (new_cs = json_array());
+  new_cs = json_array();
+  assert(new_cs);
   json_array_foreach(orig_cset, index, set_member) {
     if (json_object_get(set_member, constraint_type))
       json_array_append(new_cs, set_member);
@@ -350,12 +403,14 @@ TR_CONSTRAINT_SET *tr_constraint_set_intersect(TID_REQ *request,
     domain = constraint_intersect_internal(input, "domain");
     realm = constraint_intersect_internal(input, "realm");
   }
-  assert(result = json_object());
+  result = json_object();
+  assert(result);
   if (domain)
     json_object_set_new(result, "domain", domain);
   if (realm)
     json_object_set_new(result, "realm", realm);
-  assert(result_array = json_array());
+  result_array = json_array();
+  assert(result_array);
   json_array_append_new(result_array, result);
   tid_req_cleanup_json(request, result_array);
   return (TR_CONSTRAINT_SET *) result_array;
index 52dffb4..1df9aa4 100644 (file)
@@ -41,6 +41,7 @@
 #include <tr_filter.h>
 #include <trp_internal.h>
 #include <tid_internal.h>
+#include <tr_inet_util.h>
 #include <tr_debug.h>
 
 /* Function types for handling filter fields generally. All target values
@@ -280,15 +281,59 @@ static TR_NAME *tr_ff_get_trp_owner_realm(TR_FILTER_TARGET *target)
   return tr_dup_name(trp_inforec_get_owner_realm(target->trp_inforec));
 }
 
+/** Generic handlers for host:port fields*/
+static TR_NAME *tr_ff_get_hostname_and_port(TR_NAME *hn, int port)
+{
+  return tr_hostname_and_port_to_name(hn, port);
+}
+
+static int tr_ff_cmp_hostname_and_port(TR_NAME *hn, int port, int default_port, TR_NAME *val)
+{
+  int cmp = -1;
+  TR_NAME *n = NULL;
+
+  /* allow a match without :port if the default port is in use */
+  if ((port == default_port) && (tr_name_cmp(hn, val) == 0))
+    return 0;
+
+  /* need to match with the :port */
+  n = tr_ff_get_hostname_and_port(hn, port);
+
+  if (n) {
+    cmp = tr_name_cmp(n, val);
+    tr_free_name(n);
+  }
+  return cmp;
+}
+
 /** Handlers for TRP trust_router field */
 static int tr_ff_cmp_trp_trust_router(TR_FILTER_TARGET *target, TR_NAME *val)
 {
-  return tr_name_cmp(trp_inforec_get_trust_router(target->trp_inforec), val);
+  return tr_ff_cmp_hostname_and_port(trp_inforec_get_trust_router(target->trp_inforec),
+                                     trp_inforec_get_trust_router_port(target->trp_inforec),
+                                     TRP_PORT,
+                                     val);
 }
 
 static TR_NAME *tr_ff_get_trp_trust_router(TR_FILTER_TARGET *target)
 {
-  return tr_dup_name(trp_inforec_get_trust_router(target->trp_inforec));
+  return tr_ff_get_hostname_and_port(trp_inforec_get_trust_router(target->trp_inforec),
+                                     trp_inforec_get_trust_router_port(target->trp_inforec));
+}
+
+/** Handlers for TRP next_hop field */
+static int tr_ff_cmp_trp_next_hop(TR_FILTER_TARGET *target, TR_NAME *val)
+{
+  return tr_ff_cmp_hostname_and_port(trp_inforec_get_next_hop(target->trp_inforec),
+                                     trp_inforec_get_next_hop_port(target->trp_inforec),
+                                     TID_PORT,
+                                     val);
+}
+
+static TR_NAME *tr_ff_get_trp_next_hop(TR_FILTER_TARGET *target)
+{
+  return tr_ff_get_hostname_and_port(trp_inforec_get_next_hop(target->trp_inforec),
+                                     trp_inforec_get_next_hop_port(target->trp_inforec));
 }
 
 /** Handlers for TRP owner_contact field */
@@ -349,6 +394,10 @@ static struct tr_filter_field_entry tr_filter_field_table[] = {
     {TR_FILTER_TYPE_TRP_INBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
     {TR_FILTER_TYPE_TRP_OUTBOUND, "trust_router", tr_ff_cmp_trp_trust_router, tr_ff_get_trp_trust_router},
 
+    /* next_hop */
+    {TR_FILTER_TYPE_TRP_INBOUND, "next_hop", tr_ff_cmp_trp_next_hop, tr_ff_get_trp_next_hop},
+    {TR_FILTER_TYPE_TRP_OUTBOUND, "next_hop", tr_ff_cmp_trp_next_hop, tr_ff_get_trp_next_hop},
+
     /* owner_realm */
     {TR_FILTER_TYPE_TRP_INBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
     {TR_FILTER_TYPE_TRP_OUTBOUND, "owner_realm", tr_ff_cmp_trp_owner_realm, tr_ff_get_trp_owner_realm},
@@ -408,34 +457,34 @@ int tr_filter_apply(TR_FILTER_TARGET *target,
                     TR_CONSTRAINT_SET **constraints,
                     TR_FILTER_ACTION *out_action)
 {
-  unsigned int ii=0, jj=0;
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_FILTER_ITER *filt_iter = tr_filter_iter_new(tmp_ctx);
+  TR_FLINE *this_fline = NULL;
+  TR_FLINE_ITER *fline_iter = tr_fline_iter_new(tmp_ctx);
+  TR_FSPEC *this_fspec = NULL;
   int retval=TR_FILTER_NO_MATCH;
 
   /* Default action is reject */
   *out_action = TR_FILTER_ACTION_REJECT;
 
   /* Validate filter */
-  if ((filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN))
+  if ((filt_iter == NULL) || (fline_iter == NULL) || (filt==NULL) || (filt->type==TR_FILTER_TYPE_UNKNOWN)) {
+    talloc_free(tmp_ctx);
     return TR_FILTER_NO_MATCH;
+  }
 
   /* Step through filter lines looking for a match. If a line matches, retval
    * will be set to TR_FILTER_MATCH, so stop then. */
-  for (ii=0, retval=TR_FILTER_NO_MATCH;
-       ii<TR_MAX_FILTER_LINES;
-       ii++) {
-    /* skip empty lines (these shouldn't really happen) */
-    if (filt->lines[ii]==NULL)
-      continue;
-
+  for (this_fline = tr_filter_iter_first(filt_iter, filt);
+       this_fline != NULL;
+       this_fline = tr_filter_iter_next(filt_iter)) {
     /* Assume we are going to succeed. If any specs fail to match, we'll set
      * this to TR_FILTER_NO_MATCH. */
     retval=TR_FILTER_MATCH;
-    for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
-      /* skip empty specs (these shouldn't really happen either) */
-      if (filt->lines[ii]->specs[jj]==NULL)
-        continue;
-
-      if (!tr_fspec_matches(filt->lines[ii]->specs[jj], filt->type, target)) {
+    for (this_fspec = tr_fline_iter_first(fline_iter, this_fline);
+         this_fspec != NULL;
+         this_fspec = tr_fline_iter_next(fline_iter)) {
+      if (!tr_fspec_matches(this_fspec, filt->type, target)) {
         retval=TR_FILTER_NO_MATCH; /* set this in case this is the last filter line */
         break; /* give up on this filter line */
       }
@@ -443,15 +492,16 @@ int tr_filter_apply(TR_FILTER_TARGET *target,
 
     if (retval==TR_FILTER_MATCH)
       break;
+
   }
 
   if (retval==TR_FILTER_MATCH) {
     /* Matched line ii. Grab its action and constraints. */
-    *out_action = filt->lines[ii]->action;
+    *out_action = this_fline->action;
     if (constraints!=NULL) {
       /* if either constraint is missing, these are no-ops */
-      tr_constraint_add_to_set(constraints, filt->lines[ii]->realm_cons);
-      tr_constraint_add_to_set(constraints, filt->lines[ii]->domain_cons);
+      tr_constraint_add_to_set(constraints, this_fline->realm_cons);
+      tr_constraint_add_to_set(constraints, this_fline->domain_cons);
     }
   }
 
@@ -463,55 +513,65 @@ void tr_fspec_free(TR_FSPEC *fspec)
   talloc_free(fspec);
 }
 
+/**
+ * Helper for tr_fspec_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void fspec_destruct_helper(void *item, void *cookie)
+{
+  TR_NAME *name = (TR_NAME *) item;
+  tr_free_name(name);
+}
 static int tr_fspec_destructor(void *obj)
 {
   TR_FSPEC *fspec = talloc_get_type_abort(obj, TR_FSPEC);
-  size_t ii;
 
   if (fspec->field != NULL)
     tr_free_name(fspec->field);
-  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
-    if (fspec->match[ii] != NULL)
-      tr_free_name(fspec->match[ii]);
-  }
+
+  if (fspec->match)
+    tr_list_foreach(fspec->match, fspec_destruct_helper, NULL);
+
   return 0;
 }
 
 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx)
 {
   TR_FSPEC *fspec = talloc(mem_ctx, TR_FSPEC);
-  size_t ii=0;
 
   if (fspec != NULL) {
     fspec->field = NULL;
-    for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++)
-      fspec->match[ii] = NULL;
-
+    fspec->match = tr_list_new(fspec);
+    if (fspec->match == NULL) {
+      talloc_free(fspec);
+      return NULL;
+    }
     talloc_set_destructor((void *)fspec, tr_fspec_destructor);
   }
   return fspec;
 }
 
-void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match)
+/* Helper function and cookie structure for finding a match. The helper is called
+ * for every item in the match list, even after a match is found. If a match is found,
+ * match should be pointed to the matching item. If this is not NULL, do not change it
+ * because a match has already been found. */
+struct fspec_match_cookie { TR_NAME *name; TR_NAME *match;};
+static void fspec_match_helper(void *item, void *data)
 {
-  size_t ii;
-  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
-    if (fspec->match[ii]==NULL) {
-      fspec->match[ii]=match;
-      break;
-    }
+  TR_NAME *this_name = (TR_NAME *) item;
+  struct fspec_match_cookie *cookie = (struct fspec_match_cookie *) data;
+  if (cookie->match == NULL) {
+    if (tr_name_prefix_wildcard_match(cookie->name, this_name))
+      cookie->match = this_name;
   }
-  /* TODO: handle case that adding the match failed */
 }
-
 /* returns 1 if the spec matches */
 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target)
 {
   struct tr_filter_field_entry *field=NULL;
-  TR_NAME *name=NULL;
-  int retval=0;
-
-  size_t ii=0;
+  struct fspec_match_cookie cookie = {0};
 
   if (fspec==NULL)
     return 0;
@@ -525,32 +585,27 @@ int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *ta
     return 0;
   }
 
-  name=field->get(target);
-  if (name==NULL)
+  cookie.name = field->get(target);
+  if (cookie.name==NULL)
     return 0; /* if there's no value, there's no match */
 
-  for (ii=0; ii<TR_MAX_FILTER_SPEC_MATCHES; ii++) {
-    if (fspec->match[ii]!=NULL) {
-      if (tr_name_prefix_wildcard_match(name, fspec->match[ii])) {
-        retval=1;
-        tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" matches \"%.*s\" for %s filter.",
-                 fspec->field->len, fspec->field->buf,
-                 name->len, name->buf,
-                 fspec->match[ii]->len, fspec->match[ii]->buf,
-                 tr_filter_type_to_string(ftype));
-        break;
-      }
-    }
-  }
-
-  if (!retval) {
+  cookie.match = NULL;
+  tr_list_foreach(fspec->match,
+                  fspec_match_helper,
+                  &cookie);
+  if (cookie.match) {
+    tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" matches \"%.*s\" for %s filter.",
+             fspec->field->len, fspec->field->buf,
+             cookie.name->len, cookie.name->buf,
+             cookie.match->len, cookie.match->buf,
+             tr_filter_type_to_string(ftype));
+  } else {
         tr_debug("tr_fspec_matches: Field %.*s value \"%.*s\" does not match for %s filter.",
                  fspec->field->len, fspec->field->buf,
-                 name->len, name->buf,
+                 cookie.name->len, cookie.name->buf,
                  tr_filter_type_to_string(ftype));
   }
-  tr_free_name(name);
-  return retval;
+  return (cookie.match != NULL);
 }
 
 void tr_fline_free(TR_FLINE *fline)
@@ -561,14 +616,16 @@ void tr_fline_free(TR_FLINE *fline)
 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
 {
   TR_FLINE *fl = talloc(mem_ctx, TR_FLINE);
-  int ii = 0;
 
   if (fl != NULL) {
     fl->action = TR_FILTER_ACTION_UNKNOWN;
     fl->realm_cons = NULL;
     fl->domain_cons = NULL;
-    for (ii = 0; ii < TR_MAX_FILTER_SPECS; ii++)
-      fl->specs[ii] = NULL;
+    fl->specs = tr_list_new(fl);
+    if (fl->specs == NULL) {
+      talloc_free(fl);
+      return NULL;
+    }
   }
   return fl;
 }
@@ -576,12 +633,14 @@ TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx)
 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx)
 {
   TR_FILTER *f = talloc(mem_ctx, TR_FILTER);
-  int ii = 0;
 
   if (f != NULL) {
     f->type = TR_FILTER_TYPE_UNKNOWN;
-    for (ii = 0; ii < TR_MAX_FILTER_LINES; ii++)
-      f->lines[ii] = NULL;
+    f->lines = tr_list_new(f);
+    if (f->lines == NULL) {
+      talloc_free(f);
+      return NULL;
+    }
   }
   return f;
 }
@@ -609,10 +668,16 @@ TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt)
  */
 int tr_filter_validate(TR_FILTER *filt)
 {
-  size_t ii=0, jj=0, kk=0;
-
-  if (!filt)
+  TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+  TR_FILTER_ITER *filt_iter = tr_filter_iter_new(tmp_ctx);
+  TR_FLINE *this_fline = NULL;
+  TR_FLINE_ITER *fline_iter = tr_fline_iter_new(tmp_ctx);
+  TR_FSPEC *this_fspec = NULL;
+  
+  if ((!filt) || (!filt_iter) || (!fline_iter)) {
+    talloc_free(tmp_ctx);
     return 0;
+  }
 
   /* check that we recognize the type */
   switch(filt->type) {
@@ -622,41 +687,43 @@ int tr_filter_validate(TR_FILTER *filt)
       break;
 
     default:
+      talloc_free(tmp_ctx);
       return 0; /* if we get here, either TR_FILTER_TYPE_UNKNOWN or an invalid value was found */
   }
-  for (ii=0; ii<TR_MAX_FILTER_LINES; ii++) {
-    if (filt->lines[ii]==NULL)
-      continue; /* an empty filter line is valid */
-
+  
+  for (this_fline = tr_filter_iter_first(filt_iter, filt);
+       this_fline != NULL;
+       this_fline = tr_filter_iter_next(filt_iter)) {
     /* check that we recognize the action */
-    switch(filt->lines[ii]->action) {
+    switch(this_fline->action) {
       case TR_FILTER_ACTION_ACCEPT:
       case TR_FILTER_ACTION_REJECT:
         break;
 
       default:
         /* if we get here, either TR_FILTER_ACTION_UNKNOWN or an invalid value was found */
+        talloc_free(tmp_ctx);
         return 0;
     }
 
-    for (jj=0; jj<TR_MAX_FILTER_SPECS; jj++) {
-      if (filt->lines[ii]->specs[jj]==NULL)
-        continue; /* an empty filter spec is valid */
-
-      if (!tr_filter_validate_spec_field(filt->type, filt->lines[ii]->specs[jj]))
+    for (this_fspec = tr_fline_iter_first(fline_iter, this_fline);
+         this_fspec != NULL;
+         this_fspec = tr_fline_iter_next(fline_iter)) {
+      if (!tr_filter_validate_spec_field(filt->type, this_fspec)) {
+        talloc_free(tmp_ctx);
         return 0;
-
-      /* check that at least one match is non-null */
-      for (kk=0; kk<TR_MAX_FILTER_SPEC_MATCHES; kk++) {
-        if (filt->lines[ii]->specs[jj]->match[kk]!=NULL)
-          break;
       }
-      if (kk==TR_MAX_FILTER_SPEC_MATCHES)
+
+      /* check that at least one match is defined*/
+      if (tr_list_length(this_fspec->match) == 0) {
+        talloc_free(tmp_ctx);
         return 0;
+      }
     }
   }
 
   /* We ran the gauntlet. Success! */
+  talloc_free(tmp_ctx);
   return 1;
 }
 
diff --git a/common/tr_filter_encoders.c b/common/tr_filter_encoders.c
new file mode 100644 (file)
index 0000000..a69685d
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * 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>
+#include <tr_constraint_internal.h>
+#include <tr_json_util.h>
+
+typedef json_t *(ITEM_ENCODER_FUNC)(void *);
+
+enum array_type {
+  ARRAY_TYPE_FSPEC,
+  ARRAY_TYPE_CONSTRAINT
+};
+/**
+ * Make an array of matches from a TR_FSPEC or TR_CONSTRAINT
+ *
+ * @param obj
+ * @param type
+ * @return
+ */
+static json_t *tr_names_to_json_array(void *obj, enum array_type type)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_FSPEC_ITER fspec_iter = {0};
+  TR_CONSTRAINT_ITER cons_iter = {0};
+  TR_NAME *this_match = NULL;
+
+  if (jarray == NULL)
+    goto cleanup;
+
+  switch(type) {
+    case ARRAY_TYPE_FSPEC:
+      this_match = tr_fspec_iter_first(&fspec_iter, (TR_FSPEC *)obj);
+      break;
+
+    case ARRAY_TYPE_CONSTRAINT:
+      this_match = tr_constraint_iter_first(&cons_iter, (TR_CONSTRAINT *)obj);
+      break;
+  }
+  while(this_match) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_name_to_json_string(this_match));
+    switch(type) {
+      case ARRAY_TYPE_FSPEC:
+        this_match = tr_fspec_iter_next(&fspec_iter);
+        break;
+
+      case ARRAY_TYPE_CONSTRAINT:
+        this_match = tr_constraint_iter_next(&cons_iter);
+        break;
+    }
+  }
+  /* 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",
+                     tr_names_to_json_array(fspec, ARRAY_TYPE_FSPEC));
+
+  /* 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_fspecs_to_json_array(TR_FLINE *fline)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_FLINE_ITER *iter = tr_fline_iter_new(NULL);
+  TR_FSPEC *this_fspec = NULL;
+
+  if ((jarray == NULL) || (iter == NULL))
+    goto cleanup;
+
+  for (this_fspec = tr_fline_iter_first(iter, fline);
+       this_fspec != NULL;
+       this_fspec = tr_fline_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_fspec_to_json(this_fspec));
+  }
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+  if (iter)
+    tr_fline_iter_free(iter);
+
+  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",
+                     tr_fspecs_to_json_array(fline));
+  if (fline->realm_cons) {
+    OBJECT_SET_OR_FAIL(fline_json, "realm_constraints",
+                       tr_names_to_json_array(fline->realm_cons, ARRAY_TYPE_CONSTRAINT));
+  }
+  if (fline->domain_cons) {
+    OBJECT_SET_OR_FAIL(fline_json, "domain_constraints",
+                       tr_names_to_json_array(fline->domain_cons, ARRAY_TYPE_CONSTRAINT));
+  }
+
+  /* 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;
+}
+
+static json_t *tr_flines_to_json_array(TR_FILTER *filt)
+{
+  json_t *jarray = json_array();
+  json_t *retval = NULL;
+  TR_FILTER_ITER *iter = tr_filter_iter_new(NULL);
+  TR_FLINE *this_fline = NULL;
+
+  if ((jarray == NULL) || (iter == NULL))
+    goto cleanup;
+
+  for(this_fline = tr_filter_iter_first(iter, filt);
+      this_fline != NULL;
+      this_fline = tr_filter_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_fline_to_json(this_fline));
+  }
+  /* success */
+  retval = jarray;
+  json_incref(retval);
+
+cleanup:
+  if (jarray)
+    json_decref(jarray);
+  if (iter)
+    tr_filter_iter_free(iter);
+
+  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),
+                         tr_flines_to_json_array(filt));
+    }
+  }
+
+  /* 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..251c29e 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;
+  int err = 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;
+  /* 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
+ */
+TR_GSS_RC 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_GSS_RC rc = TR_GSS_ERROR;
+
+  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 */
+  rc = req_cb(tmp_ctx, req_msg, &resp_msg, req_cookie);
+  if (rc != TR_GSS_SUCCESS)
+    goto cleanup;
+
+  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;
+  }
+
+  /* we successfully sent a response */
+  rc = TR_GSS_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
 }
diff --git a/common/tr_gss_client.c b/common/tr_gss_client.c
new file mode 100644 (file)
index 0000000..f45af3a
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * 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>
+
+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->conn = -1;
+    gssc->gss_ctx = talloc(gssc, gss_ctx_id_t);
+    if (gssc->gss_ctx == NULL) {
+      talloc_free(gssc);
+      return NULL;
+    }
+  }
+  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, int port)
+{
+  if ((port <= 0) || (port > 65535)) {
+    tr_err("tr_gssc_open_connection: invalid port requested (%d)", port);
+    return -1;
+  }
+
+  tr_debug("tr_gssc_open_connection: opening connection to %s:%d", server, port);
+  if (0 != gsscon_connect(server, (unsigned int) 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;
+}
diff --git a/common/tr_gss_names.c b/common/tr_gss_names.c
new file mode 100644 (file)
index 0000000..416ba8c
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * 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 <glib.h>
+
+#include <tr_gss_names.h>
+#include <tr_debug.h>
+
+/**
+ * Helper for tr_gss_names_destructor - calls tr_free_name on its first argument
+ *
+ * @param item void pointer to a TR_NAME
+ * @param cookie ignored
+ */
+static void gss_names_destruct_helper(void *item, void *cookie)
+{
+  TR_NAME *name = (TR_NAME *) item;
+  tr_free_name(name);
+}
+static int tr_gss_names_destructor(void *obj)
+{
+  TR_GSS_NAMES *gss_names=talloc_get_type_abort(obj, TR_GSS_NAMES);
+  if (gss_names->names)
+    tr_list_foreach(gss_names->names, gss_names_destruct_helper, NULL);
+  return 0;
+}
+TR_GSS_NAMES *tr_gss_names_new(TALLOC_CTX *mem_ctx)
+{
+  TR_GSS_NAMES *gn = talloc(mem_ctx, TR_GSS_NAMES);
+  if (gn != NULL) {
+    gn->names = tr_list_new(gn);
+    if (gn->names == NULL) {
+      talloc_free(gn);
+      return 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)
+{
+  return (NULL == tr_list_add(gn->names, new, 0)); /* nonzero if the add failed */
+}
+
+/**
+ * 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;
+  }
+  for (this = tr_gss_names_iter_first(iter, orig);
+       this != NULL;
+       this = tr_gss_names_iter_next(iter)) {
+    if (tr_gss_names_add(new, tr_dup_name(this)) != 0) {
+      talloc_free(tmp_ctx);
+      return NULL;
+    }
+  }
+  /* success */
+  talloc_steal(mem_ctx, new);
+  return new;
+}
+
+int tr_gss_names_matches(TR_GSS_NAMES *gn, TR_NAME *name)
+{
+  TR_GSS_NAMES_ITER iter={0};
+  TR_NAME *this = NULL;
+
+  if ((!gn) || (!name))
+    return 0;
+
+  for (this = tr_gss_names_iter_first(&iter, gn);
+      this != NULL;
+      this = tr_gss_names_iter_next(&iter)) {
+    if (tr_name_cmp(name, this) == 0)
+      return 1;
+  }
+  return 0;
+}
+
+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..a3d84a7 100644 (file)
 #include <talloc.h>
 #include <time.h>
 
+#include <tr_aaa_server.h>
 #include <tr_name_internal.h>
 #include <tr_idp.h>
 #include <tr_config.h>
 #include <tr_debug.h>
 
-static int tr_aaa_server_destructor(void *obj)
-{
-  TR_AAA_SERVER *aaa=talloc_get_type_abort(obj, TR_AAA_SERVER);
-  if (aaa->hostname!=NULL)
-    tr_free_name(aaa->hostname);
-  return 0;
-}
-
-TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname)
-{
-  TR_AAA_SERVER *aaa=talloc(mem_ctx, TR_AAA_SERVER);
-  if (aaa!=NULL) {
-    aaa->next=NULL;
-    aaa->hostname=hostname;
-    talloc_set_destructor((void *)aaa, tr_aaa_server_destructor);
-  }
-  return aaa;
-}
-
-void tr_aaa_server_free(TR_AAA_SERVER *aaa)
-{
-  talloc_free(aaa);
-}
-
-TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx)
-{
-  return talloc(mem_ctx, TR_AAA_SERVER_ITER);
-}
-
-void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter)
-{
-  talloc_free(iter);
-}
-
-TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa)
-{
-  iter->this=aaa;
-  return iter->this;
-}
-
-TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter)
-{
-  if (iter->this!=NULL) {
-    iter->this=iter->this->next;
-  }
-  return iter->this;
-}
-
-
 /* fills in shared if pointer not null */
 TR_AAA_SERVER *tr_idp_aaa_server_lookup(TR_IDP_REALM *idp_realms, TR_NAME *idp_realm_name, TR_NAME *comm, int *shared_out)
 {
@@ -259,7 +211,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 +222,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 +233,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..4b3d845
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * 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>
+#include <tr_json_util.h>
+
+static char *tr_aaa_server_to_str(TALLOC_CTX *mem_ctx, TR_AAA_SERVER *aaa)
+{
+  char *aaa_hostname = tr_name_strdup( tr_aaa_server_get_hostname(aaa) );
+  char *result = NULL;
+
+  if (aaa_hostname == NULL)
+    return NULL;
+
+  result = talloc_asprintf(mem_ctx,
+                           "%s:%d",
+                           aaa_hostname,
+                           tr_aaa_server_get_port(aaa));
+  free(aaa_hostname);
+  return result;
+}
+
+
+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;
+}
+
+
+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;
+
+  for (apc = tr_apc_iter_first(iter, apcs);
+       apc != NULL;
+       apc = tr_apc_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_name_to_json_string(tr_apc_get_id(apc)));
+  }
+
+  /* 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 *s = tr_aaa_server_to_str(NULL, aaa);
+  json_t *jstr = NULL;
+
+  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;
+
+  for (aaa = tr_aaa_server_iter_first(iter, aaas);
+       aaa != NULL;
+       aaa = tr_aaa_server_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_aaa_server_to_json(aaa));
+  }
+
+  /* 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
diff --git a/common/tr_inet_util.c b/common/tr_inet_util.c
new file mode 100644 (file)
index 0000000..28f52fe
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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 <tr_name_internal.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+
+#include <tr_inet_util.h>
+#include <errno.h>
+
+/**
+ * Determine whether a string is a valid address of a given family
+ *
+ * @param s string to check
+ * @param af address family (probably AF_INET or AF_INET6)
+ * @return 1 if string is a valid address in the given family, 0 if not, -1 on error (errno set)
+ */
+static int is_valid_address(int af, const char *s)
+{
+  unsigned char buf[sizeof(struct in6_addr)];
+
+  if (s == NULL)
+    return 0;
+  return inet_pton(af, s, buf);
+}
+
+/**
+ * Determine whether a string is a valid IPv6 address reference
+ *
+ * I.e., an IPv6 address in brackets
+ *
+ * @param s string to validate
+ * @return 1 if a valid reference, 0 otherwise
+ */
+static int tr_valid_ipv6_reference(const char *s)
+{
+  char *cpy;
+  size_t len;
+  int valid_ipv6;
+
+  /* check that it starts with an open bracket */
+  if (*s != '[')
+    return 0;
+
+  /* check that it ends with a close bracket */
+  len = strlen(s);
+  if (*(s+len-1) != ']')
+    return 0;
+
+  /* make a null-terminated copy of the string omitting the brackets */
+  cpy = talloc_strndup(NULL, s+1, len-2);
+  if (cpy == NULL)
+    return 0; /* an error occurred - fail safe */
+
+  valid_ipv6 = is_valid_address(AF_INET6, cpy);
+  talloc_free(cpy);
+
+  return valid_ipv6;
+}
+
+/**
+ * Validate a host string
+ *
+ * The intention is to reject strings that may appear to contain a ':port' spec.
+ * Takes a permissive view of valid: a hostname is valid if either it is a
+ * bracketed IPv6 address reference ([address]) or has no brackets or colons.
+ * This accepts all valid DNS names and IPv4 addresses, as well as many invalid
+ * hostnames. This is ok for accepting a hostname that will later be resolved
+ * because invalid names will fail to resolve. It should *not* be used to ensure
+ * a hostname is compliant with RFC!
+ *
+ * Ignores a trailing colon followed by decimal digits.
+ *
+ * @param s string to validate
+ * @return 1 if a valid host specification, 0 otherwise
+ */
+static int tr_valid_host(const char *s)
+{
+  if (strchr(s, '[') || strchr(s, ']') || strchr(s, ':'))
+    return tr_valid_ipv6_reference(s);
+
+  return 1;
+}
+
+/**
+ * Check that all characters are decimal digits
+ *
+ * @param s
+ * @return 1 if all digits, 0 otherwise
+ */
+static int tr_str_all_digits(const char *s)
+{
+  if (s == NULL)
+    return 0;
+
+  for( ; *s; s++) {
+    if ( (*s < '0') || (*s > '9'))
+      return 0;
+  }
+
+  return 1;
+}
+
+/**
+ * Validate and parse a hostname or hostname/port
+ *
+ * If port_out is not null, accepts a port as well. This is
+ * stored in *port_out. If no port is given, a 0 is stored.
+ * If an invalid port is given, -1 is stored.
+ *
+ * If the hostname is invalid, null is returned and no value
+ * is written to *port_out.
+ *
+ * If port_out is null, null will be returned if the string
+ * contains a port.
+ *
+ * The return value must be freed with talloc_free unless
+ * it is null.
+ *
+ * @param mem_ctx talloc context for hostname result
+ * @param s string to parse
+ * @param port_out pointer to an allocated integer, or NULL
+ * @return pointer to the hostname or null on error
+ */
+char *tr_parse_host(TALLOC_CTX *mem_ctx, const char *s, int *port_out)
+{
+  const char *colon;
+  char *hostname;
+  int port;
+
+  if (s == NULL)
+    return NULL;
+
+  /* If we are accepting a port, find the last colon. */
+  if (port_out == NULL)
+    colon = NULL;
+  else
+    colon = strrchr(s, ':');
+
+  /* Get a copy of the hostname portion, which may be the entire string. */
+  if (colon == NULL)
+    hostname = talloc_strdup(NULL, s);
+  else
+    hostname = talloc_strndup(NULL, s, colon-s);
+
+  if (hostname == NULL)
+    return NULL; /* failed to dup the hostname */
+
+  /* Check that the hostname is valid; if not, return null and ignore the port. */
+  if (! tr_valid_host(hostname)) {
+    talloc_free(hostname);
+    return NULL;
+  }
+
+  /* If we are accepting a port, parse and validate it. */
+  if (port_out != NULL) {
+    if (colon == NULL) {
+      *port_out = 0;
+    } else {
+      port = tr_parse_port(colon+1);
+      if ((port > 0) && tr_str_all_digits(colon+1))
+        *port_out = port;
+      else
+        *port_out = -1;
+    }
+  }
+
+  return hostname;
+}
+
+TR_NAME *tr_hostname_and_port_to_name(TR_NAME *hn, int port)
+{
+  TR_NAME *retval = NULL;
+  char *s = NULL;
+  char *hn_s = tr_name_strdup(hn);
+
+  if (!hn_s)
+    return NULL;
+
+  s = talloc_asprintf(NULL, "%s:%d", hn_s, port);
+  free(hn_s);
+
+  if (s) {
+    retval = tr_new_name(s);
+    talloc_free(s);
+  }
+
+  return retval;
+}
+
+/**
+ * Parse a string containing a port
+ *
+ * Returns the port number, which is always in the range 1-65535.
+ * On error, returns < 0. The absolute value is an error code from errno.h
+ *
+ * @param s
+ * @return port number or < 0 on error
+ */
+int tr_parse_port(const char *s)
+{
+  long port;
+  char *end;
+
+  errno = 0; /* strtol sets this, make sure it's zero to avoid false positives */
+  port = strtol(s, &end, 10);
+  if (errno) {
+    return -errno;
+  }
+
+  if (*end != '\0') {
+    return -EINVAL;
+  }
+
+  if ((port <= 0) || (port > 65535)) {
+    return -ERANGE;
+  }
+
+  return (int) port;
+}
\ No newline at end of file
diff --git a/common/tr_list.c b/common/tr_list.c
new file mode 100644 (file)
index 0000000..e3269f4
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * 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_list.h>
+
+static int tr_list_destructor(void *object)
+{
+  TR_LIST *list = talloc_get_type_abort(object, TR_LIST);
+  if (*list)
+    g_ptr_array_unref(*list);
+  return 0;
+}
+
+/* Note that the TR_LIST type is a pointer-to-pointer to
+ * a GPtrArray. This is done so that we can hook a talloc destructor
+ * to the list to ensure that the GLib reference count is handled correctly
+ * when used with talloc. */
+TR_LIST *tr_list_new(TALLOC_CTX *mem_ctx)
+{
+  TR_LIST *list = talloc(mem_ctx, TR_LIST);
+  if (list) {
+    *list = g_ptr_array_new();
+    if (*list == NULL) {
+      talloc_free(list);
+      return NULL;
+    }
+    talloc_set_destructor((void *)list, tr_list_destructor);
+  }
+  return list;
+}
+
+void tr_list_free(TR_LIST *list)
+{
+  talloc_free(list);
+}
+
+/**
+ * Add an item to the list
+ *
+ * If steal != 0, performs a talloc_steal() to put the new item in the
+ * list's context. If steal == 0, does not do this - in that case, you'll
+ * need to be sure that the memory is cleaned up through some other means.
+ * (This allows the list to represent non-talloc'ed items.)
+ *
+ * @param list list to add an item to
+ * @param item pointer to the item to add
+ * @param steal if non-zero, the item will be added to the list's context with talloc_steal()
+ * @return pointer to the added item or null if there was an error
+ */
+void *tr_list_add(TR_LIST *list, void *item, int steal)
+{
+  guint old_len = (*list)->len;
+  g_ptr_array_add((*list), item);
+
+  if ((*list)->len == old_len)
+    return NULL; /* failed to add */
+
+  if (steal)
+    talloc_steal(list, item);
+
+  return item;
+}
+
+/**
+ * Call func(item, cookie) on each item in the list.
+ *
+ * @param list list to iterate over
+ * @param func function, takes two void pointer arguments, first is the item, second the cookie
+ * @param cookie
+ */
+void tr_list_foreach(TR_LIST *list, TR_LIST_FOREACH_FUNC *func, void *cookie)
+{
+  g_ptr_array_foreach((*list), func, cookie);
+}
+
+TR_LIST_ITER *tr_list_iter_new(TALLOC_CTX *mem_ctx)
+{
+  TR_LIST_ITER *iter = talloc(mem_ctx, TR_LIST_ITER);
+  if (iter)
+    iter->list = NULL;
+  return iter;
+}
+
+void tr_list_iter_free(TR_LIST_ITER *iter)
+{
+  talloc_free(iter);
+}
+
+void *tr_list_iter_first(TR_LIST_ITER *iter, TR_LIST *list)
+{
+  if (!iter || !list || (!(*list)))
+    return NULL;
+
+  iter->list = list;
+  iter->index = 0;
+  return tr_list_iter_next(iter);
+}
+
+void *tr_list_iter_next(TR_LIST_ITER *iter)
+{
+  if (!iter)
+    return NULL;
+
+  if (iter->index < (*(iter->list))->len)
+    return g_ptr_array_index(*(iter->list), iter->index++);
+  return NULL;
+}
index 07080b6..6676880 100644 (file)
@@ -49,12 +49,11 @@ static int tr_mq_msg_destructor(void *object)
   return 0;
 }
 
-TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *message, TR_MQ_PRIORITY prio)
+TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *message)
 {
   TR_MQ_MSG *msg=talloc(mem_ctx, TR_MQ_MSG);
   if (msg!=NULL) {
     msg->next=NULL;
-    msg->prio=prio;
     msg->message=talloc_strdup(msg, message);
     if (msg->message==NULL) {
       talloc_free(msg);
@@ -72,11 +71,6 @@ void tr_mq_msg_free(TR_MQ_MSG *msg)
     talloc_free(msg);
 }
 
-TR_MQ_PRIORITY tr_mq_msg_get_prio(TR_MQ_MSG *msg)
-{
-  return msg->prio;
-}
-
 const char *tr_mq_msg_get_message(TR_MQ_MSG *msg)
 {
   return msg->message;
@@ -121,7 +115,6 @@ TR_MQ *tr_mq_new(TALLOC_CTX *mem_ctx)
 
     mq->head=NULL;
     mq->tail=NULL;
-    mq->last_hi_prio=NULL;
 
     mq->notify_cb=NULL;
     mq->notify_cb_arg=NULL;
@@ -208,22 +201,6 @@ static void tr_mq_append(TR_MQ *mq, TR_MQ_MSG *msg)
   talloc_steal(mq, msg);
 }
 
-static void tr_mq_append_high_prio(TR_MQ *mq, TR_MQ_MSG *new)
-{
-  if (tr_mq_get_head(mq)==NULL) {
-    tr_mq_set_head(mq, new);
-    tr_mq_set_tail(mq, new);
-  } else if (mq->last_hi_prio==NULL) {
-    tr_mq_msg_set_next(new, tr_mq_get_head(mq)); /* add to front of list */
-    tr_mq_set_head(mq, new); /* update head of list */
-  } else {
-    tr_mq_msg_set_next(new, tr_mq_msg_get_next(mq->last_hi_prio));
-    tr_mq_msg_set_next(mq->last_hi_prio, new); /* add to end of hi prio msgs */
-  }
-  mq->last_hi_prio=new; /* in any case, this is now the last high priority msg */
-  talloc_steal(mq,new);
-}
-
 #define DEBUG_TR_MQ 0
 #if DEBUG_TR_MQ
 static void tr_mq_print(TR_MQ *mq)
@@ -234,8 +211,8 @@ static void tr_mq_print(TR_MQ *mq)
   tr_debug("tr_mq_print: mq contents:");
   while(m!=NULL) {
     ii++;
-    tr_debug("tr_mq_print: Entry %02d: %-15s (prio %d)",
-             ii, tr_mq_msg_get_message(m), tr_mq_msg_get_prio(m));
+    tr_debug("tr_mq_print: Entry %02d: %-15s",
+             ii, tr_mq_msg_get_message(m));
     m=tr_mq_msg_get_next(m);
   }
 }
@@ -249,14 +226,8 @@ void tr_mq_add(TR_MQ *mq, TR_MQ_MSG *msg)
   tr_mq_lock(mq);
   
   was_empty=tr_mq_empty(mq);
-  switch (tr_mq_msg_get_prio(msg)) {
-  case TR_MQ_PRIO_HIGH:
-    tr_mq_append_high_prio(mq, msg);
-    break;
-  default:
-    tr_mq_append(mq, msg);
-    break;
-  }
+  tr_mq_append(mq, msg);
+
   /* before releasing the mutex, get notify_cb data out of mq */
   notify_cb=mq->notify_cb;
   notify_cb_arg=mq->notify_cb_arg;
@@ -289,13 +260,17 @@ int tr_mq_pop_timeout(time_t seconds, struct timespec *ts)
   return 0;
 }
 
-/* Caller must free msg via tr_mq_msg_free, waiting until absolute
+/* Retrieves a message from the queue, waiting until absolute
  * time ts_abort before giving up (using CLOCK_MONOTONIC). If ts_abort
  * has passed, returns an existing message but will not wait if one is
  * not already available. If ts_abort is null, no blocking.  Not
  * guaranteed to wait if an error occurs - immediately returns without
  * a message. Use tr_mq_pop_timeout() to get an absolute time that
- * is guaranteed compatible with this function. */
+ * is guaranteed compatible with this function.
+ *
+ * Caller should free msg via tr_mq_msg_free when done with it. It stays
+ * in the TR_MQ's context, though, so use talloc_steal() if you want to do
+ * something clever with it. */
 TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort)
 {
   TR_MQ_MSG *popped=NULL;
@@ -320,9 +295,6 @@ TR_MQ_MSG *tr_mq_pop(TR_MQ *mq, struct timespec *ts_abort)
     popped=tr_mq_get_head(mq);
     tr_mq_set_head(mq, tr_mq_msg_get_next(popped)); /* popped is the old head */
 
-    if (popped==mq->last_hi_prio)
-      mq->last_hi_prio=NULL;
-
     if (tr_mq_get_head(mq)==NULL)
       tr_mq_set_tail(mq, NULL); /* just popped the last element */
   }
index 59a2efd..886b535 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_util.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>
+#include <tr_inet_util.h>
 
 /* JSON helpers */
-/* Read attribute attr from msg as an integer. Returns nonzero on error. */
-static int tr_msg_get_json_integer(json_t *jmsg, const char *attr, int *dest)
+/* Read attribute attr from msg as an integer. */
+static TRP_RC tr_msg_get_json_integer(json_t *jmsg, const char *attr, int *dest)
 {
   json_t *obj;
 
   obj=json_object_get(jmsg, attr);
   if (obj == NULL) {
-    return -1;
+    return TRP_MISSING;
   }
   /* check type */
   if (!json_is_integer(obj)) {
-    return -1;
+    return TRP_BADTYPE;
   }
 
   (*dest)=json_integer_value(obj);
-  return 0;
+  return TRP_SUCCESS;
 }
 
 /* Read attribute attr from msg as a string. Copies string into mem_ctx context so jmsg can
@@ -78,15 +80,15 @@ static TRP_RC tr_msg_get_json_string(json_t *jmsg, const char *attr, char **dest
 
   obj=json_object_get(jmsg, attr);
   if (obj == NULL)
-    return TRP_ERROR;
+    return TRP_MISSING;
 
   /* check type */
   if (!json_is_string(obj))
-    return TRP_ERROR;
+    return TRP_BADTYPE;
 
   *dest=talloc_strdup(mem_ctx, json_string_value(obj));
   if (*dest==NULL)
-    return TRP_ERROR;
+    return TRP_NOMEM;
 
   return TRP_SUCCESS;
 }
@@ -101,6 +103,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 +121,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 +146,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 +221,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 +246,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 +320,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);
@@ -226,12 +331,17 @@ static json_t * tr_msg_encode_tidreq(TID_REQ *req)
 
   jstr = tr_name_to_json_string(req->comm);
   json_object_set_new(jreq, "community", jstr);
-  
+
   if (req->orig_coi) {
     jstr = tr_name_to_json_string(req->orig_coi);
     json_object_set_new(jreq, "orig_coi", jstr);
   }
 
+  if (tid_req_get_request_id(req)) {
+    jstr = tr_name_to_json_string(tid_req_get_request_id(req));
+    json_object_set_new(jreq, "request_id", jstr);
+  }
+
   json_object_set_new(jreq, "dh_info", tr_msg_encode_dh(req->tidc_dh));
 
   if (req->cons)
@@ -246,13 +356,14 @@ 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;
   json_t *jrealm = NULL;
   json_t *jcomm = NULL;
   json_t *jorig_coi = NULL;
+  json_t *jrequest_id = NULL;
   json_t *jdh = NULL;
   json_t *jpath = NULL;
   json_t *jexpire_interval = NULL;
@@ -261,7 +372,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"))) ||
@@ -274,9 +386,9 @@ static TID_REQ *tr_msg_decode_tidreq(json_t *jreq)
   jpath = json_object_get(jreq, "path");
   jexpire_interval = json_object_get(jreq, "expiration_interval");
 
-  treq->rp_realm = tr_new_name((char *)json_string_value(jrp_realm));
-  treq->realm = tr_new_name((char *)json_string_value(jrealm));
-  treq->comm = tr_new_name((char *)json_string_value(jcomm));
+  treq->rp_realm = tr_new_name(json_string_value(jrp_realm));
+  treq->realm = tr_new_name(json_string_value(jrealm));
+  treq->comm = tr_new_name(json_string_value(jcomm));
 
   /* Get DH Info from the request */
   if (NULL == (jdh = json_object_get(jreq, "dh_info"))) {
@@ -288,7 +400,12 @@ static TID_REQ *tr_msg_decode_tidreq(json_t *jreq)
 
   /* store optional "orig_coi" field */
   if (NULL != (jorig_coi = json_object_get(jreq, "orig_coi"))) {
-    treq->orig_coi = tr_new_name((char *)json_string_value(jorig_coi));
+    treq->orig_coi = tr_new_name(json_string_value(jorig_coi));
+  }
+
+  /* store optional "request_id" field */
+  if (NULL != (jrequest_id = json_object_get(jreq, "request_id"))) {
+    tid_req_set_request_id(treq, tr_new_name(json_string_value(jrequest_id)));
   }
 
   treq->cons = (TR_CONSTRAINT_SET *) json_object_get(jreq, "constraints");
@@ -469,6 +586,11 @@ static json_t * tr_msg_encode_tidresp(TID_RESP *resp)
     json_object_set_new(jresp, "orig_coi", jstr);
   }
 
+  if (tid_resp_get_request_id(resp)) {
+    jstr = tr_name_to_json_string(tid_resp_get_request_id(resp));
+    json_object_set_new(jresp, "request_id", jstr);
+  }
+
   if (NULL == resp->servers) {
     tr_debug("tr_msg_encode_tidresp(): No servers to encode.");
   }
@@ -483,7 +605,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;
@@ -491,14 +613,14 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
   json_t *jrealm = NULL;
   json_t *jcomm = NULL;
   json_t *jorig_coi = NULL;
+  json_t *jrequest_id = NULL;
   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"))) ||
@@ -545,21 +667,46 @@ static TID_RESP *tr_msg_decode_tidresp(json_t *jresp)
 
   /* store optional "orig_coi" field */
   if ((NULL != (jorig_coi = json_object_get(jresp, "orig_coi"))) &&
-      (!json_is_object(jorig_coi))) {
+      json_is_string(jorig_coi)) {
     tresp->orig_coi = tr_new_name(json_string_value(jorig_coi));
   }
-     
+
+  /* store optional "request_id" field */
+  if ((NULL != (jrequest_id = json_object_get(jresp, "request_id"))) &&
+      json_is_string(jrequest_id)) {
+    tid_resp_set_request_id(tresp, tr_new_name(json_string_value(jrequest_id)));
+  }
+
   return tresp;
 }
 
+static json_t *hostname_and_port_to_json(TR_NAME *hostname, int port)
+{
+  char *s_hostname = tr_name_strdup(hostname);
+  char *s;
+  json_t *j;
+
+  if (s_hostname == NULL)
+    return NULL;
+
+  s = talloc_asprintf(NULL, "%s:%d", s_hostname, port);
+  free(s_hostname);
+
+  if (s == NULL)
+    return NULL;
 
-/* Information records for TRP update msg 
+  j = json_string(s);
+  talloc_free(s);
+
+  return j;
+}
+
+/* Information records for TRP update msg
  * requires that jrec already be allocated */
 static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec)
 {
   json_t *jstr=NULL;
   json_t *jint=NULL;
-  char *s=NULL;
 
   if (rec==NULL)
     return TRP_BADTYPE;
@@ -567,15 +714,18 @@ static TRP_RC tr_msg_encode_inforec_route(json_t *jrec, TRP_INFOREC *rec)
   if (trp_inforec_get_trust_router(rec)==NULL)
     return TRP_ERROR;
 
-  s=tr_name_strdup(trp_inforec_get_trust_router(rec));
-  if (s==NULL)
-    return TRP_NOMEM;
-  jstr=json_string(s);
-  free(s);s=NULL;
+  jstr=hostname_and_port_to_json(trp_inforec_get_trust_router(rec),
+                                 trp_inforec_get_trust_router_port(rec));
   if(jstr==NULL)
-    return TRP_ERROR;
+    return TRP_NOMEM;
   json_object_set_new(jrec, "trust_router", jstr);
 
+  jstr=hostname_and_port_to_json(trp_inforec_get_next_hop(rec),
+                                 trp_inforec_get_next_hop_port(rec));
+  if(jstr==NULL)
+    return TRP_NOMEM;
+  json_object_set_new(jrec, "next_hop", jstr);
+
   jint=json_integer(trp_inforec_get_metric(rec));
   if(jint==NULL)
     return TRP_ERROR;
@@ -766,18 +916,71 @@ static TRP_RC tr_msg_decode_trp_inforec_route(json_t *jrecord, TRP_INFOREC *rec)
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TRP_RC rc=TRP_ERROR;
   char *s=NULL;
+  TR_NAME *name;
+  char *hostname;
+  int port;
   int num=0;
 
+  /* get the trust router */
   rc=tr_msg_get_json_string(jrecord, "trust_router", &s, tmp_ctx);
   if (rc != TRP_SUCCESS)
     goto cleanup;
-  if (TRP_SUCCESS!=trp_inforec_set_trust_router(rec, tr_new_name(s))) {
+
+  hostname = tr_parse_host(tmp_ctx, s, &port);
+  if ((NULL == hostname)
+      || (NULL == (name = tr_new_name(hostname)))
+      || (port < 0)) {
+    rc = TRP_ERROR;
+    goto cleanup;
+  }
+  talloc_free(s); s=NULL;
+  talloc_free(hostname);
+
+  if (port == 0)
+    port = TRP_PORT;
+
+  if (TRP_SUCCESS!= trp_inforec_set_trust_router(rec, name, port)) {
     rc=TRP_ERROR;
     goto cleanup;
   }
+
+  /* Now do the next hop. If it's not present, use the trust_router for backward
+   * compatibility */
+  switch(tr_msg_get_json_string(jrecord, "next_hop", &s, tmp_ctx)) {
+    case TRP_SUCCESS:
+      /* we got a next_hop field */
+      hostname = tr_parse_host(tmp_ctx, s, &port);
+      if ((hostname == NULL)
+          || (NULL == (name = tr_new_name(hostname)))
+          || (port < 0)) {
+        rc = TRP_ERROR;
+        goto cleanup;
+      }
+      break;
+
+    case TRP_MISSING:
+      /* no next_hop field; use the trust router */
+      name = tr_dup_name(trp_inforec_get_trust_router(rec));
+      if (name == NULL) {
+        rc = TRP_ERROR;
+        goto cleanup;
+      }
+      break;
+
+    default:
+      /* something went wrong */
+      rc = TRP_ERROR;
+      goto cleanup;
+  }
   talloc_free(s); s=NULL;
 
-  trp_inforec_set_next_hop(rec, NULL); /* make sure this is null (filled in later) */
+  if (port == 0)
+    port = TID_PORT;
+
+  if (TRP_SUCCESS!= trp_inforec_set_next_hop(rec, name, port)) {
+    rc=TRP_ERROR;
+    goto cleanup;
+  }
 
   rc=tr_msg_get_json_integer(jrecord, "metric", &num);
   if ((rc != TRP_SUCCESS) || (TRP_SUCCESS!=trp_inforec_set_metric(rec,num)))
@@ -1144,21 +1347,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);
@@ -1187,18 +1392,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;
@@ -1212,14 +1436,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.");
@@ -1232,19 +1454,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;
@@ -1259,29 +1490,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 84f0c1f..e1f81b4 100644 (file)
@@ -52,9 +52,11 @@ TR_NAME *tr_new_name (const char *name)
 {
   TR_NAME *new;
 
-  if (new = malloc(sizeof(TR_NAME))) {
-    new->len = strlen(name);
-    if (new->buf = malloc((new->len)+1)) {
+  new = malloc(sizeof(TR_NAME));
+  if (new) {
+    new->len = (int) strlen(name);
+    new->buf = malloc(1 + (size_t) new->len);
+    if (new->buf) {
       strcpy(new->buf, name);
     } else {
       free(new);
@@ -64,7 +66,7 @@ TR_NAME *tr_new_name (const char *name)
   return new;
 }
 
-TR_NAME *tr_dup_name (TR_NAME *from)
+TR_NAME *tr_dup_name (const TR_NAME *from)
 {
   TR_NAME *to;
 
@@ -74,15 +76,15 @@ TR_NAME *tr_dup_name (TR_NAME *from)
 
   if (NULL != (to = malloc(sizeof(TR_NAME)))) {
     to->len = from->len;
-    if (NULL != (to->buf = malloc(to->len+1))) {
-      strncpy(to->buf, from->buf, from->len);
+    if (NULL != (to->buf = malloc(1 + (size_t) to->len))) {
+      strncpy(to->buf, from->buf, (size_t) from->len);
       to->buf[to->len] = 0;    /* NULL terminate for debugging printf()s */
     }
   }
   return to;
 }
 
-int tr_name_cmp(TR_NAME *one, TR_NAME *two)
+int tr_name_cmp(const TR_NAME *one, const TR_NAME *two)
 {
   int len=one->len;
   int cmp=0;
@@ -90,7 +92,7 @@ int tr_name_cmp(TR_NAME *one, TR_NAME *two)
   if (two->len<one->len)
     len=two->len; /* len now min(one->len,two->len) */
 
-  cmp=strncmp(one->buf, two->buf, len);
+  cmp=strncmp(one->buf, two->buf, (size_t) len);
   if (cmp==0) {
     if (one->len<two->len)
       return -1;
@@ -109,7 +111,7 @@ int tr_name_cmp(TR_NAME *one, TR_NAME *two)
  * @param two_str Ordinary C null-terminated string
  * @return 0 on match, <0 if one precedes two, >0 if two precedes one
  */
-int tr_name_cmp_str(TR_NAME *one, const char *two_str)
+int tr_name_cmp_str(const TR_NAME *one, const char *two_str)
 {
   TR_NAME *two=tr_new_name(two_str);
   int cmp=tr_name_cmp(one, two);
@@ -126,7 +128,7 @@ int tr_name_cmp_str(TR_NAME *one, const char *two_str)
  * @return 1 if the the string (str) matches the wildcard string (wc_str), 0 if not.
  *
  */
-int tr_name_prefix_wildcard_match(TR_NAME *str, TR_NAME *wc_str)
+int tr_name_prefix_wildcard_match(const TR_NAME *str, const TR_NAME *wc_str)
 {
   const char *wc_post=NULL;
   size_t wc_len = 0;
@@ -134,7 +136,8 @@ int tr_name_prefix_wildcard_match(TR_NAME *str, TR_NAME *wc_str)
   if ((!str) || (!wc_str))
     return 0;
 
-  if (0 == (wc_len = wc_str->len))
+  wc_len = (size_t) wc_str->len;
+  if (wc_len == 0)
     return 0;
 
   if ('*' == wc_str->buf[0]) {
@@ -145,7 +148,7 @@ int tr_name_prefix_wildcard_match(TR_NAME *str, TR_NAME *wc_str)
     /* No wildcard, but the strings are the same length so may match.
      * Compare the full strings. */
     wc_post=wc_str->buf;
-    wc_len=wc_str->len;
+    wc_len = (size_t) wc_str->len;
   } else {
     /* No wildcard and strings are different length, so no match */
     return 0;
@@ -165,24 +168,25 @@ void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len)
   size_t used_len;
   if (src->len >= len)
     used_len = len-1;
-  else used_len = src->len;
+  else
+    used_len = (size_t) src->len;
   if (used_len > 0)
     strncat(dest, src->buf, used_len);
   else dest[0] = '\0';
 }
 
 
-char * tr_name_strdup(TR_NAME *src)
+char * tr_name_strdup(const TR_NAME *src)
 {
-  char *s = calloc(src->len+1, 1);
+  char *s = calloc(1 + (size_t) src->len, 1);
   if (s) {
-    memcpy(s, src->buf, src->len);
+    memcpy(s, src->buf, (size_t) src->len);
     s[src->len] = '\0';
   }
   return s;
 }
 
-json_t *tr_name_to_json_string(TR_NAME *src)
+json_t *tr_name_to_json_string(const TR_NAME *src)
 {
   char *s=tr_name_strdup(src);
   json_t *js=json_string(s);
@@ -191,16 +195,16 @@ json_t *tr_name_to_json_string(TR_NAME *src)
   return js;
 }
 
-TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2)
+TR_NAME *tr_name_cat(const TR_NAME *n1, const TR_NAME *n2)
 {
-  char *s=malloc(n1->len+n2->len+1);
+  char *s=malloc((size_t) n1->len + (size_t) n2->len + 1);
   TR_NAME *name=NULL;
 
   if (s==NULL)
     return NULL;
   *s=0;
-  strncat(s, n1->buf, n1->len);
-  strncat(s, n2->buf, n2->len);
+  strncat(s, n1->buf, (size_t) n1->len);
+  strncat(s, n2->buf, (size_t) n2->len);
   name=tr_new_name(s);
   free(s);
   return name;
diff --git a/common/tr_rand_id.c b/common/tr_rand_id.c
new file mode 100644 (file)
index 0000000..a2a0a10
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <openssl/rand.h>
+#include <talloc.h>
+
+#include <tr_rand_id.h>
+
+static char *bytes_to_hex(TALLOC_CTX *mem_ctx, const unsigned char *bytes, size_t len)
+{
+  char *hex = talloc_size(mem_ctx, 1 + len * 2 * sizeof(char));
+  char *p = NULL;
+
+  if (hex) {
+    p = hex;
+    while(len--) {
+      p += sprintf(p, "%02x", *(bytes++));
+    }
+  }
+
+  return hex;
+}
+
+/**
+ * Generate n random bytes of data
+ *
+ * @param dst destination buffer, at least n bytes long
+ * @param n number of bytes to generate
+ * @return -1 on error
+ */
+static int random_bytes(unsigned char *dst, size_t n)
+{
+  return RAND_pseudo_bytes(dst, n);
+}
+
+#define ID_LENGTH 15
+/**
+ * Generate a random ID
+ *
+ * @param mem_ctx talloc context for the result
+ * @return random string of hex characters or null if it is unable to generate them
+ */
+char *tr_random_id(TALLOC_CTX *mem_ctx)
+{
+  unsigned char bytes[ID_LENGTH];
+  char *hex = NULL;
+
+  if (random_bytes(bytes, ID_LENGTH) >= 0)
+    hex = bytes_to_hex(mem_ctx, bytes, ID_LENGTH);
+
+  return hex;
+}
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..020962a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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>
+#include <tr_json_util.h>
+
+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;
+
+  for (rp_client = tr_rp_client_iter_first(iter, rp_clients);
+       rp_client != NULL;
+       rp_client = tr_rp_client_iter_next(iter)) {
+    ARRAY_APPEND_OR_FAIL(jarray, tr_rp_client_to_json(rp_client));
+  }
+
+  /* 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..2a4d34e
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * 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>
+#include <errno.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(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;
+}
+
+/**
+ * Extract a string-formatted socket address from a struct sockaddr
+ *
+ * @param s
+ * @param dst pointer to allocated space of at least INET6_ADDRSLEN bytes
+ * @param dst_len size of space allocated at dst
+ * @return pointer to dst or null on error
+ */
+static const char *tr_sock_ip_address(struct sockaddr *s, char *dst, size_t dst_len)
+{
+  switch (s->sa_family) {
+    case AF_INET:
+      inet_ntop(AF_INET,
+                &(((struct sockaddr_in *)s)->sin_addr),
+                dst,
+                (socklen_t) dst_len);
+      break;
+
+    case AF_INET6:
+      inet_ntop(AF_INET6,
+                &(((struct sockaddr_in6 *)s)->sin6_addr),
+                dst,
+                (socklen_t) dst_len);
+      break;
+
+    default:
+      snprintf(dst, dst_len, "addr family %u", s->sa_family);
+      break;
+  }
+
+  return dst;
+}
+
+/**
+ * Accept a socket connection
+ *
+ * @param sock
+ * @return -1 on error, connection fd on success
+ */
+int tr_sock_accept(int sock)
+{
+  int conn = -1;
+  struct sockaddr_storage peeraddr;
+  socklen_t addr_len = sizeof(peeraddr);
+  char peeraddr_string[INET6_ADDRSTRLEN];
+  char err[80];
+
+  if (0 > (conn = accept(sock, (struct sockaddr *)&(peeraddr), &addr_len))) {
+    if (strerror_r(errno, err, sizeof(err)))
+      snprintf(err, sizeof(err), "errno = %d", errno);
+    tr_debug("tr_sock_accept: Unable to accept connection: %s", err);
+  } else {
+    tr_info("tr_sock_accept: Incoming connection on fd %d from %s",
+              conn,
+              tr_sock_ip_address((struct sockaddr *)&peeraddr,
+                                 peeraddr_string,
+                                 sizeof(peeraddr_string)));
+  }
+  return conn;
+}
index 2ce3c82..c3d03fb 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)
@@ -51,9 +51,16 @@ void tr_bin_to_hex(const unsigned char * bin, size_t bin_len,
   }
 }
 
-/* Returns 0 if ts1==ts2, <0 if ts1<ts2, >= if ts1>ts2.
- * Assumes that tv_nsec <= 1e9. */
-int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
+/**
+ * Compare two timespecs
+ *
+ * Assumes tv_nsec <= 1e9
+ *
+ * @param ts1
+ * @param ts2
+ * @return 0 if ts1==ts2, -1 if ts1<ts2, 1 if ts1>ts2.
+ */
+int tr_cmp_timespec(const struct timespec *ts1, const struct timespec *ts2)
 {
   if (ts1->tv_sec > ts2->tv_sec)
     return 1;
@@ -71,3 +78,135 @@ int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2)
 
   return 0;
 }
+
+/**
+ * Compute ts1 + ts2
+ *
+ * @param ts1
+ * @param ts2
+ * @param sum ts1 + ts2
+ * @return 0 on success, nonzero on error
+ */
+int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum)
+{
+  const time_t ONE_BILLION = 1000000000;
+
+  if (!ts1 || !ts2 || !sum)
+    return -1;
+
+  /* would be nice to do range checking, but I don't know a portable way to get the
+   * max value of a time_t. Figure that nsec <= 1e9 and seconds are unlikely to go off
+   * too close to infinity, so overflow is unlikely */
+  sum->tv_nsec = ts1->tv_nsec + ts2->tv_nsec;
+  sum->tv_sec = ts1->tv_sec + ts2->tv_sec;
+
+  /* make sure that we have no more than a second worth of nsec */
+  while (sum->tv_nsec >= ONE_BILLION) {
+    sum->tv_nsec -= ONE_BILLION;
+    sum->tv_sec += 1;
+  }
+
+  return 0;
+}
+
+/**
+ * Compute ts1 - ts2
+ *
+ * Allows negative results, which will be represented as a negative tv_sec.
+ * The returned tv_nsec is always positive and less than 1e9.
+ *
+ * (The value represented is tv_sec + tv_nsec/1e9 seconds - this is a little
+ * counterintuitive when tv_sec is negative. E.g., -1.5 seconds is represented
+ * as {-2, 500000000). Negative time_t values are not guaranteed to be supported
+ * anyway, so it's probably best to stay away from them.)
+ *
+ * @param ts1
+ * @param ts2
+ * @param diff ts1 - ts2
+ * @return 0 on success, nonzero on error
+ */
+int tr_sub_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *diff)
+{
+  const time_t ONE_BILLION = 1000000000;
+  struct timespec ts1_copy = {0};
+  struct timespec ts2_copy = {0};
+  
+  if (!ts1 || !ts2 || !diff)
+    return -1;
+
+  ts1_copy = *ts1;
+  ts2_copy = *ts2;
+  
+  while (ts2_copy.tv_nsec > ts1_copy.tv_nsec) {
+    /* Reduce ts2 by one second worth of nsec, balanced by removing a second
+     * from ts1. Repeat until ts2->tv_nsec <= ts1->tv_nsec. */
+    ts2_copy.tv_nsec -= ONE_BILLION;
+    ts1_copy.tv_sec -= 1;
+  }
+
+  diff->tv_nsec = ts1_copy.tv_nsec - ts2_copy.tv_nsec; /* >= 0 */
+  diff->tv_sec = ts1_copy.tv_sec - ts2_copy.tv_sec; /* sign indeterminate */
+
+  /* make sure we have no more than 1 sec worth of nsec */
+  while (diff->tv_nsec > ONE_BILLION) {
+    diff->tv_nsec -= ONE_BILLION;
+    diff->tv_sec += 1;
+  }
+
+  return 0;
+}
+
+/**
+ * Convert a struct timespec to a string representation
+ * @param ts
+ * @return
+ */
+char *timespec_to_str(const struct timespec *ts)
+{
+  struct tm tm;
+  char *s=NULL;
+
+  if (gmtime_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 UTC", &tm)==0) {
+    free(s);
+    return NULL;
+  }
+  return s;
+}
+
+/**
+ * Convert a time from one clock to another
+ *
+ * Because this involves reading each clock, it is not exact.
+ *
+ * @param from clock to convert from
+ * @param when time to convert, measured on the 'from' clock
+ * @param to clock to convert to
+ * @param dst destination, measured on the 'to' clock
+ * @return dst or null on error
+ */
+struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when,
+                                  clockid_t to, struct timespec *dst)
+{
+  struct timespec now_from = {0};
+  struct timespec diff = {0}; /* difference between when and now_from */
+  struct timespec now_to = {0};
+
+  if ((clock_gettime(from, &now_from) != 0)
+      || (clock_gettime(to, &now_to) != 0)) {
+    return NULL;
+  }
+  if (tr_sub_timespec(when, &now_from, &diff) != 0) {
+    return NULL;
+  }
+  if (tr_add_timespec(&now_to, &diff, dst) != 0) {
+    return NULL;
+  }
+  return dst;
+}
index 47c962b..b3d9b27 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.63)
-AC_INIT([trust_router],[3.3.0],
+AC_INIT([trust_router],[3.4.0~3],
 [bugs@project-moonshot.org])
 AC_CONFIG_MACRO_DIR(m4)
 AC_CONFIG_AUX_DIR(build-aux)
@@ -20,15 +20,15 @@ AC_PROG_CC
            AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
 PKG_CHECK_MODULES([GLIB], [glib-2.0])
 
-AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context])
+AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context],,[AC_MSG_ERROR([Please install libkrb5 development])])
 AC_CHECK_LIB([com_err], [error_message])
 AC_CHECK_LIB(talloc, talloc_init,,
 [AC_MSG_ERROR([Please install talloc development])])
 AC_CHECK_LIB([sqlite3], [sqlite3_open],,
     [AC_MSG_ERROR([Please install sqlite3 development])])
-AC_CHECK_LIB([jansson], [json_object])
+AC_CHECK_LIB([jansson], [json_object],,[AC_MSG_ERROR([Please install libjansson development])])
 AC_CHECK_LIB([crypto], [DH_new])
-AC_CHECK_LIB([event], [event_base_new])
+AC_CHECK_LIB([event], [event_base_new],,[AC_MSG_ERROR([Please install libevent development])])
 AC_CHECK_HEADERS(gssapi.h gssapi_ext.h jansson.h talloc.h openssl/dh.h openssl/bn.h syslog.h event2/event.h)
 AC_CONFIG_FILES([Makefile gsscon/Makefile])
 AC_OUTPUT
index 1227c2b..e730b55 100755 (executable)
@@ -73,7 +73,6 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
   struct sockaddr_in saddr;
   char *port=NULL;
   gss_name_t serviceName = NULL;
-  gss_name_t clientName = NULL;
   gss_cred_id_t clientCredentials = GSS_C_NO_CREDENTIAL;
   gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
   OM_uint32 actualFlags = 0;
@@ -83,6 +82,7 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
   gss_buffer_desc nameBuffer;
   gss_buffer_t inputTokenPtr = GSS_C_NO_BUFFER;
   char *name;
+  int len = 0;
 
   if (!inServiceName) { err = EINVAL; }
   if (!outGSSContext) { err = EINVAL; }
@@ -128,7 +128,7 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
   if (fd >= 0) { close (fd); }
 
   if (!err) {
-    majorStatus = gss_acquire_cred (&minorStatus, clientName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, 
+    majorStatus = gss_acquire_cred (&minorStatus, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
                                     GSS_C_INITIATE, &clientCredentials, NULL, NULL); 
     if (majorStatus != GSS_S_COMPLETE) { 
       gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus);
@@ -185,16 +185,27 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
    */
     
   if (!err) {
-    nameBuffer.length = asprintf(&name, "%s@%s", inServiceName, inHost);
-    nameBuffer.value = name;
+    len = asprintf(&name, "%s@%s", inServiceName, inHost);
+    if (len < 0) {
+      /* asprintf failed, pick an error to return... */
+      err = GSS_S_BAD_NAME;
+    } else {
+      nameBuffer.length = (size_t) len;
+      nameBuffer.value = name;
 
-    majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); 
-    if (majorStatus != GSS_S_COMPLETE) { 
-      gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
-      err = minorStatus ? minorStatus : majorStatus; 
+      majorStatus = gss_import_name (&minorStatus, &nameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName);
+      if (majorStatus != GSS_S_COMPLETE) {
+        gsscon_print_gss_errors ("gss_import_name(inServiceName)", majorStatus, minorStatus);
+        err = minorStatus ? minorStatus : majorStatus;
+      }
+
+      /* free the input name and null pointers to avoid reuse */
+      free(name);
+      name = NULL;
+      nameBuffer.value = NULL;
     }
   }
-    
+
   /* 
    * The main authentication loop:
    *
@@ -271,7 +282,6 @@ int gsscon_connect (const char *inHost, unsigned int inPort, const char *inServi
 
   if (inputTokenBuffer) { free (inputTokenBuffer); }
   if (serviceName     ) { gss_release_name (&minorStatus, &serviceName); }
-  if (clientName      ) { gss_release_name (&minorStatus, &clientName); }
   if (ai_head         ) { freeaddrinfo(ai_head); }
 
   if (clientCredentials != GSS_C_NO_CREDENTIAL) { 
index b8d4fcf..9d6ad3c 100755 (executable)
@@ -53,6 +53,8 @@
  */
 
 #include <gsscon.h>
+#include <fcntl.h>
+#include <poll.h>
 
 /* --------------------------------------------------------------------------- */
 /* Display the contents of the buffer in hex and ascii                         */
@@ -86,6 +88,7 @@ static void PrintBuffer (const char *inBuffer,
 /* --------------------------------------------------------------------------- */
 /* Standard network read loop, accounting for EINTR, EOF and incomplete reads  */
 
+#define READBUFFER_TIMEOUT_SECONDS (60 * 1000)
 static int ReadBuffer (int     inSocket, 
                        size_t  inBufferLength, 
                        char   *ioBuffer)
@@ -94,13 +97,37 @@ static int ReadBuffer (int     inSocket,
     ssize_t bytesRead = 0;
     
     if (!ioBuffer) { err = EINVAL; }
-    
+
+    /* Read in non-blocking mode */
+    if (!err) {
+        err = fcntl(inSocket, F_SETFL, O_NONBLOCK);
+    }
+
     if (!err) {
         char *ptr = ioBuffer;
         do {
-            ssize_t count = read (inSocket, ptr, inBufferLength - bytesRead);
+            ssize_t count;
+            struct pollfd fds = {inSocket, POLLIN, 0}; /* poll for data ready on the socket */
+            int poll_rc = 0;
+
+            poll_rc = poll(&fds, 1, READBUFFER_TIMEOUT_SECONDS);
+            if (poll_rc == 0) {
+                /* timed out */
+                err = ETIMEDOUT;
+                continue;
+            } else if (poll_rc < 0) {
+                /* try again if we were interrupted, otherwise exit */
+                if (errno != EINTR) {
+                    err = errno;
+                }
+                continue;
+            }
+
+            /* Data should be ready to read */
+            count = read (inSocket, ptr, inBufferLength - bytesRead);
             if (count < 0) {
-                /* Try again on EINTR */
+                /* Try again on EINTR (if we get EAGAIN or EWOULDBLOCK, something is wrong because
+                 * we just polled the fd) */
                 if (errno != EINTR) { err = errno; }
             } else if (count == 0) {
                 err = ECONNRESET; /* EOF and we expected data */
@@ -109,8 +136,8 @@ static int ReadBuffer (int     inSocket,
                 bytesRead += count;
             }
         } while (!err && (bytesRead < inBufferLength));
-    } 
-    
+    }
+
     if (err) { gsscon_print_error (err, "ReadBuffer failed"); }
 
     return err;
index 37430ee..c2815d2 100644 (file)
@@ -68,6 +68,7 @@ int main (int argc, const char *argv[])
     int err = 0;
     int fd = -1;
     int port = kDefaultPort;
+    long tmp;
     const char *server = "127.0.0.1";
     const char *clientName = NULL;
     const char *serviceName = "host";
@@ -77,8 +78,14 @@ int main (int argc, const char *argv[])
         
     for (i = 1; (i < argc) && !err; i++) {
         if ((strcmp (argv[i], "--port") == 0) && (i < (argc - 1))) {
-            port = strtol (argv[++i], NULL, 0);
-            if (port == 0) { err = errno; }
+            errno = 0; /* ensure this starts off as 0 */
+            tmp = strtol (argv[++i], NULL, 0);
+            if (errno)
+                err = errno;
+            else if ((tmp <= 0) || (tmp > 65535))
+                err = ERANGE;
+            else
+                port = (int) tmp;
         } else if ((strcmp (argv[i], "--server") == 0) && (i < (argc - 1))) {
             server = argv[++i];
         } else if ((strcmp(argv[i], "--cprinc") == 0) && (i < (argc - 1))) {
@@ -93,7 +100,7 @@ int main (int argc, const char *argv[])
     if (!err) {
         printf ("%s: Starting up...\n", argv[0]);
         
-        err = gsscon_connect (server, port, serviceName, &fd, &gssContext);
+        err = gsscon_connect (server, (unsigned int) port, serviceName, &fd, &gssContext);
     }
     
     if (!err) {
index be56372..3ca69c4 100644 (file)
@@ -129,6 +129,7 @@ int main (int argc, const char *argv[])
     int err = 0;
     OM_uint32 minorStatus;
     int port = kDefaultPort;
+    long tmp;
     int listenFD = -1;
     gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT;
     int i = 0;
@@ -136,8 +137,14 @@ int main (int argc, const char *argv[])
 
     for (i = 1; (i < argc) && !err; i++) {
         if ((strcmp (argv[i], "--port") == 0) && (i < (argc - 1))) {
-            port = strtol (argv[++i], NULL, 0);
-            if (port == 0) { err = errno; }
+            errno = 0; /* ensure this starts off at 0 */
+            tmp = strtol (argv[++i], NULL, 0);
+            if (errno)
+                err = errno;
+            else if ((tmp <= 0) || (tmp > 65535))
+                err = ERANGE;
+            else
+                port = (int) tmp;
         } else if ((strcmp(argv[i], "--service") == 0) && (i < (argc - 1))) {
             gServiceName = argv[++i];
         } else {
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..1e6dcdd
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * 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 <glib.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 <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_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_REQS_PROCESSED,
+  OPT_TYPE_SHOW_TID_REQS_FAILED,
+  OPT_TYPE_SHOW_TID_ERROR_COUNT,
+  OPT_TYPE_SHOW_TID_REQS_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;
+  int mon_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 */
+/* mon_common.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,
+                      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);
+int monc_open_connection(MONC_INSTANCE *monc, const char *server, 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 f02c4d1..e742c4f 100644 (file)
 #ifndef TID_INTERNAL_H
 #define TID_INTERNAL_H
 #include <glib.h>
+#include <jansson.h>
 
-#include <tr_rp.h>
 #include <trust_router/tid.h>
-#include <jansson.h>
+#include <trust_router/tr_dh.h>
+#include <tr_rp.h>
+#include <tr_gss_client.h>
 
 struct tid_srvr_blk {
   TID_SRVR_BLK *next;
@@ -51,6 +53,7 @@ struct tid_srvr_blk {
 
 struct tid_resp {
   TID_RC result;
+  TR_NAME *request_id;
   TR_NAME *err_msg;
   TR_NAME *rp_realm;
   TR_NAME *realm;
@@ -66,6 +69,7 @@ struct tid_req {
   int resp_sent;
   int conn;
   int free_conn; /* free conn and gss ctx*/
+  TR_NAME *request_id;
   gss_ctx_id_t gssctx;
   int resp_rcvd;
   TR_NAME *rp_realm;
@@ -82,23 +86,28 @@ 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;
+  DH *client_dh;
+};
+
+struct tid_process {
+  pid_t pid;
+  int read_fd;
 };
 
 struct tids_instance {
-  int req_count;
+  int req_count; /* successful requests */
+  int req_error_count; /* unsuccessful requests */
+  int error_count; /* invalid requests or internal errors */
   char *priv_key;
   char *ipaddr;
   const char *hostname;
   TIDS_REQ_FUNC *req_handler;
   tids_auth_func *auth_handler;
   void *cookie;
-  uint16_t tids_port;
+  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
@@ -106,7 +115,7 @@ struct tids_instance {
     reference they already hold to the TID_REQ.*/
 void tid_req_cleanup_json(TID_REQ *, json_t *json);
 
-int tid_req_add_path(TID_REQ *, const char *this_system, unsigned port);
+int tid_req_add_path(TID_REQ *req, const char *this_system, int port);
 
 TID_SRVR_BLK *tid_srvr_blk_new(TALLOC_CTX *mem_ctx);
 void tid_srvr_blk_free(TID_SRVR_BLK *srvr);
@@ -115,7 +124,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
diff --git a/include/tr_aaa_server.h b/include/tr_aaa_server.h
new file mode 100644 (file)
index 0000000..fedc819
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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_AAA_SERVER_H
+#define TRUST_ROUTER_TR_AAA_SERVER_H
+
+#include <talloc.h>
+
+#include <tr_name_internal.h>
+
+typedef struct tr_aaa_server {
+  struct tr_aaa_server *next;
+  TR_NAME *hostname;
+  int port;
+} TR_AAA_SERVER;
+
+typedef struct tr_aaa_server_iter {
+  TR_AAA_SERVER *this;
+} TR_AAA_SERVER_ITER;
+
+TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx);
+void tr_aaa_server_free(TR_AAA_SERVER *aaa);
+
+TR_NAME *tr_aaa_server_get_hostname(TR_AAA_SERVER *aaa);
+void tr_aaa_server_set_hostname(TR_AAA_SERVER *aaa, TR_NAME *hostname);
+int tr_aaa_server_get_port(TR_AAA_SERVER *aaa);
+void tr_aaa_server_set_port(TR_AAA_SERVER *aaa, int port);
+TR_AAA_SERVER *tr_aaa_server_from_string(TALLOC_CTX *mem_ctx, const char *s);
+
+TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx);
+void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter);
+TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa);
+TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter);
+
+#endif //TRUST_ROUTER_TR_AAA_SERVER_H
index 5a770b8..461d75b 100644 (file)
@@ -113,7 +113,7 @@ void tr_comm_table_free(TR_COMM_TABLE *ctab);
 TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx);
 void tr_comm_table_free(TR_COMM_TABLE *ctab);
 void tr_comm_table_sweep(TR_COMM_TABLE *ctab);
-void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new);
+int tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new);
 void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm);
 TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id);
 void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new);
@@ -155,6 +155,7 @@ void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval);
 unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb);
 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time);
 struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb);
+struct timespec *tr_comm_memb_get_expiry_realtime(TR_COMM_MEMB *memb, struct timespec *result);
 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime);
 void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig);
 int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb);
@@ -226,4 +227,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..51b98ef 100644 (file)
 #include <syslog.h>
 #include <sys/time.h>
 #include <talloc.h>
+#include <glib.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
 #define TR_DEFAULT_TID_RESP_NUMER 2
 #define TR_DEFAULT_TID_RESP_DENOM 3
 
+/* limits on values for validations */
+#define TR_MIN_TRP_CONNECT_INTERVAL 5
+#define TR_MIN_TRP_SWEEP_INTERVAL 5
+#define TR_MIN_TRP_UPDATE_INTERVAL 5
+#define TR_MIN_CFG_POLL_INTERVAL 1
+#define TR_MIN_CFG_SETTLING_TIME 0
+#define TR_MIN_TID_REQ_TIMEOUT 1
+
+#define TR_CFG_INVALID_SERIAL -1
+
 typedef enum tr_cfg_rc {
   TR_CFG_SUCCESS = 0,  /* No error */
   TR_CFG_ERROR,                /* General processing error */
@@ -71,8 +84,9 @@ typedef enum tr_cfg_rc {
 
 typedef struct tr_cfg_internal {
   unsigned int max_tree_depth;
-  unsigned int tids_port;
-  unsigned int trps_port;
+  int tids_port;
+  int trps_port;
+  int mons_port;
   const char *hostname;
   int log_threshold;
   int console_threshold;
@@ -84,15 +98,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 +137,33 @@ 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_CFG_RC tr_cfg_validate_internal(TR_CFG_INTERNAL *int_cfg);
+
+/* 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
diff --git a/include/tr_constraint_internal.h b/include/tr_constraint_internal.h
new file mode 100644 (file)
index 0000000..2182781
--- /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_TR_CONSTRAINT_INTERNAL_H
+#define TRUST_ROUTER_TR_CONSTRAINT_INTERNAL_H
+
+#include <talloc.h>
+
+#include <tr_list.h>
+#include <tr_name_internal.h>
+#include <trust_router/tr_constraint.h>
+
+
+struct tr_constraint {
+  TR_NAME *type;
+  TR_LIST *matches;
+};
+
+TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx);
+void tr_constraint_free(TR_CONSTRAINT *cons);
+TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons);
+
+/* Iterator for TR_CONS matches */
+typedef TR_LIST_ITER TR_CONSTRAINT_ITER;
+#define tr_constraint_iter_new(CTX) (tr_list_iter_new(CTX))
+#define tr_constraint_iter_free(ITER) (tr_list_iter_free(ITER))
+#define tr_constraint_iter_first(ITER, CONS) ((TR_NAME *) tr_list_iter_first((ITER), (CONS)->matches))
+#define tr_constraint_iter_next(ITER) ((TR_NAME *) tr_list_iter_next(ITER))
+#define tr_constraint_add_match(CONS, MATCH) ((TR_NAME *) tr_list_add((CONS)->matches, (MATCH), 0))
+
+#endif //TRUST_ROUTER_TR_CONSTRAINT_INTERNAL_H
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..d241a50 100644 (file)
 
 #include <talloc.h>
 #include <jansson.h>
+#include <glib.h>
 
+#include <tr_list.h>
 #include <tr_name_internal.h>
 #include <trust_router/tr_constraint.h>
 #include <trust_router/tid.h>
 #include <trust_router/trp.h>
 
-#define TR_MAX_FILTERS  5
-#define TR_MAX_FILTER_LINES 8
-#define TR_MAX_FILTER_SPECS 8
-#define TR_MAX_FILTER_SPEC_MATCHES 64
-
 /* Filter actions */
 typedef enum tr_filter_action {
   TR_FILTER_ACTION_REJECT = 0,
@@ -69,22 +66,21 @@ typedef enum {
 
 typedef struct tr_fspec {
   TR_NAME *field;
-  TR_NAME *match[TR_MAX_FILTER_SPEC_MATCHES];
+  TR_LIST *match;
 } TR_FSPEC;
 
 typedef struct tr_fline {
   TR_FILTER_ACTION action;
-  TR_FSPEC *specs[TR_MAX_FILTER_SPECS];
+  TR_LIST *specs;
   TR_CONSTRAINT *realm_cons;
   TR_CONSTRAINT *domain_cons;
 } TR_FLINE;
 
 typedef struct tr_filter {
   TR_FILTER_TYPE type;
-  TR_FLINE *lines[TR_MAX_FILTER_LINES];
+  TR_LIST *lines;
 } TR_FILTER;
 
-
 typedef struct tr_filter_set TR_FILTER_SET;
 struct tr_filter_set {
   TR_FILTER *this;
@@ -109,25 +105,45 @@ int tr_filter_set_add(TR_FILTER_SET *set, TR_FILTER *new);
 TR_FILTER *tr_filter_set_get(TR_FILTER_SET *set, TR_FILTER_TYPE type);
 
 TR_FILTER *tr_filter_new(TALLOC_CTX *mem_ctx);
-
 void tr_filter_free(TR_FILTER *filt);
 
 void tr_filter_set_type(TR_FILTER *filt, TR_FILTER_TYPE type);
-
 TR_FILTER_TYPE tr_filter_get_type(TR_FILTER *filt);
+TR_FLINE *tr_filter_add_line(TR_FILTER *filt, TR_FLINE *line);
 
 TR_FLINE *tr_fline_new(TALLOC_CTX *mem_ctx);
-
 void tr_fline_free(TR_FLINE *fline);
+TR_FSPEC *tr_fline_add_spec(TR_FLINE *fline, TR_FSPEC *spec);
 
 TR_FSPEC *tr_fspec_new(TALLOC_CTX *mem_ctx);
-
 void tr_fspec_free(TR_FSPEC *fspec);
-
-void tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match);
+TR_NAME *tr_fspec_add_match(TR_FSPEC *fspec, TR_NAME *match);
 
 int tr_fspec_matches(TR_FSPEC *fspec, TR_FILTER_TYPE ftype, TR_FILTER_TARGET *target);
 
+/* Iterator for TR_FILTER lines */
+typedef TR_LIST_ITER TR_FILTER_ITER;
+#define tr_filter_iter_new(CTX) (tr_list_iter_new(CTX))
+#define tr_filter_iter_free(ITER) (tr_list_iter_free(ITER))
+#define tr_filter_iter_first(ITER, FILT) ((TR_FLINE *) tr_list_iter_first((ITER), (FILT)->lines))
+#define tr_filter_iter_next(ITER) ((TR_FLINE *) tr_list_iter_next(ITER))
+#define tr_filter_add_line(FILT, LINE) ((TR_FLINE *) tr_list_add((FILT)->lines, (LINE), 1))
+
+/* Iterator for TR_FSPEC matches */
+typedef TR_LIST_ITER TR_FSPEC_ITER;
+#define tr_fspec_iter_new(CTX) (tr_list_iter_new(CTX))
+#define tr_fspec_iter_free(ITER) (tr_list_iter_free(ITER))
+#define tr_fspec_iter_first(ITER, SPEC) (tr_list_iter_first((ITER), (SPEC)->match))
+#define tr_fspec_iter_next(ITER) (tr_list_iter_next(ITER))
+#define tr_fspec_add_match(SPEC, MATCH) ((TR_NAME *) tr_list_add((SPEC)->match, (MATCH), 0))
+
+/* Iterator for TR_FLINE specs */
+typedef TR_LIST_ITER TR_FLINE_ITER;
+#define tr_fline_iter_new(CTX) (tr_list_iter_new(CTX))
+#define tr_fline_iter_free(ITER) (tr_list_iter_free(ITER))
+#define tr_fline_iter_first(ITER, LINE) (tr_list_iter_first((ITER), (LINE)->specs))
+#define tr_fline_iter_next(ITER) (tr_list_iter_next(ITER))
+#define tr_fline_add_spec(LINE, SPEC) ((TR_NAME *) tr_list_add((LINE)->specs, (SPEC), 1))
 
 /*In tr_constraint.c and exported, but not really a public symbol; needed by tr_filter.c and by tr_constraint.c*/
 int TR_EXPORT tr_prefix_wildcard_match(const char *str, const char *wc_str);
@@ -144,4 +160,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..f7fcd41 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 enum tr_gss_rc (TR_GSS_HANDLE_REQ_FN)(TALLOC_CTX *, TR_MSG *, TR_MSG **, void *);
 
-typedef struct tr_gss_names {
-  TR_NAME *names[TR_MAX_GSS_NAMES];
-} TR_GSS_NAMES;
+typedef enum tr_gss_rc {
+  TR_GSS_SUCCESS = 0, /* success */
+  TR_GSS_AUTH_FAILED, /* authorization failed */
+  TR_GSS_REQUEST_FAILED, /* request failed */
+  TR_GSS_INTERNAL_ERROR, /* internal error (memory allocation, etc) */
+  TR_GSS_ERROR,       /* unspecified error */
+} TR_GSS_RC;
 
-typedef struct tr_gss_names_iter {
-  TR_GSS_NAMES *gn;
-  int ii; /* which entry did we last output? */
-} TR_GSS_NAMES_ITER;
+TR_GSS_RC 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 *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..2b24200
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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 <tr_msg.h>
+
+typedef struct tr_gssc_instance TR_GSSC_INSTANCE;
+
+/* Client instance */
+struct tr_gssc_instance {
+  const char *service_name;
+  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, int port);
+TR_MSG *tr_gssc_exchange_msgs(TALLOC_CTX *mem_ctx, TR_GSSC_INSTANCE *gssc, TR_MSG *req_msg);
+
+#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..c6192be
--- /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_list.h>
+
+#include <tr_name_internal.h>
+
+typedef struct tr_gss_names {
+  TR_LIST *names;
+} TR_GSS_NAMES;
+
+typedef TR_LIST_ITER TR_GSS_NAMES_ITER;
+
+/* Iterator for TR_FILTER lines */
+#define tr_gss_names_iter_new(CTX) (tr_list_iter_new(CTX))
+#define tr_gss_names_iter_free(ITER) (tr_list_iter_free(ITER))
+#define tr_gss_names_iter_first(ITER, GSSN) ((TR_NAME *) tr_list_iter_first((ITER), (GSSN)->names))
+#define tr_gss_names_iter_next(ITER) ((TR_NAME *) tr_list_iter_next(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);
+#define tr_gss_names_length(GSSN) (tr_list_length((GSSN)->names))
+#define tr_gss_names_index(GSSN, INDEX) (tr_list_index((GSSN)->names, (INDEX)))
+
+json_t *tr_gss_names_to_json_array(TR_GSS_NAMES *gss_names);
+
+#endif /* __TR_GSS_H__ */
index 951f7d7..8b56c37 100644 (file)
 #include <time.h>
 
 #include <tr_name_internal.h>
+#include <tr_aaa_server.h>
 #include <tr_apc.h>
 
-typedef struct tr_aaa_server {
-  struct tr_aaa_server *next;
-  TR_NAME *hostname;
-} TR_AAA_SERVER;
-
-typedef struct tr_aaa_server_iter {
-  TR_AAA_SERVER *this;
-} TR_AAA_SERVER_ITER;
-
 /* may also want to use in tr_rp.h */
 typedef enum tr_realm_origin {
   TR_REALM_LOCAL=0, /* realm we were configured to contact */
@@ -83,19 +75,16 @@ 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);
-
-TR_AAA_SERVER *tr_aaa_server_new(TALLOC_CTX *mem_ctx, TR_NAME *hostname);
-void tr_aaa_server_free(TR_AAA_SERVER *aaa);
-
-TR_AAA_SERVER_ITER *tr_aaa_server_iter_new(TALLOC_CTX *mem_ctx);
-void tr_aaa_server_iter_free(TR_AAA_SERVER_ITER *iter);
-TR_AAA_SERVER *tr_aaa_server_iter_first(TR_AAA_SERVER_ITER *iter, TR_AAA_SERVER *aaa);
-TR_AAA_SERVER *tr_aaa_server_iter_next(TR_AAA_SERVER_ITER *iter);
 
+void tr_idp_realm_decref(TR_IDP_REALM *realm);
 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_inet_util.h b/include/tr_inet_util.h
new file mode 100644 (file)
index 0000000..f24bbf8
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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_INET_UTIL_H
+#define TRUST_ROUTER_TR_INET_UTIL_H
+
+#include <arpa/inet.h>
+#include <tr_name_internal.h>
+
+char *tr_parse_host(TALLOC_CTX *mem_ctx, const char *s, int *port_out);
+
+TR_NAME *tr_hostname_and_port_to_name(TR_NAME *hn, int port);
+int tr_parse_port(const char *s);
+
+#endif //TRUST_ROUTER_TR_INET_UTIL_H
diff --git a/include/tr_json_util.h b/include/tr_json_util.h
new file mode 100644 (file)
index 0000000..3573a88
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ */
+
+/* Utilities for working with JSON/jansson */
+
+#ifndef TRUST_ROUTER_TR_JSON_UTIL_H
+#define TRUST_ROUTER_TR_JSON_UTIL_H
+
+/**
+ * @def OBJECT_SET_OR_FAIL(job, key, val)
+ * Add a key/value pair to an object or jump to the cleanup label
+ * if the value is null.
+ *
+ * @param jobj JSON object instance to receive the key/value pair
+ * @param key key to set
+ * @param val value to set
+ */
+#define OBJECT_SET_OR_FAIL(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+/**
+ * @def OBJECT_SET_OR_SKIP(job, key, val)
+ * Add a key/value pair to an object only if it is non-null.
+ *
+ * @param jobj JSON object instance to receive the key/value pair
+ * @param key key to set
+ * @param val value to set
+ */
+#define OBJECT_SET_OR_SKIP(jobj, key, val)     \
+do {                                           \
+  if (val)                                     \
+    json_object_set_new((jobj),(key),(val));   \
+} while (0)
+
+
+/**
+ * @def ARRAY_APPEND_OR_FAIL(job, key, val)
+ * Append a value to an array or jump to the cleanup label
+ * if the value is null.
+ *
+ * @param jobj JSON array instance to receive the value
+ * @param val value to set
+ */
+#define ARRAY_APPEND_OR_FAIL(jary, val)        \
+do {                                           \
+  if (val)                                     \
+    json_array_append_new((jary),(val));       \
+  else                                         \
+    goto cleanup;                              \
+} while (0)
+
+
+#endif //TRUST_ROUTER_TR_JSON_UTIL_H
diff --git a/include/tr_list.h b/include/tr_list.h
new file mode 100644 (file)
index 0000000..8bfca38
--- /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_TR_LIST_H
+#define TRUST_ROUTER_TR_LIST_H
+
+#include <talloc.h>
+#include <glib.h>
+
+typedef GPtrArray *TR_LIST;
+
+typedef void (TR_LIST_FOREACH_FUNC)(void *item, void *cookie);
+
+typedef struct tr_list_iter{
+  TR_LIST *list;
+  guint index;
+} TR_LIST_ITER;
+
+#define tr_list_index(LIST, INDEX) (g_ptr_array_index(*(LIST),(INDEX)))
+#define tr_list_length(LIST) ((size_t)((*(LIST))->len))
+
+TR_LIST *tr_list_new(TALLOC_CTX *mem_ctx);
+void tr_list_free(TR_LIST *list);
+void *tr_list_add(TR_LIST *list, void *item, int steal);
+
+TR_LIST_ITER *tr_list_iter_new(TALLOC_CTX *mem_ctx);
+void tr_list_iter_free(TR_LIST_ITER *iter);
+void *tr_list_iter_first(TR_LIST_ITER *iter, TR_LIST *list);
+void *tr_list_iter_next(TR_LIST_ITER *iter);
+void tr_list_foreach(TR_LIST *list, TR_LIST_FOREACH_FUNC *func, void *cookie);
+
+#endif //TRUST_ROUTER_TR_LIST_H
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 f60c7af..b15081c 100644 (file)
 #include <pthread.h>
 #include <time.h>
 
-/* Note on mq priorities: High priority messages are guaranteed to be
- * processed before any normal priority messages. Otherwise, messages
- * will be processed in the order they are added to the queue. */
-
-typedef enum tr_mq_priority {
-  TR_MQ_PRIO_NORMAL=0,
-  TR_MQ_PRIO_HIGH
-} TR_MQ_PRIORITY;
-
 /* msg for inter-thread messaging */
 typedef struct tr_mq_msg TR_MQ_MSG;
 struct tr_mq_msg {
   TR_MQ_MSG *next;
-  TR_MQ_PRIORITY prio;
   char *message;
   void *p; /* payload */
   void (*p_free)(void *); /* function to free payload */
@@ -67,7 +57,6 @@ struct tr_mq {
   pthread_cond_t have_msg_cond;
   TR_MQ_MSG *head;
   TR_MQ_MSG *tail;
-  TR_MQ_MSG *last_hi_prio;
   TR_MQ_NOTIFY_FN notify_cb; /* callback when queue becomes non-empty */
   void *notify_cb_arg;
 };
@@ -75,9 +64,8 @@ struct tr_mq {
 /* message string for sending trpc messages */
 #define TR_MQMSG_TRPC_SEND "trpc send msg"
 
-TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *msg, TR_MQ_PRIORITY prio);
+TR_MQ_MSG *tr_mq_msg_new(TALLOC_CTX *mem_ctx, const char *msg);
 void tr_mq_msg_free(TR_MQ_MSG *msg);
-TR_MQ_PRIORITY tr_mq_msg_get_prio(TR_MQ_MSG *msg);
 const char *tr_mq_msg_get_message(TR_MQ_MSG *msg);
 void *tr_mq_msg_get_payload(TR_MQ_MSG *msg);
 void tr_mq_msg_set_payload(TR_MQ_MSG *msg, void *p, void (*p_free)(void *));
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 02c78f8..a67a64d 100644 (file)
  */
 
 #ifndef TR_NAME_INTERNAL_H
+#define TR_NAME_INTERNAL_H
 
 #include<trust_router/tr_name.h>
 #include <jansson.h>
 
 /** Prototypes */
-json_t *tr_name_to_json_string(TR_NAME *src);
-int tr_name_cmp_str(TR_NAME *one, const char *two_str);
-int tr_name_prefix_wildcard_match(TR_NAME *str, TR_NAME *wc_str);
-
-#define TR_NAME_INTERNAL_H
+json_t *tr_name_to_json_string(const TR_NAME *src);
+int tr_name_cmp_str(const TR_NAME *one, const char *two_str);
+int tr_name_prefix_wildcard_match(const TR_NAME *str, const TR_NAME *wc_str);
 
 #endif //TRUST_ROUTER_TR_NAME_INTERNAL_H
diff --git a/include/tr_rand_id.h b/include/tr_rand_id.h
new file mode 100644 (file)
index 0000000..8632c89
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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_RAND_ID_H
+#define TRUST_ROUTER_TR_RAND_ID_H
+
+char *tr_random_id(TALLOC_CTX *mem_ctx);
+
+#endif //TRUST_ROUTER_TR_RAND_ID_H
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..73c8d15
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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
+#include <sys/socket.h>
+
+nfds_t tr_sock_listen_all(int port, int *fd_out, nfds_t max_fd);
+int tr_sock_accept(int sock);
+
+#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..9b2da06 100644 (file)
 #ifndef TR_UTIL_H
 #define TR_UTIL_H
 
+#include <stddef.h>
 #include <trust_router/tr_versioning.h>
 
-TR_EXPORT int tr_cmp_timespec(struct timespec *ts1, struct timespec *ts2);
+/* 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(const struct timespec *ts1, const struct timespec *ts2);
+int tr_add_timespec(const struct timespec *ts1, const struct timespec *ts2, struct timespec *sum);
+int tr_sub_timespec(const struct timespec *ts1_copy, const struct timespec *ts2_copy, struct timespec *diff);
+char *timespec_to_str(const struct timespec *ts);
+struct timespec *tr_clock_convert(clockid_t from, const struct timespec *when,
+                                  clockid_t to, struct timespec *dst);
 
 #endif /* TR_UTIL_H */
index d6828d5..0655a62 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>
@@ -56,8 +58,9 @@
 /* TRP update record types */
 typedef struct trp_inforec_route {
   TR_NAME *trust_router;
+  int trust_router_port;
   TR_NAME *next_hop;
-  unsigned int next_hop_port;
+  int next_hop_port;
   unsigned int metric;
   unsigned int interval;
 } TRP_INFOREC_ROUTE;
@@ -114,8 +117,8 @@ struct trp_connection {
   TRP_CONNECTION *next;
   pthread_t *thread; /* thread servicing this connection */
   int fd;
-  TR_NAME *gssname;
-  TR_NAME *peer; /* TODO: why is there a peer and a gssname? jlr */
+  TR_NAME *gssname; /* the gss service name we presented for passive auth */
+  TR_NAME *peer; /* gssname of incoming peer */
   gss_ctx_id_t *gssctx;
   TRP_CONNECTION_STATUS status;
   void (*status_change_cb)(TRP_CONNECTION *conn, void *cookie);
@@ -137,7 +140,7 @@ struct trpc_instance {
   TRPC_INSTANCE *next;
   TR_NAME *gssname;
   char *server;
-  unsigned int port;
+  int port;
   TRP_CONNECTION *conn;
   TR_MQ *mq; /* msgs from master to trpc */
 };
@@ -145,7 +148,8 @@ struct trpc_instance {
 /* TRP Server Instance Data */
 struct trps_instance {
   char *hostname;
-  unsigned int port;
+  int trps_port;
+  int tids_port; /* used for route advertisements; must agree with our tids configuration */
   TRP_AUTH_FUNC auth_handler;
   TRPS_MSG_FUNC msg_handler;
   void *cookie;
@@ -185,8 +189,8 @@ TRP_CONNECTION *trp_connection_get_next(TRP_CONNECTION *conn);
 TRP_CONNECTION *trp_connection_remove(TRP_CONNECTION *conn, TRP_CONNECTION *remove);
 void trp_connection_append(TRP_CONNECTION *conn, TRP_CONNECTION *new);
 int trp_connection_auth(TRP_CONNECTION *conn, TRP_AUTH_FUNC auth_callback, void *callback_data);
-TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gssname);
-TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int port);
+TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gss_servicename);
+TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, int port);
 
 TRPC_INSTANCE *trpc_new (TALLOC_CTX *mem_ctx);
 void trpc_free (TRPC_INSTANCE *trpc);
@@ -201,12 +205,12 @@ void trpc_set_server(TRPC_INSTANCE *trpc, char *server);
 TR_NAME *trpc_get_gssname(TRPC_INSTANCE *trpc);
 void trpc_set_gssname(TRPC_INSTANCE *trpc, TR_NAME *gssname);
 unsigned int trpc_get_port(TRPC_INSTANCE *trpc);
-void trpc_set_port(TRPC_INSTANCE *trpc, unsigned int port);
+void trpc_set_port(TRPC_INSTANCE *trpc, int port);
 TRP_CONNECTION_STATUS trpc_get_status(TRPC_INSTANCE *trpc);
 TR_MQ *trpc_get_mq(TRPC_INSTANCE *trpc);
 void trpc_set_mq(TRPC_INSTANCE *trpc, TR_MQ *mq);
 void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg);
-TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc);
+TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc, struct timespec *ts_abort);
 void trpc_mq_clear(TRPC_INSTANCE *trpc);
 void trpc_master_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg);
 TR_MQ_MSG *trpc_master_mq_pop(TRPC_INSTANCE *trpc);
@@ -237,7 +241,7 @@ int trps_get_listener(TRPS_INSTANCE *trps,
                       TRPS_MSG_FUNC msg_handler,
                       TRP_AUTH_FUNC auth_handler,
                       const char *hostname,
-                      unsigned int port,
+                      int port,
                       void *cookie,
                       int *fd_out,
                       size_t max_fd);
@@ -273,11 +277,13 @@ TR_NAME *trp_inforec_get_realm(TRP_INFOREC *rec);
 TR_NAME *trp_inforec_dup_realm(TRP_INFOREC *rec);
 TRP_RC trp_inforec_set_realm(TRP_INFOREC *rec, TR_NAME *realm);
 TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec);
+int trp_inforec_get_trust_router_port(TRP_INFOREC *rec);
 TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router);
+TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router, int port);
 TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec);
+int trp_inforec_get_next_hop_port(TRP_INFOREC *rec);
 TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec);
-TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop);
+TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop, int port);
 unsigned int trp_inforec_get_metric(TRP_INFOREC *rec);
 TRP_RC trp_inforec_set_metric(TRP_INFOREC *rec, unsigned int metric);
 unsigned int trp_inforec_get_interval(TRP_INFOREC *rec);
diff --git a/include/trp_peer.h b/include/trp_peer.h
new file mode 100644 (file)
index 0000000..557e2a3
--- /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;
+  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);
+int trp_peer_get_port(TRP_PEER *peer);
+void trp_peer_set_port(TRP_PEER *peer, 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..fb7c060
--- /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_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 */
+  int trust_router_port;
+  TR_NAME *next_hop;
+  int next_hop_port;
+  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_trust_router_port(TRP_ROUTE *entry, int port);
+int trp_route_get_trust_router_port(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_next_hop_port(TRP_ROUTE *entry, int port);
+int trp_route_get_next_hop_port(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);
+struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *comm, struct timespec *result);
+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 8fc4267..33f530c 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,9 @@ 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);
 void tid_req_set_resp_func(TID_REQ *req, TIDC_RESP_FUNC *resp_func);
 TR_EXPORT void *tid_req_get_cookie(TID_REQ *req);
@@ -119,6 +122,8 @@ TR_EXPORT TR_NAME *tid_resp_get_comm(TID_RESP *resp);
 void tid_resp_set_comm(TID_RESP *resp, TR_NAME *comm);
 TR_EXPORT TR_NAME *tid_resp_get_orig_coi(TID_RESP *resp);
 void tid_resp_set_orig_coi(TID_RESP *resp, TR_NAME *orig_coi);
+TR_EXPORT TR_NAME *tid_resp_get_request_id(TID_RESP *resp);
+void tid_resp_set_request_id(TID_RESP *resp, TR_NAME *request_id);
 TR_EXPORT TID_SRVR_BLK *tid_resp_get_server(TID_RESP *resp, size_t index);
 TR_EXPORT size_t tid_resp_get_num_servers(const TID_RESP *resp);
 TR_EXPORT const TID_PATH *tid_resp_get_error_path(const TID_RESP *);
@@ -136,14 +141,14 @@ 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 */
 TR_EXPORT TIDC_INSTANCE *tidc_create (void);
-TR_EXPORT int tidc_open_connection (TIDC_INSTANCE *tidc, const char *server, unsigned int port, gss_ctx_id_t *gssctx);
+TR_EXPORT int tidc_open_connection(TIDC_INSTANCE *tidc, const char *server, int port, gss_ctx_id_t *gssctx);
 TR_EXPORT int tidc_send_request (TIDC_INSTANCE *tidc, int conn, gss_ctx_id_t gssctx, const char *rp_realm, const char *realm, const char *coi, TIDC_RESP_FUNC *resp_handler, void *cookie);
 TR_EXPORT int tidc_fwd_request (TIDC_INSTANCE *tidc, TID_REQ *req, TIDC_RESP_FUNC *resp_handler, void *cookie);
 TR_EXPORT DH *tidc_get_dh(TIDC_INSTANCE *);
@@ -151,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 int tids_start(TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
+                         tids_auth_func *auth_handler, const char *hostname,
+                         int port, void *cookie);
+TR_EXPORT nfds_t tids_get_listener(TIDS_INSTANCE *tids, TIDS_REQ_FUNC *req_handler,
+                                   tids_auth_func *auth_handler, const char *hostname,
+                                   int port, void *cookie, int *fd_out, size_t max_fd);
 TR_EXPORT int tids_accept(TIDS_INSTANCE *tids, int listen);
 TR_EXPORT int tids_send_response (TIDS_INSTANCE *tids, TID_REQ *req, TID_RESP *resp);
 TR_EXPORT int tids_send_err_response (TIDS_INSTANCE *tids, TID_REQ *req, const char *err_msg);
index 2594b36..88619e4 100644 (file)
 #ifndef TR_CONSTRAINT_H
 #define TR_CONSTRAINT_H
 
-#include <talloc.h>
-
 #include <trust_router/tr_name.h>
 #include <trust_router/tid.h>
 
-
-#define TR_MAX_CONST_MATCHES 24
-
-
-typedef struct tr_constraint {
-    TR_NAME *type;
-    TR_NAME *matches[TR_MAX_CONST_MATCHES];
-} TR_CONSTRAINT;
-
-TR_CONSTRAINT *tr_constraint_new(TALLOC_CTX *mem_ctx);
-void tr_constraint_free(TR_CONSTRAINT *cons);
-TR_CONSTRAINT *tr_constraint_dup(TALLOC_CTX *mem_ctx, TR_CONSTRAINT *cons);
+typedef struct tr_constraint TR_CONSTRAINT;
 
 void TR_EXPORT tr_constraint_add_to_set (TR_CONSTRAINT_SET **cs, TR_CONSTRAINT *c);
 int TR_EXPORT tr_constraint_set_validate( TR_CONSTRAINT_SET *);
@@ -65,6 +52,4 @@ int TR_EXPORT tr_constraint_set_get_match_strings(TID_REQ *,
                                                   const char * constraint_type,
                                                   tr_const_string **output,
                                                   size_t *output_len);
-
-
 #endif
index 052d291..797a3be 100644 (file)
@@ -45,11 +45,11 @@ typedef struct tr__name {
 } TR_NAME;
 
 TR_EXPORT TR_NAME *tr_new_name (const char *name);
-TR_EXPORT TR_NAME *tr_dup_name (TR_NAME *from);
+TR_EXPORT TR_NAME *tr_dup_name (const TR_NAME *from);
 TR_EXPORT void tr_free_name (TR_NAME *name);
-TR_EXPORT int tr_name_cmp (TR_NAME *one, TR_NAME *two);
+TR_EXPORT int tr_name_cmp (const TR_NAME *one, const TR_NAME *two);
 TR_EXPORT void tr_name_strlcat(char *dest, const TR_NAME *src, size_t len);
-TR_EXPORT char *tr_name_strdup(TR_NAME *);
-TR_EXPORT TR_NAME *tr_name_cat(TR_NAME *n1, TR_NAME *n2);
+TR_EXPORT char *tr_name_strdup(const TR_NAME *);
+TR_EXPORT TR_NAME *tr_name_cat(const TR_NAME *n1, const TR_NAME *n2);
 
 #endif
index 1c8e193..b650cf8 100644 (file)
@@ -60,6 +60,7 @@ typedef enum trp_rc {
   TRP_UNSUPPORTED, /* unsupported feature */
   TRP_BADARG, /* bad argument */
   TRP_CLOCKERR, /* error reading time */
+  TRP_MISSING, /* value not present */
 } TRP_RC;
 
 typedef enum trp_inforec_type {
@@ -90,7 +91,7 @@ void trp_upd_set_comm(TRP_UPD *upd, TR_NAME *comm);
 TR_EXPORT TR_NAME *trp_upd_get_peer(TRP_UPD *upd);
 TR_NAME *trp_upd_dup_peer(TRP_UPD *upd);
 void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer);
-void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port);
+void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, int port);
 void trp_upd_add_to_provenance(TRP_UPD *upd, TR_NAME *name);
 
 /* Functions for TRP_REQ structures */
diff --git a/m4/.ignore-this-file b/m4/.ignore-this-file
new file mode 100644 (file)
index 0000000..15a8762
--- /dev/null
@@ -0,0 +1,2 @@
+This file only exists to ensure the m4 directory exists in the git repository.
+Without it, autoreconf complains when building for the first time.
diff --git a/mon/mon_common.c b/mon/mon_common.c
new file mode 100644 (file)
index 0000000..f09a26c
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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 <glib.h>
+#include <string.h>
+
+#include <mon_internal.h>
+
+// Monitoring common code
+
+struct mon_cmd_entry {
+  MON_CMD code;
+  const char *name;
+};
+
+struct mon_opt_entry {
+  MON_OPT_TYPE code;
+  MON_CMD cmd_code;
+  const char *name;
+};
+
+/* Table of commands */
+struct mon_cmd_entry mon_cmd_table[] = {
+    { MON_CMD_SHOW, "show" },
+    { MON_CMD_UNKNOWN } /* list terminator */
+};
+
+/* Table of options */
+struct  mon_opt_entry mon_opt_table[] = {
+    { OPT_TYPE_SHOW_VERSION,            MON_CMD_SHOW,  "version"            },
+    { OPT_TYPE_SHOW_CONFIG_FILES,       MON_CMD_SHOW,  "config_files"       },
+    { OPT_TYPE_SHOW_UPTIME,             MON_CMD_SHOW,  "uptime"             },
+    { OPT_TYPE_SHOW_TID_REQS_PROCESSED, MON_CMD_SHOW,  "tid_reqs_processed" },
+    { OPT_TYPE_SHOW_TID_REQS_FAILED,    MON_CMD_SHOW,  "tid_reqs_failed"    },
+    { OPT_TYPE_SHOW_TID_REQS_PENDING,   MON_CMD_SHOW,  "tid_reqs_pending"   },
+    { OPT_TYPE_SHOW_TID_ERROR_COUNT,    MON_CMD_SHOW,  "tid_error_count"    },
+    { OPT_TYPE_SHOW_ROUTES,             MON_CMD_SHOW,  "routes"             },
+    { OPT_TYPE_SHOW_PEERS,              MON_CMD_SHOW,  "peers"              },
+    { OPT_TYPE_SHOW_COMMUNITIES,        MON_CMD_SHOW,  "communities"        },
+    { OPT_TYPE_SHOW_REALMS,             MON_CMD_SHOW,  "realms"             },
+    { OPT_TYPE_SHOW_RP_CLIENTS,         MON_CMD_SHOW,  "rp_clients"         },
+    { OPT_TYPE_UNKNOWN } /* list terminator */
+};
+
+/*** Commands ***/
+
+static struct mon_cmd_entry *find_cmd_entry(MON_CMD code)
+{
+  struct mon_cmd_entry *entry;
+
+  for (entry=mon_cmd_table; entry->code != MON_CMD_UNKNOWN; entry++) {
+    if (entry->code == code)
+      return entry;
+  }
+
+  return NULL;
+}
+
+const char *mon_cmd_to_string(MON_CMD cmd)
+{
+  struct mon_cmd_entry *entry = find_cmd_entry(cmd);
+
+  if (entry)
+    return entry->name;
+
+  return NULL;
+}
+
+MON_CMD mon_cmd_from_string(const char *s)
+{
+  struct mon_cmd_entry *entry;
+
+  for (entry=mon_cmd_table; entry->code != MON_CMD_UNKNOWN; entry++) {
+    if (strcmp(s, entry->name) == 0)
+      return entry->code;
+  }
+
+  return MON_CMD_UNKNOWN;
+}
+
+/*** Options ***/
+
+static struct mon_opt_entry *find_opt_entry(MON_OPT_TYPE code)
+{
+  struct mon_opt_entry *entry;
+
+  for (entry=mon_opt_table; entry->code != OPT_TYPE_UNKNOWN; entry++) {
+    if (entry->code == code)
+      return entry;
+  }
+
+  return NULL;
+}
+
+const char *mon_opt_type_to_string(MON_OPT_TYPE opt_type)
+{
+  struct mon_opt_entry *entry = find_opt_entry(opt_type);
+
+  if (entry)
+    return entry->name;
+
+  return NULL;
+}
+
+MON_OPT_TYPE mon_opt_type_from_string(const char *s)
+{
+  struct mon_opt_entry *entry;
+
+  for (entry=mon_opt_table; entry->code != OPT_TYPE_UNKNOWN; entry++) {
+    if (strcmp(s, entry->name) == 0)
+      return entry->code;
+  }
+
+  return OPT_TYPE_UNKNOWN;
+}
diff --git a/mon/mon_req.c b/mon/mon_req.c
new file mode 100644 (file)
index 0000000..e7cc43b
--- /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 <glib.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..2d05620
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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;
+    }
+    if (MON_SUCCESS != mon_req_add_option(req, opt.type)){
+      return MON_NOPARSE;
+    }
+  }
+  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..12e408c
--- /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_encode(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_encode(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..3049eaa
--- /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 response 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..c6ca379
--- /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..515fb7a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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 encoder */
+
+/* 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..c16d328
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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,
+                         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;
+}
diff --git a/mon/mons.c b/mon/mons.c
new file mode 100644 (file)
index 0000000..1df11f2
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * 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 <sys/resource.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->mon_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_GSS_RC mons_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *req_msg, TR_MSG **resp_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_GSS_RC rc = TR_GSS_ERROR;
+
+  /* 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 */
+    *resp_msg = NULL; /* null this out so the caller doesn't mistake it for valid */
+    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);
+  rc = TR_GSS_SUCCESS;
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+/**
+ * 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,
+                      int port,
+                      void *cookie,
+                      int *fd_out,
+                      size_t max_fd)
+{
+  size_t n_fd=0;
+  size_t ii=0;
+
+  mons->mon_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", port);
+  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;
+}
+
+/**
+ * Process to handle an incoming monitoring request
+ *
+ * This should be run in a child process after fork(). Handles the request
+ * and terminates. Never returns to the caller.
+ *
+ * @param mons the monitoring server instance
+ * @param conn_fd file descriptor for the incoming connection
+ */
+static void mons_handle_proc(MONS_INSTANCE *mons, int conn_fd)
+{
+  struct rlimit rlim; /* for disabling core dump */
+
+  switch(tr_gss_handle_connection(conn_fd,
+                                  "trustmonitor", mons->hostname, /* acceptor name */
+                                  mons->auth_handler, mons->cookie, /* auth callback and cookie */
+                                  mons_req_cb, mons /* req callback and cookie */
+  )) {
+    case TR_GSS_SUCCESS:
+      /* do nothing */
+      break;
+
+    case TR_GSS_ERROR:
+      tr_debug("mons_accept: Error returned by tr_gss_handle_connection()");
+      break;
+
+    default:
+      tr_err("mons_accept: Unexpected value returned by tr_gss_handle_connection()");
+      break;
+  }
+  close(conn_fd);
+
+  /* This ought to be an exit(0), but log4shib does not play well with fork() due to
+   * threading issues. To ensure we do not get stuck in the exit handler, we will
+   * abort. First disable core dump for this subprocess (the main process will still
+   * dump core if the environment allows). */
+  rlim.rlim_cur = 0; /* max core size of 0 */
+  rlim.rlim_max = 0; /* prevent the core size limit from being raised later */
+  setrlimit(RLIMIT_CORE, &rlim);
+  abort(); /* exit hard */
+}
+
+/**
+ * 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 = tr_sock_accept(listen))) {
+    tr_debug("mons_accept: Error accepting connection");
+    return 1;
+  }
+
+  if (0 > (pid = fork())) {
+    perror("Error on fork()");
+    return 1;
+  }
+
+  if (pid == 0) {
+    /* Only the child process gets here */
+    close(listen); /* this belongs to the parent */
+    mons_handle_proc(mons, conn); /* never returns */
+  }
+
+  /* Only the parent process gets here */
+  close(conn); /* this belongs to the child */
+  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..24cce1f
--- /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 <glib.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_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_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..360be1f
--- /dev/null
@@ -0,0 +1,124 @@
+//
+// 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 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_REQS_PROCESSED,
+      OPT_TYPE_SHOW_TID_REQS_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 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..9fe9b1b
--- /dev/null
@@ -0,0 +1,105 @@
+//
+// 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 *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 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_REQS_PROCESSED;
+  opts[4] = OPT_TYPE_SHOW_TID_REQS_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..3c1dd83
--- /dev/null
@@ -0,0 +1,93 @@
+//
+// 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 *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_REQS_PENDING),
+                               json_integer(13)));
+  assert(! json_object_set_new(payload,
+                               mon_opt_type_to_string(OPT_TYPE_SHOW_TID_REQS_PROCESSED),
+                               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_show_success.test", show_success));
+  return 0;
+}
index 8b2a77b..ac669b2 100644 (file)
@@ -1,4 +1,5 @@
 {
+  "serial_number": 1,
   "tr_internal": {
     "hostname":"beta.example.com",
     "trps_port":12308,
     "logging": {
       "log_threshold": "info",
       "console_threshold":"notice"
+    },
+    "monitoring": {
+      "enabled": true,
+      "port": 12310,
+      "authorized_credentials": [
+        "monitor@apc.x"
+      ]
     }
   }
 }
index 58f9ac1..7cff77e 100644 (file)
@@ -1,4 +1,5 @@
 {
+  "serial_number": 2,
   "communities": [
     {
       "apcs": [],
@@ -93,8 +94,7 @@
   ],
   "peer_organizations": [
     {
-      "hostname": "peer.example.com",
-      "port": 12310,
+      "hostname": "peer.example.com:12308",
       "gss_names": ["peer-cred@apc.x"]
     }
   ]
index 26fb994..4ca3780 100644 (file)
 #include <tid_internal.h>
 #include <trust_router/tr_dh.h>
 #include <trust_router/tid.h>
+#include <tr_inet_util.h>
+
+struct tidc_resp_cookie {
+  int succeeded;
+};
 
 static void tidc_resp_handler (TIDC_INSTANCE * tidc, 
                        TID_REQ *req,
@@ -52,9 +57,12 @@ static void tidc_resp_handler (TIDC_INSTANCE * tidc,
   unsigned char *c_keybuf = NULL;
   int i;
   struct timeval tv;
+  struct tidc_resp_cookie *data = (struct tidc_resp_cookie *) cookie;
 
   printf ("Response received! Realm = %s, Community = %s.\n", resp->realm->buf, resp->comm->buf);
 
+  data->succeeded = 0;
+
   /* Generate the client key -- TBD, handle more than one server */
   if (TID_SUCCESS != resp->result) {
     fprintf(stderr, "tidc_resp_handler: Response is an error.\n");
@@ -86,6 +94,7 @@ static void tidc_resp_handler (TIDC_INSTANCE * tidc,
   }
   printf("\n");
 
+  data->succeeded = 1;
   return;
 }
 
@@ -146,7 +155,19 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
       break;
 
     case 4:
-      arguments->port=strtol(arg, NULL, 10); /* optional */
+      arguments->port=tr_parse_port(arg); /* optional */
+      if (arguments->port < 0) {
+        switch(-(arguments->port)) {
+          case ERANGE:
+            printf("\nError parsing port (%s): port must be an integer in the range 1 - 65535\n\n", arg);
+            break;
+
+          default:
+            printf("\nError parsing port (%s): %s\n\n", arg, strerror(-arguments->port));
+            break;
+        }
+        argp_usage(state);
+      }
       break;
 
     default:
@@ -173,6 +194,11 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
   return 0; /* success */
 }
 
+/* Exit values */
+#define EXIT_OK 0
+#define EXIT_REQ_FAILED 2
+#define EXIT_ERROR 1
+
 /* assemble the argp parser */
 static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
 
@@ -184,6 +210,7 @@ int main (int argc,
   int rc;
   gss_ctx_id_t gssctx;
   struct cmdline_args opts;
+  struct tidc_resp_cookie cookie = {0};
 
   /* parse the command line*/
   /* set defaults */
@@ -210,29 +237,33 @@ 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;
+    return EXIT_ERROR;
   }
 
   /* Set-up TID connection */
   if (-1 == (conn = tidc_open_connection(tidc, opts.server, opts.port, &gssctx))) {
     /* Handle error */
     printf("Error in tidc_open_connection.\n");
-    return 1;
+    return EXIT_ERROR;
   };
 
   /* Send a TID request */
   if (0 > (rc = tidc_send_request(tidc, conn, gssctx, opts.rp_realm, opts.target_realm, opts.community, 
-                                 &tidc_resp_handler, NULL))) {
+                                 &tidc_resp_handler, &cookie))) {
     /* Handle error */
     printf("Error in tidc_send_request, rc = %d.\n", rc);
-    return 1;
+    return EXIT_ERROR;
   }
     
   /* Clean-up the TID client instance, and exit */
   tidc_destroy(tidc);
 
-  return 0;
+  if (cookie.succeeded)
+    return EXIT_OK;
+  else
+    return EXIT_REQ_FAILED;
 }
 
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 db87adf..1bfce70 100644 (file)
@@ -63,6 +63,8 @@ static int destroy_tid_req(TID_REQ *req)
     tr_free_name(req->comm);
   if (req->orig_coi!=NULL)
     tr_free_name(req->orig_coi);
+  if (req->request_id!=NULL)
+    tr_free_name(req->request_id);
   return 0;
 }
 
@@ -76,6 +78,7 @@ TID_REQ *tid_req_new()
   assert(req->json_references);
   req->conn = -1;
   req->free_conn = 1;
+  req->request_id = NULL;
   return req;
 }
 
@@ -164,11 +167,21 @@ 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;
 }
 
+void tid_req_set_request_id(TID_REQ *req, TR_NAME *request_id)
+{
+  req->request_id = request_id;
+}
+
+TR_NAME *tid_req_get_request_id(TID_REQ *req)
+{
+  return(req->request_id);
+}
+
 TIDC_RESP_FUNC *tid_req_get_resp_func(TID_REQ *req)
 {
   return(req->resp_func);
@@ -215,7 +228,13 @@ TID_REQ *tid_dup_req (TID_REQ *orig_req)
       tr_crit("tid_dup_req: Can't duplicate request (orig_coi).");
     }
   }
-  
+
+  if (orig_req->request_id) {
+    if (NULL == (new_req->request_id = tr_dup_name(orig_req->request_id))) {
+      tr_crit("tid_dup_req: Can't duplicate request (request_id).");
+    }
+  }
+
   return new_req;
 }
 
@@ -234,7 +253,7 @@ void tid_req_free(TID_REQ *req)
 }
 
 int tid_req_add_path(TID_REQ *req,
-                    const char *this_system, unsigned port)
+                     const char *this_system, int port)
 {
   char *path_element = talloc_asprintf(req, "%s:%u",
                                       this_system, port);
index e75571b..3ff3d02 100644 (file)
@@ -53,6 +53,8 @@ static int tid_resp_destructor(void *obj)
     tr_free_name(resp->comm);
   if (resp->orig_coi!=NULL)
     tr_free_name(resp->orig_coi);
+  if (resp->request_id!=NULL)
+    tr_free_name(resp->request_id);
   return 0;
 }
 
@@ -68,6 +70,7 @@ TID_RESP *tid_resp_new(TALLOC_CTX *mem_ctx)
     resp->cons=NULL;
     resp->orig_coi=NULL;
     resp->servers=NULL;
+    resp->request_id=NULL;
     resp->error_path=NULL;
     talloc_set_destructor((void *)resp, tid_resp_destructor);
   }
@@ -80,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;
@@ -90,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);
@@ -166,6 +195,16 @@ void tid_resp_set_orig_coi(TID_RESP *resp, TR_NAME *orig_coi)
   resp->orig_coi = orig_coi;
 }
 
+TR_EXPORT TR_NAME *tid_resp_get_request_id(TID_RESP *resp)
+{
+  return(resp->request_id);
+}
+
+void tid_resp_set_request_id(TID_RESP *resp, TR_NAME *request_id)
+{
+  resp->request_id = request_id;
+}
+
 TR_EXPORT TID_SRVR_BLK *tid_resp_get_server(TID_RESP *resp,
                                            size_t index)
 {
index d724322..9578f3d 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;
@@ -55,12 +56,19 @@ static int tidc_destructor(void *obj)
   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;
+    tidc->gssc = tr_gssc_instance_new(tidc);
+    if (tidc->gssc == NULL) {
+      talloc_free(tidc);
+      return NULL;
+    }
+    tidc->gssc->service_name = "trustidentity";
+    tidc->client_dh = NULL;
     talloc_set_destructor((void *)tidc, tidc_destructor);
   }
   return tidc;
@@ -71,44 +79,57 @@ void tidc_destroy(TIDC_INSTANCE *tidc)
   talloc_free(tidc);
 }
 
-int tidc_open_connection (TIDC_INSTANCE *tidc, 
-                         const char *server,
-                         unsigned int port,
-                         gss_ctx_id_t *gssctx)
+int tidc_open_connection(TIDC_INSTANCE *tidc,
+                         const char *server,
+                         int port,
+                         gss_ctx_id_t *gssctx)
 {
-  int err = 0;
-  int conn = -1;
-  unsigned int use_port = 0;
+  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;
@@ -120,82 +141,80 @@ 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_get_dh(tidc));
+
+  /* generate an ID */
+  request_id = tr_random_id(NULL);
+  if (request_id) {
+    if (tid_req->request_id = tr_new_name(request_id))
+      tr_debug("tidc_send_request: Created TID request ID: %s", request_id);
+    else
+      tr_debug("tidc_send_request: Unable to set request ID, proceeding without one");
+    talloc_free(request_id);
+  } else
+    tr_debug("tidc_send_request: Failed to generate a TID request ID, proceeding without one");
 
   rc = tidc_fwd_request(tidc, tid_req, resp_handler, cookie);
   goto cleanup;
  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;
 }
 
 int tidc_fwd_request(TIDC_INSTANCE *tidc,
                      TID_REQ *tid_req,
-                    TIDC_RESP_FUNC *resp_handler,
+                     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;
-  int err;
+  TID_RESP *tid_resp = NULL;
   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");
-    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");
+  resp_msg = tr_gssc_exchange_msgs(tmp_ctx, tidc->gssc, msg);
+  if (resp_msg == NULL)
     goto error;
-  }
 
   /* TBD -- Check if this is actually a valid response */
-  if (TID_RESPONSE != tr_msg_get_msg_type(resp_msg)) {
+  tid_resp = tr_msg_get_resp(resp_msg);
+  if (tid_resp == NULL) {
     tr_err( "tidc_fwd_request: Error, no response in the response!\n");
     goto error;
   }
 
+  /* Check whether the request IDs matched and warn if not. Do nothing if we don't get
+   * an ID on the return - it is not mandatory to preserve that field. */
+  if (tid_req->request_id) {
+    if ((tid_resp->request_id)
+        && (tr_name_cmp(tid_resp->request_id, tid_req->request_id) != 0)) {
+      /* Requests present but do not match */
+      tr_warning("tidc_fwd_request: Sent request ID %.*s, received response for %.*s",
+                 tid_req->request_id->len, tid_req->request_id->buf,
+                 tid_resp->request_id->len, tid_resp->request_id->buf);
+    }
+  } else if (tid_resp->request_id) {
+    tr_warning("tidc_fwd_request: Sent request without ID, received response for %.*s",
+               tid_resp->request_id->len, tid_resp->request_id->buf);
+  }
+
   if (resp_handler) {
     /* Call the caller's response function. It must copy any data it needs before returning. */
     tr_debug("tidc_fwd_request: calling response callback function.");
@@ -207,19 +226,12 @@ 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)
+DH *tidc_get_dh(TIDC_INSTANCE *inst)
 {
   return inst->client_dh;
 }
index c1c9bcb..3775b52 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>
+#include <sys/resource.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;
   }
@@ -73,6 +81,12 @@ static TID_RESP *tids_create_response (TIDS_INSTANCE *tids, TID_REQ *req)
       goto cleanup;
     }
   }
+  if (req->request_id) {
+    if (NULL == (resp->request_id = tr_dup_name(req->request_id))) {
+      tr_crit("tids_create_response: Error allocating fields in response.");
+      goto cleanup;
+    }
+  }
 
   success=1;
 
@@ -84,250 +98,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;
   }
 
@@ -338,12 +239,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;
   }
 
@@ -355,84 +255,125 @@ 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_GSS_RC tids_req_cb(TALLOC_CTX *mem_ctx, TR_MSG *mreq, TR_MSG **mresp, 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;
-  int rc = 0;
-  gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
+  TR_GSS_RC rc = TR_GSS_ERROR;
 
-  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.");
+    rc = TR_GSS_INTERNAL_ERROR;
+    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 */
+  *mresp = talloc(tmp_ctx, TR_MSG);
+  if (*mresp == 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.");
+    rc = TR_GSS_INTERNAL_ERROR;
+    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(mresp, 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.");
+    *mresp = NULL; /* the contents are in tmp_ctx, so they will still be cleaned up */
+    rc = TR_GSS_INTERNAL_ERROR;
+    goto cleanup;
+  }
+  /* Now officially assign the response to the message. */
+  tr_msg_set_resp(*mresp, resp);
 
-    /* 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;
-    }
+  /* Handle the request and fill in resp */
+  if (tids_handle_request(tids, req, resp) >= 0)
+    rc = TR_GSS_SUCCESS;
+  else {
+    /* The TID request was an error response */
+    tr_debug("tids_req_cb: Error from tids_handle_request");
+    rc = TR_GSS_REQUEST_FAILED;
+    /* Fall through, to send the response, either way */
+  }
 
-    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 */
-    }
+  /* put the response message in the caller's context */
+  talloc_steal(mem_ctx, *mresp);
+
+cleanup:
+  talloc_free(tmp_ctx);
+  return rc;
+}
+
+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;
+}
 
-    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. */
+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,
+                         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)
-    tr_err("tids_get_listener: Error opening port %d");
+  n_fd = tr_sock_listen_all(port, fd_out, max_fd);
+
+  if (n_fd == 0)
+    tr_err("tids_get_listener: Error opening port %d", port);
   else {
     /* opening port succeeded */
     tr_info("tids_get_listener: Opened port %d.", port);
@@ -445,13 +386,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;
@@ -459,7 +400,70 @@ int tids_get_listener(TIDS_INSTANCE *tids,
     tids->cookie = cookie;
   }
 
-  return n_fd;
+  return (int)n_fd;
+}
+
+/* Strings used to report results from the handler process. The
+ * TIDS_MAX_MESSAGE_LEN must be longer than the longest message, including
+ * null termination (i.e., strlen() + 1) */
+#define TIDS_MAX_MESSAGE_LEN (10)
+#define TIDS_SUCCESS_MESSAGE "OK" /* a success message was sent */
+#define TIDS_ERROR_MESSAGE   "ERR" /* an error message was sent */
+#define TIDS_REQ_FAIL_MESSAGE "FAIL" /* sending failed */
+
+/**
+ * Process to handle an incoming TIDS request
+ *
+ * This should be run in the child process after a fork(). Handles
+ * the request, writes the result to result_fd, and terminates.
+ * Never returns to the caller.
+ *
+ * @param tids TID server instance
+ * @param conn_fd file descriptor for the incoming connection
+ * @param result_fd writable file descriptor for the result, or 0 to disable reporting
+ */
+static void tids_handle_proc(TIDS_INSTANCE *tids, int conn_fd, int result_fd)
+{
+  const char *response_message = NULL;
+  struct rlimit rlim; /* for disabling core dump */
+
+  switch(tr_gss_handle_connection(conn_fd,
+                                  "trustidentity", tids->hostname, /* acceptor name */
+                                  tids->auth_handler, tids->cookie, /* auth callback and cookie */
+                                  tids_req_cb, tids /* req callback and cookie */
+  )) {
+    case TR_GSS_SUCCESS:
+      response_message = TIDS_SUCCESS_MESSAGE;
+      break;
+
+    case TR_GSS_REQUEST_FAILED:
+      response_message = TIDS_ERROR_MESSAGE;
+      break;
+
+    case TR_GSS_INTERNAL_ERROR:
+    case TR_GSS_ERROR:
+    default:
+      response_message = TIDS_REQ_FAIL_MESSAGE;
+      break;
+  }
+
+  if (0 != result_fd) {
+    /* write strlen + 1 to include the null termination */
+    if (write(result_fd, response_message, strlen(response_message) + 1) < 0)
+      tr_err("tids_accept: child process unable to write to pipe");
+  }
+
+  close(result_fd);
+  close(conn_fd);
+
+  /* This ought to be an exit(0), but log4shib does not play well with fork() due to
+   * threading issues. To ensure we do not get stuck in the exit handler, we will
+   * abort. First disable core dump for this subprocess (the main process will still
+   * dump core if the environment allows). */
+  rlim.rlim_cur = 0; /* max core size of 0 */
+  rlim.rlim_max = 0; /* prevent the core size limit from being raised later */
+  setrlimit(RLIMIT_CORE, &rlim);
+  abort(); /* exit hard */
 }
 
 /* Accept and process a connection on a port opened with tids_get_listener() */
@@ -467,47 +471,136 @@ 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()");
+  if (0 > (conn = tr_sock_accept(listen))) {
+    tr_debug("tids_accept: Error accepting connection");
     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(listen);
-    tids_handle_connection(tids, conn);
-    close(conn);
-    exit(0); /* exit to kill forked child process */
-  } else {
-    close(conn);
+    /* Only the child process gets here */
+    close(pipe_fd[0]); /* close the read end of the pipe, the child only writes */
+    close(listen); /* close the child process's handle on the listen port */
+
+    tids_handle_proc(tids, conn, pipe_fd[1]); /* never returns */
   }
 
-  /* 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]); /* close the write end of the pipe, the parent only listens */
+  close(conn); /* connection belongs to the child, so close parent's handle */
 
+  /* remember the PID of our child process */
+  tr_info("tids_accept: Spawned TID process %d to handle incoming connection.", pid);
+  tp.pid = pid;
+  tp.read_fd = pipe_fd[0];
+  g_array_append_val(tids->pids, tp);
+
+  /* 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[TIDS_MAX_MESSAGE_LEN] = {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, TIDS_MAX_MESSAGE_LEN);
+    close(tp.read_fd);
+
+    if ((result_len > 0) && (strcmp(result, TIDS_SUCCESS_MESSAGE) == 0)) {
+      tids->req_count++;
+      tr_info("tids_sweep_procs: TID process %d exited after successful request.", tp.pid);
+    } else if ((result_len > 0) && (strcmp(result, TIDS_ERROR_MESSAGE) == 0)) {
+      tids->req_error_count++;
+      tr_info("tids_sweep_procs: TID process %d exited after unsuccessful request.", tp.pid);
+    } else {
+      tids->error_count++;
+      tr_info("tids_sweep_procs: TID process %d exited with an error.", tp.pid);
+    }
+  }
+}
+
 /* Process tids requests forever. Should not return except on error. */
-#define MAX_SOCKETS 10
-int tids_start (TIDS_INSTANCE *tids, 
-                TIDS_REQ_FUNC *req_handler,
-                tids_auth_func *auth_handler,
-                const char *hostname,
-                unsigned int port,
-                void *cookie)
+int tids_start(TIDS_INSTANCE *tids,
+               TIDS_REQ_FUNC *req_handler,
+               tids_auth_func *auth_handler,
+               const char *hostname,
+               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;
@@ -544,14 +637,13 @@ int tids_start (TIDS_INSTANCE *tids,
 
       if (poll_fd[ii].revents & POLLIN) {
         if (tids_accept(tids, poll_fd[ii].fd))
-          tr_err("tids_start: error in tids_accept().");
+          tr_debug("tids_start: error in tids_accept().");
       }
     }
   }
 
   return 1;    /* should never get here, loops "forever" */
 }
-#undef MAX_SOCKETS
 
 void tids_destroy (TIDS_INSTANCE *tids)
 {
index a03ef4b..73c429e 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>
@@ -81,13 +81,15 @@ static const char arg_doc[]=""; /* string describing arguments, if any */
  * { long-name, short-name, variable name, options, help description } */
 static const struct argp_option cmdline_options[] = {
     { "config-dir", 'c', "DIR", 0, "Specify configuration file location (default is current directory)"},
-    { "version", 'v', NULL, 0, "Print version information and exit"},
+    { "config-validate", 'C', NULL, 0, "Validate configuration files and exit"},
+    { "version", 1, NULL, 0, "Print version information and exit"},
     { NULL }
 };
 
 /* structure for communicating with option parser */
 struct cmdline_args {
     int version_requested;
+    int validate_config_and_exit;
     char *config_dir;
 };
 
@@ -106,10 +108,14 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
       arguments->config_dir=arg;
       break;
 
-    case 'v':
+    case 1:
       arguments->version_requested=1;
       break;
 
+    case 'C':
+      arguments->validate_config_and_exit=1;
+      break;
+
     default:
       return ARGP_ERR_UNKNOWN;
   }
@@ -145,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;
@@ -153,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 */
@@ -171,6 +213,7 @@ int main(int argc, char *argv[])
   /***** parse command-line arguments *****/
   /* set defaults */
   opts.version_requested=0;
+  opts.validate_config_and_exit=0;
   opts.config_dir=".";
 
   /* parse the command line*/
@@ -192,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))) {
@@ -204,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) {
@@ -219,6 +278,11 @@ int main(int argc, char *argv[])
     return 1;
   }
 
+  /***** Exit here if we are just validating our configuration *****/
+  if (opts.validate_config_and_exit) {
+    printf("Valid configuration found in %s.\n", opts.config_dir);
+    return 0;
+  }
   /***** Set up the event loop *****/
   ev_base=tr_event_loop_init(); /* Set up the event loop */
   if (ev_base==NULL) {
@@ -232,19 +296,23 @@ 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;
   }
 
+  /* tell the trps which port the tid server listens on */
+  tr->trps->tids_port = tr->tids->tids_port;
+
   /* install TRP handler events */
   tr_debug("Initializing Dynamic Trust Router Protocol events.");
   if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) {
diff --git a/tr/tr_mon.c b/tr/tr_mon.c
new file mode 100644 (file)
index 0000000..1327082
--- /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->mons_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->mons_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 0c69d55..9d32edc 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 {
@@ -84,7 +87,7 @@ struct tr_tids_fwd_cookie {
   int thread_id;
   pthread_mutex_t mutex; /* lock on the mq (separate from the locking within the mq, see below) */
   TR_MQ *mq; /* messages from thread to main process; set to NULL to disable response */
-  TR_NAME *aaa_hostname;
+  TR_AAA_SERVER *aaa; /* AAA server to contact */
   DH *dh_params;
   TID_REQ *fwd_req; /* the req to duplicate */
 };
@@ -92,8 +95,6 @@ struct tr_tids_fwd_cookie {
 static int tr_tids_fwd_cookie_destructor(void *obj)
 {
   struct tr_tids_fwd_cookie *c=talloc_get_type_abort(obj, struct tr_tids_fwd_cookie);
-  if (c->aaa_hostname!=NULL)
-    tr_free_name(c->aaa_hostname);
   if (c->dh_params!=NULL)
     tr_destroy_dh_params(c->dh_params);
   return 0;
@@ -133,6 +134,8 @@ static void *tr_tids_req_fwd_thread(void *arg)
   TIDC_INSTANCE *tidc=tidc_create();
   TR_MQ_MSG *msg=NULL;
   TR_RESP_COOKIE *cookie=NULL;
+  char *aaa_hostname = NULL;
+  int aaa_port;
   int rc=0;
   int success=0;
 
@@ -144,7 +147,7 @@ static void *tr_tids_req_fwd_thread(void *arg)
   /* create the cookie we will use for our response */
   cookie=talloc(tmp_ctx, TR_RESP_COOKIE);
   if (cookie==NULL) {
-    tr_notice("tr_tids_req_fwd_thread: unable to allocate response cookie.");
+    tr_crit("tr_tids_req_fwd_thread: unable to allocate response cookie.");
     success=0;
     goto cleanup;
   }
@@ -161,19 +164,31 @@ static void *tr_tids_req_fwd_thread(void *arg)
   }
 
   /* Set-up TID connection */
+  aaa_hostname = tr_name_strdup(tr_aaa_server_get_hostname(args->aaa));
+  if (aaa_hostname == NULL) {
+    tr_crit("tr_tids_req_fwd_thread: unable to allocate AAA hostname string");
+    success=0;
+    goto cleanup;
+  }
+  aaa_port = tr_aaa_server_get_port(args->aaa);
+  if ((aaa_port <= 0) || (aaa_port > 65535)) {
+    tr_notice("tr_tids_req_fwd_thread: invalid port (%d) for %s", aaa_port, aaa_hostname);
+    success=0;
+    goto cleanup;
+  }
+
   if (-1==(args->fwd_req->conn = tidc_open_connection(tidc, 
-                                                      args->aaa_hostname->buf,
-                                                      TID_PORT, /* TODO: make this configurable */
-                                                     &(args->fwd_req->gssctx)))) {
+                                                      aaa_hostname,
+                                                      aaa_port,
+                                                      &(args->fwd_req->gssctx)))) {
     tr_notice("tr_tids_req_fwd_thread: Error in tidc_open_connection.");
     /* tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS"); */
     /* TODO: encode reason for failure */
     success=0;
     goto cleanup;
   };
-  tr_debug("tr_tids_req_fwd_thread: thread %d opened TID connection to %s.",
-           cookie->thread_id,
-           args->aaa_hostname->buf);
+  tr_debug("tr_tids_req_fwd_thread: thread %d opened TID connection to %s:%d.",
+           cookie->thread_id, aaa_hostname, aaa_port);
 
   /* Send a TID request. */
   if (0 > (rc = tidc_fwd_request(tidc, args->fwd_req, tr_tidc_resp_handler, (void *)cookie))) {
@@ -183,7 +198,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. */
@@ -193,9 +208,9 @@ cleanup:
     /* mq is still valid, so we can queue our response */
     tr_debug("tr_tids_req_fwd_thread: thread %d using valid msg queue.", cookie->thread_id);
     if (success)
-      msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_SUCCESS, TR_MQ_PRIO_NORMAL);
+      msg= tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_SUCCESS);
     else
-      msg=tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_FAILURE, TR_MQ_PRIO_NORMAL);
+      msg= tr_mq_msg_new(tmp_ctx, TR_TID_MQMSG_FAILURE);
 
     if (msg==NULL)
       tr_notice("tr_tids_req_fwd_thread: thread %d unable to allocate response msg.", cookie->thread_id);
@@ -210,6 +225,9 @@ cleanup:
       tr_notice("tr_tids_req_fwd_thread: Error releasing mutex.");
   }
 
+  if (aaa_hostname != NULL)
+    free(aaa_hostname);
+
   talloc_free(tmp_ctx);
   return NULL;
 }
@@ -221,15 +239,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 +332,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;
@@ -275,19 +361,25 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
 
   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);
+  else
+    tr_debug("tr_tids_req_handler: TID request ID: none");
+
   tids->req_count++;
 
   /* Duplicate the request, so we can modify and forward it */
   if (NULL == (fwd_req=tid_dup_req(orig_req))) {
     tr_debug("tr_tids_req_handler: Unable to duplicate request.");
-    retval=-1;
+    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;
   }
@@ -300,7 +392,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;
   }
@@ -312,7 +404,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;
   }
@@ -341,121 +433,148 @@ 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.");
-      aaa_servers = tr_aaa_server_new(tmp_ctx, trp_route_get_next_hop(route));
+      aaa_servers = tr_aaa_server_new(tmp_ctx); /* cleaned up via talloc */
+      if (aaa_servers == NULL) {
+        tr_err("tr_tids_req_handler: error allocating next hop");
+        retval=-1;
+        goto cleanup;
+      }
+      tr_aaa_server_set_hostname(aaa_servers, trp_route_dup_next_hop(route));
+      if (tr_aaa_server_get_hostname(aaa_servers) == NULL) {
+        tr_err("tr_tids_req_handler: error allocating next hop");
+        retval=-1;
+        goto cleanup;
+      }
+      tr_aaa_server_set_port(aaa_servers, trp_route_get_next_hop_port(route));
       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);
@@ -494,7 +613,7 @@ static int tr_tids_req_handler(TIDS_INSTANCE *tids,
       goto cleanup;
     }
     aaa_cookie[n_aaa]->mq=mq;
-    aaa_cookie[n_aaa]->aaa_hostname=tr_dup_name(this_aaa->hostname);
+    aaa_cookie[n_aaa]->aaa=this_aaa;
     aaa_cookie[n_aaa]->dh_params=tr_dh_dup(orig_req->tidc_dh);
     aaa_cookie[n_aaa]->fwd_req=tid_dup_req(fwd_req);
     talloc_steal(aaa_cookie[n_aaa], aaa_cookie[n_aaa]->fwd_req);
@@ -608,13 +727,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! */
@@ -656,7 +781,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);
@@ -664,19 +789,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.");
@@ -684,6 +818,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);
@@ -698,21 +838,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],
@@ -722,6 +862,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..e9a802b
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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 successfully 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;
+}
+
+/**
+ * Get the count of completed TID requests that resulted in error responses
+ */
+static MON_RC handle_show_req_error_count(void *cookie, json_t **response_ptr)
+{
+  TIDS_INSTANCE *tids = talloc_get_type_abort(cookie, TIDS_INSTANCE);
+  *response_ptr = json_integer(tids->req_error_count);
+  return (*response_ptr == NULL) ? MON_NOMEM : MON_SUCCESS;
+}
+
+/**
+ * Get the count of TID requests that could not be completed
+ */
+static MON_RC handle_show_error_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_REQS_PROCESSED,
+                        handle_show_req_count, tids);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQS_FAILED,
+                        handle_show_req_error_count, tids);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_ERROR_COUNT,
+                        handle_show_error_count, tids);
+  mons_register_handler(mons,
+                        MON_CMD_SHOW, OPT_TYPE_SHOW_TID_REQS_PENDING,
+                        handle_show_req_pending, tids);
+}
index 298299e..c525f54 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>
@@ -91,7 +93,7 @@ static TRP_RC tr_trps_msg_handler(TRPS_INSTANCE *trps,
   /* n.b., conn is available here, but do not hold onto the reference
    * because it may be cleaned up if the originating connection goes
    * down before the message is processed */
-  mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_MSG_RECEIVED, TR_MQ_PRIO_NORMAL);
+  mq_msg= tr_mq_msg_new(tmp_ctx, TR_MQMSG_MSG_RECEIVED);
   if (mq_msg==NULL) {
     return TRP_NOMEM;
   }
@@ -112,7 +114,7 @@ static int tr_trps_gss_handler(gss_name_t client_name, gss_buffer_t gss_name,
 
   tr_debug("tr_trps_gss_handler()");
 
-  if ((!client_name) || (!gss_name) || (!trps) || (!cfg_mgr)) {
+  if ((!client_name) || (!trps) || (!cfg_mgr)) {
     tr_debug("tr_trps_gss_handler: Bad parameters.");
     return -1;
   }
@@ -145,7 +147,7 @@ static void *tr_trps_thread(void *arg)
   if (trps_authorize_connection(trps, conn)!=TRP_SUCCESS)
     goto cleanup;
 
-  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_CONNECTED, TR_MQ_PRIO_HIGH);
+  msg= tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_CONNECTED);
   tr_mq_msg_set_payload(msg, (void *)tr_dup_name(trp_connection_get_peer(conn)), tr_free_name_helper);
   if (msg==NULL) {
     tr_err("tr_trps_thread: error allocating TR_MQ_MSG");
@@ -157,7 +159,7 @@ static void *tr_trps_thread(void *arg)
   trps_handle_connection(trps, conn);
 
 cleanup:
-  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_DISCONNECTED, TR_MQ_PRIO_HIGH);
+  msg= tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPS_DISCONNECTED);
   tr_mq_msg_set_payload(msg, (void *)conn, NULL); /* do not pass a free routine */
   if (msg==NULL)
     tr_err("tr_trps_thread: error allocating TR_MQ_MSG");
@@ -182,23 +184,24 @@ static void tr_trps_event_cb(int listener, short event, void *arg)
     tr_debug("tr_trps_event_cb: unexpected event on TRPS socket (event=0x%X)", event);
   } else {
     /* create a thread to handle this connection */
-    if (asprintf(&name, "trustrouter@%s", trps->hostname)==-1) {
+    name = talloc_asprintf(tmp_ctx, "trustrouter@%s", trps->hostname);
+    if (name == NULL)
       goto cleanup;
-    }
-    gssname=tr_new_name(name);
-    free(name); name=NULL;
-    conn=trp_connection_accept(tmp_ctx, listener, gssname);
-    if (conn!=NULL) {
+    gssname=tr_new_name(name); /* name cleaned up with tmp_ctx but need to handl gssname ourselves */
+
+    conn=trp_connection_accept(tmp_ctx, listener, gssname); /* steals gssname unless it fails */
+    if (conn == NULL) {
+      tr_free_name(gssname);
+    } else {
       /* need to monitor this fd and trigger events when read becomes possible */
       thread_data=talloc(conn, struct trps_thread_data);
       if (thread_data==NULL) {
         tr_err("tr_trps_event_cb: unable to allocate trps_thread_data");
-        talloc_free(tmp_ctx);
-        return;
+        goto cleanup;
       }
       thread_data->conn=conn;
       thread_data->trps=trps;
-      trps_add_connection(trps, conn); /* remember the connection */
+      trps_add_connection(trps, conn); /* remember the connection - this puts conn and the thread data in trps's talloc context */
       pthread_create(trp_connection_get_thread(conn), NULL, tr_trps_thread, thread_data);
     }
   }
@@ -266,60 +269,91 @@ static void tr_trps_process_mq(int socket, short event, void *arg)
   TRPS_INSTANCE *trps=talloc_get_type_abort(arg, TRPS_INSTANCE);
   TR_MQ_MSG *msg=NULL;
   const char *s=NULL;
+  TRP_PEER *peer = NULL;
+  char *tmp = NULL;
 
   msg=trps_mq_pop(trps);
   while (msg!=NULL) {
     s=tr_mq_msg_get_message(msg);
     if (0==strcmp(s, TR_MQMSG_TRPS_CONNECTED)) {
-      TR_NAME *gssname=(TR_NAME *)tr_mq_msg_get_payload(msg);
-      TRP_PEER *peer=trps_get_peer_by_gssname(trps, gssname);
-      if (peer==NULL)
-        tr_err("tr_trps_process_mq: incoming connection from unknown peer (%s) reported.", gssname->buf);
-      else {
-        trp_peer_set_incoming_status(peer, PEER_CONNECTED);
-        tr_err("tr_trps_process_mq: incoming connection from %s established.", gssname->buf);
+      TR_NAME *peer_gssname=(TR_NAME *)tr_mq_msg_get_payload(msg);
+      if (NULL == peer_gssname) {
+        /* This should not happen, we should not be able to establish a connection if we do not
+         * know their GSS name */
+        tr_err("tr_trps_process_mq: incoming connection from unknown GSS name reported.");
+      } else {
+        peer = trps_get_peer_by_gssname(trps, peer_gssname); /* get the peer record */
+        tmp = tr_name_strdup(peer_gssname); /* get the name as a null-terminated string */
+        if (peer == NULL)
+          tr_err("tr_trps_process_mq: incoming connection from unknown peer (%s) reported.", tmp);
+        else {
+          trp_peer_set_incoming_status(peer, PEER_CONNECTED);
+          tr_info("tr_trps_process_mq: incoming connection from %s established.", tmp);
+        }
+        free(tmp);
       }
     }
     else if (0==strcmp(s, TR_MQMSG_TRPS_DISCONNECTED)) {
       TRP_CONNECTION *conn=talloc_get_type_abort(tr_mq_msg_get_payload(msg), TRP_CONNECTION);
-      TR_NAME *gssname=trp_connection_get_gssname(conn);
-      TRP_PEER *peer=trps_get_peer_by_gssname(trps, gssname);
-      if (peer==NULL) {
-        tr_err("tr_trps_process_mq: incoming connection from unknown peer (%s) lost.",
-               trp_connection_get_gssname(conn)->buf);
+      TR_NAME *peer_gssname=trp_connection_get_peer(conn);
+
+      if (NULL == peer_gssname) {
+        /* If the GSS auth failed, then we don't know the peer's GSS name. */
+        tr_info("tr_trps_process_mq: incoming connection failed to auth.");
       } else {
-        trp_peer_set_incoming_status(peer, PEER_DISCONNECTED);
-        tr_trps_cleanup_conn(trps, conn);
-        tr_err("tr_trps_process_mq: incoming connection from %s lost.", gssname->buf);
+        /* We do know the peer's GSS name, see if we recognize it. */
+        peer = trps_get_peer_by_gssname(trps, peer_gssname); /* get the peer record */
+        tmp = tr_name_strdup(peer_gssname); /* get the name as a null-terminated string */
+        if (peer == NULL) {
+          tr_err("tr_trps_process_mq: incoming connection from unknown peer (%.*s) lost.", tmp);
+        } else {
+          trp_peer_set_incoming_status(peer, PEER_DISCONNECTED);
+          tr_trps_cleanup_conn(trps, conn);
+          tr_info("tr_trps_process_mq: incoming connection from %s lost.", tmp);
+        }
+        free(tmp);
       }
     }
     else if (0==strcmp(s, TR_MQMSG_TRPC_CONNECTED)) {
       TR_NAME *svcname=(TR_NAME *)tr_mq_msg_get_payload(msg);
-      TRP_PEER *peer=trps_get_peer_by_servicename(trps, svcname);
-      if (peer==NULL)
-        tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) reported.", svcname->buf);
-      else {
-        trp_peer_set_outgoing_status(peer, PEER_CONNECTED);
-        tr_err("tr_trps_process_mq: outgoing connection to %s established.", svcname->buf);
+      if (NULL == svcname) {
+        /* This should not happen because we shouldn't be reporting a connection unless we were
+         * able to auth the service name. */
+        tr_err("tr_trps_process_mq: outgoing connection established to unknown GSS service name.");
+      } else {
+        peer = trps_get_peer_by_servicename(trps, svcname);
+        tmp = tr_name_strdup(svcname);
+        if (peer == NULL)
+          tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) reported.", tmp);
+        else {
+          trp_peer_set_outgoing_status(peer, PEER_CONNECTED);
+          tr_info("tr_trps_process_mq: outgoing connection to %s established.", tmp);
+        }
+        free(tmp);
       }
     }
     else if (0==strcmp(s, TR_MQMSG_TRPC_DISCONNECTED)) {
-      /* trpc connection died */
       TRPC_INSTANCE *trpc=talloc_get_type_abort(tr_mq_msg_get_payload(msg), TRPC_INSTANCE);
-      TR_NAME *gssname=trpc_get_gssname(trpc);
-      TRP_PEER *peer=trps_get_peer_by_servicename(trps, gssname);
-      if (peer==NULL)
-        tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) lost.", gssname->buf);
-      else {
-        trp_peer_set_outgoing_status(peer, PEER_DISCONNECTED);
-        tr_err("tr_trps_process_mq: outgoing connection to %s lost.", gssname->buf);
-        tr_trps_cleanup_trpc(trps, trpc);
+      TR_NAME *svcname=trpc_get_gssname(trpc);
+      if (NULL == svcname) {
+        tr_info("tr_trps_process_mq: outgoing connection to unknown GSS service name lost.");
+      } else {
+        peer = trps_get_peer_by_servicename(trps, svcname);
+        tmp = tr_name_strdup(svcname);
+        if (peer == NULL)
+          tr_err("tr_trps_process_mq: outgoing connection to unknown peer (%s) lost.", tmp);
+        else {
+          trp_peer_set_outgoing_status(peer, PEER_DISCONNECTED);
+          tr_info("tr_trps_process_mq: outgoing connection to %s lost.", tmp);
+          tr_trps_cleanup_trpc(trps, trpc);
+        }
+        free(tmp);
       }
     }
 
     else if (0==strcmp(s, TR_MQMSG_MSG_RECEIVED)) {
       if (trps_handle_tr_msg(trps, tr_mq_msg_get_payload(msg))!=TRP_SUCCESS)
-        tr_notice("tr_trps_process_mq: error handling message.");
+        tr_err("tr_trps_process_mq: error handling message.");
     }
     else
       tr_notice("tr_trps_process_mq: unknown message '%s' received.", tr_mq_msg_get_message(msg));
@@ -548,29 +582,27 @@ cleanup:
   return retval;
 }
 
-
-struct trpc_notify_cb_data {
-  int msg_ready;
-  pthread_cond_t cond;
-  pthread_mutex_t mutex;
-};
-
-static void tr_trpc_mq_cb(TR_MQ *mq, void *arg)
-{
-  struct trpc_notify_cb_data *cb_data=(struct trpc_notify_cb_data *) arg;
-  pthread_mutex_lock(&(cb_data->mutex));
-  if (!cb_data->msg_ready) {
-    cb_data->msg_ready=1;
-    pthread_cond_signal(&(cb_data->cond));
-  }
-  pthread_mutex_unlock(&(cb_data->mutex));
-}
-
 /* data passed to thread */
 struct trpc_thread_data {
   TRPC_INSTANCE *trpc;
   TRPS_INSTANCE *trps;
 };
+
+/**
+ * Thread for handling TRPC (outgoing) connections
+ *
+ * Opens a connection to a peer. If successful, notifies the trps thread by
+ * posting a TR_MQMSG_TRPC_CONNECTED message to the trps message queue.
+ * It then waits for messages on trpc->mq. Normally these will be TR_MQMSG_TRPC_SEND
+ * messages, which this thread forwards to the peer. If its connection is lost or
+ * a TR_MQMSG_ABORT message is received on trpc->mq, the thread sends a
+ * TR_MQMSG_TRPC_DISCONNECTED message to the trps thread, then cleans up and
+ * terminates.
+ *
+ * The trps may continue queueing messages for this client even when the
+ * connection is down. To prevent the queue from growing endlessly, this thread
+ * should clear its queue after failed connection attempts.
+ */
 static void *tr_trpc_thread(void *arg)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
@@ -582,37 +614,30 @@ static void *tr_trpc_thread(void *arg)
   const char *msg_type=NULL;
   char *encoded_msg=NULL;
   TR_NAME *peer_gssname=NULL;
-  int n_sent=0;
+  struct timespec wait_until = {0};
   int exit_loop=0;
 
-  struct trpc_notify_cb_data cb_data={0,
-                                      PTHREAD_COND_INITIALIZER,
-                                      PTHREAD_MUTEX_INITIALIZER};
-
   tr_debug("tr_trpc_thread: started");
 
-  /* set up the mq for receiving */
-  pthread_mutex_lock(&(cb_data.mutex)); /* hold this lock until we enter the main loop */
-
-  tr_mq_lock(trpc->mq);
-  tr_mq_set_notify_cb(trpc->mq, tr_trpc_mq_cb, (void *) &cb_data);
-  tr_mq_unlock(trpc->mq);
-
+  /* Try to make the outgoing connection */
   rc=trpc_connect(trpc);
   if (rc!=TRP_SUCCESS) {
     tr_notice("tr_trpc_thread: failed to initiate connection to %s:%d.",
               trpc_get_server(trpc),
               trpc_get_port(trpc));
+    trpc_mq_clear(trpc); /* clear the queue even though we did not connect */
   } else {
+    /* Retrieve the GSS name used by the peer for authentication */
     peer_gssname=trp_connection_get_peer(trpc_get_conn(trpc));
     if (peer_gssname==NULL) {
       tr_err("tr_trpc_thread: could not duplicate peer_gssname.");
       talloc_free(tmp_ctx);
       return NULL;
     }
-    tr_debug("tr_trpc_thread: connected to peer %s", peer_gssname->buf);
+    tr_debug("tr_trpc_thread: connected to peer %.*s",
+             peer_gssname->len, peer_gssname->buf);
 
-    msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_CONNECTED, TR_MQ_PRIO_HIGH);
+    msg= tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_CONNECTED);
     tr_mq_msg_set_payload(msg, (void *)tr_dup_name(peer_gssname), tr_free_name_helper);
     if (msg==NULL) {
       tr_err("tr_trpc_thread: error allocating TR_MQ_MSG");
@@ -622,63 +647,77 @@ static void *tr_trpc_thread(void *arg)
     trps_mq_add(trps, msg); /* steals msg context */
     msg=NULL;
 
+    /* Loop until we get an abort message or until the connection is lost. */
     while(!exit_loop) {
-      cb_data.msg_ready=0;
-      pthread_cond_wait(&(cb_data.cond), &(cb_data.mutex));
-      /* verify the condition */
-      if (cb_data.msg_ready) {
-        for (msg=trpc_mq_pop(trpc),n_sent=0; msg!=NULL; msg=trpc_mq_pop(trpc),n_sent++) {
-          msg_type=tr_mq_msg_get_message(msg);
-
-          if (0==strcmp(msg_type, TR_MQMSG_ABORT)) {
-            exit_loop=1;
-            break;
-          }
-          else if (0==strcmp(msg_type, TR_MQMSG_TRPC_SEND)) {
-            encoded_msg=tr_mq_msg_get_payload(msg);
-            if (encoded_msg==NULL)
-              tr_notice("tr_trpc_thread: null outgoing TRP message.");
-            else {
-              rc = trpc_send_msg(trpc, encoded_msg);
-              if (rc!=TRP_SUCCESS) {
-                tr_notice("tr_trpc_thread: trpc_send_msg failed.");
-                exit_loop=1;
-                break;
-              }
+      /* Wait up to 10 minutes for a message to be queued to send to the peer.
+       * Log a warning if we go longer than that, but don't give up. */
+      if (tr_mq_pop_timeout(10 * 60, &wait_until) != 0) {
+        tr_err("tr_trpc_thread: unable to set abort timeout");
+        break; /* immediately exit the loop, don't go through cleanup */
+      }
+
+      /* Pop a message from the queue. */
+      msg = trpc_mq_pop(trpc, &wait_until);
+      if (msg) {
+        msg_type = tr_mq_msg_get_message(msg);
+        if (0 == strcmp(msg_type, TR_MQMSG_ABORT)) {
+          tr_debug("tr_trpc_thread: received abort message from main thread.");
+          exit_loop = 1;
+        } else if (0 == strcmp(msg_type, TR_MQMSG_TRPC_SEND)) {
+          encoded_msg = tr_mq_msg_get_payload(msg);
+          if (encoded_msg == NULL)
+            tr_notice("tr_trpc_thread: null outgoing TRP message.");
+          else {
+            rc = trpc_send_msg(trpc, encoded_msg);
+            if (rc == TRP_SUCCESS) {
+              tr_debug("tr_trpc_thread: sent message.");
+            } else {
+              tr_notice("tr_trpc_thread: trpc_send_msg failed.");
+              /* Assume this means we lost the connection. */
+              exit_loop = 1;
             }
           }
-          else
-            tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
+        } else
+          tr_notice("tr_trpc_thread: unknown message '%s' received.", msg_type);
 
-          tr_mq_msg_free(msg);
-        }
-        if (n_sent==0)
-          tr_err("tr_trpc_thread: notified of msg, but queue empty");
-        else 
-          tr_debug("tr_trpc_thread: sent %d messages.", n_sent);
+        tr_mq_msg_free(msg);
+      } else {
+        tr_warning("tr_trpc_thread: no outgoing messages to %.*s for 10 minutes",
+                   peer_gssname->len, peer_gssname->buf);
       }
     }
   }
 
-  tr_debug("tr_trpc_thread: exiting.");
-  msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_DISCONNECTED, TR_MQ_PRIO_HIGH);
+  /* Send a DISCONNECTED message to the main thread */
+  msg= tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_DISCONNECTED);
   tr_mq_msg_set_payload(msg, (void *)trpc, NULL); /* do not pass a free routine */
-  if (msg==NULL)
+  if (msg==NULL) {
+    /* can't notify main thread */
     tr_err("tr_trpc_thread: error allocating TR_MQ_MSG");
-  else
+  } else {
     trps_mq_add(trps, msg);
-
-  trpc_mq_clear(trpc); /* clear any queued messages */
+  }
 
   talloc_free(tmp_ctx);
+  tr_debug("tr_trpc_thread: thread terminating.");
   return NULL;
 }
 
-/* convert an IDP realm into routing table entries. Outputs number in *n_routes */
+/**
+ * convert an IDP realm into routing table entries.
+ *
+ * @param mem_ctx talloc context for the result
+ * @param realm IDP realm whose routes should be generated
+ * @param trust_router hostname for TRP connections to us
+ * @param trust_router_port TRP port of our trust router
+ * @param n_routes (output) the number of routes in the returned array
+ * @return Pointer to an array of pointers to routes
+ */
 static TRP_ROUTE **tr_make_local_routes(TALLOC_CTX *mem_ctx,
-                                         TR_IDP_REALM *realm,
-                                         char *trust_router,
-                                         size_t *n_routes)
+                                        TR_IDP_REALM *realm,
+                                        const char *trust_router,
+                                        int trust_router_port,
+                                        size_t *n_routes)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_APC *comm=NULL;
@@ -707,7 +746,9 @@ static TRP_ROUTE **tr_make_local_routes(TALLOC_CTX *mem_ctx,
     trp_route_set_peer(new_entry, tr_new_name("")); /* no peer, it's us */
     trp_route_set_metric(new_entry, 0);
     trp_route_set_trust_router(new_entry, tr_new_name(trust_router));
-    trp_route_set_next_hop(new_entry, tr_new_name(""));
+    trp_route_set_trust_router_port(new_entry, trust_router_port);
+    trp_route_set_next_hop(new_entry, tr_new_name("")); /* no next hop */
+    trp_route_set_next_hop_port(new_entry, -1); /* no next hop */
     trp_route_set_local(new_entry, 1);
     entries[ii]=new_entry;
   }
@@ -787,14 +828,9 @@ TRP_RC tr_add_local_routes(TRPS_INSTANCE *trps, TR_CFG *cfg)
   TRP_ROUTE **local_routes=NULL;
   size_t n_routes=0;
   size_t ii=0;
-  char *trust_router_name=talloc_asprintf(tmp_ctx, "%s:%d", cfg->internal->hostname, cfg->internal->trps_port);
-
-  /* determine our trust router name */
-  if (trust_router_name==NULL)
-    return TRP_NOMEM;
 
   for (cur=cfg->ctable->idp_realms; cur!=NULL; cur=cur->next) {
-    local_routes=tr_make_local_routes(tmp_ctx, cur, trust_router_name, &n_routes);
+    local_routes= tr_make_local_routes(tmp_ctx, cur, cfg->internal->hostname, cfg->internal->trps_port, &n_routes);
     for (ii=0; ii<n_routes; ii++)
       trps_add_route(trps, local_routes[ii]);
 
@@ -870,6 +906,24 @@ 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);
+  }
+  if (new_cfg->internal->monitoring_credentials != NULL) {
+    tr->mons->authorized_gss_names = tr_gss_names_dup(tr->mons, new_cfg->internal->monitoring_credentials);
+  } else {
+    tr->mons->authorized_gss_names = tr_gss_names_new(tr->mons);
+  }
+  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..fe5e660
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * 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>
+#include <tr_inet_util.h>
+
+
+/* command-line option setup */
+static void print_version_info(void)
+{
+  printf("Moonshot Trust Router 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 Trust Router Monitoring Client"
+    "\v" /* options list goes here */
+    "Supported monitoring commands:\n"
+    "\n"
+    "  show [<option> ...]\n"
+    "\n"
+    "     Show information about the Trust Router's current state.\n"
+    "\n"
+    "     Options:\n"
+    "       version            - current Trust Router software version\n"
+    "       config_files       - currently loaded configuration files\n"
+    "       uptime             - time, in seconds, since the Trust Router launched\n"
+    "       tid_reqs_processed - number of TID requests completed successfully\n"
+    "       tid_reqs_failed    - number of TID requests completed with errors\n"
+    "       tid_reqs_pending   - number of TID requests currently being processed\n"
+    "       tid_error_count    - number of unprocessable TID connections\n"
+    "       routes             - current TID routing table\n"
+    "       peers              - dynamic Trust Router peer table\n"
+    "       communities        - community table\n"
+    "       realms             - known realm table\n"
+    "       rp_clients         - authorized TID RP clients\n"
+    "\n"
+    "    If no options are specified, data for all options will be retrieved.\n";
+
+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;
+  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)
+{
+  int err = 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);
+
+    case ARGP_KEY_ARG: /* handle argument (not option) */
+      switch (state->arg_num) {
+        case 0:
+          arguments->server = arg;
+          break;
+
+        case 1:
+          arguments->port=tr_parse_port(arg); /* optional */
+          if (arguments->port < 0) {
+            switch(-(arguments->port)) {
+              case ERANGE:
+                printf("\nError parsing port (%s): port must be an integer in the range 1 - 65535\n\n", arg);
+                break;
+
+              default:
+                printf("\nError parsing port (%s): %s\n\n", arg, strerror(-arguments->port));
+                break;
+            }
+            argp_usage(state);
+          }
+          break;
+
+        case 2:
+          arguments->command=mon_cmd_from_string(arg);
+          if (arguments->command == MON_CMD_UNKNOWN) {
+            printf("\nUnknown command '%s'\n\n", arg);
+            err = 1;
+          }
+          break;
+
+        default:
+          if (arguments->n_options >= MAX_OPTIONS) {
+            printf("\nToo many command options given, limit is %d\n\n", MAX_OPTIONS);
+            err = 1;
+            break;
+          }
+
+          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\n", arg);
+            err = 1;
+          }
+          arguments->n_options++;
+          break;
+      }
+      break;
+
+    case ARGP_KEY_END: /* no more arguments */
+      if (state->arg_num < 3) {
+        /* not enough arguments encountered */
+        err = 1;
+      }
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+  }
+
+  if (err) {
+    argp_usage(state);
+    return EINVAL; /* argp_usage() usually does not return, but just in case */
+  }
+
+  return 0; /* success */
+}
+
+/* assemble the argp parser */
+static struct argp argp = {cmdline_options, parse_option, arg_doc, doc, 0};
+
+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 */
+  monc = monc_new(main_ctx);
+  if (monc == NULL) {
+    printf("Error allocating client instance.\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++) {
+    if (MON_SUCCESS != mon_req_add_option(req, opts.options[ii])) {
+      printf("Error adding option '%s' to request. Request not sent.\n",
+             mon_opt_type_to_string(opts.options[ii]));
+      goto cleanup;
+    }
+
+  }
+
+  /* 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 f04b4dd..db29417 100644 (file)
@@ -41,6 +41,7 @@
 #include <gsscon.h>
 #include <tr_debug.h>
 #include <tr_trp.h>
+#include <tr_inet_util.h>
 
 
 /* command-line option setup */
@@ -49,7 +50,7 @@
 const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
 
 /* doc strings */
-static const char doc[]=PACKAGE_NAME " - TRP Client";
+static const char doc[]=PACKAGE_NAME " - Moonshot Trust Router TRP Client";
 static const char arg_doc[]="<message> <server> [<port>]"; /* string describing arguments, if any */
 
 /* define the options here. Fields are:
@@ -92,7 +93,19 @@ static error_t parse_option(int key, char *arg, struct argp_state *state)
       break;
 
     case 2:
-      arguments->port=strtol(arg, NULL, 10); /* optional */
+      arguments->port=tr_parse_port(arg); /* optional */
+      if (arguments->port < 0) {
+        switch(-(arguments->port)) {
+          case ERANGE:
+            printf("\nError parsing port (%s): port must be an integer in the range 1 - 65535\n\n", arg);
+            break;
+
+          default:
+            printf("\nError parsing port (%s): %s\n\n", arg, strerror(-arguments->port));
+            break;
+        }
+        argp_usage(state);
+      }
       break;
 
     default:
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..05c3997 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
 
 struct peer_entry {
   char *server;
   char *gss_name;
-  unsigned int port;
+  int port;
   unsigned int linkcost;
 };
 
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);
index 64ddf3c..370d5b1 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <tr_debug.h>
 #include <trp_internal.h>
+#include <tr_socket.h>
 
 /* Threading note: mutex lock is only used for protecting get_status() and set_status().
  * If needed, locking for other operations (notably adding/removing connections) must be managed
@@ -338,31 +339,36 @@ int trp_connection_auth(TRP_CONNECTION *conn, TRP_AUTH_FUNC auth_callback, void
   return !auth;
 }
 
-/* Accept connection */
-TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gssname)
+/**
+ * Accept connection
+ *
+ * @param mem_ctx talloc context for return value
+ * @param listen socket fd for incoming connection
+ * @param gss_servicename our GSS service name to use for passive auth */
+TRP_CONNECTION *trp_connection_accept(TALLOC_CTX *mem_ctx, int listen, TR_NAME *gss_servicename)
 {
   int conn_fd=-1;
   TRP_CONNECTION *conn=NULL;
 
-  conn_fd = accept(listen, NULL, NULL);
+  conn_fd = tr_sock_accept(listen);
 
   if (0 > conn_fd) {
-    tr_notice("trp_connection_accept: accept() returned error.");
+    tr_notice("trp_connection_accept: Error accepting connection.");
     return NULL;
   }
   conn=trp_connection_new(mem_ctx);
   trp_connection_set_fd(conn, conn_fd);
-  trp_connection_set_gssname(conn, gssname);
+  trp_connection_set_gssname(conn, gss_servicename);
   trp_connection_set_status(conn, TRP_CONNECTION_AUTHORIZING);
   return conn;
 }
 
 /* Initiate connection */
-TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, unsigned int port)
+TRP_RC trp_connection_initiate(TRP_CONNECTION *conn, char *server, int port)
 {
   int err = 0;
   int fd=-1;
-  unsigned int use_port=0;
+  int use_port=0;
 
   if (0 == port)
     use_port = TRP_PORT;
diff --git a/trp/trp_peer.c b/trp/trp_peer.c
new file mode 100644 (file)
index 0000000..34d4296
--- /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);
+}
+
+int trp_peer_get_port(TRP_PEER *peer)
+{
+  return peer->port;
+}
+
+void trp_peer_set_port(TRP_PEER *peer, 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..552f440
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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>
+#include <tr_json_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, 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 = timespec_to_str(trp_peer_get_last_conn_attempt(peer));
+
+    if (s) {
+      jstr = json_string(s);
+      free(s);
+    }
+  }
+
+  return jstr;
+}
+
+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_SKIP(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..db2658c
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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 = NULL;
+
+  for (peer = trp_ptable_iter_first(iter, ptbl);
+       peer != NULL;
+       peer = trp_ptable_iter_next(iter)) {
+    json_array_append_new(ptbl_json, trp_peer_to_json(peer));
+  }
+  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..d552e01
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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>
+#include <tr_util.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->trust_router_port=TRP_PORT;
+    entry->next_hop_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;
+}
+
+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;
+}
+
+/**
+ * Get the expiration according to the realtime clock
+ *
+ * @param entry
+ * @param result space to store the result
+ * @return pointer to the result, or null on error
+ */
+struct timespec *trp_route_get_expiry_realtime(TRP_ROUTE *entry, struct timespec *result)
+{
+  return tr_clock_convert(TRP_CLOCK, entry->expiry, CLOCK_REALTIME, result);
+}
+
+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;
+}
+
+void trp_route_set_trust_router_port(TRP_ROUTE *entry, int port)
+{
+  if (entry)
+    entry->trust_router_port = port;
+}
+
+/**
+ * Get the port to use for TRP connections to the trust router
+ *
+ * @param entry
+ * @return port, or -1 if entry is null
+ */
+int trp_route_get_trust_router_port(TRP_ROUTE *entry)
+{
+  if (entry)
+    return entry->trust_router_port;
+
+  return -1;
+}
+
+void trp_route_set_next_hop_port(TRP_ROUTE *entry, int port)
+{
+  if (entry)
+    entry->next_hop_port = port;
+}
+
+/**
+ * Get the port to use for TID connections to the next hop
+ *
+ * @param entry
+ * @return port, or -1 if entry is null
+ */
+int trp_route_get_next_hop_port(TRP_ROUTE *entry)
+{
+  if (entry)
+    return entry->next_hop_port;
+
+  return -1;
+}
diff --git a/trp/trp_route_encoders.c b/trp/trp_route_encoders.c
new file mode 100644 (file)
index 0000000..ad913da
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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>
+#include <tr_json_util.h>
+#include <tr_inet_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:%d%s%s:%d%s%u%s%u%s%s%s%u",
+                         comm, sep,
+                         realm, sep,
+                         peer, sep,
+                         entry->metric, sep,
+                         trust_router, entry->trust_router_port, sep,
+                         next_hop, entry->next_hop_port, 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 = {0}; /* initialization to zero is important */
+  char *s = NULL;
+  json_t *jstr = NULL;
+
+  if (tr_cmp_timespec(trp_route_get_expiry(route), &ts) > 0) {
+    if (trp_route_get_expiry_realtime(route, &ts) == NULL)
+      s = strdup("error");
+    else
+      s = timespec_to_str(&ts);
+
+    if (s) {
+      jstr = json_string(s);
+      free(s);
+    }
+  }
+
+  return jstr;
+}
+
+json_t *trp_route_to_json(TRP_ROUTE *route)
+{
+  json_t *route_json = NULL;
+  json_t *retval = NULL;
+  TR_NAME *n;
+
+  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)));
+
+  /* add trust_router as hostname:port */
+  n = tr_hostname_and_port_to_name(
+          trp_route_get_trust_router(route),
+          trp_route_get_trust_router_port(route));
+  if (n == NULL)
+    goto cleanup;
+  OBJECT_SET_OR_FAIL(route_json, "trust_router", tr_name_to_json_string(n));
+  tr_free_name(n);
+
+  /* add next_hop as hostname:port */
+  n = tr_hostname_and_port_to_name(
+      trp_route_get_next_hop(route),
+      trp_route_get_next_hop_port(route));
+  if (n == NULL)
+    goto cleanup;
+  OBJECT_SET_OR_FAIL(route_json, "next_hop", tr_name_to_json_string(n));
+  tr_free_name(n);
+
+  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..9b915a2 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;
@@ -573,7 +371,9 @@ TRP_ROUTE **trp_rtable_get_comm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, size_t
 
 /* Get all entries in an comm/realm. Returns an array of pointers in NULL talloc context.
  * Caller must free this list with talloc_free, but must not free the entries in the
- * list.. */
+ * list.
+ *
+ * If *n_out is 0, then no memory is allocated and NULL is returned. */
 TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAME *realm, size_t *n_out)
 {
   size_t ii=0;
@@ -582,16 +382,23 @@ TRP_ROUTE **trp_rtable_get_realm_entries(TRP_RTABLE *rtbl, TR_NAME *comm, TR_NAM
 
   tr_debug("trp_rtable_get_realm_entries: entered.");
   peer=trp_rtable_get_comm_realm_peers(rtbl, comm, realm, n_out);
+  if ((peer == NULL) || (*n_out == 0)) {
+    *n_out = 0; /* May be redundant. That's ok, compilers are smart. */
+    goto cleanup;
+  }
+
   ret=talloc_array(NULL, TRP_ROUTE *, *n_out);
   if (ret==NULL) {
     tr_crit("trp_rtable_get_realm_entries: could not allocate return array.");
-    talloc_free(peer);
     n_out=0;
-    return NULL;
+    goto cleanup;
   }
   for (ii=0; ii<*n_out; ii++)
     ret[ii]=trp_rtable_get_entry(rtbl, comm, realm, peer[ii]);
-  talloc_free(peer);
+
+cleanup:
+  if (peer)
+    talloc_free(peer);
   return ret;
 }
 
@@ -632,25 +439,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 +463,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 +475,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 e1057f1..7feb777 100644 (file)
@@ -211,27 +211,40 @@ void trp_inforec_set_type(TRP_INFOREC *rec, TRP_INFOREC_TYPE type)
 TR_NAME *trp_inforec_get_trust_router(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data->route!=NULL)
-      return rec->data->route->trust_router;
-    break;
-  default:
-    break;
+    case TRP_INFOREC_TYPE_ROUTE:
+      if (rec->data->route!=NULL)
+        return rec->data->route->trust_router;
+      break;
+    default:
+      break;
   }
   return NULL;
 }
 
+int trp_inforec_get_trust_router_port(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+    case TRP_INFOREC_TYPE_ROUTE:
+      if (rec->data->route!=NULL)
+        return rec->data->route->trust_router_port;
+      /* fall through */
+    default:
+      return -1;
+  }
+}
+
 TR_NAME *trp_inforec_dup_trust_router(TRP_INFOREC *rec)
 {
   return tr_dup_name(trp_inforec_get_trust_router(rec));
 }
 
-TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router)
+TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router, int port)
 {
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
     if (rec->data->route!=NULL) {
       rec->data->route->trust_router=trust_router;
+      rec->data->route->trust_router_port = port;
       return TRP_SUCCESS;
     }
     break;
@@ -241,16 +254,15 @@ TRP_RC trp_inforec_set_trust_router(TRP_INFOREC *rec, TR_NAME *trust_router)
   return TRP_ERROR;
 }
 
-/* TODO: need to return hostname/port --jlr */
 TR_NAME *trp_inforec_get_next_hop(TRP_INFOREC *rec)
 {
   switch (rec->type) {
-  case TRP_INFOREC_TYPE_ROUTE:
-    if (rec->data->route!=NULL)
-      return rec->data->route->next_hop;
-    break;
-  default:
-    break;
+    case TRP_INFOREC_TYPE_ROUTE:
+      if (rec->data->route!=NULL)
+        return rec->data->route->next_hop;
+      break;
+    default:
+      break;
   }
   return NULL;
 }
@@ -260,23 +272,48 @@ TR_NAME *trp_inforec_dup_next_hop(TRP_INFOREC *rec)
   return tr_dup_name(trp_inforec_get_next_hop(rec));
 }
 
-TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop)
+/**
+ * Set the next hop for the inforec
+ *
+ * Returns TRP_SUCCESS if it set the next hop value for the inforec.
+ * Returns TRP_UNSUPPORTED if the inforec does not have a next hop record but
+ * otherwise nothing went wrong.
+ * Returns TRP_ERROR or another error if there was a failure.
+ *
+ * @param rec
+ * @param next_hop
+ * @return TRP_SUCCESS if the value was set, TRP_UNSUPPORTED if the inforec does not support next hop, or an error code on failure
+ */
+TRP_RC trp_inforec_set_next_hop(TRP_INFOREC *rec, TR_NAME *next_hop, int port)
 {
+  /* Any inforec types that support next_hop should set it here. */
   switch (rec->type) {
   case TRP_INFOREC_TYPE_ROUTE:
     if (rec->data->route==NULL)
       return TRP_ERROR;
-    rec->data->route->next_hop=next_hop;
-    break;
-  case TRP_INFOREC_TYPE_COMMUNITY:
-    /* next hop not used for community records */
+    rec->data->route->next_hop = next_hop;
+    rec->data->route->next_hop_port = port;
     break;
+
   default:
-    break;
+    /* next hop not used for other records */
+    return TRP_UNSUPPORTED;
   }
   return TRP_SUCCESS;
 }
 
+int trp_inforec_get_next_hop_port(TRP_INFOREC *rec)
+{
+  switch (rec->type) {
+    case TRP_INFOREC_TYPE_ROUTE:
+      if (rec->data->route!=NULL)
+        return rec->data->route->next_hop_port;
+      /* fall through */
+    default:
+      return -1;
+  }
+}
+
 unsigned int trp_inforec_get_metric(TRP_INFOREC *rec)
 {
   switch (rec->type) {
@@ -766,15 +803,26 @@ void trp_upd_set_peer(TRP_UPD *upd, TR_NAME *peer)
   upd->peer=peer;
 }
 
-void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, unsigned int port)
+void trp_upd_set_next_hop(TRP_UPD *upd, const char *hostname, int port)
 {
   TRP_INFOREC *rec=NULL;
   TR_NAME *cpy=NULL;
-  
+
   for (rec=trp_upd_get_inforec(upd); rec!=NULL; rec=trp_inforec_get_next(rec)) {
-    if (trp_inforec_set_next_hop(rec, cpy=tr_new_name(hostname)) != TRP_SUCCESS) {
-      tr_err("trp_upd_set_next_hop: error setting next hop.");
-      tr_free_name(cpy);
+    switch (trp_inforec_set_next_hop(rec, cpy=tr_new_name(hostname), port)) {
+      case TRP_SUCCESS:
+        /* Success, the TR_NAME in cpy is now stored with the inforec */
+        break;
+
+      case TRP_UNSUPPORTED:
+        /* No error, but the inforec does not accept a next_hop. Free our copy. */
+        tr_free_name(cpy);
+        break;
+
+      default:
+        tr_err("trp_upd_set_next_hop: error setting next hop.");
+        tr_free_name(cpy);
+        break;
     }
   }
 }
index b6a2cea..04a8ca2 100644 (file)
@@ -153,7 +153,7 @@ unsigned int trpc_get_port(TRPC_INSTANCE *trpc)
   return trpc->port;
 }
 
-void trpc_set_port(TRPC_INSTANCE *trpc, unsigned int port)
+void trpc_set_port(TRPC_INSTANCE *trpc, int port)
 {
   trpc->port=port;
 }
@@ -189,9 +189,9 @@ void trpc_mq_add(TRPC_INSTANCE *trpc, TR_MQ_MSG *msg)
   tr_mq_add(trpc->mq, msg);
 }
 
-TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc)
+TR_MQ_MSG *trpc_mq_pop(TRPC_INSTANCE *trpc, struct timespec *ts_abort)
 {
-  return tr_mq_pop(trpc->mq, 0);
+  return tr_mq_pop(trpc->mq, ts_abort);
 }
 
 void trpc_mq_clear(TRPC_INSTANCE *trpc)
@@ -216,7 +216,7 @@ TRP_RC trpc_send_msg (TRPC_INSTANCE *trpc,
                                          *trp_connection_get_gssctx(trpc_get_conn(trpc)),
                                          msg_content, 
                                          strlen(msg_content))) {
-    tr_err( "trpc_send_msg: Error sending message over connection.\n");
+    tr_err( "trpc_send_msg: Error sending message over connection.");
     rc=TRP_ERROR;
   }
   return rc;
index ebc365d..b4051e1 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)
 {
@@ -65,7 +69,7 @@ TRPS_INSTANCE *trps_new (TALLOC_CTX *mem_ctx)
   TRPS_INSTANCE *trps=talloc(mem_ctx, TRPS_INSTANCE);
   if (trps!=NULL)  {
     trps->hostname=NULL;
-    trps->port=0;
+    trps->trps_port=0;
     trps->cookie=NULL;
     trps->conn=NULL;
     trps->trpc=NULL;
@@ -192,7 +196,7 @@ TR_NAME *trps_dup_label(TRPS_INSTANCE *trps)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_NAME *label=NULL;
-  char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->port);
+  char *s=talloc_asprintf(tmp_ctx, "%s:%u", trps->hostname, trps->trps_port);
   if (s==NULL)
     goto cleanup;
   label=tr_new_name(s);
@@ -261,12 +265,15 @@ TRP_RC trps_send_msg(TRPS_INSTANCE *trps, TRP_PEER *peer, const char *msg)
 
   /* get the connection for this peer */
   trpc=trps_find_trpc(trps, peer);
-  /* instead, let's let that happen and then clear the queue when an attempt to
-   * connect fails */
+  /* The peer connection (trpc) usually exists even if the connection is down.
+   * We will queue messages even if the connection is down. To prevent this from
+   * endlessly increasing the size of the queue, the trpc handler needs to clear
+   * its queue periodically, even if it is unable to send the messages
+   */
   if (trpc==NULL) {
     tr_warning("trps_send_msg: skipping message queued for missing TRP client entry.");
   } else {
-    mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND, TR_MQ_PRIO_NORMAL);
+    mq_msg=tr_mq_msg_new(tmp_ctx, TR_MQMSG_TRPC_SEND);
     msg_dup=talloc_strdup(mq_msg, msg); /* get local copy in mq_msg context */
     tr_mq_msg_set_payload(mq_msg, msg_dup, NULL); /* no need for a free() func */
     trpc_mq_add(trpc, mq_msg);
@@ -276,82 +283,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 +341,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;
@@ -431,7 +362,6 @@ static TRP_RC trps_read_message(TRPS_INSTANCE *trps, TRP_CONNECTION *conn, TR_MS
   switch (tr_msg_get_msg_type(*msg)) {
   case TRP_UPDATE:
     trp_upd_set_peer(tr_msg_get_trp_upd(*msg), tr_dup_name(conn_peer));
-    trp_upd_set_next_hop(tr_msg_get_trp_upd(*msg), trp_peer_get_server(peer), 0); /* TODO: 0 should be the configured TID port */
     /* update provenance if necessary */
     trp_upd_add_to_provenance(tr_msg_get_trp_upd(*msg), trp_peer_get_label(peer));
     break;
@@ -454,17 +384,18 @@ int trps_get_listener(TRPS_INSTANCE *trps,
                       TRPS_MSG_FUNC msg_handler,
                       TRP_AUTH_FUNC auth_handler,
                       const char *hostname,
-                      unsigned int port,
+                      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;
+
+  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)
-    tr_err("trps_get_listener: Error opening port %d.");
+  if (n_fd == 0)
+    tr_err("trps_get_listener: Error opening port %d.", port);
   else {
     /* opening port succeeded */
     tr_info("trps_get_listener: Opened port %d.", port);
@@ -477,33 +408,33 @@ 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;
     trps->hostname = talloc_strdup(trps, hostname);
-    trps->port = port;
+    trps->trps_port = port;
     trps->cookie = cookie;
   }
 
-  return n_fd;
+  return (int) n_fd;
 }
 
 TRP_RC trps_authorize_connection(TRPS_INSTANCE *trps, TRP_CONNECTION *conn)
 {
   /* try to establish a GSS context */
   if (0!=trp_connection_auth(conn, trps->auth_handler, trps->cookie)) {
-    tr_notice("trps_authorize_connection: failed to authorize connection");
+    tr_debug("trps_authorize_connection: failed to authorize connection");
     trp_connection_close(conn);
     return TRP_ERROR;
   }
-  tr_notice("trps_authorize_connection: authorized connection");
+  tr_debug("trps_authorize_connection: authorized connection");
   return TRP_SUCCESS;
 }
 
@@ -570,12 +501,27 @@ static TRP_RC trps_validate_inforec(TRPS_INSTANCE *trps, TRP_INFOREC *rec)
   switch(trp_inforec_get_type(rec)) {
   case TRP_INFOREC_TYPE_ROUTE:
     if ((trp_inforec_get_trust_router(rec)==NULL)
-       || (trp_inforec_get_next_hop(rec)==NULL)) {
+        || (trp_inforec_get_next_hop(rec)==NULL)) {
       tr_debug("trps_validate_inforec: missing record info.");
       return TRP_ERROR;
     }
 
-    /* check for valid metric */
+    /* check for valid ports */
+    if ((trp_inforec_get_trust_router_port(rec) <= 0)
+        || (trp_inforec_get_trust_router_port(rec) > 65535)) {
+      tr_debug("trps_validate_inforec: invalid trust router port (%d)",
+               trp_inforec_get_trust_router_port(rec));
+      return TRP_ERROR;
+    }
+
+      if ((trp_inforec_get_next_hop_port(rec) <= 0)
+          || (trp_inforec_get_next_hop_port(rec) > 65535)) {
+        tr_debug("trps_validate_inforec: invalid next hop port (%d)",
+                 trp_inforec_get_next_hop_port(rec));
+        return TRP_ERROR;
+      }
+
+      /* check for valid metric */
     if (trp_metric_is_invalid(trp_inforec_get_metric(rec))) {
       tr_debug("trps_validate_inforec: invalid metric (%u).", trp_inforec_get_metric(rec));
       return TRP_ERROR;
@@ -661,6 +607,17 @@ static struct timespec *trps_compute_expiry(TRPS_INSTANCE *trps, unsigned int in
   return ts;
 }
 
+
+/* compare hostname/port of the trust router, return 0 if they match */
+static int trust_router_changed(TRP_ROUTE *route, TRP_INFOREC *rec)
+{
+  if (trp_route_get_trust_router_port(route) != trp_inforec_get_trust_router_port(rec))
+    return 1;
+
+  return tr_name_cmp(trp_route_get_trust_router(route),
+                     trp_inforec_get_trust_router(rec));
+}
+
 static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
 {
   TRP_ROUTE *entry=NULL;
@@ -680,8 +637,9 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
     trp_route_set_realm(entry, trp_upd_dup_realm(upd));
     trp_route_set_peer(entry, trp_upd_dup_peer(upd));
     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec));
+    trp_route_set_trust_router_port(entry, trp_inforec_get_trust_router_port(rec));
     trp_route_set_next_hop(entry, trp_inforec_dup_next_hop(rec));
-    /* TODO: pass next hop port (now defaults to TID_PORT) --jlr */
+    trp_route_set_next_hop_port(entry, trp_inforec_get_next_hop_port(rec));
     if ((trp_route_get_comm(entry)==NULL)
        ||(trp_route_get_realm(entry)==NULL)
        ||(trp_route_get_peer(entry)==NULL)
@@ -703,13 +661,13 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
   trp_route_set_metric(entry, trp_inforec_get_metric(rec));
   trp_route_set_interval(entry, trp_inforec_get_interval(rec));
 
-  /* check whether the trust router has changed */
-  if (0!=tr_name_cmp(trp_route_get_trust_router(entry),
-                     trp_inforec_get_trust_router(rec))) {
+  /* check whether the trust router has changed (either name or port) */
+  if (trust_router_changed(entry, rec)) {
     /* The name changed. Set this route as triggered. */
     tr_debug("trps_accept_update: trust router for route changed.");
     trp_route_set_triggered(entry, 1);
     trp_route_set_trust_router(entry, trp_inforec_dup_trust_router(rec)); /* frees old name */
+    trp_route_set_trust_router_port(entry, trp_inforec_get_trust_router_port(rec));
   }
   if (!trps_route_retracted(trps, entry)) {
     tr_debug("trps_accept_update: route not retracted, setting expiry timer.");
@@ -724,36 +682,50 @@ static TRP_RC trps_accept_update(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC
 static TRP_RC trps_handle_inforec_route(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_INFOREC *rec)
 {
   TRP_ROUTE *route=NULL;
+  TR_COMM *comm = NULL;
   unsigned int feas=0;
 
   /* determine feasibility */
   feas=trps_check_feasibility(trps, trp_upd_get_realm(upd), trp_upd_get_comm(upd), rec);
   tr_debug("trps_handle_update: record feasibility=%d", feas);
 
-  /* do we have an existing route? */
-  route=trps_get_route(trps,
-                       trp_upd_get_comm(upd),
-                       trp_upd_get_realm(upd),
-                       trp_upd_get_peer(upd));
-  if (route!=NULL) {
-    /* there was a route table entry already */
-    tr_debug("trps_handle_updates: route entry already exists.");
-    if (feas) {
-      /* Update is feasible. Accept it. */
-      trps_accept_update(trps, upd, rec);
-    } else {
-      /* Update is infeasible. Ignore it unless the trust router has changed. */
-      if (0!=tr_name_cmp(trp_route_get_trust_router(route),
-                         trp_inforec_get_trust_router(rec))) {
-        /* the trust router associated with the route has changed, treat update as a retraction */
-        trps_retract_route(trps, route);
+  /* verify that the community is an APC */
+  comm = tr_comm_table_find_comm(trps->ctable, trp_upd_get_comm(upd));
+  if (comm == NULL) {
+    /* We don't know this community. Reject the route. */
+    tr_debug("trps_handle_updates: community %.*s unknown, ignoring route for %.*s",
+             trp_upd_get_comm(upd)->len, trp_upd_get_comm(upd)->buf,
+             trp_upd_get_realm(upd)->len, trp_upd_get_realm(upd)->buf);
+  } else if (tr_comm_get_type(comm) != TR_COMM_APC) {
+    /* The community in a route request *must* be an APC. This was not - ignore it. */
+    tr_debug("trps_handle_updates: community %.*s is not an APC, ignoring route for %.*s",
+             trp_upd_get_comm(upd)->len, trp_upd_get_comm(upd)->buf,
+             trp_upd_get_realm(upd)->len, trp_upd_get_realm(upd)->buf);
+  } else {
+    /* do we have an existing route? */
+    route=trps_get_route(trps,
+                         trp_upd_get_comm(upd),
+                         trp_upd_get_realm(upd),
+                         trp_upd_get_peer(upd));
+    if (route!=NULL) {
+      /* there was a route table entry already */
+      tr_debug("trps_handle_updates: route entry already exists.");
+      if (feas) {
+        /* Update is feasible. Accept it. */
+        trps_accept_update(trps, upd, rec);
+      } else {
+        /* Update is infeasible. Ignore it unless the trust router has changed. */
+        if (trust_router_changed(route, rec)) {
+          /* the trust router associated with the route has changed, treat update as a retraction */
+          trps_retract_route(trps, route);
+        }
       }
+    } else {
+      /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
+      tr_debug("trps_handle_update: no route entry exists yet.");
+      if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
+        trps_accept_update(trps, upd, rec);
     }
-  } else {
-    /* No existing route table entry. Ignore it unless it is feasible and not a retraction. */
-    tr_debug("trps_handle_update: no route entry exists yet.");
-    if (feas && trp_metric_is_finite(trp_inforec_get_metric(rec)))
-      trps_accept_update(trps, upd, rec);
   }
 
   return TRP_SUCCESS;
@@ -839,7 +811,7 @@ cleanup:
   return comm;
 }
 
-static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
+static TR_RP_REALM *trps_create_new_rp_realm(TALLOC_CTX *mem_ctx, TR_NAME *comm, TR_NAME *realm_id, TRP_INFOREC *rec)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_RP_REALM *rp=tr_rp_realm_new(tmp_ctx);
@@ -862,11 +834,15 @@ cleanup:
   return rp;
 }
 
-static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx, TR_NAME *realm_id, TRP_INFOREC *rec)
+static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx,
+                                               TR_NAME *comm_id,
+                                               TR_NAME *realm_id,
+                                               TRP_INFOREC *rec)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TR_IDP_REALM *idp=tr_idp_realm_new(tmp_ctx);
-  
+  TR_APC *realm_apcs = NULL;
+
   if (idp==NULL) {
     tr_debug("trps_create_new_idp_realm: unable to allocate new realm.");
     goto cleanup;
@@ -878,14 +854,52 @@ static TR_IDP_REALM *trps_create_new_idp_realm(TALLOC_CTX *mem_ctx, TR_NAME *rea
     idp=NULL;
     goto cleanup;
   }
-  if (trp_inforec_get_apcs(rec)!=NULL) {
-    tr_idp_realm_set_apcs(idp, tr_apc_dup(tmp_ctx, trp_inforec_get_apcs(rec)));
-    if (tr_idp_realm_get_apcs(idp)==NULL) {
-      tr_debug("trps_create_new_idp_realm: unable to allocate APC list.");
-      idp=NULL;
+
+  /* Set the APCs. If the community is a CoI, copy its APCs. If it is an APC, then
+   * that community itself is the APC for the realm. */
+  if (trp_inforec_get_comm_type(rec) == TR_COMM_APC) {
+    /* the community is an APC for this realm */
+    realm_apcs = tr_apc_new(tmp_ctx);
+    if (realm_apcs == NULL) {
+      tr_debug("trps_create_new_idp_realm: unable to allocate new APC list.");
+      idp = NULL;
+      goto cleanup;
+    }
+
+    tr_apc_set_id(realm_apcs, tr_dup_name(comm_id));
+    if (tr_apc_get_id(realm_apcs) == NULL) {
+      tr_debug("trps_create_new_idp_realm: unable to allocate new APC name.");
+      idp = NULL;
+      goto cleanup;
+    }
+  } else {
+    /* the community is not an APC for this realm */
+    realm_apcs = trp_inforec_get_apcs(rec);
+    if (realm_apcs == NULL) {
+      tr_debug("trps_create_new_idp_realm: no APCs for realm %.*s/%.*s, cannot add.",
+               realm_id->len, realm_id->buf,
+               comm_id->len, comm_id->buf);
+      idp = NULL;
+      goto cleanup;
+    }
+
+    /* we have APCs, make our own copy */
+    realm_apcs = tr_apc_dup(tmp_ctx, realm_apcs);
+    if (realm_apcs == NULL) {
+      tr_debug("trps_create_new_idp_realm: unable to duplicate APC list.");
+      idp = NULL;
       goto cleanup;
     }
   }
+
+  /* Whether the community is an APC or CoI, the APCs for the realm are in realm_apcs */
+  tr_idp_realm_set_apcs(idp, realm_apcs); /* takes realm_apcs out of tmp_ctx on success */
+  if (tr_idp_realm_get_apcs(idp) == NULL) {
+    tr_debug("trps_create_new_idp_realm: unable to set APC list for new realm.");
+    idp=NULL;
+    goto cleanup;
+  }
+
   idp->origin=TR_REALM_DISCOVERED;
   
   talloc_steal(mem_ctx, idp);
@@ -935,7 +949,11 @@ static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_IN
         tr_debug("trps_handle_inforec_comm: unable to create new community.");
         goto cleanup;
       }
-      tr_comm_table_add_comm(trps->ctable, comm);
+      if (tr_comm_table_add_comm(trps->ctable, comm) != 0)
+      {
+        tr_debug("trps_handle_inforec_comm: unable to add community to community table.");
+        goto cleanup;
+      }
     }
     /* TODO: see if other comm data match the new inforec and update or complain */
 
@@ -949,7 +967,7 @@ static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_IN
       if (rp_realm==NULL) {
         tr_debug("trps_handle_inforec_comm: unknown RP realm %.*s in inforec, creating it.",
                  realm_id->len, realm_id->buf);
-        rp_realm=trps_create_new_rp_realm(tmp_ctx, realm_id, rec);
+        rp_realm= trps_create_new_rp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
         if (rp_realm==NULL) {
           tr_debug("trps_handle_inforec_comm: unable to create new RP realm.");
           /* we may leave an unused community in the table, but it will only last until
@@ -970,7 +988,7 @@ static TRP_RC trps_handle_inforec_comm(TRPS_INSTANCE *trps, TRP_UPD *upd, TRP_IN
       if (idp_realm==NULL) {
         tr_debug("trps_handle_inforec_comm: unknown IDP realm %.*s in inforec, creating it.",
                  realm_id->len, realm_id->buf);
-        idp_realm=trps_create_new_idp_realm(tmp_ctx, realm_id, rec);
+        idp_realm= trps_create_new_idp_realm(tmp_ctx, tr_comm_get_id(comm), realm_id, rec);
         if (idp_realm==NULL) {
           tr_debug("trps_handle_inforec_comm: unable to create new IDP realm.");
           /* we may leave an unused community in the table, but it will only last until
@@ -1123,12 +1141,13 @@ static TRP_RC trps_validate_request(TRPS_INSTANCE *trps, TRP_REQ *req)
 
 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
 static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
-                                        TR_NAME *comm,
-                                        TR_NAME *realm,
-                                        TR_NAME *exclude_peer)
+                                       TR_NAME *comm,
+                                       TR_NAME *realm,
+                                       TR_NAME *exclude_peer_label)
 {
   TRP_ROUTE **entry=NULL;
   TRP_ROUTE *best=NULL;
+  TRP_PEER *route_peer = NULL;
   size_t n_entry=0;
   unsigned int kk=0;
   unsigned int kk_min=0;
@@ -1137,13 +1156,31 @@ static TRP_ROUTE *trps_find_best_route(TRPS_INSTANCE *trps,
   entry=trp_rtable_get_realm_entries(trps->rtable, comm, realm, &n_entry);
   for (kk=0; kk<n_entry; kk++) {
     if (trp_route_get_metric(entry[kk]) < min_metric) {
-      if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_route_get_peer(entry[kk]),
-                                                  exclude_peer))) {
-        kk_min=kk;
-        min_metric=trp_route_get_metric(entry[kk]);
-      } 
+      if (exclude_peer_label != NULL) {
+        if (!trp_route_is_local(entry[kk])) {
+          /* route is not local, check the peer label */
+          route_peer = trp_ptable_find_gss_name(trps->ptable,
+                                                trp_route_get_peer(entry[kk]));
+          if (route_peer == NULL) {
+            tr_err("trps_find_best_route: unknown peer GSS name (%.*s) for route %d to %.*s/%.*s",
+                   trp_route_get_peer(entry[kk])->len, trp_route_get_peer(entry[kk])->buf,
+                   kk,
+                   realm->len, realm->buf,
+                   comm->len, comm->buf);
+            continue; /* unknown peer, skip the route */
+          }
+          if (0 == tr_name_cmp(exclude_peer_label, trp_peer_get_label(route_peer))) {
+            /* we're excluding this peer - skip the route */
+            continue;
+          }
+        }
+      }
+      /* if we get here, we're not excluding the route */
+      kk_min = kk;
+      min_metric = trp_route_get_metric(entry[kk]);
     }
   }
+
   if (trp_metric_is_finite(min_metric))
     best=entry[kk_min];
   
@@ -1218,7 +1255,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,31 +1282,12 @@ 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)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   struct timespec sweep_time={0,0};
+  struct timespec tmp = {0};
   TR_COMM_MEMB *memb=NULL;
   TR_COMM_ITER *iter=NULL;
   TRP_RC rc=TRP_ERROR;
@@ -1302,7 +1320,7 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf,
-                 timespec_to_str(tr_comm_memb_get_expiry(memb)));
+                 timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)));
         tr_comm_table_remove_memb(trps->ctable, memb);
         tr_comm_memb_free(memb);
       } else {
@@ -1310,8 +1328,8 @@ TRP_RC trps_sweep_ctable(TRPS_INSTANCE *trps)
         tr_comm_memb_expire(memb);
         trps_compute_expiry(trps, tr_comm_memb_get_interval(memb), tr_comm_memb_get_expiry(memb));
         tr_debug("trps_sweep_ctable: community membership expired at %s, resetting expiry to %s (%.*s in %.*s, origin %.*s).",
-                 timespec_to_str(&sweep_time),
-                 timespec_to_str(tr_comm_memb_get_expiry(memb)),
+                 timespec_to_str(tr_clock_convert(TRP_CLOCK, &sweep_time, CLOCK_REALTIME, &tmp)),
+                 timespec_to_str(tr_comm_memb_get_expiry_realtime(memb, &tmp)),
                  tr_comm_memb_get_realm_id(memb)->len, tr_comm_memb_get_realm_id(memb)->buf,
                  tr_comm_get_id(tr_comm_memb_get_comm(memb))->len, tr_comm_get_id(tr_comm_memb_get_comm(memb))->buf,
                  tr_comm_memb_get_origin(memb)->len, tr_comm_memb_get_origin(memb)->buf);
@@ -1356,13 +1374,23 @@ static TRP_INFOREC *trps_route_to_inforec(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *tr
                                                               trp_route_get_peer(route)));
     }
 
-    /* Note that we leave the next hop empty since the recipient fills that in.
-     * This is where we add the link cost (currently always 1) to the next peer. */
-    if ((trp_inforec_set_trust_router(rec, trp_route_dup_trust_router(route)) != TRP_SUCCESS)
-       ||(trp_inforec_set_metric(rec,
-                                 trps_metric_add(trp_route_get_metric(route),
-                                                 linkcost)) != TRP_SUCCESS)
-       ||(trp_inforec_set_interval(rec, trps_get_update_interval(trps)) != TRP_SUCCESS)) {
+    /*
+     * This is where we add the link cost (currently always 1) to the next peer.
+     *
+     * Here, set next_hop to our TID address/port rather than passing along our own
+     * next_hop. That is the one *we* use to forward requests. We are advertising
+     * ourselves as a hop for our peers.
+     */
+    if ((TRP_SUCCESS != trp_inforec_set_trust_router(rec,
+                                                     trp_route_dup_trust_router(route),
+                                                     trp_route_get_trust_router_port(route)))
+        ||(TRP_SUCCESS != trp_inforec_set_next_hop(rec,
+                                                   tr_new_name(trps->hostname),
+                                                   trps->tids_port))
+        ||(TRP_SUCCESS != trp_inforec_set_metric(rec,
+                                                 trps_metric_add(trp_route_get_metric(route),
+                                                                 linkcost)))
+        ||(TRP_SUCCESS != trp_inforec_set_interval(rec, trps_get_update_interval(trps)))) {
       tr_err("trps_route_to_inforec: error creating route update.");
       talloc_free(rec);
       rec=NULL;
@@ -1412,26 +1440,67 @@ cleanup:
 }
 
 /* select the correct route to comm/realm to be announced to peer */
-static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
+static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_label)
 {
-  TRP_ROUTE *route;
+  TRP_ROUTE *route = NULL;
+  TRP_PEER *route_peer = NULL;
+  TR_NAME *route_peer_label = NULL;
 
   /* Take the currently selected route unless it is through the peer we're sending the update to.
-   * I.e., enforce the split horizon rule. */
+   * I.e., enforce the split horizon rule. Start by looking up the currently selected route. */
   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
   if (route==NULL) {
     /* No selected route, this should only happen if the only route has been retracted,
      * in which case we do not want to advertise it. */
     return NULL;
   }
-  tr_debug("trps_select_realm_update: %s vs %s", peer_gssname->buf,
-           trp_route_get_peer(route)->buf);
-  if (0==tr_name_cmp(peer_gssname, trp_route_get_peer(route))) {
-    tr_debug("trps_select_realm_update: matched, finding alternate route");
-    /* the selected entry goes through the peer we're reporting to, choose an alternate */
-    route=trps_find_best_route(trps, comm, realm, peer_gssname);
-    if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route))))
-      return NULL; /* don't advertise a nonexistent or retracted route */
+
+  /* Check whether it's local. */
+  if (trp_route_is_local(route)) {
+    /* It is always ok to announce a local route */
+    tr_debug("trps_select_realm_update: selected route for %.*s/%.*s is local",
+             realm->len, realm->buf,
+             comm->len, comm->buf);
+  } else {
+    /* It's not local. Get the route's peer and check whether it's the same place we
+     * got the selected route from. Peer should always correspond to an entry in our
+     * peer table. */
+    tr_debug("trps_select_realm_update: selected route for %.*s/%.*s is not local",
+             realm->len, realm->buf,
+             comm->len, comm->buf);
+    route_peer = trp_ptable_find_gss_name(trps->ptable, trp_route_get_peer(route));
+    if (route_peer == NULL) {
+      tr_err("trps_select_realm_update: unknown peer GSS name (%.*s) for selected route for %.*s/%.*s",
+             trp_route_get_peer(route)->len, trp_route_get_peer(route)->buf,
+             realm->len, realm->buf,
+             comm->len, comm->buf);
+      return NULL;
+    }
+    route_peer_label = trp_peer_get_label(route_peer);
+    if (route_peer_label == NULL) {
+      tr_err("trps_select_realm_update: error retrieving peer label for selected route for %.*s/%.*s",
+             realm->len, realm->buf,
+             comm->len, comm->buf);
+      return NULL;
+    }
+
+    /* see if these match */
+    tr_debug("trps_select_realm_update: %.*s vs %.*s",
+             peer_label->len, peer_label->buf,
+             route_peer_label->len, route_peer_label->buf);
+
+    if (0==tr_name_cmp(peer_label, route_peer_label)) {
+      /* the selected entry goes through the peer we're reporting to, choose an alternate */
+      tr_debug("trps_select_realm_update: matched, finding alternate route");
+      route=trps_find_best_route(trps, comm, realm, peer_label);
+      if ((route==NULL) || (!trp_metric_is_finite(trp_route_get_metric(route)))) {
+        tr_debug("trps_select_realm_update: no route to %.*s/%.*s suitable to advertise to %.*s",
+                 realm->len, realm->buf,
+                 comm->len, comm->buf,
+                 peer_label->len, peer_label->buf);
+        return NULL; /* don't advertise a nonexistent or retracted route */
+      }
+    }
   }
   return route;
 }
@@ -1440,7 +1509,7 @@ static TRP_ROUTE *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, T
 static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx,
                                                  GPtrArray *updates,
                                                  TRPS_INSTANCE *trps,
-                                                 TR_NAME *peer_gssname,
+                                                 TR_NAME *peer_label,
                                                  int triggered)
 {
   size_t n_comm=0;
@@ -1457,7 +1526,7 @@ static TRP_RC trps_select_route_updates_for_peer(TALLOC_CTX *mem_ctx,
   for (ii=0; ii<n_comm; ii++) {
     realm=trp_rtable_get_comm_realms(trps->rtable, comm[ii], &n_realm);
     for (jj=0; jj<n_realm; jj++) {
-      best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_gssname);
+      best=trps_select_realm_update(trps, comm[ii], realm[jj], peer_label);
       /* If we found a route, add it to the list. If triggered!=0, then only
        * add triggered routes. */
       if ((best!=NULL) && ((!triggered) || trp_route_is_triggered(best))) {
@@ -1548,7 +1617,11 @@ cleanup:
 }
 
 /* construct an update with all the inforecs for comm/realm/role to be sent to peer */
-static TRP_UPD *trps_comm_update(TALLOC_CTX *mem_ctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, TR_COMM *comm, TR_REALM *realm)
+static TRP_UPD *trps_comm_update(TALLOC_CTX *mem_ctx,
+                                 TRPS_INSTANCE *trps,
+                                 TR_NAME *peer_label,
+                                 TR_COMM *comm,
+                                 TR_REALM *realm)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
   TRP_UPD *upd=trp_upd_new(tmp_ctx);
@@ -1614,7 +1687,7 @@ cleanup:
 static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx,
                                                 GPtrArray *updates,
                                                 TRPS_INSTANCE *trps,
-                                                TR_NAME *peer_gssname,
+                                                TR_NAME *peer_label,
                                                 int triggered)
 {
   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
@@ -1654,7 +1727,7 @@ static TRP_RC trps_select_comm_updates_for_peer(TALLOC_CTX *mem_ctx,
       tr_debug("trps_select_comm_updates_for_peer: adding realm %.*s",
                tr_realm_get_id(realm)->len,
                tr_realm_get_id(realm)->buf);
-      upd=trps_comm_update(mem_ctx, trps, peer_gssname, comm, realm);
+      upd=trps_comm_update(mem_ctx, trps, peer_label, comm, realm);
       if (upd!=NULL)
         g_ptr_array_add(updates, upd);
     }
@@ -1821,7 +1894,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 +2079,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..cdd5f6f 100644 (file)
@@ -1,7 +1,8 @@
 %global optflags %{optflags} -Wno-parentheses
+%{!?_release_number: %define _release_number 1}
 Name:           trust_router
-Version:        3.3.0
-Release:        1%{?dist}
+Version:        3.4.0~3
+Release:        %{_release_number}%{?dist}
 Summary:        Moonshot Trust Router
 
 Group:          System Environment/Libraries
@@ -118,6 +119,7 @@ chmod 770 /var/log/trust_router
 #%{_bindir}/tidc-wrapper
 #%{_bindir}/tids
 #%{_bindir}/tids-wrapper
+#%{_bindir}/trmon
 #%{_bindir}/trust_router
 #%{_bindir}/trust_router-wrapper
 %{_bindir}/*