First pass at a trmon command-line interface; fix a few bugs
authorJennifer Richards <jennifer@painless-security.com>
Fri, 13 Apr 2018 21:02:18 +0000 (17:02 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Fri, 13 Apr 2018 21:02:18 +0000 (17:02 -0400)
At this point, if you hack tr_mons_auth_handler() to always return 0
(success), then trmon can connect to the trust router's monitoring port
and retrieve a test message. That counts as first contact, I guess.
Actual functionality is still to come.

  * Create basic trmon utility based closely on tidc
  * Temporarily use void pointers for trps/tids handles in the MON_INSTANCE
    structure - there is a header file cycle that prevents compliation.
    Need to sort that out, but this works for the moment.
  * Fill in tr_msg handlers for monitoring message encoders/decoders
  * Revert to the monitoring msg decoder working from json, not a string,
    since that is what we need. This breaks the test programs for now.

CMakeLists.txt
Makefile.am
common/tr_msg.c
include/mon_internal.h
include/tr_msg.h
mon/mon_req_decode.c
mon/monc.c [new file with mode: 0644]
tr/trmon_main.c [new file with mode: 0644]

index 2fe9749..2256e88 100644 (file)
@@ -96,6 +96,8 @@ set(SOURCE_FILES
 # Does not actually build!
 add_executable(trust_router ${SOURCE_FILES})
 
+add_executable(trmon mon/monc.c tr/trmon_main.c)
+
 # Test build targets - for debugging
 add_executable(test_mon_req_encode mon/mon_common.c mon/mon_req.c mon/tests/test_mon_req_encode.c mon/mon_req_encode.c)
 target_link_libraries(test_mon_req_encode jansson talloc glib-2.0)
index 49bf774..2d94db2 100644 (file)
@@ -4,7 +4,7 @@ DISTCHECK_CONFIGURE_FLAGS = \
 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 mon/tests/test_mon_req_encode \
-              mon/tests/test_mon_req_decode mon/tests/test_mon_resp_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 
@@ -41,6 +41,7 @@ common/tr_mq.c
 
 mon_srcs =                   \
     mon/mons.c               \
+    mon/monc.c               \
     mon/mon_common.c         \
     mon/mon_req.c            \
     mon/mon_req_encode.c     \
@@ -99,6 +100,15 @@ $(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 \
+$(tid_srcs) \
+$(trp_srcs) \
+$(mon_srcs) \
+$(common_srcs)
+tr_trmon_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+tr_trmon_LDFLAGS = $(AM_LDFLAGS) -pthread
+
 trp_msgtst_SOURCES = trp/msgtst.c \
 $(common_srcs) \
 trp/trp_req.c \
index e0e406a..8d792cb 100644 (file)
 
 #include <tr_apc.h>
 #include <tr_comm.h>
+#include <mon_internal.h>
+#include <trp_internal.h>
 #include <tr_msg.h>
 #include <tr_name_internal.h>
-#include <trp_internal.h>
 #include <trust_router/tr_constraint.h>
 #include <trust_router/tr_dh.h>
 #include <tr_debug.h>
@@ -127,6 +128,32 @@ void tr_msg_set_resp(TR_MSG *msg, TID_RESP *resp)
   msg->msg_type = TID_RESPONSE;
 }
 
+MON_REQ *tr_msg_get_mon_req(TR_MSG *msg)
+{
+  if (msg->msg_type == MON_REQUEST)
+    return (MON_REQ *)msg->msg_rep;
+  return NULL;
+}
+
+void tr_msg_set_mon_req(TR_MSG *msg, MON_REQ *req)
+{
+  msg->msg_rep = req;
+  msg->msg_type = MON_REQUEST;
+}
+
+MON_RESP *tr_msg_get_mon_resp(TR_MSG *msg)
+{
+  if (msg->msg_type == MON_RESPONSE)
+    return (MON_RESP *)msg->msg_rep;
+  return NULL;
+}
+
+void tr_msg_set_mon_resp(TR_MSG *msg, MON_RESP *resp)
+{
+  msg->msg_rep = resp;
+  msg->msg_type = MON_RESPONSE;
+}
+
 TRP_UPD *tr_msg_get_trp_upd(TR_MSG *msg)
 {
   if (msg->msg_type == TRP_UPDATE)
@@ -1154,6 +1181,8 @@ char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg)
   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();
@@ -1174,21 +1203,35 @@ char *tr_msg_encode(TALLOC_CTX *mem_ctx, TR_MSG *msg)
       json_object_set_new(jmsg, "msg_body", tr_msg_encode_tidresp(tidresp));
       break;
 
-    case TRP_UPDATE:
-      jmsg_type = json_string("trp_update");
-      json_object_set_new(jmsg, "msg_type", jmsg_type);
-      trpupd=tr_msg_get_trp_upd(msg);
-      json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_upd(trpupd));
-      break;
-
-    case TRP_REQUEST:
-      jmsg_type = json_string("trp_request");
-      json_object_set_new(jmsg, "msg_type", jmsg_type);
-      trpreq=tr_msg_get_trp_req(msg);
-      json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_req(trpreq));
-      break;
-
-    default:
+      case TRP_UPDATE:
+        jmsg_type = json_string("trp_update");
+        json_object_set_new(jmsg, "msg_type", jmsg_type);
+        trpupd=tr_msg_get_trp_upd(msg);
+        json_object_set_new(jmsg, "msg_body", tr_msg_encode_trp_upd(trpupd));
+        break;
+
+      case TRP_REQUEST:
+        jmsg_type = json_string("trp_request");
+        json_object_set_new(jmsg, "msg_type", jmsg_type);
+        trpreq=tr_msg_get_trp_req(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;
     }
@@ -1252,6 +1295,15 @@ TR_MSG *tr_msg_decode(const char *jbuf, size_t buflen)
     msg->msg_type = TRP_UPDATE;
     tr_msg_set_trp_req(msg, tr_msg_decode_trp_req(NULL, jbody)); /* null talloc context for now */
   }
+  else if (0 == strcmp(mtype, "mon_request")) {
+    msg->msg_type = MON_REQUEST;
+    tr_msg_set_mon_req(msg, mon_req_decode(NULL, 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(NULL, jbody));
+//  }
   else {
     msg->msg_type = TR_UNKNOWN;
     msg->msg_rep = NULL;
@@ -1286,6 +1338,12 @@ void tr_msg_free_decoded(TR_MSG *msg)
           trp_req_free(tr_msg_get_trp_req(msg));
         default:
           break;
+        case MON_REQUEST:
+          mon_req_free(tr_msg_get_mon_req(msg));
+          break;
+        case MON_RESPONSE:
+          mon_resp_free(tr_msg_get_mon_resp(msg));
+          break;
       }
     }
     free (msg);
index 171ce18..189695f 100644 (file)
 #include <stdint.h>
 #include <jansson.h>
 #include <gmodule.h>
-#include <trp_internal.h>
+//#include <trp_internal.h>
 #include <tr_gss_names.h>
 #include <tr_name_internal.h>
 #include <gssapi.h>
+#include <trust_router/tr_dh.h>
 
 /* Typedefs */
 typedef struct mon_req MON_REQ;
@@ -58,9 +59,11 @@ 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 {
@@ -121,13 +124,18 @@ struct mons_instance {
   const char *hostname;
   unsigned int port;
   TR_GSS_NAMES *authorized_gss_names;
-  TIDS_INSTANCE *tids;
-  TRPS_INSTANCE *trps;
+  void *tids; // TODO sort out header file cycles and use typed pointers
+  void *trps; // TODO sort out header file cycles and use typed pointers
   MONS_REQ_FUNC *req_handler;
   MONS_AUTH_FUNC *auth_handler;
   void *cookie;
 };
 
+/* Monitoring client instance */
+struct monc_instance {
+  DH *client_dh;
+};
+
 /* Prototypes */
 /* tr_mon.c */
 const char *mon_cmd_to_string(MON_CMD cmd);
@@ -146,7 +154,7 @@ MON_OPT *mon_req_opt_index(MON_REQ *req, size_t index);
 json_t *mon_req_encode(MON_REQ *req);
 
 /* mon_req_decode.c */
-MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, const char *req_json);
+MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json);
 
 /* mon_resp.c */
 MON_RESP *mon_resp_new(TALLOC_CTX *mem_ctx,
@@ -165,4 +173,15 @@ int mons_get_listener(MONS_INSTANCE *mons, MONS_REQ_FUNC *req_handler, MONS_AUTH
                       unsigned int port, void *cookie, int *fd_out, size_t max_fd);
 int mons_accept(MONS_INSTANCE *mons, int listen);
 
+/* monc.c */
+MONC_INSTANCE *monc_create(void);
+void monc_destroy(MONC_INSTANCE *monc);
+int monc_open_connection (MONC_INSTANCE *monc, const char *server, unsigned int port, gss_ctx_id_t *gssctx);
+int monc_send_request (MONC_INSTANCE *monc, int conn, gss_ctx_id_t gssctx, MONC_RESP_FUNC *resp_handler,
+                       void *cookie);
+int monc_fwd_request(MONC_INSTANCE *monc, int conn, gss_ctx_id_t gssctx, MON_REQ *mon_req,
+                     MONC_RESP_FUNC *resp_handler, void *cookie);
+DH * monc_get_dh(MONC_INSTANCE *inst);
+DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh);
+
 #endif //TRUST_ROUTER_MON_REQ_H
index 59b7cf7..17979c1 100644 (file)
@@ -38,6 +38,8 @@
 #include <jansson.h>
 #include <trust_router/tid.h>
 #include <trust_router/trp.h>
+#include <mon_internal.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,6 +69,10 @@ 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 */
index c1eeed1..66763a2 100644 (file)
@@ -106,14 +106,14 @@ static MON_RC mon_options_decode(json_t *opts_json, MON_REQ *req)
 /**
  * Parse JSON for a request
  */
-static json_t *mon_req_parse(const char *input)
-{
-  json_t *parsed_json = NULL;
-  json_error_t json_error;
-
-  parsed_json = json_loads(input, JSON_REJECT_DUPLICATES, &json_error);
-  return parsed_json;
-}
+//static json_t *mon_req_parse(const char *input)
+//{
+//  json_t *parsed_json = NULL;
+//  json_error_t json_error;
+//
+//  parsed_json = json_loads(input, JSON_REJECT_DUPLICATES, &json_error);
+//  return parsed_json;
+//}
 
 /**
  * Decode a JSON request
@@ -132,16 +132,15 @@ static json_t *mon_req_parse(const char *input)
  * @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, const char *req_str)
+MON_REQ *mon_req_decode(TALLOC_CTX *mem_ctx, json_t *req_json) //const char *req_str)
 {
   TALLOC_CTX *tmp_ctx = talloc_new(NULL);
   MON_REQ *req = NULL;
-  json_t *req_json = NULL;
   json_t *jval = NULL;
   json_t *opts_json = NULL;
   MON_CMD cmd = MON_CMD_UNKNOWN;
 
-  req_json = mon_req_parse(req_str); // TODO: Check errors
+  //req_json = mon_req_parse(req_str); // TODO: Check errors
 
   if (! json_is_object(req_json))
     goto cleanup;
diff --git a/mon/monc.c b/mon/monc.c
new file mode 100644 (file)
index 0000000..3e567ce
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * 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>
+
+
+static int monc_destructor(void *obj)
+{
+  MONC_INSTANCE *monc=talloc_get_type_abort(obj, MONC_INSTANCE);
+  if (NULL!=monc) {
+    if (NULL!=monc->client_dh)
+      tr_destroy_dh_params(monc->client_dh);
+  }
+  return 0;
+}
+
+/* creates struct in talloc null context */
+MONC_INSTANCE *monc_create(void)
+{
+  MONC_INSTANCE *monc=talloc(NULL, MONC_INSTANCE);
+  if (monc!=NULL) {
+    monc->client_dh=NULL;
+    talloc_set_destructor((void *)monc, monc_destructor);
+  }
+  return monc;
+}
+
+void monc_destroy(MONC_INSTANCE *monc)
+{
+  talloc_free(monc);
+}
+
+int monc_open_connection (MONC_INSTANCE *monc,
+                          const char *server,
+                          unsigned int port,
+                          gss_ctx_id_t *gssctx)
+{
+  int err = 0;
+  int conn = -1;
+
+  tr_debug("monc_open_connection: opening monc connection to %s:%d", server, port);
+  err = gsscon_connect(server, port, "trustmonitor", &conn, gssctx);
+
+  if (!err)
+    return conn;
+  else
+    return -1;
+}
+
+int monc_send_request (MONC_INSTANCE *monc,
+                       int conn,
+                       gss_ctx_id_t gssctx,
+                       MONC_RESP_FUNC *resp_handler,
+                       void *cookie)
+{
+  MON_REQ *mon_req = NULL;
+  int rc;
+
+  /* Create and populate a MON req structure */
+  if (!(mon_req = mon_req_new(NULL, MON_CMD_SHOW))) // TODO accept command as a parameter
+    goto error;
+
+  rc = monc_fwd_request(monc, conn, gssctx, mon_req, resp_handler, cookie);
+  goto cleanup;
+error:
+  rc = -1;
+cleanup:
+  mon_req_free(mon_req);
+  return rc;
+}
+
+int monc_fwd_request(MONC_INSTANCE *monc,
+                     int conn,
+                     gss_ctx_id_t gssctx,
+                     MON_REQ *mon_req,
+                     MONC_RESP_FUNC *resp_handler,
+                     void *cookie)
+{
+  char *req_buf = NULL;
+  char *resp_buf = NULL;
+  size_t resp_buflen = 0;
+  TR_MSG *msg = NULL;
+  TR_MSG *resp_msg = NULL;
+  int err;
+  int rc = 0;
+
+  /* Create and populate a MON msg structure */
+  if (!(msg = talloc_zero(mon_req, TR_MSG)))
+    goto error;
+
+  msg->msg_type = MON_REQUEST;
+  tr_msg_set_mon_req(msg, mon_req);
+
+  /* store the response function and cookie */
+  // mon_req->resp_func = resp_handler;
+  // mon_req->cookie = cookie;
+
+
+  /* Encode the request into a json string */
+  if (!(req_buf = tr_msg_encode(NULL, msg))) {
+    tr_err("monc_fwd_request: Error encoding MON request.\n");
+    goto error;
+  }
+
+  tr_debug( "monc_fwd_request: Sending MON request:\n");
+  tr_debug( "%s\n", req_buf);
+
+  /* Send the request over the connection */
+  err = gsscon_write_encrypted_token (conn, gssctx, req_buf, strlen(req_buf));
+  if (err) {
+    tr_err( "monc_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(conn, gssctx, &resp_buf, &resp_buflen)) {
+    if (resp_buf)
+      free(resp_buf);
+    goto error;
+  }
+
+  tr_debug( "monc_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( "monc_fwd_request: Error decoding response.\n");
+//    goto error;
+//  }
+//
+//  /* TBD -- Check if this is actually a valid response */
+//  if (MON_RESPONSE != tr_msg_get_msg_type(resp_msg)) {
+//    tr_err( "monc_fwd_request: Error, no response in the response!\n");
+//    goto error;
+//  }
+//
+//  if (resp_handler) {
+//    /* Call the caller's response function. It must copy any data it needs before returning. */
+//    tr_debug("monc_fwd_request: calling response callback function.");
+//    (*resp_handler)(monc, mon_req, tr_msg_get_resp(resp_msg), cookie);
+//  }
+
+  goto cleanup;
+
+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);
+  return rc;
+}
+
+
+DH * monc_get_dh(MONC_INSTANCE *inst)
+{
+  return inst->client_dh;
+}
+
+DH *monc_set_dh(MONC_INSTANCE *inst, DH *dh)
+{
+  inst->client_dh = dh;
+  return dh;
+}
diff --git a/tr/trmon_main.c b/tr/trmon_main.c
new file mode 100644 (file)
index 0000000..507bf34
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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 <unistd.h>
+
+#include <mon_internal.h>
+#include <gsscon.h>
+#include <tr_debug.h>
+#include <trust_router/tr_dh.h>
+
+
+/* command-line option setup */
+
+/* argp global parameters */
+const char *argp_program_bug_address=PACKAGE_BUGREPORT; /* bug reporting address */
+
+/* doc strings */
+static const char doc[]=PACKAGE_NAME " - TR Monitoring Client";
+static const char arg_doc[]="<message> <server> [<port>]"; /* string describing arguments, if any */
+
+/* define the options here. Fields are:
+ * { long-name, short-name, variable name, options, help description } */
+static const struct argp_option cmdline_options[] = {
+  { "repeat", 'r', "N", OPTION_ARG_OPTIONAL, "Repeat message until terminated, or N times." },
+  {NULL}
+};
+
+/* structure for communicating with option parser */
+struct cmdline_args {
+  char *msg;
+  char *server;
+  int port; /* optional */
+  int repeat; /* how many times to repeat, or -1 for infinite */
+};
+
+/* parser for individual options - fills in a struct cmdline_args */
+static error_t parse_option(int key, char *arg, struct argp_state *state)
+{
+  /* get a shorthand to the command line argument structure, part of state */
+  struct cmdline_args *arguments=state->input;
+
+  switch (key) {
+  case 'r':
+    if (arg==NULL)
+      arguments->repeat=-1;
+    else
+      arguments->repeat=strtol(arg, NULL, 10);
+    break;
+
+  case ARGP_KEY_ARG: /* handle argument (not option) */
+    switch (state->arg_num) {
+    case 0:
+      arguments->msg=arg;
+      break;
+
+    case 1:
+      arguments->server=arg;
+      break;
+
+    case 2:
+      arguments->port=strtol(arg, NULL, 10); /* optional */
+      break;
+
+    default:
+      /* too many arguments */
+      argp_usage(state);
+    }
+    break;
+
+  case ARGP_KEY_END: /* no more arguments */
+    if (state->arg_num < 2) {
+      /* not enough arguments encountered */
+      argp_usage(state);
+    }
+    break;
+
+  default:
+    return ARGP_ERR_UNKNOWN;
+  }
+
+  return 0; /* success */
+}
+
+
+/* assemble the argp parser */
+static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
+
+int main (int argc, 
+          char *argv[]) 
+{
+  TALLOC_CTX *main_ctx=talloc_new(NULL);
+  MONC_INSTANCE *monc=NULL;
+  struct cmdline_args opts;
+  int rc;
+  int conn;
+  gss_ctx_id_t gssctx;
+
+  /* parse the command line*/
+  /* set defaults */
+  opts.msg=NULL;
+  opts.server=NULL;
+  opts.port=TRP_PORT;
+  opts.repeat=1;
+
+  argp_parse(&argp, argc, argv, 0, 0, &opts);
+
+  /* Use standalone logging */
+  tr_log_open();
+
+  /* set logging levels */
+  talloc_set_log_stderr();
+  tr_log_threshold(LOG_CRIT);
+  tr_console_threshold(LOG_DEBUG);
+
+  printf("TR Monitor:\nServer = %s, port = %i\n", opts.server, opts.port);
+
+  /* Create a MON client instance & the client DH */
+  monc = monc_create();
+  if (NULL == (monc->client_dh = tr_create_dh_params(main_ctx, 0))) {
+    printf("Error creating client DH params.\n");
+    return 1;
+  }
+
+  /* Set-up MON connection */
+  if (-1 == (conn = monc_open_connection(monc, opts.server, opts.port, &gssctx))) {
+    /* Handle error */
+    printf("Error in monc_open_connection.\n");
+    return 1;
+  };
+
+  /* Send a MON request */
+  if (0 > (rc = monc_send_request(monc, conn, gssctx, NULL, NULL))) {
+    /* Handle error */
+    printf("Error in monc_send_request, rc = %d.\n", rc);
+    return 1;
+  }
+
+  /* Clean-up the MON client instance, and exit */
+  monc_destroy(monc);
+  talloc_free(main_ctx);
+  return 0;
+}
+