summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRaj Raman <rajramanca@gmail.com>2013-10-03 12:26:24 -0700
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2013-10-04 12:33:43 +0200
commit47922925bc4e277e69ae692d3205a9fcb00601bf (patch)
treec053c3567ac564a59c77ffe7a538dcecb63980c0 /src
parentbd255113a4a94a4f71fd19fdad6ebd41cbba514c (diff)
downloadgnutls-47922925bc4e277e69ae692d3205a9fcb00601bf.tar.gz
support inline command infrastructure in gnutls-cli
Signed-off-by: Raj Raman <rajramanca@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/cli-args.def13
-rw-r--r--src/cli.c382
-rwxr-xr-xsrc/inline_cmds.h74
3 files changed, 393 insertions, 76 deletions
diff --git a/src/cli-args.def b/src/cli-args.def
index b226e7e342..c4e71d83e6 100644
--- a/src/cli-args.def
+++ b/src/cli-args.def
@@ -324,6 +324,19 @@ flag = {
doc = "This option disables all TLS extensions. Deprecated option. Use the priority string.";
};
+flag = {
+ name = inline-commands;
+ descrip = "Inline commands of the form ^<cmd>^";
+ doc = "Enable inline commands of the form ^<cmd>^. The inline commands are expected to be in a line by themselves";
+};
+
+flag = {
+ name = inline-commands-prefix;
+ arg-type = string;
+ descrip = "Change the default (^) used as a delimiter for inline commands.
+\t\t\t\tThe value is a single US-ASCII character (octets 0 - 127).";
+ doc = "Change the default (^) delimiter used for inline commands. The delimiter is expected to be a single US-ASCII character (octets 0 - 127). This option is only relevant if inline commands are enabled via the inline-commands option";
+};
doc-section = {
ds-type = 'SEE ALSO'; // or anything else
diff --git a/src/cli.c b/src/cli.c
index 105b1d8263..162fa13287 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -52,6 +52,7 @@
#include "sockets.h"
#include "benchmark.h"
+#include "inline_cmds.h"
#ifdef HAVE_DANE
#include <gnutls/dane.h>
@@ -66,7 +67,7 @@
#define MAX_BUF 4096
/* global stuff here */
-int resume, starttls, insecure, ranges, rehandshake, udp, mtu;
+int resume, starttls, insecure, ranges, rehandshake, udp, mtu, inline_commands;
const char *hostname = NULL;
const char *service = NULL;
int record_max_size;
@@ -89,6 +90,7 @@ static int disable_extensions;
static int disable_sni;
static unsigned int init_flags = GNUTLS_CLIENT;
static const char * priorities = NULL;
+static const char * inline_commands_prefix;
const char *psk_username = NULL;
gnutls_datum_t psk_key = { NULL, 0 };
@@ -838,19 +840,242 @@ static int check_net_or_keyboard_input(socket_st* hd)
return IN_NONE;
}
+static int try_rehandshake(socket_st *hd)
+{
+ int ret;
+
+ ret = do_handshake (hd);
+ if (ret < 0)
+ {
+ fprintf (stderr, "*** ReHandshake has failed\n");
+ gnutls_perror (ret);
+ return ret;
+ }
+ else
+ {
+ printf ("- ReHandshake was completed\n");
+ return 0;
+ }
+}
+
+static int try_resume (socket_st *hd)
+{
+ int ret;
+
+ char *session_data;
+ size_t session_data_size = 0;
+
+ gnutls_session_get_data (hd->session, NULL, &session_data_size);
+ session_data = (char *) malloc (session_data_size);
+ if (session_data == NULL)
+ return GNUTLS_E_MEMORY_ERROR;
+
+ gnutls_session_get_data (hd->session, session_data,
+ &session_data_size);
+
+ printf ("- Disconnecting\n");
+ socket_bye (hd);
+
+ printf ("\n\n- Connecting again- trying to resume previous session\n");
+ socket_open (hd, hostname, service, udp);
+
+ hd->session = init_tls_session (hostname);
+ gnutls_session_set_data (hd->session, session_data, session_data_size);
+ free (session_data);
+
+ ret = do_handshake (hd);
+ if (ret < 0)
+ {
+ fprintf (stderr, "*** Resume handshake has failed\n");
+ gnutls_perror (ret);
+ return ret;
+ }
+
+ printf ("- Resume Handshake was completed\n");
+ if (gnutls_session_is_resumed (hd->session) != 0)
+ printf ("*** This is a resumed session\n");
+
+ return 0;
+}
+
+static
+bool parse_for_inline_commands_in_buffer (char *buffer, size_t bytes,
+ inline_cmds_st *inline_cmds)
+{
+ int local_bytes, match_bytes, prev_bytes_copied, ii, jj;
+ char *local_buffer_ptr, *ptr;
+ char inline_command_string[MAX_INLINE_COMMAND_BYTES];
+
+ inline_cmds->bytes_to_flush = 0;
+ inline_cmds->cmd_found = INLINE_COMMAND_NONE;
+
+ if (inline_cmds->bytes_copied)
+ {
+ local_buffer_ptr =
+ &inline_cmds->inline_cmd_buffer[inline_cmds->bytes_copied];
+
+ local_bytes =
+ ((inline_cmds->bytes_copied + bytes) <= MAX_INLINE_COMMAND_BYTES) ?
+ bytes : (MAX_INLINE_COMMAND_BYTES - inline_cmds->bytes_copied);
+
+ memcpy (local_buffer_ptr, buffer, local_bytes);
+ prev_bytes_copied = inline_cmds->bytes_copied;
+ inline_cmds->new_buffer_ptr = buffer + local_bytes;
+ inline_cmds->bytes_copied += local_bytes;
+ local_buffer_ptr = inline_cmds->inline_cmd_buffer;
+ local_bytes = inline_cmds->bytes_copied;
+ }
+ else
+ {
+ prev_bytes_copied = 0;
+ local_buffer_ptr = buffer;
+ local_bytes = bytes;
+ inline_cmds->new_buffer_ptr = buffer + bytes;
+ }
+
+ inline_cmds->current_ptr = local_buffer_ptr;
+
+ if (local_buffer_ptr[0] == inline_commands_prefix[0] && inline_cmds->lf_found)
+ {
+ for (jj = 0; jj < NUM_INLINE_COMMANDS; jj ++)
+ {
+ if (inline_commands_prefix[0] != '^') /* refer inline_cmds.h for usage of ^ */
+ {
+ strcpy (inline_command_string, inline_commands_def[jj].string);
+ inline_command_string[strlen(inline_commands_def[jj].string)] = '\0';
+ inline_command_string[0] = inline_commands_prefix[0];
+ /* Inline commands are delimited by the inline_commands_prefix[0] (default is ^).
+ The inline_commands_def[].string includes a trailing LF */
+ inline_command_string[strlen(inline_commands_def[jj].string) - 2] = inline_commands_prefix[0];
+ ptr = inline_command_string;
+ }
+ else
+ ptr = inline_commands_def[jj].string;
+
+ match_bytes = (local_bytes <= strlen (ptr)) ? local_bytes : strlen (ptr);
+ if (strncmp (ptr, local_buffer_ptr, match_bytes) == 0)
+ {
+ if (match_bytes == strlen (ptr))
+ {
+ inline_cmds->new_buffer_ptr = buffer + match_bytes - prev_bytes_copied;
+ inline_cmds->cmd_found = inline_commands_def[jj].command;
+ inline_cmds->bytes_copied = 0; /* reset it */
+ }
+ else
+ {
+ /* partial command */
+ memcpy (&inline_cmds->inline_cmd_buffer[inline_cmds->bytes_copied],
+ buffer, bytes);
+ inline_cmds->bytes_copied += bytes;
+ }
+ return true;
+ }
+ /* else - if not a match, do nothing here */
+ } /* for */
+ }
+
+ for (ii = prev_bytes_copied; ii < local_bytes; ii ++)
+ {
+ if (ii && local_buffer_ptr[ii] == inline_commands_prefix[0] && inline_cmds->lf_found)
+ {
+ /* possible inline command. First, let's flush bytes up to ^ */
+ inline_cmds->new_buffer_ptr = buffer + ii - prev_bytes_copied;
+ inline_cmds->bytes_to_flush = ii;
+ inline_cmds->lf_found = true;
+
+ /* bytes to flush starts at inline_cmds->current_ptr */
+ return true;
+ }
+ else if (local_buffer_ptr[ii] == '\n')
+ {
+ inline_cmds->lf_found = true;
+ }
+ else
+ {
+ inline_cmds->lf_found = false;
+ }
+ } /* for */
+
+ inline_cmds->bytes_copied = 0; /* reset it */
+ return false; /* not an inline command */
+}
+
+static
+int run_inline_command (inline_cmds_st *cmd, socket_st * hd)
+{
+ switch (cmd->cmd_found)
+ {
+ case INLINE_COMMAND_RESUME:
+ return try_resume (hd);
+ case INLINE_COMMAND_RENEGOTIATE:
+ return try_rehandshake (hd);
+ default:
+ return -1;
+ }
+}
+
+static
+int do_inline_command_processing (char *buffer_ptr, size_t curr_bytes, socket_st * hd, inline_cmds_st *inline_cmds)
+{
+ int skip_bytes, bytes;
+ bool inline_cmd_start_found;
+
+ bytes = curr_bytes;
+
+continue_inline_processing:
+ /* parse_for_inline_commands_in_buffer hunts for start of an inline command
+ * sequence. The function maintains state information in inline_cmds.
+ */
+ inline_cmd_start_found = parse_for_inline_commands_in_buffer (buffer_ptr,
+ bytes, inline_cmds);
+ if (!inline_cmd_start_found)
+ return bytes;
+
+ /* inline_cmd_start_found is set */
+
+ if (inline_cmds->bytes_to_flush)
+ {
+ /* start of an inline command sequence found, but is not
+ * at the beginning of buffer. So, we flush all preceding bytes.
+ */
+ return inline_cmds->bytes_to_flush;
+ }
+ else if (inline_cmds->cmd_found == INLINE_COMMAND_NONE)
+ {
+ /* partial command found */
+ return 0;
+ }
+ else
+ {
+ /* complete inline command found and is at the start */
+ if (run_inline_command (inline_cmds, hd))
+ return -1;
+
+ inline_cmds->cmd_found = INLINE_COMMAND_NONE;
+ skip_bytes = inline_cmds->new_buffer_ptr - buffer_ptr;
+
+ if (skip_bytes >= bytes)
+ return 0;
+ else
+ {
+ buffer_ptr = inline_cmds->new_buffer_ptr;
+ bytes -= skip_bytes;
+ goto continue_inline_processing;
+ }
+ }
+}
+
int
main (int argc, char **argv)
{
int ret;
- int ii, i, inp;
+ int ii, inp;
char buffer[MAX_BUF + 1];
- char *session_data = NULL;
- char *session_id = NULL;
- size_t session_data_size;
- size_t session_id_size = 0;
int user_term = 0, retval = 0;
socket_st hd;
- ssize_t bytes;
+ ssize_t bytes, keyboard_bytes;
+ char *keyboard_buffer_ptr;
+ inline_cmds_st inline_cmds;
#ifndef _WIN32
struct sigaction new_action;
#endif
@@ -882,60 +1107,21 @@ main (int argc, char **argv)
if (starttls)
goto after_handshake;
- for (i = 0; i < 2; i++)
+ ret = do_handshake (&hd);
+
+ if (ret < 0)
{
-
-
- if (i == 1)
- {
- hd.session = init_tls_session (hostname);
- gnutls_session_set_data (hd.session, session_data,
- session_data_size);
- free (session_data);
- }
-
- ret = do_handshake (&hd);
-
- if (ret < 0)
- {
- fprintf (stderr, "*** Handshake has failed\n");
- gnutls_perror (ret);
- gnutls_deinit (hd.session);
- return 1;
- }
- else
- {
- printf ("- Handshake was completed\n");
- if (gnutls_session_is_resumed (hd.session) != 0)
- printf ("*** This is a resumed session\n");
- }
-
- if (resume != 0 && i == 0)
- {
-
- gnutls_session_get_data (hd.session, NULL, &session_data_size);
- session_data = malloc (session_data_size);
-
- gnutls_session_get_data (hd.session, session_data,
- &session_data_size);
-
- gnutls_session_get_id (hd.session, NULL, &session_id_size);
-
- session_id = malloc (session_id_size);
- gnutls_session_get_id (hd.session, session_id, &session_id_size);
-
- printf ("- Disconnecting\n");
- socket_bye (&hd);
-
- printf
- ("\n\n- Connecting again- trying to resume previous session\n");
- socket_open (&hd, hostname, service, udp);
- }
- else
- {
- break;
- }
+ fprintf (stderr, "*** Handshake has failed\n");
+ gnutls_perror (ret);
+ gnutls_deinit (hd.session);
+ return 1;
}
+ else
+ printf ("- Handshake was completed\n");
+
+ if (resume != 0)
+ if (try_resume (&hd))
+ return 1;
after_handshake:
@@ -944,21 +1130,8 @@ after_handshake:
printf ("\n- Simple Client Mode:\n\n");
if (rehandshake)
- {
- ret = do_handshake (&hd);
-
- if (ret < 0)
- {
- fprintf (stderr, "*** ReHandshake has failed\n");
- gnutls_perror (ret);
- gnutls_deinit (hd.session);
- return 1;
- }
- else
- {
- printf ("- ReHandshake was completed\n");
- }
- }
+ if (try_rehandshake (&hd))
+ return 1;
#ifndef _WIN32
new_action.sa_handler = starttls_alarm;
@@ -978,6 +1151,12 @@ after_handshake:
setbuf (stdout, NULL);
setbuf (stderr, NULL);
+ if (inline_commands)
+ {
+ memset (&inline_cmds, 0, sizeof (inline_cmds_st));
+ inline_cmds.lf_found = true; /* initially, at start of line */
+ }
+
for (;;)
{
if (starttls_alarmed && !hd.secure)
@@ -1069,16 +1248,42 @@ after_handshake:
}
}
+ keyboard_bytes = bytes;
+ keyboard_buffer_ptr = buffer;
+
+inline_command_processing:
+
+ if (inline_commands)
+ {
+ keyboard_bytes = do_inline_command_processing (
+ keyboard_buffer_ptr, keyboard_bytes,
+ &hd, &inline_cmds);
+ if (keyboard_bytes == 0)
+ continue;
+ else if (keyboard_bytes < 0)
+ { /* error processing an inline command */
+ retval = 1;
+ break;
+ }
+ else
+ {
+ /* current_ptr could point to either an inline_cmd_buffer
+ * or may point to start or an offset into buffer.
+ */
+ keyboard_buffer_ptr = inline_cmds.current_ptr;
+ }
+ }
+
if (ranges && gnutls_record_can_use_length_hiding(hd.session))
{
gnutls_range_st range;
range.low = 0;
range.high = MAX_BUF;
- ret = socket_send_range (&hd, buffer, bytes, &range);
+ ret = socket_send_range (&hd, keyboard_buffer_ptr, keyboard_bytes, &range);
}
else
{
- ret = socket_send(&hd, buffer, bytes);
+ ret = socket_send(&hd, keyboard_buffer_ptr, keyboard_bytes);
}
if (ret > 0)
@@ -1089,6 +1294,13 @@ after_handshake:
else
handle_error (&hd, ret);
+ if (inline_commands &&
+ inline_cmds.new_buffer_ptr < (buffer + bytes))
+ {
+ keyboard_buffer_ptr = inline_cmds.new_buffer_ptr;
+ keyboard_bytes = (buffer + bytes) - keyboard_buffer_ptr;
+ goto inline_command_processing;
+ }
}
}
@@ -1174,6 +1386,24 @@ const char* rest = NULL;
if (disable_extensions)
init_flags |= GNUTLS_NO_EXTENSIONS;
+ inline_commands = HAVE_OPT(INLINE_COMMANDS);
+ if (HAVE_OPT(INLINE_COMMANDS_PREFIX))
+ {
+ if (strlen(OPT_ARG(INLINE_COMMANDS_PREFIX)) > 1)
+ {
+ fprintf(stderr, "inline-commands-prefix value is a single US-ASCII character (octets 0 - 127)\n");
+ exit(1);
+ }
+ inline_commands_prefix = (unsigned char *) OPT_ARG(INLINE_COMMANDS_PREFIX);
+ if (inline_commands_prefix[0] > 127)
+ {
+ fprintf(stderr, "inline-commands-prefix value is a single US-ASCII character (octets 0 - 127)\n");
+ exit(1);
+ }
+ }
+ else
+ inline_commands_prefix = (const unsigned char *) "^";
+
starttls = HAVE_OPT(STARTTLS);
resume = HAVE_OPT(RESUME);
rehandshake = HAVE_OPT(REHANDSHAKE);
diff --git a/src/inline_cmds.h b/src/inline_cmds.h
new file mode 100755
index 0000000000..49bc5dd120
--- /dev/null
+++ b/src/inline_cmds.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011-2012 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * The inline commands is a facility that can be used optionally
+ * when --inline-commands is set during invocation of gnutls-cli
+ * to send inline commands at any time while a secure connection
+ * between the client and server is active. This is especially
+ * useful when the HTTPS connection is (HTTP) persistent -
+ * inline commands can be issued between HTTP requests, ex: GET.
+ * session renegotiation and session resumption can be issued
+ * inline between GET requests.
+ *
+ * Following inline commands are currently supported:
+ * ^resume^ - perform session resumption (similar to option -r)
+ * ^renegotiate^ - perform session renegotiation (similar to option -e)
+ *
+ * inline-commands-prefix is an additional option that can be set
+ * from gnutls-cli to change the default prefix (^) of inline commands.
+ * This option is only relevant if inline-commands option is enabled.
+ * This option expects a single US-ASCII character (octets 0 - 127).
+ * For ex: if --inline-commands-prefix=@, the inline commands will be
+ * @resume@, @renegotiate@, etc...
+ */
+typedef enum INLINE_COMMAND
+{ INLINE_COMMAND_NONE,
+ INLINE_COMMAND_RESUME,
+ INLINE_COMMAND_RENEGOTIATE
+} inline_command_t;
+#define NUM_INLINE_COMMANDS 2
+
+#define MAX_INLINE_COMMAND_BYTES 20
+
+typedef struct inline_cmds
+{
+ char *current_ptr; /* points to the start of the current buffer being processed */
+ char *new_buffer_ptr; /* points to start or offset within the caller's buffer,
+ * and refers to bytes yet to be processed. */
+ inline_command_t cmd_found;
+ int lf_found;
+ int bytes_to_flush;
+ ssize_t bytes_copied;
+ char inline_cmd_buffer[MAX_INLINE_COMMAND_BYTES];
+} inline_cmds_st;
+
+
+struct inline_command_definitions
+{
+ int command;
+ char string[MAX_INLINE_COMMAND_BYTES];
+};
+
+/* All inline commands will contain a trailing LF */
+struct inline_command_definitions inline_commands_def[] =
+{
+ {INLINE_COMMAND_RESUME, "^resume^\n"},
+ {INLINE_COMMAND_RENEGOTIATE, "^renegotiate^\n"},
+};