]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
support inline command infrastructure in gnutls-cli
authorRaj Raman <rajramanca@gmail.com>
Thu, 3 Oct 2013 19:26:24 +0000 (12:26 -0700)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Fri, 4 Oct 2013 10:33:43 +0000 (12:33 +0200)
Signed-off-by: Raj Raman <rajramanca@gmail.com>
src/cli-args.def
src/cli.c
src/inline_cmds.h [new file with mode: 0755]

index b226e7e342b6f2f5cd88499d197be383d731625f..c4e71d83e6a1b679edca4733e5142d5fe913de9a 100644 (file)
@@ -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
index 105b1d8263c195c41c3d72d51e73f6fc3157d0ab..162fa132870419649a0c0911810d9dfdd8485348 100644 (file)
--- 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 (executable)
index 0000000..49bc5dd
--- /dev/null
@@ -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"},
+};