diff options
Diffstat (limited to 'src/openvpnmsica')
-rw-r--r-- | src/openvpnmsica/Makefile.am | 56 | ||||
-rw-r--r-- | src/openvpnmsica/Makefile.in | 865 | ||||
-rw-r--r-- | src/openvpnmsica/dllmain.c | 198 | ||||
-rw-r--r-- | src/openvpnmsica/msica_arg.c | 139 | ||||
-rw-r--r-- | src/openvpnmsica/msica_arg.h | 112 | ||||
-rw-r--r-- | src/openvpnmsica/msiex.c | 265 | ||||
-rw-r--r-- | src/openvpnmsica/msiex.h | 112 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica-Debug.props | 14 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica-Release.props | 14 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica.c | 1297 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica.h | 166 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica.props | 18 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica.vcxproj | 160 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica.vcxproj.filters | 62 | ||||
-rw-r--r-- | src/openvpnmsica/openvpnmsica_resources.rc | 62 |
15 files changed, 3540 insertions, 0 deletions
diff --git a/src/openvpnmsica/Makefile.am b/src/openvpnmsica/Makefile.am new file mode 100644 index 0000000..0fdc1f6 --- /dev/null +++ b/src/openvpnmsica/Makefile.am @@ -0,0 +1,56 @@ +# +# openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages +# +# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net> +# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation. +# +# 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 $(top_srcdir)/build/ltrc.inc + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in + +EXTRA_DIST = \ + openvpnmsica.vcxproj \ + openvpnmsica.vcxproj.filters \ + openvpnmsica.props \ + openvpnmsica-Debug.props \ + openvpnmsica-Release.props + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat + +AM_CFLAGS = \ + $(TAP_CFLAGS) + +if WIN32 +lib_LTLIBRARIES = libopenvpnmsica.la +libopenvpnmsica_la_CFLAGS = \ + -municode -D_UNICODE \ + -UNTDDI_VERSION -U_WIN32_WINNT \ + -D_WIN32_WINNT=_WIN32_WINNT_VISTA \ + -Wl,--kill-at +libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshell32 -lshlwapi -lversion -no-undefined -avoid-version +endif + +libopenvpnmsica_la_SOURCES = \ + dllmain.c \ + msiex.c msiex.h \ + msica_arg.c msica_arg.h \ + openvpnmsica.c openvpnmsica.h \ + $(top_srcdir)/src/tapctl/basic.h \ + $(top_srcdir)/src/tapctl/error.c $(top_srcdir)/src/tapctl/error.h \ + $(top_srcdir)/src/tapctl/tap.c $(top_srcdir)/src/tapctl/tap.h \ + openvpnmsica_resources.rc diff --git a/src/openvpnmsica/Makefile.in b/src/openvpnmsica/Makefile.in new file mode 100644 index 0000000..b21ea04 --- /dev/null +++ b/src/openvpnmsica/Makefile.in @@ -0,0 +1,865 @@ +# Makefile.in generated by automake 1.16.2 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# +# openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages +# +# Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net> +# Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 +# as published by the Free Software Foundation. +# +# 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. +# + +# +# OpenVPN -- An application to securely tunnel IP networks +# over a single UDP port, with support for SSL/TLS-based +# session authentication and key exchange, +# packet encryption, packet authentication, and +# packet compression. +# +# Copyright (C) 2008-2012 Alon Bar-Lev <alon.barlev@gmail.com> +# +# Required to build Windows resource file + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/openvpnmsica +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_emptyarray.m4 \ + $(top_srcdir)/m4/ax_socklen_t.m4 \ + $(top_srcdir)/m4/ax_varargs.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/version.m4 \ + $(top_srcdir)/compat.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h \ + $(top_builddir)/include/openvpn-plugin.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libopenvpnmsica_la_LIBADD = +am_libopenvpnmsica_la_OBJECTS = libopenvpnmsica_la-dllmain.lo \ + libopenvpnmsica_la-msiex.lo libopenvpnmsica_la-msica_arg.lo \ + libopenvpnmsica_la-openvpnmsica.lo libopenvpnmsica_la-error.lo \ + libopenvpnmsica_la-tap.lo openvpnmsica_resources.lo +libopenvpnmsica_la_OBJECTS = $(am_libopenvpnmsica_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libopenvpnmsica_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) \ + $(libopenvpnmsica_la_LDFLAGS) $(LDFLAGS) -o $@ +@WIN32_TRUE@am_libopenvpnmsica_la_rpath = -rpath $(libdir) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) -I$(top_builddir)/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo \ + ./$(DEPDIR)/libopenvpnmsica_la-error.Plo \ + ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo \ + ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo \ + ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo \ + ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libopenvpnmsica_la_SOURCES) +DIST_SOURCES = $(libopenvpnmsica_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/ltrc.inc \ + $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CMOCKA_CFLAGS = @CMOCKA_CFLAGS@ +CMOCKA_LIBS = @CMOCKA_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_UNITTESTS = @ENABLE_UNITTESTS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GIT = @GIT@ +GREP = @GREP@ +IFCONFIG = @IFCONFIG@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IPROUTE = @IPROUTE@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBPAM_CFLAGS = @LIBPAM_CFLAGS@ +LIBPAM_LIBS = @LIBPAM_LIBS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LZ4_CFLAGS = @LZ4_CFLAGS@ +LZ4_LIBS = @LZ4_LIBS@ +LZO_CFLAGS = @LZO_CFLAGS@ +LZO_LIBS = @LZO_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MBEDTLS_CFLAGS = @MBEDTLS_CFLAGS@ +MBEDTLS_LIBS = @MBEDTLS_LIBS@ +MKDIR_P = @MKDIR_P@ +NETSTAT = @NETSTAT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ +OPENSSL_LIBS = @OPENSSL_LIBS@ +OPENVPN_VERSION_MAJOR = @OPENVPN_VERSION_MAJOR@ +OPENVPN_VERSION_MINOR = @OPENVPN_VERSION_MINOR@ +OPENVPN_VERSION_PATCH = @OPENVPN_VERSION_PATCH@ +OPTIONAL_CRYPTO_CFLAGS = @OPTIONAL_CRYPTO_CFLAGS@ +OPTIONAL_CRYPTO_LIBS = @OPTIONAL_CRYPTO_LIBS@ +OPTIONAL_DL_LIBS = @OPTIONAL_DL_LIBS@ +OPTIONAL_INOTIFY_CFLAGS = @OPTIONAL_INOTIFY_CFLAGS@ +OPTIONAL_INOTIFY_LIBS = @OPTIONAL_INOTIFY_LIBS@ +OPTIONAL_LZ4_CFLAGS = @OPTIONAL_LZ4_CFLAGS@ +OPTIONAL_LZ4_LIBS = @OPTIONAL_LZ4_LIBS@ +OPTIONAL_LZO_CFLAGS = @OPTIONAL_LZO_CFLAGS@ +OPTIONAL_LZO_LIBS = @OPTIONAL_LZO_LIBS@ +OPTIONAL_PKCS11_HELPER_CFLAGS = @OPTIONAL_PKCS11_HELPER_CFLAGS@ +OPTIONAL_PKCS11_HELPER_LIBS = @OPTIONAL_PKCS11_HELPER_LIBS@ +OPTIONAL_SELINUX_LIBS = @OPTIONAL_SELINUX_LIBS@ +OPTIONAL_SYSTEMD_LIBS = @OPTIONAL_SYSTEMD_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +P11KIT_CFLAGS = @P11KIT_CFLAGS@ +P11KIT_LIBS = @P11KIT_LIBS@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKCS11_HELPER_CFLAGS = @PKCS11_HELPER_CFLAGS@ +PKCS11_HELPER_LIBS = @PKCS11_HELPER_LIBS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PLUGINDIR = @PLUGINDIR@ +PLUGIN_AUTH_PAM_CFLAGS = @PLUGIN_AUTH_PAM_CFLAGS@ +PLUGIN_AUTH_PAM_LIBS = @PLUGIN_AUTH_PAM_LIBS@ +RANLIB = @RANLIB@ +RC = @RC@ +ROUTE = @ROUTE@ +RST2HTML = @RST2HTML@ +RST2MAN = @RST2MAN@ +SED = @SED@ +SELINUX_LIBS = @SELINUX_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKETS_LIBS = @SOCKETS_LIBS@ +STRIP = @STRIP@ +SYSTEMD_ASK_PASSWORD = @SYSTEMD_ASK_PASSWORD@ +SYSTEMD_UNIT_DIR = @SYSTEMD_UNIT_DIR@ +TAP_CFLAGS = @TAP_CFLAGS@ +TAP_WIN_COMPONENT_ID = @TAP_WIN_COMPONENT_ID@ +TAP_WIN_MIN_MAJOR = @TAP_WIN_MIN_MAJOR@ +TAP_WIN_MIN_MINOR = @TAP_WIN_MIN_MINOR@ +TEST_CFLAGS = @TEST_CFLAGS@ +TEST_LDFLAGS = @TEST_LDFLAGS@ +TMPFILES_DIR = @TMPFILES_DIR@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +libsystemd_CFLAGS = @libsystemd_CFLAGS@ +libsystemd_LIBS = @libsystemd_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sampledir = @sampledir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdunitdir = @systemdunitdir@ +target_alias = @target_alias@ +tmpfilesdir = @tmpfilesdir@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) + +LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +EXTRA_DIST = \ + openvpnmsica.vcxproj \ + openvpnmsica.vcxproj.filters \ + openvpnmsica.props \ + openvpnmsica-Debug.props \ + openvpnmsica-Release.props + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include -I$(top_srcdir)/src/compat + +AM_CFLAGS = \ + $(TAP_CFLAGS) + +@WIN32_TRUE@lib_LTLIBRARIES = libopenvpnmsica.la +@WIN32_TRUE@libopenvpnmsica_la_CFLAGS = \ +@WIN32_TRUE@ -municode -D_UNICODE \ +@WIN32_TRUE@ -UNTDDI_VERSION -U_WIN32_WINNT \ +@WIN32_TRUE@ -D_WIN32_WINNT=_WIN32_WINNT_VISTA \ +@WIN32_TRUE@ -Wl,--kill-at + +@WIN32_TRUE@libopenvpnmsica_la_LDFLAGS = -ladvapi32 -lole32 -lmsi -lsetupapi -liphlpapi -lshell32 -lshlwapi -lversion -no-undefined -avoid-version +libopenvpnmsica_la_SOURCES = \ + dllmain.c \ + msiex.c msiex.h \ + msica_arg.c msica_arg.h \ + openvpnmsica.c openvpnmsica.h \ + $(top_srcdir)/src/tapctl/basic.h \ + $(top_srcdir)/src/tapctl/error.c $(top_srcdir)/src/tapctl/error.h \ + $(top_srcdir)/src/tapctl/tap.c $(top_srcdir)/src/tapctl/tap.h \ + openvpnmsica_resources.rc + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .mc .o .obj .rc +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/build/ltrc.inc $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/openvpnmsica/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/openvpnmsica/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/build/ltrc.inc $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libopenvpnmsica.la: $(libopenvpnmsica_la_OBJECTS) $(libopenvpnmsica_la_DEPENDENCIES) $(EXTRA_libopenvpnmsica_la_DEPENDENCIES) + $(AM_V_CCLD)$(libopenvpnmsica_la_LINK) $(am_libopenvpnmsica_la_rpath) $(libopenvpnmsica_la_OBJECTS) $(libopenvpnmsica_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-error.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libopenvpnmsica_la-tap.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libopenvpnmsica_la-dllmain.lo: dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-dllmain.Tpo -c -o libopenvpnmsica_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-dllmain.Tpo $(DEPDIR)/libopenvpnmsica_la-dllmain.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libopenvpnmsica_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c + +libopenvpnmsica_la-msiex.lo: msiex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-msiex.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-msiex.Tpo -c -o libopenvpnmsica_la-msiex.lo `test -f 'msiex.c' || echo '$(srcdir)/'`msiex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-msiex.Tpo $(DEPDIR)/libopenvpnmsica_la-msiex.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msiex.c' object='libopenvpnmsica_la-msiex.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-msiex.lo `test -f 'msiex.c' || echo '$(srcdir)/'`msiex.c + +libopenvpnmsica_la-msica_arg.lo: msica_arg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-msica_arg.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-msica_arg.Tpo -c -o libopenvpnmsica_la-msica_arg.lo `test -f 'msica_arg.c' || echo '$(srcdir)/'`msica_arg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-msica_arg.Tpo $(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msica_arg.c' object='libopenvpnmsica_la-msica_arg.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-msica_arg.lo `test -f 'msica_arg.c' || echo '$(srcdir)/'`msica_arg.c + +libopenvpnmsica_la-openvpnmsica.lo: openvpnmsica.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-openvpnmsica.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Tpo -c -o libopenvpnmsica_la-openvpnmsica.lo `test -f 'openvpnmsica.c' || echo '$(srcdir)/'`openvpnmsica.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Tpo $(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openvpnmsica.c' object='libopenvpnmsica_la-openvpnmsica.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-openvpnmsica.lo `test -f 'openvpnmsica.c' || echo '$(srcdir)/'`openvpnmsica.c + +libopenvpnmsica_la-error.lo: $(top_srcdir)/src/tapctl/error.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-error.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-error.Tpo -c -o libopenvpnmsica_la-error.lo `test -f '$(top_srcdir)/src/tapctl/error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/error.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-error.Tpo $(DEPDIR)/libopenvpnmsica_la-error.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/tapctl/error.c' object='libopenvpnmsica_la-error.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-error.lo `test -f '$(top_srcdir)/src/tapctl/error.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/error.c + +libopenvpnmsica_la-tap.lo: $(top_srcdir)/src/tapctl/tap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -MT libopenvpnmsica_la-tap.lo -MD -MP -MF $(DEPDIR)/libopenvpnmsica_la-tap.Tpo -c -o libopenvpnmsica_la-tap.lo `test -f '$(top_srcdir)/src/tapctl/tap.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/tap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libopenvpnmsica_la-tap.Tpo $(DEPDIR)/libopenvpnmsica_la-tap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_srcdir)/src/tapctl/tap.c' object='libopenvpnmsica_la-tap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libopenvpnmsica_la_CFLAGS) $(CFLAGS) -c -o libopenvpnmsica_la-tap.lo `test -f '$(top_srcdir)/src/tapctl/tap.c' || echo '$(srcdir)/'`$(top_srcdir)/src/tapctl/tap.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-error.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-dllmain.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-error.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msica_arg.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-msiex.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-openvpnmsica.Plo + -rm -f ./$(DEPDIR)/libopenvpnmsica_la-tap.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +.rc.lo: + $(LTRCCOMPILE) -i "$<" -o "$@" + +.rc.o: + $(RCCOMPILE) -i "$<" -o "$@" + +.mc.rc: + $(WINDMC) "$<" + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/openvpnmsica/dllmain.c b/src/openvpnmsica/dllmain.c new file mode 100644 index 0000000..7315543 --- /dev/null +++ b/src/openvpnmsica/dllmain.c @@ -0,0 +1,198 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#elif defined(_MSC_VER) +#include <config-msvc.h> +#endif + +#include "openvpnmsica.h" +#include "../tapctl/error.h" + +#include <windows.h> +#include <msi.h> +#include <msiquery.h> +#ifdef _MSC_VER +#pragma comment(lib, "msi.lib") +#endif +#include <stdio.h> +#include <tchar.h> + + +DWORD openvpnmsica_thread_data_idx = TLS_OUT_OF_INDEXES; + + +/** + * DLL entry point + */ +BOOL WINAPI +DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD dwReason, + _In_ LPVOID lpReserved) +{ + UNREFERENCED_PARAMETER(hinstDLL); + UNREFERENCED_PARAMETER(lpReserved); + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + /* Allocate thread local storage index. */ + openvpnmsica_thread_data_idx = TlsAlloc(); + if (openvpnmsica_thread_data_idx == TLS_OUT_OF_INDEXES) + { + return FALSE; + } + /* Fall through. */ + + case DLL_THREAD_ATTACH: + { + /* Create thread local storage data. */ + struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)calloc(1, sizeof(struct openvpnmsica_thread_data)); + if (s == NULL) + { + return FALSE; + } + + TlsSetValue(openvpnmsica_thread_data_idx, s); + break; + } + + case DLL_PROCESS_DETACH: + if (openvpnmsica_thread_data_idx != TLS_OUT_OF_INDEXES) + { + /* Free thread local storage data and index. */ + free(TlsGetValue(openvpnmsica_thread_data_idx)); + TlsFree(openvpnmsica_thread_data_idx); + } + break; + + case DLL_THREAD_DETACH: + /* Free thread local storage data. */ + free(TlsGetValue(openvpnmsica_thread_data_idx)); + break; + } + + return TRUE; +} + + +bool +dont_mute(unsigned int flags) +{ + UNREFERENCED_PARAMETER(flags); + + return true; +} + + +void +x_msg_va(const unsigned int flags, const char *format, va_list arglist) +{ + /* Secure last error before it is overridden. */ + DWORD dwResult = (flags & M_ERRNO) != 0 ? GetLastError() : ERROR_SUCCESS; + + struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)TlsGetValue(openvpnmsica_thread_data_idx); + if (s->hInstall == 0) + { + /* No MSI session, no fun. */ + return; + } + + /* Prepare the message record. The record will contain up to four fields. */ + MSIHANDLE hRecordProg = MsiCreateRecord(4); + + { + /* Field 2: The message string. */ + char szBufStack[128]; + int iResultLen = vsnprintf(szBufStack, _countof(szBufStack), format, arglist); + if (iResultLen < _countof(szBufStack)) + { + /* Use from stack. */ + MsiRecordSetStringA(hRecordProg, 2, szBufStack); + } + else + { + /* Allocate on heap and retry. */ + char *szMessage = (char *)malloc(++iResultLen * sizeof(char)); + if (szMessage != NULL) + { + vsnprintf(szMessage, iResultLen, format, arglist); + MsiRecordSetStringA(hRecordProg, 2, szMessage); + free(szMessage); + } + else + { + /* Use stack variant anyway, but make sure it's zero-terminated. */ + szBufStack[_countof(szBufStack) - 1] = 0; + MsiRecordSetStringA(hRecordProg, 2, szBufStack); + } + } + } + + if ((flags & M_ERRNO) == 0) + { + /* Field 1: MSI Error Code */ + MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA); + } + else + { + /* Field 1: MSI Error Code */ + MsiRecordSetInteger(hRecordProg, 1, ERROR_MSICA_ERRNO); + + /* Field 3: The Windows error number. */ + MsiRecordSetInteger(hRecordProg, 3, dwResult); + + /* Field 4: The Windows error description. */ + LPTSTR szErrMessage = NULL; + if (FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, + dwResult, + 0, + (LPTSTR)&szErrMessage, + 0, + NULL) && szErrMessage) + { + /* Trim trailing whitespace. Set terminator after the last non-whitespace character. This prevents excessive trailing line breaks. */ + for (size_t i = 0, i_last = 0;; i++) + { + if (szErrMessage[i]) + { + if (!_istspace(szErrMessage[i])) + { + i_last = i + 1; + } + } + else + { + szErrMessage[i_last] = 0; + break; + } + } + MsiRecordSetString(hRecordProg, 4, szErrMessage); + LocalFree(szErrMessage); + } + } + + MsiProcessMessage(s->hInstall, (flags & M_WARN) ? INSTALLMESSAGE_INFO : INSTALLMESSAGE_ERROR, hRecordProg); + MsiCloseHandle(hRecordProg); +} diff --git a/src/openvpnmsica/msica_arg.c b/src/openvpnmsica/msica_arg.c new file mode 100644 index 0000000..cde0577 --- /dev/null +++ b/src/openvpnmsica/msica_arg.c @@ -0,0 +1,139 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#elif defined(_MSC_VER) +#include <config-msvc.h> +#endif + +#include "msica_arg.h" +#include "../tapctl/error.h" +#include "../tapctl/tap.h" + +#include <windows.h> +#include <malloc.h> + + +void +msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq) +{ + seq->head = NULL; + seq->tail = NULL; +} + + +void +msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq) +{ + while (seq->head) + { + struct msica_arg *p = seq->head; + seq->head = seq->head->next; + free(p); + } + seq->tail = NULL; +} + + +void +msica_arg_seq_add_head( + _Inout_ struct msica_arg_seq *seq, + _In_z_ LPCTSTR argument) +{ + size_t argument_size = (_tcslen(argument) + 1) * sizeof(TCHAR); + struct msica_arg *p = malloc(sizeof(struct msica_arg) + argument_size); + if (p == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct msica_arg) + argument_size); + } + memcpy(p->val, argument, argument_size); + p->next = seq->head; + seq->head = p; + if (seq->tail == NULL) + { + seq->tail = p; + } +} + + +void +msica_arg_seq_add_tail( + _Inout_ struct msica_arg_seq *seq, + _Inout_ LPCTSTR argument) +{ + size_t argument_size = (_tcslen(argument) + 1) * sizeof(TCHAR); + struct msica_arg *p = malloc(sizeof(struct msica_arg) + argument_size); + if (p == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, sizeof(struct msica_arg) + argument_size); + } + memcpy(p->val, argument, argument_size); + p->next = NULL; + *(seq->tail ? &seq->tail->next : &seq->head) = p; + seq->tail = p; +} + + +LPTSTR +msica_arg_seq_join(_In_ const struct msica_arg_seq *seq) +{ + /* Count required space. */ + size_t size = 2 /*x + zero-terminator*/; + for (struct msica_arg *p = seq->head; p != NULL; p = p->next) + { + size += _tcslen(p->val) + 1 /*space delimiter|zero-terminator*/; + } + size *= sizeof(TCHAR); + + /* Allocate. */ + LPTSTR str = malloc(size); + if (str == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, size); + return NULL; + } + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4996) /* Using unsafe string functions: The space in s and termination of p->val has been implicitly verified at the beginning of this function. */ +#endif + + /* Dummy argv[0] (i.e. executable name), for CommandLineToArgvW to work correctly when parsing this string. */ + _tcscpy(str, TEXT("x")); + + /* Join. */ + LPTSTR s = str + 1 /*x*/; + for (struct msica_arg *p = seq->head; p != NULL; p = p->next) + { + /* Convert zero-terminator into space delimiter. */ + s[0] = TEXT(' '); + s++; + /* Append argument. */ + _tcscpy(s, p->val); + s += _tcslen(p->val); + } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + return str; +} diff --git a/src/openvpnmsica/msica_arg.h b/src/openvpnmsica/msica_arg.h new file mode 100644 index 0000000..4bf3c09 --- /dev/null +++ b/src/openvpnmsica/msica_arg.h @@ -0,0 +1,112 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 MSICA_ARG_H +#define MSICA_ARG_H + +#include <windows.h> +#include <tchar.h> +#include "../tapctl/basic.h" + + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4200) /* Using zero-sized arrays in struct/union. */ +#endif + + +/** + * Argument list + */ +struct msica_arg +{ + struct msica_arg *next; /** Pointer to the next argument in the sequence */ + TCHAR val[]; /** Zero terminated argument string */ +}; + + +/** + * Argument sequence + */ +struct msica_arg_seq +{ + struct msica_arg *head; /** Pointer to the first argument in the sequence */ + struct msica_arg *tail; /** Pointer to the last argument in the sequence */ +}; + + +/** + * Initializes argument sequence + * + * @param seq Pointer to uninitialized argument sequence + */ +void +msica_arg_seq_init(_Inout_ struct msica_arg_seq *seq); + + +/** + * Frees argument sequence + * + * @param seq Pointer to the argument sequence + */ +void +msica_arg_seq_free(_Inout_ struct msica_arg_seq *seq); + + +/** + * Inserts argument to the beginning of the argument sequence + * + * @param seq Pointer to the argument sequence + * + * @param argument Zero-terminated argument string to insert. + */ +void +msica_arg_seq_add_head( + _Inout_ struct msica_arg_seq *seq, + _In_z_ LPCTSTR argument); + + +/** + * Appends argument to the end of the argument sequence + * + * @param seq Pointer to the argument sequence + * + * @param argument Zero-terminated argument string to append. + */ +void +msica_arg_seq_add_tail( + _Inout_ struct msica_arg_seq *seq, + _Inout_ LPCTSTR argument); + +/** + * Join arguments of the argument sequence into a space delimited string + * + * @param seq Pointer to the argument sequence + * + * @return Joined argument string. Must be released with free() after use. + */ +LPTSTR +msica_arg_seq_join(_In_ const struct msica_arg_seq *seq); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* ifndef MSICA_ARG_H */ diff --git a/src/openvpnmsica/msiex.c b/src/openvpnmsica/msiex.c new file mode 100644 index 0000000..54b2b97 --- /dev/null +++ b/src/openvpnmsica/msiex.c @@ -0,0 +1,265 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#elif defined(_MSC_VER) +#include <config-msvc.h> +#endif + +#include "msiex.h" +#include "../tapctl/error.h" + +#include <windows.h> +#include <malloc.h> +#include <memory.h> +#include <msiquery.h> +#ifdef _MSC_VER +#pragma comment(lib, "msi.lib") +#endif + + +UINT +msi_get_string( + _In_ MSIHANDLE hInstall, + _In_z_ LPCTSTR szName, + _Out_ LPTSTR *pszValue) +{ + if (pszValue == NULL) + { + return ERROR_BAD_ARGUMENTS; + } + + /* Try with stack buffer first. */ + TCHAR szBufStack[128]; + DWORD dwLength = _countof(szBufStack); + UINT uiResult = MsiGetProperty(hInstall, szName, szBufStack, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + /* Copy from stack. */ + *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (*pszValue == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR)); + return ERROR_SUCCESS; + } + else if (uiResult == ERROR_MORE_DATA) + { + /* Allocate on heap and retry. */ + LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (szBufHeap == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + uiResult = MsiGetProperty(hInstall, szName, szBufHeap, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + *pszValue = szBufHeap; + } + else + { + free(szBufHeap); + } + return uiResult; + } + else + { + SetLastError(uiResult); /* MSDN does not mention MsiGetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiGetProperty failed", __FUNCTION__); + return uiResult; + } +} + + +UINT +msi_get_record_string( + _In_ MSIHANDLE hRecord, + _In_ unsigned int iField, + _Out_ LPTSTR *pszValue) +{ + if (pszValue == NULL) + { + return ERROR_BAD_ARGUMENTS; + } + + /* Try with stack buffer first. */ + TCHAR szBufStack[128]; + DWORD dwLength = _countof(szBufStack); + UINT uiResult = MsiRecordGetString(hRecord, iField, szBufStack, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + /* Copy from stack. */ + *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (*pszValue == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR)); + return ERROR_SUCCESS; + } + else if (uiResult == ERROR_MORE_DATA) + { + /* Allocate on heap and retry. */ + LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (szBufHeap == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + uiResult = MsiRecordGetString(hRecord, iField, szBufHeap, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + *pszValue = szBufHeap; + } + else + { + free(szBufHeap); + } + return uiResult; + } + else + { + SetLastError(uiResult); /* MSDN does not mention MsiRecordGetString() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordGetString failed", __FUNCTION__); + return uiResult; + } +} + + +UINT +msi_format_record( + _In_ MSIHANDLE hInstall, + _In_ MSIHANDLE hRecord, + _Out_ LPTSTR *pszValue) +{ + if (pszValue == NULL) + { + return ERROR_BAD_ARGUMENTS; + } + + /* Try with stack buffer first. */ + TCHAR szBufStack[128]; + DWORD dwLength = _countof(szBufStack); + UINT uiResult = MsiFormatRecord(hInstall, hRecord, szBufStack, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + /* Copy from stack. */ + *pszValue = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (*pszValue == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + memcpy(*pszValue, szBufStack, dwLength * sizeof(TCHAR)); + return ERROR_SUCCESS; + } + else if (uiResult == ERROR_MORE_DATA) + { + /* Allocate on heap and retry. */ + LPTSTR szBufHeap = (LPTSTR)malloc(++dwLength * sizeof(TCHAR)); + if (szBufHeap == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwLength * sizeof(TCHAR)); + return ERROR_OUTOFMEMORY; + } + + uiResult = MsiFormatRecord(hInstall, hRecord, szBufHeap, &dwLength); + if (uiResult == ERROR_SUCCESS) + { + *pszValue = szBufHeap; + } + else + { + free(szBufHeap); + } + return uiResult; + } + else + { + SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__); + return uiResult; + } +} + + +UINT +msi_format_field( + _In_ MSIHANDLE hInstall, + _In_ MSIHANDLE hRecord, + _In_ unsigned int iField, + _Out_ LPTSTR *pszValue) +{ + if (pszValue == NULL) + { + return ERROR_BAD_ARGUMENTS; + } + + /* Read string to format. */ + LPTSTR szValue = NULL; + UINT uiResult = msi_get_record_string(hRecord, iField, &szValue); + if (uiResult != ERROR_SUCCESS) + { + return uiResult; + } + if (szValue[0] == 0) + { + /* The string is empty. There's nothing left to do. */ + *pszValue = szValue; + return ERROR_SUCCESS; + } + + /* Create a temporary record. */ + MSIHANDLE hRecordEx = MsiCreateRecord(1); + if (!hRecordEx) + { + uiResult = ERROR_INVALID_HANDLE; + msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__); + goto cleanup_szValue; + } + + /* Populate the record with data. */ + uiResult = MsiRecordSetString(hRecordEx, 0, szValue); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__); + goto cleanup_hRecordEx; + } + + /* Do the formatting. */ + uiResult = msi_format_record(hInstall, hRecordEx, pszValue); + +cleanup_hRecordEx: + MsiCloseHandle(hRecordEx); +cleanup_szValue: + free(szValue); + return uiResult; +} diff --git a/src/openvpnmsica/msiex.h b/src/openvpnmsica/msiex.h new file mode 100644 index 0000000..cae4298 --- /dev/null +++ b/src/openvpnmsica/msiex.h @@ -0,0 +1,112 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 MSIHLP_H +#define MSIHLP_H + +#include <windows.h> +#include <msi.h> +#include "../tapctl/basic.h" + + +/** + * Gets MSI property value + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @param szName Property name + * + * @param pszValue Pointer to string to retrieve property value. The string must + * be released with free() after use. + * + * @return ERROR_SUCCESS on success; Win32 error code otherwise + */ +UINT +msi_get_string( + _In_ MSIHANDLE hInstall, + _In_z_ LPCTSTR szName, + _Out_ LPTSTR *pszValue); + + +/** + * Gets MSI record string value + * + * @param hRecord Handle to the record + * + * @param iField Field index + * + * @param pszValue Pointer to string to retrieve field value. The string must be + * released with free() after use. + * + * @return ERROR_SUCCESS on success; Win32 error code otherwise + */ +UINT +msi_get_record_string( + _In_ MSIHANDLE hRecord, + _In_ unsigned int iField, + _Out_ LPTSTR *pszValue); + + +/** + * Formats MSI record + * + * @param hInstall Handle to the installation. This may be omitted, in which case only the + * record field parameters are processed and properties are not available + * for substitution. + * + * @param hRecord Handle to the record to format. The template string must be stored in + * record field 0 followed by referenced data parameters. + * + * @param pszValue Pointer to string to retrieve formatted value. The string must be + * released with free() after use. + * + * @return ERROR_SUCCESS on success; Win32 error code otherwise + */ +UINT +msi_format_record( + _In_ MSIHANDLE hInstall, + _In_ MSIHANDLE hRecord, + _Out_ LPTSTR *pszValue); + + +/** + * Formats MSI record field + * + * @param hInstall Handle to the installation. This may be omitted, in which case only the + * record field parameters are processed and properties are not available + * for substitution. + * + * @param hRecord Handle to the field record + * + * @param iField Field index + * + * @param pszValue Pointer to string to retrieve formatted value. The string must be + * released with free() after use. + * + * @return ERROR_SUCCESS on success; Win32 error code otherwise + */ +UINT +msi_format_field( + _In_ MSIHANDLE hInstall, + _In_ MSIHANDLE hRecord, + _In_ unsigned int iField, + _Out_ LPTSTR *pszValue); + +#endif /* ifndef MSIHLP_H */ diff --git a/src/openvpnmsica/openvpnmsica-Debug.props b/src/openvpnmsica/openvpnmsica-Debug.props new file mode 100644 index 0000000..43532cf --- /dev/null +++ b/src/openvpnmsica/openvpnmsica-Debug.props @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets"> + <Import Project="openvpnmsica.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <ItemDefinitionGroup> + <ClCompile> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup /> +</Project>
\ No newline at end of file diff --git a/src/openvpnmsica/openvpnmsica-Release.props b/src/openvpnmsica/openvpnmsica-Release.props new file mode 100644 index 0000000..848fda8 --- /dev/null +++ b/src/openvpnmsica/openvpnmsica-Release.props @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets"> + <Import Project="openvpnmsica.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup /> + <ItemDefinitionGroup> + <ClCompile> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup /> +</Project>
\ No newline at end of file diff --git a/src/openvpnmsica/openvpnmsica.c b/src/openvpnmsica/openvpnmsica.c new file mode 100644 index 0000000..98111fb --- /dev/null +++ b/src/openvpnmsica/openvpnmsica.c @@ -0,0 +1,1297 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#elif defined(_MSC_VER) +#include <config-msvc.h> +#endif +#include <winsock2.h> /* Must be included _before_ <windows.h> */ + +#include "openvpnmsica.h" +#include "msica_arg.h" +#include "msiex.h" + +#include "../tapctl/basic.h" +#include "../tapctl/error.h" +#include "../tapctl/tap.h" + +#include <windows.h> +#include <iphlpapi.h> +#include <malloc.h> +#include <memory.h> +#include <msiquery.h> +#include <shellapi.h> +#include <shlwapi.h> +#include <stdbool.h> +#include <stdlib.h> +#include <tchar.h> + +#ifdef _MSC_VER +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "iphlpapi.lib") +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "version.lib") +#endif + + +/** + * Local constants + */ + +#define MSICA_ADAPTER_TICK_SIZE (16*1024) /** Amount of tick space to reserve for one TAP/TUN adapter creation/deletition. */ + +#define FILE_NEED_REBOOT L".ovpn_need_reboot" + +/** + * Joins an argument sequence and sets it to the MSI property. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @param szProperty MSI property name to set to the joined argument sequence. + * + * @param seq The argument sequence. + * + * @return ERROR_SUCCESS on success; An error code otherwise + */ +static UINT +setup_sequence( + _In_ MSIHANDLE hInstall, + _In_z_ LPCTSTR szProperty, + _In_ struct msica_arg_seq *seq) +{ + UINT uiResult; + LPTSTR szSequence = msica_arg_seq_join(seq); + uiResult = MsiSetProperty(hInstall, szProperty, szSequence); + free(szSequence); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szProperty); + return uiResult; + } + return ERROR_SUCCESS; +} + + +#ifdef _DEBUG + +/** + * Pops up a message box creating a time window to attach a debugger to the installer process in + * order to debug custom actions. + * + * @param szFunctionName Function name that triggered the pop-up. Displayed in message box's + * title. + */ +static void +_debug_popup(_In_z_ LPCTSTR szFunctionName) +{ + TCHAR szTitle[0x100], szMessage[0x100+MAX_PATH], szProcessPath[MAX_PATH]; + + /* Compose pop-up title. The dialog title will contain function name to ease the process + * locating. Mind that Visual Studio displays window titles on the process list. */ + _stprintf_s(szTitle, _countof(szTitle), TEXT("%s v%s"), szFunctionName, TEXT(PACKAGE_VERSION)); + + /* Get process name. */ + GetModuleFileName(NULL, szProcessPath, _countof(szProcessPath)); + LPCTSTR szProcessName = _tcsrchr(szProcessPath, TEXT('\\')); + szProcessName = szProcessName ? szProcessName + 1 : szProcessPath; + + /* Compose the pop-up message. */ + _stprintf_s( + szMessage, _countof(szMessage), + TEXT("The %s process (PID: %u) has started to execute the %s custom action.\r\n") + TEXT("\r\n") + TEXT("If you would like to debug the custom action, attach a debugger to this process and set breakpoints before dismissing this dialog.\r\n") + TEXT("\r\n") + TEXT("If you are not debugging this custom action, you can safely ignore this message."), + szProcessName, + GetCurrentProcessId(), + szFunctionName); + + MessageBox(NULL, szMessage, szTitle, MB_OK); +} + +#define debug_popup(f) _debug_popup(f) +#else /* ifdef _DEBUG */ +#define debug_popup(f) +#endif /* ifdef _DEBUG */ + + +/** + * Detects if the OpenVPNService service is in use (running or paused) and sets + * OPENVPNSERVICE to the service process PID, or its path if it is set to + * auto-start, but not running. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +static UINT +set_openvpnserv_state(_In_ MSIHANDLE hInstall) +{ + UINT uiResult; + + /* Get Service Control Manager handle. */ + SC_HANDLE hSCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT); + if (hSCManager == NULL) + { + uiResult = GetLastError(); + msg(M_NONFATAL | M_ERRNO, "%s: OpenSCManager() failed", __FUNCTION__); + return uiResult; + } + + /* Get OpenVPNService service handle. */ + SC_HANDLE hService = OpenService(hSCManager, TEXT("OpenVPNService"), SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + if (hService == NULL) + { + uiResult = GetLastError(); + if (uiResult == ERROR_SERVICE_DOES_NOT_EXIST) + { + /* This is not actually an error. */ + goto cleanup_OpenSCManager; + } + msg(M_NONFATAL | M_ERRNO, "%s: OpenService(\"OpenVPNService\") failed", __FUNCTION__); + goto cleanup_OpenSCManager; + } + + /* Query service status. */ + SERVICE_STATUS_PROCESS ssp; + DWORD dwBufSize; + if (QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, sizeof(ssp), &dwBufSize)) + { + switch (ssp.dwCurrentState) + { + case SERVICE_START_PENDING: + case SERVICE_RUNNING: + case SERVICE_STOP_PENDING: + case SERVICE_PAUSE_PENDING: + case SERVICE_PAUSED: + case SERVICE_CONTINUE_PENDING: + { + /* Service is started (kind of). Set OPENVPNSERVICE property to service PID. */ + TCHAR szPID[10 /*MAXDWORD in decimal*/ + 1 /*terminator*/]; + _stprintf_s( + szPID, _countof(szPID), + TEXT("%u"), + ssp.dwProcessId); + + uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), szPID); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__); + } + + /* We know user is using the service. Skip auto-start setting check. */ + goto cleanup_OpenService; + } + break; + } + } + else + { + uiResult = GetLastError(); + msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"OpenVPNService\") failed", __FUNCTION__); + } + + /* Service is not started. Is it set to auto-start? */ + /* MSDN describes the maximum buffer size for QueryServiceConfig() to be 8kB. */ + /* This is small enough to fit on stack. */ + BYTE _buffer_8k[8192]; + LPQUERY_SERVICE_CONFIG pQsc = (LPQUERY_SERVICE_CONFIG)_buffer_8k; + dwBufSize = sizeof(_buffer_8k); + if (!QueryServiceConfig(hService, pQsc, dwBufSize, &dwBufSize)) + { + uiResult = GetLastError(); + msg(M_NONFATAL | M_ERRNO, "%s: QueryServiceStatusEx(\"QueryServiceConfig\") failed", __FUNCTION__); + goto cleanup_OpenService; + } + + if (pQsc->dwStartType <= SERVICE_AUTO_START) + { + /* Service is set to auto-start. Set OPENVPNSERVICE property to its path. */ + uiResult = MsiSetProperty(hInstall, TEXT("OPENVPNSERVICE"), pQsc->lpBinaryPathName); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"OPENVPNSERVICE\") failed", __FUNCTION__); + goto cleanup_OpenService; + } + } + + uiResult = ERROR_SUCCESS; + +cleanup_OpenService: + CloseServiceHandle(hService); +cleanup_OpenSCManager: + CloseServiceHandle(hSCManager); + return uiResult; +} + + +static void +find_adapters( + _In_ MSIHANDLE hInstall, + _In_z_ LPCTSTR szzHardwareIDs, + _In_z_ LPCTSTR szAdaptersPropertyName, + _In_z_ LPCTSTR szActiveAdaptersPropertyName) +{ + UINT uiResult; + + /* Get network adapters with given hardware ID. */ + struct tap_adapter_node *pAdapterList = NULL; + uiResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList); + if (uiResult != ERROR_SUCCESS) + { + return; + } + else if (pAdapterList == NULL) + { + /* No adapters - no fun. */ + return; + } + + /* Get IPv4/v6 info for all network adapters. Actually, we're interested in link status only: up/down? */ + PIP_ADAPTER_ADDRESSES pAdapterAdresses = NULL; + ULONG ulAdapterAdressesSize = 16*1024; + for (size_t iteration = 0; iteration < 2; iteration++) + { + pAdapterAdresses = (PIP_ADAPTER_ADDRESSES)malloc(ulAdapterAdressesSize); + if (pAdapterAdresses == NULL) + { + msg(M_NONFATAL, "%s: malloc(%u) failed", __FUNCTION__, ulAdapterAdressesSize); + uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterList; + } + + ULONG ulResult = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_ALL_INTERFACES, + NULL, + pAdapterAdresses, + &ulAdapterAdressesSize); + + if (ulResult == ERROR_SUCCESS) + { + break; + } + + free(pAdapterAdresses); + if (ulResult != ERROR_BUFFER_OVERFLOW) + { + SetLastError(ulResult); /* MSDN does not mention GetAdaptersAddresses() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: GetAdaptersAddresses() failed", __FUNCTION__); + uiResult = ulResult; goto cleanup_pAdapterList; + } + } + + /* Count adapters. */ + size_t adapter_count = 0; + for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext) + { + adapter_count++; + } + + /* Prepare semicolon delimited list of TAP adapter ID(s) and active TAP adapter ID(s). */ + LPTSTR + szAdapters = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)), + szAdaptersTail = szAdapters; + if (szAdapters == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)); + uiResult = ERROR_OUTOFMEMORY; goto cleanup_pAdapterAdresses; + } + + LPTSTR + szAdaptersActive = (LPTSTR)malloc(adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)), + szAdaptersActiveTail = szAdaptersActive; + if (szAdaptersActive == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, adapter_count * (38 /*GUID*/ + 1 /*separator/terminator*/) * sizeof(TCHAR)); + uiResult = ERROR_OUTOFMEMORY; goto cleanup_szAdapters; + } + + for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter; pAdapter = pAdapter->pNext) + { + /* Convert adapter GUID to UTF-16 string. (LPOLESTR defaults to LPWSTR) */ + LPOLESTR szAdapterId = NULL; + StringFromIID((REFIID)&pAdapter->guid, &szAdapterId); + + /* Append to the list of TAP adapter ID(s). */ + if (szAdapters < szAdaptersTail) + { + *(szAdaptersTail++) = TEXT(';'); + } + memcpy(szAdaptersTail, szAdapterId, 38 * sizeof(TCHAR)); + szAdaptersTail += 38; + + /* If this adapter is active (connected), add it to the list of active TAP adapter ID(s). */ + for (PIP_ADAPTER_ADDRESSES p = pAdapterAdresses; p; p = p->Next) + { + OLECHAR szId[38 /*GUID*/ + 1 /*terminator*/]; + GUID guid; + if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, p->AdapterName, -1, szId, _countof(szId)) > 0 + && SUCCEEDED(IIDFromString(szId, &guid)) + && memcmp(&guid, &pAdapter->guid, sizeof(GUID)) == 0) + { + if (p->OperStatus == IfOperStatusUp) + { + /* This TAP adapter is active (connected). */ + if (szAdaptersActive < szAdaptersActiveTail) + { + *(szAdaptersActiveTail++) = TEXT(';'); + } + memcpy(szAdaptersActiveTail, szAdapterId, 38 * sizeof(TCHAR)); + szAdaptersActiveTail += 38; + } + break; + } + } + CoTaskMemFree(szAdapterId); + } + szAdaptersTail [0] = 0; + szAdaptersActiveTail[0] = 0; + + /* Set Installer properties. */ + uiResult = MsiSetProperty(hInstall, szAdaptersPropertyName, szAdapters); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szAdaptersPropertyName); + goto cleanup_szAdaptersActive; + } + uiResult = MsiSetProperty(hInstall, szActiveAdaptersPropertyName, szAdaptersActive); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiSetProperty() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiSetProperty(\"%s\") failed", __FUNCTION__, szActiveAdaptersPropertyName); + goto cleanup_szAdaptersActive; + } + +cleanup_szAdaptersActive: + free(szAdaptersActive); +cleanup_szAdapters: + free(szAdapters); +cleanup_pAdapterAdresses: + free(pAdapterAdresses); +cleanup_pAdapterList: + tap_free_adapter_list(pAdapterList); +} + + +UINT __stdcall +FindSystemInfo(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + + debug_popup(TEXT(__FUNCTION__)); + + BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL)); + + OPENVPNMSICA_SAVE_MSI_SESSION(hInstall); + + set_openvpnserv_state(hInstall); + find_adapters( + hInstall, + TEXT("root\\") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0") TEXT(TAP_WIN_COMPONENT_ID) TEXT("\0"), + TEXT("TAPWINDOWS6ADAPTERS"), + TEXT("ACTIVETAPWINDOWS6ADAPTERS")); + find_adapters( + hInstall, + TEXT("Wintun") TEXT("\0"), + TEXT("WINTUNADAPTERS"), + TEXT("ACTIVEWINTUNADAPTERS")); + + if (bIsCoInitialized) + { + CoUninitialize(); + } + return ERROR_SUCCESS; +} + + +UINT __stdcall +CloseOpenVPNGUI(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + UNREFERENCED_PARAMETER(hInstall); /* This CA is does not interact with MSI session (report errors, access properties, tables, etc.). */ + + debug_popup(TEXT(__FUNCTION__)); + + /* Find OpenVPN GUI window. */ + HWND hWnd = FindWindow(TEXT("OpenVPN-GUI"), NULL); + if (hWnd) + { + /* Ask it to close and wait for 100ms. Unfortunately, this will succeed only for recent OpenVPN GUI that do not run elevated. */ + SendMessage(hWnd, WM_CLOSE, 0, 0); + Sleep(100); + } + + return ERROR_SUCCESS; +} + + +UINT __stdcall +StartOpenVPNGUI(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + + debug_popup(TEXT(__FUNCTION__)); + + UINT uiResult; + BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL)); + + OPENVPNMSICA_SAVE_MSI_SESSION(hInstall); + + /* Create and populate a MSI record. */ + MSIHANDLE hRecord = MsiCreateRecord(1); + if (!hRecord) + { + uiResult = ERROR_INVALID_HANDLE; + msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__); + goto cleanup_CoInitialize; + } + uiResult = MsiRecordSetString(hRecord, 0, TEXT("\"[#bin.openvpn_gui.exe]\"")); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiRecordSetString() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiRecordSetString failed", __FUNCTION__); + goto cleanup_MsiCreateRecord; + } + + /* Format string. */ + TCHAR szStackBuf[MAX_PATH]; + DWORD dwPathSize = _countof(szStackBuf); + LPTSTR szPath = szStackBuf; + uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize); + if (uiResult == ERROR_MORE_DATA) + { + /* Allocate buffer on heap (+1 for terminator), and retry. */ + szPath = (LPTSTR)malloc((++dwPathSize) * sizeof(TCHAR)); + if (szPath == NULL) + { + msg(M_FATAL, "%s: malloc(%u) failed", __FUNCTION__, dwPathSize * sizeof(TCHAR)); + uiResult = ERROR_OUTOFMEMORY; goto cleanup_MsiCreateRecord; + } + + uiResult = MsiFormatRecord(hInstall, hRecord, szPath, &dwPathSize); + } + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiFormatRecord() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiFormatRecord failed", __FUNCTION__); + goto cleanup_malloc_szPath; + } + + /* Launch the OpenVPN GUI. */ + SHELLEXECUTEINFO sei = { + .cbSize = sizeof(SHELLEXECUTEINFO), + .fMask = SEE_MASK_FLAG_NO_UI, /* Don't show error UI, we'll display it. */ + .lpFile = szPath, + .nShow = SW_SHOWNORMAL + }; + if (!ShellExecuteEx(&sei)) + { + uiResult = GetLastError(); + msg(M_NONFATAL | M_ERRNO, "%s: ShellExecuteEx(%s) failed", __FUNCTION__, szPath); + goto cleanup_malloc_szPath; + } + + uiResult = ERROR_SUCCESS; + +cleanup_malloc_szPath: + if (szPath != szStackBuf) + { + free(szPath); + } +cleanup_MsiCreateRecord: + MsiCloseHandle(hRecord); +cleanup_CoInitialize: + if (bIsCoInitialized) + { + CoUninitialize(); + } + return uiResult; +} + + +/** + * Schedules adapter creation. + * + * When the rollback is enabled, the adapter deletition is scheduled on rollback. + * + * @param seq The argument sequence to pass to InstallTUNTAPAdapters custom action + * + * @param seqRollback The argument sequence to pass to InstallTUNTAPAdaptersRollback custom + * action. NULL when rollback is disabled. + * + * @param szDisplayName Adapter display name + * + * @param szHardwareId Adapter hardware ID + * + * @param iTicks Pointer to an integer that represents amount of work (on progress + * indicator) the InstallTUNTAPAdapters will take. This function increments it + * by MSICA_ADAPTER_TICK_SIZE for each adapter to create. + * + * @return ERROR_SUCCESS on success; An error code otherwise + */ +static DWORD +schedule_adapter_create( + _Inout_ struct msica_arg_seq *seq, + _Inout_opt_ struct msica_arg_seq *seqRollback, + _In_z_ LPCTSTR szDisplayName, + _In_z_ LPCTSTR szHardwareId, + _Inout_ int *iTicks) +{ + /* Get existing network adapters. */ + struct tap_adapter_node *pAdapterList = NULL; + DWORD dwResult = tap_list_adapters(NULL, NULL, &pAdapterList); + if (dwResult != ERROR_SUCCESS) + { + return dwResult; + } + + /* Does adapter exist? */ + for (struct tap_adapter_node *pAdapterOther = pAdapterList;; pAdapterOther = pAdapterOther->pNext) + { + if (pAdapterOther == NULL) + { + /* No adapter with a same name found. */ + TCHAR szArgument[10 /*create=""|deleteN=""*/ + MAX_PATH /*szDisplayName*/ + 1 /*|*/ + MAX_PATH /*szHardwareId*/ + 1 /*terminator*/]; + + /* InstallTUNTAPAdapters will create the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("create=\"%.*s|%.*s\""), + MAX_PATH, szDisplayName, + MAX_PATH, szHardwareId); + msica_arg_seq_add_tail(seq, szArgument); + + if (seqRollback) + { + /* InstallTUNTAPAdaptersRollback will delete the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("deleteN=\"%.*s\""), + MAX_PATH, szDisplayName); + msica_arg_seq_add_head(seqRollback, szArgument); + } + + *iTicks += MSICA_ADAPTER_TICK_SIZE; + break; + } + else if (_tcsicmp(szDisplayName, pAdapterOther->szName) == 0) + { + /* Adapter with a same name found. */ + for (LPCTSTR hwid = pAdapterOther->szzHardwareIDs;; hwid += _tcslen(hwid) + 1) + { + if (hwid[0] == 0) + { + /* This adapter has a different hardware ID. */ + msg(M_NONFATAL, "%s: Adapter with name \"%" PRIsLPTSTR "\" already exists", __FUNCTION__, pAdapterOther->szName); + dwResult = ERROR_ALREADY_EXISTS; + goto cleanup_pAdapterList; + } + else if (_tcsicmp(hwid, szHardwareId) == 0) + { + /* This is an adapter with the requested hardware ID. We already have what we want! */ + break; + } + } + break; /* Adapter names are unique. There should be no other adapter with this name. */ + } + } + +cleanup_pAdapterList: + tap_free_adapter_list(pAdapterList); + return dwResult; +} + + +/** + * Schedules adapter deletion. + * + * When the rollback is enabled, the adapter deletition is scheduled as: disable in + * UninstallTUNTAPAdapters, enable on rollback, delete on commit. + * + * When rollback is disabled, the adapter deletition is scheduled as delete in + * UninstallTUNTAPAdapters. + * + * @param seq The argument sequence to pass to UninstallTUNTAPAdapters custom action + * + * @param seqCommit The argument sequence to pass to UninstallTUNTAPAdaptersCommit custom + * action. NULL when rollback is disabled. + * + * @param seqRollback The argument sequence to pass to UninstallTUNTAPAdaptersRollback custom + * action. NULL when rollback is disabled. + * + * @param szDisplayName Adapter display name + * + * @param szzHardwareIDs String of strings with acceptable adapter hardware IDs + * + * @param iTicks Pointer to an integer that represents amount of work (on progress + * indicator) the UninstallTUNTAPAdapters will take. This function increments + * it by MSICA_ADAPTER_TICK_SIZE for each adapter to delete. + * + * @return ERROR_SUCCESS on success; An error code otherwise + */ +static DWORD +schedule_adapter_delete( + _Inout_ struct msica_arg_seq *seq, + _Inout_opt_ struct msica_arg_seq *seqCommit, + _Inout_opt_ struct msica_arg_seq *seqRollback, + _In_z_ LPCTSTR szDisplayName, + _In_z_ LPCTSTR szzHardwareIDs, + _Inout_ int *iTicks) +{ + /* Get adapters with given hardware ID. */ + struct tap_adapter_node *pAdapterList = NULL; + DWORD dwResult = tap_list_adapters(NULL, szzHardwareIDs, &pAdapterList); + if (dwResult != ERROR_SUCCESS) + { + return dwResult; + } + + /* Does adapter exist? */ + for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext) + { + if (_tcsicmp(szDisplayName, pAdapter->szName) == 0) + { + /* Adapter found. */ + TCHAR szArgument[8 /*disable=|enable=|delete=*/ + 38 /*{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}*/ + 1 /*terminator*/]; + if (seqCommit && seqRollback) + { + /* UninstallTUNTAPAdapters will disable the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("disable=") TEXT(PRIXGUID), + PRIGUID_PARAM(pAdapter->guid)); + msica_arg_seq_add_tail(seq, szArgument); + + /* UninstallTUNTAPAdaptersRollback will re-enable the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("enable=") TEXT(PRIXGUID), + PRIGUID_PARAM(pAdapter->guid)); + msica_arg_seq_add_head(seqRollback, szArgument); + + /* UninstallTUNTAPAdaptersCommit will delete the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("delete=") TEXT(PRIXGUID), + PRIGUID_PARAM(pAdapter->guid)); + msica_arg_seq_add_tail(seqCommit, szArgument); + } + else + { + /* UninstallTUNTAPAdapters will delete the adapter. */ + _stprintf_s( + szArgument, _countof(szArgument), + TEXT("delete=") TEXT(PRIXGUID), + PRIGUID_PARAM(pAdapter->guid)); + msica_arg_seq_add_tail(seq, szArgument); + } + + iTicks += MSICA_ADAPTER_TICK_SIZE; + break; /* Adapter names are unique. There should be no other adapter with this name. */ + } + } + + tap_free_adapter_list(pAdapterList); + return dwResult; +} + + +UINT __stdcall +EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + + debug_popup(TEXT(__FUNCTION__)); + + UINT uiResult; + BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL)); + + OPENVPNMSICA_SAVE_MSI_SESSION(hInstall); + + struct msica_arg_seq + seqInstall, + seqInstallCommit, + seqInstallRollback, + seqUninstall, + seqUninstallCommit, + seqUninstallRollback; + msica_arg_seq_init(&seqInstall); + msica_arg_seq_init(&seqInstallCommit); + msica_arg_seq_init(&seqInstallRollback); + msica_arg_seq_init(&seqUninstall); + msica_arg_seq_init(&seqUninstallCommit); + msica_arg_seq_init(&seqUninstallRollback); + + /* Check rollback state. */ + bool bRollbackEnabled = MsiEvaluateCondition(hInstall, TEXT("RollbackDisabled")) != MSICONDITION_TRUE; + + /* Open MSI database. */ + MSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall); + if (hDatabase == 0) + { + msg(M_NONFATAL, "%s: MsiGetActiveDatabase failed", __FUNCTION__); + uiResult = ERROR_INVALID_HANDLE; + goto cleanup_exec_seq; + } + + /* Check if TUNTAPAdapter table exists. If it doesn't exist, there's nothing to do. */ + switch (MsiDatabaseIsTablePersistent(hDatabase, TEXT("TUNTAPAdapter"))) + { + case MSICONDITION_FALSE: + case MSICONDITION_TRUE: break; + + default: + uiResult = ERROR_SUCCESS; + goto cleanup_hDatabase; + } + + /* Prepare a query to get a list/view of adapters. */ + MSIHANDLE hViewST = 0; + LPCTSTR szQuery = TEXT("SELECT `Adapter`,`DisplayName`,`Condition`,`Component_`,`HardwareId` FROM `TUNTAPAdapter`"); + uiResult = MsiDatabaseOpenView(hDatabase, szQuery, &hViewST); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiDatabaseOpenView() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiDatabaseOpenView(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery); + goto cleanup_hDatabase; + } + + /* Execute query! */ + uiResult = MsiViewExecute(hViewST, 0); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiViewExecute() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiViewExecute(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szQuery); + goto cleanup_hViewST; + } + + /* Create a record to report progress with. */ + MSIHANDLE hRecordProg = MsiCreateRecord(2); + if (!hRecordProg) + { + uiResult = ERROR_INVALID_HANDLE; + msg(M_NONFATAL, "%s: MsiCreateRecord failed", __FUNCTION__); + goto cleanup_hViewST_close; + } + + for (;; ) + { + /* Fetch one record from the view. */ + MSIHANDLE hRecord = 0; + uiResult = MsiViewFetch(hViewST, &hRecord); + if (uiResult == ERROR_NO_MORE_ITEMS) + { + uiResult = ERROR_SUCCESS; + break; + } + else if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiViewFetch() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiViewFetch failed", __FUNCTION__); + goto cleanup_hRecordProg; + } + + INSTALLSTATE iInstalled, iAction; + { + /* Read adapter component ID (`Component_` is field #4). */ + LPTSTR szValue = NULL; + uiResult = msi_get_record_string(hRecord, 4, &szValue); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_hRecord; + } + + /* Get the component state. */ + uiResult = MsiGetComponentState(hInstall, szValue, &iInstalled, &iAction); + if (uiResult != ERROR_SUCCESS) + { + SetLastError(uiResult); /* MSDN does not mention MsiGetComponentState() to set GetLastError(). But we do have an error code. Set last error manually. */ + msg(M_NONFATAL | M_ERRNO, "%s: MsiGetComponentState(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue); + free(szValue); + goto cleanup_hRecord; + } + free(szValue); + } + + /* Get adapter display name (`DisplayName` is field #2). */ + LPTSTR szDisplayName = NULL; + uiResult = msi_format_field(hInstall, hRecord, 2, &szDisplayName); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_hRecord; + } + /* `DisplayName` field type is [Filename](https://docs.microsoft.com/en-us/windows/win32/msi/filename), which is either "8.3|long name" or "8.3". */ + LPTSTR szDisplayNameEx = _tcschr(szDisplayName, TEXT('|')); + szDisplayNameEx = szDisplayNameEx != NULL ? szDisplayNameEx + 1 : szDisplayName; + + /* Get adapter hardware ID (`HardwareId` is field #5). */ + TCHAR szzHardwareIDs[0x100] = { 0 }; + { + LPTSTR szHwId = NULL; + uiResult = msi_get_record_string(hRecord, 5, &szHwId); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_szDisplayName; + } + memcpy_s(szzHardwareIDs, sizeof(szzHardwareIDs) - 2*sizeof(TCHAR) /*requires double zero termination*/, szHwId, _tcslen(szHwId)*sizeof(TCHAR)); + free(szHwId); + } + + if (iAction > INSTALLSTATE_BROKEN) + { + int iTicks = 0; + + if (iAction >= INSTALLSTATE_LOCAL) + { + /* Read and evaluate adapter condition (`Condition` is field #3). */ + LPTSTR szValue = NULL; + uiResult = msi_get_record_string(hRecord, 3, &szValue); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_szDisplayName; + } +#ifdef __GNUC__ +/* + * warning: enumeration value ‘MSICONDITION_TRUE’ not handled in switch + * warning: enumeration value ‘MSICONDITION_NONE’ not handled in switch + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#endif + switch (MsiEvaluateCondition(hInstall, szValue)) + { + case MSICONDITION_FALSE: + free(szValue); + goto cleanup_szDisplayName; + + case MSICONDITION_ERROR: + uiResult = ERROR_INVALID_FIELD; + msg(M_NONFATAL | M_ERRNO, "%s: MsiEvaluateCondition(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, szValue); + free(szValue); + goto cleanup_szDisplayName; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + free(szValue); + + /* Component is or should be installed. Schedule adapter creation. */ + if (schedule_adapter_create( + &seqInstall, + bRollbackEnabled ? &seqInstallRollback : NULL, + szDisplayNameEx, + szzHardwareIDs, + &iTicks) != ERROR_SUCCESS) + { + uiResult = ERROR_INSTALL_FAILED; + goto cleanup_szDisplayName; + } + } + else + { + /* Component is installed, but should be degraded to advertised/removed. Schedule adapter deletition. + * + * Note: On adapter removal (product is being uninstalled), we tolerate dwResult error. + * Better a partial uninstallation than no uninstallation at all. + */ + schedule_adapter_delete( + &seqUninstall, + bRollbackEnabled ? &seqUninstallCommit : NULL, + bRollbackEnabled ? &seqUninstallRollback : NULL, + szDisplayNameEx, + szzHardwareIDs, + &iTicks); + } + + /* Arrange the amount of tick space to add to the progress indicator. + * Do this within the loop to poll for user cancellation. */ + MsiRecordSetInteger(hRecordProg, 1, 3 /* OP3 = Add ticks to the expected total number of progress of the progress bar */); + MsiRecordSetInteger(hRecordProg, 2, iTicks); + if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) + { + uiResult = ERROR_INSTALL_USEREXIT; + goto cleanup_szDisplayName; + } + } + +cleanup_szDisplayName: + free(szDisplayName); +cleanup_hRecord: + MsiCloseHandle(hRecord); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_hRecordProg; + } + } + + /* save path to user's temp dir to be used later by deferred actions */ + TCHAR tmpDir[MAX_PATH]; + GetTempPath(MAX_PATH, tmpDir); + + TCHAR str[MAX_PATH + 7]; + _stprintf_s(str, _countof(str), TEXT("tmpdir=%") TEXT(PRIsLPTSTR), tmpDir); + msica_arg_seq_add_tail(&seqInstall, str); + msica_arg_seq_add_tail(&seqInstallCommit, str); + msica_arg_seq_add_tail(&seqInstallRollback, str); + msica_arg_seq_add_tail(&seqUninstall, str); + msica_arg_seq_add_tail(&seqUninstallCommit, str); + msica_arg_seq_add_tail(&seqUninstallRollback, str); + + /* Store deferred custom action parameters. */ + if ((uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdapters" ), &seqInstall )) != ERROR_SUCCESS + || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersCommit" ), &seqInstallCommit )) != ERROR_SUCCESS + || (uiResult = setup_sequence(hInstall, TEXT("InstallTUNTAPAdaptersRollback" ), &seqInstallRollback )) != ERROR_SUCCESS + || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdapters" ), &seqUninstall )) != ERROR_SUCCESS + || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersCommit" ), &seqUninstallCommit )) != ERROR_SUCCESS + || (uiResult = setup_sequence(hInstall, TEXT("UninstallTUNTAPAdaptersRollback"), &seqUninstallRollback)) != ERROR_SUCCESS) + { + goto cleanup_hRecordProg; + } + + uiResult = ERROR_SUCCESS; + +cleanup_hRecordProg: + MsiCloseHandle(hRecordProg); +cleanup_hViewST_close: + MsiViewClose(hViewST); +cleanup_hViewST: + MsiCloseHandle(hViewST); +cleanup_hDatabase: + MsiCloseHandle(hDatabase); +cleanup_exec_seq: + msica_arg_seq_free(&seqInstall); + msica_arg_seq_free(&seqInstallCommit); + msica_arg_seq_free(&seqInstallRollback); + msica_arg_seq_free(&seqUninstall); + msica_arg_seq_free(&seqUninstallCommit); + msica_arg_seq_free(&seqUninstallRollback); + if (bIsCoInitialized) + { + CoUninitialize(); + } + return uiResult; +} + + +/** + * Parses string encoded GUID. + * + * @param szArg Zero terminated string where the GUID string starts + * + * @param guid Pointer to GUID that receives parsed value + * + * @return TRUE on success; FALSE otherwise + */ +static BOOL +parse_guid( + _In_z_ LPCWSTR szArg, + _Out_ GUID *guid) +{ + if (swscanf_s(szArg, _L(PRIXGUID), PRIGUID_PARAM_REF(*guid)) != 11) + { + msg(M_NONFATAL | M_ERRNO, "%s: swscanf_s(\"%ls\") failed", __FUNCTION__, szArg); + return FALSE; + } + return TRUE; +} + + +/** + * Create empty file in user's temp directory. The existence of this file + * is checked in the end of installation by ScheduleReboot immediate custom action + * which schedules reboot. + * + * @param szTmpDir path to user's temp dirctory + * + */ +static void +CreateRebootFile(_In_z_ LPCWSTR szTmpDir) +{ + TCHAR path[MAX_PATH]; + swprintf_s(path, _countof(path), L"%s%s", szTmpDir, FILE_NEED_REBOOT); + + msg(M_WARN, "%s: Reboot required, create reboot indication file \"%" PRIsLPTSTR "\"", __FUNCTION__, path); + + HANDLE file = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file == INVALID_HANDLE_VALUE) + { + msg(M_NONFATAL | M_ERRNO, "%s: CreateFile(\"%" PRIsLPTSTR "\") failed", __FUNCTION__, path); + } + else + { + CloseHandle(file); + } +} + +UINT __stdcall +ProcessDeferredAction(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + + debug_popup(TEXT(__FUNCTION__)); + + UINT uiResult; + BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL)); + WCHAR tmpDir[MAX_PATH] = {0}; + + OPENVPNMSICA_SAVE_MSI_SESSION(hInstall); + + BOOL bIsCleanup = MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK); + + /* Get sequence arguments. Always Unicode as CommandLineToArgvW() is available as Unicode-only. */ + LPWSTR szSequence = NULL; + uiResult = msi_get_string(hInstall, L"CustomActionData", &szSequence); + if (uiResult != ERROR_SUCCESS) + { + goto cleanup_CoInitialize; + } + int nArgs; + LPWSTR *szArg = CommandLineToArgvW(szSequence, &nArgs); + if (szArg == NULL) + { + uiResult = GetLastError(); + msg(M_NONFATAL | M_ERRNO, "%s: CommandLineToArgvW(\"%ls\") failed", __FUNCTION__, szSequence); + goto cleanup_szSequence; + } + + /* Tell the installer to use explicit progress messages. */ + MSIHANDLE hRecordProg = MsiCreateRecord(3); + MsiRecordSetInteger(hRecordProg, 1, 1); + MsiRecordSetInteger(hRecordProg, 2, 1); + MsiRecordSetInteger(hRecordProg, 3, 0); + MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg); + + /* Prepare hRecordProg for progress messages. */ + MsiRecordSetInteger(hRecordProg, 1, 2); + MsiRecordSetInteger(hRecordProg, 3, 0); + + BOOL bRebootRequired = FALSE; + + for (int i = 1 /*CommandLineToArgvW injects msiexec.exe as szArg[0]*/; i < nArgs; ++i) + { + DWORD dwResult = ERROR_SUCCESS; + + if (wcsncmp(szArg[i], L"create=", 7) == 0) + { + /* Create an adapter with a given name and hardware ID. */ + LPWSTR szName = szArg[i] + 7; + LPWSTR szHardwareId = wcschr(szName, L'|'); + if (szHardwareId == NULL) + { + goto invalid_argument; + } + szHardwareId[0] = 0; + ++szHardwareId; + + { + /* Report the name of the adapter to installer. */ + MSIHANDLE hRecord = MsiCreateRecord(4); + MsiRecordSetString(hRecord, 1, TEXT("Creating adapter")); + MsiRecordSetString(hRecord, 2, szName); + MsiRecordSetString(hRecord, 3, szHardwareId); + int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord); + MsiCloseHandle(hRecord); + if (iResult == IDCANCEL) + { + uiResult = ERROR_INSTALL_USEREXIT; + goto cleanup; + } + } + + GUID guidAdapter; + dwResult = tap_create_adapter(NULL, NULL, szHardwareId, &bRebootRequired, &guidAdapter); + if (dwResult == ERROR_SUCCESS) + { + /* Set adapter name. May fail on some machines, but that is not critical - use silent + flag to mute messagebox and print error only to log */ + tap_set_adapter_name(&guidAdapter, szName, TRUE); + } + } + else if (wcsncmp(szArg[i], L"deleteN=", 8) == 0) + { + /* Delete the adapter by name. */ + LPCWSTR szName = szArg[i] + 8; + + { + /* Report the name of the adapter to installer. */ + MSIHANDLE hRecord = MsiCreateRecord(3); + MsiRecordSetString(hRecord, 1, TEXT("Deleting adapter")); + MsiRecordSetString(hRecord, 2, szName); + int iResult = MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecord); + MsiCloseHandle(hRecord); + if (iResult == IDCANCEL) + { + uiResult = ERROR_INSTALL_USEREXIT; + goto cleanup; + } + } + + /* Get existing adapters. */ + struct tap_adapter_node *pAdapterList = NULL; + dwResult = tap_list_adapters(NULL, NULL, &pAdapterList); + if (dwResult == ERROR_SUCCESS) + { + /* Does the adapter exist? */ + for (struct tap_adapter_node *pAdapter = pAdapterList; pAdapter != NULL; pAdapter = pAdapter->pNext) + { + if (_tcsicmp(szName, pAdapter->szName) == 0) + { + /* Adapter found. */ + dwResult = tap_delete_adapter(NULL, &pAdapter->guid, &bRebootRequired); + break; + } + } + + tap_free_adapter_list(pAdapterList); + } + } + else if (wcsncmp(szArg[i], L"delete=", 7) == 0) + { + /* Delete the adapter by GUID. */ + GUID guid; + if (!parse_guid(szArg[i] + 7, &guid)) + { + goto invalid_argument; + } + dwResult = tap_delete_adapter(NULL, &guid, &bRebootRequired); + } + else if (wcsncmp(szArg[i], L"enable=", 7) == 0) + { + /* Enable the adapter. */ + GUID guid; + if (!parse_guid(szArg[i] + 7, &guid)) + { + goto invalid_argument; + } + dwResult = tap_enable_adapter(NULL, &guid, TRUE, &bRebootRequired); + } + else if (wcsncmp(szArg[i], L"disable=", 8) == 0) + { + /* Disable the adapter. */ + GUID guid; + if (!parse_guid(szArg[i] + 8, &guid)) + { + goto invalid_argument; + } + dwResult = tap_enable_adapter(NULL, &guid, FALSE, &bRebootRequired); + } + else if (wcsncmp(szArg[i], L"tmpdir=", 7) == 0) + { + wcscpy_s(tmpDir, _countof(tmpDir), szArg[i] + 7); + } + else + { + goto invalid_argument; + } + + if (dwResult != ERROR_SUCCESS && !bIsCleanup /* Ignore errors in case of commit/rollback to do as much work as possible. */) + { + uiResult = ERROR_INSTALL_FAILURE; + goto cleanup; + } + + /* Report progress and check for user cancellation. */ + MsiRecordSetInteger(hRecordProg, 2, MSICA_ADAPTER_TICK_SIZE); + if (MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) + { + dwResult = ERROR_INSTALL_USEREXIT; + goto cleanup; + } + + continue; + +invalid_argument: + msg(M_NONFATAL, "%s: Ignoring invalid argument: %ls", __FUNCTION__, szArg[i]); + } + +cleanup: + if (bRebootRequired && wcslen(tmpDir) > 0) + { + CreateRebootFile(tmpDir); + } + MsiCloseHandle(hRecordProg); + LocalFree(szArg); +cleanup_szSequence: + free(szSequence); +cleanup_CoInitialize: + if (bIsCoInitialized) + { + CoUninitialize(); + } + return uiResult; +} + +UINT __stdcall +CheckAndScheduleReboot(_In_ MSIHANDLE hInstall) +{ +#ifdef _MSC_VER +#pragma comment(linker, DLLEXP_EXPORT) +#endif + + debug_popup(TEXT(__FUNCTION__)); + + UINT ret = ERROR_SUCCESS; + BOOL bIsCoInitialized = SUCCEEDED(CoInitialize(NULL)); + + OPENVPNMSICA_SAVE_MSI_SESSION(hInstall); + + /* get user-specific temp path, to where we create reboot indication file */ + TCHAR tempPath[MAX_PATH]; + GetTempPath(MAX_PATH, tempPath); + + /* check if reboot file exists */ + TCHAR path[MAX_PATH]; + _stprintf_s(path, _countof(path), L"%s%s", tempPath, FILE_NEED_REBOOT); + WIN32_FIND_DATA data = { 0 }; + HANDLE searchHandle = FindFirstFile(path, &data); + if (searchHandle != INVALID_HANDLE_VALUE) + { + msg(M_WARN, "%s: Reboot file exists, schedule reboot", __FUNCTION__); + + FindClose(searchHandle); + DeleteFile(path); + + MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE); + } + + if (bIsCoInitialized) + { + CoUninitialize(); + } + return ret; +} diff --git a/src/openvpnmsica/openvpnmsica.h b/src/openvpnmsica/openvpnmsica.h new file mode 100644 index 0000000..bfc40ea --- /dev/null +++ b/src/openvpnmsica/openvpnmsica.h @@ -0,0 +1,166 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * https://community.openvpn.net/openvpn/wiki/OpenVPNMSICA + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 MSICA_H +#define MSICA_H + +#include <windows.h> +#include <msi.h> +#include "../tapctl/basic.h" + + +/* + * Error codes (next unused 2552L) + */ +#define ERROR_MSICA 2550L +#define ERROR_MSICA_ERRNO 2551L + + +/** + * Thread local storage data + */ +struct openvpnmsica_thread_data +{ + MSIHANDLE hInstall; /** Handle to the installation session. */ +}; + + +/** + * MSI session handle thread local storage index + */ +extern DWORD openvpnmsica_thread_data_idx; + + +/** + * Set MSI session handle in thread local storage. + */ +#define OPENVPNMSICA_SAVE_MSI_SESSION(hInstall) \ +{ \ + struct openvpnmsica_thread_data *s = (struct openvpnmsica_thread_data *)TlsGetValue(openvpnmsica_thread_data_idx); \ + s->hInstall = (hInstall); \ +} + + +/* + * Exported DLL Functions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define DLLEXP_DECL __declspec(dllexport) +#else +#define DLLEXP_DECL +#define DLLEXP_EXPORT "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__ +#endif + + +/** + * Determines Windows information: + * + * - Sets `OPENVPNSERVICE` MSI property to PID of OpenVPN Service if running, or its EXE path if + * configured for auto-start. + * + * - Finds existing TAP-Windows6 adapters and set TAPWINDOWS6ADAPTERS and + * ACTIVETAPWINDOWS6ADAPTERS properties with semicolon delimited list of all installed adapter + * GUIDs and active adapter GUIDs respectively. + * + * - Finds existing Wintun adapters and set WINTUNADAPTERS and ACTIVEWINTUNADAPTERS properties + * with semicolon delimited list of all installed adapter GUIDs and active adapter GUIDs + * respectively. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +FindSystemInfo(_In_ MSIHANDLE hInstall); + + +/** + * Find OpenVPN GUI window and send it a WM_CLOSE message. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +CloseOpenVPNGUI(_In_ MSIHANDLE hInstall); + + +/** + * Launches OpenVPN GUI. It's path is obtained by expanding the `[#bin.openvpn_gui.exe]` + * therefore, its Id field in File table must be "bin.openvpn_gui.exe". + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +StartOpenVPNGUI(_In_ MSIHANDLE hInstall); + + +/** + * Evaluate the TUNTAPAdapter table of the MSI package database and prepare a list of TAP + * adapters to install/remove. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +EvaluateTUNTAPAdapters(_In_ MSIHANDLE hInstall); + + +/** + * Perform scheduled deferred action. + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +ProcessDeferredAction(_In_ MSIHANDLE hInstall); + + +/** + * Schedule reboot after installation if reboot + * indication file is found in user's temp directory + * + * @param hInstall Handle to the installation provided to the DLL custom action + * + * @return ERROR_SUCCESS on success; An error code otherwise + * See: https://msdn.microsoft.com/en-us/library/windows/desktop/aa368072.aspx + */ +DLLEXP_DECL UINT __stdcall +CheckAndScheduleReboot(_In_ MSIHANDLE hInstall); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef MSICA_H */ diff --git a/src/openvpnmsica/openvpnmsica.props b/src/openvpnmsica/openvpnmsica.props new file mode 100644 index 0000000..074635d --- /dev/null +++ b/src/openvpnmsica/openvpnmsica.props @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets" /> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <TargetName>lib$(ProjectName)</TargetName> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <AdditionalIncludeDirectories>..\compat;$(TAP_WINDOWS_HOME)/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>_WIN32_WINNT=_WIN32_WINNT_VISTA;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + </Link> + </ItemDefinitionGroup> + <ItemGroup /> +</Project>
\ No newline at end of file diff --git a/src/openvpnmsica/openvpnmsica.vcxproj b/src/openvpnmsica/openvpnmsica.vcxproj new file mode 100644 index 0000000..c39b124 --- /dev/null +++ b/src/openvpnmsica/openvpnmsica.vcxproj @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <VCProjectVersion>15.0</VCProjectVersion> + <ProjectGuid>{D41AA9D6-B818-476E-992E-0E16EB86BEE2}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>openvpnmsica</RootNamespace> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v142</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Debug.props" /> + <Import Project="openvpnmsica-Debug.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Debug.props" /> + <Import Project="openvpnmsica-Debug.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Debug.props" /> + <Import Project="openvpnmsica-Debug.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Release.props" /> + <Import Project="openvpnmsica-Release.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Release.props" /> + <Import Project="openvpnmsica-Release.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\compat\Release.props" /> + <Import Project="openvpnmsica-Release.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <VcpkgEnabled>true</VcpkgEnabled> + </PropertyGroup> + <ItemGroup> + <ClCompile Include="..\tapctl\error.c" /> + <ClCompile Include="..\tapctl\tap.c" /> + <ClCompile Include="dllmain.c" /> + <ClCompile Include="msiex.c" /> + <ClCompile Include="msica_arg.c" /> + <ClCompile Include="openvpnmsica.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\tapctl\basic.h" /> + <ClInclude Include="..\tapctl\error.h" /> + <ClInclude Include="..\tapctl\tap.h" /> + <ClInclude Include="msiex.h" /> + <ClInclude Include="msica_arg.h" /> + <ClInclude Include="openvpnmsica.h" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="openvpnmsica_resources.rc" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\build\msvc\msvc-generate\msvc-generate.vcxproj"> + <Project>{8598c2c8-34c4-47a1-99b0-7c295a890615}</Project> + <ReferenceOutputAssembly>false</ReferenceOutputAssembly> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/src/openvpnmsica/openvpnmsica.vcxproj.filters b/src/openvpnmsica/openvpnmsica.vcxproj.filters new file mode 100644 index 0000000..cb050f9 --- /dev/null +++ b/src/openvpnmsica/openvpnmsica.vcxproj.filters @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="dllmain.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\tapctl\error.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="msiex.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="openvpnmsica.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="msica_arg.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\tapctl\tap.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="openvpnmsica.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="msiex.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="msica_arg.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\tapctl\tap.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\tapctl\error.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\tapctl\basic.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="openvpnmsica_resources.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/openvpnmsica/openvpnmsica_resources.rc b/src/openvpnmsica/openvpnmsica_resources.rc new file mode 100644 index 0000000..323f0e7 --- /dev/null +++ b/src/openvpnmsica/openvpnmsica_resources.rc @@ -0,0 +1,62 @@ +/* + * openvpnmsica -- Custom Action DLL to provide OpenVPN-specific support to MSI packages + * + * Copyright (C) 2018-2021 Simon Rozman <simon@rozman.si> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#else +#include <config-msvc-version.h> +#endif +#include <winresrc.h> + +#pragma code_page(65001) /* UTF8 */ + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +VS_VERSION_INFO VERSIONINFO + FILEVERSION OPENVPN_VERSION_RESOURCE + PRODUCTVERSION OPENVPN_VERSION_RESOURCE + FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PATCHED | VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS_NT_WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "The OpenVPN Project" + VALUE "FileDescription", "Custom Action DLL to provide OpenVPN-specific support to MSI packages" + VALUE "FileVersion", PACKAGE_VERSION ".0" + VALUE "InternalName", "OpenVPN" + VALUE "LegalCopyright", "Copyright © The OpenVPN Project" + VALUE "OriginalFilename", "libopenvpnmsica.dll" + VALUE "ProductName", "OpenVPN" + VALUE "ProductVersion", PACKAGE_VERSION ".0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END |