From 6582e07c476f028eaf27d964efd90e22609ffb2a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 12 Jul 2015 15:44:57 +0200 Subject: [PATCH] adding -f command-line option --- doc/taler-mint-httpd.1 | 4 ++ src/mint/taler-mint-httpd.c | 140 +++++++++++++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/doc/taler-mint-httpd.1 b/doc/taler-mint-httpd.1 index 1f8440e3c..43587006e 100644 --- a/doc/taler-mint-httpd.1 +++ b/doc/taler-mint-httpd.1 @@ -21,6 +21,10 @@ Print short help on options. .B .IP "\-v, \-\-version" Print version information. +.B +.IP "\-f FILENAME, \-\-file\-input=FILENAME" +This option is only available if the mint was compiled with the configure option +\-\-enable\-developer\-mode. It is used for generating test cases against the mint using AFL. When this option is present, the HTTP server will (1) terminate after the first client's HTTP connection is completed, and (2) automatically start such a client using a helper process based on the 'nc' or 'ncat' binary using FILENAME as the standard input to the helper process. As a result, the process will effectively run with FILENAME as the input from an HTTP client and then immediately exit. This is useful to test taler\-mint\-httpd against many different possible inputs in a controlled way. .SH BUGS Report bugs by using Mantis or by sending electronic mail to diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index de0a0368e..14fde3ac7 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -429,20 +429,134 @@ mint_serve_process_config (const char *mint_directory) } +/* Developer logic for supporting the `-f' option. */ +#if HAVE_DEVELOPER + /** - * The main function of the serve tool + * Option `-f' (specifies an input file to give to the HTTP server). + */ +static char *input_filename; + + +/** + * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename + * as the input for the request. If launching the client worked, + * run the #TMH_KS_loop() event loop as usual. + * + * @return #GNUNET_OK + */ +static int +run_fake_client () +{ + pid_t cld; + char ports[6]; + int fd; + int ret; + int status; + + fd = open (input_filename, O_RDONLY); + if (-1 == fd) + { + fprintf (stderr, + "Failed to open `%s': %s\n", + input_filename, + strerror (errno)); + return GNUNET_SYSERR; + } + /* Fake HTTP client request with #input_filename as input. + We do this using the nc tool. */ + GNUNET_snprintf (ports, + sizeof (ports), + "%u", + serve_port); + if (0 == (cld = fork())) + { + GNUNET_break (0 == close (0)); + GNUNET_break (0 == dup2 (fd, 0)); + GNUNET_break (0 == close (fd)); + if ( (0 != execlp ("nc", + "nc", + "localhost", + ports, + NULL)) && + (0 != execlp ("ncat", + "ncat", + "localhost", + ports, + NULL)) ) + { + fprintf (stderr, + "Failed to run both `nc' and `ncat': %s\n", + strerror (errno)); + } + exit (0); + } + /* parent process */ + GNUNET_break (0 == close (fd)); + ret = TMH_KS_loop (); + if (cld != waitpid (cld, &status, 0)) + fprintf (stderr, + "Waiting for `nc' child failed: %s\n", + strerror (errno)); + return ret; +} + + +/** + * Signature of the callback used by MHD to notify the application + * about completed connections. If we are running in test-mode with + * an #input_filename, this function is used to terminate the HTTPD + * after the first request has been processed. + * + * @param cls client-defined closure, NULL + * @param connection connection handle (ignored) + * @param socket_context socket-specific pointer (ignored) + * @param toe reason for connection notification + */ +static void +connection_done (void *cls, + struct MHD_Connection *connection, + void **socket_context, + enum MHD_ConnectionNotificationCode toe) +{ + /* We only act if the connection is closed. */ + if (MHD_CONNECTION_NOTIFY_CLOSED != toe) + return; + /* This callback is also present if the option wasn't, so + make sure the option was actually set. */ + if (NULL == input_filename) + return; + /* We signal ourselves to terminate. */ + if (0 != kill (getpid(), + SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "kill"); +} + +/* end of HAVE_DEVELOPER */ +#endif + + +/** + * The main function of the taler-mint-httpd server ("the mint"). * * @param argc number of arguments from the command line * @param argv command line arguments * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { static const struct GNUNET_GETOPT_CommandLineOption options[] = { {'d', "mint-dir", "DIR", - "mint directory", 1, + "mint directory with configuration and keys for operating the mint", 1, &GNUNET_GETOPT_set_filename, &TMH_mint_directory}, +#if HAVE_DEVELOPER + {'f', "file-input", "FILENAME", + "run in test-mode using FILENAME as the HTTP request to process", 1, + &GNUNET_GETOPT_set_filename, &input_filename}, +#endif TALER_GETOPT_OPTION_HELP ("HTTP server providing a RESTful API to access a Taler mint"), GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION), GNUNET_GETOPT_OPTION_END @@ -474,6 +588,9 @@ main (int argc, char *const *argv) NULL, NULL, &handle_mhd_request, NULL, MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL, +#if HAVE_DEVELOPER + MHD_OPTION_NOTIFY_CONNECTION, &connection_done, NULL, +#endif MHD_OPTION_END); if (NULL == mydaemon) @@ -482,7 +599,22 @@ main (int argc, char *const *argv) "Failed to start HTTP server.\n"); return 1; } +#if HAVE_DEVELOPER + if (NULL != input_filename) + { + /* run only the testfile input, then terminate */ + ret = run_fake_client (); + } + else + { + /* normal behavior */ + ret = TMH_KS_loop (); + } +#else + /* normal behavior */ ret = TMH_KS_loop (); +#endif + switch (ret) { case GNUNET_OK: @@ -524,3 +656,5 @@ main (int argc, char *const *argv) TALER_MINTDB_plugin_unload (TMH_plugin); return (GNUNET_SYSERR == ret) ? 1 : 0; } + +/* end of taler-mint-httpd.c */