[network-manager-openvpn/dcbw/config-parsing: 1/2] core: implement testcases for openvpn config parsing
- From: Dan Williams <dcbw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [network-manager-openvpn/dcbw/config-parsing: 1/2] core: implement testcases for openvpn config parsing
- Date: Fri, 5 Dec 2014 17:57:32 +0000 (UTC)
commit 49c735c1438fa7d9e8327550c4d9593218b874ce
Author: Dan Williams <dcbw redhat com>
Date: Thu Dec 4 14:41:19 2014 -0600
core: implement testcases for openvpn config parsing
configure.ac | 1 +
src/Makefile.am | 4 +
src/helper-config.c | 671 ++++++++++++++++++++++++++++++++++++++++
src/helper-config.h | 40 +++
src/tests/Makefile.am | 22 ++
src/tests/test-helper-config.c | 390 +++++++++++++++++++++++
6 files changed, 1128 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 353b9b8..8616600 100644
--- a/configure.ac
+++ b/configure.ac
@@ -125,6 +125,7 @@ fi
AC_CONFIG_FILES([
Makefile
src/Makefile
+src/tests/Makefile
common/Makefile
auth-dialog/Makefile
properties/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 700f0d9..544974c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS= . tests
+
AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(DBUS_CFLAGS) \
@@ -26,6 +28,8 @@ nm_openvpn_service_LDADD = \
$(top_builddir)/common/libnm-openvpn-common.la
nm_openvpn_service_openvpn_helper_SOURCES = \
+ helper-config.c \
+ helper-config.h \
nm-openvpn-service-openvpn-helper.c
nm_openvpn_service_openvpn_helper_LDADD = \
diff --git a/src/helper-config.c b/src/helper-config.c
new file mode 100644
index 0000000..2d30b49
--- /dev/null
+++ b/src/helper-config.c
@@ -0,0 +1,671 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-openvpn-service-openvpn-helper - helper called after OpenVPN established
+ * a connection, uses DBUS to send information back to nm-openvpn-service
+ *
+ * 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.
+ *
+ * Copyright 2005 - 2014 Red Hat, Inc.
+ */
+
+#include "config.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "nm-utils.h"
+
+#include "helper-config.h"
+#include "nm-openvpn-service.h"
+
+static GValue *
+str_to_gvalue (const char *str, gboolean try_convert, GError **error)
+{
+ GValue *val;
+ char *converted = NULL;
+
+ /* Empty */
+ if (!str || !str[0]) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "expected non-empty string");
+ return NULL;
+ }
+
+ if (!g_utf8_validate (str, -1, NULL)) {
+ if (try_convert) {
+ converted = g_convert (str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL);
+ if (!converted)
+ converted = g_convert (str, -1, "C", "UTF-8", NULL, NULL, NULL);
+ if (!converted) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR,
NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "failed to convert non-UTF-8 string");
+ return NULL;
+ }
+ str = converted;
+ } else {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "string not UTF-8");
+ return NULL;
+ }
+ }
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+ g_free (converted);
+
+ return val;
+}
+
+static GValue *
+uint_to_gvalue (guint32 num)
+{
+ GValue *val;
+
+ if (num == 0)
+ return NULL;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, num);
+
+ return val;
+}
+
+static GValue *
+bool_to_gvalue (gboolean b)
+{
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (val, b);
+ return val;
+}
+
+static GValue *
+addr4_to_gvalue (const char *str, GError **error)
+{
+ struct in_addr temp_addr;
+ GValue *val;
+
+ /* Empty */
+ if (!str || !str[0]) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "expected IPv4 address, not empty string");
+ return NULL;
+ }
+
+ if (inet_pton (AF_INET, str, &temp_addr) <= 0) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "failed to convert IPv4 address '%s'", str);
+ return NULL;
+ }
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, temp_addr.s_addr);
+
+ return val;
+}
+
+static GValue *
+parse_addr4_list (GValue *value_array, const char *str)
+{
+ char **split;
+ int i;
+ struct in_addr temp_addr;
+ GArray *array;
+
+ /* Empty */
+ if (!str || !str[0])
+ return value_array;
+
+ if (value_array)
+ array = (GArray *) g_value_get_boxed (value_array);
+ else
+ array = g_array_new (FALSE, FALSE, sizeof (guint));
+
+ split = g_strsplit (str, " ", -1);
+ for (i = 0; split[i]; i++) {
+ if (inet_pton (AF_INET, split[i], &temp_addr) > 0)
+ g_array_append_val (array, temp_addr.s_addr);
+ }
+
+ g_strfreev (split);
+
+ if (!value_array && array->len > 0) {
+ value_array = g_slice_new0 (GValue);
+ g_value_init (value_array, DBUS_TYPE_G_UINT_ARRAY);
+ g_value_set_boxed (value_array, array);
+ }
+ if (!value_array)
+ g_array_free (array, TRUE);
+
+ return value_array;
+}
+
+static GValue *
+addr6_to_gvalue (const char *str, GError **error)
+{
+ struct in6_addr temp_addr;
+ GValue *val;
+ GByteArray *ba;
+
+ /* Empty */
+ if (!str || !str[0]) {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "expected IPv6 address, not empty string");
+ return NULL;
+ }
+
+ if (inet_pton (AF_INET6, str, &temp_addr) <= 0) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "failed to convert IPv6 address '%s'", str);
+ return NULL;
+ }
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_new ();
+ g_byte_array_append (ba, (guint8 *) &temp_addr, sizeof (temp_addr));
+ g_value_take_boxed (val, ba);
+ return val;
+}
+
+static inline gboolean
+is_domain_valid (const char *str)
+{
+ return (str && (strlen(str) >= 1) && (strlen(str) <= 255));
+}
+
+#define BUFLEN 256
+
+static GValue *
+get_ip4_routes (void)
+{
+ GValue *value = NULL;
+ GPtrArray *routes;
+ char *tmp;
+ int i;
+
+ routes = g_ptr_array_new ();
+
+ for (i = 1; i < 256; i++) {
+ GArray *array;
+ char buf[BUFLEN];
+ struct in_addr network;
+ struct in_addr netmask;
+ struct in_addr gateway = { 0, };
+ guint32 prefix, metric = 0;
+
+ snprintf (buf, BUFLEN, "route_network_%d", i);
+ tmp = getenv (buf);
+ if (!tmp || !tmp[0])
+ break;
+
+ if (inet_pton (AF_INET, tmp, &network) <= 0) {
+ g_warning ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
+ continue;
+ }
+
+ snprintf (buf, BUFLEN, "route_netmask_%d", i);
+ tmp = getenv (buf);
+ if (!tmp || inet_pton (AF_INET, tmp, &netmask) <= 0) {
+ g_warning ("Ignoring invalid static route netmask '%s'", tmp ? tmp : "NULL");
+ continue;
+ }
+
+ snprintf (buf, BUFLEN, "route_gateway_%d", i);
+ tmp = getenv (buf);
+ /* gateway can be missing */
+ if (tmp && (inet_pton (AF_INET, tmp, &gateway) <= 0)) {
+ g_warning ("Ignoring invalid static route gateway '%s'", tmp ? tmp : "NULL");
+ continue;
+ }
+
+ snprintf (buf, BUFLEN, "route_metric_%d", i);
+ tmp = getenv (buf);
+ /* metric can be missing */
+ if (tmp && tmp[0]) {
+ long int tmp_metric;
+
+ errno = 0;
+ tmp_metric = strtol (tmp, NULL, 10);
+ if (errno || tmp_metric < 0 || tmp_metric > G_MAXUINT32) {
+ g_warning ("Ignoring invalid static route metric '%s'", tmp);
+ continue;
+ }
+ metric = (guint32) tmp_metric;
+ }
+
+ array = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
+ g_array_append_val (array, network.s_addr);
+ prefix = nm_utils_ip4_netmask_to_prefix (netmask.s_addr);
+ g_array_append_val (array, prefix);
+ g_array_append_val (array, gateway.s_addr);
+ g_array_append_val (array, metric);
+ g_ptr_array_add (routes, array);
+ }
+
+ if (routes->len > 0) {
+ value = g_new0 (GValue, 1);
+ g_value_init (value, DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT);
+ g_value_take_boxed (value, routes);
+ } else
+ g_ptr_array_free (routes, TRUE);
+
+ return value;
+}
+
+static GValue *
+get_ip6_routes (void)
+{
+ GValue *value = NULL;
+ GSList *routes;
+ char *tmp;
+ int i;
+
+ routes = NULL;
+
+ for (i = 1; i < 256; i++) {
+ NMIP6Route *route;
+ char buf[BUFLEN];
+ struct in6_addr network, gateway;
+ guint32 prefix;
+ gchar **dest_prefix;
+
+ snprintf (buf, BUFLEN, "route_ipv6_network_%d", i);
+ tmp = getenv (buf);
+ if (!tmp || !tmp[0])
+ break;
+
+ /* Split network string in "dest/prefix" format */
+ dest_prefix = g_strsplit (tmp, "/", 2);
+
+ tmp = dest_prefix[0];
+ if (inet_pton (AF_INET6, tmp, &network) <= 0) {
+ g_warning ("Ignoring invalid static route address '%s'", tmp ? tmp : "NULL");
+ g_strfreev (dest_prefix);
+ continue;
+ }
+
+ tmp = dest_prefix[1];
+ if (tmp) {
+ long int tmp_prefix;
+
+ errno = 0;
+ tmp_prefix = strtol (tmp, NULL, 10);
+ if (errno || tmp_prefix <= 0 || tmp_prefix > 128) {
+ g_warning ("Ignoring invalid static route prefix '%s'", tmp ? tmp : "NULL");
+ g_strfreev (dest_prefix);
+ continue;
+ }
+ prefix = (guint32) tmp_prefix;
+ } else {
+ g_warning ("Ignoring static route %d with no prefix length", i);
+ g_strfreev (dest_prefix);
+ continue;
+ }
+ g_strfreev (dest_prefix);
+
+ snprintf (buf, BUFLEN, "route_ipv6_gateway_%d", i);
+ tmp = getenv (buf);
+ /* gateway can be missing */
+ if (tmp && (inet_pton (AF_INET6, tmp, &gateway) <= 0)) {
+ g_warning ("Ignoring invalid static route gateway '%s'", tmp ? tmp : "NULL");
+ continue;
+ }
+
+ route = nm_ip6_route_new ();
+ nm_ip6_route_set_dest (route, &network);
+ nm_ip6_route_set_prefix (route, prefix);
+ nm_ip6_route_set_next_hop (route, &gateway);
+
+ routes = g_slist_append (routes, route);
+ }
+
+ if (routes) {
+ GSList *iter;
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE);
+ nm_utils_ip6_routes_to_gvalue (routes, value);
+
+ for (iter = routes; iter; iter = iter->next)
+ nm_ip6_route_unref (iter->data);
+ g_slist_free (routes);
+ }
+
+ return value;
+}
+
+static GValue *
+trusted_remote_to_gvalue (GError **error)
+{
+ const char *tmp;
+ GValue *val = NULL;
+ const char *p;
+ gboolean is_name = FALSE;
+
+ tmp = getenv ("trusted_ip6");
+ if (tmp) {
+ val = addr6_to_gvalue (tmp, error);
+ if (!val)
+ g_prefix_error (error, "%s: failed to convert VPN gateway address (trusted_ip6): ",
__func__);
+ return val;
+ }
+
+ tmp = getenv ("trusted_ip");
+ if (!tmp)
+ tmp = getenv ("remote_1");
+ if (!tmp) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "%s: did not receive remote gateway address (trusted_ip or remote_1)", __func__);
+ return NULL;
+ }
+
+ /* Check if it seems to be a hostname */
+ p = tmp;
+ while (*p) {
+ if (*p != '.' && !isdigit (*p)) {
+ is_name = TRUE;
+ break;
+ }
+ p++;
+ }
+
+ /* Resolve a hostname if required. Only look for IPv4 addresses */
+ if (is_name) {
+ struct in_addr addr;
+ struct addrinfo hints;
+ struct addrinfo *result = NULL, *rp;
+ int err;
+
+ addr.s_addr = 0;
+ memset (&hints, 0, sizeof (hints));
+
+ hints.ai_family = AF_INET;
+ hints.ai_flags = AI_ADDRCONFIG;
+ err = getaddrinfo (tmp, NULL, &hints, &result);
+ if (err != 0) {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "%s: failed to look up VPN gateway address '%s' (%d)",
+ __func__, tmp, err);
+ return NULL;
+ }
+
+ /* FIXME: so what if the name resolves to multiple IP addresses? We
+ * don't know which one pptp decided to use so we could end up using a
+ * different one here, and the VPN just won't work.
+ */
+ for (rp = result; rp; rp = rp->ai_next) {
+ if ( (rp->ai_family == AF_INET)
+ && (rp->ai_addrlen == sizeof (struct sockaddr_in))) {
+ struct sockaddr_in *inptr = (struct sockaddr_in *) rp->ai_addr;
+
+ memcpy (&addr, &(inptr->sin_addr), sizeof (struct in_addr));
+ break;
+ }
+ }
+
+ freeaddrinfo (result);
+ if (addr.s_addr != 0)
+ return uint_to_gvalue (addr.s_addr);
+ else {
+ g_set_error (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "%s: failed to convert or look up VPN gateway address '%s'",
+ __func__, tmp);
+ return NULL;
+ }
+ } else {
+ val = addr4_to_gvalue (tmp, error);
+ if (val == NULL) {
+ g_prefix_error (error, "%s: failed to convert VPN gateway address: ", __func__);
+ return NULL;
+ }
+ }
+
+ return val;
+}
+
+gboolean
+helper_generate_config (const char **args,
+ gboolean is_tapdev,
+ gboolean is_restart,
+ GHashTable **out_config,
+ GHashTable **out_ip4_config,
+ GHashTable **out_ip6_config,
+ GError **error)
+{
+ GHashTable *config = NULL, *ip4config = NULL, *ip6config = NULL;
+ const char *tmp;
+ GValue *val;
+ int i;
+ GValue *dns_list = NULL;
+ GValue *nbns_list = NULL;
+ GPtrArray *dns_domains = NULL;
+ struct in_addr temp_addr;
+
+ config = g_hash_table_new (g_str_hash, g_str_equal);
+ ip4config = g_hash_table_new (g_str_hash, g_str_equal);
+ ip6config = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* External world-visible VPN gateway */
+ val = trusted_remote_to_gvalue (error);
+ if (!val)
+ return FALSE;
+ g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, val);
+
+ /* Internal VPN subnet gateway */
+ tmp = getenv ("route_vpn_gateway");
+ val = addr4_to_gvalue (tmp, NULL);
+ if (val)
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, val);
+ else {
+ val = addr6_to_gvalue (tmp, NULL);
+ if (val)
+ g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, val);
+ }
+
+ /* VPN device */
+ tmp = getenv ("dev");
+ val = str_to_gvalue (tmp, FALSE, error);
+ if (!val) {
+ g_prefix_error (error, "%s: failed to parse 'dev': ", __func__);
+ return FALSE;
+ }
+ g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_TUNDEV, val);
+
+ /* IPv4 address */
+ tmp = getenv ("ifconfig_local");
+ if (!tmp && is_restart)
+ tmp = args[4];
+ if (tmp && tmp[0]) {
+ val = addr4_to_gvalue (tmp, error);
+ if (!val) {
+ g_prefix_error (error, "%s: failed to parse 'ifconfig_local': ", __func__);
+ return FALSE;
+ }
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, val);
+ }
+
+ /* PTP address; for vpnc PTP address == internal IP4 address */
+ tmp = getenv ("ifconfig_remote");
+ if (!tmp && is_restart)
+ tmp = args[5];
+ val = addr4_to_gvalue (tmp, NULL);
+ if (val) {
+ /* Sigh. Openvpn added 'topology' stuff in 2.1 that changes the meaning
+ * of the ifconfig bits without actually telling you what they are
+ * supposed to mean; basically relying on specific 'ifconfig' behavior.
+ */
+ if (tmp && !strncmp (tmp, "255.", 4)) {
+ guint32 addr;
+
+ /* probably a netmask, not a PTP address; topology == subnet */
+ addr = g_value_get_uint (val);
+ g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (addr));
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
+ } else
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PTP, val);
+ }
+
+ /* Netmask
+ *
+ * Either TAP or TUN modes can have an arbitrary netmask in newer versions
+ * of openvpn, while in older versions only TAP mode would. So accept a
+ * netmask if passed, otherwise default to /32 for TUN devices since they
+ * are usually point-to-point.
+ */
+ tmp = getenv ("ifconfig_netmask");
+ if (tmp && inet_pton (AF_INET, tmp, &temp_addr) > 0) {
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, nm_utils_ip4_netmask_to_prefix (temp_addr.s_addr));
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
+ } else if (!is_tapdev) {
+ if (!g_hash_table_lookup (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX)) {
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_UINT);
+ g_value_set_uint (val, 32);
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, val);
+ }
+ } else {
+ g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
+ "missing or invalid netmask/prefix");
+ return FALSE;
+ }
+
+ val = get_ip4_routes ();
+ if (val)
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, val);
+
+ /* IPv6 address */
+ tmp = getenv ("ifconfig_ipv6_local");
+ if (tmp && tmp[0]) {
+ val = addr6_to_gvalue (tmp, error);
+ if (!val) {
+ g_prefix_error (error, "%s: failed to parse 'ifconfig_ipv6_local': ", __func__);
+ return FALSE;
+ }
+ g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, val);
+ }
+
+ /* IPv6 remote address */
+ tmp = getenv ("ifconfig_ipv6_remote");
+ if (tmp && tmp[0]) {
+ val = addr6_to_gvalue (tmp, error);
+ if (!val) {
+ g_prefix_error (error, "%s: failed to parse 'ifconfig_ipv6_remote': ", __func__);
+ return FALSE;
+ }
+ g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_PTP, val);
+ }
+
+ /* IPv6 netbits */
+ tmp = getenv ("ifconfig_ipv6_netbits");
+ if (tmp && tmp[0]) {
+ long int netbits;
+
+ errno = 0;
+ netbits = strtol (tmp, NULL, 10);
+ if (errno || netbits < 0 || netbits > 128) {
+ g_warning ("Ignoring invalid prefix '%s'", tmp);
+ } else {
+ val = uint_to_gvalue ((guint32) netbits);
+ g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, val);
+ }
+ }
+
+ val = get_ip6_routes ();
+ if (val)
+ g_hash_table_insert (ip6config, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, val);
+
+ /* DNS and WINS servers */
+ dns_domains = g_ptr_array_sized_new (3);
+ for (i = 1; i < 256; i++) {
+ char buf[50];
+
+ snprintf (buf, sizeof (buf), "foreign_option_%d", i);
+ tmp = getenv (buf);
+
+ if (!tmp || !tmp[0])
+ break;
+
+ if (!g_str_has_prefix (tmp, "dhcp-option "))
+ continue;
+
+ tmp += 12; /* strlen ("dhcp-option ") */
+
+ if (g_str_has_prefix (tmp, "DNS "))
+ dns_list = parse_addr4_list (dns_list, tmp + 4);
+ else if (g_str_has_prefix (tmp, "WINS "))
+ nbns_list = parse_addr4_list (nbns_list, tmp + 5);
+ else if (g_str_has_prefix (tmp, "DOMAIN ") && is_domain_valid (tmp + 7))
+ g_ptr_array_add (dns_domains, (gpointer) tmp + 7);
+ }
+
+ if (dns_list)
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_DNS, dns_list);
+ if (nbns_list)
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, nbns_list);
+ if (dns_domains->len) {
+ val = g_slice_new0 (GValue);
+ g_value_init (val, DBUS_TYPE_G_PTR_ARRAY_OF_STRING);
+ g_value_take_boxed (val, dns_domains);
+ g_hash_table_insert (ip4config, NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, val);
+ } else
+ g_ptr_array_free (dns_domains, TRUE);
+
+ /* Tunnel MTU */
+ tmp = getenv ("tun_mtu");
+ if (tmp && tmp[0]) {
+ long int mtu;
+
+ errno = 0;
+ mtu = strtol (tmp, NULL, 10);
+ if (errno || mtu < 0 || mtu > 20000) {
+ g_warning ("Ignoring invalid tunnel MTU '%s'", tmp);
+ } else {
+ val = uint_to_gvalue ((guint32) mtu);
+ g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_MTU, val);
+ }
+ }
+
+ if (g_hash_table_size (ip4config)) {
+ g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, bool_to_gvalue (TRUE));
+ if (out_ip4_config)
+ *out_ip4_config = g_hash_table_ref (ip4config);
+ }
+ g_hash_table_unref (ip4config);
+
+ if (g_hash_table_size (ip6config)) {
+ g_hash_table_insert (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, bool_to_gvalue (TRUE));
+ if (out_ip6_config)
+ *out_ip6_config = g_hash_table_ref (ip6config);
+ }
+ g_hash_table_unref (ip6config);
+
+ if (out_config)
+ *out_config = g_hash_table_ref (config);
+ g_hash_table_unref (config);
+ return TRUE;
+}
+
diff --git a/src/helper-config.h b/src/helper-config.h
new file mode 100644
index 0000000..59c0229
--- /dev/null
+++ b/src/helper-config.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* nm-openvpn-service - openvpn integration with NetworkManager
+ *
+ * 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.
+ *
+ */
+
+#ifndef HELPER_CONFIG_H
+#define HELPER_CONFIG_H
+
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#define DBUS_TYPE_G_ARRAY_OF_UINT (dbus_g_type_get_collection ("GArray", G_TYPE_UINT))
+#define DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT (dbus_g_type_get_collection ("GPtrArray",
DBUS_TYPE_G_ARRAY_OF_UINT))
+#define DBUS_TYPE_G_PTR_ARRAY_OF_STRING (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING))
+#define DBUS_TYPE_G_IP6_ROUTE (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY,
G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ROUTE))
+
+gboolean helper_generate_config (const char **args,
+ gboolean is_tapdev,
+ gboolean is_restart,
+ GHashTable **out_config,
+ GHashTable **out_ip4_config,
+ GHashTable **out_ip6_config,
+ GError **error);
+
+#endif /* HELPER_CONFIG_H */
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
new file mode 100644
index 0000000..8904611
--- /dev/null
+++ b/src/tests/Makefile.am
@@ -0,0 +1,22 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GLIB_CFLAGS) \
+ $(NM_CFLAGS) \
+ -DTESTDIR="\"$(abs_srcdir)\""
+
+noinst_PROGRAMS = test-helper-config
+
+test_helper_config_SOURCES = \
+ test-helper-config.c \
+ ../helper-config.c \
+ ../helper-config.h
+
+test_helper_config_LDADD = \
+ $(GLIB_LIBS) \
+ $(NM_LIBS)
+
+TESTS = test-helper-config
+
+EXTRA_DIST = \
+ test-basic-init.conf \
+ test-basic-restart.conf
diff --git a/src/tests/test-helper-config.c b/src/tests/test-helper-config.c
new file mode 100644
index 0000000..73258a8
--- /dev/null
+++ b/src/tests/test-helper-config.c
@@ -0,0 +1,390 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "src/nm-openvpn-service.h"
+#include "src/helper-config.h"
+
+/******************************************************************/
+/* libnm-util still uses GValueArray... remove when porting to libnm */
+
+#ifdef __clang__
+
+#undef G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#undef G_GNUC_END_IGNORE_DEPRECATIONS
+
+#define G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+
+#define G_GNUC_END_IGNORE_DEPRECATIONS \
+ _Pragma("clang diagnostic pop")
+
+#endif
+
+#define g_value_array_get_type() \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_get_type (); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_get_nth(value_array, index_) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_get_nth (value_array, index_); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+/******************************************************************/
+
+/* returns argv */
+static char **
+load_config (const char *file)
+{
+ char *contents = NULL;
+ gboolean success;
+ GError *error = NULL;
+ char **lines, **iter;
+ char **argv = NULL;
+
+ g_assert (file);
+ success = g_file_get_contents (file, &contents, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert (contents && contents[0]);
+
+ lines = g_strsplit_set (contents, "\r\n", -1);
+ g_assert (g_strv_length (lines) > 5);
+
+ clearenv ();
+ for (iter = lines; iter && *iter; iter++) {
+ if (!argv) {
+ argv = g_strsplit_set (*iter, " \t", -1);
+ g_assert (argv && g_strv_length (argv));
+ } else {
+ g_assert (!*iter[0] || strchr (*iter, '='));
+ putenv (*iter);
+ }
+ }
+
+ return argv;
+}
+
+/********************/
+/* Remove these when porting to libnm */
+
+#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
+
+static char _nm_utils_inet_ntop_buffer[NM_UTILS_INET_ADDRSTRLEN];
+
+/**
+ * nm_utils_inet4_ntop: (skip)
+ * @inaddr: the address that should be converted to string.
+ * @dst: the destination buffer, it must contain at least %INET_ADDRSTRLEN
+ * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return
+ * a pointer to an internal, static buffer (shared with nm_utils_inet6_ntop()).
+ * Beware, that the internal buffer will be overwritten with ever new call
+ * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's
+ * own @dst buffer. Also, using the internal buffer is not thread safe. When
+ * in doubt, pass your own @dst buffer to avoid these issues.
+ *
+ * Wrapper for inet_ntop.
+ *
+ * Returns: the input buffer @dst, or a pointer to an
+ * internal, static buffer. This function cannot fail.
+ **/
+static const char *
+nm_utils_inet4_ntop (in_addr_t inaddr, char *dst)
+{
+ return inet_ntop (AF_INET, &inaddr, dst ? dst : _nm_utils_inet_ntop_buffer,
+ INET_ADDRSTRLEN);
+}
+
+/**
+ * nm_utils_inet6_ntop: (skip)
+ * @in6addr: the address that should be converted to string.
+ * @dst: the destination buffer, it must contain at least %INET6_ADDRSTRLEN
+ * or %NM_UTILS_INET_ADDRSTRLEN characters. If set to %NULL, it will return
+ * a pointer to an internal, static buffer (shared with nm_utils_inet4_ntop()).
+ * Beware, that the internal buffer will be overwritten with ever new call
+ * of nm_utils_inet4_ntop() or nm_utils_inet6_ntop() that does not provied it's
+ * own @dst buffer. Also, using the internal buffer is not thread safe. When
+ * in doubt, pass your own @dst buffer to avoid these issues.
+ *
+ * Wrapper for inet_ntop.
+ *
+ * Returns: the input buffer @dst, or a pointer to an
+ * internal, static buffer. %NULL is not allowed as @in6addr,
+ * otherwise, this function cannot fail.
+ **/
+static const char *
+nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst)
+{
+ g_return_val_if_fail (in6addr, NULL);
+ return inet_ntop (AF_INET6, in6addr, dst ? dst : _nm_utils_inet_ntop_buffer,
+ INET6_ADDRSTRLEN);
+}
+
+/**********************/
+
+typedef enum {
+ OPT_TYPE_STRING,
+ OPT_TYPE_UINT,
+ OPT_TYPE_BOOLEAN,
+ OPT_TYPE_IP4_ADDR,
+ OPT_TYPE_IP4_ARRAY,
+ OPT_TYPE_ROUTES4_ARRAY,
+ OPT_TYPE_IP6_ADDR,
+ OPT_TYPE_ROUTES6_ARRAY,
+} OptType;
+
+static GType
+opt_type_to_gtype (OptType t)
+{
+ switch (t) {
+ case OPT_TYPE_STRING:
+ return G_TYPE_STRING;
+ case OPT_TYPE_UINT:
+ return G_TYPE_UINT;
+ case OPT_TYPE_BOOLEAN:
+ return G_TYPE_BOOLEAN;
+ case OPT_TYPE_IP4_ADDR:
+ return G_TYPE_UINT;
+ case OPT_TYPE_IP6_ADDR:
+ return DBUS_TYPE_G_UCHAR_ARRAY;
+ case OPT_TYPE_IP4_ARRAY:
+ return DBUS_TYPE_G_ARRAY_OF_UINT;
+ case OPT_TYPE_ROUTES4_ARRAY:
+ return DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT;
+ case OPT_TYPE_ROUTES6_ARRAY:
+ return DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE;
+ }
+ g_assert_not_reached ();
+}
+
+typedef struct {
+ const char *name;
+ OptType type;
+ union {
+ const char *s; /* string, ipv4, ipv6 address */
+ guint u; /* uint and boolean */
+ const char *sarray[6];
+ } u;
+} Option;
+
+static void
+config_equal (const Option *options, GHashTable *config)
+{
+ guint i = 0;
+ const Option *expected;
+
+ for (expected = options; expected->name; expected++, i++) {
+ GValue *val = g_hash_table_lookup (config, expected->name);
+
+ g_assert (val);
+ g_assert (G_VALUE_HOLDS (val, opt_type_to_gtype (expected->type)));
+
+ switch (expected->type) {
+ case OPT_TYPE_STRING:
+ g_assert_cmpstr (expected->u.s, ==, g_value_get_string (val));
+ break;
+ case OPT_TYPE_UINT:
+ g_assert_cmpint (expected->u.u, ==, g_value_get_uint (val));
+ break;
+ case OPT_TYPE_BOOLEAN:
+ g_assert_cmpint (!!expected->u.u, ==, g_value_get_boolean (val));
+ break;
+ case OPT_TYPE_IP4_ADDR:
+ g_assert_cmpstr (expected->u.s, ==, nm_utils_inet4_ntop (g_value_get_uint (val),
NULL));
+ break;
+ case OPT_TYPE_IP6_ADDR: {
+ GByteArray *ba = g_value_get_boxed (val);
+
+ g_assert_cmpint (ba->len, ==, sizeof (struct in6_addr));
+ g_assert_cmpstr (expected->u.s, ==, nm_utils_inet6_ntop ((struct in6_addr *)
ba->data, NULL));
+ break;
+ }
+ case OPT_TYPE_IP4_ARRAY: {
+ GArray *a = g_value_get_boxed (val);
+ guint n;
+
+ g_assert_cmpint (g_strv_length ((char **) expected->u.sarray), ==, a->len);
+ for (n = 0; n < a->len; n++)
+ g_assert_cmpstr (expected->u.sarray[n], ==, nm_utils_inet4_ntop
(g_array_index (a, guint, n), NULL));
+ break;
+ }
+ case OPT_TYPE_ROUTES4_ARRAY: {
+ GPtrArray *a = g_value_get_boxed (val);
+ GString *s = g_string_sized_new (30);
+ guint n;
+
+ g_assert_cmpint (g_strv_length ((char **) expected->u.sarray), ==, a->len);
+ for (n = 0; n < a->len; n++) {
+ GArray *r = g_ptr_array_index (a, n);
+
+ g_string_set_size (s, 0);
+ g_string_append_printf (s, "%s/%u",
+ nm_utils_inet4_ntop (g_array_index (r, guint, 0),
NULL),
+ g_array_index (r, guint, 1));
+ /* Split due to static buffer in nm_utils_inet4_ntop() */
+ g_string_append_printf (s, ",%s,%u",
+ nm_utils_inet4_ntop (g_array_index (r, guint, 2),
NULL),
+ g_array_index (r, guint, 3));
+
+ g_assert_cmpstr (expected->u.sarray[n], ==, s->str);
+ }
+ g_string_free (s, TRUE);
+ break;
+ }
+ case OPT_TYPE_ROUTES6_ARRAY: {
+ GPtrArray *a = g_value_get_boxed (val);
+ GString *s = g_string_sized_new (30);
+ guint n;
+
+ g_assert_cmpint (g_strv_length ((char **) expected->u.sarray), ==, a->len);
+ for (n = 0; n < a->len; n++) {
+ GValueArray *r = g_ptr_array_index (a, n);
+ GByteArray *b;
+
+ g_string_set_size (s, 0);
+
+ b = g_value_get_boxed (g_value_array_get_nth (r, 0));
+ g_assert_cmpint (b->len, ==, 16);
+ g_string_append_printf (s, "%s/%u",
+ nm_utils_inet6_ntop ((struct in6_addr *) b->data,
NULL),
+ g_value_get_uint (g_value_array_get_nth (r, 1)));
+
+ /* Split due to static buffer in nm_utils_inet6_ntop() */
+ b = g_value_get_boxed (g_value_array_get_nth (r, 2));
+ g_assert_cmpint (b->len, ==, 16);
+ g_string_append_printf (s, ",%s,%u",
+ nm_utils_inet6_ntop ((struct in6_addr *) b->data,
NULL),
+ g_value_get_uint (g_value_array_get_nth (r, 3)));
+
+ g_assert_cmpstr (expected->u.sarray[n], ==, s->str);
+ }
+ g_string_free (s, TRUE);
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ g_assert_cmpint (i, ==, g_hash_table_size (config));
+}
+
+static void
+test_init (void)
+{
+ gboolean success;
+ char **argv;
+ GError *error = NULL;
+ GHashTable *config = NULL;
+ GHashTable *ip4_config = NULL;
+ GHashTable *ip6_config = NULL;
+ const Option options[] = {
+ { NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, OPT_TYPE_IP4_ADDR, { .s = "87.238.35.145" } },
+ { NM_VPN_PLUGIN_CONFIG_TUNDEV, OPT_TYPE_STRING, { .s = "tun0"} },
+ { NM_VPN_PLUGIN_CONFIG_MTU, OPT_TYPE_UINT, { .u = 1500 } },
+ { NM_VPN_PLUGIN_CONFIG_HAS_IP4, OPT_TYPE_BOOLEAN, { .u = TRUE } },
+ { NM_VPN_PLUGIN_CONFIG_HAS_IP6, OPT_TYPE_BOOLEAN, { .u = TRUE } },
+ { NULL }
+ };
+ const Option ip4_options[] = {
+ { NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, OPT_TYPE_IP4_ADDR, { .s = "100.64.48.5" } },
+ { NM_VPN_PLUGIN_IP4_CONFIG_PTP, OPT_TYPE_IP4_ADDR, { .s = "100.64.48.5" } },
+ { NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, OPT_TYPE_IP4_ADDR, { .s = "100.64.48.6" } },
+ { NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, OPT_TYPE_UINT, { .u = 32 } },
+ { NM_VPN_PLUGIN_IP4_CONFIG_DNS, OPT_TYPE_IP4_ARRAY, { .sarray = { "8.8.8.8", NULL
} } },
+ { NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, OPT_TYPE_ROUTES4_ARRAY, { .sarray = {
"10.0.0.0/24,100.64.48.5,0", "100.64.48.1/32,100.64.48.5,0", NULL } } },
+ { NULL }
+ };
+ const Option ip6_options[] = {
+ { NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, OPT_TYPE_IP6_ADDR, { .s = "2001:db8::1000" } },
+ { NM_VPN_PLUGIN_IP6_CONFIG_PTP, OPT_TYPE_IP6_ADDR, { .s = "2001:db8::1" } },
+ { NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, OPT_TYPE_UINT, { .u = 64 } },
+ { NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, OPT_TYPE_ROUTES6_ARRAY, { .sarray = {
"fd00::/64,2001:db8::1,0", NULL } } },
+ { NULL }
+ };
+
+ argv = load_config (TESTDIR "/test-basic-init.conf");
+ g_assert (argv);
+ success = helper_generate_config ((const char **) argv, FALSE, FALSE, &config, &ip4_config,
&ip6_config, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert (config);
+ config_equal (options, config);
+
+ g_assert (ip4_config);
+ config_equal (ip4_options, ip4_config);
+
+ g_assert (ip6_config);
+ config_equal (ip6_options, ip6_config);
+
+ g_hash_table_unref (config);
+ g_hash_table_unref (ip4_config);
+ g_hash_table_unref (ip6_config);
+ g_strfreev (argv);
+}
+
+#if 0
+static void
+test_restart (void)
+{
+ gboolean success;
+ char **argv;
+ GError *error = NULL;
+ GHashTable *config = NULL;
+ GHashTable *ip4_config = NULL;
+ GHashTable *ip6_config = NULL;
+
+ argv = load_config (TESTDIR "/test-basic-restart.conf");
+ g_assert (argv);
+ success = helper_generate_config ((const char **) argv, FALSE, TRUE, &config, &ip4_config,
&ip6_config, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_hash_table_unref (config);
+ g_hash_table_unref (ip4_config);
+ g_hash_table_unref (ip6_config);
+ g_strfreev (argv);
+}
+#endif
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ g_test_add_func ("/helper-config/init", test_init);
+#if 0
+ g_test_add_func ("/helper-config/restart", test_restart);
+#endif
+
+ return g_test_run ();
+}
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]