Merge pull request #50 from painless-security/jennifer/refactoring_tids
[trust_router.git] / tr / tr_main.c
index 0e659df..8c8202d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, JANET(UK)
+ * Copyright (c) 2012, 2015, JANET(UK)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 
 #include <stdio.h>
-#include <jansson.h>
-
-#include <tr.h>
-#include <tr_filter.h>
-#include <trust_router/tid.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <event2/event.h>
+#include <talloc.h>
+#include <signal.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>
-#include <tr_comm.h>
-#include <tr_idp.h>
-#include <tr_rp.h>
+#include <tr_event.h>
+#include <tr_cfgwatch.h>
+#include <tr.h>
+#include <tr_debug.h>
 
-/* Structure to hold TR instance and original request in one cookie */
-typedef struct tr_resp_cookie {
-  TR_INSTANCE *tr;
-  TID_REQ *orig_req;
-} TR_RESP_COOKIE;
+#define TALLOC_DEBUG_ENABLE 1
 
+/***** command-line option handling / setup *****/
 
-static void tr_tidc_resp_handler (TIDC_INSTANCE *tidc, 
-                       TID_REQ *req,
-                       TID_RESP *resp, 
-                       void *resp_cookie) 
+static void print_version_info(void)
 {
-  fprintf(stderr, "tr_tidc_resp_handler: Response received (conn = %d)! Realm = %s, Community = %s.\n", ((TR_RESP_COOKIE *)resp_cookie)->orig_req->conn, resp->realm->buf, resp->comm->buf);
-  req->resp_rcvd = 1;
-
-  /* TBD -- handle concatentation of multiple responses to single req */
-  tids_send_response(((TR_RESP_COOKIE *)resp_cookie)->tr->tids, 
-                    ((TR_RESP_COOKIE *)resp_cookie)->orig_req, 
-                    resp);
-  
-  return;
+  printf("Moonshot Trust Router %s\n\n", PACKAGE_VERSION);
 }
 
-static int tr_tids_req_handler (TIDS_INSTANCE *tids,
-                     TID_REQ *orig_req, 
-                     TID_RESP **resp,
-                     void *tr)
-{
-  TIDC_INSTANCE *tidc = NULL;
-  TR_RESP_COOKIE resp_cookie;
-  TR_AAA_SERVER *aaa_servers = NULL;
-  TR_NAME *apc = NULL;
-  TID_REQ *fwd_req = NULL;
-  TR_COMM *cfg_comm = NULL;
-  TR_COMM *cfg_apc = NULL;
-  int rc;
-
-  if ((!tids) || (!orig_req) || (!resp) || (!(*resp)) || (!tr)) {
-    fprintf(stderr, "tids_req_handler: Bad parameters\n");
-    return -1;
-  }
-
-  fprintf(stdout, "tr_tids_req_handler: Request received (conn = %d)! Realm = %s, Comm = %s\n", orig_req->conn, 
-        orig_req->realm->buf, orig_req->comm->buf);
-  if (tids)
-    tids->req_count++;
+/* Strip trailing / from a path name.*/
+static void remove_trailing_slash(char *s) {
+  size_t n;
 
-  /* Duplicate the request, so we can modify and forward it */
-  if (NULL == (fwd_req = tid_dup_req(orig_req))) {
-    fprintf(stderr, "tr_tids_req_handler: Unable to duplicate request.\n");
-    return -1;
-  }
-
-  if (NULL == (cfg_comm = tr_comm_lookup((TR_INSTANCE *)tids->cookie, orig_req->comm))) {
-    fprintf(stderr, "tr_tids_req_hander: Request for unknown comm: %s.\n", orig_req->comm->buf);
-    tids_send_err_response(tids, orig_req, "Unknown community");
-    return -1;
-  }
-
-  /* Check that the rp_realm matches the filter for the GSS name that 
-   * was received. */
-
-  if ((!((TR_INSTANCE *)tr)->rp_gss) || 
-      (!((TR_INSTANCE *)tr)->rp_gss->rp_match)) {
-    fprintf(stderr, "tr_tids_req_handler: No GSS name for incoming request.\n");
-    tids_send_err_response(tids, orig_req, "No GSS name for request");
-    return -1;
+  n=strlen(s);
+  if(s[n-1]=='/') {
+    s[n-1]='\0';
   }
+}
 
-  if (!tr_prefix_wildcard_match(((TR_INSTANCE *)tr)->rp_gss->rp_match->buf, 
-                               orig_req->rp_realm->buf)) {
-    fprintf(stderr, "tr_tids_req_handler: RP realm (%s) does not match RP Realm filter for GSS name (%s)\n", orig_req->rp_realm->buf, ((TR_INSTANCE *)tr)->rp_gss->rp_match->buf);
-    tids_send_err_response(tids, orig_req, "RP Realm filter error");
-    return -1;
+/* 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 " PACKAGE_VERSION;
+static const char arg_doc[]=""; /* 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[] = {
+    { "config-dir", 'c', "DIR", 0, "Specify configuration file location (default is current directory)"},
+    { "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;
+};
+
+/* 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 'c':
+      if (arg == NULL) {
+        /* somehow we got called without an argument */
+        return ARGP_ERR_UNKNOWN;
+      }
+      arguments->config_dir=arg;
+      break;
+
+    case 1:
+      arguments->version_requested=1;
+      break;
+
+    case 'C':
+      arguments->validate_config_and_exit=1;
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
   }
 
-  /* Check that the rp_realm and target_realm are members of the community in the request */
-  if (NULL == (tr_find_comm_rp(cfg_comm, orig_req->rp_realm))) {
-    fprintf(stderr, "tr_tids_req_hander: RP Realm (%s) not member of community (%s).\n", orig_req->rp_realm->buf, orig_req->comm->buf);
-    tids_send_err_response(tids, orig_req, "RP COI membership error");
-    return -1;
-  }
-  if (NULL == (tr_find_comm_idp(cfg_comm, orig_req->realm))) {
-    fprintf(stderr, "tr_tids_req_hander: IDP Realm (%s) not member of APC (%s).\n", orig_req->realm->buf, orig_req->comm->buf);
-    tids_send_err_response(tids, orig_req, "IDP COI membership error");
-    return -1;
-  }
+  return 0; /* success */
+}
 
-  /* Map the comm in the request from a COI to an APC, if needed */
-  if (TR_COMM_COI == cfg_comm->type) {
-    fprintf(stderr, "tr_tids_req_handler: Community was a COI, switching.\n");
-    /* TBD -- In theory there can be more than one?  How would that work? */
-    if ((!cfg_comm->apcs) || (!cfg_comm->apcs->id)) {
-      fprintf(stderr, "No valid APC for COI %s.\n", orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "No valid APC for community");
-      return -1;
-    }
-    apc = tr_dup_name(cfg_comm->apcs->id);
-
-    /* Check that the APC is configured */
-    if (NULL == (cfg_apc = tr_comm_lookup((TR_INSTANCE *)tids->cookie, apc))) {
-      fprintf(stderr, "tr_tids_req_hander: Request for unknown comm: %s.\n", apc->buf);
-      tids_send_err_response(tids, orig_req, "Unknown APC");
-      return -1;
-    }
-
-    fwd_req->comm = apc;
-    fwd_req->orig_coi = orig_req->comm;
-
-    /* Check that rp_realm and target_realm are members of this APC */
-    if (NULL == (tr_find_comm_rp(cfg_apc, orig_req->rp_realm))) {
-      fprintf(stderr, "tr_tids_req_hander: RP Realm (%s) not member of community (%s).\n", orig_req->rp_realm->buf, orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "RP APC membership error");
-      return -1;
-    }
-    if (NULL == (tr_find_comm_idp(cfg_apc, orig_req->realm))) {
-      fprintf(stderr, "tr_tids_req_hander: IDP Realm (%s) not member of APC (%s).\n", orig_req->realm->buf, orig_req->comm->buf);
-      tids_send_err_response(tids, orig_req, "IDP APC membership error");
-      return -1;
-    }
-  }
+/* assemble the argp parser */
+static struct argp argp = {cmdline_options, parse_option, arg_doc, doc};
 
-  /* Find the AAA server(s) for this request */
-  if (NULL == (aaa_servers = tr_idp_aaa_server_lookup((TR_INSTANCE *)tids->cookie, 
-                                                     orig_req->realm, 
-                                                     orig_req->comm))) {
-      fprintf(stderr, "tr_tids_req_handler: No AAA Servers for realm %s.\n", orig_req->realm->buf);
-      tids_send_err_response(tids, orig_req, "No path to AAA Server(s) for realm");
-      return -1;
-    }
-  /* send a TID request to the AAA server(s), and get the answer(s) */
-  /* TBD -- Handle multiple servers */
-
-  /* Create a TID client instance */
-  if (NULL == (tidc = tidc_create())) {
-    fprintf(stderr, "tr_tids_req_hander: Unable to allocate TIDC instance.\n");
-    tids_send_err_response(tids, orig_req, "Memory allocation failure");
-    return -1;
-  }
 
-  /* Use the DH parameters from the original request */
-  /* TBD -- this needs to be fixed when we handle more than one req per conn */
-  tidc->client_dh = orig_req->tidc_dh;
-
-  /* Save information about this request for the response */
-  resp_cookie.tr = tr;
-  resp_cookie.orig_req = orig_req;
-
-  /* Set-up TID connection */
-  /* TBD -- handle IPv6 Addresses */
-  if (-1 == (fwd_req->conn = tidc_open_connection(tidc, 
-                                             inet_ntoa(aaa_servers->aaa_server_addr), 
-                                             &(fwd_req->gssctx)))) {
-    fprintf(stderr, "tr_tids_req_handler: Error in tidc_open_connection.\n");
-    tids_send_err_response(tids, orig_req, "Can't open connection to next hop TIDS");
-    return -1;
-  };
-
-  /* Send a TID request */
-  if (0 > (rc = tidc_fwd_request(tidc, fwd_req, &tr_tidc_resp_handler, (void *)&resp_cookie))) {
-    fprintf(stderr, "Error from tidc_fwd_request, rc = %d.\n", rc);
-    tids_send_err_response(tids, orig_req, "Can't forward request to next hop TIDS");
-    return -1;
-  }
-    
-  return 0;
+/***** talloc error handling *****/
+/* called when talloc tries to abort */
+static void tr_abort(const char *reason)
+{
+  tr_crit("tr_abort: Critical error, talloc aborted. Reason: %s", reason);
+  abort();
 }
 
-static int tr_tids_gss_handler(gss_name_t client_name, TR_NAME *gss_name,
-                       void *tr)
+#if TALLOC_DEBUG_ENABLE
+static void tr_talloc_log(const char *msg)
 {
-  TR_RP_CLIENT *rp;
-
-  if ((!client_name) || (!gss_name) || (!tr)) {
-    fprintf(stderr, "tr_tidc_gss_handler: Bad parameters.\n");
-    return -1;
-  }
-  
-  /* look up the RP client matching the GSS name */
-  if ((NULL == (rp = tr_rp_client_lookup(tr, gss_name)))) {
-    fprintf(stderr, "tr_tids_gss_handler: Unknown GSS name %s", gss_name->buf);
-    return -1;
-  }
-
-  /* Store the rp client in the TR_INSTANCE structure for now... 
-   * TBD -- fix me for new tasking model. */
-  ((TR_INSTANCE *)tr)->rp_gss = rp;
-  return 0;
+  tr_debug("talloc: %s", msg);
 }
+#endif /* TALLOC_DEBUG_ENABLE */
 
+static void configure_signals(void)
+{
+  sigset_t signals;
+  /* ignore SIGPIPE */
+  sigemptyset(&signals);
+  sigaddset(&signals, SIGPIPE);
+  pthread_sigmask(SIG_BLOCK, &signals, NULL);
+}
 
-int main (int argc, const char *argv[])
+int main(int argc, char *argv[])
 {
+  TALLOC_CTX *main_ctx=NULL;
+
   TR_INSTANCE *tr = NULL;
-  struct dirent **cfg_files = NULL;
-  json_t *jcfg = NULL;
-  TR_CFG_RC rc = TR_CFG_SUCCESS;       /* presume success */
-  int err = 0, n = 0;;
+  struct cmdline_args opts;
+  struct event_base *ev_base;
+  struct tr_socket_event tids_ev = {0};
+  struct tr_socket_event mon_ev = {0};
+  struct event *cfgwatch_ev;
+
+  configure_signals();
+
+  /* we're going to be multithreaded, so disable null context tracking */
+  talloc_set_abort_fn(tr_abort);
+  talloc_disable_null_tracking();
+#if TALLOC_DEBUG_ENABLE
+  talloc_set_log_fn(tr_talloc_log);
+#endif /* TALLOC_DEBUG_ENABLE */
+  main_ctx=talloc_new(NULL);
+
+  /* Use standalone logging */
+  tr_log_open();
+
+  /***** parse command-line arguments *****/
+  /* set defaults */
+  opts.version_requested=0;
+  opts.validate_config_and_exit=0;
+  opts.config_dir=".";
+
+  /* parse the command line*/
+  argp_parse(&argp, argc, argv, 0, 0, &opts);
+
+  /* process options */
+  remove_trailing_slash(opts.config_dir);
+
+
+  /***** Print version info *****/
+  print_version_info();
+  if (opts.version_requested)
+    return 0; /* requested that we print version and exit */
+
+  /***** create a Trust Router instance *****/
+  if (NULL == (tr = tr_create(main_ctx))) {
+    tr_crit("Unable to create Trust Router instance, exiting.");
+    return 1;
+  }
 
-  /* parse command-line arguments? -- TBD */
+  /***** initialize the trust path query server instance *****/
+  if (NULL == (tr->tids = tids_new(tr))) {
+    tr_crit("Error initializing Trust Path Query Server instance.");
+    return 1;
+  }
 
-  /* create a Trust Router instance */
-  if (NULL == (tr = tr_create())) {
-    fprintf(stderr, "Unable to create Trust Router instance, exiting.\n");
+  /***** initialize the trust router protocol server instance *****/
+  if (NULL == (tr->trps = trps_new(tr))) {
+    tr_crit("Error initializing Trust Router Protocol Server instance.");
     return 1;
   }
 
-  /* find the configuration files */
-  if (0 == (n = tr_find_config_files(&cfg_files))) {
-    fprintf (stderr, "Can't locate configuration files, exiting.\n");
-    exit(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;
+
+  /***** process configuration *****/
+  tr->cfgwatch=tr_cfgwatch_create(tr);
+  if (tr->cfgwatch == NULL) {
+    tr_crit("Unable to create configuration watcher object, exiting.");
+    return 1;
+  }
+  tr->cfgwatch->config_dir=opts.config_dir;
+  tr->cfgwatch->cfg_mgr=tr->cfg_mgr;
+  tr->cfgwatch->update_cb=tr_config_changed; /* handle configuration changes */
+  tr->cfgwatch->update_cookie=(void *)tr;
+  if (0 != tr_read_and_apply_config(tr->cfgwatch)) {
+    tr_crit("Error reading configuration, exiting.");
+    return 1;
   }
 
-  /* read and parse initial configuration */
-  if (NULL == (jcfg = tr_read_config (n, cfg_files))) {
-    fprintf (stderr, "Error reading or parsing configuration files, exiting.\n");
-    exit(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) {
+    tr_crit("Error initializing event loop.");
+    return 1;
   }
-  if (TR_CFG_SUCCESS != tr_parse_config(tr, jcfg)) {
-    fprintf (stderr, "Error decoding configuration information, exiting.\n");
-    exit(1);
+
+  /* already set config_dir, fstat_list and n_files earlier */
+  if (0 != tr_cfgwatch_event_init(ev_base, tr->cfgwatch, &cfgwatch_ev)) {
+    tr_crit("Error initializing configuration file watcher.");
+    return 1;
   }
 
-  /* apply initial configuration */
-  if (TR_CFG_SUCCESS != (rc = tr_apply_new_config(tr))) {
-    fprintf (stderr, "Error applying configuration, rc = %d.\n", rc);
-    exit(1);
+  /* 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;
   }
 
-  /* initialize the trust path query server instance */
-  if (0 == (tr->tids = tids_create ())) {
-    fprintf (stderr, "Error initializing Trust Path Query Server instance.\n");
-    exit(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)) {
+    tr_crit("Error initializing Trust Path Query Server instance.");
+    return 1;
   }
 
-  /* start the trust path query server, won't return unless fatal error. */
-  if (0 != (err = tids_start(tr->tids, &tr_tids_req_handler, &tr_tids_gss_handler, (void *)tr))) {
-    fprintf (stderr, "Error from Trust Path Query Server, err = %d.\n", err);
-    exit(err);
+  /* install TRP handler events */
+  tr_debug("Initializing Dynamic Trust Router Protocol events.");
+  if (TRP_SUCCESS != tr_trps_event_init(ev_base, tr)) {
+    tr_crit("Error initializing Trust Path Query Server instance.");
+    return 1;
   }
 
-  tids_destroy(tr->tids);
-  tr_destroy(tr);
-  exit(0);
+  tr_debug("Starting event loop.");
+  tr_event_loop_run(ev_base); /* does not return until we are done */
+
+  tr_destroy(tr); /* thanks to talloc, should destroy everything */
+
+  talloc_free(main_ctx);
+  return 0;
 }