[NetworkManager-openvpn/th/rework-detect-version] service: rework detection of openvpn version




commit aab0d902d7d485fc7a54c079b52bb9a0a843f84f
Author: Thomas Haller <thaller redhat com>
Date:   Fri Sep 30 16:31:08 2022 +0200

    service: rework detection of openvpn version
    
    The few enums we detected before are not flexible enough. Also, they
    require code changes to the parsing, whenever we want to detect a new
    version.
    
    Rework the parsing and combine the number to one version that can be
    compared with greater/less.
    
    https://gitlab.gnome.org/GNOME/NetworkManager-openvpn/-/merge_requests/61

 properties/tests/test-import-export.c | 114 ++++++++++++++++++++++++++++++++++
 shared/utils.c                        |  44 +++++++++++++
 shared/utils.h                        |  26 ++++++++
 src/nm-openvpn-service.c              |  89 +++++++++-----------------
 4 files changed, 213 insertions(+), 60 deletions(-)
---
diff --git a/properties/tests/test-import-export.c b/properties/tests/test-import-export.c
index fdc9e50..7f585d0 100644
--- a/properties/tests/test-import-export.c
+++ b/properties/tests/test-import-export.c
@@ -1264,6 +1264,118 @@ test_args_parse_line (void)
 
 /*****************************************************************************/
 
+static void test_version(void) {
+       const struct {
+               guint version;
+               char *const data;
+               } test_data[] = {
+       {
+               .version = 20507,
+               .data =
+                       "OpenVPN 2.5.7 x86_64-redhat-linux-gnu [SSL (OpenSSL)] "
+                       "[LZO] "
+                       "[LZ4] "
+                       "[EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on May 31 "
+                       "2022\n"
+                       "library versions: OpenSSL 3.0.5 5 Jul 2022, LZO 2.10\n"
+                       "Originally developed by James Yonan\n"
+                       "Copyright (C) 2002-2022 OpenVPN Inc <sales openvpn net>\n"
+                       "Compile time defines: enable_async_push=yes "
+                       "enable_comp_stub=no "
+                       "enable_crypto_ofb_cfb=yes enable_debug=yes "
+                       "enable_def_auth=yes "
+                       "enable_dependency_tracking=no enable_dlopen=unknown "
+                       "enable_dlopen_self=unknown "
+                       "enable_dlopen_self_static=unknown "
+                       "enable_fast_install=needless enable_fragment=yes "
+                       "enable_iproute2=no "
+                       "enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes "
+                       "enable_management=yes enable_multihome=yes "
+                       "enable_pam_dlopen=no "
+                       "enable_pedantic=no enable_pf=yes enable_pkcs11=yes "
+                       "enable_plugin_auth_pam=yes enable_plugin_down_root=yes "
+                       "enable_plugins=yes enable_port_share=yes "
+                       "enable_selinux=yes "
+                       "enable_shared=yes enable_shared_with_static_runtimes=no "
+                       "enable_silent_rules=yes enable_small=no enable_static=yes "
+                       "enable_strict=no enable_strict_options=no "
+                       "enable_systemd=yes "
+                       "enable_werror=no enable_win32_dll=yes "
+                       "enable_x509_alt_username=yes "
+                       "with_aix_soname=aix with_crypto_library=openssl "
+                       "with_gnu_ld=yes "
+                       "with_mem_check=no with_openssl_engine=auto "
+                       "with_sysroot=no\n"
+                       "",
+               },
+               {
+               .version = 20310,
+               .data =
+                       "OpenVPN 2.3.10 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] "
+                       "[EPOLL] [PKCS11] [MH] [IPv6] built on Jan  9 2019\n"
+                       "library versions: OpenSSL 1.0.2g  1 Mar 2016, LZO 2.08\n"
+                       "Originally developed by James Yonan\n"
+                       "Copyright (C) 2002-2010 OpenVPN Technologies, Inc. "
+                       "<sales openvpn net>\n"
+                       "Compile time defines: enable_crypto=yes "
+                       "enable_crypto_ofb_cfb=yes enable_debug=yes "
+                       "enable_def_auth=yes enable_dependency_tracking=no "
+                       "enable_dlopen=unknown enable_dlopen_self=unknown "
+                       "enable_dlopen_self_static=unknown enable_fast_install=yes "
+                       "enable_fragment=yes enable_http_proxy=yes "
+                       "enable_iproute2=yes enable_libtool_lock=yes "
+                       "enable_lzo=yes enable_lzo_stub=no "
+                       "enable_maintainer_mode=no enable_management=yes "
+                       "enable_multi=yes enable_multihome=yes "
+                       "enable_pam_dlopen=no enable_password_save=yes "
+                       "enable_pedantic=no enable_pf=yes enable_pkcs11=yes "
+                       "enable_plugin_auth_pam=yes enable_plugin_down_root=yes "
+                       "enable_plugins=yes enable_port_share=yes "
+                       "enable_selinux=no enable_server=yes enable_shared=yes "
+                       "enable_shared_with_static_runtimes=no "
+                       "enable_silent_rules=no enable_small=no enable_socks=yes "
+                       "enable_ssl=yes enable_static=yes enable_strict=no "
+                       "enable_strict_options=no enable_systemd=yes "
+                       "enable_win32_dll=yes enable_x509_alt_username=yes "
+                       "with_crypto_library=openssl with_gnu_ld=yes "
+                       "with_mem_check=no with_plugindir='${prefix}/lib/openvpn' "
+                       "with_sysroot=no\n"
+                       "",
+               },
+       };
+       int i;
+
+       for (i = 0; i < (int)G_N_ELEMENTS(test_data); i++) {
+               g_assert_cmpint(test_data[i].version, ==,
+                               nmovpn_version_parse(test_data[i].data));
+       }
+
+#define _test_version(v_x, v_y, v_z, encoded) \
+       G_STMT_START { \
+               const guint _encoded = (encoded); \
+               const guint _v_x = (v_x); \
+               const guint _v_y = (v_y); \
+               const guint _v_z = (v_z); \
+               guint _v2_x; \
+               guint _v2_y; \
+               guint _v2_z; \
+               \
+               g_assert_cmpint(nmovpn_version_encode(_v_x, _v_y, _v_z), ==, _encoded); \
+               \
+               nmovpn_version_decode(_encoded, &_v2_x, &_v2_y, &_v2_z); \
+               g_assert_cmpint(_v_x, ==, _v2_x); \
+               g_assert_cmpint(_v_y, ==, _v2_y); \
+               g_assert_cmpint(_v_z, ==, _v2_z); \
+       } G_STMT_END
+
+       _test_version(1, 5, 88, 10588);
+       _test_version(2, 5, 0, 20500);
+       _test_version(2, 5, 4, 20504);
+       _test_version(3, 0, 0, 30000);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -1396,6 +1508,8 @@ int main (int argc, char **argv)
 
        _add_test_func_simple (test_args_parse_line);
 
+       _add_test_func_simple (test_version);
+
        result = g_test_run ();
        if (result != EXIT_SUCCESS)
                return result;
diff --git a/shared/utils.c b/shared/utils.c
index 133da87..d688adc 100644
--- a/shared/utils.c
+++ b/shared/utils.c
@@ -335,3 +335,47 @@ out_fail:
 }
 
 /*****************************************************************************/
+
+guint
+nmovpn_version_parse (const char *version_str)
+{
+       const char *s;
+       guint v_x;
+       guint v_y;
+       guint v_z;
+
+       /* the output for --version starts with title_string, which starts with PACKAGE_STRING,
+        * which looks like "OpenVPN 2.#...". Do a strict parsing here... */
+       if (   !version_str
+           || !g_str_has_prefix (version_str, "OpenVPN 2."))
+               return NMOVPN_VERSION_UNKNOWN;
+       s = &version_str[NM_STRLEN ("OpenVPN 2.")];
+
+       if (!g_ascii_isdigit (s[0]))
+               return NMOVPN_VERSION_UNKNOWN;
+
+       v_x = 2;
+
+       v_y = 0;
+       do {
+               if (v_y > G_MAXINT / 100)
+                       return NMOVPN_VERSION_UNKNOWN;
+               v_y = (v_y * 10) + (s[0] - '0');
+       } while (g_ascii_isdigit ((++s)[0]));
+       if (v_y > 99)
+               return NMOVPN_VERSION_UNKNOWN;
+
+       v_z = 0;
+       if (s[0] == '.') {
+               s++;
+               do {
+                       if (v_z > G_MAXINT / 100)
+                               break;
+                       v_z = (v_z * 10) + (s[0] - '0');
+               } while (g_ascii_isdigit ((++s)[0]));
+       }
+       if (v_z > 99)
+               v_z = 0;
+
+       return nmovpn_version_encode (v_x, v_y, v_z);
+}
diff --git a/shared/utils.h b/shared/utils.h
index 064da72..216b708 100644
--- a/shared/utils.h
+++ b/shared/utils.h
@@ -144,4 +144,30 @@ void nmovpn_compression_to_options (NMOvpnComp comp,
                                     const char **comp_lzo,
                                     const char **compress);
 
+/*****************************************************************************/
+
+#define NMOVPN_VERSION_MAX     999999u
+#define NMOVPN_VERSION_UNKNOWN (NMOVPN_VERSION_MAX+1u)
+#define NMOVPN_VERSION_INVALID (NMOVPN_VERSION_MAX+2u)
+
+static inline guint
+nmovpn_version_encode (guint x, guint y, guint z)
+{
+       nm_assert(x <= 99);
+       nm_assert(y <= 99);
+       nm_assert(z <= 99);
+
+       return ((x * 100u + y) * 100u) + z;
+}
+
+static inline void
+nmovpn_version_decode (guint version, guint *out_x, guint *out_y, guint *out_z)
+{
+       *out_x = (version / 10000u);
+       *out_y = (version / 100u) % 100u;
+       *out_z = (version % 100u);
+}
+
+guint nmovpn_version_parse (const char *version_str);
+
 #endif  /* UTILS_H */
diff --git a/src/nm-openvpn-service.c b/src/nm-openvpn-service.c
index c5c6eee..aeb0cdb 100644
--- a/src/nm-openvpn-service.c
+++ b/src/nm-openvpn-service.c
@@ -85,14 +85,6 @@ NMOpenvpnPlugin *nm_openvpn_plugin_new (const char *bus_name);
 
 /*****************************************************************************/
 
-typedef enum {
-       OPENVPN_BINARY_VERSION_INVALID,
-       OPENVPN_BINARY_VERSION_UNKNOWN,
-       OPENVPN_BINARY_VERSION_2_3_OR_OLDER,
-       OPENVPN_BINARY_VERSION_2_4_OR_OLDER,
-       OPENVPN_BINARY_VERSION_2_4_OR_NEWER,
-} OpenvpnBinaryVersion;
-
 typedef struct {
        GPid pid;
        guint watch_id;
@@ -530,15 +522,13 @@ openvpn_binary_find_exepath (void)
        return NULL;
 }
 
-static OpenvpnBinaryVersion
+static guint
 openvpn_binary_detect_version (const char *exepath)
 {
        gs_free char *s_stdout = NULL;
-       const char *s;
        int exit_code;
-       int n;
 
-       g_return_val_if_fail (exepath && exepath[0] == '/', OPENVPN_BINARY_VERSION_UNKNOWN);
+       g_return_val_if_fail (exepath && exepath[0] == '/', NMOVPN_VERSION_UNKNOWN);
 
        if (!g_spawn_sync (NULL,
                           (char *[]) { (char *) exepath, "--version", NULL },
@@ -550,61 +540,40 @@ openvpn_binary_detect_version (const char *exepath)
                           NULL,
                           &exit_code,
                           NULL))
-               return OPENVPN_BINARY_VERSION_UNKNOWN;
+               return NMOVPN_VERSION_UNKNOWN;
 
        if (   !WIFEXITED (exit_code)
            || !NM_IN_SET(WEXITSTATUS (exit_code), 0, 1)) {
                /* expect return code 1 (OPENVPN_EXIT_STATUS_USAGE).
                 * Since 2.5.0, it returns 0. */
-               return OPENVPN_BINARY_VERSION_UNKNOWN;
+               return NMOVPN_VERSION_UNKNOWN;
        }
 
-       /* the output for --version starts with title_string, which starts with PACKAGE_STRING,
-        * which looks like "OpenVPN 2.#...". Do a strict parsing here... */
-       if (   !s_stdout
-           || !g_str_has_prefix (s_stdout, "OpenVPN 2."))
-               return OPENVPN_BINARY_VERSION_UNKNOWN;
-       s = &s_stdout[NM_STRLEN ("OpenVPN 2.")];
-
-       if (!g_ascii_isdigit (s[0]))
-               return OPENVPN_BINARY_VERSION_UNKNOWN;
-
-       n = 0;
-       do {
-               if (n > G_MAXINT / 100)
-                       return OPENVPN_BINARY_VERSION_UNKNOWN;
-               n = (n * 10) + (s[0] - '0');
-       } while (g_ascii_isdigit ((++s)[0]));
-
-       if (n <= 3)
-               return OPENVPN_BINARY_VERSION_2_3_OR_OLDER;
-       if (n <= 4)
-               return OPENVPN_BINARY_VERSION_2_4_OR_OLDER;
-
-       return OPENVPN_BINARY_VERSION_2_4_OR_NEWER;
+       return nmovpn_version_parse (s_stdout);
 }
 
-static OpenvpnBinaryVersion
-openvpn_binary_detect_version_cached (const char *exepath, OpenvpnBinaryVersion *cached)
+static guint
+openvpn_binary_detect_version_cached (const char *exepath, guint *cached)
 {
-       if (G_UNLIKELY (*cached == OPENVPN_BINARY_VERSION_INVALID)) {
-               const char *str;
+       guint v;
+
+       v = *cached;
+       if (G_UNLIKELY (v == NMOVPN_VERSION_INVALID)) {
+               v = openvpn_binary_detect_version (exepath);
+               if (v >= NMOVPN_VERSION_UNKNOWN) {
+                       v = NMOVPN_VERSION_UNKNOWN;
+                       _LOGI ("detected openvpn version UNKNOWN, assume max");
+               } else {
+                       guint v_x;
+                       guint v_y;
+                       guint v_z;
 
-               *cached = openvpn_binary_detect_version (exepath);
-               switch (*cached) {
-               case OPENVPN_BINARY_VERSION_2_3_OR_OLDER:
-                       str = "2.3 or older";
-                       break;
-               case OPENVPN_BINARY_VERSION_2_4_OR_NEWER:
-                       str = "2.4 or newer";
-                       break;
-               default:
-                       str = "unknown";
-                       break;
+                       nmovpn_version_decode (v, &v_x, &v_y, &v_z);
+                       _LOGI ("detected openvpn version %u.%u.%u", v_x, v_y, v_z);
                }
-               _LOGI ("detected openvpn version %s", str);
+               *cached = v;
        }
-       return *cached;
+       return v;
 }
 
 /*****************************************************************************/
@@ -1352,7 +1321,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
        NMSettingVpn *s_vpn;
        const char *connection_type;
        gint64 v_int64;
-       OpenvpnBinaryVersion openvpn_binary_version = OPENVPN_BINARY_VERSION_INVALID;
+       guint openvpn_binary_version = NMOVPN_VERSION_INVALID;
        guint num_remotes = 0;
        gs_free char *cmd_log = NULL;
        NMOvpnComp comp;
@@ -1547,7 +1516,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
        openvpn_binary_detect_version_cached (openvpn_binary, &openvpn_binary_version);
 
        if (nmovpn_arg_is_set (allow_compression)) {
-               if (openvpn_binary_version == OPENVPN_BINARY_VERSION_2_4_OR_OLDER) {
+               if (openvpn_binary_version < nmovpn_version_encode (2, 5, 0)) {
                        _LOGW ("\"allow-compression\" is only supported in OpenVPN 2.5 and later versions");
                } else {
                        args_add_strv (args, "--allow-compression", allow_compression);
@@ -1559,7 +1528,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                case NMOVPN_COMP_DISABLED:
                        break;
                case NMOVPN_COMP_LZO:
-                       if (openvpn_binary_version == OPENVPN_BINARY_VERSION_2_3_OR_OLDER)
+                       if (openvpn_binary_version < nmovpn_version_encode (2, 4, 0))
                                args_add_strv (args, "--comp-lzo", "yes");
                        else
                                args_add_strv (args, "--compress", "lzo");
@@ -1567,7 +1536,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                case NMOVPN_COMP_LZ4:
                case NMOVPN_COMP_LZ4_V2:
                case NMOVPN_COMP_AUTO:
-                       if (openvpn_binary_version == OPENVPN_BINARY_VERSION_2_3_OR_OLDER)
+                       if (openvpn_binary_version < nmovpn_version_encode (2, 4, 0))
                                _LOGW ("\"compress\" option supported only by OpenVPN >= 2.4");
 
                        if (comp == NMOVPN_COMP_LZ4)
@@ -1579,7 +1548,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
                        break;
                case NMOVPN_COMP_LEGACY_LZO_DISABLED:
                case NMOVPN_COMP_LEGACY_LZO_ADAPTIVE:
-                       if (openvpn_binary_version != OPENVPN_BINARY_VERSION_2_3_OR_OLDER)
+                       if (openvpn_binary_version >= nmovpn_version_encode (2, 4, 0))
                                _LOGW ("\"comp-lzo\" is deprecated and will be removed in future OpenVPN 
releases");
 
                        args_add_strv (args, "--comp-lzo",
@@ -1767,7 +1736,7 @@ nm_openvpn_start_openvpn_binary (NMOpenvpnPlugin *plugin,
 
        tmp = nm_setting_vpn_get_data_item (s_vpn, NM_OPENVPN_KEY_TLS_REMOTE);
        if (nmovpn_arg_is_set (tmp)) {
-               if (openvpn_binary_detect_version_cached (openvpn_binary, &openvpn_binary_version) == 
OPENVPN_BINARY_VERSION_2_3_OR_OLDER) {
+               if (openvpn_binary_detect_version_cached (openvpn_binary, &openvpn_binary_version) < 
nmovpn_version_encode (2, 4, 0)) {
                        _LOGW ("the tls-remote option is deprecated and removed from OpenVPN 2.4. Update your 
connection to use verify-x509-name (for example, \"verify-x509-name=name:%s\")", tmp);
                        args_add_strv (args, "--tls-remote", tmp);
                } else {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]