[network-manager-vpnc/dcbw/need-secrets: 1/3] core: add function and testcase to parse vpnc stdout/stderr
- From: Dan Williams <dcbw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [network-manager-vpnc/dcbw/need-secrets: 1/3] core: add function and testcase to parse vpnc stdout/stderr
- Date: Fri, 13 Dec 2013 20:06:22 +0000 (UTC)
commit ac9e6adeb500d0ae5497d2e1612859419b8aea4a
Author: Dan Williams <dcbw redhat com>
Date: Tue Jun 25 08:48:21 2013 -0500
core: add function and testcase to parse vpnc stdout/stderr
We'll use this to run vpnc in interactive mode and handle server
messages and input prompts during the connection, instead of all
before connecting.
src/Makefile.am | 17 +++-
src/test-vpnc-output.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++
src/utils.c | 166 ++++++++++++++++++++++++++++
src/utils.h | 35 ++++++
4 files changed, 497 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 3e458d6..aaa467f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,8 +19,9 @@ libexec_PROGRAMS = nm-vpnc-service nm-vpnc-service-vpnc-helper
nm_vpnc_service_SOURCES = \
nm-vpnc-service.c \
- nm-vpnc-service.h
-
+ nm-vpnc-service.h \
+ utils.c \
+ utils.h
nm_vpnc_service_LDADD = \
$(DBUS_LIBS) \
@@ -36,4 +37,16 @@ nm_vpnc_service_vpnc_helper_LDADD = \
$(GTHREAD_LIBS) \
$(NM_LIBS)
+noinst_PROGRAMS = test-vpnc-output
+
+test_vpnc_output_SOURCES = \
+ test-vpnc-output.c \
+ utils.c \
+ utils.h
+
+test_vpnc_output_LDADD = $(GLIB_LIBS)
+
+check-local:
+ $(abs_builddir)/test-vpnc-output
+
CLEANFILES = *~
diff --git a/src/test-vpnc-output.c b/src/test-vpnc-output.c
new file mode 100644
index 0000000..e722946
--- /dev/null
+++ b/src/test-vpnc-output.c
@@ -0,0 +1,281 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include "utils.h"
+
+#define TEST_HEADER \
+ GString *output;\
+ guint pos = 0, num, olen, linelen;\
+ gboolean message_done = FALSE;\
+ GString *message = g_string_new (NULL);\
+ gsize consumed;\
+ const char *p;\
+\
+ output = g_string_sized_new (512);\
+ olen = strlen (o);\
+ while (pos < olen) {\
+ num = g_random_int_range (0, MIN (olen - pos, 30));\
+ if (num == 0)\
+ num++;\
+ g_string_append_len (output, o + pos, num);\
+ pos += num;\
+ p = strchr (output->str, '\n');\
+ linelen = p ? (p - output->str) + 1 : output->len;\
+
+#define TEST_FOOTER \
+ g_string_erase (output, 0, consumed);\
+ } \
+
+#define TEST_CLEANUP \
+ g_string_free (output, TRUE);\
+ g_string_free (message, TRUE);
+
+
+static void
+test_no_prompt (const char *data, gsize dlen, gpointer user_data)
+{
+ g_assert_not_reached ();
+}
+
+static void
+test_output_simple (void)
+{
+ const char *o = \
+"vpnc version 0.5.3\n\
+ hex_test: 00010203\n\
+\n\
+S1 init_sockaddr\n\
+ [2011-06-03 11:11:12]\n\
+S3 setup_tunnel\n\
+ [2011-06-03 11:11:12]\n\
+ using interface tun0\n\
+\n\
+S4.1 create_nonce\n\
+ [2011-06-03 11:11:12]\n\
+ i_cookie: 4b3e235d 02d5dcd5\n\
+ i_nonce:\n\
+ a1d46e05 4175bbcc 6de34f7d fc374fe4 2acb8991\n\
+\n\
+S4.3 AM packet_1\n\
+ [2011-06-03 11:11:12]\n\
+\n\
+ sending: ========================>\n\
+ BEGIN_PARSE\n\
+ Recieved Packet Len: 1287\n\
+ i_cookie: 4b3e235d 02d5dcd5\n\
+ r_cookie: 00000000 00000000\n\
+ payload: 01 (ISAKMP_PAYLOAD_SA)\n\
+ isakmp_version: 10\n\
+ exchange_type: 04 (ISAKMP_EXCHANGE_AGGRESSIVE)\n\
+ flags: 00\n\
+ message_id: 00000000\n\
+ len: 00000507\n\
+ \n\
+ PARSING PAYLOAD type: 03 (ISAKMP_PAYLOAD_T)\n\
+ next_type: 03 (ISAKMP_PAYLOAD_T)\n\
+ length: 0028\n\
+ t.number: 00\n\
+ t.id: 01 (ISAKMP_IPSEC_KEY_IKE)\n\
+ t.attributes.type: 000e (IKE_ATTRIB_KEY_LENGTH)\n\
+ t.attributes.u.attr_16: 0100\n\
+ t.attributes.type: 0001 (IKE_ATTRIB_ENC)\n\
+ t.attributes.type: 000c (IKE_ATTRIB_LIFE_DURATION)\n\
+ t.attributes.u.lots.length: 0004\n\
+ t.attributes.u.lots.data: 0020c49b\n\
+ DONE PARSING PAYLOAD type: 03 (ISAKMP_PAYLOAD_T)\n\
+";
+
+ TEST_HEADER
+ /* We expect no input prompts and no server messages */
+ consumed = utils_handle_output (output, message, &message_done, test_no_prompt, NULL);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+ g_assert_cmpint (message->len, ==, 0);
+ g_assert (!message_done);
+ TEST_FOOTER
+
+ g_assert_cmpstr (message->str, ==, "");
+ g_assert_cmpint (message->len, ==, 0);
+
+ TEST_CLEANUP
+}
+
+static void
+test_output_message (void)
+{
+ const char *o = \
+"S5.4 xauth type check\n\
+ [2011-06-03 11:11:13]\n\
+\n\
+Wait for token to change,\n\
+then enter the new tokencode:\n\
+\n\
+S5.5 do xauth authentication\n\
+ [2011-06-03 11:11:13]\n\
+";
+ const char *expected_message = "Wait for token to change,\nthen enter the new tokencode:\n";
+
+ TEST_HEADER
+ /* We expect a server message but no input prompts */
+ consumed = utils_handle_output (output, message, &message_done, test_no_prompt, NULL);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+
+ if (message_done)
+ g_assert_cmpstr (message->str, ==, expected_message);
+ TEST_FOOTER
+
+ g_assert (message_done);
+ TEST_CLEANUP
+}
+
+static void
+test_output_message_oneline (void)
+{
+ const char *o = \
+"S5.3 type-is-xauth check\n\
+ [2013-06-27 11:24:50]\n\
+\n\
+S5.4 xauth type check\n\
+ [2013-06-27 11:24:50]\n\
+Enter Username and Password.\n\
+\n\
+S5.5 do xauth reply\n\
+ [2013-06-27 11:24:50]\n\
+";
+ const char *expected_message = "Enter Username and Password.\n";
+
+ TEST_HEADER
+ /* We expect a server message but no input prompts */
+ consumed = utils_handle_output (output, message, &message_done, test_no_prompt, NULL);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+
+ if (message_done)
+ g_assert_cmpstr (message->str, ==, expected_message);
+ TEST_FOOTER
+
+ g_assert (message_done);
+ TEST_CLEANUP
+}
+
+static void
+test_has_prompt (const char *data, gsize dlen, gpointer user_data)
+{
+ g_assert_cmpstr (data, ==, (const char *) user_data);
+}
+
+static void
+test_output_prompt (void)
+{
+ const char *o = \
+"S5.5 do xauth authentication\n\
+ [2011-06-03 11:11:13]\n\
+Password for VPN person 1 1 1 1: ";
+
+ const char *expected_prompt = "Password for VPN person 1 1 1 1: ";
+
+ TEST_HEADER
+ /* We expect an input prompt but no server message */
+ consumed = utils_handle_output (output, message, &message_done, test_has_prompt, (gpointer)
expected_prompt);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+
+ g_assert_cmpint (message->len, ==, 0);
+ g_assert (!message_done);
+ TEST_FOOTER
+
+ TEST_CLEANUP
+}
+
+static void
+test_output_message_and_prompt (void)
+{
+ const char *o = \
+"Wait for token to change,\n\
+then enter the new tokencode:\n\
+\n\
+Password for VPN person 1 1 1 1: ";
+
+ const char *expected_prompt = "Password for VPN person 1 1 1 1: ";
+ const char *expected_message = "Wait for token to change,\nthen enter the new tokencode:\n";
+
+ TEST_HEADER
+ /* We expect an input prompt but no server message */
+ consumed = utils_handle_output (output, message, &message_done, test_has_prompt, (gpointer)
expected_prompt);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+ TEST_FOOTER
+
+ g_assert_cmpstr (message->str, ==, expected_message);
+ g_assert (message_done);
+
+ TEST_CLEANUP
+}
+
+static void
+test_output_message_and_prompt_debug (void)
+{
+ const char *o = \
+"S5.3 type-is-xauth check\n\
+ [2011-06-03 11:11:13]\n\
+\n\
+S5.4 xauth type check\n\
+ [2011-06-03 11:11:13]\n\
+\n\
+Wait for token to change,\n\
+then enter the new tokencode:\n\
+\n\
+S5.5 do xauth authentication\n\
+ [2011-06-03 11:11:13]\n\
+Password for VPN person 1 1 1 1: ";
+
+ const char *expected_prompt = "Password for VPN person 1 1 1 1: ";
+ const char *expected_message = "Wait for token to change,\nthen enter the new tokencode:\n";
+
+ TEST_HEADER
+ /* We expect an input prompt but no server message */
+ consumed = utils_handle_output (output, message, &message_done, test_has_prompt, (gpointer)
expected_prompt);
+ if (consumed)
+ g_assert_cmpint (consumed, ==, linelen);
+ TEST_FOOTER
+
+ g_assert_cmpstr (message->str, ==, expected_message);
+ g_assert (message_done);
+
+ TEST_CLEANUP
+}
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/output/simple", test_output_simple);
+ g_test_add_func ("/output/message", test_output_message);
+ g_test_add_func ("/output/message-oneline", test_output_message_oneline);
+ g_test_add_func ("/output/prompt", test_output_prompt);
+ g_test_add_func ("/output/message-and-prompt", test_output_message_and_prompt);
+ g_test_add_func ("/output/message-and-prompt-debug", test_output_message_and_prompt_debug);
+
+ return g_test_run ();
+}
+
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..7ac76b2
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2013 Red Hat, Inc.
+ */
+
+#include <string.h>
+#include "utils.h"
+
+#define IS_EOL(a) (a == '\r' || a == '\n')
+#define VPNC_VERSION_STR "vpnc version "
+
+/**
+ * utils_handle_output:
+ * @output: buffer containing vpnc output
+ * @server_message: buffer in which to store a message from the VPN server
+ * @server_message_done: flag which is set to %TRUE when a server message is
+ * complete
+ * @prompt_fn: function to call when vpnc (or the server) sends a request for
+ * passwords or more information
+ * @prompt_fn_data: pointer to pass to @prompt_fn
+ *
+ * Parses new vpnc output to extract server messages and detect prompts for
+ * more information. Since vpnc can print variable numbers of bytes at a time,
+ * not necessarily a complete line or block, this function should be called
+ * multiple times on the same buffer. It will return the number of bytes which
+ * it consumed, and that number of bytes should be removed from the start of
+ * @output. If a request for a password or username is parsed, it will call
+ * @prompt_fn with the prompt message.
+ *
+ * Returns: the number of bytes consumed, which should be removed from the
+ * start of @output.
+ **/
+gsize
+utils_handle_output (GString *output,
+ GString *server_message,
+ gboolean *server_message_done,
+ PromptFn prompt_fn,
+ gpointer prompt_fn_data)
+{
+ guint32 i;
+
+ g_return_val_if_fail (output != NULL, 0);
+ g_return_val_if_fail (server_message != NULL, 0);
+ g_return_val_if_fail (server_message_done != NULL, 0);
+ g_return_val_if_fail (prompt_fn != NULL, 0);
+
+ /* vpnc output is loosely formatted, with "blocks of interest" starting with
+ * no leading whitespace, and separated by double newlines, but unfortunately
+ * it doesn't output both newlines at the same time (one newline is printed
+ * at the end of one block and a second at the start of the next block with
+ * variable time in between) and some input prompts don't print newlines at
+ * all.
+ *
+ * S5.4 xauth type check
+ * [2011-06-03 11:11:13]
+ *
+ * Wait for token to change, (server message line #1)
+ * then enter the new tokencode: (server message line #2)
+ *
+ * S5.5 do xauth authentication
+ * [2011-06-03 11:11:13]
+ * Password for VPN person 1 1 1 1: (waits for input without newline)
+ * size = 42, blksz = 16, padding = 6
+ *
+ * So we can't just listen for '\n\n' or we won't react immediately to
+ * input prompts or correctly process service messages.
+ *
+ * Instead we pay attention to any lines that have no leading whitespace
+ * and do not start with "S[1 - 9]". If the line ends with ":" it is an
+ * input prompt. If it doesn't then we cache it and wait for the next line
+ * or newline, in which case it's a server message.
+ */
+
+ if (output->len == 0)
+ return 0;
+
+ /* Find the end of the line or the end of the string; all lines *except*
+ * prompts will be newline terminated, while prompts stop at the end of the
+ * buffer because vpnc is waiting for the input.
+ */
+ for (i = 0; i < output->len; i++) {
+ if (!output->str[i] || IS_EOL (output->str[i]))
+ break;
+ }
+
+ /* Decide whether to stop parsing a server message, which is terminated by
+ * a single empty line or some whitespace; it looks like:
+ *
+ * <stuff>
+ *
+ * Wait for token to change,
+ * then enter the new tokencode:
+ *
+ * <more stuff>
+ */
+ if (server_message->len) {
+ if (g_ascii_isspace (output->str[0]) || (i == 1 && IS_EOL (output->str[i])))
+ *server_message_done = TRUE;
+ }
+
+ if (i < output->len) {
+ /* Lines starting with whitespace are debug output that we don't care
+ * about.
+ */
+ if (g_ascii_isspace (output->str[0]))
+ return i + 1;
+ } else if (i == output->len) {
+ /* Check for a prompt; it will not begin with whitespace, and will end
+ * with a ':' and no newline, because vpnc will be waiting for the response.
+ */
+ if (!g_ascii_isspace (output->str[0]) &&
+ (i > 2) &&
+ (strncmp ((output->str + (i - 2)), ": ", 2) == 0)) {
+ /* Note: if vpnc sent a server message ending with ':' but we
+ * happened to only read up to the ':' but not the EOL, we'll
+ * confuse the server message with an input prompt. vpnc is not
+ * helpful here.
+ */
+ prompt_fn (output->str, i, prompt_fn_data);
+ return i;
+ }
+
+ /* No newline and no ending semicolon; probably a partial read so wait
+ * for more output
+ */
+ return 0;
+ } else
+ g_assert_not_reached ();
+
+ /* No newline at the end, wait for one */
+ if (!IS_EOL (output->str[i]))
+ return 0;
+
+ /* Ignore vpnc version debug output */
+ if (i >= strlen (VPNC_VERSION_STR) &&
+ strncmp (output->str, VPNC_VERSION_STR, strlen (VPNC_VERSION_STR)) == 0)
+ return i + 1;
+
+ /* Ignore vpnc debug messages like "S1 init_sockaddr" */
+ if (i > 2 && output->str[0] == 'S' && g_ascii_isdigit (output->str[1]))
+ return i + 1;
+
+ /* What's left is probably a server message */
+ if (*server_message_done) {
+ g_string_truncate (server_message, 0);
+ *server_message_done = FALSE;
+ }
+ g_string_append_len (server_message, output->str, i + 1);
+ return i + 1;
+}
+
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..a2cb49a
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <glib.h>
+
+typedef void (*PromptFn) (const char *data, gsize len, gpointer user_data);
+
+gsize utils_handle_output (GString *output,
+ GString *server_message,
+ gboolean *server_message_done,
+ PromptFn prompt_fn,
+ gpointer prompt_fn_data);
+
+#endif /* _UTILS_H_ */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]