Initial commit

PiperOrigin-RevId: 370075328
Change-Id: I13f3c994cb8607e02c36ca3c90a7f7b811edbe8b
diff --git a/INSTALL.openvpn b/INSTALL.openvpn
new file mode 100644
index 0000000..a5936b3
--- /dev/null
+++ b/INSTALL.openvpn
@@ -0,0 +1,368 @@
+Installation instructions for OpenVPN, a Secure Tunneling Daemon
+
+Copyright (C) 2002-2019 OpenVPN Inc. 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.
+
+*************************************************************************
+
+QUICK START:
+
+  Unix:
+    ./configure && make && make install
+
+*************************************************************************
+
+To download OpenVPN source code of releases, go to:
+
+    https://openvpn.net/community-downloads/
+
+OpenVPN releases are also available as Debian/RPM packages:
+
+    https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos
+
+OpenVPN development versions can be found here:
+
+   https://github.com/OpenVPN/openvpn
+   https://gitlab.com/OpenVPN/openvpn
+   https://sourceforge.net/p/openvpn/openvpn/ci/master/tree/
+
+They should all be in sync at any time.
+
+To download easy-rsa go to:
+
+    https://github.com/OpenVPN/easy-rsa
+
+To download tap-windows (NDIS 6) driver source code go to:
+
+    https://github.com/OpenVPN/tap-windows6
+
+To get the cross-compilation environment go to:
+
+    https://github.com/OpenVPN/openvpn-build
+
+For step-by-step instructions with real-world examples see:
+
+    https://community.openvpn.net/openvpn/wiki/GettingStartedwithOVPN
+    https://community.openvpn.net/openvpn/wiki
+    https://openvpn.net/community-resources/
+
+Also see the man page for more information.
+
+*************************************************************************
+
+SUPPORTED PLATFORMS:
+  (1) Linux (kernel 2.6+)
+  (2) Solaris
+  (3) OpenBSD 5.1+
+  (4) Mac OS X Darwin 10.5+
+  (5) FreeBSD 7.4+
+  (6) NetBSD 5.0+
+  (7) Windows Vista or later for OpenVPN 2.4
+  (8) Windows XP or later for OpenVPN 2.3
+
+SUPPORTED PROCESSOR ARCHITECTURES:
+   In general, OpenVPN is word size and endian independent, so
+   most processors should be supported.  Architectures known to
+   work include Intel x86, Alpha, Sparc, Amd64, and ARM.
+
+REQUIRES:
+  (1) TUN and/or TAP driver to allow user-space programs to control
+      a virtual point-to-point IP or Ethernet device.  See
+      TUN/TAP Driver Configuration section below for more info.
+
+OPTIONAL (but recommended):
+  (1) OpenSSL library, necessary for encryption, version 0.9.8 or higher
+      required, available from http://www.openssl.org/
+  (2) mbed TLS library, an alternative for encryption, version 2.0 or higher
+      required, available from https://tls.mbed.org/
+  (3) LZO real-time compression library, required for link compression,
+      available from http://www.oberhumer.com/opensource/lzo/
+      OpenBSD users can use ports or packages to install lzo, but remember
+      to add CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib"
+      directives to "configure", since gcc will not find them otherwise.
+
+OPTIONAL (for developers only):
+  (1) Autoconf 2.59 or higher + Automake 1.9 or higher
+      -- available from http://www.gnu.org/software/software.html
+  (2) Dmalloc library
+      -- available from http://dmalloc.com/
+  (3) If using t_client.sh test framework, fping/fping6 is needed
+      -- Available from http://www.fping.org/
+      Note: t_client.sh needs an external configured OpenVPN server.
+      See t_client.rc-sample for more info.
+
+*************************************************************************
+
+CHECK OUT SOURCE FROM SOURCE REPOSITORY:
+
+  Clone the repository:
+
+    git clone https://github.com/OpenVPN/openvpn
+    git clone https://gitlab.com/OpenVPN/openvpn
+    git clone git://openvpn.git.sourceforge.net/gitroot/openvpn/openvpn
+
+  Check out stable version:
+
+    git checkout release/2.4
+
+  Check out master (unstable) branch:
+
+    git checkout master
+
+
+*************************************************************************
+
+BUILD COMMANDS FROM TARBALL:
+
+	./configure
+	make
+	make install
+
+*************************************************************************
+
+BUILD COMMANDS FROM SOURCE REPOSITORY CHECKOUT:
+
+	autoreconf -i -v -f
+	./configure
+	make
+	make install
+
+*************************************************************************
+
+BUILD A TARBALL FROM SOURCE REPOSITORY CHECKOUT:
+
+	autoreconf -i -v -f
+	./configure
+	make distcheck
+
+*************************************************************************
+
+TESTS (after BUILD):
+
+make check (Run all tests below)
+
+Test Crypto:
+
+./openvpn --genkey --secret key
+./openvpn --test-crypto --secret key
+
+Test SSL/TLS negotiations (runs for 2 minutes):
+
+./openvpn --config sample/sample-config-files/loopback-client (In one window)
+./openvpn --config sample/sample-config-files/loopback-server (Simultaneously in another window)
+
+For more thorough client-server tests you can configure your own, private test
+environment. See tests/t_client.rc-sample for details.
+
+*************************************************************************
+
+OPTIONS for ./configure:
+
+  --disable-lzo           disable LZO compression support [default=yes]
+  --disable-lz4           Disable LZ4 compression support
+  --enable-comp-stub      Don't compile compression support but still allow limited interoperability with compression-enabled peers
+  --disable-crypto        disable crypto support [default=yes]
+  --disable-ofb-cfb       disable support for OFB and CFB cipher modes
+                          [default=yes]
+  --enable-x509-alt-username
+                          enable the --x509-username-field feature
+                          [default=no]
+  --disable-server        disable server support only (but retain client
+                          support) [default=yes]
+  --disable-plugins       disable plug-in support [default=yes]
+  --disable-management    disable management server support [default=yes]
+  --enable-pkcs11         enable pkcs11 support [default=no]
+  --disable-fragment      disable internal fragmentation support (--fragment)
+                          [default=yes]
+  --disable-multihome     disable multi-homed UDP server support (--multihome)
+                          [default=yes]
+  --disable-port-share    disable TCP server port-share support (--port-share)
+                          [default=yes]
+  --disable-debug         disable debugging support (disable gremlin and verb
+                          7+ messages) [default=yes]
+  --enable-small          enable smaller executable size (disable OCC, usage
+                          message, and verb 4 parm list) [default=no]
+  --enable-iproute2       enable support for iproute2 [default=no]
+  --disable-def-auth      disable deferred authentication [default=yes]
+  --disable-pf            disable internal packet filter [default=yes]
+  --disable-plugin-auth-pam
+                          disable auth-pam plugin [default=platform specific]
+  --disable-plugin-down-root
+                          disable down-root plugin [default=platform specific]
+  --enable-pam-dlopen     dlopen libpam [default=no]
+  --enable-strict         enable strict compiler warnings (debugging option)
+                          [default=no]
+  --enable-pedantic       enable pedantic compiler warnings, will not generate
+                          a working executable (debugging option) [default=no]
+  --enable-werror         promote compiler warnings to errors, will cause
+                          builds to fail if the compiler issues warnings
+                          (debugging option) [default=no]
+  --enable-strict-options enable strict options check between peers (debugging
+                          option) [default=no]
+  --enable-selinux        enable SELinux support [default=no]
+  --enable-systemd        enable systemd support [default=no]
+  --enable-async-push     enable async-push support for plugins providing
+                          deferred authentication [default=no]
+
+ENVIRONMENT for ./configure:
+
+  PLUGINDIR   Path of plug-in directory [default=LIBDIR/openvpn/plugins]
+  IFCONFIG    full path to ipconfig utility
+  ROUTE       full path to route utility
+  IPROUTE     full path to ip utility
+  NETSTAT     path to netstat utility
+  MAN2HTML    path to man2html utility
+  GIT         path to git utility
+  SYSTEMD_ASK_PASSWORD
+              path to systemd-ask-password utility
+  SYSTEMD_UNIT_DIR
+              Path of systemd unit directory [default=LIBDIR/systemd/system]
+  TMPFILES_DIR
+              Path of tmpfiles directory [default=LIBDIR/tmpfiles.d]
+
+ENVIRONMENT variables adjusting parameters related to dependencies
+
+  TAP_CFLAGS  C compiler flags for tap
+  LIBPAM_CFLAGS
+              C compiler flags for libpam
+  LIBPAM_LIBS linker flags for libpam
+  PKCS11_HELPER_CFLAGS
+              C compiler flags for PKCS11_HELPER, overriding pkg-config
+  PKCS11_HELPER_LIBS
+              linker flags for PKCS11_HELPER, overriding pkg-config
+  OPENSSL_CFLAGS
+              C compiler flags for OpenSSL
+  OPENSSL_LIBS
+              linker flags for OpenSSL
+  MBEDTLS_CFLAGS
+              C compiler flags for mbedtls
+  MBEDTLS_LIBS
+              linker flags for mbedtls
+  LZO_CFLAGS  C compiler flags for lzo
+  LZO_LIBS    linker flags for lzo
+  LZ4_CFLAGS  C compiler flags for lz4
+  LZ4_LIBS    linker flags for lz4
+  libsystemd_CFLAGS
+              C compiler flags for libsystemd, overriding pkg-config
+  libsystemd_LIBS
+              linker flags for libsystemd, overriding pkg-config
+  P11KIT_CFLAGS
+              C compiler flags for P11KIT, overriding pkg-config
+  P11KIT_LIBS linker flags for P11KIT, overriding pkg-config
+
+*************************************************************************
+
+Linux distribution packaging:
+
+Each Linux distribution has their own way of doing packaging and their
+own set of guidelines of how proper packaging should be done.  It
+is therefore recommended to reach out to the Linux distributions you
+want to have OpenVPN packaged for directly.  The OpenVPN project wants
+to focus more on the OpenVPN development and less on the packaging
+and how packaging is done in all various distributions.
+
+For more details:
+
+* Arch Linux
+  https://www.archlinux.org/packages/?name=openvpn
+
+* Debian
+  https://packages.debian.org/search?keywords=openvpn&searchon=names
+  https://tracker.debian.org/pkg/openvpn
+
+* Fedora / Fedora EPEL (Red Hat Enterprise Linux/CentOS/Scientific Linux)
+  https://apps.fedoraproject.org/packages/openvpn/overview/
+  https://src.fedoraproject.org/rpms/openvpn
+
+* Gentoo
+  https://packages.gentoo.org/packages/net-vpn/openvpn
+  https://gitweb.gentoo.org/repo/gentoo.git/tree/net-vpn/openvpn
+
+* openSUSE
+  https://build.opensuse.org/package/show/network:vpn/openvpn
+
+* Ubuntu
+  https://packages.ubuntu.com/search?keywords=openvpn
+
+In addition, the OpenVPN community provides a best-effort APT repository
+for Debian and Ubuntu:
+https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos
+
+*************************************************************************
+
+TUN/TAP Driver Configuration:
+
+* Linux 2.6 or higher (with integrated TUN/TAP driver):
+
+  (1) load driver:              modprobe tun
+  (2) enable routing:           echo 1 > /proc/sys/net/ipv4/ip_forward
+
+  Note that (1) needs to be done once per reboot.  If you install from RPM (see
+  above) and use the openvpn.init script, these steps are taken care of for you.
+
+* FreeBSD:
+
+  FreeBSD ships with the TUN/TAP driver, and the device nodes for tap0,
+  tap1, tap2, tap3, tun0, tun1, tun2 and tun3 are made by default.
+  However, only the TUN driver is linked into the GENERIC kernel.
+
+  To load the TAP driver, enter: 
+
+	kldload if_tap
+
+  See man rc(8) to find out how you can do this at boot time.
+
+  The easiest way is to install OpenVPN from the FreeBSD ports system,
+  the port includes a sample script to automatically load the TAP driver
+  at boot-up time.
+
+* OpenBSD:
+
+  OpenBSD has dynamically created tun* devices so you only need
+  to create an empty /etc/hostname.tun0 (tun1, tun2 and so on) for each tun
+  you plan to use to create the device(s) at boot.
+
+* Solaris:
+
+  You need a TUN/TAP kernel driver for OpenVPN to work:
+
+    http://www.whiteboard.ne.jp/~admin2/tuntap/
+
+* Windows
+
+  OpenVPN on Windows needs a TUN/TAP kernel driver to work. OpenVPN installers
+  include this driver, so installing it separately is not usually required.
+  Windows XP/2003 must use the NDIS 5 (tap-windows) driver, whereas on more
+  recent Windows versions it is recommended to use the NDIS 6 driver
+  (tap-windows6) instead.
+
+*************************************************************************
+
+CAVEATS & BUGS:
+
+* I have noticed cases where TCP sessions tunneled over the Linux
+  TAP driver (kernel 2.4.21 and 2.4.22) stall when lower --mssfix
+  values are used.  The TCP sessions appear to unstall and resume
+  normally when the remote VPN endpoint is pinged.
+
+* If run through a firewall using OpenBSDs packet filter PF and the
+  filter rules include a "scrub" directive, you may get problems talking
+  to Linux hosts over the tunnel, since the scrubbing will kill packets
+  sent from Linux hosts if they are fragmented. This is usually seen as
+  tunnels where small packets and pings get through but large packets
+  and "regular traffic" don't. To circumvent this, add "no-df" to
+  the scrub directive so that the packet filter will let fragments with
+  the "dont fragment"-flag set through anyway.
+
+* Mixing OFB or CFB cipher modes with static key mode is not recommended,
+  and is flagged as an error on OpenVPN versions 1.2.1 and greater.
+  If you use the --cipher option to explicitly select an OFB or CFB
+  cipher AND you are using static key mode, it is possible that there
+  could be an IV collision if the OpenVPN daemons on both sides
+  of the connection are started at exactly the same time, since
+  OpenVPN uses a timestamp combined with a sequence number as the cipher
+  IV for OFB and CFB modes.  This is not an issue if you are
+  using CBC cipher mode (the default), or if you are using OFB or CFB
+  cipher mode with SSL/TLS authentication.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..769ac15
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,1027 @@
+BEGIN COPYING
+
+OpenVPN (TM) -- An Open Source VPN daemon
+
+Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+
+This distribution contains multiple components, some
+of which fall under different licenses.  By using OpenVPN
+or any of the bundled components enumerated below, you
+agree to be bound by the conditions of the license for
+each respective component.
+
+OpenVPN trademark
+-----------------
+
+  "OpenVPN" is a trademark of OpenVPN Inc
+
+
+OpenVPN license:
+----------------
+
+  OpenVPN is distributed under the GPL license version 2 (see Below).
+
+  Special exception for linking OpenVPN with OpenSSL:
+
+  In addition, as a special exception, OpenVPN Inc gives
+  permission to link the code of this program with the OpenSSL
+  library (or with modified versions of OpenSSL that use the same
+  license as OpenSSL), and distribute linked combinations including
+  the two.  You must obey the GNU General Public License in all
+  respects for all of the code used other than OpenSSL.  If you modify
+  this file, you may extend this exception to your version of the
+  file, but you are not obligated to do so.  If you do not wish to
+  do so, delete this exception statement from your version.
+
+LZO license:
+------------
+
+  LZO is Copyright (C) Markus F.X.J. Oberhumer,
+  and is licensed under the GPL.
+
+  Special exception for linking OpenVPN with both OpenSSL and LZO:
+
+  Hereby I grant a special exception to the OpenVPN project 
+  (http://openvpn.net/) to link the LZO library with 
+  the OpenSSL library (http://www.openssl.org).
+ 
+  Markus F.X.J. Oberhumer
+
+TAP-Win32/TAP-Win64 Driver license:
+-----------------------------------
+
+  This device driver was inspired by the CIPE-Win32 driver by
+  Damion K. Wilson.
+
+  The source and object code of the TAP-Win32/TAP-Win64 driver
+  is Copyright (C) 2002-2018 OpenVPN Inc, and is released under
+  the GPL version 2.
+
+Windows DDK Samples:
+--------------------
+
+  The Windows binary distribution includes devcon.exe, a
+  Microsoft DDK sample which is redistributed under the terms
+  of the DDK EULA.
+
+NSIS License:
+-------------
+
+  Copyright (C) 2002-2003 Joost Verburg
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute
+  it freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; 
+     you must not claim that you wrote the original software.
+     If you use this software in a product, an acknowledgment in the
+     product documentation would be appreciated but is not required.
+  2. Altered versions must be plainly marked as such,
+     and must not be misrepresented as being the original software.
+  3. This notice may not be removed or altered from any distribution.
+
+OpenSSL License:
+----------------
+
+  The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+  the OpenSSL License and the original SSLeay license apply to the toolkit.
+  See below for the actual license texts. Actually both licenses are BSD-style
+  Open Source licenses. In case of any license issues related to OpenSSL
+  please contact openssl-core@openssl.org.
+
+/* ====================================================================
+ * Copyright (c) 1998-2003 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+GNU Public License (GPL)
+------------------------
+
+  OpenVPN, LZO, and the TAP-Win32 distributions are
+  licensed under the GPL version 2 (see COPYRIGHT.GPL).
+
+  In the Windows binary distribution of OpenVPN, the
+  GPL is reproduced below.
+
+
+
+END COPYING
+
+------------------
+
+BEGIN COPYRIGHT.GPL
+
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
+
+END COPYRIGHT.GPL
+
+------------------
+
+BSD 3-Clause License:
+-----------------
+
+/*
+ * Redistribution and use in source and binary forms, with or without modifi-
+ * cation, are permitted provided that the following conditions are met:
+ *
+ *   o  Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *
+ *   o  Redistributions in binary form must reproduce the above copyright no-
+ *      tice, this list of conditions and the following disclaimer in the do-
+ *      cumentation and/or other materials provided with the distribution.
+ *
+ *   o  The names of the contributors may not be used to endorse or promote
+ *      products derived from this software without specific prior written
+ *      permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI-
+ * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN-
+ * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV-
+ * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI-
+ * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+------------------
+src/compat/compat-lz4.c
+
+/*
+   LZ4 - Fast LZ compression algorithm
+   Copyright (C) 2011-present, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+    - LZ4 homepage : http://www.lz4.org
+    - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+------------------
+m4/pkg.m4
+
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+       GNU General Public License v2.0 w/Autoconf exception
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
+
+Autoconf Exception
+
+As a special exception, the Free Software Foundation gives unlimited permission
+to copy, distribute and modify the configure scripts that are the output of
+Autoconf. You need not follow the terms of the GNU General Public License
+when using or distributing such scripts, even though portions of the text
+of Autoconf appear in them. The GNU General Public License (GPL) does govern
+all other use of the material that constitutes the Autoconf program.
+
+Certain portions of the Autoconf source text are designed to be copied (in
+certain cases, depending on the input) into the output of Autoconf. We call
+these the "data" portions. The rest of the Autoconf source text consists of
+comments plus executable code that decides which of the data portions to output
+in any given case. We call these comments and executable code the "non-data"
+portions. Autoconf never copies any of the non-data portions into its output.
+
+This special exception to the GPL applies to versions of Autoconf released
+by the Free Software Foundation. When you make and distribute a modified version
+of Autoconf, you may extend this special exception to the GPL to apply to
+your modified version as well, *unless* your modified version has the potential
+to copy into its output some of the text that was the non-data portion of
+the version that you started with. (In other words, unless your change moves
+or copies text from the non-data portions to the data portions.) If your modification
+has such potential, you must delete any notice of this special exception to
+the GPL from your modified version.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..753c526
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,86 @@
+#
+#  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+#  Copyright (C) 2010      David Sommerseth <dazo@users.sourceforge.net>
+#  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+#  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.
+#
+
+# This option prevents autoreconf from overriding our COPYING and
+# INSTALL targets:
+AUTOMAKE_OPTIONS = foreign 1.9
+ACLOCAL_AMFLAGS = -I m4
+
+MAINTAINERCLEANFILES = \
+	config.log config.status \
+	$(srcdir)/Makefile.in \
+	$(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \
+	$(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \
+	$(srcdir)/m4/libtool.m4 $(srcdir)/m4/lt~obsolete.m4 \
+	$(srcdir)/m4/ltoptions.m4 $(srcdir)/m4/ltsugar.m4 \
+	$(srcdir)/m4/ltversion.m4 \
+	$(srcdir)/depcomp $(srcdir)/aclocal.m4 \
+	$(srcdir)/config.guess $(srcdir)/config.sub
+
+CLEANFILES = \
+	config-version.h tests/t_client.sh
+
+EXTRA_DIST = \
+	contrib \
+	debug
+
+.PHONY: config-version.h
+
+if GIT_CHECKOUT
+BUILT_SOURCES = \
+	config-version.h
+endif
+
+SUBDIRS = include src
+
+dist_doc_DATA = \
+	README.IPv6
+	LICENSE
+
+dist_noinst_DATA = \
+	README.IPv6
+
+dist_noinst_HEADERS = \
+	config-msvc.h \
+	config-msvc-version.h.in
+
+if WIN32
+rootdir=$(prefix)
+root_DATA = version.sh
+endif
+
+config-version.h:
+	@CONFIGURE_GIT_CHFILES="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-files --name-status -r --ignore-submodules --quiet -- || echo \"+\"`"; \
+	CONFIGURE_GIT_UNCOMMITTED="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) diff-index --cached  --quiet --ignore-submodules HEAD || echo \"*\"`"; \
+	CONFIGURE_GIT_REVISION="`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) rev-parse --symbolic-full-name HEAD | cut -d/ -f3-`/`GIT_DIR=\"$(top_srcdir)/.git\" $(GIT) rev-parse --short=16 HEAD`"; \
+	echo "#define CONFIGURE_GIT_REVISION \"$${CONFIGURE_GIT_REVISION}\"" > config-version.h.tmp; \
+	echo "#define CONFIGURE_GIT_FLAGS \"$${CONFIGURE_GIT_CHFILES}$${CONFIGURE_GIT_UNCOMMITTED}\"" >> config-version.h.tmp
+
+	@if ! [ -f config-version.h ] || ! cmp -s config-version.h.tmp config-version.h; then \
+		echo "replacing config-version.h"; \
+		mv config-version.h.tmp config-version.h; \
+	else \
+		rm -f config-version.h.tmp; \
+	fi
diff --git a/README.IPv6 b/README.IPv6
new file mode 100644
index 0000000..18068fe
--- /dev/null
+++ b/README.IPv6
@@ -0,0 +1,56 @@
+Since 2.3.0, OpenVPN officially supports IPv6, and all widely used
+patches floating around for older versions have been integrated.
+
+IPv6 payload support
+--------------------
+
+This is for "IPv6 inside OpenVPN", with server-pushed IPv6 configuration
+on the client, and support for IPv6 configuration on the tun/tap interface
+from within the openvpn config.
+
+The code in 2.3.0 supersedes the IPv6 payload patches from Gert Doering,
+formerly located at http://www.greenie.net/ipv6/openvpn.html
+
+
+The following options have been added to handle IPv6 configuration,
+analogous to their IPv4 counterparts (--server <-> --server-ipv6, etc.)
+
+     - server-ipv6
+     - ifconfig-ipv6
+     - ifconfig-ipv6-pool
+     - ifconfig-ipv6-push
+     - route-ipv6
+     - iroute-ipv6
+
+see "man openvpn" for details how they are used.
+
+
+
+IPv6 transport support
+----------------------
+
+This is to enable OpenVPN peers or client/servers to talk to each other
+over an IPv6 network ("OpenVPN over IPv6").
+
+The code in 2.3.0 supersedes the IPv6 transport patches from JuanJo Ciarlante,
+formerly located at http://github.com/jjo/openvpn-ipv6
+
+OpenVPN 2.4.0 includes a big overhaul of the IPv6 transport patches
+originally implemented for the Android client (ics-openvpn)
+
+IPv4/IPv6 transport is automatically is selected when resolving addresses.
+Use a 6 or 4 suffix to force IPv6/IPv4:
+
+  --proto udp6
+  --proto tcp4
+  --proto tcp6-client
+  --proto tcp4-server
+  --proto tcp6 --client / --proto tcp6 --server
+
+On systems that allow IPv4 connections on IPv6 sockets
+(all systems supporting IPV6_V6ONLY setsockopt), an OpenVPN server can
+handle IPv4 connections on the IPv6 socket as well, making it a true
+dual-stacked server. Use bind ipv6only to disable this behaviour.
+
+On other systems, as of 2.3.0, you need to run separate server instances
+for IPv4 and IPv6.
diff --git a/README.ec b/README.ec
new file mode 100644
index 0000000..3293801
--- /dev/null
+++ b/README.ec
@@ -0,0 +1,35 @@
+Since 2.4.0, OpenVPN has official support for elliptic curve crypto. Elliptic
+curves are an alternative to RSA for asymmetric encryption.
+
+Elliptic curve crypto ('ECC') can be used for the ('TLS') control channel only
+in OpenVPN; the data channel (encrypting the actual network traffic) uses
+symmetric encryption. ECC can be used in TLS for authentication (ECDSA) and key
+exchange (ECDH).
+
+Key exchange (ECDH)
+-------------------
+OpenVPN 2.4.0 and newer automatically initialize ECDH parameters. When ECDSA is
+used for authentication, the curve used for the server certificate will be used
+for ECDH too. When autodetection fails (e.g. when using RSA certificates)
+OpenVPN lets the crypto library decide if possible, or falls back to the
+secp384r1 curve.
+
+An administrator can force an OpenVPN/OpenSSL server to use a specific curve
+using the --ecdh-curve <curvename> option with one of the curves listed as
+available by the --show-curves option. Clients will use the same curve as
+selected by the server.
+
+Note that not all curves listed by --show-curves are available for use with TLS;
+in that case connecting will fail with a 'no shared cipher' TLS error.
+
+Authentication (ECDSA)
+----------------------
+Since OpenVPN 2.4.0, using ECDSA certificates works 'out of the box'. Which
+specific curves and cipher suites are available depends on your version and
+configuration of the crypto library. The crypto library will automatically
+select a cipher suite for the TLS control channel.
+
+Support for generating an ECDSA certificate chain is available in EasyRSA (in
+spite of it's name) since EasyRSA 3.0. The parameters you're looking for are
+'--use-algo=ec' and '--curve=<curve_name>'. See the EasyRSA documentation for
+more details on generating ECDSA certificates.
diff --git a/README.openvpn b/README.openvpn
new file mode 100644
index 0000000..b75a568
--- /dev/null
+++ b/README.openvpn
@@ -0,0 +1,74 @@
+OpenVPN -- A Secure tunneling daemon
+
+Copyright (C) 2002-2018 OpenVPN Inc. 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.
+
+*************************************************************************
+
+To get the latest release of OpenVPN, go to:
+
+	https://openvpn.net/index.php/download/community-downloads.html
+
+To Build and Install,
+
+	tar -zxf openvpn-<version>.tar.gz
+	cd openvpn-<version>
+	./configure
+	make
+	make install
+
+or see the file INSTALL for more info.
+
+*************************************************************************
+
+For detailed information on OpenVPN, including examples, see the man page
+  http://openvpn.net/man.html
+
+For a sample VPN configuration, see
+  http://openvpn.net/howto.html
+
+To report an issue, see
+  https://community.openvpn.net/openvpn/report
+
+For a description of OpenVPN's underlying protocol,
+  see the file ssl.h included in the source distribution.
+
+*************************************************************************
+
+Other Files & Directories:
+
+* configure.ac -- script to rebuild our configure
+  script and makefile.
+
+* sample/sample-scripts/verify-cn
+
+  A sample perl script which can be used with OpenVPN's
+  --tls-verify option to provide a customized authentication
+  test on embedded X509 certificate fields.
+
+* sample/sample-keys/
+
+  Sample RSA keys and certificates.  DON'T USE THESE FILES
+  FOR ANYTHING OTHER THAN TESTING BECAUSE THEY ARE TOTALLY INSECURE.
+
+* sample/sample-config-files/
+
+  A collection of OpenVPN config files and scripts from
+  the HOWTO at http://openvpn.net/howto.html
+
+*************************************************************************
+
+Note that easy-rsa and tap-windows are now maintained in their own subprojects.
+Their source code is available here:
+
+  https://github.com/OpenVPN/easy-rsa
+  https://github.com/OpenVPN/tap-windows
+
+The old cross-compilation environment (domake-win) and the Python-based
+buildsystem have been replaced with openvpn-build:
+
+  https://github.com/OpenVPN/openvpn-build
+
+See the INSTALL file for usage information.
diff --git a/compat.m4 b/compat.m4
new file mode 100644
index 0000000..e54a720
--- /dev/null
+++ b/compat.m4
@@ -0,0 +1,74 @@
+dnl  OpenVPN -- An application to securely tunnel IP networks
+dnl             over a single UDP port, with support for SSL/TLS-based
+dnl             session authentication and key exchange,
+dnl             packet encryption, packet authentication, and
+dnl             packet compression.
+dnl
+dnl  Copyright (C) 2008-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+dnl
+dnl  This program is free software; you can redistribute it and/or modify
+dnl  it under the terms of the GNU General Public License as published by
+dnl  the Free Software Foundation; either version 2 of the License, or
+dnl  (at your option) any later version.
+dnl
+dnl  This program is distributed in the hope that it will be useful,
+dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl  GNU General Public License for more details.
+dnl
+dnl  You should have received a copy of the GNU General Public License along
+dnl  with this program; if not, write to the Free Software Foundation, Inc.,
+dnl  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+dnl Compatibility layer for <autoconf-2.60 <automake-1.10
+dnl REMOVE THIS IN FUTURE!
+
+ifdef(
+	[AS_VAR_IF],
+	,
+	[
+		AC_DEFUN([AS_VAR_IF], [dnl
+			if test "$$1" = "$2"; then
+				m4_ifval([$3], [$3], [:])
+			else
+				m4_ifval([$4], [$4], [:])
+			fi
+		])
+	]
+)
+ifdef(
+	[AC_USE_SYSTEM_EXTENSIONS],
+	,
+	[AC_DEFUN([AC_USE_SYSTEM_EXTENSIONS], [GNU_SOURCE])]
+)
+ifdef(
+	[AC_PROG_SED],
+	,
+	[AC_DEFUN([AC_PROG_SED], [AC_CHECK_PROGS([SED], [sed])])]
+)
+ifdef(
+	[AC_TYPE_INT8_T],
+	,
+	[
+		AC_CHECK_HEADERS([inttypes.h stdint.h])
+		test -z "${ac_cv_header_inttypes_h}${ac_cv_header_stdint_h}" && \
+			AC_MSG_ERROR([Required inttypes.h stdint.h not found])
+		
+		AC_DEFUN([AC_TYPE_INT8_T], [])
+		AC_DEFUN([AC_TYPE_INT16_T], [])
+		AC_DEFUN([AC_TYPE_INT32_T], [])
+		AC_DEFUN([AC_TYPE_INT64_T], [])
+		AC_DEFUN([AC_TYPE_UINT8_T], [])
+		AC_DEFUN([AC_TYPE_UINT16_T], [])
+		AC_DEFUN([AC_TYPE_UINT32_T], [])
+		AC_DEFUN([AC_TYPE_UINT64_T], [])
+	]
+)
+if test -z "${docdir}"; then
+	docdir="\$(datadir)/doc/\$(PACKAGE_NAME)"
+	AC_SUBST([docdir])
+fi
+if test -z "${htmldir}"; then
+	htmldir="\$(docdir)"
+	AC_SUBST([htmldir])
+fi
diff --git a/config-msvc-version.h.in b/config-msvc-version.h.in
new file mode 100644
index 0000000..7977cb8
--- /dev/null
+++ b/config-msvc-version.h.in
@@ -0,0 +1,14 @@
+#define PACKAGE_NAME "@PRODUCT_NAME@"
+#define PACKAGE_STRING "@PRODUCT_NAME@ @PRODUCT_VERSION_MAJOR@.@PRODUCT_VERSION_MINOR@@PRODUCT_VERSION_PATCH@"
+#define PACKAGE_TARNAME "@PRODUCT_TARNAME@"
+#define PACKAGE "@PRODUCT_TARNAME@"
+#define PRODUCT_VERSION_MAJOR "@PRODUCT_VERSION_MAJOR@"
+#define PRODUCT_VERSION_MINOR "@PRODUCT_VERSION_MINOR@"
+#define PRODUCT_VERSION_PATCH "@PRODUCT_VERSION_PATCH@"
+#define PACKAGE_VERSION "@PRODUCT_VERSION_MAJOR@.@PRODUCT_VERSION_MINOR@.@PRODUCT_VERSION_PATCH@"
+#define PRODUCT_VERSION "@PRODUCT_VERSION_MAJOR@.@PRODUCT_VERSION_MINOR@.@PRODUCT_VERSION_PATCH@"
+#define PRODUCT_BUGREPORT "@PRODUCT_BUGREPORT@"
+#define OPENVPN_VERSION_RESOURCE @PRODUCT_VERSION_RESOURCE@
+#define TAP_WIN_COMPONENT_ID "@PRODUCT_TAP_WIN_COMPONENT_ID@"
+#define TAP_WIN_MIN_MAJOR @PRODUCT_TAP_WIN_MIN_MAJOR@
+#define TAP_WIN_MIN_MINOR @PRODUCT_TAP_WIN_MIN_MINOR@
diff --git a/config-msvc.h b/config-msvc.h
new file mode 100644
index 0000000..0bb153d
--- /dev/null
+++ b/config-msvc.h
@@ -0,0 +1,138 @@
+#include <config-msvc-version.h>
+
+#define CONFIGURE_DEFINES "N/A"
+
+#define ENABLE_DEF_AUTH 1
+#define ENABLE_PF 1
+#define ENABLE_CRYPTO 1
+#define ENABLE_CRYPTO_OPENSSL 1
+#define ENABLE_DEBUG 1
+#define ENABLE_EUREPHIA 1
+#define ENABLE_FRAGMENT 1
+#define ENABLE_HTTP_PROXY 1
+#define ENABLE_LZO 1
+#define ENABLE_LZ4 1
+#define NEED_COMPAT_LZ4 1
+#define ENABLE_MANAGEMENT 1
+#define ENABLE_MULTIHOME 1
+#define ENABLE_PKCS11 1
+#define ENABLE_PLUGIN 1
+#define ENABLE_PORT_SHARE 1
+#define ENABLE_SOCKS 1
+
+#define HAVE_ERRNO_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_CTYPE_H 1
+#define HAVE_STDARG_H 1
+#define HAVE_STDIO_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRDUP 1
+#define HAVE_STRERROR 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_LIMITS_H 1
+#define HAVE_SYSTEM 1
+#define HAVE_TIME 1
+#define HAVE_TIME_H 1
+#define HAVE_UNLINK 1
+#define HAVE_VSNPRINTF 1
+#define HAVE_WINDOWS_H 1
+#define HAVE_WINSOCK2_H 1
+#define HAVE_WS2TCPIP_H 1
+#define HAVE_IO_H 1
+#define HAVE_DIRECT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_LZO_LZO1X_H 1
+#define HAVE_LZO_LZOUTIL_H 1
+#define HAVE_VERSIONHELPERS_H 1
+
+#define HAVE_ACCESS 1
+#define HAVE_CHDIR 1
+#define HAVE_CHSIZE 1
+#define HAVE_CPP_VARARG_MACRO_ISO 1
+#define HAVE_CTIME 1
+#define HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH 1
+#define HAVE_IN_PKTINFO 1
+#define HAVE_MEMSET 1
+#define HAVE_PUTENV 1
+#define HAVE_STAT 1
+
+#define HAVE_SOCKET 1
+#define HAVE_RECV 1
+#define HAVE_RECVFROM 1
+#define HAVE_SEND 1
+#define HAVE_SENDTO 1
+#define HAVE_LISTEN 1
+#define HAVE_ACCEPT 1
+#define HAVE_CONNECT 1
+#define HAVE_BIND 1
+#define HAVE_SELECT 1
+#define HAVE_GETHOSTBYNAME 1
+#define HAVE_INET_NTOA 1
+#define HAVE_SETSOCKOPT 1
+#define HAVE_GETSOCKOPT 1
+#define HAVE_GETSOCKNAME 1
+#define HAVE_POLL 1
+
+#define HAVE_OPENSSL_ENGINE 1
+
+#define PATH_SEPARATOR     '\\'
+#define PATH_SEPARATOR_STR "\\"
+
+#ifndef __cplusplus
+#define inline __inline
+#endif
+
+#define EMPTY_ARRAY_SIZE 0
+#define TARGET_WIN32 1
+#define TARGET_ALIAS "Windows-MSVC"
+
+#define HAVE_DECL_SO_MARK 0
+
+#define strncasecmp strnicmp
+#define strcasecmp _stricmp
+
+#if _MSC_VER<1900
+#define snprintf _snprintf
+#endif
+
+#if _MSC_VER < 1800
+#define strtoull strtoul
+#endif
+
+#define in_addr_t uint32_t
+#define ssize_t SSIZE_T
+
+#define S_IRUSR 0
+#define S_IWUSR 0
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#define F_OK 0
+
+#define SIGHUP    1
+#define SIGINT    2
+#define SIGUSR1   10
+#define SIGUSR2   12
+#define SIGTERM   15
+
+typedef unsigned __int64 uint64_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int8 uint8_t;
+typedef __int64 int64_t;
+typedef __int32 int32_t;
+typedef __int16 int16_t;
+typedef __int8 int8_t;
+typedef uint16_t in_port_t;
+
+#ifdef HAVE_CONFIG_MSVC_LOCAL_H
+#include <config-msvc-local.h>
+#endif
+
+/* Vista and above has implementation of inet_ntop / inet_pton */
+#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
+    #define HAVE_INET_NTOP
+    #define HAVE_INET_PTON
+#endif
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..14ccd59
--- /dev/null
+++ b/config.h
@@ -0,0 +1,969 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Configuration settings */
+#define CONFIGURE_DEFINES "enable_async_push=no enable_comp_stub=no enable_crypto=yes enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=no enable_libtool_lock=yes enable_lz4=no enable_lzo=yes enable_management=yes enable_multihome=yes enable_pam_dlopen=no enable_pedantic=no enable_pf=yes enable_pkcs11=no enable_plugin_auth_pam=no enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_server=yes enable_shared=yes enable_shared_with_static_runtimes=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=no enable_werror=no enable_win32_dll=yes enable_x509_alt_username=no with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=yes with_mem_check=no with_sysroot=no"
+
+/* special build string */
+/* #undef CONFIGURE_SPECIAL_BUILD */
+
+/* Use memory debugging function in OpenSSL */
+/* #undef CRYPTO_MDEBUG */
+
+/* p11-kit proxy */
+/* #undef DEFAULT_PKCS11_MODULE */
+
+/* Use dmalloc memory debugging library */
+/* #undef DMALLOC */
+
+/* Dimension to use for empty array declaration */
+#define EMPTY_ARRAY_SIZE 0
+
+/* Enable async push */
+/* #undef ENABLE_ASYNC_PUSH */
+
+/* Enable client capability only */
+/* #undef ENABLE_CLIENT_ONLY */
+
+/* Enable compression stub capability */
+/* #undef ENABLE_COMP_STUB */
+
+/* Enable crypto library */
+#define ENABLE_CRYPTO 1
+
+/* Use mbed TLS library */
+/* #undef ENABLE_CRYPTO_MBEDTLS */
+
+/* Use OpenSSL library */
+#define ENABLE_CRYPTO_OPENSSL 1
+
+/* Enable debugging support */
+#define ENABLE_DEBUG 1
+
+/* Enable deferred authentication */
+#define ENABLE_DEF_AUTH 1
+
+/* We have persist tun capability */
+#define ENABLE_FEATURE_TUN_PERSIST 1
+
+/* Enable internal fragmentation support */
+#define ENABLE_FRAGMENT 1
+
+/* enable iproute2 support */
+/* #undef ENABLE_IPROUTE */
+
+/* Enable LZ4 compression library */
+/* #undef ENABLE_LZ4 */
+
+/* Enable LZO compression library */
+#define ENABLE_LZO 1
+
+/* Enable management server capability */
+#define ENABLE_MANAGEMENT 1
+
+/* Enable multi-homed UDP server capability */
+#define ENABLE_MULTIHOME 1
+
+/* Enable OFB and CFB cipher modes */
+#define ENABLE_OFB_CFB_MODE 1
+
+/* Enable internal packet filter */
+#define ENABLE_PF 1
+
+/* Enable PKCS11 */
+/* #undef ENABLE_PKCS11 */
+
+/* Enable plug-in support */
+#define ENABLE_PLUGIN 1
+
+/* Enable TCP Server port sharing */
+#define ENABLE_PORT_SHARE 1
+
+/* SELinux support */
+/* #undef ENABLE_SELINUX */
+
+/* Enable smaller executable size */
+/* #undef ENABLE_SMALL */
+
+/* Enable strict options check between peers */
+/* #undef ENABLE_STRICT_OPTIONS_CHECK */
+
+/* Enable systemd integration */
+/* #undef ENABLE_SYSTEMD */
+
+/* Enable --x509-username-field feature */
+/* #undef ENABLE_X509ALTUSERNAME */
+
+/* Define to 1 if you have the `accept' function. */
+#define HAVE_ACCEPT 1
+
+/* Define to 1 if you have the `access' function. */
+#define HAVE_ACCESS 1
+
+/* Use crypto library */
+#define HAVE_AEAD_CIPHER_MODES 1
+
+/* Compiler supports anonymous unions */
+#define HAVE_ANONYMOUS_UNION_SUPPORT /**/
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the `basename' function. */
+#define HAVE_BASENAME 1
+
+/* Define to 1 if you have the `bind' function. */
+#define HAVE_BIND 1
+
+/* Define to 1 if you have the `chdir' function. */
+#define HAVE_CHDIR 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the `chsize' function. */
+/* #undef HAVE_CHSIZE */
+
+/* struct cmsghdr needed for extended socket error support */
+#define HAVE_CMSGHDR 1
+
+/* extra version available in config-version.h */
+/* #undef HAVE_CONFIG_VERSION_H */
+
+/* Define to 1 if you have the `connect' function. */
+#define HAVE_CONNECT 1
+
+/* Define to 1 if your compiler supports GNU GCC-style variadic macros */
+#define HAVE_CPP_VARARG_MACRO_GCC 1
+
+/* Define to 1 if your compiler supports ISO C99 variadic macros */
+#define HAVE_CPP_VARARG_MACRO_ISO 1
+
+/* Define to 1 if you have the `ctime' function. */
+#define HAVE_CTIME 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `daemon' function. */
+#define HAVE_DAEMON 1
+
+/* Define to 1 if you have the declaration of `SIGHUP', and to 0 if you don't.
+   */
+#define HAVE_DECL_SIGHUP 1
+
+/* Define to 1 if you have the declaration of `SIGINT', and to 0 if you don't.
+   */
+#define HAVE_DECL_SIGINT 1
+
+/* Define to 1 if you have the declaration of `SIGTERM', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIGTERM 1
+
+/* Define to 1 if you have the declaration of `SIGUSR1', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIGUSR1 1
+
+/* Define to 1 if you have the declaration of `SIGUSR2', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIGUSR2 1
+
+/* Define to 1 if you have the declaration of `SO_MARK', and to 0 if you
+   don't. */
+#define HAVE_DECL_SO_MARK 1
+
+/* Define to 1 if you have the declaration of `TUNSETPERSIST', and to 0 if you
+   don't. */
+#define HAVE_DECL_TUNSETPERSIST 1
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the `dirname' function. */
+#define HAVE_DIRNAME 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <dmalloc.h> header file. */
+/* #undef HAVE_DMALLOC_H */
+
+/* Define to 1 if you have the `DSA_bits' function. */
+#define HAVE_DSA_BITS 1
+
+/* Define to 1 if you have the `DSA_get0_pqg' function. */
+#define HAVE_DSA_GET0_PQG 1
+
+/* Define to 1 if you have the `dup' function. */
+#define HAVE_DUP 1
+
+/* Define to 1 if you have the `dup2' function. */
+#define HAVE_DUP2 1
+
+/* Define to 1 if you have the `EC_GROUP_order_bits' function. */
+#define HAVE_EC_GROUP_ORDER_BITS 1
+
+/* Define to 1 if you have the `ENGINE_cleanup' function. */
+/* #undef HAVE_ENGINE_CLEANUP */
+
+/* Define to 1 if you have the `ENGINE_load_builtin_engines' function. */
+#define HAVE_ENGINE_LOAD_BUILTIN_ENGINES 1
+
+/* Define to 1 if you have the `ENGINE_register_all_complete' function. */
+#define HAVE_ENGINE_REGISTER_ALL_COMPLETE 1
+
+/* Define to 1 if you have the `epoll_create' function. */
+#define HAVE_EPOLL_CREATE 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the `EVP_aes_256_gcm' function. */
+#define HAVE_EVP_AES_256_GCM 1
+
+/* Define to 1 if you have the `EVP_CIPHER_CTX_reset' function. */
+#define HAVE_EVP_CIPHER_CTX_RESET 1
+
+/* Define to 1 if you have the `EVP_CIPHER_CTX_set_key_length' function. */
+#define HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH 1
+
+/* Define to 1 if you have the `EVP_MD_CTX_free' function. */
+#define HAVE_EVP_MD_CTX_FREE 1
+
+/* Define to 1 if you have the `EVP_MD_CTX_new' function. */
+#define HAVE_EVP_MD_CTX_NEW 1
+
+/* Define to 1 if you have the `EVP_MD_CTX_reset' function. */
+#define HAVE_EVP_MD_CTX_RESET 1
+
+/* Define to 1 if you have the `EVP_PKEY_get0_DSA' function. */
+#define HAVE_EVP_PKEY_GET0_DSA 1
+
+/* Define to 1 if you have the `EVP_PKEY_get0_EC_KEY' function. */
+#define HAVE_EVP_PKEY_GET0_EC_KEY 1
+
+/* Define to 1 if you have the `EVP_PKEY_get0_RSA' function. */
+#define HAVE_EVP_PKEY_GET0_RSA 1
+
+/* Define to 1 if you have the `EVP_PKEY_id' function. */
+#define HAVE_EVP_PKEY_ID 1
+
+/* Define to 1 if you have the `execve' function. */
+#define HAVE_EXECVE 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `flock' function. */
+#define HAVE_FLOCK 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `getgrnam' function. */
+#define HAVE_GETGRNAM 1
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#define HAVE_GETHOSTBYNAME 1
+
+/* Define to 1 if you have the `getpass' function. */
+#define HAVE_GETPASS 1
+
+/* Define to 1 if you have the `getpeereid' function. */
+/* #undef HAVE_GETPEEREID */
+
+/* Define to 1 if you have the `getpeername' function. */
+#define HAVE_GETPEERNAME 1
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam' function. */
+#define HAVE_GETPWNAM 1
+
+/* Define to 1 if you have the `getsockname' function. */
+#define HAVE_GETSOCKNAME 1
+
+/* Define to 1 if you have the `getsockopt' function. */
+#define HAVE_GETSOCKOPT 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define to 1 if you have the `HMAC_CTX_free' function. */
+#define HAVE_HMAC_CTX_FREE 1
+
+/* Define to 1 if you have the `HMAC_CTX_new' function. */
+#define HAVE_HMAC_CTX_NEW 1
+
+/* Define to 1 if you have the `HMAC_CTX_reset' function. */
+#define HAVE_HMAC_CTX_RESET 1
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#define HAVE_INET_NTOA 1
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#define HAVE_INET_NTOP 1
+
+/* Define to 1 if you have the `inet_pton' function. */
+#define HAVE_INET_PTON 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if the system has the type `in_addr_t'. */
+#define HAVE_IN_ADDR_T 1
+
+/* struct in_pktinfo needed for IP_PKTINFO support */
+#define HAVE_IN_PKTINFO 1
+
+/* Define to 1 if the system has the type `in_port_t'. */
+#define HAVE_IN_PORT_T 1
+
+/* struct iovec needed for IPv6 support */
+#define HAVE_IOVEC 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* struct iphdr needed for IPv6 support */
+#define HAVE_IPHDR 1
+
+/* struct in_pktinfo.ipi_spec_dst needed for IP_PKTINFO support */
+#define HAVE_IPI_SPEC_DST 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <linux/if_tun.h> header file. */
+#define HAVE_LINUX_IF_TUN_H 1
+
+/* Define to 1 if you have the <linux/sockios.h> header file. */
+#define HAVE_LINUX_SOCKIOS_H 1
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#define HAVE_LINUX_TYPES_H 1
+
+/* Define to 1 if you have the `listen' function. */
+#define HAVE_LISTEN 1
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* Define to 1 if you have the <lzo1x.h> header file. */
+/* #undef HAVE_LZO1X_H */
+
+/* Define to 1 if you have the <lzoutil.h> header file. */
+/* #undef HAVE_LZOUTIL_H */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+#define HAVE_LZO_LZO1X_H 1
+
+/* Define to 1 if you have the <lzo/lzoutil.h> header file. */
+#define HAVE_LZO_LZOUTIL_H 1
+
+/* Define to 1 if you have the `mbedtls_cipher_check_tag' function. */
+/* #undef HAVE_MBEDTLS_CIPHER_CHECK_TAG */
+
+/* Define to 1 if you have the `mbedtls_cipher_write_tag' function. */
+/* #undef HAVE_MBEDTLS_CIPHER_WRITE_TAG */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mlockall' function. */
+#define HAVE_MLOCKALL 1
+
+/* struct msghdr needed for extended socket error support */
+#define HAVE_MSGHDR 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+#define HAVE_NETINET_IN_SYSTM_H 1
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+#define HAVE_NETINET_IP_H 1
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#define HAVE_NETINET_TCP_H 1
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the <net/if_tun.h> header file. */
+/* #undef HAVE_NET_IF_TUN_H */
+
+/* Define to 1 if you have the <net/if_utun.h> header file. */
+/* #undef HAVE_NET_IF_UTUN_H */
+
+/* Define to 1 if you have the <net/tun/if_tun.h> header file. */
+/* #undef HAVE_NET_TUN_IF_TUN_H */
+
+/* Define to 1 if you have the `nice' function. */
+#define HAVE_NICE 1
+
+/* Define to 1 if you have the `openlog' function. */
+#define HAVE_OPENLOG 1
+
+/* OpenSSL engine support available */
+/* #undef HAVE_OPENSSL_ENGINE */
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the `putenv' function. */
+#define HAVE_PUTENV 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `readv' function. */
+#define HAVE_READV 1
+
+/* Define to 1 if you have the `recv' function. */
+#define HAVE_RECV 1
+
+/* Define to 1 if you have the `recvfrom' function. */
+#define HAVE_RECVFROM 1
+
+/* Define to 1 if you have the `recvmsg' function. */
+#define HAVE_RECVMSG 1
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define to 1 if you have the `RSA_bits' function. */
+#define HAVE_RSA_BITS 1
+
+/* Define to 1 if you have the `RSA_get0_key' function. */
+#define HAVE_RSA_GET0_KEY 1
+
+/* Define to 1 if you have the `RSA_meth_free' function. */
+#define HAVE_RSA_METH_FREE 1
+
+/* Define to 1 if you have the `RSA_meth_get0_app_data' function. */
+#define HAVE_RSA_METH_GET0_APP_DATA 1
+
+/* Define to 1 if you have the `RSA_meth_new' function. */
+#define HAVE_RSA_METH_NEW 1
+
+/* Define to 1 if you have the `RSA_meth_set0_app_data' function. */
+#define HAVE_RSA_METH_SET0_APP_DATA 1
+
+/* Define to 1 if you have the `RSA_meth_set_finish' function. */
+#define HAVE_RSA_METH_SET_FINISH 1
+
+/* Define to 1 if you have the `RSA_meth_set_init' function. */
+#define HAVE_RSA_METH_SET_INIT 1
+
+/* Define to 1 if you have the `RSA_meth_set_priv_dec' function. */
+#define HAVE_RSA_METH_SET_PRIV_DEC 1
+
+/* Define to 1 if you have the `RSA_meth_set_priv_enc' function. */
+#define HAVE_RSA_METH_SET_PRIV_ENC 1
+
+/* Define to 1 if you have the `RSA_meth_set_pub_dec' function. */
+#define HAVE_RSA_METH_SET_PUB_DEC 1
+
+/* Define to 1 if you have the `RSA_meth_set_pub_enc' function. */
+#define HAVE_RSA_METH_SET_PUB_ENC 1
+
+/* Define to 1 if you have the `RSA_meth_set_sign' function. */
+#define HAVE_RSA_METH_SET_SIGN 1
+
+/* Define to 1 if you have the `RSA_set0_key' function. */
+#define HAVE_RSA_SET0_KEY 1
+
+/* Define to 1 if you have the `RSA_set_flags' function. */
+#define HAVE_RSA_SET_FLAGS 1
+
+/* sa_family_t, needed to hold AF_* info */
+#define HAVE_SA_FAMILY_T 1
+
+/* Define to 1 if you have the `sd_booted' function. */
+/* #undef HAVE_SD_BOOTED */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `send' function. */
+#define HAVE_SEND 1
+
+/* Define to 1 if you have the `sendmsg' function. */
+#define HAVE_SENDMSG 1
+
+/* Define to 1 if you have the `sendto' function. */
+#define HAVE_SENDTO 1
+
+/* Define to 1 if you have the `setgid' function. */
+#define HAVE_SETGID 1
+
+/* Define to 1 if you have the `setgroups' function. */
+#define HAVE_SETGROUPS 1
+
+/* Define to 1 if you have the `setsid' function. */
+#define HAVE_SETSID 1
+
+/* Define to 1 if you have the `setsockopt' function. */
+#define HAVE_SETSOCKOPT 1
+
+/* Define to 1 if you have the `setuid' function. */
+#define HAVE_SETUID 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the `socket' function. */
+#define HAVE_SOCKET 1
+
+/* struct sock_extended_err needed for extended socket error support */
+/* #undef HAVE_SOCK_EXTENDED_ERR */
+
+/* Define to 1 if you have the `SSL_CTX_get_default_passwd_cb' function. */
+#define HAVE_SSL_CTX_GET_DEFAULT_PASSWD_CB 1
+
+/* Define to 1 if you have the `SSL_CTX_get_default_passwd_cb_userdata'
+   function. */
+#define HAVE_SSL_CTX_GET_DEFAULT_PASSWD_CB_USERDATA 1
+
+/* Define to 1 if you have the `SSL_CTX_new' function. */
+#define HAVE_SSL_CTX_NEW 1
+
+/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */
+#define HAVE_SSL_CTX_SET_SECURITY_LEVEL 1
+
+/* Define to 1 if you have the `stat' function. */
+#define HAVE_STAT 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <stropts.h> header file. */
+/* #undef HAVE_STROPTS_H */
+
+/* Define to 1 if you have the `syslog' function. */
+#define HAVE_SYSLOG 1
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the `system' function. */
+#define HAVE_SYSTEM 1
+
+/* Define to 1 if you have the <systemd/sd-daemon.h> header file. */
+/* #undef HAVE_SYSTEMD_SD_DAEMON_H */
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#define HAVE_SYS_EPOLL_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/inotify.h> header file. */
+/* #undef HAVE_SYS_INOTIFY_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/kern_control.h> header file. */
+/* #undef HAVE_SYS_KERN_CONTROL_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <tap-windows.h> header file. */
+/* #undef HAVE_TAP_WINDOWS_H */
+
+/* Define to 1 if you have the `time' function. */
+#define HAVE_TIME 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `umask' function. */
+#define HAVE_UMASK 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlink' function. */
+#define HAVE_UNLINK 1
+
+/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
+/* #undef HAVE_VALGRIND_MEMCHECK_H */
+
+/* Define to 1 if you have the <versionhelpers.h> header file. */
+/* #undef HAVE_VERSIONHELPERS_H */
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+/* #undef HAVE_WINSOCK2_H */
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the `writev' function. */
+#define HAVE_WRITEV 1
+
+/* Define to 1 if you have the <ws2tcpip.h> header file. */
+/* #undef HAVE_WS2TCPIP_H */
+
+/* Define to 1 if you have the `X509_get0_notAfter' function. */
+#define HAVE_X509_GET0_NOTAFTER 1
+
+/* Define to 1 if you have the `X509_get0_notBefore' function. */
+#define HAVE_X509_GET0_NOTBEFORE 1
+
+/* Define to 1 if you have the `X509_get0_pubkey' function. */
+#define HAVE_X509_GET0_PUBKEY 1
+
+/* Define to 1 if you have the `X509_OBJECT_free' function. */
+#define HAVE_X509_OBJECT_FREE 1
+
+/* Define to 1 if you have the `X509_OBJECT_get_type' function. */
+#define HAVE_X509_OBJECT_GET_TYPE 1
+
+/* Define to 1 if you have the `X509_STORE_get0_objects' function. */
+#define HAVE_X509_STORE_GET0_OBJECTS 1
+
+/* Path to ifconfig tool */
+#define IFCONFIG_PATH "/sbin/ifconfig"
+
+/* Path to iproute tool */
+#define IPROUTE_PATH "/sbin/ip"
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* use copy of LZ4 source in compat/ */
+/* #undef NEED_COMPAT_LZ4 */
+
+/* OpenVPN major version - integer */
+#define OPENVPN_VERSION_MAJOR 2
+
+/* OpenVPN minor version - integer */
+#define OPENVPN_VERSION_MINOR 4
+
+/* OpenVPN patch level - may be a string or integer */
+#define OPENVPN_VERSION_PATCH ".9"
+
+/* Version in windows resource format */
+#define OPENVPN_VERSION_RESOURCE 2,4,9,0
+
+/* Name of package */
+#define PACKAGE "openvpn"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "openvpn-users@lists.sourceforge.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "OpenVPN"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "OpenVPN 2.4.9"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "openvpn"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.4.9"
+
+/* Path separator */
+#define PATH_SEPARATOR '/'
+
+/* Path separator */
+#define PATH_SEPARATOR_STR "/"
+
+/* Enable pedantic mode */
+/* #undef PEDANTIC */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Path to route tool */
+#define ROUTE_PATH "/sbin/route"
+
+/* SIGHUP replacement */
+/* #undef SIGHUP */
+
+/* SIGINT replacement */
+/* #undef SIGINT */
+
+/* SIGTERM replacement */
+/* #undef SIGTERM */
+
+/* SIGUSR1 replacement */
+/* #undef SIGUSR1 */
+
+/* SIGUSR2 replacement */
+/* #undef SIGUSR2 */
+
+/* The size of `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 8
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Path to systemd-ask-password tool */
+#define SYSTEMD_ASK_PASSWORD_PATH "/bin/systemd-ask-password"
+
+/* systemd is newer than v216 */
+/* #undef SYSTEMD_NEWER_THAN_216 */
+
+/* The tap-windows id */
+#define TAP_WIN_COMPONENT_ID "tap0901"
+
+/* The tap-windows version number is required for OpenVPN */
+#define TAP_WIN_MIN_MAJOR 9
+
+/* The tap-windows version number is required for OpenVPN */
+#define TAP_WIN_MIN_MINOR 9
+
+/* Are we running AIX? */
+/* #undef TARGET_AIX */
+
+/* A string representing our host */
+#define TARGET_ALIAS "x86_64-pc-linux-gnu"
+
+/* Are we running on Mac OS X? */
+/* #undef TARGET_DARWIN */
+
+/* Are we running on DragonFlyBSD? */
+/* #undef TARGET_DRAGONFLY */
+
+/* Are we running on FreeBSD? */
+/* #undef TARGET_FREEBSD */
+
+/* Are we running on Linux? */
+#define TARGET_LINUX 1
+
+/* Are we running NetBSD? */
+/* #undef TARGET_NETBSD */
+
+/* Are we running on OpenBSD? */
+/* #undef TARGET_OPENBSD */
+
+/* Target prefix */
+#define TARGET_PREFIX "L"
+
+/* Are we running on Solaris? */
+/* #undef TARGET_SOLARIS */
+
+/* Are we running WIN32? */
+/* #undef TARGET_WIN32 */
+
+/* dlopen libpam */
+/* #undef USE_PAM_DLOPEN */
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Use valgrind memory debugging library */
+/* #undef USE_VALGRIND */
+
+/* Version number of package */
+#define VERSION "2.4.9"
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Workaround missing in_addr_t */
+/* #undef in_addr_t */
+
+/* Workaround missing in_port_t */
+/* #undef in_port_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+   calls it, or to nothing if 'inline' is not supported under any name.  */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int16_t */
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int32_t */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to the type of a signed integer type of width exactly 8 bits if such
+   a type exists and the standard includes do not define it. */
+/* #undef int8_t */
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* type to use in place of socklen_t if not defined */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+   code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f61255b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1424 @@
+dnl  OpenVPN -- An application to securely tunnel IP networks
+dnl             over a single UDP port, with support for SSL/TLS-based
+dnl             session authentication and key exchange,
+dnl             packet encryption, packet authentication, and
+dnl             packet compression.
+dnl
+dnl  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+dnl  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+dnl
+dnl  This program is free software; you can redistribute it and/or modify
+dnl  it under the terms of the GNU General Public License as published by
+dnl  the Free Software Foundation; either version 2 of the License, or
+dnl  (at your option) any later version.
+dnl
+dnl  This program is distributed in the hope that it will be useful,
+dnl  but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl  GNU General Public License for more details.
+dnl
+dnl  You should have received a copy of the GNU General Public License along
+dnl  with this program; if not, write to the Free Software Foundation, Inc.,
+dnl  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+
+m4_include(version.m4)
+AC_INIT([PRODUCT_NAME], [PRODUCT_VERSION], [PRODUCT_BUGREPORT], [PRODUCT_TARNAME])
+m4_include(compat.m4)
+AC_DEFINE([OPENVPN_VERSION_RESOURCE], [PRODUCT_VERSION_RESOURCE], [Version in windows resource format])
+AC_SUBST([OPENVPN_VERSION_MAJOR], [PRODUCT_VERSION_MAJOR], [OpenVPN major version])
+AC_SUBST([OPENVPN_VERSION_MINOR], [PRODUCT_VERSION_MINOR], [OpenVPN minor version])
+AC_SUBST([OPENVPN_VERSION_PATCH], [PRODUCT_VERSION_PATCH], [OpenVPN patch level - may be a string or integer])
+AC_DEFINE([OPENVPN_VERSION_MAJOR], [PRODUCT_VERSION_MAJOR], [OpenVPN major version - integer])
+AC_DEFINE([OPENVPN_VERSION_MINOR], [PRODUCT_VERSION_MINOR], [OpenVPN minor version - integer])
+AC_DEFINE([OPENVPN_VERSION_PATCH], ["PRODUCT_VERSION_PATCH"], [OpenVPN patch level - may be a string or integer])
+
+AC_CONFIG_AUX_DIR([.])
+AC_CONFIG_HEADERS([config.h include/openvpn-plugin.h])
+AC_CONFIG_SRCDIR([src/openvpn/syshead.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+dnl Initialize automake.  automake < 1.12 didn't have serial-tests and
+dnl gives an error if it sees this, but for automake >= 1.13
+dnl serial-tests is required so we have to include it.  Solution is to
+dnl test for the version of automake (by running an external command)
+dnl and provide it if necessary.  Note we have to do this entirely using
+dnl m4 macros since automake queries this macro by running
+dnl 'autoconf --trace ...'.
+m4_define([serial_tests], [
+    m4_esyscmd([automake --version |
+                head -1 |
+                awk '{split ($NF,a,"."); if (a[1] == 1 && a[2] >= 12) { print "serial-tests" }}'
+    ])
+])
+AM_INIT_AUTOMAKE(foreign serial_tests) dnl NB: Do not [quote] this parameter.
+AC_CANONICAL_HOST
+AC_USE_SYSTEM_EXTENSIONS
+
+AC_ARG_ENABLE(
+	[lzo],
+	[AS_HELP_STRING([--disable-lzo], [disable LZO compression support @<:@default=yes@:>@])],
+	,
+	[enable_lzo="yes"]
+)
+
+AC_ARG_ENABLE(lz4,
+	[  --disable-lz4           Disable LZ4 compression support],
+	[enable_lz4="$enableval"],
+	[enable_lz4="yes"]
+)
+
+AC_ARG_ENABLE(comp-stub,
+	[  --enable-comp-stub      Don't compile compression support but still allow limited interoperability with compression-enabled peers],
+	[enable_comp_stub="$enableval"],
+	[enable_comp_stub="no"]
+)
+
+AC_ARG_ENABLE(
+	[crypto],
+	[AS_HELP_STRING([--disable-crypto], [disable crypto support @<:@default=yes@:>@])],
+	,
+	[enable_crypto="yes"]
+)
+
+AC_ARG_ENABLE(
+	[ofb-cfb],
+	[AS_HELP_STRING([--disable-ofb-cfb], [disable support for OFB and CFB cipher modes @<:@default=yes@:>@])],
+	,
+	[enable_crypto_ofb_cfb="yes"]
+)
+
+AC_ARG_ENABLE(
+	[x509-alt-username],
+	[AS_HELP_STRING([--enable-x509-alt-username], [enable the --x509-username-field feature @<:@default=no@:>@])],
+	,
+	[enable_x509_alt_username="no"]
+)
+
+AC_ARG_ENABLE(
+	[server],
+	[AS_HELP_STRING([--disable-server], [disable server support only (but retain client support) @<:@default=yes@:>@])],
+	,
+	[enable_server="yes"]
+)
+
+AC_ARG_ENABLE(
+	[plugins],
+	[AS_HELP_STRING([--disable-plugins], [disable plug-in support @<:@default=yes@:>@])],
+	,
+	[enable_plugins="yes"]
+)
+
+AC_ARG_ENABLE(
+	[management],
+	[AS_HELP_STRING([--disable-management], [disable management server support @<:@default=yes@:>@])],
+	,
+	[enable_management="yes"]
+)
+
+AC_ARG_ENABLE(
+	[pkcs11],
+	[AS_HELP_STRING([--enable-pkcs11], [enable pkcs11 support @<:@default=no@:>@])],
+	,
+	[enable_pkcs11="no"]
+)
+
+AC_ARG_ENABLE(
+	[fragment],
+	[AS_HELP_STRING([--disable-fragment], [disable internal fragmentation support (--fragment) @<:@default=yes@:>@])],
+	,
+	[enable_fragment="yes"]
+)
+
+AC_ARG_ENABLE(
+	[multihome],
+	[AS_HELP_STRING([--disable-multihome], [disable multi-homed UDP server support (--multihome) @<:@default=yes@:>@])],
+	,
+	[enable_multihome="yes"]
+)
+
+AC_ARG_ENABLE(
+	[port-share],
+	[AS_HELP_STRING([--disable-port-share], [disable TCP server port-share support (--port-share) @<:@default=yes@:>@])],
+	,
+	[enable_port_share="yes"]
+)
+
+AC_ARG_ENABLE(
+	[debug],
+	[AS_HELP_STRING([--disable-debug], [disable debugging support (disable gremlin and verb 7+ messages) @<:@default=yes@:>@])],
+	,
+	[enable_debug="yes"]
+)
+
+AC_ARG_ENABLE(
+	[small],
+	[AS_HELP_STRING([--enable-small], [enable smaller executable size (disable OCC, usage message, and verb 4 parm list) @<:@default=no@:>@])],
+	,
+	[enable_small="no"]
+)
+
+AC_ARG_ENABLE(
+	[iproute2],
+	[AS_HELP_STRING([--enable-iproute2], [enable support for iproute2 @<:@default=no@:>@])],
+	,
+	[enable_iproute2="no"]
+)
+
+AC_ARG_ENABLE(
+	[def-auth],
+	[AS_HELP_STRING([--disable-def-auth], [disable deferred authentication @<:@default=yes@:>@])],
+	,
+	[enable_def_auth="yes"]
+)
+
+AC_ARG_ENABLE(
+	[pf],
+	[AS_HELP_STRING([--disable-pf], [disable internal packet filter @<:@default=yes@:>@])],
+	,
+	[enable_pf="yes"]
+)
+
+AC_ARG_ENABLE(
+	[plugin-auth-pam],
+	[AS_HELP_STRING([--disable-plugin-auth-pam], [disable auth-pam plugin @<:@default=platform specific@:>@])],
+	,
+	[
+		case "$host" in
+			*-*-openbsd*) enable_plugin_auth_pam="no";;
+			*-mingw*) enable_plugin_auth_pam="no";;
+			*) enable_plugin_auth_pam="yes";;
+		esac
+	]
+)
+
+AC_ARG_ENABLE(
+	[plugin-down-root],
+	[AS_HELP_STRING([--disable-plugin-down-root], [disable down-root plugin @<:@default=platform specific@:>@])],
+	,
+	[
+		case "$host" in
+			*-mingw*) enable_plugin_down_root="no";;
+			*) enable_plugin_down_root="yes";;
+		esac
+	]
+)
+
+AC_ARG_ENABLE(
+	[pam-dlopen],
+	[AS_HELP_STRING([--enable-pam-dlopen], [dlopen libpam @<:@default=no@:>@])],
+	,
+	[enable_pam_dlopen="no"]
+)
+
+AC_ARG_ENABLE(
+	[strict],
+	[AS_HELP_STRING([--enable-strict], [enable strict compiler warnings (debugging option) @<:@default=no@:>@])],
+	,
+	[enable_strict="no"]
+)
+
+AC_ARG_ENABLE(
+	[pedantic],
+	[AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings, will not generate a working executable (debugging option) @<:@default=no@:>@])],
+	,
+	[enable_pedantic="no"]
+)
+
+AC_ARG_ENABLE(
+	[werror],
+	[AS_HELP_STRING([--enable-werror], [promote compiler warnings to errors, will cause builds to fail if the compiler issues warnings (debugging option) @<:@default=no@:>@])],
+	,
+	[enable_werror="no"]
+)
+
+AC_ARG_ENABLE(
+	[strict-options],
+	[AS_HELP_STRING([--enable-strict-options], [enable strict options check between peers (debugging option) @<:@default=no@:>@])],
+	,
+	[enable_strict_options="no"]
+)
+
+AC_ARG_ENABLE(
+	[selinux],
+	[AS_HELP_STRING([--enable-selinux], [enable SELinux support @<:@default=no@:>@])],
+	,
+	[enable_selinux="no"]
+)
+
+AC_ARG_ENABLE(
+	[systemd],
+	[AS_HELP_STRING([--enable-systemd], [enable systemd suppport @<:@default=no@:>@])],
+	,
+	[enable_systemd="no"]
+)
+
+AC_ARG_ENABLE(
+	[async-push],
+	[AS_HELP_STRING([--enable-async-push], [enable async-push support for plugins providing deferred authentication @<:@default=no@:>@])],
+	,
+	[enable_async_push="no"]
+)
+
+AC_ARG_WITH(
+	[special-build],
+	[AS_HELP_STRING([--with-special-build=STRING], [specify special build string])],
+	[test -n "${withval}" && AC_DEFINE_UNQUOTED([CONFIGURE_SPECIAL_BUILD], ["${withval}"], [special build string])]
+)
+
+AC_ARG_WITH(
+	[mem-check],
+	[AS_HELP_STRING([--with-mem-check=TYPE], [build with debug memory checking, TYPE=no|dmalloc|valgrind|ssl @<:@default=no@:>@])],
+	[
+		case "${withval}" in
+			dmalloc|valgrind|ssl|no) ;;
+			*) AC_MSG_ERROR([bad value ${withval} for --mem-check]) ;;
+		esac
+	],
+	[with_mem_check="no"]
+)
+
+AC_ARG_WITH(
+	[crypto-library],
+	[AS_HELP_STRING([--with-crypto-library=library], [build with the given crypto library, TYPE=openssl|mbedtls @<:@default=openssl@:>@])],
+	[
+		case "${withval}" in
+			openssl|mbedtls) ;;
+			*) AC_MSG_ERROR([bad value ${withval} for --with-crypto-library]) ;;
+		esac
+	],
+	[with_crypto_library="openssl"]
+)
+
+AC_ARG_VAR([PLUGINDIR], [Path of plug-in directory @<:@default=LIBDIR/openvpn/plugins@:>@])
+if test -n "${PLUGINDIR}"; then
+	plugindir="${PLUGINDIR}"
+else
+	plugindir="\${libdir}/openvpn/plugins"
+fi
+
+AC_DEFINE_UNQUOTED([TARGET_ALIAS], ["${host}"], [A string representing our host])
+case "$host" in
+	*-*-linux*)
+		AC_DEFINE([TARGET_LINUX], [1], [Are we running on Linux?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["L"], [Target prefix])
+		;;
+	*-*-solaris*)
+		AC_DEFINE([TARGET_SOLARIS], [1], [Are we running on Solaris?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["S"], [Target prefix])
+		CPPFLAGS="$CPPFLAGS -D_XPG4_2"
+		;;
+	*-*-openbsd*)
+		AC_DEFINE([TARGET_OPENBSD], [1], [Are we running on OpenBSD?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["O"], [Target prefix])
+		;;
+	*-*-freebsd*)
+		AC_DEFINE([TARGET_FREEBSD], [1], [Are we running on FreeBSD?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["F"], [Target prefix])
+		;;
+	*-*-netbsd*)
+		AC_DEFINE([TARGET_NETBSD], [1], [Are we running NetBSD?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["N"], [Target prefix])
+		;;
+	*-*-darwin*)
+		AC_DEFINE([TARGET_DARWIN], [1], [Are we running on Mac OS X?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["M"], [Target prefix])
+		have_tap_header="yes"
+		ac_cv_type_struct_in_pktinfo=no
+		;;
+	*-mingw*)
+		AC_DEFINE([TARGET_WIN32], [1], [Are we running WIN32?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["W"], [Target prefix])
+		CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN"
+		CPPFLAGS="${CPPFLAGS} -DNTDDI_VERSION=NTDDI_VISTA -D_WIN32_WINNT=_WIN32_WINNT_VISTA"
+		WIN32=yes
+		;;
+	*-*-dragonfly*)
+		AC_DEFINE([TARGET_DRAGONFLY], [1], [Are we running on DragonFlyBSD?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["D"], [Target prefix])
+		;;
+	*-aix*)
+		AC_DEFINE([TARGET_AIX], [1], [Are we running AIX?])
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["A"], [Target prefix])
+		ROUTE="/usr/sbin/route"
+		have_tap_header="yes"
+		ac_cv_header_net_if_h="no"	# exists, but breaks things
+		;;
+	*)
+		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["X"], [Target prefix])
+		have_tap_header="yes"
+		;;
+esac
+
+PKG_PROG_PKG_CONFIG
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_SED
+AC_PROG_MAKE_SET
+
+AC_ARG_VAR([IFCONFIG], [full path to ipconfig utility])
+AC_ARG_VAR([ROUTE], [full path to route utility])
+AC_ARG_VAR([IPROUTE], [full path to ip utility])
+AC_ARG_VAR([NETSTAT], [path to netstat utility]) # tests
+AC_ARG_VAR([MAN2HTML], [path to man2html utility])
+AC_ARG_VAR([GIT], [path to git utility])
+AC_ARG_VAR([SYSTEMD_ASK_PASSWORD], [path to systemd-ask-password utility])
+AC_ARG_VAR([SYSTEMD_UNIT_DIR], [Path of systemd unit directory @<:@default=LIBDIR/systemd/system@:>@])
+AC_ARG_VAR([TMPFILES_DIR], [Path of tmpfiles directory @<:@default=LIBDIR/tmpfiles.d@:>@])
+AC_PATH_PROGS([IFCONFIG], [ifconfig],, [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
+AC_PATH_PROGS([ROUTE], [route],, [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
+AC_PATH_PROGS([IPROUTE], [ip],, [$PATH:/usr/local/sbin:/usr/sbin:/sbin])
+AC_PATH_PROGS([SYSTEMD_ASK_PASSWORD], [systemd-ask-password],, [$PATH:/usr/local/bin:/usr/bin:/bin])
+AC_CHECK_PROGS([NETSTAT], [netstat], [netstat], [$PATH:/usr/local/sbin:/usr/sbin:/sbin:/etc]) # tests
+AC_CHECK_PROGS([MAN2HTML], [man2html])
+AC_CHECK_PROGS([GIT], [git]) # optional
+AC_DEFINE_UNQUOTED([IFCONFIG_PATH], ["$IFCONFIG"], [Path to ifconfig tool])
+AC_DEFINE_UNQUOTED([IPROUTE_PATH], ["$IPROUTE"], [Path to iproute tool])
+AC_DEFINE_UNQUOTED([ROUTE_PATH], ["$ROUTE"], [Path to route tool])
+AC_DEFINE_UNQUOTED([SYSTEMD_ASK_PASSWORD_PATH], ["$SYSTEMD_ASK_PASSWORD"], [Path to systemd-ask-password tool])
+
+# Set -std=c99 unless user already specified a -std=
+case "${CFLAGS}" in
+  *-std=*) ;;
+  *)       CFLAGS="${CFLAGS} -std=c99" ;;
+esac
+
+#
+# Libtool
+#
+ifdef(
+	[LT_INIT],
+	[
+		LT_INIT([win32-dll])
+		LT_LANG([Windows Resource])
+	],
+	[
+		AC_LIBTOOL_WIN32_DLL
+		AC_LIBTOOL_RC
+		AC_PROG_LIBTOOL
+	]
+)
+
+AC_C_CONST
+AC_C_INLINE
+AC_C_VOLATILE
+AC_TYPE_OFF_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_UID_T
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_SIGNAL
+AX_CPP_VARARG_MACRO_ISO
+AX_CPP_VARARG_MACRO_GCC
+AX_TYPE_SOCKLEN_T
+AX_EMPTY_ARRAY
+AC_CHECK_SIZEOF([unsigned int])
+AC_CHECK_SIZEOF([unsigned long])
+AC_CHECK_HEADERS([ \
+	stdio.h stdarg.h limits.h \
+	time.h errno.h fcntl.h io.h direct.h \
+	ctype.h sys/types.h sys/socket.h \
+	signal.h unistd.h dlfcn.h \
+	netinet/in.h netinet/in_systm.h \
+	netinet/tcp.h arpa/inet.h netdb.h \
+	windows.h winsock2.h ws2tcpip.h \
+	versionhelpers.h \
+])
+AC_CHECK_HEADERS([ \
+	sys/time.h sys/ioctl.h sys/stat.h \
+	sys/mman.h sys/file.h sys/wait.h \
+	unistd.h signal.h libgen.h stropts.h \
+	syslog.h pwd.h grp.h \
+	sys/sockio.h sys/uio.h linux/sockios.h \
+	linux/types.h sys/poll.h sys/epoll.h err.h \
+])
+
+SOCKET_INCLUDES="
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+"
+
+AC_CHECK_HEADERS(
+	[net/if.h netinet/ip.h resolv.h sys/un.h net/if_utun.h sys/kern_control.h],
+	,
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+
+AC_CHECK_TYPES(
+	[in_addr_t],
+	,
+	[AC_DEFINE([in_addr_t], [uint32_t], [Workaround missing in_addr_t])],
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPES(
+	[in_port_t],
+	,
+	[AC_DEFINE([in_port_t], [uint16_t], [Workaround missing in_port_t])],
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct iphdr],
+	[AC_DEFINE([HAVE_IPHDR], [1], [struct iphdr needed for IPv6 support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct sock_extended_err],
+	[AC_DEFINE([HAVE_SOCK_EXTENDED_ERR], [1], [struct sock_extended_err needed for extended socket error support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct msghdr],
+	[AC_DEFINE([HAVE_MSGHDR], [1], [struct msghdr needed for extended socket error support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct cmsghdr],
+	[AC_DEFINE([HAVE_CMSGHDR], [1], [struct cmsghdr needed for extended socket error support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct in_pktinfo],
+	[AC_DEFINE([HAVE_IN_PKTINFO], [1], [struct in_pktinfo needed for IP_PKTINFO support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+        [sa_family_t],
+        [AC_DEFINE([HAVE_SA_FAMILY_T], [1], [sa_family_t, needed to hold AF_* info])],
+        ,
+        [[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_MEMBER(
+	[struct in_pktinfo.ipi_spec_dst],
+	[AC_DEFINE([HAVE_IPI_SPEC_DST], [1], [struct in_pktinfo.ipi_spec_dst needed for IP_PKTINFO support])],
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_TYPE(
+	[struct sockaddr_in6],
+	,
+	[AC_MSG_ERROR([struct sockaddr_in6 not found, needed for ipv6 transport support.])],
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECK_DECLS(
+	[SO_MARK],
+	,
+	,
+	[[${SOCKET_INCLUDES}]]
+)
+AC_CHECKING([anonymous union support])
+AC_COMPILE_IFELSE(
+	[AC_LANG_PROGRAM(
+		[[
+			struct mystruct {
+			  union {
+			    int m1;
+			    char m2;
+			  };
+			};
+		]],
+		[[
+			struct mystruct s;
+			s.m1 = 1; s.m2 = 2;
+		]]
+	)],
+	[
+		AC_MSG_RESULT([yes])
+		AC_DEFINE([HAVE_ANONYMOUS_UNION_SUPPORT], [], [Compiler supports anonymous unions])
+	],
+	[AC_MSG_RESULT([no])]
+)
+
+saved_LDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS -Wl,--wrap=exit"
+AC_MSG_CHECKING([linker supports --wrap])
+AC_LINK_IFELSE(
+	[AC_LANG_PROGRAM(
+		[[
+			void exit(int);
+			void __real_exit(int);
+			void __wrap_exit(int i) {
+				__real_exit(i);
+			}
+		]],
+		[[
+			exit(0);
+		]]
+	)],
+	[
+		AC_MSG_RESULT([yes])
+		have_ld_wrap_support=yes
+	],
+	[AC_MSG_RESULT([no])],
+)
+LDFLAGS="$saved_LDFLAGS"
+
+dnl We emulate signals in Windows
+AC_CHECK_DECLS(
+	[SIGHUP],
+	,
+	[AC_DEFINE([SIGHUP], [1], [SIGHUP replacement])],
+	[[
+		#ifdef HAVE_SIGNAL_H
+		#include <signal.h>
+		#endif
+	]]
+)
+AC_CHECK_DECLS(
+	[SIGINT],
+	,
+	[AC_DEFINE([SIGINT], [2], [SIGINT replacement])],
+	[[
+		#ifdef HAVE_SIGNAL_H
+		#include <signal.h>
+		#endif
+	]]
+)
+AC_CHECK_DECLS(
+	[SIGUSR1],
+	,
+	[AC_DEFINE([SIGUSR1], [10], [SIGUSR1 replacement])],
+	[[
+		#ifdef HAVE_SIGNAL_H
+		#include <signal.h>
+		#endif
+	]]
+)
+AC_CHECK_DECLS(
+	[SIGUSR2],
+	,
+	[AC_DEFINE([SIGUSR2], [12], [SIGUSR2 replacement])],
+	[[
+		#ifdef HAVE_SIGNAL_H
+		#include <signal.h>
+		#endif
+	]]
+)
+AC_CHECK_DECLS(
+	[SIGTERM],
+	,
+	[AC_DEFINE([SIGTERM], [15], [SIGTERM replacement])],
+	[[
+		#ifdef HAVE_SIGNAL_H
+		#include <signal.h>
+		#endif
+	]]
+)
+
+AC_FUNC_FORK
+
+AC_CHECK_FUNCS([ \
+	daemon chroot getpwnam setuid nice system getpid dup dup2 \
+	getpass syslog openlog mlockall getgrnam setgid \
+	setgroups stat flock readv writev time gettimeofday \
+	ctime memset vsnprintf strdup \
+	setsid chdir putenv getpeername unlink \
+	chsize ftruncate execve getpeereid umask basename dirname access \
+	epoll_create \
+])
+
+AC_CHECK_LIB(
+	[dl],
+	[dlopen],
+	[DL_LIBS="-ldl"]
+)
+AC_SUBST([DL_LIBS])
+
+AC_CHECK_LIB(
+	[nsl],
+	[inet_ntoa],
+	[SOCKETS_LIBS="${SOCKETS_LIBS} -lnsl"]
+)
+AC_CHECK_LIB(
+	[socket],
+	[socket],
+	[SOCKETS_LIBS="${SOCKETS_LIBS} -lsocket"]
+)
+AC_CHECK_LIB(
+	[resolv],
+	[gethostbyname],
+	[SOCKETS_LIBS="${SOCKETS_LIBS} -lresolv"]
+)
+AC_SUBST([SOCKETS_LIBS])
+
+old_LIBS="${LIBS}"
+LIBS="${LIBS} ${SOCKETS_LIBS}"
+AC_CHECK_FUNCS([sendmsg recvmsg])
+# Windows use stdcall for winsock so we cannot auto detect these
+m4_define(
+	[SOCKET_FUNCS],
+[socket recv recvfrom send sendto listen dnl
+accept connect bind select gethostbyname inet_ntoa]dnl
+)
+m4_define(
+	[SOCKET_OPT_FUNCS],
+	[setsockopt getsockopt getsockname poll]dnl
+)
+if test "${WIN32}" = "yes"; then
+# normal autoconf function checking does not find inet_ntop/inet_pton
+# because they need to include the actual header file and link ws2_32.dll
+	LIBS="${LIBS} -lws2_32"
+	AC_MSG_CHECKING([for MinGW inet_ntop()/inet_pton()])
+	AC_LINK_IFELSE(
+		[AC_LANG_PROGRAM(
+			[[
+#include <ws2tcpip.h>
+			]],
+			[[
+int r = (int) inet_ntop (0, NULL, NULL, 0);
+    r += inet_pton(AF_INET, NULL, NULL);
+return r;
+			]]
+		)],
+		[AC_MSG_RESULT([OK])
+		 AC_DEFINE([HAVE_INET_NTOP],[1],[MinGW inet_ntop])
+		 AC_DEFINE([HAVE_INET_PTON],[1],[MinGW inet_pton])
+		],
+		[AC_MSG_RESULT([not found])]
+	)
+	m4_foreach(
+		[F],
+		m4_split(SOCKET_FUNCS SOCKET_OPT_FUNCS),
+			m4_define([UF], [[m4_join([_], [HAVE], m4_toupper(F))]])
+			AC_DEFINE([UF], [1], [Win32 builtin])
+	)
+else
+	AC_CHECK_FUNCS([inet_ntop inet_pton])
+	AC_CHECK_FUNCS(
+		SOCKET_FUNCS,
+		,
+		[AC_MSG_ERROR([Required library function not found])]
+	)
+	AC_CHECK_FUNCS(SOCKET_OPT_FUNCS)
+fi
+LIBS="${old_LIBS}"
+
+# we assume res_init() always exist, but need to find out *where*...
+AC_SEARCH_LIBS(__res_init, resolv bind, ,
+    AC_SEARCH_LIBS(res_9_init, resolv bind, ,
+	AC_SEARCH_LIBS(res_init, resolv bind, , )))
+
+AC_ARG_VAR([TAP_CFLAGS], [C compiler flags for tap])
+old_CFLAGS="${CFLAGS}"
+CFLAGS="${CFLAGS} ${TAP_CFLAGS}"
+AC_CHECK_HEADERS(
+	[ \
+		net/if_tun.h net/tun/if_tun.h \
+		linux/if_tun.h \
+		tap-windows.h \
+	],
+	[have_tap_header="yes"]
+)
+AC_CHECK_DECLS(
+	[TUNSETPERSIST],
+	[AC_DEFINE([ENABLE_FEATURE_TUN_PERSIST], [1], [We have persist tun capability])],
+	,
+	[[
+		#ifdef HAVE_LINUX_IF_TUN_H
+		#include <linux/if_tun.h>
+		#endif
+	]]
+)
+CFLAGS="${old_CFLAGS}"
+test "${have_tap_header}" = "yes" || AC_MSG_ERROR([no tap header could be found])
+
+AC_CHECK_LIB(
+	[selinux],
+	[setcon],
+	[SELINUX_LIBS="-lselinux"]
+)
+AC_SUBST([SELINUX_LIBS])
+
+AC_ARG_VAR([LIBPAM_CFLAGS], [C compiler flags for libpam])
+AC_ARG_VAR([LIBPAM_LIBS], [linker flags for libpam])
+if test -z "${LIBPAM_LIBS}"; then
+	AC_CHECK_LIB(
+		[pam],
+		[pam_start],
+		[LIBPAM_LIBS="-lpam"]
+	)
+fi
+
+case "${with_mem_check}" in
+	valgrind)
+		AC_CHECK_HEADERS(
+			[valgrind/memcheck.h],
+			[
+				CFLAGS="${CFLAGS} -g -fno-inline"
+				AC_DEFINE(
+					[USE_VALGRIND],
+					[1],
+					[Use valgrind memory debugging library]
+				)
+			],
+			[AC_MSG_ERROR([valgrind headers not found.])]
+		)
+		;;
+	dmalloc)
+		AC_CHECK_HEADERS(
+			[dmalloc.h],
+			[AC_CHECK_LIB(
+				[dmalloc],
+				[malloc],
+				[
+					LIBS="${LIBS} -ldmalloc"
+					AC_DEFINE(
+						[DMALLOC],
+						[1],
+						[Use dmalloc memory debugging library]
+					)
+				],
+				[AC_MSG_ERROR([dmalloc library not found.])]
+			)],
+			[AC_MSG_ERROR([dmalloc headers not found.])]
+		)
+		;;
+	ssl)
+		AC_CHECK_LIB(
+			[ssl],
+			[CRYPTO_mem_ctrl],
+			[
+				AC_DEFINE(
+					[CRYPTO_MDEBUG],
+					[1],
+					[Use memory debugging function in OpenSSL]
+				)
+				AC_MSG_NOTICE([NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG])
+			],
+			[AC_MSG_ERROR([Memory Debugging function in OpenSSL library not found.])]
+		)
+		;;
+esac
+
+PKG_CHECK_MODULES(
+	[PKCS11_HELPER],
+	[libpkcs11-helper-1 >= 1.11],
+	[have_pkcs11_helper="yes"],
+	[]
+)
+
+if test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "openssl"; then
+	AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL])
+	AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL])
+
+	if test -z "${OPENSSL_CFLAGS}" -a -z "${OPENSSL_LIBS}"; then
+		# if the user did not explicitly specify flags, try to autodetect
+		PKG_CHECK_MODULES(
+			[OPENSSL],
+			[libcrypto >= 0.9.8, libssl >= 0.9.8],
+	        [have_openssl="yes"],
+			[have_openssl="no"] # Provide if-not-found to prevent erroring out
+		)
+
+		OPENSSL_LIBS=${OPENSSL_LIBS:--lssl -lcrypto}
+	fi
+
+	saved_CFLAGS="${CFLAGS}"
+	saved_LIBS="${LIBS}"
+	CFLAGS="${CFLAGS} ${OPENSSL_CFLAGS}"
+	LIBS="${LIBS} ${OPENSSL_LIBS}"
+
+	AC_CHECK_FUNCS([SSL_CTX_new EVP_CIPHER_CTX_set_key_length],
+				   ,
+				   [AC_MSG_ERROR([openssl check failed])]
+	)
+
+	have_openssl_engine="yes"
+	AC_CHECK_FUNCS(
+		[ \
+			ENGINE_load_builtin_engines \
+			ENGINE_register_all_complete \
+			ENGINE_cleanup \
+		],
+		,
+		[have_openssl_engine="no"; break]
+	)
+	if test "${have_openssl_engine}" = "no"; then
+		AC_CHECK_DECL( [ENGINE_cleanup], [have_openssl_engine="yes"],,
+			[[
+				#include <openssl/engine.h>
+			]]
+		)
+	fi
+	if test "${have_openssl_engine}" = "yes"; then
+		AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [OpenSSL engine support available])
+	fi
+
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[EVP_aes_256_gcm],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
+	AC_CHECK_FUNCS(
+		[ \
+			HMAC_CTX_new \
+			HMAC_CTX_free \
+			HMAC_CTX_reset \
+			EVP_MD_CTX_new \
+			EVP_MD_CTX_free \
+			EVP_MD_CTX_reset \
+			EVP_CIPHER_CTX_reset \
+			SSL_CTX_get_default_passwd_cb \
+			SSL_CTX_get_default_passwd_cb_userdata \
+			SSL_CTX_set_security_level \
+			X509_get0_notBefore \
+			X509_get0_notAfter \
+			X509_get0_pubkey \
+			X509_STORE_get0_objects \
+			X509_OBJECT_free \
+			X509_OBJECT_get_type \
+			EVP_PKEY_id \
+			EVP_PKEY_get0_RSA \
+			EVP_PKEY_get0_DSA \
+			EVP_PKEY_get0_EC_KEY \
+			RSA_set_flags \
+			RSA_bits \
+			RSA_get0_key \
+			RSA_set0_key \
+			DSA_get0_pqg \
+			DSA_bits \
+			RSA_meth_new \
+			RSA_meth_free \
+			RSA_meth_set_pub_enc \
+			RSA_meth_set_pub_dec \
+			RSA_meth_set_priv_enc \
+			RSA_meth_set_priv_dec \
+			RSA_meth_set_init \
+			RSA_meth_set_sign \
+			RSA_meth_set_finish \
+			RSA_meth_set0_app_data \
+			RSA_meth_get0_app_data \
+			EC_GROUP_order_bits
+		]
+	)
+
+	CFLAGS="${saved_CFLAGS}"
+	LIBS="${saved_LIBS}"
+
+	have_crypto="yes"
+	AC_DEFINE([ENABLE_CRYPTO_OPENSSL], [1], [Use OpenSSL library])
+	CRYPTO_CFLAGS="${OPENSSL_CFLAGS}"
+	CRYPTO_LIBS="${OPENSSL_LIBS}"
+elif test "${enable_crypto}" = "yes" -a "${with_crypto_library}" = "mbedtls"; then
+	AC_ARG_VAR([MBEDTLS_CFLAGS], [C compiler flags for mbedtls])
+	AC_ARG_VAR([MBEDTLS_LIBS], [linker flags for mbedtls])
+
+	saved_CFLAGS="${CFLAGS}"
+	saved_LIBS="${LIBS}"
+
+	if test -z "${MBEDTLS_CFLAGS}" -a -z "${MBEDTLS_LIBS}"; then
+		# if the user did not explicitly specify flags, try to autodetect
+		LIBS="${LIBS} -lmbedtls -lmbedx509 -lmbedcrypto"
+		AC_CHECK_LIB(
+			[mbedtls],
+			[mbedtls_ssl_init],
+			[MBEDTLS_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"],
+			[AC_MSG_ERROR([Could not find mbed TLS.])],
+			[${PKCS11_HELPER_LIBS}]
+		)
+	fi
+
+	CFLAGS="${MBEDTLS_CFLAGS} ${PKCS11_HELPER_CFLAGS} ${CFLAGS}"
+	LIBS="${MBEDTLS_LIBS} ${PKCS11_HELPER_LIBS} ${LIBS}"
+
+	AC_MSG_CHECKING([mbedtls version])
+	AC_COMPILE_IFELSE(
+		[AC_LANG_PROGRAM(
+			[[
+#include <mbedtls/version.h>
+			]],
+			[[
+#if MBEDTLS_VERSION_NUMBER < 0x02000000 || MBEDTLS_VERSION_NUMBER >= 0x03000000
+#error invalid version
+#endif
+			]]
+		)],
+		[AC_MSG_RESULT([ok])],
+		[AC_MSG_ERROR([mbed TLS 2.y.z required])]
+	)
+
+	mbedtls_with_pkcs11="no"
+	AC_COMPILE_IFELSE(
+		[AC_LANG_PROGRAM(
+			[[
+#include <mbedtls/config.h>
+			]],
+			[[
+#ifndef MBEDTLS_PKCS11_C
+#error pkcs11 wrapper missing
+#endif
+			]]
+		)],
+		mbedtls_with_pkcs11="yes")
+
+	AC_MSG_CHECKING([mbedtls pkcs11 support])
+	if test "${enable_pkcs11}" = "yes"; then
+		if test "${mbedtls_with_pkcs11}" = "yes"; then
+			AC_MSG_RESULT([ok])
+		else
+			AC_MSG_ERROR([mbedtls has no pkcs11 wrapper compiled in])
+		fi
+	else
+		if test "${mbedtls_with_pkcs11}" != "yes"; then
+			AC_MSG_RESULT([ok])
+		else
+			AC_MSG_ERROR([mbed TLS compiled with PKCS11, while OpenVPN is not])
+		fi
+	fi
+
+	have_crypto_aead_modes="yes"
+	AC_CHECK_FUNCS(
+		[ \
+			mbedtls_cipher_write_tag \
+			mbedtls_cipher_check_tag \
+		],
+		,
+		[have_crypto_aead_modes="no"; break]
+	)
+
+	CFLAGS="${saved_CFLAGS}"
+	LIBS="${saved_LIBS}"
+	have_crypto="yes"
+	AC_DEFINE([ENABLE_CRYPTO_MBEDTLS], [1], [Use mbed TLS library])
+	CRYPTO_CFLAGS="${MBEDTLS_CFLAGS}"
+	CRYPTO_LIBS="${MBEDTLS_LIBS}"
+elif test "${enable_crypto}" = "yes"; then
+	AC_MSG_ERROR([Invalid crypto library: ${with_crypto_library}])
+fi
+
+AC_ARG_VAR([LZO_CFLAGS], [C compiler flags for lzo])
+AC_ARG_VAR([LZO_LIBS], [linker flags for lzo])
+have_lzo="yes"
+if test -z "${LZO_LIBS}"; then
+	AC_CHECK_LIB(
+		[lzo2],
+		[lzo1x_1_15_compress],
+		[LZO_LIBS="-llzo2"],
+		[AC_CHECK_LIB(
+			[lzo],
+			[lzo1x_1_15_compress],
+			[LZO_LIBS="-llzo"],
+			[have_lzo="no"]
+		)]
+	)
+fi
+if test "${have_lzo}" = "yes"; then
+	saved_CFLAGS="${CFLAGS}"
+	CFLAGS="${CFLAGS} ${LZO_CFLAGS}"
+	AC_CHECK_HEADERS(
+		[lzo/lzoutil.h],
+		,
+		[AC_CHECK_HEADERS(
+			[lzoutil.h],
+			,
+			[AC_MSG_ERROR([lzoutil.h is missing])]
+		)]
+	)
+	AC_CHECK_HEADERS(
+		[lzo/lzo1x.h],
+		,
+		[AC_CHECK_HEADERS(
+			[lzo1x.h],
+			,
+			[AC_MSG_ERROR([lzo1x.h is missing])]
+		)]
+	)
+	CFLAGS="${saved_CFLAGS}"
+fi
+
+dnl
+dnl check for LZ4 library
+dnl
+
+AC_ARG_VAR([LZ4_CFLAGS], [C compiler flags for lz4])
+AC_ARG_VAR([LZ4_LIBS], [linker flags for lz4])
+if test "$enable_lz4" = "yes" && test "$enable_comp_stub" = "no"; then
+    if test -z "${LZ4_CFLAGS}" -a -z "${LZ4_LIBS}"; then
+	# if the user did not explicitly specify flags, try to autodetect
+	PKG_CHECK_MODULES([LZ4],
+			  [liblz4 >= 1.7.1 liblz4 < 100],
+			  [have_lz4="yes"],
+			  [LZ4_LIBS="-llz4"] # If this fails, we will do another test next.
+					     # We also add set LZ4_LIBS otherwise the
+					     # linker will not know about the lz4 library
+	)
+    fi
+
+    saved_CFLAGS="${CFLAGS}"
+    saved_LIBS="${LIBS}"
+    CFLAGS="${CFLAGS} ${LZ4_CFLAGS}"
+    LIBS="${LIBS} ${LZ4_LIBS}"
+
+    # If pkgconfig check failed or LZ4_CFLAGS/LZ4_LIBS env vars
+    # are used, check the version directly in the LZ4 include file
+    if test "${have_lz4}" != "yes"; then
+	AC_CHECK_HEADERS([lz4.h],
+			 [have_lz4h="yes"],
+			 [])
+
+	if test "${have_lz4h}" = "yes" ; then
+	    AC_MSG_CHECKING([additionally if system LZ4 version >= 1.7.1])
+	    AC_COMPILE_IFELSE(
+		[AC_LANG_PROGRAM([[
+#include <lz4.h>
+				 ]],
+				 [[
+/* Version encoding: MMNNPP (Major miNor Patch) - see lz4.h for details */
+#if LZ4_VERSION_NUMBER < 10701L
+#error LZ4 is too old
+#endif
+				 ]]
+				)],
+		[
+		    AC_MSG_RESULT([ok])
+		    have_lz4="yes"
+		],
+		[AC_MSG_RESULT([system LZ4 library is too old])]
+	    )
+	fi
+    fi
+
+    # Double check we have a few needed functions
+    if test "${have_lz4}" = "yes" ; then
+	AC_CHECK_LIB([lz4],
+		     [LZ4_compress_default],
+		     [],
+		     [have_lz4="no"])
+	AC_CHECK_LIB([lz4],
+		     [LZ4_decompress_safe],
+		     [],
+		     [have_lz4="no"])
+    fi
+
+    if test "${have_lz4}" != "yes" ; then
+	AC_MSG_RESULT([		usable LZ4 library or header not found, using version in src/compat/compat-lz4.*])
+	AC_DEFINE([NEED_COMPAT_LZ4], [1], [use copy of LZ4 source in compat/])
+	LZ4_LIBS=""
+    fi
+    OPTIONAL_LZ4_CFLAGS="${LZ4_CFLAGS}"
+    OPTIONAL_LZ4_LIBS="${LZ4_LIBS}"
+    AC_DEFINE(ENABLE_LZ4, [1], [Enable LZ4 compression library])
+    CFLAGS="${saved_CFLAGS}"
+    LIBS="${saved_LIBS}"
+fi
+
+
+dnl
+dnl Check for systemd
+dnl
+AM_CONDITIONAL([ENABLE_SYSTEMD], [test "${enable_systemd}" = "yes"])
+if test "$enable_systemd" = "yes" ; then
+    PKG_CHECK_MODULES([libsystemd], [systemd libsystemd],
+                      [],
+                      [PKG_CHECK_MODULES([libsystemd], [libsystemd-daemon])]
+                      )
+
+    PKG_CHECK_EXISTS( [libsystemd > 216],
+                     [AC_DEFINE([SYSTEMD_NEWER_THAN_216], [1],
+                           [systemd is newer than v216])]
+                    )
+
+    AC_CHECK_HEADERS(systemd/sd-daemon.h,
+       ,
+       [
+	   AC_MSG_ERROR([systemd development headers not found.])
+       ])
+
+    saved_LIBS="${LIBS}"
+    LIBS="${LIBS} ${libsystemd_LIBS}"
+    AC_CHECK_FUNCS([sd_booted], [], [AC_MSG_ERROR([systemd library is missing sd_booted()])])
+    OPTIONAL_SYSTEMD_LIBS="${libsystemd_LIBS}"
+    AC_DEFINE(ENABLE_SYSTEMD, 1, [Enable systemd integration])
+    LIBS="${saved_LIBS}"
+
+    if test -n "${SYSTEMD_UNIT_DIR}"; then
+        systemdunitdir="${SYSTEMD_UNIT_DIR}"
+    else
+        systemdunitdir="\${libdir}/systemd/system"
+    fi
+
+    if test -n "${TMPFILES_DIR}"; then
+        tmpfilesdir="${TMPFILES_DIR}"
+    else
+        tmpfilesdir="\${libdir}/tmpfiles.d"
+    fi
+fi
+
+
+AC_MSG_CHECKING([git checkout])
+GIT_CHECKOUT="no"
+if test -n "${GIT}" -a -d "${srcdir}/.git"; then
+	AC_DEFINE([HAVE_CONFIG_VERSION_H], [1], [extra version available in config-version.h])
+	GIT_CHECKOUT="yes"
+fi
+AC_MSG_RESULT([${GIT_CHECKOUT}])
+
+if test -n "${SP_PLATFORM_WINDOWS}"; then
+	AC_DEFINE_UNQUOTED([PATH_SEPARATOR], ['\\\\'], [Path separator]) #"
+	AC_DEFINE_UNQUOTED([PATH_SEPARATOR_STR], ["\\\\"], [Path separator]) #"
+else
+	AC_DEFINE_UNQUOTED([PATH_SEPARATOR], ['/'], [Path separator])
+	AC_DEFINE_UNQUOTED([PATH_SEPARATOR_STR], ["/"], [Path separator])
+fi
+
+dnl enable --x509-username-field feature if requested
+if test "${enable_x509_alt_username}" = "yes"; then
+	if test "${with_crypto_library}" = "mbedtls" ; then
+		AC_MSG_ERROR([mbed TLS does not support the --x509-username-field feature])
+	fi
+
+	AC_DEFINE([ENABLE_X509ALTUSERNAME], [1], [Enable --x509-username-field feature])
+fi
+
+test "${ac_cv_header_sys_uio_h}" = "yes" && AC_DEFINE([HAVE_IOVEC], [1], [struct iovec needed for IPv6 support])
+test "${enable_server}" = "no" && AC_DEFINE([ENABLE_CLIENT_ONLY], [1], [Enable client capability only])
+test "${enable_management}" = "yes" && AC_DEFINE([ENABLE_MANAGEMENT], [1], [Enable management server capability])
+test "${enable_multihome}" = "yes" && AC_DEFINE([ENABLE_MULTIHOME], [1], [Enable multi-homed UDP server capability])
+test "${enable_debug}" = "yes" && AC_DEFINE([ENABLE_DEBUG], [1], [Enable debugging support])
+test "${enable_small}" = "yes" && AC_DEFINE([ENABLE_SMALL], [1], [Enable smaller executable size])
+test "${enable_fragment}" = "yes" && AC_DEFINE([ENABLE_FRAGMENT], [1], [Enable internal fragmentation support])
+test "${enable_port_share}" = "yes" && AC_DEFINE([ENABLE_PORT_SHARE], [1], [Enable TCP Server port sharing])
+test "${enable_def_auth}" = "yes" && AC_DEFINE([ENABLE_DEF_AUTH], [1], [Enable deferred authentication])
+test "${enable_pf}" = "yes" && AC_DEFINE([ENABLE_PF], [1], [Enable internal packet filter])
+test "${enable_strict_options}" = "yes" && AC_DEFINE([ENABLE_STRICT_OPTIONS_CHECK], [1], [Enable strict options check between peers])
+
+if test "${enable_crypto}" = "yes"; then
+	test "${have_crypto}" != "yes" && AC_MSG_ERROR([${with_crypto_library} crypto is required but missing])
+	test "${enable_crypto_ofb_cfb}" = "yes" && AC_DEFINE([ENABLE_OFB_CFB_MODE], [1], [Enable OFB and CFB cipher modes])
+	test "${have_crypto_aead_modes}" = "yes" && AC_DEFINE([HAVE_AEAD_CIPHER_MODES], [1], [Use crypto library])
+	OPTIONAL_CRYPTO_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${CRYPTO_CFLAGS}"
+	OPTIONAL_CRYPTO_LIBS="${OPTIONAL_CRYPTO_LIBS} ${CRYPTO_LIBS}"
+	AC_DEFINE([ENABLE_CRYPTO], [1], [Enable crypto library])
+fi
+
+if test "${enable_plugins}" = "yes"; then
+	OPTIONAL_DL_LIBS="${DL_LIBS}"
+	AC_DEFINE([ENABLE_PLUGIN], [1], [Enable plug-in support])
+else
+	enable_plugin_auth_pam="no"
+	enable_plugin_down_root="no"
+fi
+
+if test "${enable_iproute2}" = "yes"; then
+	test -z "${IPROUTE}" && AC_MSG_ERROR([ip utility is required but missing])
+	AC_DEFINE([ENABLE_IPROUTE], [1], [enable iproute2 support])
+else
+	if test "${WIN32}" != "yes"; then
+		test -z "${ROUTE}" && AC_MSG_ERROR([route utility is required but missing])
+		test -z "${IFCONFIG}" && AC_MSG_ERROR([ifconfig utility is required but missing])
+	fi
+fi
+
+if test "${enable_selinux}" = "yes"; then
+	test -z "${SELINUX_LIBS}" && AC_MSG_ERROR([libselinux required but missing])
+	OPTIONAL_SELINUX_LIBS="${SELINUX_LIBS}"
+	AC_DEFINE([ENABLE_SELINUX], [1], [SELinux support])
+fi
+
+if test "${enable_lzo}" = "yes"; then
+	test "${have_lzo}" != "yes" && AC_MSG_ERROR([lzo enabled but missing])
+	OPTIONAL_LZO_CFLAGS="${LZO_CFLAGS}"
+	OPTIONAL_LZO_LIBS="${LZO_LIBS}"
+	AC_DEFINE([ENABLE_LZO], [1], [Enable LZO compression library])
+fi
+if test "${enable_comp_stub}" = "yes"; then
+	test "${enable_lzo}" = "yes" && AC_MSG_ERROR([Cannot have both comp stub and lzo enabled (use --disable-lzo)])
+	test "${enable_lz4}" = "yes" && AC_MSG_ERROR([Cannot have both comp stub and LZ4 enabled (use --disable-lz4)])
+	AC_DEFINE([ENABLE_COMP_STUB], [1], [Enable compression stub capability])
+fi
+
+if test "${enable_pkcs11}" = "yes"; then
+	test "${have_pkcs11_helper}" != "yes" && AC_MSG_ERROR([PKCS11 enabled but libpkcs11-helper is missing])
+	test "${enable_crypto}" != "yes" && AC_MSG_ERROR([PKCS11 can be enabled only if crypto is enabled])
+	OPTIONAL_PKCS11_HELPER_CFLAGS="${PKCS11_HELPER_CFLAGS}"
+	OPTIONAL_PKCS11_HELPER_LIBS="${PKCS11_HELPER_LIBS}"
+	AC_DEFINE([ENABLE_PKCS11], [1], [Enable PKCS11])
+	PKG_CHECK_MODULES(
+		[P11KIT],
+		[p11-kit-1],
+		[proxy_module="`$PKG_CONFIG --variable=proxy_module p11-kit-1`"
+		 AC_DEFINE_UNQUOTED([DEFAULT_PKCS11_MODULE], "${proxy_module}", [p11-kit proxy])],
+		[]
+	)
+fi
+
+AC_DEFUN([ACL_CHECK_ADD_COMPILE_FLAGS], [
+    old_cflags="$CFLAGS"
+    CFLAGS="$1 $CFLAGS"
+    AC_MSG_CHECKING([whether the compiler acceppts $1])
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AC_MSG_RESULT([yes])],
+        [AC_MSG_RESULT([no]); CFLAGS="$old_cflags"])]
+)
+
+ACL_CHECK_ADD_COMPILE_FLAGS([-Wno-unused-function])
+ACL_CHECK_ADD_COMPILE_FLAGS([-Wno-unused-parameter])
+ACL_CHECK_ADD_COMPILE_FLAGS([-Wall])
+
+if test "${enable_pedantic}" = "yes"; then
+	enable_strict="yes"
+	CFLAGS="${CFLAGS} -pedantic"
+	AC_DEFINE([PEDANTIC], [1], [Enable pedantic mode])
+fi
+if test "${enable_strict}" = "yes"; then
+	CFLAGS="${CFLAGS} -Wsign-compare -Wuninitialized"
+fi
+if test "${enable_werror}" = "yes"; then
+	CFLAGS="${CFLAGS} -Werror"
+fi
+
+if test "${enable_plugin_auth_pam}" = "yes"; then
+	PLUGIN_AUTH_PAM_CFLAGS="${LIBPAM_CFLAGS}"
+	if test "${enable_pam_dlopen}" = "yes"; then
+		AC_DEFINE([USE_PAM_DLOPEN], [1], [dlopen libpam])
+		PLUGIN_AUTH_PAM_LIBS="${DL_LIBS}"
+	else
+		test -z "${LIBPAM_LIBS}" && AC_MSG_ERROR([libpam required but missing])
+		PLUGIN_AUTH_PAM_LIBS="${LIBPAM_LIBS}"
+	fi
+fi
+
+if test "${enable_async_push}" = "yes"; then
+	case "$host" in
+		*-*-freebsd*)
+			PKG_CHECK_MODULES(
+				[OPTIONAL_INOTIFY],
+				[libinotify],
+				[
+					AC_DEFINE([HAVE_SYS_INOTIFY_H])
+					AC_DEFINE([ENABLE_ASYNC_PUSH], [1], [Enable async push])
+				]
+			)
+		;;
+		*)
+			AC_CHECK_HEADERS(
+				[sys/inotify.h],
+				AC_DEFINE([ENABLE_ASYNC_PUSH], [1], [Enable async push]),
+				AC_MSG_ERROR([inotify.h not found.])
+			)
+		;;
+	esac
+fi
+
+CONFIGURE_DEFINES="`set | grep '^enable_.*=' ; set | grep '^with_.*='`"
+AC_DEFINE_UNQUOTED([CONFIGURE_DEFINES], ["`echo ${CONFIGURE_DEFINES}`"], [Configuration settings])
+
+TAP_WIN_COMPONENT_ID="PRODUCT_TAP_WIN_COMPONENT_ID"
+TAP_WIN_MIN_MAJOR="PRODUCT_TAP_WIN_MIN_MAJOR"
+TAP_WIN_MIN_MINOR="PRODUCT_TAP_WIN_MIN_MINOR"
+AC_DEFINE_UNQUOTED([TAP_WIN_COMPONENT_ID], ["${TAP_WIN_COMPONENT_ID}"], [The tap-windows id])
+AC_DEFINE_UNQUOTED([TAP_WIN_MIN_MAJOR], [${TAP_WIN_MIN_MAJOR}], [The tap-windows version number is required for OpenVPN])
+AC_DEFINE_UNQUOTED([TAP_WIN_MIN_MINOR], [${TAP_WIN_MIN_MINOR}], [The tap-windows version number is required for OpenVPN])
+AC_SUBST([TAP_WIN_COMPONENT_ID])
+AC_SUBST([TAP_WIN_MIN_MAJOR])
+AC_SUBST([TAP_WIN_MIN_MINOR])
+
+AC_SUBST([OPTIONAL_DL_LIBS])
+AC_SUBST([OPTIONAL_SELINUX_LIBS])
+AC_SUBST([OPTIONAL_CRYPTO_CFLAGS])
+AC_SUBST([OPTIONAL_CRYPTO_LIBS])
+AC_SUBST([OPTIONAL_LZO_CFLAGS])
+AC_SUBST([OPTIONAL_LZO_LIBS])
+AC_SUBST([OPTIONAL_LZ4_CFLAGS])
+AC_SUBST([OPTIONAL_LZ4_LIBS])
+AC_SUBST([OPTIONAL_SYSTEMD_LIBS])
+AC_SUBST([OPTIONAL_PKCS11_HELPER_CFLAGS])
+AC_SUBST([OPTIONAL_PKCS11_HELPER_LIBS])
+AC_SUBST([OPTIONAL_INOTIFY_CFLAGS])
+AC_SUBST([OPTIONAL_INOTIFY_LIBS])
+
+AC_SUBST([PLUGIN_AUTH_PAM_CFLAGS])
+AC_SUBST([PLUGIN_AUTH_PAM_LIBS])
+
+AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
+AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"])
+AM_CONDITIONAL([ENABLE_PLUGIN_AUTH_PAM], [test "${enable_plugin_auth_pam}" = "yes"])
+AM_CONDITIONAL([ENABLE_PLUGIN_DOWN_ROOT], [test "${enable_plugin_down_root}" = "yes"])
+AM_CONDITIONAL([ENABLE_CRYPTO], [test "${enable_crypto}" = "yes"])
+AM_CONDITIONAL([HAVE_LD_WRAP_SUPPORT], [test "${have_ld_wrap_support}" = "yes"])
+
+sampledir="\$(docdir)/sample"
+AC_SUBST([plugindir])
+AC_SUBST([sampledir])
+
+AC_SUBST([systemdunitdir])
+AC_SUBST([tmpfilesdir])
+
+TEST_LDFLAGS="${OPTIONAL_CRYPTO_LIBS} ${OPTIONAL_PKCS11_HELPER_LIBS} -lcmocka -L\$(top_builddir)/vendor/dist/lib -Wl,-rpath,\$(top_builddir)/vendor/dist/lib"
+TEST_CFLAGS="${OPTIONAL_CRYPTO_CFLAGS} ${OPTIONAL_PKCS11_HELPER_CFLAGS} -I\$(top_srcdir)/include -I\$(top_builddir)/vendor/dist/include"
+
+AC_SUBST([TEST_LDFLAGS])
+AC_SUBST([TEST_CFLAGS])
+
+# Check if cmake is available and cmocka git submodule is initialized,
+# needed for unit testing
+AC_CHECK_PROGS([CMAKE], [cmake])
+if test -n "${CMAKE}"; then
+   if test -f "${srcdir}/vendor/cmocka/CMakeLists.txt"; then
+      AM_CONDITIONAL([CMOCKA_INITIALIZED], [true])
+   else
+      AM_CONDITIONAL([CMOCKA_INITIALIZED], [false])
+      AC_MSG_RESULT([!! WARNING !! The cmoka git submodule has not been initialized or updated.  Unit testing cannot be performed.])
+   fi
+else
+   AC_MSG_RESULT([!! WARNING !! CMake is NOT available.  Unit testing cannot be performed.])
+   AM_CONDITIONAL([CMOCKA_INITIALIZED], [false])
+fi
+
+
+AC_CONFIG_FILES([
+	version.sh
+	Makefile
+	include/Makefile
+	src/Makefile
+	src/compat/Makefile
+	src/openvpn/Makefile
+	src/plugins/Makefile
+	src/plugins/down-root/Makefile
+])
+AC_OUTPUT
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..484e4e1
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,18 @@
+#
+#  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+#  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+
+MAINTAINERCLEANFILES = \
+	$(srcdir)/Makefile.in \
+	$(srcdir)/openvpn-plugin.h.in
+
+include_HEADERS = \
+	openvpn-plugin.h \
+	openvpn-msg.h
diff --git a/include/openvpn-msg.h b/include/openvpn-msg.h
new file mode 100644
index 0000000..66177a2
--- /dev/null
+++ b/include/openvpn-msg.h
@@ -0,0 +1,120 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2013-2018 Heiko Hund <heiko.hund@sophos.com>
+ *
+ *  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 OPENVPN_MSG_H_
+#define OPENVPN_MSG_H_
+
+typedef enum {
+    msg_acknowledgement,
+    msg_add_address,
+    msg_del_address,
+    msg_add_route,
+    msg_del_route,
+    msg_add_dns_cfg,
+    msg_del_dns_cfg,
+    msg_add_nbt_cfg,
+    msg_del_nbt_cfg,
+    msg_flush_neighbors,
+    msg_add_block_dns,
+    msg_del_block_dns,
+    msg_register_dns,
+    msg_enable_dhcp,
+} message_type_t;
+
+typedef struct {
+    message_type_t type;
+    size_t size;
+    int message_id;
+} message_header_t;
+
+typedef union {
+    struct in_addr ipv4;
+    struct in6_addr ipv6;
+} inet_address_t;
+
+typedef struct {
+    int index;
+    char name[256];
+} interface_t;
+
+typedef struct {
+    message_header_t header;
+    short family;
+    inet_address_t address;
+    int prefix_len;
+    interface_t iface;
+} address_message_t;
+
+typedef struct {
+    message_header_t header;
+    short family;
+    inet_address_t prefix;
+    int prefix_len;
+    inet_address_t gateway;
+    interface_t iface;
+    int metric;
+} route_message_t;
+
+typedef struct {
+    message_header_t header;
+    interface_t iface;
+    char domains[512];
+    short family;
+    int addr_len;
+    inet_address_t addr[4]; /* support up to 4 dns addresses */
+} dns_cfg_message_t;
+
+typedef struct {
+    message_header_t header;
+    interface_t iface;
+    int disable_nbt;
+    int nbt_type;
+    char scope_id[256];
+    struct in_addr primary_nbns;
+    struct in_addr secondary_nbns;
+} nbt_cfg_message_t;
+
+/* TODO: NTP */
+
+typedef struct {
+    message_header_t header;
+    short family;
+    interface_t iface;
+} flush_neighbors_message_t;
+
+typedef struct {
+    message_header_t header;
+    int error_number;
+} ack_message_t;
+
+typedef struct {
+    message_header_t header;
+    interface_t iface;
+} block_dns_message_t;
+
+typedef struct {
+    message_header_t header;
+    interface_t iface;
+} enable_dhcp_message_t;
+
+#endif /* ifndef OPENVPN_MSG_H_ */
diff --git a/include/openvpn-plugin.h b/include/openvpn-plugin.h
new file mode 100644
index 0000000..0a9f3fe
--- /dev/null
+++ b/include/openvpn-plugin.h
@@ -0,0 +1,871 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 OPENVPN_PLUGIN_H_
+#define OPENVPN_PLUGIN_H_
+
+#define OPENVPN_PLUGIN_VERSION 3
+
+#ifdef ENABLE_CRYPTO
+#ifdef ENABLE_CRYPTO_MBEDTLS
+#include <mbedtls/x509_crt.h>
+#ifndef __OPENVPN_X509_CERT_T_DECLARED
+#define __OPENVPN_X509_CERT_T_DECLARED
+typedef mbedtls_x509_crt openvpn_x509_cert_t;
+#endif
+#else  /* ifdef ENABLE_CRYPTO_MBEDTLS */
+#include <openssl/x509.h>
+#ifndef __OPENVPN_X509_CERT_T_DECLARED
+#define __OPENVPN_X509_CERT_T_DECLARED
+typedef X509 openvpn_x509_cert_t;
+#endif
+#endif
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Plug-in types.  These types correspond to the set of script callbacks
+ * supported by OpenVPN.
+ *
+ * This is the general call sequence to expect when running in server mode:
+ *
+ * Initial Server Startup:
+ *
+ * FUNC: openvpn_plugin_open_v1
+ * FUNC: openvpn_plugin_client_constructor_v1 (this is the top-level "generic"
+ *                                             client template)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_UP
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ROUTE_UP
+ *
+ * New Client Connection:
+ *
+ * FUNC: openvpn_plugin_client_constructor_v1
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ENABLE_PF
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
+ *                                                     in the server chain)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_IPCHANGE
+ *
+ * [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
+ * we don't proceed until authentication is verified via auth_control_file]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_CLIENT_CONNECT_V2
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_LEARN_ADDRESS
+ *
+ * [Client session ensues]
+ *
+ * For each "TLS soft reset", according to reneg-sec option (or similar):
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ENABLE_PF
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
+ *                                                     in the server chain)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
+ *
+ * [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
+ * we expect that authentication is verified via auth_control_file within
+ * the number of seconds defined by the "hand-window" option.  Data channel traffic
+ * will continue to flow uninterrupted during this period.]
+ *
+ * [Client session continues]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_CLIENT_DISCONNECT
+ * FUNC: openvpn_plugin_client_destructor_v1
+ *
+ * [ some time may pass ]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_LEARN_ADDRESS (this coincides with a
+ *                                                            lazy free of initial
+ *                                                            learned addr object)
+ * Server Shutdown:
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_DOWN
+ * FUNC: openvpn_plugin_client_destructor_v1 (top-level "generic" client)
+ * FUNC: openvpn_plugin_close_v1
+ */
+#define OPENVPN_PLUGIN_UP                    0
+#define OPENVPN_PLUGIN_DOWN                  1
+#define OPENVPN_PLUGIN_ROUTE_UP              2
+#define OPENVPN_PLUGIN_IPCHANGE              3
+#define OPENVPN_PLUGIN_TLS_VERIFY            4
+#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
+#define OPENVPN_PLUGIN_CLIENT_CONNECT        6
+#define OPENVPN_PLUGIN_CLIENT_DISCONNECT     7
+#define OPENVPN_PLUGIN_LEARN_ADDRESS         8
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2     9
+#define OPENVPN_PLUGIN_TLS_FINAL             10
+#define OPENVPN_PLUGIN_ENABLE_PF             11
+#define OPENVPN_PLUGIN_ROUTE_PREDOWN         12
+#define OPENVPN_PLUGIN_N                     13
+
+/*
+ * Build a mask out of a set of plug-in types.
+ */
+#define OPENVPN_PLUGIN_MASK(x) (1<<(x))
+
+/*
+ * A pointer to a plugin-defined object which contains
+ * the object state.
+ */
+typedef void *openvpn_plugin_handle_t;
+
+/*
+ * Return value for openvpn_plugin_func_v1 function
+ */
+#define OPENVPN_PLUGIN_FUNC_SUCCESS  0
+#define OPENVPN_PLUGIN_FUNC_ERROR    1
+#define OPENVPN_PLUGIN_FUNC_DEFERRED 2
+
+/*
+ * For Windows (needs to be modified for MSVC)
+ */
+#if defined(_WIN32) && !defined(OPENVPN_PLUGIN_H)
+#define OPENVPN_EXPORT __declspec(dllexport)
+#else
+#define OPENVPN_EXPORT
+#endif
+
+/*
+ * If OPENVPN_PLUGIN_H is defined, we know that we are being
+ * included in an OpenVPN compile, rather than a plugin compile.
+ */
+#ifdef OPENVPN_PLUGIN_H
+
+/*
+ * We are compiling OpenVPN.
+ */
+#define OPENVPN_PLUGIN_DEF        typedef
+#define OPENVPN_PLUGIN_FUNC(name) (*name)
+
+#else  /* ifdef OPENVPN_PLUGIN_H */
+
+/*
+ * We are compiling plugin.
+ */
+#define OPENVPN_PLUGIN_DEF        OPENVPN_EXPORT
+#define OPENVPN_PLUGIN_FUNC(name) name
+
+#endif
+
+/*
+ * Used by openvpn_plugin_func to return structured
+ * data.  The plugin should allocate all structure
+ * instances, name strings, and value strings with
+ * malloc, since OpenVPN will assume that it
+ * can free the list by calling free() over the same.
+ */
+struct openvpn_plugin_string_list
+{
+    struct openvpn_plugin_string_list *next;
+    char *name;
+    char *value;
+};
+
+
+/* openvpn_plugin_{open,func}_v3() related structs */
+
+/**
+ * Defines version of the v3 plugin argument structs
+ *
+ * Whenever one or more of these structs are modified, this constant
+ * must be updated.  A changelog should be appended in this comment
+ * as well, to make it easier to see what information is available
+ * in the different versions.
+ *
+ * Version   Comment
+ *    1      Initial plugin v3 structures providing the same API as
+ *           the v2 plugin interface, X509 certificate information +
+ *           a logging API for plug-ins.
+ *
+ *    2      Added ssl_api member in struct openvpn_plugin_args_open_in
+ *           which identifies the SSL implementation OpenVPN is compiled
+ *           against.
+ *
+ *    3      Added ovpn_version, ovpn_version_major, ovpn_version_minor
+ *           and ovpn_version_patch to provide the runtime version of
+ *           OpenVPN to plug-ins.
+ *
+ *    4      Exported secure_memzero() as plugin_secure_memzero()
+ *
+ *    5      Exported openvpn_base64_encode() as plugin_base64_encode()
+ *           Exported openvpn_base64_decode() as plugin_base64_decode()
+ */
+#define OPENVPN_PLUGINv3_STRUCTVER 5
+
+/**
+ * Definitions needed for the plug-in callback functions.
+ */
+typedef enum
+{
+    PLOG_ERR    = (1 << 0),/* Error condition message */
+    PLOG_WARN   = (1 << 1),/* General warning message */
+    PLOG_NOTE   = (1 << 2),/* Informational message */
+    PLOG_DEBUG  = (1 << 3),/* Debug message, displayed if verb >= 7 */
+
+    PLOG_ERRNO  = (1 << 8),/* Add error description to message */
+    PLOG_NOMUTE = (1 << 9), /* Mute setting does not apply for message */
+
+} openvpn_plugin_log_flags_t;
+
+
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+#define _ovpn_chk_fmt(a, b) __attribute__ ((format(gnu_printf, (a), (b))))
+#else
+#define _ovpn_chk_fmt(a, b) __attribute__ ((format(__printf__, (a), (b))))
+#endif
+#else  /* ifdef __GNUC__ */
+#define _ovpn_chk_fmt(a, b)
+#endif
+
+typedef void (*plugin_log_t)(openvpn_plugin_log_flags_t flags,
+                             const char *plugin_name,
+                             const char *format, ...) _ovpn_chk_fmt (3, 4);
+
+typedef void (*plugin_vlog_t)(openvpn_plugin_log_flags_t flags,
+                              const char *plugin_name,
+                              const char *format,
+                              va_list arglist) _ovpn_chk_fmt (3, 0);
+#undef _ovpn_chk_fmt
+
+/**
+ *  Export of secure_memzero() to be used inside plug-ins
+ *
+ *  @param data   Pointer to data to zeroise
+ *  @param len    Length of data, in bytes
+ *
+ */
+typedef void (*plugin_secure_memzero_t)(void *data, size_t len);
+
+/**
+ *  Export of openvpn_base64_encode() to be used inside plug-ins
+ *
+ *  @param data   Pointer to data to BASE64 encode
+ *  @param size   Length of data, in bytes
+ *  @param *str   Pointer to the return buffer.  This needed memory is
+ *                allocated by openvpn_base64_encode() and needs to be free()d
+ *                after use.
+ *
+ *  @return int   Returns the length of the buffer created, or -1 on error.
+ *
+ */
+typedef int (*plugin_base64_encode_t)(const void *data, int size, char **str);
+
+/**
+ *  Export of openvpn_base64_decode() to be used inside plug-ins
+ *
+ *  @param str    Pointer to the BASE64 encoded data
+ *  @param data   Pointer to the buffer where save the decoded data
+ *  @param size   Size of the destination buffer
+ *
+ *  @return int   Returns the length of the decoded data, or -1 on error or
+ *                if the destination buffer is too small.
+ *
+ */
+typedef int (*plugin_base64_decode_t)(const char *str, void *data, int size);
+
+
+/**
+ * Used by the openvpn_plugin_open_v3() function to pass callback
+ * function pointers to the plug-in.
+ *
+ * plugin_log
+ * plugin_vlog : Use these functions to add information to the OpenVPN log file.
+ *               Messages will only be displayed if the plugin_name parameter
+ *               is set. PLOG_DEBUG messages will only be displayed with plug-in
+ *               debug log verbosity (at the time of writing that's verb >= 7).
+ *
+ * plugin_secure_memzero
+ *             : Use this function to securely wipe sensitive information from
+ *               memory.  This function is declared in a way that the compiler
+ *               will not remove these function calls during the compiler
+ *               optimization phase.
+ */
+struct openvpn_plugin_callbacks
+{
+    plugin_log_t plugin_log;
+    plugin_vlog_t plugin_vlog;
+    plugin_secure_memzero_t plugin_secure_memzero;
+    plugin_base64_encode_t plugin_base64_encode;
+    plugin_base64_decode_t plugin_base64_decode;
+};
+
+/**
+ * Used by the openvpn_plugin_open_v3() function to indicate to the
+ * plug-in what kind of SSL implementation OpenVPN uses.  This is
+ * to avoid SEGV issues when OpenVPN is complied against mbed TLS
+ * and the plug-in against OpenSSL.
+ */
+typedef enum {
+    SSLAPI_NONE,
+    SSLAPI_OPENSSL,
+    SSLAPI_MBEDTLS
+} ovpnSSLAPI;
+
+/**
+ * Arguments used to transport variables to the plug-in.
+ * The struct openvpn_plugin_args_open_in is only used
+ * by the openvpn_plugin_open_v3() function.
+ *
+ * STRUCT MEMBERS
+ *
+ * type_mask : Set by OpenVPN to the logical OR of all script
+ *             types which this version of OpenVPN supports.
+ *
+ * argv : a NULL-terminated array of options provided to the OpenVPN
+ *        "plug-in" directive.  argv[0] is the dynamic library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * callbacks : a pointer to the plug-in callback function struct.
+ *
+ */
+struct openvpn_plugin_args_open_in
+{
+    const int type_mask;
+    const char **const argv;
+    const char **const envp;
+    struct openvpn_plugin_callbacks *callbacks;
+    const ovpnSSLAPI ssl_api;
+    const char *ovpn_version;
+    const unsigned int ovpn_version_major;
+    const unsigned int ovpn_version_minor;
+    const char *const ovpn_version_patch;
+};
+
+
+/**
+ * Arguments used to transport variables from the plug-in back
+ * to the OpenVPN process.  The struct openvpn_plugin_args_open_return
+ * is only used by the openvpn_plugin_open_v3() function.
+ *
+ * STRUCT MEMBERS
+ *
+ * type_mask  : The plug-in should set this value to the logical OR of all script
+ *              types which the plug-in wants to intercept.  For example, if the
+ *              script wants to intercept the client-connect and client-disconnect
+ *              script types:
+ *
+ *              type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT)
+ *                         | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
+ *
+ * handle :     Pointer to a global plug-in context, created by the plug-in.  This pointer
+ *              is passed on to the other plug-in calls.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ */
+struct openvpn_plugin_args_open_return
+{
+    int type_mask;
+    openvpn_plugin_handle_t handle;
+    struct openvpn_plugin_string_list **return_list;
+};
+
+/**
+ * Arguments used to transport variables to and from the
+ * plug-in.  The struct openvpn_plugin_args_func is only used
+ * by the openvpn_plugin_func_v3() function.
+ *
+ * STRUCT MEMBERS:
+ *
+ * type : one of the PLUGIN_x types.
+ *
+ * argv : a NULL-terminated array of "command line" options which
+ *        would normally be passed to the script.  argv[0] is the dynamic
+ *        library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * handle : Pointer to a global plug-in context, created by the plug-in's openvpn_plugin_open_v3().
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ *
+ * current_cert_depth : Certificate depth of the certificate being passed over (only if compiled with ENABLE_CRYPTO defined)
+ *
+ * *current_cert : X509 Certificate object received from the client (only if compiled with ENABLE_CRYPTO defined)
+ *
+ */
+struct openvpn_plugin_args_func_in
+{
+    const int type;
+    const char **const argv;
+    const char **const envp;
+    openvpn_plugin_handle_t handle;
+    void *per_client_context;
+#ifdef ENABLE_CRYPTO
+    int current_cert_depth;
+    openvpn_x509_cert_t *current_cert;
+#else
+    int __current_cert_depth_disabled; /* Unused, for compatibility purposes only */
+    void *__current_cert_disabled; /* Unused, for compatibility purposes only */
+#endif
+};
+
+
+/**
+ * Arguments used to transport variables to and from the
+ * plug-in.  The struct openvpn_plugin_args_func is only used
+ * by the openvpn_plugin_func_v3() function.
+ *
+ * STRUCT MEMBERS:
+ *
+ * return_list : used to return data back to OpenVPN for further processing/usage by
+ *               the OpenVPN executable.
+ *
+ */
+struct openvpn_plugin_args_func_return
+{
+    struct openvpn_plugin_string_list **return_list;
+};
+
+/*
+ * Multiple plugin modules can be cascaded, and modules can be
+ * used in tandem with scripts.  The order of operation is that
+ * the module func() functions are called in the order that
+ * the modules were specified in the config file.  If a script
+ * was specified as well, it will be called last.  If the
+ * return code of the module/script controls an authentication
+ * function (such as tls-verify or auth-user-pass-verify), then
+ * every module and script must return success (0) in order for
+ * the connection to be authenticated.
+ *
+ * Notes:
+ *
+ * Plugins which use a privilege-separation model (by forking in
+ * their initialization function before the main OpenVPN process
+ * downgrades root privileges and/or executes a chroot) must
+ * daemonize after a fork if the "daemon" environmental variable is
+ * set.  In addition, if the "daemon_log_redirect" variable is set,
+ * the plugin should preserve stdout/stderr across the daemon()
+ * syscall.  See the daemonize() function in plugin/auth-pam/auth-pam.c
+ * for an example.
+ */
+
+/*
+ * Prototypes for functions which OpenVPN plug-ins must define.
+ */
+
+/*
+ * FUNCTION: openvpn_plugin_open_v2
+ *
+ * REQUIRED: YES
+ *
+ * Called on initial plug-in load.  OpenVPN will preserve plug-in state
+ * across SIGUSR1 restarts but not across SIGHUP restarts.  A SIGHUP reset
+ * will cause the plugin to be closed and reopened.
+ *
+ * ARGUMENTS
+ *
+ * *type_mask : Set by OpenVPN to the logical OR of all script
+ *              types which this version of OpenVPN supports.  The plug-in
+ *              should set this value to the logical OR of all script types
+ *              which the plug-in wants to intercept.  For example, if the
+ *              script wants to intercept the client-connect and
+ *              client-disconnect script types:
+ *
+ *              *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT)
+ *                         | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
+ *
+ * argv : a NULL-terminated array of options provided to the OpenVPN
+ *        "plug-in" directive.  argv[0] is the dynamic library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * An openvpn_plugin_handle_t value on success, NULL on failure
+ */
+OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v2)
+    (unsigned int *type_mask,
+    const char *argv[],
+    const char *envp[],
+    struct openvpn_plugin_string_list **return_list);
+
+/*
+ * FUNCTION: openvpn_plugin_func_v2
+ *
+ * Called to perform the work of a given script type.
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * type : one of the PLUGIN_x types
+ *
+ * argv : a NULL-terminated array of "command line" options which
+ *        would normally be passed to the script.  argv[0] is the dynamic
+ *        library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ *
+ * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY.  This enables asynchronous
+ * authentication where the plugin (or one of its agents) may indicate
+ * authentication success/failure some number of seconds after the return
+ * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
+ * char to the file named by auth_control_file in the environmental variable
+ * list (envp).
+ *
+ * first char of auth_control_file:
+ * '0' -- indicates auth failure
+ * '1' -- indicates auth success
+ *
+ * OpenVPN will delete the auth_control_file after it goes out of scope.
+ *
+ * If an OPENVPN_PLUGIN_ENABLE_PF handler is defined and returns success
+ * for a particular client instance, packet filtering will be enabled for that
+ * instance.  OpenVPN will then attempt to read the packet filter configuration
+ * from the temporary file named by the environmental variable pf_file.  This
+ * file may be generated asynchronously and may be dynamically updated during the
+ * client session, however the client will be blocked from sending or receiving
+ * VPN tunnel packets until the packet filter file has been generated.  OpenVPN
+ * will periodically test the packet filter file over the life of the client
+ * instance and reload when modified.  OpenVPN will delete the packet filter file
+ * when the client instance goes out of scope.
+ *
+ * Packet filter file grammar:
+ *
+ * [CLIENTS DROP|ACCEPT]
+ * {+|-}common_name1
+ * {+|-}common_name2
+ * . . .
+ * [SUBNETS DROP|ACCEPT]
+ * {+|-}subnet1
+ * {+|-}subnet2
+ * . . .
+ * [END]
+ *
+ * Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS
+ *
+ * CLIENTS refers to the set of clients (by their common-name) which
+ * this instance is allowed ('+') to connect to, or is excluded ('-')
+ * from connecting to.  Note that in the case of client-to-client
+ * connections, such communication must be allowed by the packet filter
+ * configuration files of both clients.
+ *
+ * SUBNETS refers to IP addresses or IP address subnets which this
+ * instance may connect to ('+') or is excluded ('-') from connecting
+ * to.
+ *
+ * DROP or ACCEPT defines default policy when there is no explicit match
+ * for a common-name or subnet.  The [END] tag must exist.  A special
+ * purpose tag called [KILL] will immediately kill the client instance.
+ * A given client or subnet rule applies to both incoming and outgoing
+ * packets.
+ *
+ * See plugin/defer/simple.c for an example on using asynchronous
+ * authentication and client-specific packet filtering.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2)
+    (openvpn_plugin_handle_t handle,
+    const int type,
+    const char *argv[],
+    const char *envp[],
+    void *per_client_context,
+    struct openvpn_plugin_string_list **return_list);
+
+
+/*
+ * FUNCTION: openvpn_plugin_open_v3
+ *
+ * REQUIRED: YES
+ *
+ * Called on initial plug-in load.  OpenVPN will preserve plug-in state
+ * across SIGUSR1 restarts but not across SIGHUP restarts.  A SIGHUP reset
+ * will cause the plugin to be closed and reopened.
+ *
+ * ARGUMENTS
+ *
+ * version : fixed value, defines the API version of the OpenVPN plug-in API.  The plug-in
+ *           should validate that this value is matching the OPENVPN_PLUGINv3_STRUCTVER
+ *           value.
+ *
+ * arguments : Structure with all arguments available to the plug-in.
+ *
+ * retptr :    used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
+    (const int version,
+    struct openvpn_plugin_args_open_in const *arguments,
+    struct openvpn_plugin_args_open_return *retptr);
+
+/*
+ * FUNCTION: openvpn_plugin_func_v3
+ *
+ * Called to perform the work of a given script type.
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * version : fixed value, defines the API version of the OpenVPN plug-in API.  The plug-in
+ *           should validate that this value is matching the OPENVPN_PLUGIN_VERSION value.
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ *
+ * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY.  This enables asynchronous
+ * authentication where the plugin (or one of its agents) may indicate
+ * authentication success/failure some number of seconds after the return
+ * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
+ * char to the file named by auth_control_file in the environmental variable
+ * list (envp).
+ *
+ * first char of auth_control_file:
+ * '0' -- indicates auth failure
+ * '1' -- indicates auth success
+ *
+ * OpenVPN will delete the auth_control_file after it goes out of scope.
+ *
+ * If an OPENVPN_PLUGIN_ENABLE_PF handler is defined and returns success
+ * for a particular client instance, packet filtering will be enabled for that
+ * instance.  OpenVPN will then attempt to read the packet filter configuration
+ * from the temporary file named by the environmental variable pf_file.  This
+ * file may be generated asynchronously and may be dynamically updated during the
+ * client session, however the client will be blocked from sending or receiving
+ * VPN tunnel packets until the packet filter file has been generated.  OpenVPN
+ * will periodically test the packet filter file over the life of the client
+ * instance and reload when modified.  OpenVPN will delete the packet filter file
+ * when the client instance goes out of scope.
+ *
+ * Packet filter file grammar:
+ *
+ * [CLIENTS DROP|ACCEPT]
+ * {+|-}common_name1
+ * {+|-}common_name2
+ * . . .
+ * [SUBNETS DROP|ACCEPT]
+ * {+|-}subnet1
+ * {+|-}subnet2
+ * . . .
+ * [END]
+ *
+ * Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS
+ *
+ * CLIENTS refers to the set of clients (by their common-name) which
+ * this instance is allowed ('+') to connect to, or is excluded ('-')
+ * from connecting to.  Note that in the case of client-to-client
+ * connections, such communication must be allowed by the packet filter
+ * configuration files of both clients.
+ *
+ * SUBNETS refers to IP addresses or IP address subnets which this
+ * instance may connect to ('+') or is excluded ('-') from connecting
+ * to.
+ *
+ * DROP or ACCEPT defines default policy when there is no explicit match
+ * for a common-name or subnet.  The [END] tag must exist.  A special
+ * purpose tag called [KILL] will immediately kill the client instance.
+ * A given client or subnet rule applies to both incoming and outgoing
+ * packets.
+ *
+ * See plugin/defer/simple.c for an example on using asynchronous
+ * authentication and client-specific packet filtering.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v3)
+    (const int version,
+    struct openvpn_plugin_args_func_in const *arguments,
+    struct openvpn_plugin_args_func_return *retptr);
+
+/*
+ * FUNCTION: openvpn_plugin_close_v1
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * Called immediately prior to plug-in unload.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_close_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_abort_v1
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * Called when OpenVPN is in the process of aborting due to a fatal error.
+ * Will only be called on an open context returned by a prior successful
+ * openvpn_plugin_open callback.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_abort_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_client_constructor_v1
+ *
+ * Called to allocate a per-client memory region, which
+ * is then passed to the openvpn_plugin_func_v2 function.
+ * This function is called every time the OpenVPN server
+ * constructs a client instance object, which normally
+ * occurs when a session-initiating packet is received
+ * by a new client, even before the client has authenticated.
+ *
+ * This function should allocate the private memory needed
+ * by the plugin to track individual OpenVPN clients, and
+ * return a void * to this memory region.
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * RETURN VALUE
+ *
+ * void * pointer to plugin's private per-client memory region, or NULL
+ * if no memory region is required.
+ */
+OPENVPN_PLUGIN_DEF void *OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_constructor_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_client_destructor_v1
+ *
+ * This function is called on client instance object destruction.
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_destructor_v1)
+    (openvpn_plugin_handle_t handle, void *per_client_context);
+
+/*
+ * FUNCTION: openvpn_plugin_select_initialization_point_v1
+ *
+ * Several different points exist in OpenVPN's initialization sequence where
+ * the openvpn_plugin_open function can be called.  While the default is
+ * OPENVPN_PLUGIN_INIT_PRE_DAEMON, this function can be used to select a
+ * different initialization point.  For example, if your plugin needs to
+ * return configuration parameters to OpenVPN, use
+ * OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE.
+ *
+ * REQUIRED: NO
+ *
+ * RETURN VALUE:
+ *
+ * An OPENVPN_PLUGIN_INIT_x value.
+ */
+#define OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE 1
+#define OPENVPN_PLUGIN_INIT_PRE_DAEMON       2 /* default */
+#define OPENVPN_PLUGIN_INIT_POST_DAEMON      3
+#define OPENVPN_PLUGIN_INIT_POST_UID_CHANGE  4
+
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_select_initialization_point_v1)
+    (void);
+
+/*
+ * FUNCTION: openvpn_plugin_min_version_required_v1
+ *
+ * This function is called by OpenVPN to query the minimum
+ * plugin interface version number required by the plugin.
+ *
+ * REQUIRED: NO
+ *
+ * RETURN VALUE
+ *
+ * The minimum OpenVPN plugin interface version number necessary to support
+ * this plugin.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_min_version_required_v1)
+    (void);
+
+/*
+ * Deprecated functions which are still supported for backward compatibility.
+ */
+
+OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v1)
+    (unsigned int *type_mask,
+    const char *argv[],
+    const char *envp[]);
+
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v1)
+    (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OPENVPN_PLUGIN_H_ */
diff --git a/include/openvpn-plugin.h.in b/include/openvpn-plugin.h.in
new file mode 100644
index 0000000..a604f1c
--- /dev/null
+++ b/include/openvpn-plugin.h.in
@@ -0,0 +1,878 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 OPENVPN_PLUGIN_H_
+#define OPENVPN_PLUGIN_H_
+
+#define OPENVPN_PLUGIN_VERSION 3
+
+#ifdef ENABLE_CRYPTO
+#ifdef ENABLE_CRYPTO_MBEDTLS
+#include <mbedtls/x509_crt.h>
+#ifndef __OPENVPN_X509_CERT_T_DECLARED
+#define __OPENVPN_X509_CERT_T_DECLARED
+typedef mbedtls_x509_crt openvpn_x509_cert_t;
+#endif
+#else  /* ifdef ENABLE_CRYPTO_MBEDTLS */
+#include <openssl/x509.h>
+#ifndef __OPENVPN_X509_CERT_T_DECLARED
+#define __OPENVPN_X509_CERT_T_DECLARED
+typedef X509 openvpn_x509_cert_t;
+#endif
+#endif
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Provide some basic version information to plug-ins at OpenVPN compile time
+ * This is will not be the complete version
+ */
+#define OPENVPN_VERSION_MAJOR @OPENVPN_VERSION_MAJOR@
+#define OPENVPN_VERSION_MINOR @OPENVPN_VERSION_MINOR@
+#define OPENVPN_VERSION_PATCH "@OPENVPN_VERSION_PATCH@"
+
+/*
+ * Plug-in types.  These types correspond to the set of script callbacks
+ * supported by OpenVPN.
+ *
+ * This is the general call sequence to expect when running in server mode:
+ *
+ * Initial Server Startup:
+ *
+ * FUNC: openvpn_plugin_open_v1
+ * FUNC: openvpn_plugin_client_constructor_v1 (this is the top-level "generic"
+ *                                             client template)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_UP
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ROUTE_UP
+ *
+ * New Client Connection:
+ *
+ * FUNC: openvpn_plugin_client_constructor_v1
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ENABLE_PF
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
+ *                                                     in the server chain)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_IPCHANGE
+ *
+ * [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
+ * we don't proceed until authentication is verified via auth_control_file]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_CLIENT_CONNECT_V2
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_LEARN_ADDRESS
+ *
+ * [Client session ensues]
+ *
+ * For each "TLS soft reset", according to reneg-sec option (or similar):
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_ENABLE_PF
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_VERIFY (called once for every cert
+ *                                                     in the server chain)
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_TLS_FINAL
+ *
+ * [If OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY returned OPENVPN_PLUGIN_FUNC_DEFERRED,
+ * we expect that authentication is verified via auth_control_file within
+ * the number of seconds defined by the "hand-window" option.  Data channel traffic
+ * will continue to flow uninterrupted during this period.]
+ *
+ * [Client session continues]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_CLIENT_DISCONNECT
+ * FUNC: openvpn_plugin_client_destructor_v1
+ *
+ * [ some time may pass ]
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_LEARN_ADDRESS (this coincides with a
+ *                                                            lazy free of initial
+ *                                                            learned addr object)
+ * Server Shutdown:
+ *
+ * FUNC: openvpn_plugin_func_v1 OPENVPN_PLUGIN_DOWN
+ * FUNC: openvpn_plugin_client_destructor_v1 (top-level "generic" client)
+ * FUNC: openvpn_plugin_close_v1
+ */
+#define OPENVPN_PLUGIN_UP                    0
+#define OPENVPN_PLUGIN_DOWN                  1
+#define OPENVPN_PLUGIN_ROUTE_UP              2
+#define OPENVPN_PLUGIN_IPCHANGE              3
+#define OPENVPN_PLUGIN_TLS_VERIFY            4
+#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5
+#define OPENVPN_PLUGIN_CLIENT_CONNECT        6
+#define OPENVPN_PLUGIN_CLIENT_DISCONNECT     7
+#define OPENVPN_PLUGIN_LEARN_ADDRESS         8
+#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2     9
+#define OPENVPN_PLUGIN_TLS_FINAL             10
+#define OPENVPN_PLUGIN_ENABLE_PF             11
+#define OPENVPN_PLUGIN_ROUTE_PREDOWN         12
+#define OPENVPN_PLUGIN_N                     13
+
+/*
+ * Build a mask out of a set of plug-in types.
+ */
+#define OPENVPN_PLUGIN_MASK(x) (1<<(x))
+
+/*
+ * A pointer to a plugin-defined object which contains
+ * the object state.
+ */
+typedef void *openvpn_plugin_handle_t;
+
+/*
+ * Return value for openvpn_plugin_func_v1 function
+ */
+#define OPENVPN_PLUGIN_FUNC_SUCCESS  0
+#define OPENVPN_PLUGIN_FUNC_ERROR    1
+#define OPENVPN_PLUGIN_FUNC_DEFERRED 2
+
+/*
+ * For Windows (needs to be modified for MSVC)
+ */
+#if defined(_WIN32) && !defined(OPENVPN_PLUGIN_H)
+#define OPENVPN_EXPORT __declspec(dllexport)
+#else
+#define OPENVPN_EXPORT
+#endif
+
+/*
+ * If OPENVPN_PLUGIN_H is defined, we know that we are being
+ * included in an OpenVPN compile, rather than a plugin compile.
+ */
+#ifdef OPENVPN_PLUGIN_H
+
+/*
+ * We are compiling OpenVPN.
+ */
+#define OPENVPN_PLUGIN_DEF        typedef
+#define OPENVPN_PLUGIN_FUNC(name) (*name)
+
+#else  /* ifdef OPENVPN_PLUGIN_H */
+
+/*
+ * We are compiling plugin.
+ */
+#define OPENVPN_PLUGIN_DEF        OPENVPN_EXPORT
+#define OPENVPN_PLUGIN_FUNC(name) name
+
+#endif
+
+/*
+ * Used by openvpn_plugin_func to return structured
+ * data.  The plugin should allocate all structure
+ * instances, name strings, and value strings with
+ * malloc, since OpenVPN will assume that it
+ * can free the list by calling free() over the same.
+ */
+struct openvpn_plugin_string_list
+{
+    struct openvpn_plugin_string_list *next;
+    char *name;
+    char *value;
+};
+
+
+/* openvpn_plugin_{open,func}_v3() related structs */
+
+/**
+ * Defines version of the v3 plugin argument structs
+ *
+ * Whenever one or more of these structs are modified, this constant
+ * must be updated.  A changelog should be appended in this comment
+ * as well, to make it easier to see what information is available
+ * in the different versions.
+ *
+ * Version   Comment
+ *    1      Initial plugin v3 structures providing the same API as
+ *           the v2 plugin interface, X509 certificate information +
+ *           a logging API for plug-ins.
+ *
+ *    2      Added ssl_api member in struct openvpn_plugin_args_open_in
+ *           which identifies the SSL implementation OpenVPN is compiled
+ *           against.
+ *
+ *    3      Added ovpn_version, ovpn_version_major, ovpn_version_minor
+ *           and ovpn_version_patch to provide the runtime version of
+ *           OpenVPN to plug-ins.
+ *
+ *    4      Exported secure_memzero() as plugin_secure_memzero()
+ *
+ *    5      Exported openvpn_base64_encode() as plugin_base64_encode()
+ *           Exported openvpn_base64_decode() as plugin_base64_decode()
+ */
+#define OPENVPN_PLUGINv3_STRUCTVER 5
+
+/**
+ * Definitions needed for the plug-in callback functions.
+ */
+typedef enum
+{
+    PLOG_ERR    = (1 << 0),/* Error condition message */
+    PLOG_WARN   = (1 << 1),/* General warning message */
+    PLOG_NOTE   = (1 << 2),/* Informational message */
+    PLOG_DEBUG  = (1 << 3),/* Debug message, displayed if verb >= 7 */
+
+    PLOG_ERRNO  = (1 << 8),/* Add error description to message */
+    PLOG_NOMUTE = (1 << 9), /* Mute setting does not apply for message */
+
+} openvpn_plugin_log_flags_t;
+
+
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+#define _ovpn_chk_fmt(a, b) __attribute__ ((format(gnu_printf, (a), (b))))
+#else
+#define _ovpn_chk_fmt(a, b) __attribute__ ((format(__printf__, (a), (b))))
+#endif
+#else  /* ifdef __GNUC__ */
+#define _ovpn_chk_fmt(a, b)
+#endif
+
+typedef void (*plugin_log_t)(openvpn_plugin_log_flags_t flags,
+                             const char *plugin_name,
+                             const char *format, ...) _ovpn_chk_fmt (3, 4);
+
+typedef void (*plugin_vlog_t)(openvpn_plugin_log_flags_t flags,
+                              const char *plugin_name,
+                              const char *format,
+                              va_list arglist) _ovpn_chk_fmt (3, 0);
+#undef _ovpn_chk_fmt
+
+/**
+ *  Export of secure_memzero() to be used inside plug-ins
+ *
+ *  @param data   Pointer to data to zeroise
+ *  @param len    Length of data, in bytes
+ *
+ */
+typedef void (*plugin_secure_memzero_t)(void *data, size_t len);
+
+/**
+ *  Export of openvpn_base64_encode() to be used inside plug-ins
+ *
+ *  @param data   Pointer to data to BASE64 encode
+ *  @param size   Length of data, in bytes
+ *  @param *str   Pointer to the return buffer.  This needed memory is
+ *                allocated by openvpn_base64_encode() and needs to be free()d
+ *                after use.
+ *
+ *  @return int   Returns the length of the buffer created, or -1 on error.
+ *
+ */
+typedef int (*plugin_base64_encode_t)(const void *data, int size, char **str);
+
+/**
+ *  Export of openvpn_base64_decode() to be used inside plug-ins
+ *
+ *  @param str    Pointer to the BASE64 encoded data
+ *  @param data   Pointer to the buffer where save the decoded data
+ *  @param size   Size of the destination buffer
+ *
+ *  @return int   Returns the length of the decoded data, or -1 on error or
+ *                if the destination buffer is too small.
+ *
+ */
+typedef int (*plugin_base64_decode_t)(const char *str, void *data, int size);
+
+
+/**
+ * Used by the openvpn_plugin_open_v3() function to pass callback
+ * function pointers to the plug-in.
+ *
+ * plugin_log
+ * plugin_vlog : Use these functions to add information to the OpenVPN log file.
+ *               Messages will only be displayed if the plugin_name parameter
+ *               is set. PLOG_DEBUG messages will only be displayed with plug-in
+ *               debug log verbosity (at the time of writing that's verb >= 7).
+ *
+ * plugin_secure_memzero
+ *             : Use this function to securely wipe sensitive information from
+ *               memory.  This function is declared in a way that the compiler
+ *               will not remove these function calls during the compiler
+ *               optimization phase.
+ */
+struct openvpn_plugin_callbacks
+{
+    plugin_log_t plugin_log;
+    plugin_vlog_t plugin_vlog;
+    plugin_secure_memzero_t plugin_secure_memzero;
+    plugin_base64_encode_t plugin_base64_encode;
+    plugin_base64_decode_t plugin_base64_decode;
+};
+
+/**
+ * Used by the openvpn_plugin_open_v3() function to indicate to the
+ * plug-in what kind of SSL implementation OpenVPN uses.  This is
+ * to avoid SEGV issues when OpenVPN is complied against mbed TLS
+ * and the plug-in against OpenSSL.
+ */
+typedef enum {
+    SSLAPI_NONE,
+    SSLAPI_OPENSSL,
+    SSLAPI_MBEDTLS
+} ovpnSSLAPI;
+
+/**
+ * Arguments used to transport variables to the plug-in.
+ * The struct openvpn_plugin_args_open_in is only used
+ * by the openvpn_plugin_open_v3() function.
+ *
+ * STRUCT MEMBERS
+ *
+ * type_mask : Set by OpenVPN to the logical OR of all script
+ *             types which this version of OpenVPN supports.
+ *
+ * argv : a NULL-terminated array of options provided to the OpenVPN
+ *        "plug-in" directive.  argv[0] is the dynamic library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * callbacks : a pointer to the plug-in callback function struct.
+ *
+ */
+struct openvpn_plugin_args_open_in
+{
+    const int type_mask;
+    const char **const argv;
+    const char **const envp;
+    struct openvpn_plugin_callbacks *callbacks;
+    const ovpnSSLAPI ssl_api;
+    const char *ovpn_version;
+    const unsigned int ovpn_version_major;
+    const unsigned int ovpn_version_minor;
+    const char *const ovpn_version_patch;
+};
+
+
+/**
+ * Arguments used to transport variables from the plug-in back
+ * to the OpenVPN process.  The struct openvpn_plugin_args_open_return
+ * is only used by the openvpn_plugin_open_v3() function.
+ *
+ * STRUCT MEMBERS
+ *
+ * type_mask  : The plug-in should set this value to the logical OR of all script
+ *              types which the plug-in wants to intercept.  For example, if the
+ *              script wants to intercept the client-connect and client-disconnect
+ *              script types:
+ *
+ *              type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT)
+ *                         | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
+ *
+ * handle :     Pointer to a global plug-in context, created by the plug-in.  This pointer
+ *              is passed on to the other plug-in calls.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ */
+struct openvpn_plugin_args_open_return
+{
+    int type_mask;
+    openvpn_plugin_handle_t handle;
+    struct openvpn_plugin_string_list **return_list;
+};
+
+/**
+ * Arguments used to transport variables to and from the
+ * plug-in.  The struct openvpn_plugin_args_func is only used
+ * by the openvpn_plugin_func_v3() function.
+ *
+ * STRUCT MEMBERS:
+ *
+ * type : one of the PLUGIN_x types.
+ *
+ * argv : a NULL-terminated array of "command line" options which
+ *        would normally be passed to the script.  argv[0] is the dynamic
+ *        library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * handle : Pointer to a global plug-in context, created by the plug-in's openvpn_plugin_open_v3().
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ *
+ * current_cert_depth : Certificate depth of the certificate being passed over (only if compiled with ENABLE_CRYPTO defined)
+ *
+ * *current_cert : X509 Certificate object received from the client (only if compiled with ENABLE_CRYPTO defined)
+ *
+ */
+struct openvpn_plugin_args_func_in
+{
+    const int type;
+    const char **const argv;
+    const char **const envp;
+    openvpn_plugin_handle_t handle;
+    void *per_client_context;
+#ifdef ENABLE_CRYPTO
+    int current_cert_depth;
+    openvpn_x509_cert_t *current_cert;
+#else
+    int __current_cert_depth_disabled; /* Unused, for compatibility purposes only */
+    void *__current_cert_disabled; /* Unused, for compatibility purposes only */
+#endif
+};
+
+
+/**
+ * Arguments used to transport variables to and from the
+ * plug-in.  The struct openvpn_plugin_args_func is only used
+ * by the openvpn_plugin_func_v3() function.
+ *
+ * STRUCT MEMBERS:
+ *
+ * return_list : used to return data back to OpenVPN for further processing/usage by
+ *               the OpenVPN executable.
+ *
+ */
+struct openvpn_plugin_args_func_return
+{
+    struct openvpn_plugin_string_list **return_list;
+};
+
+/*
+ * Multiple plugin modules can be cascaded, and modules can be
+ * used in tandem with scripts.  The order of operation is that
+ * the module func() functions are called in the order that
+ * the modules were specified in the config file.  If a script
+ * was specified as well, it will be called last.  If the
+ * return code of the module/script controls an authentication
+ * function (such as tls-verify or auth-user-pass-verify), then
+ * every module and script must return success (0) in order for
+ * the connection to be authenticated.
+ *
+ * Notes:
+ *
+ * Plugins which use a privilege-separation model (by forking in
+ * their initialization function before the main OpenVPN process
+ * downgrades root privileges and/or executes a chroot) must
+ * daemonize after a fork if the "daemon" environmental variable is
+ * set.  In addition, if the "daemon_log_redirect" variable is set,
+ * the plugin should preserve stdout/stderr across the daemon()
+ * syscall.  See the daemonize() function in plugin/auth-pam/auth-pam.c
+ * for an example.
+ */
+
+/*
+ * Prototypes for functions which OpenVPN plug-ins must define.
+ */
+
+/*
+ * FUNCTION: openvpn_plugin_open_v2
+ *
+ * REQUIRED: YES
+ *
+ * Called on initial plug-in load.  OpenVPN will preserve plug-in state
+ * across SIGUSR1 restarts but not across SIGHUP restarts.  A SIGHUP reset
+ * will cause the plugin to be closed and reopened.
+ *
+ * ARGUMENTS
+ *
+ * *type_mask : Set by OpenVPN to the logical OR of all script
+ *              types which this version of OpenVPN supports.  The plug-in
+ *              should set this value to the logical OR of all script types
+ *              which the plug-in wants to intercept.  For example, if the
+ *              script wants to intercept the client-connect and
+ *              client-disconnect script types:
+ *
+ *              *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_CONNECT)
+ *                         | OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_CLIENT_DISCONNECT)
+ *
+ * argv : a NULL-terminated array of options provided to the OpenVPN
+ *        "plug-in" directive.  argv[0] is the dynamic library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * An openvpn_plugin_handle_t value on success, NULL on failure
+ */
+OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v2)
+    (unsigned int *type_mask,
+    const char *argv[],
+    const char *envp[],
+    struct openvpn_plugin_string_list **return_list);
+
+/*
+ * FUNCTION: openvpn_plugin_func_v2
+ *
+ * Called to perform the work of a given script type.
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * type : one of the PLUGIN_x types
+ *
+ * argv : a NULL-terminated array of "command line" options which
+ *        would normally be passed to the script.  argv[0] is the dynamic
+ *        library pathname.
+ *
+ * envp : a NULL-terminated array of OpenVPN-set environmental
+ *        variables in "name=value" format.  Note that for security reasons,
+ *        these variables are not actually written to the "official"
+ *        environmental variable store of the process.
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ *
+ * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY.  This enables asynchronous
+ * authentication where the plugin (or one of its agents) may indicate
+ * authentication success/failure some number of seconds after the return
+ * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
+ * char to the file named by auth_control_file in the environmental variable
+ * list (envp).
+ *
+ * first char of auth_control_file:
+ * '0' -- indicates auth failure
+ * '1' -- indicates auth success
+ *
+ * OpenVPN will delete the auth_control_file after it goes out of scope.
+ *
+ * If an OPENVPN_PLUGIN_ENABLE_PF handler is defined and returns success
+ * for a particular client instance, packet filtering will be enabled for that
+ * instance.  OpenVPN will then attempt to read the packet filter configuration
+ * from the temporary file named by the environmental variable pf_file.  This
+ * file may be generated asynchronously and may be dynamically updated during the
+ * client session, however the client will be blocked from sending or receiving
+ * VPN tunnel packets until the packet filter file has been generated.  OpenVPN
+ * will periodically test the packet filter file over the life of the client
+ * instance and reload when modified.  OpenVPN will delete the packet filter file
+ * when the client instance goes out of scope.
+ *
+ * Packet filter file grammar:
+ *
+ * [CLIENTS DROP|ACCEPT]
+ * {+|-}common_name1
+ * {+|-}common_name2
+ * . . .
+ * [SUBNETS DROP|ACCEPT]
+ * {+|-}subnet1
+ * {+|-}subnet2
+ * . . .
+ * [END]
+ *
+ * Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS
+ *
+ * CLIENTS refers to the set of clients (by their common-name) which
+ * this instance is allowed ('+') to connect to, or is excluded ('-')
+ * from connecting to.  Note that in the case of client-to-client
+ * connections, such communication must be allowed by the packet filter
+ * configuration files of both clients.
+ *
+ * SUBNETS refers to IP addresses or IP address subnets which this
+ * instance may connect to ('+') or is excluded ('-') from connecting
+ * to.
+ *
+ * DROP or ACCEPT defines default policy when there is no explicit match
+ * for a common-name or subnet.  The [END] tag must exist.  A special
+ * purpose tag called [KILL] will immediately kill the client instance.
+ * A given client or subnet rule applies to both incoming and outgoing
+ * packets.
+ *
+ * See plugin/defer/simple.c for an example on using asynchronous
+ * authentication and client-specific packet filtering.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v2)
+    (openvpn_plugin_handle_t handle,
+    const int type,
+    const char *argv[],
+    const char *envp[],
+    void *per_client_context,
+    struct openvpn_plugin_string_list **return_list);
+
+
+/*
+ * FUNCTION: openvpn_plugin_open_v3
+ *
+ * REQUIRED: YES
+ *
+ * Called on initial plug-in load.  OpenVPN will preserve plug-in state
+ * across SIGUSR1 restarts but not across SIGHUP restarts.  A SIGHUP reset
+ * will cause the plugin to be closed and reopened.
+ *
+ * ARGUMENTS
+ *
+ * version : fixed value, defines the API version of the OpenVPN plug-in API.  The plug-in
+ *           should validate that this value is matching the OPENVPN_PLUGINv3_STRUCTVER
+ *           value.
+ *
+ * arguments : Structure with all arguments available to the plug-in.
+ *
+ * retptr :    used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v3)
+    (const int version,
+    struct openvpn_plugin_args_open_in const *arguments,
+    struct openvpn_plugin_args_open_return *retptr);
+
+/*
+ * FUNCTION: openvpn_plugin_func_v3
+ *
+ * Called to perform the work of a given script type.
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * version : fixed value, defines the API version of the OpenVPN plug-in API.  The plug-in
+ *           should validate that this value is matching the OPENVPN_PLUGIN_VERSION value.
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * return_list : used to return data back to OpenVPN.
+ *
+ * RETURN VALUE
+ *
+ * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure
+ *
+ * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by
+ * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY.  This enables asynchronous
+ * authentication where the plugin (or one of its agents) may indicate
+ * authentication success/failure some number of seconds after the return
+ * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single
+ * char to the file named by auth_control_file in the environmental variable
+ * list (envp).
+ *
+ * first char of auth_control_file:
+ * '0' -- indicates auth failure
+ * '1' -- indicates auth success
+ *
+ * OpenVPN will delete the auth_control_file after it goes out of scope.
+ *
+ * If an OPENVPN_PLUGIN_ENABLE_PF handler is defined and returns success
+ * for a particular client instance, packet filtering will be enabled for that
+ * instance.  OpenVPN will then attempt to read the packet filter configuration
+ * from the temporary file named by the environmental variable pf_file.  This
+ * file may be generated asynchronously and may be dynamically updated during the
+ * client session, however the client will be blocked from sending or receiving
+ * VPN tunnel packets until the packet filter file has been generated.  OpenVPN
+ * will periodically test the packet filter file over the life of the client
+ * instance and reload when modified.  OpenVPN will delete the packet filter file
+ * when the client instance goes out of scope.
+ *
+ * Packet filter file grammar:
+ *
+ * [CLIENTS DROP|ACCEPT]
+ * {+|-}common_name1
+ * {+|-}common_name2
+ * . . .
+ * [SUBNETS DROP|ACCEPT]
+ * {+|-}subnet1
+ * {+|-}subnet2
+ * . . .
+ * [END]
+ *
+ * Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS
+ *
+ * CLIENTS refers to the set of clients (by their common-name) which
+ * this instance is allowed ('+') to connect to, or is excluded ('-')
+ * from connecting to.  Note that in the case of client-to-client
+ * connections, such communication must be allowed by the packet filter
+ * configuration files of both clients.
+ *
+ * SUBNETS refers to IP addresses or IP address subnets which this
+ * instance may connect to ('+') or is excluded ('-') from connecting
+ * to.
+ *
+ * DROP or ACCEPT defines default policy when there is no explicit match
+ * for a common-name or subnet.  The [END] tag must exist.  A special
+ * purpose tag called [KILL] will immediately kill the client instance.
+ * A given client or subnet rule applies to both incoming and outgoing
+ * packets.
+ *
+ * See plugin/defer/simple.c for an example on using asynchronous
+ * authentication and client-specific packet filtering.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v3)
+    (const int version,
+    struct openvpn_plugin_args_func_in const *arguments,
+    struct openvpn_plugin_args_func_return *retptr);
+
+/*
+ * FUNCTION: openvpn_plugin_close_v1
+ *
+ * REQUIRED: YES
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * Called immediately prior to plug-in unload.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_close_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_abort_v1
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * Called when OpenVPN is in the process of aborting due to a fatal error.
+ * Will only be called on an open context returned by a prior successful
+ * openvpn_plugin_open callback.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_abort_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_client_constructor_v1
+ *
+ * Called to allocate a per-client memory region, which
+ * is then passed to the openvpn_plugin_func_v2 function.
+ * This function is called every time the OpenVPN server
+ * constructs a client instance object, which normally
+ * occurs when a session-initiating packet is received
+ * by a new client, even before the client has authenticated.
+ *
+ * This function should allocate the private memory needed
+ * by the plugin to track individual OpenVPN clients, and
+ * return a void * to this memory region.
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * RETURN VALUE
+ *
+ * void * pointer to plugin's private per-client memory region, or NULL
+ * if no memory region is required.
+ */
+OPENVPN_PLUGIN_DEF void *OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_constructor_v1)
+    (openvpn_plugin_handle_t handle);
+
+/*
+ * FUNCTION: openvpn_plugin_client_destructor_v1
+ *
+ * This function is called on client instance object destruction.
+ *
+ * REQUIRED: NO
+ *
+ * ARGUMENTS
+ *
+ * handle : the openvpn_plugin_handle_t value which was returned by
+ *          openvpn_plugin_open.
+ *
+ * per_client_context : the per-client context pointer which was returned by
+ *        openvpn_plugin_client_constructor_v1, if defined.
+ */
+OPENVPN_PLUGIN_DEF void OPENVPN_PLUGIN_FUNC(openvpn_plugin_client_destructor_v1)
+    (openvpn_plugin_handle_t handle, void *per_client_context);
+
+/*
+ * FUNCTION: openvpn_plugin_select_initialization_point_v1
+ *
+ * Several different points exist in OpenVPN's initialization sequence where
+ * the openvpn_plugin_open function can be called.  While the default is
+ * OPENVPN_PLUGIN_INIT_PRE_DAEMON, this function can be used to select a
+ * different initialization point.  For example, if your plugin needs to
+ * return configuration parameters to OpenVPN, use
+ * OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE.
+ *
+ * REQUIRED: NO
+ *
+ * RETURN VALUE:
+ *
+ * An OPENVPN_PLUGIN_INIT_x value.
+ */
+#define OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE 1
+#define OPENVPN_PLUGIN_INIT_PRE_DAEMON       2 /* default */
+#define OPENVPN_PLUGIN_INIT_POST_DAEMON      3
+#define OPENVPN_PLUGIN_INIT_POST_UID_CHANGE  4
+
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_select_initialization_point_v1)
+    (void);
+
+/*
+ * FUNCTION: openvpn_plugin_min_version_required_v1
+ *
+ * This function is called by OpenVPN to query the minimum
+ * plugin interface version number required by the plugin.
+ *
+ * REQUIRED: NO
+ *
+ * RETURN VALUE
+ *
+ * The minimum OpenVPN plugin interface version number necessary to support
+ * this plugin.
+ */
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_min_version_required_v1)
+    (void);
+
+/*
+ * Deprecated functions which are still supported for backward compatibility.
+ */
+
+OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_open_v1)
+    (unsigned int *type_mask,
+    const char *argv[],
+    const char *envp[]);
+
+OPENVPN_PLUGIN_DEF int OPENVPN_PLUGIN_FUNC(openvpn_plugin_func_v1)
+    (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OPENVPN_PLUGIN_H_ */
diff --git a/ltrc.inc b/ltrc.inc
new file mode 100644
index 0000000..701f200
--- /dev/null
+++ b/ltrc.inc
@@ -0,0 +1,23 @@
+#
+#  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
+
+RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS)
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
+
+.rc.lo:
+	$(LTRCCOMPILE) -i "$<" -o "$@"
+
+.rc.o:
+	$(RCCOMPILE) -i "$<" -o "$@"
+
+.mc.rc:
+	$(WINDMC) "$<"
diff --git a/m4/ax_emptyarray.m4 b/m4/ax_emptyarray.m4
new file mode 100644
index 0000000..c6781c1
--- /dev/null
+++ b/m4/ax_emptyarray.m4
@@ -0,0 +1,40 @@
+dnl @synopsis AX_EMPTY_ARRAY
+dnl
+dnl Define EMPTY_ARRAY_SIZE to be either "0"
+dnl or "" depending on which syntax the compiler
+dnl prefers for empty arrays in structs.
+dnl
+dnl @version
+dnl @author James Yonan <jim@yonan.net>
+AC_DEFUN([AX_EMPTY_ARRAY], [
+	AS_VAR_PUSHDEF([VAR],[ax_cv_c_empty_array])dnl
+	AC_CACHE_CHECK(
+		[for C compiler empty array size],
+		[VAR],
+		[AC_COMPILE_IFELSE(
+			[AC_LANG_PROGRAM(
+				,
+				[[
+struct { int foo; int bar[0]; } mystruct;
+				]]
+			)],
+			[VAR=0],
+			[AC_COMPILE_IFELSE(
+				[AC_LANG_PROGRAM(
+					,
+					[[
+struct { int foo; int bar[]; } mystruct;
+					]]
+				)],
+				[VAR=],
+				[AC_MSG_ERROR([C compiler is unable to creaty empty arrays])]
+			)]
+		)]
+	)dnl
+	AC_DEFINE_UNQUOTED(
+		[EMPTY_ARRAY_SIZE],
+		[$VAR],
+		[Dimension to use for empty array declaration]
+	)dnl
+	AS_VAR_POPDEF([VAR])dnl
+])
diff --git a/m4/ax_socklen_t.m4 b/m4/ax_socklen_t.m4
new file mode 100644
index 0000000..b420a17
--- /dev/null
+++ b/m4/ax_socklen_t.m4
@@ -0,0 +1,65 @@
+dnl -- The following is base of curl's acinclude.m4 --
+dnl Check for socklen_t: historically on BSD it is an int, and in
+dnl POSIX 1g it is a type of its own, but some platforms use different
+dnl types for the argument to getsockopt, getpeername, etc.  So we
+dnl have to test to find something that will work.
+AC_DEFUN([AX_TYPE_SOCKLEN_T], [
+	AC_CHECK_TYPE(
+		[socklen_t],
+		,
+		[
+			AS_VAR_PUSHDEF([VAR],[ax_cv_socklen_t_equiv])dnl
+			AC_CACHE_CHECK(
+				[for socklen_t equivalent],
+				[VAR],
+				[
+					#AS_CASE is not supported on <autoconf-2.60
+					case "${host}" in
+					*-mingw*) VAR=int ;;
+					*)
+						# Systems have either "struct sockaddr *" or
+						# "void *" as the second argument to getpeername
+						for arg2 in "struct sockaddr" void; do
+							for t in int size_t unsigned long "unsigned long"; do
+								AC_COMPILE_IFELSE(
+									[AC_LANG_PROGRAM(
+										[[
+#include <sys/types.h>
+#include <sys/socket.h>
+int getpeername (int, $arg2 *, $t *);
+										]],
+										[[
+$t len;
+getpeername(0,0,&len);
+										]]
+									)],
+									[VAR="$t"; break]
+								)
+							done
+							test -n "$VAR" && break
+						done
+						;;
+					esac
+				]
+				AS_VAR_IF(
+					[VAR],
+					[],
+					[AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])],
+					[AC_DEFINE_UNQUOTED(
+						[socklen_t],
+						[$VAR],
+						[type to use in place of socklen_t if not defined]
+					)]
+				)
+			)
+		],
+		[[
+#include <sys/types.h>
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#endif
+		]]
+	)
+])
diff --git a/m4/ax_varargs.m4 b/m4/ax_varargs.m4
new file mode 100644
index 0000000..c295d21
--- /dev/null
+++ b/m4/ax_varargs.m4
@@ -0,0 +1,77 @@
+dnl @synopsis AX_CPP_VARARG_MACRO_GCC
+dnl
+dnl Test if the preprocessor understands GNU GCC-style vararg macros.
+dnl If it does, defines HAVE_CPP_VARARG_MACRO_GCC to 1.
+dnl
+dnl @version
+dnl @author James Yonan <jim@yonan.net>, Matthias Andree <matthias.andree@web.de>
+AC_DEFUN([AX_CPP_VARARG_MACRO_GCC], [dnl
+	AS_VAR_PUSHDEF([VAR], [ax_cv_cpp_vararg_macro_gcc])dnl
+	AC_CACHE_CHECK(
+		[for GNU GCC vararg macro support],
+		[VAR],
+		[AC_COMPILE_IFELSE(
+			[AC_LANG_PROGRAM(
+				[[
+#define macro(a, b...) func(a, b)
+int func(int a, int b, int c);
+				]],
+				[[
+int i = macro(1, 2, 3);
+				]]
+			)],
+			[VAR=yes],
+			[VAR=no]
+		)]
+	)dnl
+
+	AS_VAR_IF(
+		[VAR],
+		[yes],
+		[AC_DEFINE(
+			[HAVE_CPP_VARARG_MACRO_GCC],
+			[1], 
+			[Define to 1 if your compiler supports GNU GCC-style variadic macros]
+		)]
+	)dnl
+	AS_VAR_POPDEF([VAR])dnl
+])
+
+dnl @synopsis AX_CPP_VARARG_MACRO_ISO
+dnl
+dnl Test if the preprocessor understands ISO C 1999 vararg macros.
+dnl If it does, defines HAVE_CPP_VARARG_MACRO_ISO to 1.
+dnl
+dnl @version
+dnl @author James Yonan <jim@yonan.net>, Matthias Andree <matthias.andree@web.de>
+AC_DEFUN([AX_CPP_VARARG_MACRO_ISO], [dnl
+	AS_VAR_PUSHDEF([VAR],[ax_cv_cpp_vararg_macro_iso])dnl
+	AC_CACHE_CHECK(
+		[for ISO C 1999 vararg macro support],
+		[VAR],
+		[AC_COMPILE_IFELSE(
+			[AC_LANG_PROGRAM(
+				[[
+#define macro(a, ...) func(a, __VA_ARGS__)
+int func(int a, int b, int c);
+				]],
+				[[
+int i = macro(1, 2, 3);
+				]]
+			)],
+			[VAR=yes],
+			[VAR=no]
+		)]
+	)dnl
+
+	AS_VAR_IF(
+		[VAR],
+		[yes],
+		[AC_DEFINE(
+			[HAVE_CPP_VARARG_MACRO_ISO],
+			[1], 
+			[Define to 1 if your compiler supports ISO C99 variadic macros]
+		)]
+	)dnl
+	AS_VAR_POPDEF([VAR])dnl
+])
diff --git a/m4/pkg.m4 b/m4/pkg.m4
new file mode 100644
index 0000000..12d2a58
--- /dev/null
+++ b/m4/pkg.m4
@@ -0,0 +1,159 @@
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+# 
+# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+	AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+	_pkg_min_version=m4_default([$1], [0.9.0])
+	AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+	if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+		PKG_CONFIG=""
+	fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes ],
+		     [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+   	AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else 
+	        $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+	m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+     	AC_MSG_RESULT([no])
+	m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+	$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+	$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+	$3
+fi[]dnl
+])# PKG_CHECK_MODULES
diff --git a/patches/add_missing_licenses.patch b/patches/add_missing_licenses.patch
new file mode 100644
index 0000000..390ac29
--- /dev/null
+++ b/patches/add_missing_licenses.patch
@@ -0,0 +1,467 @@
+Index: openvpn/LICENSE
+===================================================================
+--- openvpn.orig/LICENSE
++++ openvpn/LICENSE
+@@ -566,3 +566,462 @@ Public License instead of this License.
+ 
+ 
+ END COPYRIGHT.GPL
++
++------------------
++
++BSD 3-Clause License:
++-----------------
++
++/*
++ * Redistribution and use in source and binary forms, with or without modifi-
++ * cation, are permitted provided that the following conditions are met:
++ *
++ *   o  Redistributions of source code must retain the above copyright notice,
++ *      this list of conditions and the following disclaimer.
++ *
++ *   o  Redistributions in binary form must reproduce the above copyright no-
++ *      tice, this list of conditions and the following disclaimer in the do-
++ *      cumentation and/or other materials provided with the distribution.
++ *
++ *   o  The names of the contributors may not be used to endorse or promote
++ *      products derived from this software without specific prior written
++ *      permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI-
++ * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN-
++ * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV-
++ * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI-
++ * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
++ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++------------------
++src/compat/compat-lz4.c
++
++/*
++   LZ4 - Fast LZ compression algorithm
++   Copyright (C) 2011-present, Yann Collet.
++
++   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
++
++   Redistribution and use in source and binary forms, with or without
++   modification, are permitted provided that the following conditions are
++   met:
++
++       * Redistributions of source code must retain the above copyright
++   notice, this list of conditions and the following disclaimer.
++       * Redistributions in binary form must reproduce the above
++   copyright notice, this list of conditions and the following disclaimer
++   in the documentation and/or other materials provided with the
++   distribution.
++
++   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++   You can contact the author at :
++    - LZ4 homepage : http://www.lz4.org
++    - LZ4 source repository : https://github.com/lz4/lz4
++*/
++
++------------------
++m4/pkg.m4
++
++# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License along
++# with this program; if not, write to the Free Software Foundation, Inc.,
++# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
++#
++# As a special exception to the GNU General Public License, if you
++# distribute this file as part of a program that contains a
++# configuration script generated by Autoconf, you may include it under
++# the same distribution terms that you use for the rest of that program.
++
++       GNU General Public License v2.0 w/Autoconf exception
++                    GNU GENERAL PUBLIC LICENSE
++                       Version 2, June 1991
++
++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ Everyone is permitted to copy and distribute verbatim copies
++ of this license document, but changing it is not allowed.
++
++                            Preamble
++
++  The licenses for most software are designed to take away your
++freedom to share and change it.  By contrast, the GNU General Public
++License is intended to guarantee your freedom to share and change free
++software--to make sure the software is free for all its users.  This
++General Public License applies to most of the Free Software
++Foundation's software and to any other program whose authors commit to
++using it.  (Some other Free Software Foundation software is covered by
++the GNU Lesser General Public License instead.)  You can apply it to
++your programs, too.
++
++  When we speak of free software, we are referring to freedom, not
++price.  Our General Public Licenses are designed to make sure that you
++have the freedom to distribute copies of free software (and charge for
++this service if you wish), that you receive source code or can get it
++if you want it, that you can change the software or use pieces of it
++in new free programs; and that you know you can do these things.
++
++  To protect your rights, we need to make restrictions that forbid
++anyone to deny you these rights or to ask you to surrender the rights.
++These restrictions translate to certain responsibilities for you if you
++distribute copies of the software, or if you modify it.
++
++  For example, if you distribute copies of such a program, whether
++gratis or for a fee, you must give the recipients all the rights that
++you have.  You must make sure that they, too, receive or can get the
++source code.  And you must show them these terms so they know their
++rights.
++
++  We protect your rights with two steps: (1) copyright the software, and
++(2) offer you this license which gives you legal permission to copy,
++distribute and/or modify the software.
++
++  Also, for each author's protection and ours, we want to make certain
++that everyone understands that there is no warranty for this free
++software.  If the software is modified by someone else and passed on, we
++want its recipients to know that what they have is not the original, so
++that any problems introduced by others will not reflect on the original
++authors' reputations.
++
++  Finally, any free program is threatened constantly by software
++patents.  We wish to avoid the danger that redistributors of a free
++program will individually obtain patent licenses, in effect making the
++program proprietary.  To prevent this, we have made it clear that any
++patent must be licensed for everyone's free use or not licensed at all.
++
++  The precise terms and conditions for copying, distribution and
++modification follow.
++
++                    GNU GENERAL PUBLIC LICENSE
++   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
++
++  0. This License applies to any program or other work which contains
++a notice placed by the copyright holder saying it may be distributed
++under the terms of this General Public License.  The "Program", below,
++refers to any such program or work, and a "work based on the Program"
++means either the Program or any derivative work under copyright law:
++that is to say, a work containing the Program or a portion of it,
++either verbatim or with modifications and/or translated into another
++language.  (Hereinafter, translation is included without limitation in
++the term "modification".)  Each licensee is addressed as "you".
++
++Activities other than copying, distribution and modification are not
++covered by this License; they are outside its scope.  The act of
++running the Program is not restricted, and the output from the Program
++is covered only if its contents constitute a work based on the
++Program (independent of having been made by running the Program).
++Whether that is true depends on what the Program does.
++
++  1. You may copy and distribute verbatim copies of the Program's
++source code as you receive it, in any medium, provided that you
++conspicuously and appropriately publish on each copy an appropriate
++copyright notice and disclaimer of warranty; keep intact all the
++notices that refer to this License and to the absence of any warranty;
++and give any other recipients of the Program a copy of this License
++along with the Program.
++
++You may charge a fee for the physical act of transferring a copy, and
++you may at your option offer warranty protection in exchange for a fee.
++
++  2. You may modify your copy or copies of the Program or any portion
++of it, thus forming a work based on the Program, and copy and
++distribute such modifications or work under the terms of Section 1
++above, provided that you also meet all of these conditions:
++
++    a) You must cause the modified files to carry prominent notices
++    stating that you changed the files and the date of any change.
++
++    b) You must cause any work that you distribute or publish, that in
++    whole or in part contains or is derived from the Program or any
++    part thereof, to be licensed as a whole at no charge to all third
++    parties under the terms of this License.
++
++    c) If the modified program normally reads commands interactively
++    when run, you must cause it, when started running for such
++    interactive use in the most ordinary way, to print or display an
++    announcement including an appropriate copyright notice and a
++    notice that there is no warranty (or else, saying that you provide
++    a warranty) and that users may redistribute the program under
++    these conditions, and telling the user how to view a copy of this
++    License.  (Exception: if the Program itself is interactive but
++    does not normally print such an announcement, your work based on
++    the Program is not required to print an announcement.)
++
++These requirements apply to the modified work as a whole.  If
++identifiable sections of that work are not derived from the Program,
++and can be reasonably considered independent and separate works in
++themselves, then this License, and its terms, do not apply to those
++sections when you distribute them as separate works.  But when you
++distribute the same sections as part of a whole which is a work based
++on the Program, the distribution of the whole must be on the terms of
++this License, whose permissions for other licensees extend to the
++entire whole, and thus to each and every part regardless of who wrote it.
++
++Thus, it is not the intent of this section to claim rights or contest
++your rights to work written entirely by you; rather, the intent is to
++exercise the right to control the distribution of derivative or
++collective works based on the Program.
++
++In addition, mere aggregation of another work not based on the Program
++with the Program (or with a work based on the Program) on a volume of
++a storage or distribution medium does not bring the other work under
++the scope of this License.
++
++  3. You may copy and distribute the Program (or a work based on it,
++under Section 2) in object code or executable form under the terms of
++Sections 1 and 2 above provided that you also do one of the following:
++
++    a) Accompany it with the complete corresponding machine-readable
++    source code, which must be distributed under the terms of Sections
++    1 and 2 above on a medium customarily used for software interchange; or,
++
++    b) Accompany it with a written offer, valid for at least three
++    years, to give any third party, for a charge no more than your
++    cost of physically performing source distribution, a complete
++    machine-readable copy of the corresponding source code, to be
++    distributed under the terms of Sections 1 and 2 above on a medium
++    customarily used for software interchange; or,
++
++    c) Accompany it with the information you received as to the offer
++    to distribute corresponding source code.  (This alternative is
++    allowed only for noncommercial distribution and only if you
++    received the program in object code or executable form with such
++    an offer, in accord with Subsection b above.)
++
++The source code for a work means the preferred form of the work for
++making modifications to it.  For an executable work, complete source
++code means all the source code for all modules it contains, plus any
++associated interface definition files, plus the scripts used to
++control compilation and installation of the executable.  However, as a
++special exception, the source code distributed need not include
++anything that is normally distributed (in either source or binary
++form) with the major components (compiler, kernel, and so on) of the
++operating system on which the executable runs, unless that component
++itself accompanies the executable.
++
++If distribution of executable or object code is made by offering
++access to copy from a designated place, then offering equivalent
++access to copy the source code from the same place counts as
++distribution of the source code, even though third parties are not
++compelled to copy the source along with the object code.
++
++  4. You may not copy, modify, sublicense, or distribute the Program
++except as expressly provided under this License.  Any attempt
++otherwise to copy, modify, sublicense or distribute the Program is
++void, and will automatically terminate your rights under this License.
++However, parties who have received copies, or rights, from you under
++this License will not have their licenses terminated so long as such
++parties remain in full compliance.
++
++  5. You are not required to accept this License, since you have not
++signed it.  However, nothing else grants you permission to modify or
++distribute the Program or its derivative works.  These actions are
++prohibited by law if you do not accept this License.  Therefore, by
++modifying or distributing the Program (or any work based on the
++Program), you indicate your acceptance of this License to do so, and
++all its terms and conditions for copying, distributing or modifying
++the Program or works based on it.
++
++  6. Each time you redistribute the Program (or any work based on the
++Program), the recipient automatically receives a license from the
++original licensor to copy, distribute or modify the Program subject to
++these terms and conditions.  You may not impose any further
++restrictions on the recipients' exercise of the rights granted herein.
++You are not responsible for enforcing compliance by third parties to
++this License.
++
++  7. If, as a consequence of a court judgment or allegation of patent
++infringement or for any other reason (not limited to patent issues),
++conditions are imposed on you (whether by court order, agreement or
++otherwise) that contradict the conditions of this License, they do not
++excuse you from the conditions of this License.  If you cannot
++distribute so as to satisfy simultaneously your obligations under this
++License and any other pertinent obligations, then as a consequence you
++may not distribute the Program at all.  For example, if a patent
++license would not permit royalty-free redistribution of the Program by
++all those who receive copies directly or indirectly through you, then
++the only way you could satisfy both it and this License would be to
++refrain entirely from distribution of the Program.
++
++If any portion of this section is held invalid or unenforceable under
++any particular circumstance, the balance of the section is intended to
++apply and the section as a whole is intended to apply in other
++circumstances.
++
++It is not the purpose of this section to induce you to infringe any
++patents or other property right claims or to contest validity of any
++such claims; this section has the sole purpose of protecting the
++integrity of the free software distribution system, which is
++implemented by public license practices.  Many people have made
++generous contributions to the wide range of software distributed
++through that system in reliance on consistent application of that
++system; it is up to the author/donor to decide if he or she is willing
++to distribute software through any other system and a licensee cannot
++impose that choice.
++
++This section is intended to make thoroughly clear what is believed to
++be a consequence of the rest of this License.
++
++  8. If the distribution and/or use of the Program is restricted in
++certain countries either by patents or by copyrighted interfaces, the
++original copyright holder who places the Program under this License
++may add an explicit geographical distribution limitation excluding
++those countries, so that distribution is permitted only in or among
++countries not thus excluded.  In such case, this License incorporates
++the limitation as if written in the body of this License.
++
++  9. The Free Software Foundation may publish revised and/or new versions
++of the General Public License from time to time.  Such new versions will
++be similar in spirit to the present version, but may differ in detail to
++address new problems or concerns.
++
++Each version is given a distinguishing version number.  If the Program
++specifies a version number of this License which applies to it and "any
++later version", you have the option of following the terms and conditions
++either of that version or of any later version published by the Free
++Software Foundation.  If the Program does not specify a version number of
++this License, you may choose any version ever published by the Free Software
++Foundation.
++
++  10. If you wish to incorporate parts of the Program into other free
++programs whose distribution conditions are different, write to the author
++to ask for permission.  For software which is copyrighted by the Free
++Software Foundation, write to the Free Software Foundation; we sometimes
++make exceptions for this.  Our decision will be guided by the two goals
++of preserving the free status of all derivatives of our free software and
++of promoting the sharing and reuse of software generally.
++
++                            NO WARRANTY
++
++  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
++REPAIR OR CORRECTION.
++
++  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
++POSSIBILITY OF SUCH DAMAGES.
++
++                     END OF TERMS AND CONDITIONS
++
++            How to Apply These Terms to Your New Programs
++
++  If you develop a new program, and you want it to be of the greatest
++possible use to the public, the best way to achieve this is to make it
++free software which everyone can redistribute and change under these terms.
++
++  To do so, attach the following notices to the program.  It is safest
++to attach them to the start of each source file to most effectively
++convey the exclusion of warranty; and each file should have at least
++the "copyright" line and a pointer to where the full notice is found.
++
++    <one line to give the program's name and a brief idea of what it does.>
++    Copyright (C) <year>  <name of author>
++
++    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.
++
++Also add information on how to contact you by electronic and paper mail.
++
++If the program is interactive, make it output a short notice like this
++when it starts in an interactive mode:
++
++    Gnomovision version 69, Copyright (C) year name of author
++    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
++    This is free software, and you are welcome to redistribute it
++    under certain conditions; type `show c' for details.
++
++The hypothetical commands `show w' and `show c' should show the appropriate
++parts of the General Public License.  Of course, the commands you use may
++be called something other than `show w' and `show c'; they could even be
++mouse-clicks or menu items--whatever suits your program.
++
++You should also get your employer (if you work as a programmer) or your
++school, if any, to sign a "copyright disclaimer" for the program, if
++necessary.  Here is a sample; alter the names:
++
++  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
++  `Gnomovision' (which makes passes at compilers) written by James Hacker.
++
++  <signature of Ty Coon>, 1 April 1989
++  Ty Coon, President of Vice
++
++This General Public License does not permit incorporating your program into
++proprietary programs.  If your program is a subroutine library, you may
++consider it more useful to permit linking proprietary applications with the
++library.  If this is what you want to do, use the GNU Lesser General
++Public License instead of this License.
++
++Autoconf Exception
++
++As a special exception, the Free Software Foundation gives unlimited permission
++to copy, distribute and modify the configure scripts that are the output of
++Autoconf. You need not follow the terms of the GNU General Public License
++when using or distributing such scripts, even though portions of the text
++of Autoconf appear in them. The GNU General Public License (GPL) does govern
++all other use of the material that constitutes the Autoconf program.
++
++Certain portions of the Autoconf source text are designed to be copied (in
++certain cases, depending on the input) into the output of Autoconf. We call
++these the "data" portions. The rest of the Autoconf source text consists of
++comments plus executable code that decides which of the data portions to output
++in any given case. We call these comments and executable code the "non-data"
++portions. Autoconf never copies any of the non-data portions into its output.
++
++This special exception to the GPL applies to versions of Autoconf released
++by the Free Software Foundation. When you make and distribute a modified version
++of Autoconf, you may extend this special exception to the GPL to apply to
++your modified version as well, *unless* your modified version has the potential
++to copy into its output some of the text that was the non-data portion of
++the version that you started with. (In other words, unless your change moves
++or copies text from the non-data portions to the data portions.) If your modification
++has such potential, you must delete any notice of this special exception to
++the GPL from your modified version.
diff --git a/patches/cleanup_makefiles.patch b/patches/cleanup_makefiles.patch
new file mode 100644
index 0000000..33c3a3b
--- /dev/null
+++ b/patches/cleanup_makefiles.patch
@@ -0,0 +1,112 @@
+Index: openvpn/configure.ac
+===================================================================
+--- openvpn.orig/configure.ac
++++ openvpn/configure.ac
+@@ -1312,10 +1312,6 @@ if test "${enable_werror}" = "yes"; then
+ 	CFLAGS="${CFLAGS} -Werror"
+ fi
+ 
+-if test "${WIN32}" = "yes"; then
+-	test -z "${MAN2HTML}" && AC_MSG_ERROR([man2html is required for win32])
+-fi
+-
+ if test "${enable_plugin_auth_pam}" = "yes"; then
+ 	PLUGIN_AUTH_PAM_CFLAGS="${LIBPAM_CFLAGS}"
+ 	if test "${enable_pam_dlopen}" = "yes"; then
+@@ -1418,28 +1414,11 @@ fi
+ AC_CONFIG_FILES([
+ 	version.sh
+ 	Makefile
+-	build/Makefile
+-	build/msvc/Makefile
+-	build/msvc/msvc-generate/Makefile
+-	distro/Makefile
+-	distro/systemd/Makefile
+ 	include/Makefile
+ 	src/Makefile
+ 	src/compat/Makefile
+ 	src/openvpn/Makefile
+-	src/openvpnserv/Makefile
+ 	src/plugins/Makefile
+-	src/plugins/auth-pam/Makefile
+ 	src/plugins/down-root/Makefile
+-	tests/Makefile
+-        tests/unit_tests/Makefile
+-        tests/unit_tests/example_test/Makefile
+-        tests/unit_tests/openvpn/Makefile
+-        tests/unit_tests/plugins/Makefile
+-        tests/unit_tests/plugins/auth-pam/Makefile
+-        vendor/Makefile
+-	sample/Makefile
+-	doc/Makefile
+ ])
+-AC_CONFIG_FILES([tests/t_client.sh], [chmod +x tests/t_client.sh])
+ AC_OUTPUT
+Index: openvpn/src/Makefile.am
+===================================================================
+--- openvpn.orig/src/Makefile.am
++++ openvpn/src/Makefile.am
+@@ -12,4 +12,4 @@
+ MAINTAINERCLEANFILES = \
+ 	$(srcdir)/Makefile.in
+ 
+-SUBDIRS = compat openvpn openvpnserv plugins
++SUBDIRS = compat openvpn plugins
+Index: openvpn/src/openvpn/Makefile.am
+===================================================================
+--- openvpn.orig/src/openvpn/Makefile.am
++++ openvpn/src/openvpn/Makefile.am
+@@ -9,7 +9,7 @@
+ #  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+ #
+ 
+-include $(top_srcdir)/build/ltrc.inc
++include $(top_srcdir)/ltrc.inc
+ 
+ MAINTAINERCLEANFILES = \
+ 	$(srcdir)/Makefile.in
+Index: openvpn/src/plugins/Makefile.am
+===================================================================
+--- openvpn.orig/src/plugins/Makefile.am
++++ openvpn/src/plugins/Makefile.am
+@@ -12,4 +12,4 @@
+ MAINTAINERCLEANFILES = \
+ 	$(srcdir)/Makefile.in
+ 
+-SUBDIRS = auth-pam down-root
++SUBDIRS = down-root
+Index: openvpn/Makefile.am
+===================================================================
+--- openvpn.orig/Makefile.am
++++ openvpn/Makefile.am
+@@ -53,26 +53,14 @@ BUILT_SOURCES = \
+ 	config-version.h
+ endif
+ 
+-SUBDIRS = build distro include src sample doc vendor tests
++SUBDIRS = include src
+ 
+ dist_doc_DATA = \
+-	README \
+-	README.IPv6 \
+-	README.mbedtls \
+-	Changes.rst \
+-	COPYRIGHT.GPL \
+-	COPYING
++	README.IPv6
++	LICENSE
+ 
+ dist_noinst_DATA = \
+-	.gitignore \
+-	.gitattributes \
+-	PORTS \
+-	README.IPv6 TODO.IPv6 \
+-	README.mbedtls \
+-	openvpn.sln \
+-	msvc-env.bat \
+-	msvc-dev.bat \
+-	msvc-build.bat
++	README.IPv6
+ 
+ dist_noinst_HEADERS = \
+ 	config-msvc.h \
diff --git a/patches/fix_long_password.patch b/patches/fix_long_password.patch
new file mode 100644
index 0000000..b731dac
--- /dev/null
+++ b/patches/fix_long_password.patch
@@ -0,0 +1,18 @@
+Index: openvpn/src/openvpn/options.c
+===================================================================
+--- openvpn.orig/src/openvpn/options.c
++++ openvpn/src/openvpn/options.c
+@@ -4243,7 +4243,12 @@ parse_line(const char *line,
+     bool backslash = false;
+     char in, out;
+ 
+-    char parm[OPTION_PARM_SIZE];
++#ifdef ENABLE_PKCS11
++#   define PARM_SIZE USER_PASS_LEN
++#else
++#   define PARM_SIZE OPTION_PARM_SIZE
++#endif
++    char parm[PARM_SIZE];
+     unsigned int parm_len = 0;
+ 
+     msglevel &= ~M_OPTERR;
diff --git a/patches/remove_autoconf_vars.patch b/patches/remove_autoconf_vars.patch
new file mode 100644
index 0000000..50e9ea6
--- /dev/null
+++ b/patches/remove_autoconf_vars.patch
@@ -0,0 +1,18 @@
+Index: openvpn/include/openvpn-plugin.h
+===================================================================
+--- openvpn.orig/include/openvpn-plugin.h
++++ openvpn/include/openvpn-plugin.h
+@@ -49,13 +49,6 @@ typedef X509 openvpn_x509_cert_t;
+ extern "C" {
+ #endif
+ 
+-/* Provide some basic version information to plug-ins at OpenVPN compile time
+- * This is will not be the complete version
+- */
+-#define OPENVPN_VERSION_MAJOR @OPENVPN_VERSION_MAJOR@
+-#define OPENVPN_VERSION_MINOR @OPENVPN_VERSION_MINOR@
+-#define OPENVPN_VERSION_PATCH "@OPENVPN_VERSION_PATCH@"
+-
+ /*
+  * Plug-in types.  These types correspond to the set of script callbacks
+  * supported by OpenVPN.
diff --git a/patches/series b/patches/series
new file mode 100644
index 0000000..73fef3e
--- /dev/null
+++ b/patches/series
@@ -0,0 +1,4 @@
+remove_autoconf_vars.patch
+add_missing_licenses.patch
+cleanup_makefiles.patch
+fix_long_password.patch
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..14bca42
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,15 @@
+#
+#  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+#  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+
+MAINTAINERCLEANFILES = \
+	$(srcdir)/Makefile.in
+
+SUBDIRS = compat openvpn plugins
diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am
new file mode 100644
index 0000000..b4c3a4a
--- /dev/null
+++ b/src/compat/Makefile.am
@@ -0,0 +1,30 @@
+#
+#  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+#  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+
+MAINTAINERCLEANFILES = \
+	$(srcdir)/Makefile.in
+
+EXTRA_DIST = \
+	compat.vcxproj \
+	compat.vcxproj.filters
+
+noinst_LTLIBRARIES = libcompat.la
+
+libcompat_la_SOURCES = \
+	compat.h \
+	compat-dirname.c \
+	compat-basename.c \
+	compat-gettimeofday.c \
+	compat-daemon.c \
+	compat-inet_ntop.c \
+	compat-inet_pton.c \
+	compat-lz4.c compat-lz4.h \
+	compat-versionhelpers.h
diff --git a/src/compat/compat-basename.c b/src/compat/compat-basename.c
new file mode 100644
index 0000000..e66e225
--- /dev/null
+++ b/src/compat/compat-basename.c
@@ -0,0 +1,50 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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
+
+#ifndef HAVE_BASENAME
+
+#include "compat.h"
+#include <string.h>
+
+/* Modified version based on glibc-2.14.1 by Roland McGrath <roland@gnu.org>
+ * This version is extended to handle both / and \ in path names
+ */
+char *
+basename(char *filename)
+{
+    char *p = strrchr(filename, '/');
+    if (!p)
+    {
+        /* If NULL, check for \ instead ... might be Windows a path */
+        p = strrchr(filename, '\\');
+    }
+    return p ? p + 1 : (char *) filename;
+}
+
+#endif /* HAVE_BASENAME */
diff --git a/src/compat/compat-daemon.c b/src/compat/compat-daemon.c
new file mode 100644
index 0000000..4ef28fa
--- /dev/null
+++ b/src/compat/compat-daemon.c
@@ -0,0 +1,109 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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
+
+#ifndef HAVE_DAEMON
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+int
+daemon(int nochdir, int noclose)
+{
+#if defined(HAVE_FORK) && defined(HAVE_SETSID)
+    switch (fork())
+    {
+        case -1:
+            return (-1);
+
+        case 0:
+            break;
+
+        default:
+            exit(0);
+    }
+
+    if (setsid() == -1)
+    {
+        return (-1);
+    }
+
+    if (!nochdir)
+    {
+        chdir("/");
+    }
+
+    if (!noclose)
+    {
+#if defined(HAVE_DUP) && defined(HAVE_DUP2)
+        int fd;
+        if ((fd = open("/dev/null", O_RDWR, 0)) != -1)
+        {
+            dup2(fd, 0);
+            dup2(fd, 1);
+            dup2(fd, 2);
+            if (fd > 2)
+            {
+                close(fd);
+            }
+        }
+#endif
+    }
+
+    return 0;
+#else  /* if defined(HAVE_FORK) && defined(HAVE_SETSID) */
+    (void)nochdir;
+    (void)noclose;
+    errno = EFAULT;
+    return -1;
+#endif /* if defined(HAVE_FORK) && defined(HAVE_SETSID) */
+}
+
+#endif /* ifndef HAVE_DAEMON */
+
diff --git a/src/compat/compat-dirname.c b/src/compat/compat-dirname.c
new file mode 100644
index 0000000..c1523d9
--- /dev/null
+++ b/src/compat/compat-dirname.c
@@ -0,0 +1,146 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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
+
+
+#ifndef HAVE_DIRNAME
+
+#include "compat.h"
+#include <string.h>
+
+/* Unoptimised version of glibc memrchr().
+ * This is considered fast enough, as only this compat
+ * version of dirname() depends on it.
+ */
+static const char *
+__memrchr(const char *str, int c, size_t n)
+{
+    const char *end = str;
+
+    end += n - 1; /* Go to the end of the string */
+    while (end >= str)
+    {
+        if (c == *end)
+        {
+            return end;
+        }
+        else
+        {
+            end--;
+        }
+    }
+    return NULL;
+}
+
+/* Modified version based on glibc-2.14.1 by Ulrich Drepper <drepper@akkadia.org>
+ * This version is extended to handle both / and \ in path names.
+ */
+char *
+dirname(char *path)
+{
+    static const char dot[] = ".";
+    char *last_slash;
+    char separator = '/';
+
+    /* Find last '/'.  */
+    last_slash = path != NULL ? strrchr(path, '/') : NULL;
+    /* If NULL, check for \ instead ... might be Windows a path */
+    if (!last_slash)
+    {
+        last_slash = path != NULL ? strrchr(path, '\\') : NULL;
+        separator = last_slash ? '\\' : '/'; /* Change the separator if \ was found */
+    }
+
+    if (last_slash != NULL && last_slash != path && last_slash[1] == '\0')
+    {
+        /* Determine whether all remaining characters are slashes.  */
+        char *runp;
+
+        for (runp = last_slash; runp != path; --runp)
+        {
+            if (runp[-1] != separator)
+            {
+                break;
+            }
+        }
+
+        /* The '/' is the last character, we have to look further.  */
+        if (runp != path)
+        {
+            last_slash = (char *) __memrchr(path, separator, runp - path);
+        }
+    }
+
+    if (last_slash != NULL)
+    {
+        /* Determine whether all remaining characters are slashes.  */
+        char *runp;
+
+        for (runp = last_slash; runp != path; --runp)
+        {
+            if (runp[-1] != separator)
+            {
+                break;
+            }
+        }
+
+        /* Terminate the path.  */
+        if (runp == path)
+        {
+            /* The last slash is the first character in the string.  We have to
+             * return "/".  As a special case we have to return "//" if there
+             * are exactly two slashes at the beginning of the string.  See
+             * XBD 4.10 Path Name Resolution for more information.  */
+            if (last_slash == path + 1)
+            {
+                ++last_slash;
+            }
+            else
+            {
+                last_slash = path + 1;
+            }
+        }
+        else
+        {
+            last_slash = runp;
+        }
+
+        last_slash[0] = '\0';
+    }
+    else
+    {
+        /* This assignment is ill-designed but the XPG specs require to
+         * return a string containing "." in any case no directory part is
+         * found and so a static and constant string is required.  */
+        path = (char *) dot;
+    }
+
+    return path;
+}
+
+#endif /* HAVE_DIRNAME */
diff --git a/src/compat/compat-gettimeofday.c b/src/compat/compat-gettimeofday.c
new file mode 100644
index 0000000..7cae641
--- /dev/null
+++ b/src/compat/compat-gettimeofday.c
@@ -0,0 +1,134 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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
+
+#ifndef HAVE_GETTIMEOFDAY
+
+#include "compat.h"
+
+#ifdef _WIN32
+/*
+ * NOTICE: mingw has much faster gettimeofday!
+ * autoconf will set HAVE_GETTIMEOFDAY
+ */
+
+#include <windows.h>
+#include <time.h>
+
+static time_t gtc_base = 0;
+static DWORD gtc_last = 0;
+static time_t last_sec = 0;
+static unsigned int last_msec = 0;
+static int bt_last = 0;
+
+static void
+gettimeofday_calibrate(void)
+{
+    const time_t t = time(NULL);
+    const DWORD gtc = GetTickCount();
+    gtc_base = t - gtc/1000;
+    gtc_last = gtc;
+}
+
+/*
+ * Rewritten by JY for OpenVPN 2.1, after I realized that
+ * QueryPerformanceCounter takes nearly 2 orders of magnitude
+ * more processor cycles than GetTickCount.
+ */
+int
+gettimeofday(struct timeval *tv, void *tz)
+{
+    const DWORD gtc = GetTickCount();
+    int bt = 0;
+    time_t sec;
+    unsigned int msec;
+    const int backtrack_hold_seconds = 10;
+
+    (void)tz;
+
+    /* recalibrate at the dreaded 49.7 day mark */
+    if (!gtc_base || gtc < gtc_last)
+    {
+        gettimeofday_calibrate();
+    }
+    gtc_last = gtc;
+
+    sec = gtc_base + gtc / 1000;
+    msec = gtc % 1000;
+
+    if (sec == last_sec)
+    {
+        if (msec < last_msec)
+        {
+            msec = last_msec;
+            bt = 1;
+        }
+    }
+    else if (sec < last_sec)
+    {
+        /* We try to dampen out backtracks of less than backtrack_hold_seconds.
+         * Larger backtracks will be passed through and dealt with by the
+         * TIME_BACKTRACK_PROTECTION code (if enabled) */
+        if (sec > last_sec - backtrack_hold_seconds)
+        {
+            sec = last_sec;
+            msec = last_msec;
+        }
+        bt = 1;
+    }
+
+    tv->tv_sec = (long)last_sec = (long)sec;
+    tv->tv_usec = (last_msec = msec) * 1000;
+
+    if (bt && !bt_last)
+    {
+        gettimeofday_calibrate();
+    }
+    bt_last = bt;
+
+    return 0;
+}
+
+#else  /* ifdef _WIN32 */
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+int
+gettimeofday(struct timeval *tv, void *tz)
+{
+    (void)tz;
+    tv->tv_sec = time(NULL);
+    tv->tv_usec = 0;
+    return 0;
+}
+
+#endif /* _WIN32 */
+
+#endif /* HAVE_GETTIMEOFDAY */
diff --git a/src/compat/compat-inet_ntop.c b/src/compat/compat-inet_ntop.c
new file mode 100644
index 0000000..f2a181e
--- /dev/null
+++ b/src/compat/compat-inet_ntop.c
@@ -0,0 +1,78 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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
+
+#ifndef HAVE_INET_NTOP
+
+#include "compat.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+/*
+ * inet_ntop() and inet_pton() wrap-implementations using
+ * WSAAddressToString() and WSAStringToAddress() functions
+ *
+ * this is needed as long as we support running OpenVPN on WinXP
+ */
+
+const char *
+inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+    struct sockaddr_storage ss;
+    unsigned long s = size;
+
+    ZeroMemory(&ss, sizeof(ss));
+    ss.ss_family = af;
+
+    switch (af)
+    {
+        case AF_INET:
+            ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src;
+            break;
+
+        case AF_INET6:
+            ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src;
+            break;
+
+        default:
+            return NULL;
+    }
+    /* cannot direclty use &size because of strict aliasing rules */
+    return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ?
+           dst : NULL;
+}
+
+#else  /* ifdef _WIN32 */
+
+#error no emulation for inet_ntop
+
+#endif /* ifdef _WIN32 */
+
+#endif /* ifndef HAVE_INET_NTOP */
diff --git a/src/compat/compat-inet_pton.c b/src/compat/compat-inet_pton.c
new file mode 100644
index 0000000..9d451cc
--- /dev/null
+++ b/src/compat/compat-inet_pton.c
@@ -0,0 +1,81 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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
+
+#ifndef HAVE_INET_PTON
+
+#include "compat.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <string.h>
+
+/*
+ * inet_ntop() and inet_pton() wrap-implementations using
+ * WSAAddressToString() and WSAStringToAddress() functions
+ *
+ * this is needed as long as we support running OpenVPN on WinXP
+ */
+
+
+int
+inet_pton(int af, const char *src, void *dst)
+{
+    struct sockaddr_storage ss;
+    int size = sizeof(ss);
+    char src_copy[INET6_ADDRSTRLEN+1];
+
+    ZeroMemory(&ss, sizeof(ss));
+    /* stupid non-const API */
+    strncpy(src_copy, src, INET6_ADDRSTRLEN+1);
+    src_copy[INET6_ADDRSTRLEN] = 0;
+
+    if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0)
+    {
+        switch (af)
+        {
+            case AF_INET:
+                *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr;
+                return 1;
+
+            case AF_INET6:
+                *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr;
+                return 1;
+        }
+    }
+    return 0;
+}
+
+#else  /* ifdef _WIN32 */
+
+#error no emulation for inet_ntop
+
+#endif /* ifdef _WIN32 */
+
+#endif /* ifndef HAVE_INET_PTON */
diff --git a/src/compat/compat-lz4.c b/src/compat/compat-lz4.c
new file mode 100644
index 0000000..723157d
--- /dev/null
+++ b/src/compat/compat-lz4.c
@@ -0,0 +1,1474 @@
+/* This file has been backported by dev-tools/lz4-rebaser.sh
+ * from upstream lz4 commit 7bb64ff2b69a9f8367de (v1.7.5)
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#ifdef NEED_COMPAT_LZ4
+/*
+   LZ4 - Fast LZ compression algorithm
+   Copyright (C) 2011-2016, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+    - LZ4 homepage : http://www.lz4.org
+    - LZ4 source repository : https://github.com/lz4/lz4
+*/
+
+
+/*-************************************
+*  Tuning parameters
+**************************************/
+/*
+ * HEAPMODE :
+ * Select how default compression functions will allocate memory for their hash table,
+ * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).
+ */
+#ifndef HEAPMODE
+#  define HEAPMODE 0
+#endif
+
+/*
+ * ACCELERATION_DEFAULT :
+ * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0
+ */
+#define ACCELERATION_DEFAULT 1
+
+
+/*-************************************
+*  CPU Feature Detection
+**************************************/
+/* LZ4_FORCE_MEMORY_ACCESS
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method is portable but violate C standard.
+ *            It can generate buggy code on targets which generate assembly depending on alignment.
+ *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef LZ4_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */
+#  if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )
+#    define LZ4_FORCE_MEMORY_ACCESS 2
+#  elif defined(__INTEL_COMPILER) || \
+  (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) ))
+#    define LZ4_FORCE_MEMORY_ACCESS 1
+#  endif
+#endif
+
+/*
+ * LZ4_FORCE_SW_BITCOUNT
+ * Define this parameter if your target system or compiler does not support hardware bit count
+ */
+#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for Windows CE does not support Hardware bit count */
+#  define LZ4_FORCE_SW_BITCOUNT
+#endif
+
+
+/*-************************************
+*  Dependency
+**************************************/
+#include "compat-lz4.h"
+/* see also "memory routines" below */
+
+
+/*-************************************
+*  Compiler Options
+**************************************/
+#ifdef _MSC_VER    /* Visual Studio */
+#  define FORCE_INLINE static __forceinline
+#  include <intrin.h>
+#  pragma warning(disable : 4127)        /* disable: C4127: conditional expression is constant */
+#  pragma warning(disable : 4293)        /* disable: C4293: too large shift (32-bits) */
+#else
+#  if defined(__GNUC__) || defined(__clang__)
+#    define FORCE_INLINE static inline __attribute__((always_inline))
+#  elif defined(__cplusplus) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#    define FORCE_INLINE static inline
+#  else
+#    define FORCE_INLINE static
+#  endif
+#endif  /* _MSC_VER */
+
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
+#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )
+#else
+#  define expect(expr,value)    (expr)
+#endif
+
+#define likely(expr)     expect((expr) != 0, 1)
+#define unlikely(expr)   expect((expr) != 0, 0)
+
+
+/*-************************************
+*  Memory routines
+**************************************/
+#include <stdlib.h>   /* malloc, calloc, free */
+#define ALLOCATOR(n,s) calloc(n,s)
+#define FREEMEM        free
+#include <string.h>   /* memset, memcpy */
+#define MEM_INIT       memset
+
+
+/*-************************************
+*  Basic Types
+**************************************/
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# include <stdint.h>
+  typedef  uint8_t BYTE;
+  typedef uint16_t U16;
+  typedef uint32_t U32;
+  typedef  int32_t S32;
+  typedef uint64_t U64;
+  typedef uintptr_t uptrval;
+#else
+  typedef unsigned char       BYTE;
+  typedef unsigned short      U16;
+  typedef unsigned int        U32;
+  typedef   signed int        S32;
+  typedef unsigned long long  U64;
+  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */
+#endif
+
+#if defined(__x86_64__)
+  typedef U64    reg_t;   /* 64-bits in x32 mode */
+#else
+  typedef size_t reg_t;   /* 32-bits in x32 mode */
+#endif
+
+/*-************************************
+*  Reading and writing into memory
+**************************************/
+static unsigned LZ4_isLittleEndian(void)
+{
+    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */
+    return one.c[0];
+}
+
+
+#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)
+/* lie to the compiler about data alignment; use with caution */
+
+static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }
+static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }
+static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }
+
+static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }
+static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }
+
+#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign;
+
+static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; }
+static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; }
+
+static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; }
+static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; }
+
+#else  /* safe and portable access through memcpy() */
+
+static U16 LZ4_read16(const void* memPtr)
+{
+    U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static U32 LZ4_read32(const void* memPtr)
+{
+    U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static reg_t LZ4_read_ARCH(const void* memPtr)
+{
+    reg_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+}
+
+static void LZ4_write16(void* memPtr, U16 value)
+{
+    memcpy(memPtr, &value, sizeof(value));
+}
+
+static void LZ4_write32(void* memPtr, U32 value)
+{
+    memcpy(memPtr, &value, sizeof(value));
+}
+
+#endif /* LZ4_FORCE_MEMORY_ACCESS */
+
+
+static U16 LZ4_readLE16(const void* memPtr)
+{
+    if (LZ4_isLittleEndian()) {
+        return LZ4_read16(memPtr);
+    } else {
+        const BYTE* p = (const BYTE*)memPtr;
+        return (U16)((U16)p[0] + (p[1]<<8));
+    }
+}
+
+static void LZ4_writeLE16(void* memPtr, U16 value)
+{
+    if (LZ4_isLittleEndian()) {
+        LZ4_write16(memPtr, value);
+    } else {
+        BYTE* p = (BYTE*)memPtr;
+        p[0] = (BYTE) value;
+        p[1] = (BYTE)(value>>8);
+    }
+}
+
+static void LZ4_copy8(void* dst, const void* src)
+{
+    memcpy(dst,src,8);
+}
+
+/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */
+static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd)
+{
+    BYTE* d = (BYTE*)dstPtr;
+    const BYTE* s = (const BYTE*)srcPtr;
+    BYTE* const e = (BYTE*)dstEnd;
+
+    do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e);
+}
+
+
+/*-************************************
+*  Common Constants
+**************************************/
+#define MINMATCH 4
+
+#define WILDCOPYLENGTH 8
+#define LASTLITERALS 5
+#define MFLIMIT (WILDCOPYLENGTH+MINMATCH)
+static const int LZ4_minLength = (MFLIMIT+1);
+
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+
+#define MAXD_LOG 16
+#define MAX_DISTANCE ((1 << MAXD_LOG) - 1)
+
+#define ML_BITS  4
+#define ML_MASK  ((1U<<ML_BITS)-1)
+#define RUN_BITS (8-ML_BITS)
+#define RUN_MASK ((1U<<RUN_BITS)-1)
+
+
+/*-************************************
+*  Common Utils
+**************************************/
+#define LZ4_STATIC_ASSERT(c)    { enum { LZ4_static_assert = 1/(int)(!!(c)) }; }   /* use only *after* variable declarations */
+
+
+/*-************************************
+*  Common functions
+**************************************/
+static unsigned LZ4_NbCommonBytes (register reg_t val)
+{
+    if (LZ4_isLittleEndian()) {
+        if (sizeof(val)==8) {
+#       if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanForward64( &r, (U64)val );
+            return (int)(r>>3);
+#       elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_ctzll((U64)val) >> 3);
+#       else
+            static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
+            return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+#       endif
+        } else /* 32 bits */ {
+#       if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r;
+            _BitScanForward( &r, (U32)val );
+            return (int)(r>>3);
+#       elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_ctz((U32)val) >> 3);
+#       else
+            static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
+            return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+#       endif
+        }
+    } else   /* Big Endian CPU */ {
+        if (sizeof(val)==8) {
+#       if defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanReverse64( &r, val );
+            return (unsigned)(r>>3);
+#       elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_clzll((U64)val) >> 3);
+#       else
+            unsigned r;
+            if (!(val>>32)) { r=4; } else { r=0; val>>=32; }
+            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+            r += (!val);
+            return r;
+#       endif
+        } else /* 32 bits */ {
+#       if defined(_MSC_VER) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            unsigned long r = 0;
+            _BitScanReverse( &r, (unsigned long)val );
+            return (unsigned)(r>>3);
+#       elif (defined(__clang__) || (defined(__GNUC__) && (__GNUC__>=3))) && !defined(LZ4_FORCE_SW_BITCOUNT)
+            return (__builtin_clz((U32)val) >> 3);
+#       else
+            unsigned r;
+            if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+            r += (!val);
+            return r;
+#       endif
+        }
+    }
+}
+
+#define STEPSIZE sizeof(reg_t)
+static unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)
+{
+    const BYTE* const pStart = pIn;
+
+    while (likely(pIn<pInLimit-(STEPSIZE-1))) {
+        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);
+        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }
+        pIn += LZ4_NbCommonBytes(diff);
+        return (unsigned)(pIn - pStart);
+    }
+
+    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }
+    if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }
+    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;
+    return (unsigned)(pIn - pStart);
+}
+
+
+#ifndef LZ4_COMMONDEFS_ONLY
+/*-************************************
+*  Local Constants
+**************************************/
+static const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));
+static const U32 LZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */
+
+
+/*-************************************
+*  Local Structures and types
+**************************************/
+typedef enum { notLimited = 0, limitedOutput = 1 } limitedOutput_directive;
+typedef enum { byPtr, byU32, byU16 } tableType_t;
+
+typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive;
+typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;
+
+typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive;
+typedef enum { full = 0, partial = 1 } earlyEnd_directive;
+
+
+/*-************************************
+*  Local Utils
+**************************************/
+int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }
+const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }
+int LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }
+int LZ4_sizeofState() { return LZ4_STREAMSIZE; }
+
+
+/*-******************************
+*  Compression functions
+********************************/
+static U32 LZ4_hash4(U32 sequence, tableType_t const tableType)
+{
+    if (tableType == byU16)
+        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));
+    else
+        return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));
+}
+
+static U32 LZ4_hash5(U64 sequence, tableType_t const tableType)
+{
+    static const U64 prime5bytes = 889523592379ULL;
+    static const U64 prime8bytes = 11400714785074694791ULL;
+    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;
+    if (LZ4_isLittleEndian())
+        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));
+    else
+        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));
+}
+
+FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)
+{
+    if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);
+    return LZ4_hash4(LZ4_read32(p), tableType);
+}
+
+static void LZ4_putPositionOnHash(const BYTE* p, U32 h, void* tableBase, tableType_t const tableType, const BYTE* srcBase)
+{
+    switch (tableType)
+    {
+    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }
+    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }
+    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }
+    }
+}
+
+FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+    U32 const h = LZ4_hashPosition(p, tableType);
+    LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);
+}
+
+static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+    if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
+    if (tableType == byU32) { const U32* const hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
+    { const U16* const hashTable = (U16*) tableBase; return hashTable[h] + srcBase; }   /* default, to ensure a return */
+}
+
+FORCE_INLINE const BYTE* LZ4_getPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)
+{
+    U32 const h = LZ4_hashPosition(p, tableType);
+    return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);
+}
+
+
+/** LZ4_compress_generic() :
+    inlined, to ensure branches are decided at compilation time */
+FORCE_INLINE int LZ4_compress_generic(
+                 LZ4_stream_t_internal* const cctx,
+                 const char* const source,
+                 char* const dest,
+                 const int inputSize,
+                 const int maxOutputSize,
+                 const limitedOutput_directive outputLimited,
+                 const tableType_t tableType,
+                 const dict_directive dict,
+                 const dictIssue_directive dictIssue,
+                 const U32 acceleration)
+{
+    const BYTE* ip = (const BYTE*) source;
+    const BYTE* base;
+    const BYTE* lowLimit;
+    const BYTE* const lowRefLimit = ip - cctx->dictSize;
+    const BYTE* const dictionary = cctx->dictionary;
+    const BYTE* const dictEnd = dictionary + cctx->dictSize;
+    const ptrdiff_t dictDelta = dictEnd - (const BYTE*)source;
+    const BYTE* anchor = (const BYTE*) source;
+    const BYTE* const iend = ip + inputSize;
+    const BYTE* const mflimit = iend - MFLIMIT;
+    const BYTE* const matchlimit = iend - LASTLITERALS;
+
+    BYTE* op = (BYTE*) dest;
+    BYTE* const olimit = op + maxOutputSize;
+
+    U32 forwardH;
+
+    /* Init conditions */
+    if ((U32)inputSize > (U32)LZ4_MAX_INPUT_SIZE) return 0;   /* Unsupported inputSize, too large (or negative) */
+    switch(dict)
+    {
+    case noDict:
+    default:
+        base = (const BYTE*)source;
+        lowLimit = (const BYTE*)source;
+        break;
+    case withPrefix64k:
+        base = (const BYTE*)source - cctx->currentOffset;
+        lowLimit = (const BYTE*)source - cctx->dictSize;
+        break;
+    case usingExtDict:
+        base = (const BYTE*)source - cctx->currentOffset;
+        lowLimit = (const BYTE*)source;
+        break;
+    }
+    if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) return 0;   /* Size too large (not within 64K limit) */
+    if (inputSize<LZ4_minLength) goto _last_literals;                  /* Input too small, no compression (all literals) */
+
+    /* First Byte */
+    LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+    ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+    /* Main Loop */
+    for ( ; ; ) {
+        ptrdiff_t refDelta = 0;
+        const BYTE* match;
+        BYTE* token;
+
+        /* Find a match */
+        {   const BYTE* forwardIp = ip;
+            unsigned step = 1;
+            unsigned searchMatchNb = acceleration << LZ4_skipTrigger;
+            do {
+                U32 const h = forwardH;
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+                match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);
+                if (dict==usingExtDict) {
+                    if (match < (const BYTE*)source) {
+                        refDelta = dictDelta;
+                        lowLimit = dictionary;
+                    } else {
+                        refDelta = 0;
+                        lowLimit = (const BYTE*)source;
+                }   }
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);
+
+            } while ( ((dictIssue==dictSmall) ? (match < lowRefLimit) : 0)
+                || ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+                || (LZ4_read32(match+refDelta) != LZ4_read32(ip)) );
+        }
+
+        /* Catch up */
+        while (((ip>anchor) & (match+refDelta > lowLimit)) && (unlikely(ip[-1]==match[refDelta-1]))) { ip--; match--; }
+
+        /* Encode Literals */
+        {   unsigned const litLength = (unsigned)(ip - anchor);
+            token = op++;
+            if ((outputLimited) &&  /* Check output buffer overflow */
+                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)))
+                return 0;
+            if (litLength >= RUN_MASK) {
+                int len = (int)litLength-RUN_MASK;
+                *token = (RUN_MASK<<ML_BITS);
+                for(; len >= 255 ; len-=255) *op++ = 255;
+                *op++ = (BYTE)len;
+            }
+            else *token = (BYTE)(litLength<<ML_BITS);
+
+            /* Copy Literals */
+            LZ4_wildCopy(op, anchor, op+litLength);
+            op+=litLength;
+        }
+
+_next_match:
+        /* Encode Offset */
+        LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+        /* Encode MatchLength */
+        {   unsigned matchCode;
+
+            if ((dict==usingExtDict) && (lowLimit==dictionary)) {
+                const BYTE* limit;
+                match += refDelta;
+                limit = ip + (dictEnd-match);
+                if (limit > matchlimit) limit = matchlimit;
+                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);
+                ip += MINMATCH + matchCode;
+                if (ip==limit) {
+                    unsigned const more = LZ4_count(ip, (const BYTE*)source, matchlimit);
+                    matchCode += more;
+                    ip += more;
+                }
+            } else {
+                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+                ip += MINMATCH + matchCode;
+            }
+
+            if ( outputLimited &&    /* Check output buffer overflow */
+                (unlikely(op + (1 + LASTLITERALS) + (matchCode>>8) > olimit)) )
+                return 0;
+            if (matchCode >= ML_MASK) {
+                *token += ML_MASK;
+                matchCode -= ML_MASK;
+                LZ4_write32(op, 0xFFFFFFFF);
+                while (matchCode >= 4*255) op+=4, LZ4_write32(op, 0xFFFFFFFF), matchCode -= 4*255;
+                op += matchCode / 255;
+                *op++ = (BYTE)(matchCode % 255);
+            } else
+                *token += (BYTE)(matchCode);
+        }
+
+        anchor = ip;
+
+        /* Test end of chunk */
+        if (ip > mflimit) break;
+
+        /* Fill table */
+        LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);
+
+        /* Test next position */
+        match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);
+        if (dict==usingExtDict) {
+            if (match < (const BYTE*)source) {
+                refDelta = dictDelta;
+                lowLimit = dictionary;
+            } else {
+                refDelta = 0;
+                lowLimit = (const BYTE*)source;
+        }   }
+        LZ4_putPosition(ip, cctx->hashTable, tableType, base);
+        if ( ((dictIssue==dictSmall) ? (match>=lowRefLimit) : 1)
+            && (match+MAX_DISTANCE>=ip)
+            && (LZ4_read32(match+refDelta)==LZ4_read32(ip)) )
+        { token=op++; *token=0; goto _next_match; }
+
+        /* Prepare next loop */
+        forwardH = LZ4_hashPosition(++ip, tableType);
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {   size_t const lastRun = (size_t)(iend - anchor);
+        if ( (outputLimited) &&  /* Check output buffer overflow */
+            ((op - (BYTE*)dest) + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > (U32)maxOutputSize) )
+            return 0;
+        if (lastRun >= RUN_MASK) {
+            size_t accumulator = lastRun - RUN_MASK;
+            *op++ = RUN_MASK << ML_BITS;
+            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+            *op++ = (BYTE) accumulator;
+        } else {
+            *op++ = (BYTE)(lastRun<<ML_BITS);
+        }
+        memcpy(op, anchor, lastRun);
+        op += lastRun;
+    }
+
+    /* End */
+    return (int) (((char*)op)-dest);
+}
+
+
+int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;
+    LZ4_resetStream((LZ4_stream_t*)state);
+    if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+    if (maxOutputSize >= LZ4_compressBound(inputSize)) {
+        if (inputSize < LZ4_64Klimit)
+            return LZ4_compress_generic(ctx, source, dest, inputSize,             0,    notLimited,                        byU16, noDict, noDictIssue, acceleration);
+        else
+            return LZ4_compress_generic(ctx, source, dest, inputSize,             0,    notLimited, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+    } else {
+        if (inputSize < LZ4_64Klimit)
+            return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput,                        byU16, noDict, noDictIssue, acceleration);
+        else
+            return LZ4_compress_generic(ctx, source, dest, inputSize, maxOutputSize, limitedOutput, (sizeof(void*)==8) ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+    }
+}
+
+
+int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+#if (HEAPMODE)
+    void* ctxPtr = ALLOCATOR(1, sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */
+#else
+    LZ4_stream_t ctx;
+    void* const ctxPtr = &ctx;
+#endif
+
+    int const result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);
+
+#if (HEAPMODE)
+    FREEMEM(ctxPtr);
+#endif
+    return result;
+}
+
+
+int LZ4_compress_default(const char* source, char* dest, int inputSize, int maxOutputSize)
+{
+    return LZ4_compress_fast(source, dest, inputSize, maxOutputSize, 1);
+}
+
+
+/* hidden debug function */
+/* strangely enough, gcc generates faster code when this function is uncommented, even if unused */
+int LZ4_compress_fast_force(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t ctx;
+    LZ4_resetStream(&ctx);
+
+    if (inputSize < LZ4_64Klimit)
+        return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, byU16,                        noDict, noDictIssue, acceleration);
+    else
+        return LZ4_compress_generic(&ctx.internal_donotuse, source, dest, inputSize, maxOutputSize, limitedOutput, sizeof(void*)==8 ? byU32 : byPtr, noDict, noDictIssue, acceleration);
+}
+
+
+/*-******************************
+*  *_destSize() variant
+********************************/
+
+static int LZ4_compress_destSize_generic(
+                       LZ4_stream_t_internal* const ctx,
+                 const char* const src,
+                       char* const dst,
+                       int*  const srcSizePtr,
+                 const int targetDstSize,
+                 const tableType_t tableType)
+{
+    const BYTE* ip = (const BYTE*) src;
+    const BYTE* base = (const BYTE*) src;
+    const BYTE* lowLimit = (const BYTE*) src;
+    const BYTE* anchor = ip;
+    const BYTE* const iend = ip + *srcSizePtr;
+    const BYTE* const mflimit = iend - MFLIMIT;
+    const BYTE* const matchlimit = iend - LASTLITERALS;
+
+    BYTE* op = (BYTE*) dst;
+    BYTE* const oend = op + targetDstSize;
+    BYTE* const oMaxLit = op + targetDstSize - 2 /* offset */ - 8 /* because 8+MINMATCH==MFLIMIT */ - 1 /* token */;
+    BYTE* const oMaxMatch = op + targetDstSize - (LASTLITERALS + 1 /* token */);
+    BYTE* const oMaxSeq = oMaxLit - 1 /* token */;
+
+    U32 forwardH;
+
+
+    /* Init conditions */
+    if (targetDstSize < 1) return 0;                                     /* Impossible to store anything */
+    if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0;            /* Unsupported input size, too large (or negative) */
+    if ((tableType == byU16) && (*srcSizePtr>=LZ4_64Klimit)) return 0;   /* Size too large (not within 64K limit) */
+    if (*srcSizePtr<LZ4_minLength) goto _last_literals;                  /* Input too small, no compression (all literals) */
+
+    /* First Byte */
+    *srcSizePtr = 0;
+    LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+    ip++; forwardH = LZ4_hashPosition(ip, tableType);
+
+    /* Main Loop */
+    for ( ; ; ) {
+        const BYTE* match;
+        BYTE* token;
+
+        /* Find a match */
+        {   const BYTE* forwardIp = ip;
+            unsigned step = 1;
+            unsigned searchMatchNb = 1 << LZ4_skipTrigger;
+
+            do {
+                U32 h = forwardH;
+                ip = forwardIp;
+                forwardIp += step;
+                step = (searchMatchNb++ >> LZ4_skipTrigger);
+
+                if (unlikely(forwardIp > mflimit)) goto _last_literals;
+
+                match = LZ4_getPositionOnHash(h, ctx->hashTable, tableType, base);
+                forwardH = LZ4_hashPosition(forwardIp, tableType);
+                LZ4_putPositionOnHash(ip, h, ctx->hashTable, tableType, base);
+
+            } while ( ((tableType==byU16) ? 0 : (match + MAX_DISTANCE < ip))
+                || (LZ4_read32(match) != LZ4_read32(ip)) );
+        }
+
+        /* Catch up */
+        while ((ip>anchor) && (match > lowLimit) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }
+
+        /* Encode Literal length */
+        {   unsigned litLength = (unsigned)(ip - anchor);
+            token = op++;
+            if (op + ((litLength+240)/255) + litLength > oMaxLit) {
+                /* Not enough space for a last match */
+                op--;
+                goto _last_literals;
+            }
+            if (litLength>=RUN_MASK) {
+                unsigned len = litLength - RUN_MASK;
+                *token=(RUN_MASK<<ML_BITS);
+                for(; len >= 255 ; len-=255) *op++ = 255;
+                *op++ = (BYTE)len;
+            }
+            else *token = (BYTE)(litLength<<ML_BITS);
+
+            /* Copy Literals */
+            LZ4_wildCopy(op, anchor, op+litLength);
+            op += litLength;
+        }
+
+_next_match:
+        /* Encode Offset */
+        LZ4_writeLE16(op, (U16)(ip-match)); op+=2;
+
+        /* Encode MatchLength */
+        {   size_t matchLength = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);
+
+            if (op + ((matchLength+240)/255) > oMaxMatch) {
+                /* Match description too long : reduce it */
+                matchLength = (15-1) + (oMaxMatch-op) * 255;
+            }
+            ip += MINMATCH + matchLength;
+
+            if (matchLength>=ML_MASK) {
+                *token += ML_MASK;
+                matchLength -= ML_MASK;
+                while (matchLength >= 255) { matchLength-=255; *op++ = 255; }
+                *op++ = (BYTE)matchLength;
+            }
+            else *token += (BYTE)(matchLength);
+        }
+
+        anchor = ip;
+
+        /* Test end of block */
+        if (ip > mflimit) break;
+        if (op > oMaxSeq) break;
+
+        /* Fill table */
+        LZ4_putPosition(ip-2, ctx->hashTable, tableType, base);
+
+        /* Test next position */
+        match = LZ4_getPosition(ip, ctx->hashTable, tableType, base);
+        LZ4_putPosition(ip, ctx->hashTable, tableType, base);
+        if ( (match+MAX_DISTANCE>=ip)
+            && (LZ4_read32(match)==LZ4_read32(ip)) )
+        { token=op++; *token=0; goto _next_match; }
+
+        /* Prepare next loop */
+        forwardH = LZ4_hashPosition(++ip, tableType);
+    }
+
+_last_literals:
+    /* Encode Last Literals */
+    {   size_t lastRunSize = (size_t)(iend - anchor);
+        if (op + 1 /* token */ + ((lastRunSize+240)/255) /* litLength */ + lastRunSize /* literals */ > oend) {
+            /* adapt lastRunSize to fill 'dst' */
+            lastRunSize  = (oend-op) - 1;
+            lastRunSize -= (lastRunSize+240)/255;
+        }
+        ip = anchor + lastRunSize;
+
+        if (lastRunSize >= RUN_MASK) {
+            size_t accumulator = lastRunSize - RUN_MASK;
+            *op++ = RUN_MASK << ML_BITS;
+            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;
+            *op++ = (BYTE) accumulator;
+        } else {
+            *op++ = (BYTE)(lastRunSize<<ML_BITS);
+        }
+        memcpy(op, anchor, lastRunSize);
+        op += lastRunSize;
+    }
+
+    /* End */
+    *srcSizePtr = (int) (((const char*)ip)-src);
+    return (int) (((char*)op)-dst);
+}
+
+
+static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+    LZ4_resetStream(state);
+
+    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */
+        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);
+    } else {
+        if (*srcSizePtr < LZ4_64Klimit)
+            return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, byU16);
+        else
+            return LZ4_compress_destSize_generic(&state->internal_donotuse, src, dst, srcSizePtr, targetDstSize, sizeof(void*)==8 ? byU32 : byPtr);
+    }
+}
+
+
+int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)
+{
+#if (HEAPMODE)
+    LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOCATOR(1, sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */
+#else
+    LZ4_stream_t ctxBody;
+    LZ4_stream_t* ctx = &ctxBody;
+#endif
+
+    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);
+
+#if (HEAPMODE)
+    FREEMEM(ctx);
+#endif
+    return result;
+}
+
+
+
+/*-******************************
+*  Streaming functions
+********************************/
+
+LZ4_stream_t* LZ4_createStream(void)
+{
+    LZ4_stream_t* lz4s = (LZ4_stream_t*)ALLOCATOR(8, LZ4_STREAMSIZE_U64);
+    LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal));    /* A compilation error here means LZ4_STREAMSIZE is not large enough */
+    LZ4_resetStream(lz4s);
+    return lz4s;
+}
+
+void LZ4_resetStream (LZ4_stream_t* LZ4_stream)
+{
+    MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t));
+}
+
+int LZ4_freeStream (LZ4_stream_t* LZ4_stream)
+{
+    FREEMEM(LZ4_stream);
+    return (0);
+}
+
+
+#define HASH_UNIT sizeof(reg_t)
+int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)
+{
+    LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;
+    const BYTE* p = (const BYTE*)dictionary;
+    const BYTE* const dictEnd = p + dictSize;
+    const BYTE* base;
+
+    if ((dict->initCheck) || (dict->currentOffset > 1 GB))  /* Uninitialized structure, or reuse overflow */
+        LZ4_resetStream(LZ4_dict);
+
+    if (dictSize < (int)HASH_UNIT) {
+        dict->dictionary = NULL;
+        dict->dictSize = 0;
+        return 0;
+    }
+
+    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;
+    dict->currentOffset += 64 KB;
+    base = p - dict->currentOffset;
+    dict->dictionary = p;
+    dict->dictSize = (U32)(dictEnd - p);
+    dict->currentOffset += dict->dictSize;
+
+    while (p <= dictEnd-HASH_UNIT) {
+        LZ4_putPosition(p, dict->hashTable, byU32, base);
+        p+=3;
+    }
+
+    return dict->dictSize;
+}
+
+
+static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, const BYTE* src)
+{
+    if ((LZ4_dict->currentOffset > 0x80000000) ||
+        ((uptrval)LZ4_dict->currentOffset > (uptrval)src)) {   /* address space overflow */
+        /* rescale hash table */
+        U32 const delta = LZ4_dict->currentOffset - 64 KB;
+        const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;
+        int i;
+        for (i=0; i<LZ4_HASH_SIZE_U32; i++) {
+            if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;
+            else LZ4_dict->hashTable[i] -= delta;
+        }
+        LZ4_dict->currentOffset = 64 KB;
+        if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;
+        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;
+    }
+}
+
+
+int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)
+{
+    LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse;
+    const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+    const BYTE* smallest = (const BYTE*) source;
+    if (streamPtr->initCheck) return 0;   /* Uninitialized structure detected */
+    if ((streamPtr->dictSize>0) && (smallest>dictEnd)) smallest = dictEnd;
+    LZ4_renormDictT(streamPtr, smallest);
+    if (acceleration < 1) acceleration = ACCELERATION_DEFAULT;
+
+    /* Check overlapping input/dictionary space */
+    {   const BYTE* sourceEnd = (const BYTE*) source + inputSize;
+        if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) {
+            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);
+            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;
+            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;
+            streamPtr->dictionary = dictEnd - streamPtr->dictSize;
+        }
+    }
+
+    /* prefix mode : source data follows dictionary */
+    if (dictEnd == (const BYTE*)source) {
+        int result;
+        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+            result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, dictSmall, acceleration);
+        else
+            result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, withPrefix64k, noDictIssue, acceleration);
+        streamPtr->dictSize += (U32)inputSize;
+        streamPtr->currentOffset += (U32)inputSize;
+        return result;
+    }
+
+    /* external dictionary mode */
+    {   int result;
+        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))
+            result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, dictSmall, acceleration);
+        else
+            result = LZ4_compress_generic(streamPtr, source, dest, inputSize, maxOutputSize, limitedOutput, byU32, usingExtDict, noDictIssue, acceleration);
+        streamPtr->dictionary = (const BYTE*)source;
+        streamPtr->dictSize = (U32)inputSize;
+        streamPtr->currentOffset += (U32)inputSize;
+        return result;
+    }
+}
+
+
+/* Hidden debug function, to force external dictionary mode */
+int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int inputSize)
+{
+    LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;
+    int result;
+    const BYTE* const dictEnd = streamPtr->dictionary + streamPtr->dictSize;
+
+    const BYTE* smallest = dictEnd;
+    if (smallest > (const BYTE*) source) smallest = (const BYTE*) source;
+    LZ4_renormDictT(streamPtr, smallest);
+
+    result = LZ4_compress_generic(streamPtr, source, dest, inputSize, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);
+
+    streamPtr->dictionary = (const BYTE*)source;
+    streamPtr->dictSize = (U32)inputSize;
+    streamPtr->currentOffset += (U32)inputSize;
+
+    return result;
+}
+
+
+/*! LZ4_saveDict() :
+ *  If previously compressed data block is not guaranteed to remain available at its memory location,
+ *  save it into a safer place (char* safeBuffer).
+ *  Note : you don't need to call LZ4_loadDict() afterwards,
+ *         dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ *  Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)
+{
+    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;
+    const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;
+
+    if ((U32)dictSize > 64 KB) dictSize = 64 KB;   /* useless to define a dictionary > 64 KB */
+    if ((U32)dictSize > dict->dictSize) dictSize = dict->dictSize;
+
+    memmove(safeBuffer, previousDictEnd - dictSize, dictSize);
+
+    dict->dictionary = (const BYTE*)safeBuffer;
+    dict->dictSize = (U32)dictSize;
+
+    return dictSize;
+}
+
+
+
+/*-*****************************
+*  Decompression functions
+*******************************/
+/*! LZ4_decompress_generic() :
+ *  This generic decompression function cover all use cases.
+ *  It shall be instantiated several times, using different sets of directives
+ *  Note that it is important this generic function is really inlined,
+ *  in order to remove useless branches during compilation optimization.
+ */
+FORCE_INLINE int LZ4_decompress_generic(
+                 const char* const source,
+                 char* const dest,
+                 int inputSize,
+                 int outputSize,         /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */
+
+                 int endOnInput,         /* endOnOutputSize, endOnInputSize */
+                 int partialDecoding,    /* full, partial */
+                 int targetOutputSize,   /* only used if partialDecoding==partial */
+                 int dict,               /* noDict, withPrefix64k, usingExtDict */
+                 const BYTE* const lowPrefix,  /* == dest when no prefix */
+                 const BYTE* const dictStart,  /* only if dict==usingExtDict */
+                 const size_t dictSize         /* note : = 0 if noDict */
+                 )
+{
+    /* Local Variables */
+    const BYTE* ip = (const BYTE*) source;
+    const BYTE* const iend = ip + inputSize;
+
+    BYTE* op = (BYTE*) dest;
+    BYTE* const oend = op + outputSize;
+    BYTE* cpy;
+    BYTE* oexit = op + targetOutputSize;
+    const BYTE* const lowLimit = lowPrefix - dictSize;
+
+    const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize;
+    const unsigned dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4};
+    const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
+
+    const int safeDecode = (endOnInput==endOnInputSize);
+    const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB)));
+
+
+    /* Special cases */
+    if ((partialDecoding) && (oexit > oend-MFLIMIT)) oexit = oend-MFLIMIT;                        /* targetOutputSize too high => decode everything */
+    if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1;  /* Empty output buffer */
+    if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1);
+
+    /* Main Loop : decode sequences */
+    while (1) {
+        size_t length;
+        const BYTE* match;
+        size_t offset;
+
+        /* get literal length */
+        unsigned const token = *ip++;
+        if ((length=(token>>ML_BITS)) == RUN_MASK) {
+            unsigned s;
+            do {
+                s = *ip++;
+                length += s;
+            } while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) & (s==255) );
+            if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) goto _output_error;   /* overflow detection */
+            if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) goto _output_error;   /* overflow detection */
+        }
+
+        /* copy literals */
+        cpy = op+length;
+        if ( ((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) )
+            || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) )
+        {
+            if (partialDecoding) {
+                if (cpy > oend) goto _output_error;                           /* Error : write attempt beyond end of output buffer */
+                if ((endOnInput) && (ip+length > iend)) goto _output_error;   /* Error : read attempt beyond end of input buffer */
+            } else {
+                if ((!endOnInput) && (cpy != oend)) goto _output_error;       /* Error : block decoding must stop exactly there */
+                if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error;   /* Error : input must be consumed */
+            }
+            memcpy(op, ip, length);
+            ip += length;
+            op += length;
+            break;     /* Necessarily EOF, due to parsing restrictions */
+        }
+        LZ4_wildCopy(op, ip, cpy);
+        ip += length; op = cpy;
+
+        /* get offset */
+        offset = LZ4_readLE16(ip); ip+=2;
+        match = op - offset;
+        if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error;   /* Error : offset outside buffers */
+        LZ4_write32(op, (U32)offset);   /* costs ~1%; silence an msan warning when offset==0 */
+
+        /* get matchlength */
+        length = token & ML_MASK;
+        if (length == ML_MASK) {
+            unsigned s;
+            do {
+                s = *ip++;
+                if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error;
+                length += s;
+            } while (s==255);
+            if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */
+        }
+        length += MINMATCH;
+
+        /* check external dictionary */
+        if ((dict==usingExtDict) && (match < lowPrefix)) {
+            if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error;   /* doesn't respect parsing restriction */
+
+            if (length <= (size_t)(lowPrefix-match)) {
+                /* match can be copied as a single segment from external dictionary */
+                memmove(op, dictEnd - (lowPrefix-match), length);
+                op += length;
+            } else {
+                /* match encompass external dictionary and current block */
+                size_t const copySize = (size_t)(lowPrefix-match);
+                size_t const restSize = length - copySize;
+                memcpy(op, dictEnd - copySize, copySize);
+                op += copySize;
+                if (restSize > (size_t)(op-lowPrefix)) {  /* overlap copy */
+                    BYTE* const endOfMatch = op + restSize;
+                    const BYTE* copyFrom = lowPrefix;
+                    while (op < endOfMatch) *op++ = *copyFrom++;
+                } else {
+                    memcpy(op, lowPrefix, restSize);
+                    op += restSize;
+            }   }
+            continue;
+        }
+
+        /* copy match within block */
+        cpy = op + length;
+        if (unlikely(offset<8)) {
+            const int dec64 = dec64table[offset];
+            op[0] = match[0];
+            op[1] = match[1];
+            op[2] = match[2];
+            op[3] = match[3];
+            match += dec32table[offset];
+            memcpy(op+4, match, 4);
+            match -= dec64;
+        } else { LZ4_copy8(op, match); match+=8; }
+        op += 8;
+
+        if (unlikely(cpy>oend-12)) {
+            BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1);
+            if (cpy > oend-LASTLITERALS) goto _output_error;    /* Error : last LASTLITERALS bytes must be literals (uncompressed) */
+            if (op < oCopyLimit) {
+                LZ4_wildCopy(op, match, oCopyLimit);
+                match += oCopyLimit - op;
+                op = oCopyLimit;
+            }
+            while (op<cpy) *op++ = *match++;
+        } else {
+            LZ4_copy8(op, match);
+            if (length>16) LZ4_wildCopy(op+8, match+8, cpy);
+        }
+        op=cpy;   /* correction */
+    }
+
+    /* end of decoding */
+    if (endOnInput)
+       return (int) (((char*)op)-dest);     /* Nb of output bytes decoded */
+    else
+       return (int) (((const char*)ip)-source);   /* Nb of input bytes read */
+
+    /* Overflow error detected */
+_output_error:
+    return (int) (-(((const char*)ip)-source))-1;
+}
+
+
+int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, full, 0, noDict, (BYTE*)dest, NULL, 0);
+}
+
+int LZ4_decompress_safe_partial(const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, endOnInputSize, partial, targetOutputSize, noDict, (BYTE*)dest, NULL, 0);
+}
+
+int LZ4_decompress_fast(const char* source, char* dest, int originalSize)
+{
+    return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)(dest - 64 KB), NULL, 64 KB);
+}
+
+
+/*===== streaming decompression functions =====*/
+
+/*
+ * If you prefer dynamic allocation methods,
+ * LZ4_createStreamDecode()
+ * provides a pointer (void*) towards an initialized LZ4_streamDecode_t structure.
+ */
+LZ4_streamDecode_t* LZ4_createStreamDecode(void)
+{
+    LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOCATOR(1, sizeof(LZ4_streamDecode_t));
+    return lz4s;
+}
+
+int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)
+{
+    FREEMEM(LZ4_stream);
+    return 0;
+}
+
+/*!
+ * LZ4_setStreamDecode() :
+ * Use this function to instruct where to find the dictionary.
+ * This function is not necessary if previous data is still available where it was decoded.
+ * Loading a size of 0 is allowed (same effect as no dictionary).
+ * Return : 1 if OK, 0 if error
+ */
+int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)
+{
+    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+    lz4sd->prefixSize = (size_t) dictSize;
+    lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;
+    lz4sd->externalDict = NULL;
+    lz4sd->extDictSize  = 0;
+    return 1;
+}
+
+/*
+*_continue() :
+    These decoding functions allow decompression of multiple blocks in "streaming" mode.
+    Previously decoded blocks must still be available at the memory position where they were decoded.
+    If it's not possible, save the relevant part of decoded data into a safe buffer,
+    and indicate where it stands using LZ4_setStreamDecode()
+*/
+int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+    int result;
+
+    if (lz4sd->prefixEnd == (BYTE*)dest) {
+        result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                        endOnInputSize, full, 0,
+                                        usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize += result;
+        lz4sd->prefixEnd  += result;
+    } else {
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+        result = LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,
+                                        endOnInputSize, full, 0,
+                                        usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = result;
+        lz4sd->prefixEnd  = (BYTE*)dest + result;
+    }
+
+    return result;
+}
+
+int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize)
+{
+    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;
+    int result;
+
+    if (lz4sd->prefixEnd == (BYTE*)dest) {
+        result = LZ4_decompress_generic(source, dest, 0, originalSize,
+                                        endOnOutputSize, full, 0,
+                                        usingExtDict, lz4sd->prefixEnd - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize += originalSize;
+        lz4sd->prefixEnd  += originalSize;
+    } else {
+        lz4sd->extDictSize = lz4sd->prefixSize;
+        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;
+        result = LZ4_decompress_generic(source, dest, 0, originalSize,
+                                        endOnOutputSize, full, 0,
+                                        usingExtDict, (BYTE*)dest, lz4sd->externalDict, lz4sd->extDictSize);
+        if (result <= 0) return result;
+        lz4sd->prefixSize = originalSize;
+        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;
+    }
+
+    return result;
+}
+
+
+/*
+Advanced decoding functions :
+*_usingDict() :
+    These decoding functions work the same as "_continue" ones,
+    the dictionary must be explicitly provided within parameters
+*/
+
+FORCE_INLINE int LZ4_decompress_usingDict_generic(const char* source, char* dest, int compressedSize, int maxOutputSize, int safe, const char* dictStart, int dictSize)
+{
+    if (dictSize==0)
+        return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest, NULL, 0);
+    if (dictStart+dictSize == dest) {
+        if (dictSize >= (int)(64 KB - 1))
+            return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, withPrefix64k, (BYTE*)dest-64 KB, NULL, 0);
+        return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, noDict, (BYTE*)dest-dictSize, NULL, 0);
+    }
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, safe, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+    return LZ4_decompress_usingDict_generic(source, dest, compressedSize, maxOutputSize, 1, dictStart, dictSize);
+}
+
+int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)
+{
+    return LZ4_decompress_usingDict_generic(source, dest, 0, originalSize, 0, dictStart, dictSize);
+}
+
+/* debug function */
+int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize);
+}
+
+
+/*=*************************************************
+*  Obsolete Functions
+***************************************************/
+/* obsolete compression functions */
+int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) { return LZ4_compress_default(source, dest, inputSize, maxOutputSize); }
+int LZ4_compress(const char* source, char* dest, int inputSize) { return LZ4_compress_default(source, dest, inputSize, LZ4_compressBound(inputSize)); }
+int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); }
+int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); }
+int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, maxDstSize, 1); }
+int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) { return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); }
+
+/*
+These function names are deprecated and should no longer be used.
+They are only provided here for compatibility with older user programs.
+- LZ4_uncompress is totally equivalent to LZ4_decompress_fast
+- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe
+*/
+int LZ4_uncompress (const char* source, char* dest, int outputSize) { return LZ4_decompress_fast(source, dest, outputSize); }
+int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) { return LZ4_decompress_safe(source, dest, isize, maxOutputSize); }
+
+
+/* Obsolete Streaming functions */
+
+int LZ4_sizeofStreamState() { return LZ4_STREAMSIZE; }
+
+static void LZ4_init(LZ4_stream_t* lz4ds, BYTE* base)
+{
+    MEM_INIT(lz4ds, 0, sizeof(LZ4_stream_t));
+    lz4ds->internal_donotuse.bufferStart = base;
+}
+
+int LZ4_resetStreamState(void* state, char* inputBuffer)
+{
+    if ((((uptrval)state) & 3) != 0) return 1;   /* Error : pointer is not aligned on 4-bytes boundary */
+    LZ4_init((LZ4_stream_t*)state, (BYTE*)inputBuffer);
+    return 0;
+}
+
+void* LZ4_create (char* inputBuffer)
+{
+    LZ4_stream_t* lz4ds = (LZ4_stream_t*)ALLOCATOR(8, sizeof(LZ4_stream_t));
+    LZ4_init (lz4ds, (BYTE*)inputBuffer);
+    return lz4ds;
+}
+
+char* LZ4_slideInputBuffer (void* LZ4_Data)
+{
+    LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)LZ4_Data)->internal_donotuse;
+    int dictSize = LZ4_saveDict((LZ4_stream_t*)LZ4_Data, (char*)ctx->bufferStart, 64 KB);
+    return (char*)(ctx->bufferStart + dictSize);
+}
+
+/* Obsolete streaming decompression functions */
+
+int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)
+{
+    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, endOnInputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)
+{
+    return LZ4_decompress_generic(source, dest, 0, originalSize, endOnOutputSize, full, 0, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 64 KB);
+}
+
+#endif   /* LZ4_COMMONDEFS_ONLY */
+#endif /* NEED_COMPAT_LZ4 */
diff --git a/src/compat/compat-lz4.h b/src/compat/compat-lz4.h
new file mode 100644
index 0000000..0aae19c
--- /dev/null
+++ b/src/compat/compat-lz4.h
@@ -0,0 +1,463 @@
+/*
+ *  LZ4 - Fast LZ compression algorithm
+ *  Header File
+ *  Copyright (C) 2011-2016, Yann Collet.
+
+   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   You can contact the author at :
+    - LZ4 homepage : http://www.lz4.org
+    - LZ4 source repository : https://github.com/lz4/lz4
+*/
+#ifndef LZ4_H_2983827168210
+#define LZ4_H_2983827168210
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+/* --- Dependency --- */
+#include <stddef.h>   /* size_t */
+
+
+/**
+  Introduction
+
+  LZ4 is lossless compression algorithm, providing compression speed at 400 MB/s per core,
+  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
+  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
+
+  The LZ4 compression library provides in-memory compression and decompression functions.
+  Compression can be done in:
+    - a single step (described as Simple Functions)
+    - a single step, reusing a context (described in Advanced Functions)
+    - unbounded multiple steps (described as Streaming compression)
+
+  lz4.h provides block compression functions. It gives full buffer control to user.
+  Decompressing an lz4-compressed block also requires metadata (such as compressed size).
+  Each application is free to encode such metadata in whichever way it wants.
+
+  An additional format, called LZ4 frame specification (doc/lz4_Frame_format.md),
+  take care of encoding standard metadata alongside LZ4-compressed blocks.
+  If your application requires interoperability, it's recommended to use it.
+  A library is provided to take care of it, see lz4frame.h.
+*/
+
+/*^***************************************************************
+*  Export parameters
+*****************************************************************/
+/*
+*  LZ4_DLL_EXPORT :
+*  Enable exporting of functions when building a Windows DLL
+*/
+#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
+#  define LZ4LIB_API __declspec(dllexport)
+#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
+#  define LZ4LIB_API __declspec(dllimport) /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
+#else
+#  define LZ4LIB_API
+#endif
+
+
+/*========== Version =========== */
+#define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */
+#define LZ4_VERSION_MINOR    7    /* for new (non-breaking) interface capabilities */
+#define LZ4_VERSION_RELEASE  5    /* for tweaks, bug-fixes, or development */
+
+#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
+
+#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+#define LZ4_QUOTE(str) #str
+#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
+#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
+
+LZ4LIB_API int LZ4_versionNumber (void);
+LZ4LIB_API const char* LZ4_versionString (void);
+
+
+/*-************************************
+*  Tuning parameter
+**************************************/
+/*!
+ * LZ4_MEMORY_USAGE :
+ * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
+ * Increasing memory usage improves compression ratio
+ * Reduced memory usage can improve speed, due to cache effect
+ * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
+ */
+#define LZ4_MEMORY_USAGE 14
+
+
+/*-************************************
+*  Simple Functions
+**************************************/
+/*! LZ4_compress_default() :
+    Compresses 'sourceSize' bytes from buffer 'source'
+    into already allocated 'dest' buffer of size 'maxDestSize'.
+    Compression is guaranteed to succeed if 'maxDestSize' >= LZ4_compressBound(sourceSize).
+    It also runs faster, so it's a recommended setting.
+    If the function cannot compress 'source' into a more limited 'dest' budget,
+    compression stops *immediately*, and the function result is zero.
+    As a consequence, 'dest' content is not valid.
+    This function never writes outside 'dest' buffer, nor read outside 'source' buffer.
+        sourceSize  : Max supported value is LZ4_MAX_INPUT_VALUE
+        maxDestSize : full or partial size of buffer 'dest' (which must be already allocated)
+        return : the number of bytes written into buffer 'dest' (necessarily <= maxOutputSize)
+              or 0 if compression fails */
+LZ4LIB_API int LZ4_compress_default(const char* source, char* dest, int sourceSize, int maxDestSize);
+
+/*! LZ4_decompress_safe() :
+    compressedSize : is the precise full size of the compressed block.
+    maxDecompressedSize : is the size of destination buffer, which must be already allocated.
+    return : the number of bytes decompressed into destination buffer (necessarily <= maxDecompressedSize)
+             If destination buffer is not large enough, decoding will stop and output an error code (<0).
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             This function is protected against buffer overflow exploits, including malicious data packets.
+             It never writes outside output buffer, nor reads outside input buffer.
+*/
+LZ4LIB_API int LZ4_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+
+
+/*-************************************
+*  Advanced Functions
+**************************************/
+#define LZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */
+#define LZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
+
+/*!
+LZ4_compressBound() :
+    Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
+    This function is primarily useful for memory allocation purposes (destination buffer size).
+    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
+    Note that LZ4_compress_default() compress faster when dest buffer size is >= LZ4_compressBound(srcSize)
+        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE
+        return : maximum output size in a "worst case" scenario
+              or 0, if input size is too large ( > LZ4_MAX_INPUT_SIZE)
+*/
+LZ4LIB_API int LZ4_compressBound(int inputSize);
+
+/*!
+LZ4_compress_fast() :
+    Same as LZ4_compress_default(), but allows to select an "acceleration" factor.
+    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
+    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
+    An acceleration value of "1" is the same as regular LZ4_compress_default()
+    Values <= 0 will be replaced by ACCELERATION_DEFAULT (see lz4.c), which is 1.
+*/
+LZ4LIB_API int LZ4_compress_fast (const char* source, char* dest, int sourceSize, int maxDestSize, int acceleration);
+
+
+/*!
+LZ4_compress_fast_extState() :
+    Same compression function, just using an externally allocated memory space to store compression state.
+    Use LZ4_sizeofState() to know how much memory must be allocated,
+    and allocate it on 8-bytes boundaries (using malloc() typically).
+    Then, provide it as 'void* state' to compression function.
+*/
+LZ4LIB_API int LZ4_sizeofState(void);
+LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* source, char* dest, int inputSize, int maxDestSize, int acceleration);
+
+
+/*!
+LZ4_compress_destSize() :
+    Reverse the logic, by compressing as much data as possible from 'source' buffer
+    into already allocated buffer 'dest' of size 'targetDestSize'.
+    This function either compresses the entire 'source' content into 'dest' if it's large enough,
+    or fill 'dest' buffer completely with as much data as possible from 'source'.
+        *sourceSizePtr : will be modified to indicate how many bytes where read from 'source' to fill 'dest'.
+                         New value is necessarily <= old value.
+        return : Nb bytes written into 'dest' (necessarily <= targetDestSize)
+              or 0 if compression fails
+*/
+LZ4LIB_API int LZ4_compress_destSize (const char* source, char* dest, int* sourceSizePtr, int targetDestSize);
+
+
+/*!
+LZ4_decompress_fast() :
+    originalSize : is the original and therefore uncompressed size
+    return : the number of bytes read from the source buffer (in other words, the compressed size)
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             Destination buffer must be already allocated. Its size must be a minimum of 'originalSize' bytes.
+    note : This function fully respect memory boundaries for properly formed compressed data.
+           It is a bit faster than LZ4_decompress_safe().
+           However, it does not provide any protection against intentionally modified data stream (malicious input).
+           Use this function in trusted environment only (data to decode comes from a trusted source).
+*/
+LZ4LIB_API int LZ4_decompress_fast (const char* source, char* dest, int originalSize);
+
+/*!
+LZ4_decompress_safe_partial() :
+    This function decompress a compressed block of size 'compressedSize' at position 'source'
+    into destination buffer 'dest' of size 'maxDecompressedSize'.
+    The function tries to stop decompressing operation as soon as 'targetOutputSize' has been reached,
+    reducing decompression time.
+    return : the number of bytes decoded in the destination buffer (necessarily <= maxDecompressedSize)
+       Note : this number can be < 'targetOutputSize' should the compressed block to decode be smaller.
+             Always control how many bytes were decoded.
+             If the source stream is detected malformed, the function will stop decoding and return a negative result.
+             This function never writes outside of output buffer, and never reads outside of input buffer. It is therefore protected against malicious data packets
+*/
+LZ4LIB_API int LZ4_decompress_safe_partial (const char* source, char* dest, int compressedSize, int targetOutputSize, int maxDecompressedSize);
+
+
+/*-*********************************************
+*  Streaming Compression Functions
+***********************************************/
+typedef union LZ4_stream_u LZ4_stream_t;   /* incomplete type (defined later) */
+
+/*! LZ4_createStream() and LZ4_freeStream() :
+ *  LZ4_createStream() will allocate and initialize an `LZ4_stream_t` structure.
+ *  LZ4_freeStream() releases its memory.
+ */
+LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
+LZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_resetStream() :
+ *  An LZ4_stream_t structure can be allocated once and re-used multiple times.
+ *  Use this function to init an allocated `LZ4_stream_t` structure and start a new compression.
+ */
+LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
+
+/*! LZ4_loadDict() :
+ *  Use this function to load a static dictionary into LZ4_stream.
+ *  Any previous data will be forgotten, only 'dictionary' will remain in memory.
+ *  Loading a size of 0 is allowed.
+ *  Return : dictionary size, in bytes (necessarily <= 64 KB)
+ */
+LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
+
+/*! LZ4_compress_fast_continue() :
+ *  Compress buffer content 'src', using data from previously compressed blocks as dictionary to improve compression ratio.
+ *  Important : Previous data blocks are assumed to still be present and unmodified !
+ *  'dst' buffer must be already allocated.
+ *  If maxDstSize >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
+ *  If not, and if compressed data cannot fit into 'dst' buffer size, compression stops, and function returns a zero.
+ */
+LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int maxDstSize, int acceleration);
+
+/*! LZ4_saveDict() :
+ *  If previously compressed data block is not guaranteed to remain available at its memory location,
+ *  save it into a safer place (char* safeBuffer).
+ *  Note : you don't need to call LZ4_loadDict() afterwards,
+ *         dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue().
+ *  Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.
+ */
+LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int dictSize);
+
+
+/*-**********************************************
+*  Streaming Decompression Functions
+*  Bufferless synchronous API
+************************************************/
+typedef union LZ4_streamDecode_u LZ4_streamDecode_t;   /* incomplete type (defined later) */
+
+/* creation / destruction of streaming decompression tracking structure */
+LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
+LZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
+
+/*! LZ4_setStreamDecode() :
+ *  Use this function to instruct where to find the dictionary.
+ *  Setting a size of 0 is allowed (same effect as reset).
+ *  @return : 1 if OK, 0 if error
+ */
+LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
+
+/*!
+LZ4_decompress_*_continue() :
+    These decoding functions allow decompression of multiple blocks in "streaming" mode.
+    Previously decoded blocks *must* remain available at the memory position where they were decoded (up to 64 KB)
+    In the case of a ring buffers, decoding buffer must be either :
+    - Exactly same size as encoding buffer, with same update rule (block boundaries at same positions)
+      In which case, the decoding & encoding ring buffer can have any size, including very small ones ( < 64 KB).
+    - Larger than encoding buffer, by a minimum of maxBlockSize more bytes.
+      maxBlockSize is implementation dependent. It's the maximum size you intend to compress into a single block.
+      In which case, encoding and decoding buffers do not need to be synchronized,
+      and encoding ring buffer can have any size, including small ones ( < 64 KB).
+    - _At least_ 64 KB + 8 bytes + maxBlockSize.
+      In which case, encoding and decoding buffers do not need to be synchronized,
+      and encoding ring buffer can have any size, including larger than decoding buffer.
+    Whenever these conditions are not possible, save the last 64KB of decoded data into a safe buffer,
+    and indicate where it is saved using LZ4_setStreamDecode()
+*/
+LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxDecompressedSize);
+LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize);
+
+
+/*! LZ4_decompress_*_usingDict() :
+ *  These decoding functions work the same as
+ *  a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
+ *  They are stand-alone, and don't need an LZ4_streamDecode_t structure.
+ */
+LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize);
+LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* source, char* dest, int originalSize, const char* dictStart, int dictSize);
+
+
+/*^**********************************************
+ * !!!!!!   STATIC LINKING ONLY   !!!!!!
+ ***********************************************/
+/*-************************************
+ *  Private definitions
+ **************************************
+ * Do not use these definitions.
+ * They are exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
+ * Using these definitions will expose code to API and/or ABI break in future versions of the library.
+ **************************************/
+#define LZ4_HASHLOG   (LZ4_MEMORY_USAGE-2)
+#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
+#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)       /* required as macro for static allocation */
+
+#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+#include <stdint.h>
+
+typedef struct {
+    uint32_t hashTable[LZ4_HASH_SIZE_U32];
+    uint32_t currentOffset;
+    uint32_t initCheck;
+    const uint8_t* dictionary;
+    uint8_t* bufferStart;   /* obsolete, used for slideInputBuffer */
+    uint32_t dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+    const uint8_t* externalDict;
+    size_t extDictSize;
+    const uint8_t* prefixEnd;
+    size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#else
+
+typedef struct {
+    unsigned int hashTable[LZ4_HASH_SIZE_U32];
+    unsigned int currentOffset;
+    unsigned int initCheck;
+    const unsigned char* dictionary;
+    unsigned char* bufferStart;   /* obsolete, used for slideInputBuffer */
+    unsigned int dictSize;
+} LZ4_stream_t_internal;
+
+typedef struct {
+    const unsigned char* externalDict;
+    size_t extDictSize;
+    const unsigned char* prefixEnd;
+    size_t prefixSize;
+} LZ4_streamDecode_t_internal;
+
+#endif
+
+/*!
+ * LZ4_stream_t :
+ * information structure to track an LZ4 stream.
+ * init this structure before first use.
+ * note : only use in association with static linking !
+ *        this definition is not API/ABI safe,
+ *        and may change in a future version !
+ */
+#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4)
+#define LZ4_STREAMSIZE     (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
+union LZ4_stream_u {
+    unsigned long long table[LZ4_STREAMSIZE_U64];
+    LZ4_stream_t_internal internal_donotuse;
+} ;  /* previously typedef'd to LZ4_stream_t */
+
+
+/*!
+ * LZ4_streamDecode_t :
+ * information structure to track an LZ4 stream during decompression.
+ * init this structure  using LZ4_setStreamDecode (or memset()) before first use
+ * note : only use in association with static linking !
+ *        this definition is not API/ABI safe,
+ *        and may change in a future version !
+ */
+#define LZ4_STREAMDECODESIZE_U64  4
+#define LZ4_STREAMDECODESIZE     (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
+union LZ4_streamDecode_u {
+    unsigned long long table[LZ4_STREAMDECODESIZE_U64];
+    LZ4_streamDecode_t_internal internal_donotuse;
+} ;   /* previously typedef'd to LZ4_streamDecode_t */
+
+
+/*=************************************
+*  Obsolete Functions
+**************************************/
+/* Deprecation warnings */
+/* Should these warnings be a problem,
+   it is generally possible to disable them,
+   typically with -Wno-deprecated-declarations for gcc
+   or _CRT_SECURE_NO_WARNINGS in Visual.
+   Otherwise, it's also possible to define LZ4_DISABLE_DEPRECATE_WARNINGS */
+#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
+#  define LZ4_DEPRECATED(message)   /* disable deprecation warnings */
+#else
+#  define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+#    define LZ4_DEPRECATED(message) [[deprecated(message)]]
+#  elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
+#    define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
+#  elif (LZ4_GCC_VERSION >= 301)
+#    define LZ4_DEPRECATED(message) __attribute__((deprecated))
+#  elif defined(_MSC_VER)
+#    define LZ4_DEPRECATED(message) __declspec(deprecated(message))
+#  else
+#    pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
+#    define LZ4_DEPRECATED(message)
+#  endif
+#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
+
+/* Obsolete compression functions */
+LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress               (const char* source, char* dest, int sourceSize);
+LZ4_DEPRECATED("use LZ4_compress_default() instead") int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_continue                (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
+LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") int LZ4_compress_limitedOutput_continue  (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
+
+/* Obsolete decompression functions */
+/* These function names are completely deprecated and must no longer be used.
+   They are only provided in lz4.c for compatibility with older programs.
+    - LZ4_uncompress is the same as LZ4_decompress_fast
+    - LZ4_uncompress_unknownOutputSize is the same as LZ4_decompress_safe
+   These function prototypes are now disabled; uncomment them only if you really need them.
+   It is highly recommended to stop using these prototypes and migrate to maintained ones */
+/* int LZ4_uncompress (const char* source, char* dest, int outputSize); */
+/* int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); */
+
+/* Obsolete streaming functions; use new streaming interface whenever possible */
+LZ4_DEPRECATED("use LZ4_createStream() instead") void* LZ4_create (char* inputBuffer);
+LZ4_DEPRECATED("use LZ4_createStream() instead") int   LZ4_sizeofStreamState(void);
+LZ4_DEPRECATED("use LZ4_resetStream() instead")  int   LZ4_resetStreamState(void* state, char* inputBuffer);
+LZ4_DEPRECATED("use LZ4_saveDict() instead")     char* LZ4_slideInputBuffer (void* state);
+
+/* Obsolete streaming decoding functions */
+LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
+LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* LZ4_H_2983827168210 */
diff --git a/src/compat/compat-versionhelpers.h b/src/compat/compat-versionhelpers.h
new file mode 100644
index 0000000..251fb04
--- /dev/null
+++ b/src/compat/compat-versionhelpers.h
@@ -0,0 +1,106 @@
+/**
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within this package.
+ */
+
+#ifndef _INC_VERSIONHELPERS
+#define _INC_VERSIONHELPERS
+
+#include <winapifamily.h>
+
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIDL__)
+
+#ifdef __cplusplus
+#define VERSIONHELPERAPI inline bool
+#else
+#define VERSIONHELPERAPI FORCEINLINE BOOL
+#endif
+
+#define _WIN32_WINNT_WINBLUE    0x0603
+
+VERSIONHELPERAPI
+IsWindowsVersionOrGreater(WORD major, WORD minor, WORD servpack)
+{
+    OSVERSIONINFOEXW vi = {sizeof(vi),major,minor,0,0,{0},servpack};
+    return VerifyVersionInfoW(&vi, VER_MAJORVERSION|VER_MINORVERSION|VER_SERVICEPACKMAJOR,
+                              VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0,
+                                                                                          VER_MAJORVERSION,VER_GREATER_EQUAL),
+                                                                      VER_MINORVERSION,VER_GREATER_EQUAL),
+                                                  VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL));
+}
+
+VERSIONHELPERAPI
+IsWindowsXPOrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0);
+}
+
+VERSIONHELPERAPI
+IsWindowsXPSP1OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);
+}
+
+VERSIONHELPERAPI
+IsWindowsXPSP2OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);
+}
+
+VERSIONHELPERAPI
+IsWindowsXPSP3OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);
+}
+
+VERSIONHELPERAPI
+IsWindowsVistaOrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);
+}
+
+VERSIONHELPERAPI
+IsWindowsVistaSP1OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);
+}
+
+VERSIONHELPERAPI
+IsWindowsVistaSP2OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);
+}
+
+VERSIONHELPERAPI
+IsWindows7OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);
+}
+
+VERSIONHELPERAPI
+IsWindows7SP1OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
+}
+
+VERSIONHELPERAPI
+IsWindows8OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
+}
+
+VERSIONHELPERAPI
+IsWindows8Point1OrGreater(void)
+{
+    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);
+}
+
+VERSIONHELPERAPI
+IsWindowsServer(void)
+{
+    OSVERSIONINFOEXW vi = {sizeof(vi),0,0,0,0,{0},0,0,0,VER_NT_WORKSTATION};
+    return !VerifyVersionInfoW(&vi, VER_PRODUCT_TYPE, VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL));
+}
+
+#endif /* if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIDL__) */
+#endif /* ifndef _INC_VERSIONHELPERS */
diff --git a/src/compat/compat.h b/src/compat/compat.h
new file mode 100644
index 0000000..d522898
--- /dev/null
+++ b/src/compat/compat.h
@@ -0,0 +1,73 @@
+/*
+ *  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) 2011 - David Sommerseth <davids@redhat.com>
+ *
+ *  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 COMPAT_H
+#define COMPAT_H
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+#ifdef HAVE_WS2TCPIP_H
+#include <ws2tcpip.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifndef HAVE_DIRNAME
+char *dirname(char *str);
+
+#endif /* HAVE_DIRNAME */
+
+#ifndef HAVE_BASENAME
+char *basename(char *str);
+
+#endif /* HAVE_BASENAME */
+
+#ifndef HAVE_GETTIMEOFDAY
+int gettimeofday(struct timeval *tv, void *tz);
+
+#endif
+
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose);
+
+#endif
+
+#ifndef HAVE_INET_NTOP
+const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
+
+#endif
+
+#ifndef HAVE_INET_PTON
+int inet_pton(int af, const char *src, void *dst);
+
+#endif
+
+#endif /* COMPAT_H */
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
new file mode 100644
index 0000000..1f91e59
--- /dev/null
+++ b/src/openvpn/Makefile.am
@@ -0,0 +1,138 @@
+#
+#  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+#  Copyright (C) 2006-2012 Alon Bar-Lev <alon.barlev@gmail.com>
+#
+
+include $(top_srcdir)/ltrc.inc
+
+MAINTAINERCLEANFILES = \
+	$(srcdir)/Makefile.in
+
+EXTRA_DIST = \
+	openvpn.vcxproj \
+	openvpn.vcxproj.filters
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/src/compat
+
+AM_CFLAGS = \
+	$(TAP_CFLAGS) \
+	$(OPTIONAL_CRYPTO_CFLAGS) \
+	$(OPTIONAL_LZO_CFLAGS) \
+	$(OPTIONAL_LZ4_CFLAGS) \
+	$(OPTIONAL_PKCS11_HELPER_CFLAGS) \
+	$(OPTIONAL_INOTIFY_CFLAGS) \
+	-DPLUGIN_LIBDIR=\"${plugindir}\"
+
+if WIN32
+# we want unicode entry point but not the macro
+AM_CFLAGS += -municode -UUNICODE
+endif
+
+sbin_PROGRAMS = openvpn
+
+openvpn_SOURCES = \
+	argv.c argv.h \
+	base64.c base64.h \
+	basic.h \
+	buffer.c buffer.h \
+	circ_list.h \
+	clinat.c clinat.h \
+	common.h \
+	comp.c comp.h compstub.c \
+	comp-lz4.c comp-lz4.h \
+	crypto.c crypto.h crypto_backend.h \
+	crypto_openssl.c crypto_openssl.h \
+	crypto_mbedtls.c crypto_mbedtls.h \
+	dhcp.c dhcp.h \
+	errlevel.h \
+	error.c error.h \
+	event.c event.h \
+	fdmisc.c fdmisc.h \
+	forward.c forward.h forward-inline.h \
+	fragment.c fragment.h \
+	gremlin.c gremlin.h \
+	helper.c helper.h \
+	httpdigest.c httpdigest.h \
+	lladdr.c lladdr.h \
+	init.c init.h \
+	integer.h \
+	interval.c interval.h \
+	list.c list.h \
+	lzo.c lzo.h \
+	manage.c manage.h \
+	mbuf.c mbuf.h \
+	memdbg.h \
+	misc.c misc.h \
+	platform.c platform.h \
+	console.c console.h console_builtin.c console_systemd.c \
+	mroute.c mroute.h \
+	mss.c mss.h \
+	mstats.c mstats.h \
+	mtcp.c mtcp.h \
+	mtu.c mtu.h \
+	mudp.c mudp.h \
+	multi.c multi.h \
+	ntlm.c ntlm.h \
+	occ.c occ.h occ-inline.h \
+	openssl_compat.h \
+	pkcs11.c pkcs11.h pkcs11_backend.h \
+	pkcs11_openssl.c \
+	pkcs11_mbedtls.c \
+	openvpn.c openvpn.h \
+	options.c options.h \
+	otime.c otime.h \
+	packet_id.c packet_id.h \
+	perf.c perf.h \
+	pf.c pf.h pf-inline.h \
+	ping.c ping.h ping-inline.h \
+	plugin.c plugin.h \
+	pool.c pool.h \
+	proto.c proto.h \
+	proxy.c proxy.h \
+	ps.c ps.h \
+	push.c push.h \
+	pushlist.h \
+	reliable.c reliable.h \
+	route.c route.h \
+	schedule.c schedule.h \
+	session_id.c session_id.h \
+	shaper.c shaper.h \
+	sig.c sig.h \
+	socket.c socket.h \
+	socks.c socks.h \
+	ssl.c ssl.h  ssl_backend.h \
+	ssl_openssl.c ssl_openssl.h \
+	ssl_mbedtls.c ssl_mbedtls.h \
+	ssl_common.h \
+	ssl_verify.c ssl_verify.h ssl_verify_backend.h \
+	ssl_verify_openssl.c ssl_verify_openssl.h \
+	ssl_verify_mbedtls.c ssl_verify_mbedtls.h \
+	status.c status.h \
+	syshead.h \
+	tls_crypt.c tls_crypt.h \
+	tun.c tun.h \
+	win32.h win32.c \
+	cryptoapi.h cryptoapi.c
+openvpn_LDADD = \
+	$(top_builddir)/src/compat/libcompat.la \
+	$(SOCKETS_LIBS) \
+	$(OPTIONAL_LZO_LIBS) \
+	$(OPTIONAL_LZ4_LIBS) \
+	$(OPTIONAL_PKCS11_HELPER_LIBS) \
+	$(OPTIONAL_CRYPTO_LIBS) \
+	$(OPTIONAL_SELINUX_LIBS) \
+	$(OPTIONAL_SYSTEMD_LIBS) \
+	$(OPTIONAL_DL_LIBS) \
+	$(OPTIONAL_INOTIFY_LIBS)
+if WIN32
+openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h
+openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt
+endif
diff --git a/src/openvpn/argv.c b/src/openvpn/argv.c
new file mode 100644
index 0000000..7d06951
--- /dev/null
+++ b/src/openvpn/argv.c
@@ -0,0 +1,360 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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.
+ *
+ *
+ *  A printf-like function (that only recognizes a subset of standard printf
+ *  format operators) that prints arguments to an argv list instead
+ *  of a standard string.  This is used to build up argv arrays for passing
+ *  to execve.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include "argv.h"
+#include "integer.h"
+#include "options.h"
+
+static void
+argv_init(struct argv *a)
+{
+    a->capacity = 0;
+    a->argc = 0;
+    a->argv = NULL;
+}
+
+struct argv
+argv_new(void)
+{
+    struct argv ret;
+    argv_init(&ret);
+    return ret;
+}
+
+void
+argv_reset(struct argv *a)
+{
+    size_t i;
+    for (i = 0; i < a->argc; ++i)
+    {
+        free(a->argv[i]);
+    }
+    free(a->argv);
+    argv_init(a);
+}
+
+static void
+argv_extend(struct argv *a, const size_t newcap)
+{
+    if (newcap > a->capacity)
+    {
+        char **newargv;
+        size_t i;
+        ALLOC_ARRAY_CLEAR(newargv, char *, newcap);
+        for (i = 0; i < a->argc; ++i)
+        {
+            newargv[i] = a->argv[i];
+        }
+        free(a->argv);
+        a->argv = newargv;
+        a->capacity = newcap;
+    }
+}
+
+static void
+argv_grow(struct argv *a, const size_t add)
+{
+    const size_t newargc = a->argc + add + 1;
+    ASSERT(newargc > a->argc);
+    argv_extend(a, adjust_power_of_2(newargc));
+}
+
+static void
+argv_append(struct argv *a, char *str)  /* str must have been malloced or be NULL */
+{
+    argv_grow(a, 1);
+    a->argv[a->argc++] = str;
+}
+
+static struct argv
+argv_clone(const struct argv *a, const size_t headroom)
+{
+    struct argv r;
+    size_t i;
+
+    argv_init(&r);
+    for (i = 0; i < headroom; ++i)
+    {
+        argv_append(&r, NULL);
+    }
+    if (a)
+    {
+        for (i = 0; i < a->argc; ++i)
+        {
+            argv_append(&r, string_alloc(a->argv[i], NULL));
+        }
+    }
+    return r;
+}
+
+struct argv
+argv_insert_head(const struct argv *a, const char *head)
+{
+    struct argv r;
+    r = argv_clone(a, 1);
+    r.argv[0] = string_alloc(head, NULL);
+    return r;
+}
+
+static char *
+argv_term(const char **f)
+{
+    const char *p = *f;
+    const char *term = NULL;
+    size_t termlen = 0;
+
+    if (*p == '\0')
+    {
+        return NULL;
+    }
+
+    while (true)
+    {
+        const int c = *p;
+        if (c == '\0')
+        {
+            break;
+        }
+        if (term)
+        {
+            if (!isspace(c))
+            {
+                ++termlen;
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            if (!isspace(c))
+            {
+                term = p;
+                termlen = 1;
+            }
+        }
+        ++p;
+    }
+    *f = p;
+
+    if (term)
+    {
+        char *ret;
+        ASSERT(termlen > 0);
+        ret = malloc(termlen + 1);
+        check_malloc_return(ret);
+        memcpy(ret, term, termlen);
+        ret[termlen] = '\0';
+        return ret;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+const char *
+argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags)
+{
+    if (a->argv)
+    {
+        return print_argv((const char **)a->argv, gc, flags);
+    }
+    else
+    {
+        return "";
+    }
+}
+
+void
+argv_msg(const int msglev, const struct argv *a)
+{
+    struct gc_arena gc = gc_new();
+    msg(msglev, "%s", argv_str(a, &gc, 0));
+    gc_free(&gc);
+}
+
+void
+argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix)
+{
+    struct gc_arena gc = gc_new();
+    msg(msglev, "%s: %s", prefix, argv_str(a, &gc, 0));
+    gc_free(&gc);
+}
+
+static void
+argv_printf_arglist(struct argv *a, const char *format, va_list arglist)
+{
+    char *term;
+    const char *f = format;
+
+    argv_extend(a, 1); /* ensure trailing NULL */
+
+    while ((term = argv_term(&f)) != NULL)
+    {
+        if (term[0] == '%')
+        {
+            if (!strcmp(term, "%s"))
+            {
+                char *s = va_arg(arglist, char *);
+                if (!s)
+                {
+                    s = "";
+                }
+                argv_append(a, string_alloc(s, NULL));
+            }
+            else if (!strcmp(term, "%d"))
+            {
+                char numstr[64];
+                openvpn_snprintf(numstr, sizeof(numstr), "%d", va_arg(arglist, int));
+                argv_append(a, string_alloc(numstr, NULL));
+            }
+            else if (!strcmp(term, "%u"))
+            {
+                char numstr[64];
+                openvpn_snprintf(numstr, sizeof(numstr), "%u", va_arg(arglist, unsigned int));
+                argv_append(a, string_alloc(numstr, NULL));
+            }
+            else if (!strcmp(term, "%lu"))
+            {
+                char numstr[64];
+                openvpn_snprintf(numstr, sizeof(numstr), "%lu",
+                                 va_arg(arglist, unsigned long));
+                argv_append(a, string_alloc(numstr, NULL));
+            }
+            else if (!strcmp(term, "%s/%d"))
+            {
+                char numstr[64];
+                char *s = va_arg(arglist, char *);
+
+                if (!s)
+                {
+                    s = "";
+                }
+
+                openvpn_snprintf(numstr, sizeof(numstr), "%d", va_arg(arglist, int));
+
+                {
+                    const size_t len = strlen(s) + strlen(numstr) + 2;
+                    char *combined = (char *) malloc(len);
+                    check_malloc_return(combined);
+
+                    strcpy(combined, s);
+                    strcat(combined, "/");
+                    strcat(combined, numstr);
+                    argv_append(a, combined);
+                }
+            }
+            else if (!strcmp(term, "%s%sc"))
+            {
+                char *s1 = va_arg(arglist, char *);
+                char *s2 = va_arg(arglist, char *);
+                char *combined;
+
+                if (!s1)
+                {
+                    s1 = "";
+                }
+                if (!s2)
+                {
+                    s2 = "";
+                }
+                combined = (char *) malloc(strlen(s1) + strlen(s2) + 1);
+                check_malloc_return(combined);
+                strcpy(combined, s1);
+                strcat(combined, s2);
+                argv_append(a, combined);
+            }
+            else
+            {
+                ASSERT(0);
+            }
+            free(term);
+        }
+        else
+        {
+            argv_append(a, term);
+        }
+    }
+}
+
+void
+argv_printf(struct argv *a, const char *format, ...)
+{
+    va_list arglist;
+    argv_reset(a);
+    va_start(arglist, format);
+    argv_printf_arglist(a, format, arglist);
+    va_end(arglist);
+}
+
+void
+argv_printf_cat(struct argv *a, const char *format, ...)
+{
+    va_list arglist;
+    va_start(arglist, format);
+    argv_printf_arglist(a, format, arglist);
+    va_end(arglist);
+}
+
+void
+argv_parse_cmd(struct argv *a, const char *s)
+{
+    int nparms;
+    char *parms[MAX_PARMS + 1];
+    struct gc_arena gc = gc_new();
+
+    argv_reset(a);
+    argv_extend(a, 1); /* ensure trailing NULL */
+
+    nparms = parse_line(s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc);
+    if (nparms)
+    {
+        int i;
+        for (i = 0; i < nparms; ++i)
+        {
+            argv_append(a, string_alloc(parms[i], NULL));
+        }
+    }
+    else
+    {
+        argv_append(a, string_alloc(s, NULL));
+    }
+
+    gc_free(&gc);
+}
diff --git a/src/openvpn/argv.h b/src/openvpn/argv.h
new file mode 100644
index 0000000..9d9f387
--- /dev/null
+++ b/src/openvpn/argv.h
@@ -0,0 +1,75 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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.
+ *
+ *
+ *  A printf-like function (that only recognizes a subset of standard printf
+ *  format operators) that prints arguments to an argv list instead
+ *  of a standard string.  This is used to build up argv arrays for passing
+ *  to execve.
+ */
+
+#ifndef ARGV_H
+#define ARGV_H
+
+#include "buffer.h"
+
+struct argv {
+    size_t capacity;
+    size_t argc;
+    char **argv;
+};
+
+struct argv argv_new(void);
+
+void argv_reset(struct argv *a);
+
+const char *argv_str(const struct argv *a, struct gc_arena *gc, const unsigned int flags);
+
+struct argv argv_insert_head(const struct argv *a, const char *head);
+
+void argv_msg(const int msglev, const struct argv *a);
+
+void argv_msg_prefix(const int msglev, const struct argv *a, const char *prefix);
+
+void argv_parse_cmd(struct argv *a, const char *s);
+
+void argv_printf(struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+__attribute__ ((format(gnu_printf, 2, 3)))
+#else
+__attribute__ ((format(__printf__, 2, 3)))
+#endif
+#endif
+;
+
+void argv_printf_cat(struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+__attribute__ ((format(gnu_printf, 2, 3)))
+#else
+__attribute__ ((format(__printf__, 2, 3)))
+#endif
+#endif
+;
+
+#endif /* ifndef ARGV_H */
diff --git a/src/openvpn/base64.c b/src/openvpn/base64.c
new file mode 100644
index 0000000..0ac65e9
--- /dev/null
+++ b/src/openvpn/base64.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include "base64.h"
+
+#include "memdbg.h"
+
+static char base64_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+/*
+ * base64 encode input data of length size to malloced
+ * buffer which is returned as *str.  Returns string
+ * length of *str.
+ */
+int
+openvpn_base64_encode(const void *data, int size, char **str)
+{
+    char *s, *p;
+    int i;
+    int c;
+    const unsigned char *q;
+
+    if (size < 0)
+    {
+        return -1;
+    }
+    p = s = (char *) malloc(size * 4 / 3 + 4);
+    if (p == NULL)
+    {
+        return -1;
+    }
+    q = (const unsigned char *) data;
+    i = 0;
+    for (i = 0; i < size; )
+    {
+        c = q[i++];
+        c *= 256;
+        if (i < size)
+        {
+            c += q[i];
+        }
+        i++;
+        c *= 256;
+        if (i < size)
+        {
+            c += q[i];
+        }
+        i++;
+        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+        p[1] = base64_chars[(c & 0x0003f000) >> 12];
+        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+        p[3] = base64_chars[(c & 0x0000003f) >> 0];
+        if (i > size)
+        {
+            p[3] = '=';
+        }
+        if (i > size + 1)
+        {
+            p[2] = '=';
+        }
+        p += 4;
+    }
+    *p = 0;
+    *str = s;
+    return strlen(s);
+}
+
+static int
+pos(char c)
+{
+    char *p;
+    for (p = base64_chars; *p; p++)
+    {
+        if (*p == c)
+        {
+            return p - base64_chars;
+        }
+    }
+    return -1;
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static unsigned int
+token_decode(const char *token)
+{
+    int i;
+    unsigned int val = 0;
+    int marker = 0;
+    if (!token[0] || !token[1] || !token[2] || !token[3])
+    {
+        return DECODE_ERROR;
+    }
+    for (i = 0; i < 4; i++)
+    {
+        val *= 64;
+        if (token[i] == '=')
+        {
+            marker++;
+        }
+        else if (marker > 0)
+        {
+            return DECODE_ERROR;
+        }
+        else
+        {
+            val += pos(token[i]);
+        }
+    }
+    if (marker > 2)
+    {
+        return DECODE_ERROR;
+    }
+    return (marker << 24) | val;
+}
+/*
+ * Decode base64 str, outputting data to buffer
+ * at data of length size.  Return length of
+ * decoded data written or -1 on error or overflow.
+ */
+int
+openvpn_base64_decode(const char *str, void *data, int size)
+{
+    const char *p;
+    unsigned char *q;
+    unsigned char *e = NULL;
+
+    q = data;
+    if (size >= 0)
+    {
+        e = q + size;
+    }
+    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4)
+    {
+        unsigned int val = token_decode(p);
+        unsigned int marker = (val >> 24) & 0xff;
+        if (val == DECODE_ERROR)
+        {
+            return -1;
+        }
+        if (e && q >= e)
+        {
+            return -1;
+        }
+        *q++ = (val >> 16) & 0xff;
+        if (marker < 2)
+        {
+            if (e && q >= e)
+            {
+                return -1;
+            }
+            *q++ = (val >> 8) & 0xff;
+        }
+        if (marker < 1)
+        {
+            if (e && q >= e)
+            {
+                return -1;
+            }
+            *q++ = val & 0xff;
+        }
+    }
+    return q - (unsigned char *) data;
+}
diff --git a/src/openvpn/base64.h b/src/openvpn/base64.h
new file mode 100644
index 0000000..5679bc9
--- /dev/null
+++ b/src/openvpn/base64.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+int openvpn_base64_encode(const void *data, int size, char **str);
+
+int openvpn_base64_decode(const char *str, void *data, int size);
+
+#endif
diff --git a/src/openvpn/basic.h b/src/openvpn/basic.h
new file mode 100644
index 0000000..eb9f211
--- /dev/null
+++ b/src/openvpn/basic.h
@@ -0,0 +1,37 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 BASIC_H
+#define BASIC_H
+
+#define BOOL_CAST(x) ((x) ? (true) : (false))
+
+/* size of an array */
+#define SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/* clear an object (may be optimized away, use secure_memzero() to erase secrets) */
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+#define IPV4_NETMASK_HOST 0xffffffffU
+
+#endif
diff --git a/src/openvpn/block_dns.c b/src/openvpn/block_dns.c
new file mode 100644
index 0000000..889d6bb
--- /dev/null
+++ b/src/openvpn/block_dns.c
@@ -0,0 +1,431 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *                2015-2016  <iam@valdikss.org.ru>
+ *                2016 Selva Nair <selva.nair@gmail.com>
+ *
+ *  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
+#ifdef HAVE_CONFIG_VERSION_H
+#include "config-version.h"
+#endif
+
+#include "syshead.h"
+
+#ifdef _WIN32
+
+#include <fwpmu.h>
+#include <initguid.h>
+#include <fwpmtypes.h>
+#include <winsock2.h>
+#include <ws2ipdef.h>
+#include <iphlpapi.h>
+#include "block_dns.h"
+
+/*
+ * WFP-related defines and GUIDs not in mingw32
+ */
+
+#ifndef FWPM_SESSION_FLAG_DYNAMIC
+#define FWPM_SESSION_FLAG_DYNAMIC 0x00000001
+#endif
+
+/* c38d57d1-05a7-4c33-904f-7fbceee60e82 */
+DEFINE_GUID(
+    FWPM_LAYER_ALE_AUTH_CONNECT_V4,
+    0xc38d57d1,
+    0x05a7,
+    0x4c33,
+    0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82
+    );
+
+/* 4a72393b-319f-44bc-84c3-ba54dcb3b6b4 */
+DEFINE_GUID(
+    FWPM_LAYER_ALE_AUTH_CONNECT_V6,
+    0x4a72393b,
+    0x319f,
+    0x44bc,
+    0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4
+    );
+
+/* d78e1e87-8644-4ea5-9437-d809ecefc971 */
+DEFINE_GUID(
+    FWPM_CONDITION_ALE_APP_ID,
+    0xd78e1e87,
+    0x8644,
+    0x4ea5,
+    0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71
+    );
+
+/* c35a604d-d22b-4e1a-91b4-68f674ee674b */
+DEFINE_GUID(
+    FWPM_CONDITION_IP_REMOTE_PORT,
+    0xc35a604d,
+    0xd22b,
+    0x4e1a,
+    0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b
+    );
+
+/* 4cd62a49-59c3-4969-b7f3-bda5d32890a4 */
+DEFINE_GUID(
+    FWPM_CONDITION_IP_LOCAL_INTERFACE,
+    0x4cd62a49,
+    0x59c3,
+    0x4969,
+    0xb7, 0xf3, 0xbd, 0xa5, 0xd3, 0x28, 0x90, 0xa4
+    );
+
+/* UUID of WFP sublayer used by all instances of openvpn
+ * 2f660d7e-6a37-11e6-a181-001e8c6e04a2 */
+DEFINE_GUID(
+    OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER,
+    0x2f660d7e,
+    0x6a37,
+    0x11e6,
+    0xa1, 0x81, 0x00, 0x1e, 0x8c, 0x6e, 0x04, 0xa2
+    );
+
+static WCHAR *FIREWALL_NAME = L"OpenVPN";
+
+VOID NETIOAPI_API_
+InitializeIpInterfaceEntry(PMIB_IPINTERFACE_ROW Row);
+
+/*
+ * Default msg handler does nothing
+ */
+static inline void
+default_msg_handler(DWORD err, const char *msg)
+{
+    return;
+}
+
+#define CHECK_ERROR(err, msg) \
+    if (err) { msg_handler(err, msg); goto out; }
+
+/*
+ * Add a persistent sublayer with specified uuid.
+ */
+static DWORD
+add_sublayer(GUID uuid)
+{
+    FWPM_SESSION0 session;
+    HANDLE engine = NULL;
+    DWORD err = 0;
+    FWPM_SUBLAYER0 sublayer;
+
+    memset(&session, 0, sizeof(session));
+    memset(&sublayer, 0, sizeof(sublayer));
+
+    err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
+    if (err != ERROR_SUCCESS)
+    {
+        goto out;
+    }
+
+    sublayer.subLayerKey = uuid;
+    sublayer.displayData.name = FIREWALL_NAME;
+    sublayer.displayData.description = FIREWALL_NAME;
+    sublayer.flags = 0;
+    sublayer.weight = 0x100;
+
+    /* Add sublayer to the session */
+    err = FwpmSubLayerAdd0(engine, &sublayer, NULL);
+
+out:
+    if (engine)
+    {
+        FwpmEngineClose0(engine);
+    }
+    return err;
+}
+
+/*
+ * Block outgoing port 53 traffic except for
+ * (i) adapter with the specified index
+ * OR
+ * (ii) processes with the specified executable path
+ * The firewall filters added here are automatically removed when the process exits or
+ * on calling delete_block_dns_filters().
+ * Arguments:
+ *   engine_handle : On successful return contains the handle for a newly opened fwp session
+ *                   in which the filters are added.
+ *                   May be closed by passing to delete_block_dns_filters to remove the filters.
+ *   index         : The index of adapter for which traffic is permitted.
+ *   exe_path      : Path of executable for which traffic is permitted.
+ *   msg_handler   : An optional callback function for error reporting.
+ * Returns 0 on success, a non-zero status code of the last failed action on failure.
+ */
+
+DWORD
+add_block_dns_filters(HANDLE *engine_handle,
+                      int index,
+                      const WCHAR *exe_path,
+                      block_dns_msg_handler_t msg_handler
+                      )
+{
+    FWPM_SESSION0 session = {0};
+    FWPM_SUBLAYER0 *sublayer_ptr = NULL;
+    NET_LUID tapluid;
+    UINT64 filterid;
+    FWP_BYTE_BLOB *openvpnblob = NULL;
+    FWPM_FILTER0 Filter = {0};
+    FWPM_FILTER_CONDITION0 Condition[2] = {0};
+    DWORD err = 0;
+
+    if (!msg_handler)
+    {
+        msg_handler = default_msg_handler;
+    }
+
+    /* Add temporary filters which don't survive reboots or crashes. */
+    session.flags = FWPM_SESSION_FLAG_DYNAMIC;
+
+    *engine_handle = NULL;
+
+    err = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, engine_handle);
+    CHECK_ERROR(err, "FwpEngineOpen: open fwp session failed");
+    msg_handler(0, "Block_DNS: WFP engine opened");
+
+    /* Check sublayer exists and add one if it does not. */
+    if (FwpmSubLayerGetByKey0(*engine_handle, &OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER, &sublayer_ptr)
+        == ERROR_SUCCESS)
+    {
+        msg_handler(0, "Block_DNS: Using existing sublayer");
+        FwpmFreeMemory0((void **)&sublayer_ptr);
+    }
+    else
+    {  /* Add a new sublayer -- as another process may add it in the meantime,
+        * do not treat "already exists" as an error */
+        err = add_sublayer(OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER);
+
+        if (err == FWP_E_ALREADY_EXISTS || err == ERROR_SUCCESS)
+        {
+            msg_handler(0, "Block_DNS: Added a persistent sublayer with pre-defined UUID");
+        }
+        else
+        {
+            CHECK_ERROR(err, "add_sublayer: failed to add persistent sublayer");
+        }
+    }
+
+    err = ConvertInterfaceIndexToLuid(index, &tapluid);
+    CHECK_ERROR(err, "Convert interface index to luid failed");
+
+    err = FwpmGetAppIdFromFileName0(exe_path, &openvpnblob);
+    CHECK_ERROR(err, "Get byte blob for openvpn executable name failed");
+
+    /* Prepare filter. */
+    Filter.subLayerKey = OPENVPN_BLOCK_OUTSIDE_DNS_SUBLAYER;
+    Filter.displayData.name = FIREWALL_NAME;
+    Filter.weight.type = FWP_UINT8;
+    Filter.weight.uint8 = 0xF;
+    Filter.filterCondition = Condition;
+    Filter.numFilterConditions = 2;
+
+    /* First filter. Permit IPv4 DNS queries from OpenVPN itself. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+    Filter.action.type = FWP_ACTION_PERMIT;
+
+    Condition[0].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+    Condition[0].matchType = FWP_MATCH_EQUAL;
+    Condition[0].conditionValue.type = FWP_UINT16;
+    Condition[0].conditionValue.uint16 = 53;
+
+    Condition[1].fieldKey = FWPM_CONDITION_ALE_APP_ID;
+    Condition[1].matchType = FWP_MATCH_EQUAL;
+    Condition[1].conditionValue.type = FWP_BYTE_BLOB_TYPE;
+    Condition[1].conditionValue.byteBlob = openvpnblob;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to permit IPv4 port 53 traffic from OpenVPN failed");
+
+    /* Second filter. Permit IPv6 DNS queries from OpenVPN itself. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to permit IPv6 port 53 traffic from OpenVPN failed");
+
+    msg_handler(0, "Block_DNS: Added permit filters for exe_path");
+
+    /* Third filter. Block all IPv4 DNS queries. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+    Filter.action.type = FWP_ACTION_BLOCK;
+    Filter.weight.type = FWP_EMPTY;
+    Filter.numFilterConditions = 1;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to block IPv4 DNS traffic failed");
+
+    /* Forth filter. Block all IPv6 DNS queries. */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to block IPv6 DNS traffic failed");
+
+    msg_handler(0, "Block_DNS: Added block filters for all interfaces");
+
+    /* Fifth filter. Permit IPv4 DNS queries from TAP.
+     * Use a non-zero weight so that the permit filters get higher priority
+     * over the block filter added with automatic weighting */
+
+    Filter.weight.type = FWP_UINT8;
+    Filter.weight.uint8 = 0xE;
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4;
+    Filter.action.type = FWP_ACTION_PERMIT;
+    Filter.numFilterConditions = 2;
+
+    Condition[1].fieldKey = FWPM_CONDITION_IP_LOCAL_INTERFACE;
+    Condition[1].matchType = FWP_MATCH_EQUAL;
+    Condition[1].conditionValue.type = FWP_UINT64;
+    Condition[1].conditionValue.uint64 = &tapluid.Value;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to permit IPv4 DNS traffic through TAP failed");
+
+    /* Sixth filter. Permit IPv6 DNS queries from TAP.
+     * Use same weight as IPv4 filter */
+    Filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6;
+
+    err = FwpmFilterAdd0(*engine_handle, &Filter, NULL, &filterid);
+    CHECK_ERROR(err, "Add filter to permit IPv6 DNS traffic through TAP failed");
+
+    msg_handler(0, "Block_DNS: Added permit filters for TAP interface");
+
+out:
+
+    if (openvpnblob)
+    {
+        FwpmFreeMemory0((void **)&openvpnblob);
+    }
+
+    if (err && *engine_handle)
+    {
+        FwpmEngineClose0(*engine_handle);
+        *engine_handle = NULL;
+    }
+
+    return err;
+}
+
+DWORD
+delete_block_dns_filters(HANDLE engine_handle)
+{
+    DWORD err = 0;
+    /*
+     * For dynamic sessions closing the engine removes all filters added in the session
+     */
+    if (engine_handle)
+    {
+        err = FwpmEngineClose0(engine_handle);
+    }
+    return err;
+}
+
+/*
+ * Return interface metric value for the specified interface index.
+ *
+ * Arguments:
+ *   index         : The index of TAP adapter.
+ *   family        : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
+ *   is_auto       : On return set to true if automatic metric is in use.
+ *                   Unused if NULL.
+ *
+ * Returns positive metric value or -1 on error.
+ */
+int
+get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto)
+{
+    DWORD err = 0;
+    MIB_IPINTERFACE_ROW ipiface;
+    InitializeIpInterfaceEntry(&ipiface);
+    ipiface.Family = family;
+    ipiface.InterfaceIndex = index;
+
+    if (is_auto)
+    {
+        *is_auto = 0;
+    }
+    err = GetIpInterfaceEntry(&ipiface);
+
+    /* On Windows metric is never > INT_MAX so return value of int is ok.
+     * But we check for overflow nevertheless.
+     */
+    if (err == NO_ERROR && ipiface.Metric <= INT_MAX)
+    {
+        if (is_auto)
+        {
+            *is_auto = ipiface.UseAutomaticMetric;
+        }
+        return (int)ipiface.Metric;
+    }
+    return -1;
+}
+
+/*
+ * Sets interface metric value for specified interface index.
+ *
+ * Arguments:
+ *   index         : The index of TAP adapter.
+ *   family        : Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
+ *   metric        : Metric value. 0 for automatic metric.
+ * Returns 0 on success, a non-zero status code of the last failed action on failure.
+ */
+
+DWORD
+set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family,
+                     const ULONG metric)
+{
+    DWORD err = 0;
+    MIB_IPINTERFACE_ROW ipiface;
+    InitializeIpInterfaceEntry(&ipiface);
+    ipiface.Family = family;
+    ipiface.InterfaceIndex = index;
+    err = GetIpInterfaceEntry(&ipiface);
+    if (err == NO_ERROR)
+    {
+        if (family == AF_INET)
+        {
+            /* required for IPv4 as per MSDN */
+            ipiface.SitePrefixLength = 0;
+        }
+        ipiface.Metric = metric;
+        if (metric == 0)
+        {
+            ipiface.UseAutomaticMetric = TRUE;
+        }
+        else
+        {
+            ipiface.UseAutomaticMetric = FALSE;
+        }
+        err = SetIpInterfaceEntry(&ipiface);
+        if (err == NO_ERROR)
+        {
+            return 0;
+        }
+    }
+    return err;
+}
+
+#endif /* ifdef _WIN32 */
diff --git a/src/openvpn/block_dns.h b/src/openvpn/block_dns.h
new file mode 100644
index 0000000..50b383f
--- /dev/null
+++ b/src/openvpn/block_dns.h
@@ -0,0 +1,69 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2016 Selva Nair <selva.nair@gmail.com>
+ *
+ *  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 _WIN32
+
+#ifndef OPENVPN_BLOCK_DNS_H
+#define OPENVPN_BLOCK_DNS_H
+
+/* Any value less than 5 should work fine. 3 is chosen without any real reason. */
+#define BLOCK_DNS_IFACE_METRIC 3
+
+typedef void (*block_dns_msg_handler_t) (DWORD err, const char *msg);
+
+DWORD
+delete_block_dns_filters(HANDLE engine);
+
+DWORD
+add_block_dns_filters(HANDLE *engine, int iface_index, const WCHAR *exe_path,
+                      block_dns_msg_handler_t msg_handler_callback);
+
+/**
+ * Return interface metric value for the specified interface index.
+ *
+ * @param index         The index of TAP adapter.
+ * @param family        Address family (AF_INET for IPv4 and AF_INET6 for IPv6).
+ * @param is_auto       On return set to true if automatic metric is in use.
+ *                      Unused if NULL.
+ *
+ * @return positive interface metric on success or -1 on error
+ */
+int
+get_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family, int *is_auto);
+
+/**
+ * Sets interface metric value for specified interface index.
+ *
+ * @param index The index of TAP adapter
+ * @param family Address family (AF_INET for IPv4 and AF_INET6 for IPv6)
+ * @param metric Metric value. 0 for automatic metric
+ *
+ * @return 0 on success, a non-zero status code of the last failed action on failure.
+ */
+
+DWORD
+set_interface_metric(const NET_IFINDEX index, const ADDRESS_FAMILY family,
+                     const ULONG metric);
+
+#endif
+#endif
diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
new file mode 100644
index 0000000..f9c76b1
--- /dev/null
+++ b/src/openvpn/buffer.c
@@ -0,0 +1,1337 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 "syshead.h"
+
+#include "common.h"
+#include "buffer.h"
+#include "error.h"
+#include "mtu.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+size_t
+array_mult_safe(const size_t m1, const size_t m2, const size_t extra)
+{
+    const size_t limit = 0xFFFFFFFF;
+    unsigned long long res = (unsigned long long)m1 * (unsigned long long)m2 + (unsigned long long)extra;
+    if (unlikely(m1 > limit) || unlikely(m2 > limit) || unlikely(extra > limit) || unlikely(res > (unsigned long long)limit))
+    {
+        msg(M_FATAL, "attemped allocation of excessively large array");
+    }
+    return (size_t) res;
+}
+
+void
+buf_size_error(const size_t size)
+{
+    msg(M_FATAL, "fatal buffer size error, size=%lu", (unsigned long)size);
+}
+
+struct buffer
+#ifdef DMALLOC
+alloc_buf_debug(size_t size, const char *file, int line)
+#else
+alloc_buf(size_t size)
+#endif
+{
+    struct buffer buf;
+
+    if (!buf_size_valid(size))
+    {
+        buf_size_error(size);
+    }
+    buf.capacity = (int)size;
+    buf.offset = 0;
+    buf.len = 0;
+#ifdef DMALLOC
+    buf.data = openvpn_dmalloc(file, line, size);
+#else
+    buf.data = calloc(1, size);
+#endif
+    check_malloc_return(buf.data);
+
+    return buf;
+}
+
+struct buffer
+#ifdef DMALLOC
+alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line)
+#else
+alloc_buf_gc(size_t size, struct gc_arena *gc)
+#endif
+{
+    struct buffer buf;
+    if (!buf_size_valid(size))
+    {
+        buf_size_error(size);
+    }
+    buf.capacity = (int)size;
+    buf.offset = 0;
+    buf.len = 0;
+#ifdef DMALLOC
+    buf.data = (uint8_t *) gc_malloc_debug(size, false, gc, file, line);
+#else
+    buf.data = (uint8_t *) gc_malloc(size, false, gc);
+#endif
+    if (size)
+    {
+        *buf.data = 0;
+    }
+    return buf;
+}
+
+struct buffer
+#ifdef DMALLOC
+clone_buf_debug(const struct buffer *buf, const char *file, int line)
+#else
+clone_buf(const struct buffer *buf)
+#endif
+{
+    struct buffer ret;
+    ret.capacity = buf->capacity;
+    ret.offset = buf->offset;
+    ret.len = buf->len;
+#ifdef DMALLOC
+    ret.data = (uint8_t *) openvpn_dmalloc(file, line, buf->capacity);
+#else
+    ret.data = (uint8_t *) malloc(buf->capacity);
+#endif
+    check_malloc_return(ret.data);
+    memcpy(BPTR(&ret), BPTR(buf), BLEN(buf));
+    return ret;
+}
+
+#ifdef BUF_INIT_TRACKING
+
+bool
+buf_init_debug(struct buffer *buf, int offset, const char *file, int line)
+{
+    buf->debug_file = file;
+    buf->debug_line = line;
+    return buf_init_dowork(buf, offset);
+}
+
+static inline int
+buf_debug_line(const struct buffer *buf)
+{
+    return buf->debug_line;
+}
+
+static const char *
+buf_debug_file(const struct buffer *buf)
+{
+    return buf->debug_file;
+}
+
+#else  /* ifdef BUF_INIT_TRACKING */
+
+#define buf_debug_line(buf) 0
+#define buf_debug_file(buf) "[UNDEF]"
+
+#endif /* ifdef BUF_INIT_TRACKING */
+
+void
+buf_clear(struct buffer *buf)
+{
+    if (buf->capacity > 0)
+    {
+        secure_memzero(buf->data, buf->capacity);
+    }
+    buf->len = 0;
+    buf->offset = 0;
+}
+
+bool
+buf_assign(struct buffer *dest, const struct buffer *src)
+{
+    if (!buf_init(dest, src->offset))
+    {
+        return false;
+    }
+    return buf_write(dest, BPTR(src), BLEN(src));
+}
+
+struct buffer
+clear_buf(void)
+{
+    struct buffer buf;
+    CLEAR(buf);
+    return buf;
+}
+
+void
+free_buf(struct buffer *buf)
+{
+    if (buf->data)
+    {
+        free(buf->data);
+    }
+    CLEAR(*buf);
+}
+
+/*
+ * Return a buffer for write that is a subset of another buffer
+ */
+struct buffer
+buf_sub(struct buffer *buf, int size, bool prepend)
+{
+    struct buffer ret;
+    uint8_t *data;
+
+    CLEAR(ret);
+    data = prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
+    if (data)
+    {
+        ret.capacity = size;
+        ret.data = data;
+    }
+    return ret;
+}
+
+/*
+ * printf append to a buffer with overflow check
+ */
+bool
+buf_printf(struct buffer *buf, const char *format, ...)
+{
+    int ret = false;
+    if (buf_defined(buf))
+    {
+        va_list arglist;
+        uint8_t *ptr = BEND(buf);
+        int cap = buf_forward_capacity(buf);
+
+        if (cap > 0)
+        {
+            int stat;
+            va_start(arglist, format);
+            stat = vsnprintf((char *)ptr, cap, format, arglist);
+            va_end(arglist);
+            *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
+            buf->len += (int) strlen((char *)ptr);
+            if (stat >= 0 && stat < cap)
+            {
+                ret = true;
+            }
+        }
+    }
+    return ret;
+}
+
+bool
+buf_puts(struct buffer *buf, const char *str)
+{
+    int ret = false;
+    uint8_t *ptr = BEND(buf);
+    int cap = buf_forward_capacity(buf);
+    if (cap > 0)
+    {
+        strncpynt((char *)ptr,str, cap);
+        *(buf->data + buf->capacity - 1) = 0; /* windows vsnprintf needs this */
+        buf->len += (int) strlen((char *)ptr);
+        ret = true;
+    }
+    return ret;
+}
+
+
+/*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+ *
+ * Return false on overflow.
+ *
+ * This functionality is duplicated in src/openvpnserv/common.c
+ * Any modifications here should be done to the other place as well.
+ */
+
+bool
+openvpn_snprintf(char *str, size_t size, const char *format, ...)
+{
+    va_list arglist;
+    int len = -1;
+    if (size > 0)
+    {
+        va_start(arglist, format);
+        len = vsnprintf(str, size, format, arglist);
+        va_end(arglist);
+        str[size - 1] = 0;
+    }
+    return (len >= 0 && len < size);
+}
+
+/*
+ * write a string to the end of a buffer that was
+ * truncated by buf_printf
+ */
+void
+buf_catrunc(struct buffer *buf, const char *str)
+{
+    if (buf_forward_capacity(buf) <= 1)
+    {
+        int len = (int) strlen(str) + 1;
+        if (len < buf_forward_capacity_total(buf))
+        {
+            strncpynt((char *)(buf->data + buf->capacity - len), str, len);
+        }
+    }
+}
+
+/*
+ * convert a multi-line output to one line
+ */
+void
+convert_to_one_line(struct buffer *buf)
+{
+    uint8_t *cp = BPTR(buf);
+    int len = BLEN(buf);
+    while (len--)
+    {
+        if (*cp == '\n')
+        {
+            *cp = '|';
+        }
+        ++cp;
+    }
+}
+
+/* NOTE: requires that string be null terminated */
+void
+buf_write_string_file(const struct buffer *buf, const char *filename, int fd)
+{
+    const int len = strlen((char *) BPTR(buf));
+    const int size = write(fd, BPTR(buf), len);
+    if (size != len)
+    {
+        msg(M_ERR, "Write error on file '%s'", filename);
+    }
+}
+
+/*
+ * Garbage collection
+ */
+
+void *
+#ifdef DMALLOC
+gc_malloc_debug(size_t size, bool clear, struct gc_arena *a, const char *file, int line)
+#else
+gc_malloc(size_t size, bool clear, struct gc_arena *a)
+#endif
+{
+    void *ret;
+    if (a)
+    {
+        struct gc_entry *e;
+#ifdef DMALLOC
+        e = (struct gc_entry *) openvpn_dmalloc(file, line, size + sizeof(struct gc_entry));
+#else
+        e = (struct gc_entry *) malloc(size + sizeof(struct gc_entry));
+#endif
+        check_malloc_return(e);
+        ret = (char *) e + sizeof(struct gc_entry);
+        e->next = a->list;
+        a->list = e;
+    }
+    else
+    {
+#ifdef DMALLOC
+        ret = openvpn_dmalloc(file, line, size);
+#else
+        ret = malloc(size);
+#endif
+        check_malloc_return(ret);
+    }
+#ifndef ZERO_BUFFER_ON_ALLOC
+    if (clear)
+#endif
+    memset(ret, 0, size);
+    return ret;
+}
+
+void
+x_gc_free(struct gc_arena *a)
+{
+    struct gc_entry *e;
+    e = a->list;
+    a->list = NULL;
+
+    while (e != NULL)
+    {
+        struct gc_entry *next = e->next;
+        free(e);
+        e = next;
+    }
+}
+
+/*
+ * Functions to handle special objects in gc_entries
+ */
+
+void
+x_gc_freespecial(struct gc_arena *a)
+{
+    struct gc_entry_special *e;
+    e = a->list_special;
+    a->list_special = NULL;
+
+    while (e != NULL)
+    {
+        struct gc_entry_special *next = e->next;
+        e->free_fnc(e->addr);
+        free(e);
+        e = next;
+    }
+}
+
+void
+gc_addspecial(void *addr, void(free_function)(void *), struct gc_arena *a)
+{
+    ASSERT(a);
+    struct gc_entry_special *e;
+#ifdef DMALLOC
+    e = (struct gc_entry_special *) openvpn_dmalloc(file, line, sizeof(struct gc_entry_special));
+#else
+    e = (struct gc_entry_special *) malloc(sizeof(struct gc_entry_special));
+#endif
+    check_malloc_return(e);
+    e->free_fnc = free_function;
+    e->addr = addr;
+
+    e->next = a->list_special;
+    a->list_special = e;
+}
+
+
+/*
+ * Transfer src arena to dest, resetting src to an empty arena.
+ */
+void
+gc_transfer(struct gc_arena *dest, struct gc_arena *src)
+{
+    if (dest && src)
+    {
+        struct gc_entry *e = src->list;
+        if (e)
+        {
+            while (e->next != NULL)
+            {
+                e = e->next;
+            }
+            e->next = dest->list;
+            dest->list = src->list;
+            src->list = NULL;
+        }
+    }
+}
+
+/*
+ * Hex dump -- Output a binary buffer to a hex string and return it.
+ */
+
+char *
+format_hex_ex(const uint8_t *data, int size, int maxoutput,
+              unsigned int space_break_flags, const char *separator,
+              struct gc_arena *gc)
+{
+    const size_t bytes_per_hexblock = space_break_flags & FHE_SPACE_BREAK_MASK;
+    const size_t separator_len = separator ? strlen(separator) : 0;
+    static_assert(INT_MAX <= SIZE_MAX, "Code assumes INT_MAX <= SIZE_MAX");
+    const size_t out_len = maxoutput > 0 ? maxoutput :
+                           ((size * 2) + ((size / bytes_per_hexblock) * separator_len) + 2);
+
+    struct buffer out = alloc_buf_gc(out_len, gc);
+    for (int i = 0; i < size; ++i)
+    {
+        if (separator && i && !(i % bytes_per_hexblock))
+        {
+            buf_printf(&out, "%s", separator);
+        }
+        if (space_break_flags & FHE_CAPS)
+        {
+            buf_printf(&out, "%02X", data[i]);
+        }
+        else
+        {
+            buf_printf(&out, "%02x", data[i]);
+        }
+    }
+    buf_catrunc(&out, "[more...]");
+    return (char *)out.data;
+}
+
+/*
+ * remove specific trailing character
+ */
+
+void
+buf_rmtail(struct buffer *buf, uint8_t remove)
+{
+    uint8_t *cp = BLAST(buf);
+    if (cp && *cp == remove)
+    {
+        *cp = '\0';
+        --buf->len;
+    }
+}
+
+/*
+ * force a null termination even it requires
+ * truncation of the last char.
+ */
+void
+buf_null_terminate(struct buffer *buf)
+{
+    char *last = (char *) BLAST(buf);
+    if (last && *last == '\0') /* already terminated? */
+    {
+        return;
+    }
+
+    if (!buf_safe(buf, 1))   /* make space for trailing null */
+    {
+        buf_inc_len(buf, -1);
+    }
+
+    buf_write_u8(buf, 0);
+}
+
+/*
+ * Remove trailing \r and \n chars and ensure
+ * null termination.
+ */
+void
+buf_chomp(struct buffer *buf)
+{
+    while (true)
+    {
+        char *last = (char *) BLAST(buf);
+        if (!last)
+        {
+            break;
+        }
+        if (char_class(*last, CC_CRLF|CC_NULL))
+        {
+            if (!buf_inc_len(buf, -1))
+            {
+                break;
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    buf_null_terminate(buf);
+}
+
+const char *
+skip_leading_whitespace(const char *str)
+{
+    while (*str)
+    {
+        const char c = *str;
+        if (!(c == ' ' || c == '\t'))
+        {
+            break;
+        }
+        ++str;
+    }
+    return str;
+}
+
+/*
+ * like buf_null_terminate, but operate on strings
+ */
+void
+string_null_terminate(char *str, int len, int capacity)
+{
+    ASSERT(len >= 0 && len <= capacity && capacity > 0);
+    if (len < capacity)
+    {
+        *(str + len) = '\0';
+    }
+    else if (len == capacity)
+    {
+        *(str + len - 1) = '\0';
+    }
+}
+
+/*
+ * Remove trailing \r and \n chars.
+ */
+void
+chomp(char *str)
+{
+    rm_trailing_chars(str, "\r\n");
+}
+
+/*
+ * Remove trailing chars
+ */
+void
+rm_trailing_chars(char *str, const char *what_to_delete)
+{
+    bool modified;
+    do
+    {
+        const int len = strlen(str);
+        modified = false;
+        if (len > 0)
+        {
+            char *cp = str + (len - 1);
+            if (strchr(what_to_delete, *cp) != NULL)
+            {
+                *cp = '\0';
+                modified = true;
+            }
+        }
+    } while (modified);
+}
+
+/*
+ * Allocate a string
+ */
+char *
+#ifdef DMALLOC
+string_alloc_debug(const char *str, struct gc_arena *gc, const char *file, int line)
+#else
+string_alloc(const char *str, struct gc_arena *gc)
+#endif
+{
+    if (str)
+    {
+        const int n = strlen(str) + 1;
+        char *ret;
+
+        if (gc)
+        {
+#ifdef DMALLOC
+            ret = (char *) gc_malloc_debug(n, false, gc, file, line);
+#else
+            ret = (char *) gc_malloc(n, false, gc);
+#endif
+        }
+        else
+        {
+            /* If there are no garbage collector available, it's expected
+             * that the caller cleans up afterwards.  This is coherent with the
+             * earlier behaviour when gc_malloc() would be called with gc == NULL
+             */
+#ifdef DMALLOC
+            ret = openvpn_dmalloc(file, line, n);
+            memset(ret, 0, n);
+#else
+            ret = calloc(1, n);
+#endif
+            check_malloc_return(ret);
+        }
+        memcpy(ret, str, n);
+        return ret;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+/*
+ * Erase all characters in a string
+ */
+void
+string_clear(char *str)
+{
+    if (str)
+    {
+        secure_memzero(str, strlen(str));
+    }
+}
+
+/*
+ * Return the length of a string array
+ */
+int
+string_array_len(const char **array)
+{
+    int i = 0;
+    if (array)
+    {
+        while (array[i])
+        {
+            ++i;
+        }
+    }
+    return i;
+}
+
+char *
+print_argv(const char **p, struct gc_arena *gc, const unsigned int flags)
+{
+    struct buffer out = alloc_buf_gc(256, gc);
+    int i = 0;
+    for (;; )
+    {
+        const char *cp = *p++;
+        if (!cp)
+        {
+            break;
+        }
+        if (i)
+        {
+            buf_printf(&out, " ");
+        }
+        if (flags & PA_BRACKET)
+        {
+            buf_printf(&out, "[%s]", cp);
+        }
+        else
+        {
+            buf_printf(&out, "%s", cp);
+        }
+        ++i;
+    }
+    return BSTR(&out);
+}
+
+/*
+ * Allocate a string inside a buffer
+ */
+struct buffer
+#ifdef DMALLOC
+string_alloc_buf_debug(const char *str, struct gc_arena *gc, const char *file, int line)
+#else
+string_alloc_buf(const char *str, struct gc_arena *gc)
+#endif
+{
+    struct buffer buf;
+
+    ASSERT(str);
+
+#ifdef DMALLOC
+    buf_set_read(&buf, (uint8_t *) string_alloc_debug(str, gc, file, line), strlen(str) + 1);
+#else
+    buf_set_read(&buf, (uint8_t *) string_alloc(str, gc), strlen(str) + 1);
+#endif
+
+    if (buf.len > 0) /* Don't count trailing '\0' as part of length */
+    {
+        --buf.len;
+    }
+
+    return buf;
+}
+
+/*
+ * String comparison
+ */
+
+bool
+buf_string_match_head_str(const struct buffer *src, const char *match)
+{
+    const int size = strlen(match);
+    if (size < 0 || size > src->len)
+    {
+        return false;
+    }
+    return memcmp(BPTR(src), match, size) == 0;
+}
+
+bool
+buf_string_compare_advance(struct buffer *src, const char *match)
+{
+    if (buf_string_match_head_str(src, match))
+    {
+        buf_advance(src, strlen(match));
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+int
+buf_substring_len(const struct buffer *buf, int delim)
+{
+    int i = 0;
+    struct buffer tmp = *buf;
+    int c;
+
+    while ((c = buf_read_u8(&tmp)) >= 0)
+    {
+        ++i;
+        if (c == delim)
+        {
+            return i;
+        }
+    }
+    return -1;
+}
+
+/*
+ * String parsing
+ */
+
+bool
+buf_parse(struct buffer *buf, const int delim, char *line, const int size)
+{
+    bool eol = false;
+    int n = 0;
+    int c;
+
+    ASSERT(size > 0);
+
+    do
+    {
+        c = buf_read_u8(buf);
+        if (c < 0)
+        {
+            eol = true;
+        }
+        if (c <= 0 || c == delim)
+        {
+            c = 0;
+        }
+        if (n >= size)
+        {
+            break;
+        }
+        line[n++] = c;
+    }
+    while (c);
+
+    line[size-1] = '\0';
+    return !(eol && !strlen(line));
+}
+
+/*
+ * Print a string which might be NULL
+ */
+const char *
+np(const char *str)
+{
+    if (str)
+    {
+        return str;
+    }
+    else
+    {
+        return "[NULL]";
+    }
+}
+
+/*
+ * Classify and mutate strings based on character types.
+ */
+
+bool
+char_class(const unsigned char c, const unsigned int flags)
+{
+    if (!flags)
+    {
+        return false;
+    }
+    if (flags & CC_ANY)
+    {
+        return true;
+    }
+
+    if ((flags & CC_NULL) && c == '\0')
+    {
+        return true;
+    }
+
+    if ((flags & CC_ALNUM) && isalnum(c))
+    {
+        return true;
+    }
+    if ((flags & CC_ALPHA) && isalpha(c))
+    {
+        return true;
+    }
+    if ((flags & CC_ASCII) && isascii(c))
+    {
+        return true;
+    }
+    if ((flags & CC_CNTRL) && iscntrl(c))
+    {
+        return true;
+    }
+    if ((flags & CC_DIGIT) && isdigit(c))
+    {
+        return true;
+    }
+    if ((flags & CC_PRINT) && (c >= 32 && c != 127)) /* allow ascii non-control and UTF-8, consider DEL to be a control */
+    {
+        return true;
+    }
+    if ((flags & CC_PUNCT) && ispunct(c))
+    {
+        return true;
+    }
+    if ((flags & CC_SPACE) && isspace(c))
+    {
+        return true;
+    }
+    if ((flags & CC_XDIGIT) && isxdigit(c))
+    {
+        return true;
+    }
+
+    if ((flags & CC_BLANK) && (c == ' ' || c == '\t'))
+    {
+        return true;
+    }
+    if ((flags & CC_NEWLINE) && c == '\n')
+    {
+        return true;
+    }
+    if ((flags & CC_CR) && c == '\r')
+    {
+        return true;
+    }
+
+    if ((flags & CC_BACKSLASH) && c == '\\')
+    {
+        return true;
+    }
+    if ((flags & CC_UNDERBAR) && c == '_')
+    {
+        return true;
+    }
+    if ((flags & CC_DASH) && c == '-')
+    {
+        return true;
+    }
+    if ((flags & CC_DOT) && c == '.')
+    {
+        return true;
+    }
+    if ((flags & CC_COMMA) && c == ',')
+    {
+        return true;
+    }
+    if ((flags & CC_COLON) && c == ':')
+    {
+        return true;
+    }
+    if ((flags & CC_SLASH) && c == '/')
+    {
+        return true;
+    }
+    if ((flags & CC_SINGLE_QUOTE) && c == '\'')
+    {
+        return true;
+    }
+    if ((flags & CC_DOUBLE_QUOTE) && c == '\"')
+    {
+        return true;
+    }
+    if ((flags & CC_REVERSE_QUOTE) && c == '`')
+    {
+        return true;
+    }
+    if ((flags & CC_AT) && c == '@')
+    {
+        return true;
+    }
+    if ((flags & CC_EQUAL) && c == '=')
+    {
+        return true;
+    }
+    if ((flags & CC_LESS_THAN) && c == '<')
+    {
+        return true;
+    }
+    if ((flags & CC_GREATER_THAN) && c == '>')
+    {
+        return true;
+    }
+    if ((flags & CC_PIPE) && c == '|')
+    {
+        return true;
+    }
+    if ((flags & CC_QUESTION_MARK) && c == '?')
+    {
+        return true;
+    }
+    if ((flags & CC_ASTERISK) && c == '*')
+    {
+        return true;
+    }
+
+    return false;
+}
+
+static inline bool
+char_inc_exc(const char c, const unsigned int inclusive, const unsigned int exclusive)
+{
+    return char_class(c, inclusive) && !char_class(c, exclusive);
+}
+
+bool
+string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive)
+{
+    char c;
+    ASSERT(str);
+    while ((c = *str++))
+    {
+        if (!char_inc_exc(c, inclusive, exclusive))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+/*
+ * Modify string in place.
+ * Guaranteed to not increase string length.
+ */
+bool
+string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace)
+{
+    const char *in = str;
+    bool ret = true;
+
+    ASSERT(str);
+
+    while (true)
+    {
+        char c = *in++;
+        if (c)
+        {
+            if (!char_inc_exc(c, inclusive, exclusive))
+            {
+                c = replace;
+                ret = false;
+            }
+            if (c)
+            {
+                *str++ = c;
+            }
+        }
+        else
+        {
+            *str = '\0';
+            break;
+        }
+    }
+    return ret;
+}
+
+const char *
+string_mod_const(const char *str,
+                 const unsigned int inclusive,
+                 const unsigned int exclusive,
+                 const char replace,
+                 struct gc_arena *gc)
+{
+    if (str)
+    {
+        char *buf = string_alloc(str, gc);
+        string_mod(buf, inclusive, exclusive, replace);
+        return buf;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+void
+string_replace_leading(char *str, const char match, const char replace)
+{
+    ASSERT(match != '\0');
+    while (*str)
+    {
+        if (*str == match)
+        {
+            *str = replace;
+        }
+        else
+        {
+            break;
+        }
+        ++str;
+    }
+}
+
+#ifdef CHARACTER_CLASS_DEBUG
+
+#define CC_INCLUDE    (CC_PRINT)
+#define CC_EXCLUDE    (0)
+#define CC_REPLACE    ('.')
+
+void
+character_class_debug(void)
+{
+    char buf[256];
+
+    while (fgets(buf, sizeof(buf), stdin) != NULL)
+    {
+        string_mod(buf, CC_INCLUDE, CC_EXCLUDE, CC_REPLACE);
+        printf("%s", buf);
+    }
+}
+
+#endif
+
+#ifdef VERIFY_ALIGNMENT
+void
+valign4(const struct buffer *buf, const char *file, const int line)
+{
+    if (buf && buf->len)
+    {
+        int msglevel = D_ALIGN_DEBUG;
+        const unsigned int u = (unsigned int) BPTR(buf);
+
+        if (u & (PAYLOAD_ALIGN-1))
+        {
+            msglevel = D_ALIGN_ERRORS;
+        }
+
+        msg(msglevel, "%sAlignment at %s/%d ptr=" ptr_format " OLC=%d/%d/%d I=%s/%d",
+            (msglevel == D_ALIGN_ERRORS) ? "ERROR: " : "",
+            file,
+            line,
+            (ptr_type)buf->data,
+            buf->offset,
+            buf->len,
+            buf->capacity,
+            buf_debug_file(buf),
+            buf_debug_line(buf));
+    }
+}
+#endif /* ifdef VERIFY_ALIGNMENT */
+
+/*
+ * struct buffer_list
+ */
+struct buffer_list *
+buffer_list_new(const int max_size)
+{
+    struct buffer_list *ret;
+    ALLOC_OBJ_CLEAR(ret, struct buffer_list);
+    ret->max_size = max_size;
+    ret->size = 0;
+    return ret;
+}
+
+void
+buffer_list_free(struct buffer_list *ol)
+{
+    if (ol)
+    {
+        buffer_list_reset(ol);
+        free(ol);
+    }
+}
+
+bool
+buffer_list_defined(const struct buffer_list *ol)
+{
+    return ol && ol->head != NULL;
+}
+
+void
+buffer_list_reset(struct buffer_list *ol)
+{
+    struct buffer_entry *e = ol->head;
+    while (e)
+    {
+        struct buffer_entry *next = e->next;
+        free_buf(&e->buf);
+        free(e);
+        e = next;
+    }
+    ol->head = ol->tail = NULL;
+    ol->size = 0;
+}
+
+void
+buffer_list_push(struct buffer_list *ol, const char *str)
+{
+    if (str)
+    {
+        const size_t len = strlen((const char *)str);
+        struct buffer_entry *e = buffer_list_push_data(ol, str, len+1);
+        if (e)
+        {
+            e->buf.len = len; /* Don't count trailing '\0' as part of length */
+        }
+    }
+}
+
+struct buffer_entry *
+buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size)
+{
+    struct buffer_entry *e = NULL;
+    if (data && (!ol->max_size || ol->size < ol->max_size))
+    {
+        ALLOC_OBJ_CLEAR(e, struct buffer_entry);
+
+        ++ol->size;
+        if (ol->tail)
+        {
+            ASSERT(ol->head);
+            ol->tail->next = e;
+        }
+        else
+        {
+            ASSERT(!ol->head);
+            ol->head = e;
+        }
+        e->buf = alloc_buf(size);
+        memcpy(e->buf.data, data, size);
+        e->buf.len = (int)size;
+        ol->tail = e;
+    }
+    return e;
+}
+
+struct buffer *
+buffer_list_peek(struct buffer_list *ol)
+{
+    if (ol && ol->head)
+    {
+        return &ol->head->buf;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+void
+buffer_list_aggregate_separator(struct buffer_list *bl, const size_t max_len,
+                                const char *sep)
+{
+    const int sep_len = strlen(sep);
+    struct buffer_entry *more = bl->head;
+    size_t size = 0;
+    int count = 0;
+    for (count = 0; more; ++count)
+    {
+        size_t extra_len = BLEN(&more->buf) + sep_len;
+        if (size + extra_len > max_len)
+        {
+            break;
+        }
+
+        size += extra_len;
+        more = more->next;
+    }
+
+    if (count >= 2)
+    {
+        struct buffer_entry *f;
+        ALLOC_OBJ_CLEAR(f, struct buffer_entry);
+        f->buf = alloc_buf(size + 1); /* prevent 0-byte malloc */
+
+        struct buffer_entry *e = bl->head;
+        for (size_t i = 0; e && i < count; ++i)
+        {
+            struct buffer_entry *next = e->next;
+            buf_copy(&f->buf, &e->buf);
+            buf_write(&f->buf, sep, sep_len);
+            free_buf(&e->buf);
+            free(e);
+            e = next;
+        }
+        bl->head = f;
+        bl->size -= count - 1;
+        f->next = more;
+        if (!more)
+        {
+            bl->tail = f;
+        }
+    }
+}
+
+void
+buffer_list_aggregate(struct buffer_list *bl, const size_t max)
+{
+    buffer_list_aggregate_separator(bl, max, "");
+}
+
+void
+buffer_list_pop(struct buffer_list *ol)
+{
+    if (ol && ol->head)
+    {
+        struct buffer_entry *e = ol->head->next;
+        free_buf(&ol->head->buf);
+        free(ol->head);
+        ol->head = e;
+        --ol->size;
+        if (!e)
+        {
+            ol->tail = NULL;
+        }
+    }
+}
+
+void
+buffer_list_advance(struct buffer_list *ol, int n)
+{
+    if (ol->head)
+    {
+        struct buffer *buf = &ol->head->buf;
+        ASSERT(buf_advance(buf, n));
+        if (!BLEN(buf))
+        {
+            buffer_list_pop(ol);
+        }
+    }
+}
+
+struct buffer_list *
+buffer_list_file(const char *fn, int max_line_len)
+{
+    FILE *fp = platform_fopen(fn, "r");
+    struct buffer_list *bl = NULL;
+
+    if (fp)
+    {
+        char *line = (char *) malloc(max_line_len);
+        if (line)
+        {
+            bl = buffer_list_new(0);
+            while (fgets(line, max_line_len, fp) != NULL)
+            {
+                buffer_list_push(bl, line);
+            }
+            free(line);
+        }
+        fclose(fp);
+    }
+    return bl;
+}
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
new file mode 100644
index 0000000..c510c00
--- /dev/null
+++ b/src/openvpn/buffer.h
@@ -0,0 +1,1177 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 BUFFER_H
+#define BUFFER_H
+
+#include "basic.h"
+#include "error.h"
+
+#define BUF_SIZE_MAX 1000000
+
+/*
+ * Define verify_align function, otherwise
+ * it will be a noop.
+ */
+/* #define VERIFY_ALIGNMENT */
+
+/*
+ * Keep track of source file/line of buf_init calls
+ */
+#ifdef VERIFY_ALIGNMENT
+#define BUF_INIT_TRACKING
+#endif
+
+/**************************************************************************/
+/**
+ * Wrapper structure for dynamically allocated memory.
+ *
+ * The actual content stored in a \c buffer structure starts at the memory
+ * location \c buffer.data \c + \c buffer.offset, and has a length of \c
+ * buffer.len bytes.  This, together with the space available before and
+ * after the content, is represented in the pseudocode below:
+ * @code
+ * uint8_t *content_start    = buffer.data + buffer.offset;
+ * uint8_t *content_end      = buffer.data + buffer.offset + buffer.len;
+ * int      prepend_capacity = buffer.offset;
+ * int      append_capacity  = buffer.capacity - (buffer.offset + buffer.len);
+ * @endcode
+ */
+struct buffer
+{
+    int capacity;               /**< Size in bytes of memory allocated by
+                                 *   \c malloc(). */
+    int offset;                 /**< Offset in bytes of the actual content
+                                 *   within the allocated memory. */
+    int len;                    /**< Length in bytes of the actual content
+                                 *   within the allocated memory. */
+    uint8_t *data;              /**< Pointer to the allocated memory. */
+
+#ifdef BUF_INIT_TRACKING
+    const char *debug_file;
+    int debug_line;
+#endif
+};
+
+
+/**************************************************************************/
+/**
+ * Garbage collection entry for one dynamically allocated block of memory.
+ *
+ * This structure represents one link in the linked list contained in a \c
+ * gc_arena structure.  Each time the \c gc_malloc() function is called,
+ * it allocates \c sizeof(gc_entry) + the requested number of bytes.  The
+ * \c gc_entry is then stored as a header in front of the memory address
+ * returned to the caller.
+ */
+struct gc_entry
+{
+    struct gc_entry *next;      /**< Pointer to the next item in the
+                                 *   linked list. */
+};
+
+/**
+ * Garbage collection entry for a specially allocated structure that needs
+ * a custom free function to be freed like struct addrinfo
+ *
+ */
+struct gc_entry_special
+{
+    struct gc_entry_special *next;
+    void (*free_fnc)(void *);
+    void *addr;
+};
+
+
+/**
+ * Garbage collection arena used to keep track of dynamically allocated
+ * memory.
+ *
+ * This structure contains a linked list of \c gc_entry structures.  When
+ * a block of memory is allocated using the \c gc_malloc() function, the
+ * allocation is registered in the function's \c gc_arena argument.  All
+ * the dynamically allocated memory registered in a \c gc_arena can be
+ * freed using the \c gc_free() function.
+ */
+struct gc_arena
+{
+    struct gc_entry *list;      /**< First element of the linked list of
+                                 *   \c gc_entry structures. */
+    struct gc_entry_special *list_special;
+};
+
+
+#define BPTR(buf)  (buf_bptr(buf))
+#define BEND(buf)  (buf_bend(buf))
+#define BLAST(buf) (buf_blast(buf))
+#define BLEN(buf)  (buf_len(buf))
+#define BDEF(buf)  (buf_defined(buf))
+#define BSTR(buf)  (buf_str(buf))
+#define BCAP(buf)  (buf_forward_capacity(buf))
+
+void buf_clear(struct buffer *buf);
+
+struct buffer clear_buf(void);
+
+void free_buf(struct buffer *buf);
+
+bool buf_assign(struct buffer *dest, const struct buffer *src);
+
+void string_clear(char *str);
+
+int string_array_len(const char **array);
+
+size_t array_mult_safe(const size_t m1, const size_t m2, const size_t extra);
+
+#define PA_BRACKET (1<<0)
+char *print_argv(const char **p, struct gc_arena *gc, const unsigned int flags);
+
+void buf_size_error(const size_t size);
+
+/* for dmalloc debugging */
+
+#ifdef DMALLOC
+
+#define alloc_buf(size)               alloc_buf_debug(size, __FILE__, __LINE__)
+#define alloc_buf_gc(size, gc)        alloc_buf_gc_debug(size, gc, __FILE__, __LINE__);
+#define clone_buf(buf)                clone_buf_debug(buf, __FILE__, __LINE__);
+#define gc_malloc(size, clear, arena) gc_malloc_debug(size, clear, arena, __FILE__, __LINE__)
+#define string_alloc(str, gc)         string_alloc_debug(str, gc, __FILE__, __LINE__)
+#define string_alloc_buf(str, gc)     string_alloc_buf_debug(str, gc, __FILE__, __LINE__)
+
+struct buffer alloc_buf_debug(size_t size, const char *file, int line);
+
+struct buffer alloc_buf_gc_debug(size_t size, struct gc_arena *gc, const char *file, int line);
+
+struct buffer clone_buf_debug(const struct buffer *buf, const char *file, int line);
+
+void *gc_malloc_debug(size_t size, bool clear, struct gc_arena *a, const char *file, int line);
+
+char *string_alloc_debug(const char *str, struct gc_arena *gc, const char *file, int line);
+
+struct buffer string_alloc_buf_debug(const char *str, struct gc_arena *gc, const char *file, int line);
+
+#else  /* ifdef DMALLOC */
+
+struct buffer alloc_buf(size_t size);
+
+struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc);  /* allocate buffer with garbage collection */
+
+struct buffer clone_buf(const struct buffer *buf);
+
+void *gc_malloc(size_t size, bool clear, struct gc_arena *a);
+
+char *string_alloc(const char *str, struct gc_arena *gc);
+
+struct buffer string_alloc_buf(const char *str, struct gc_arena *gc);
+
+#endif /* ifdef DMALLOC */
+
+void gc_addspecial(void *addr, void (*free_function)(void *), struct gc_arena *a);
+
+
+#ifdef BUF_INIT_TRACKING
+#define buf_init(buf, offset) buf_init_debug(buf, offset, __FILE__, __LINE__)
+bool buf_init_debug(struct buffer *buf, int offset, const char *file, int line);
+
+#else
+#define buf_init(buf, offset) buf_init_dowork(buf, offset)
+#endif
+
+
+/* inline functions */
+inline static void
+gc_freeaddrinfo_callback(void *addr)
+{
+    freeaddrinfo((struct addrinfo *) addr);
+}
+
+static inline bool
+buf_defined(const struct buffer *buf)
+{
+    return buf->data != NULL;
+}
+
+static inline bool
+buf_valid(const struct buffer *buf)
+{
+    return likely(buf->data != NULL) && likely(buf->len >= 0);
+}
+
+static inline uint8_t *
+buf_bptr(const struct buffer *buf)
+{
+    if (buf_valid(buf))
+    {
+        return buf->data + buf->offset;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+static int
+buf_len(const struct buffer *buf)
+{
+    if (buf_valid(buf))
+    {
+        return buf->len;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+static inline uint8_t *
+buf_bend(const struct buffer *buf)
+{
+    return buf_bptr(buf) + buf_len(buf);
+}
+
+static inline uint8_t *
+buf_blast(const struct buffer *buf)
+{
+    if (buf_len(buf) > 0)
+    {
+        return buf_bptr(buf) + buf_len(buf) - 1;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
+static inline bool
+buf_size_valid(const size_t size)
+{
+    return likely(size < BUF_SIZE_MAX);
+}
+
+static inline bool
+buf_size_valid_signed(const int size)
+{
+    return likely(size >= -BUF_SIZE_MAX) && likely(size < BUF_SIZE_MAX);
+}
+
+static inline char *
+buf_str(const struct buffer *buf)
+{
+    return (char *)buf_bptr(buf);
+}
+
+static inline void
+buf_reset(struct buffer *buf)
+{
+    buf->capacity = 0;
+    buf->offset = 0;
+    buf->len = 0;
+    buf->data = NULL;
+}
+
+static inline void
+buf_reset_len(struct buffer *buf)
+{
+    buf->len = 0;
+    buf->offset = 0;
+}
+
+static inline bool
+buf_init_dowork(struct buffer *buf, int offset)
+{
+    if (offset < 0 || offset > buf->capacity || buf->data == NULL)
+    {
+        return false;
+    }
+    buf->len = 0;
+    buf->offset = offset;
+    return true;
+}
+
+static inline void
+buf_set_write(struct buffer *buf, uint8_t *data, int size)
+{
+    if (!buf_size_valid(size))
+    {
+        buf_size_error(size);
+    }
+    buf->len = 0;
+    buf->offset = 0;
+    buf->capacity = size;
+    buf->data = data;
+    if (size > 0 && data)
+    {
+        *data = 0;
+    }
+}
+
+static inline void
+buf_set_read(struct buffer *buf, const uint8_t *data, int size)
+{
+    if (!buf_size_valid(size))
+    {
+        buf_size_error(size);
+    }
+    buf->len = buf->capacity = size;
+    buf->offset = 0;
+    buf->data = (uint8_t *)data;
+}
+
+/* Like strncpy but makes sure dest is always null terminated */
+static inline void
+strncpynt(char *dest, const char *src, size_t maxlen)
+{
+    strncpy(dest, src, maxlen);
+    if (maxlen > 0)
+    {
+        dest[maxlen - 1] = 0;
+    }
+}
+
+/* return true if string contains at least one numerical digit */
+static inline bool
+has_digit(const unsigned char *src)
+{
+    unsigned char c;
+    while ((c = *src++))
+    {
+        if (isdigit(c))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Securely zeroise memory.
+ *
+ * This code and description are based on code supplied by Zhaomo Yang, of the
+ * University of California, San Diego (which was released into the public
+ * domain).
+ *
+ * The secure_memzero function attempts to ensure that an optimizing compiler
+ * does not remove the intended operation if cleared memory is not accessed
+ * again by the program. This code has been tested under Clang 3.9.0 and GCC
+ * 6.2 with optimization flags -O, -Os, -O0, -O1, -O2, and -O3 on
+ * Ubuntu 16.04.1 LTS; under Clang 3.9.0 with optimization flags -O, -Os,
+ * -O0, -O1, -O2, and -O3 on FreeBSD 10.2-RELEASE; under Microsoft Visual Studio
+ * 2015 with optimization flags /O1, /O2 and /Ox on Windows 10.
+ *
+ * Theory of operation:
+ *
+ * 1. On Windows, use the SecureZeroMemory which ensures that data is
+ *    overwritten.
+ * 2. Under GCC or Clang, use a memory barrier, which forces the preceding
+ *    memset to be carried out. The overhead of a memory barrier is usually
+ *    negligible.
+ * 3. If none of the above are available, use the volatile pointer
+ *    technique to zero memory one byte at a time.
+ *
+ * @param data  Pointer to data to zeroise.
+ * @param len   Length of data, in bytes.
+ */
+static inline void
+secure_memzero(void *data, size_t len)
+{
+#if defined(_WIN32)
+    SecureZeroMemory(data, len);
+#elif defined(__GNUC__) || defined(__clang__)
+    memset(data, 0, len);
+    __asm__ __volatile__ ("" : : "r" (data) : "memory");
+#else
+    volatile char *p = (volatile char *) data;
+    while (len--)
+    {
+        *p++ = 0;
+    }
+#endif
+}
+
+/*
+ * printf append to a buffer with overflow check,
+ * due to usage of vsnprintf, it will leave space for
+ * a final null character and thus use only
+ * capacity - 1
+ */
+bool buf_printf(struct buffer *buf, const char *format, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+__attribute__ ((format(gnu_printf, 2, 3)))
+#else
+__attribute__ ((format(__printf__, 2, 3)))
+#endif
+#endif
+;
+
+/*
+ * puts append to a buffer with overflow check
+ */
+bool buf_puts(struct buffer *buf, const char *str);
+
+/*
+ * Like snprintf but guarantees null termination for size > 0
+ */
+bool openvpn_snprintf(char *str, size_t size, const char *format, ...)
+#ifdef __GNUC__
+#if __USE_MINGW_ANSI_STDIO
+__attribute__ ((format(gnu_printf, 3, 4)))
+#else
+__attribute__ ((format(__printf__, 3, 4)))
+#endif
+#endif
+;
+
+/*
+ * remove/add trailing characters
+ */
+
+void buf_null_terminate(struct buffer *buf);
+
+void buf_chomp(struct buffer *buf);
+
+void buf_rmtail(struct buffer *buf, uint8_t remove);
+
+/*
+ * non-buffer string functions
+ */
+void chomp(char *str);
+
+void rm_trailing_chars(char *str, const char *what_to_delete);
+
+const char *skip_leading_whitespace(const char *str);
+
+void string_null_terminate(char *str, int len, int capacity);
+
+/*
+ * Write string in buf to file descriptor fd.
+ * NOTE: requires that string be null terminated.
+ */
+void buf_write_string_file(const struct buffer *buf, const char *filename, int fd);
+
+/*
+ * write a string to the end of a buffer that was
+ * truncated by buf_printf
+ */
+void buf_catrunc(struct buffer *buf, const char *str);
+
+/*
+ * convert a multi-line output to one line
+ */
+void convert_to_one_line(struct buffer *buf);
+
+/*
+ * Parse a string based on a given delimiter char
+ */
+bool buf_parse(struct buffer *buf, const int delim, char *line, const int size);
+
+/*
+ * Hex dump -- Output a binary buffer to a hex string and return it.
+ */
+#define FHE_SPACE_BREAK_MASK 0xFF /* space_break parameter in lower 8 bits */
+#define FHE_CAPS 0x100            /* output hex in caps */
+char *
+format_hex_ex(const uint8_t *data, int size, int maxoutput,
+              unsigned int space_break_flags, const char *separator,
+              struct gc_arena *gc);
+
+static inline char *
+format_hex(const uint8_t *data, int size, int maxoutput, struct gc_arena *gc)
+{
+    return format_hex_ex(data, size, maxoutput, 4, " ", gc);
+}
+
+/*
+ * Return a buffer that is a subset of another buffer.
+ */
+struct buffer buf_sub(struct buffer *buf, int size, bool prepend);
+
+/*
+ * Check if sufficient space to append to buffer.
+ */
+
+static inline bool
+buf_safe(const struct buffer *buf, int len)
+{
+    return buf_valid(buf) && buf_size_valid(len)
+           && buf->offset + buf->len + len <= buf->capacity;
+}
+
+static inline bool
+buf_safe_bidir(const struct buffer *buf, int len)
+{
+    if (buf_valid(buf) && buf_size_valid_signed(len))
+    {
+        const int newlen = buf->len + len;
+        return newlen >= 0 && buf->offset + newlen <= buf->capacity;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+static inline int
+buf_forward_capacity(const struct buffer *buf)
+{
+    if (buf_valid(buf))
+    {
+        int ret = buf->capacity - (buf->offset + buf->len);
+        if (ret < 0)
+        {
+            ret = 0;
+        }
+        return ret;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+static inline int
+buf_forward_capacity_total(const struct buffer *buf)
+{
+    if (buf_valid(buf))
+    {
+        int ret = buf->capacity - buf->offset;
+        if (ret < 0)
+        {
+            ret = 0;
+        }
+        return ret;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+static inline int
+buf_reverse_capacity(const struct buffer *buf)
+{
+    if (buf_valid(buf))
+    {
+        return buf->offset;
+    }
+    else
+    {
+        return 0;
+    }
+}
+
+static inline bool
+buf_inc_len(struct buffer *buf, int inc)
+{
+    if (!buf_safe_bidir(buf, inc))
+    {
+        return false;
+    }
+    buf->len += inc;
+    return true;
+}
+
+/*
+ * Make space to prepend to a buffer.
+ * Return NULL if no space.
+ */
+
+static inline uint8_t *
+buf_prepend(struct buffer *buf, int size)
+{
+    if (!buf_valid(buf) || size < 0 || size > buf->offset)
+    {
+        return NULL;
+    }
+    buf->offset -= size;
+    buf->len += size;
+    return BPTR(buf);
+}
+
+static inline bool
+buf_advance(struct buffer *buf, int size)
+{
+    if (!buf_valid(buf) || size < 0 || buf->len < size)
+    {
+        return false;
+    }
+    buf->offset += size;
+    buf->len -= size;
+    return true;
+}
+
+/*
+ * Return a pointer to allocated space inside a buffer.
+ * Return NULL if no space.
+ */
+
+static inline uint8_t *
+buf_write_alloc(struct buffer *buf, int size)
+{
+    uint8_t *ret;
+    if (!buf_safe(buf, size))
+    {
+        return NULL;
+    }
+    ret = BPTR(buf) + buf->len;
+    buf->len += size;
+    return ret;
+}
+
+static inline uint8_t *
+buf_write_alloc_prepend(struct buffer *buf, int size, bool prepend)
+{
+    return prepend ? buf_prepend(buf, size) : buf_write_alloc(buf, size);
+}
+
+static inline uint8_t *
+buf_read_alloc(struct buffer *buf, int size)
+{
+    uint8_t *ret;
+    if (size < 0 || buf->len < size)
+    {
+        return NULL;
+    }
+    ret = BPTR(buf);
+    buf->offset += size;
+    buf->len -= size;
+    return ret;
+}
+
+static inline bool
+buf_write(struct buffer *dest, const void *src, int size)
+{
+    uint8_t *cp = buf_write_alloc(dest, size);
+    if (!cp)
+    {
+        return false;
+    }
+    memcpy(cp, src, size);
+    return true;
+}
+
+static inline bool
+buf_write_prepend(struct buffer *dest, const void *src, int size)
+{
+    uint8_t *cp = buf_prepend(dest, size);
+    if (!cp)
+    {
+        return false;
+    }
+    memcpy(cp, src, size);
+    return true;
+}
+
+static inline bool
+buf_write_u8(struct buffer *dest, int data)
+{
+    uint8_t u8 = (uint8_t) data;
+    return buf_write(dest, &u8, sizeof(uint8_t));
+}
+
+static inline bool
+buf_write_u16(struct buffer *dest, int data)
+{
+    uint16_t u16 = htons((uint16_t) data);
+    return buf_write(dest, &u16, sizeof(uint16_t));
+}
+
+static inline bool
+buf_write_u32(struct buffer *dest, int data)
+{
+    uint32_t u32 = htonl((uint32_t) data);
+    return buf_write(dest, &u32, sizeof(uint32_t));
+}
+
+static inline bool
+buf_copy(struct buffer *dest, const struct buffer *src)
+{
+    return buf_write(dest, BPTR(src), BLEN(src));
+}
+
+static inline bool
+buf_copy_n(struct buffer *dest, struct buffer *src, int n)
+{
+    uint8_t *cp = buf_read_alloc(src, n);
+    if (!cp)
+    {
+        return false;
+    }
+    return buf_write(dest, cp, n);
+}
+
+static inline bool
+buf_copy_range(struct buffer *dest,
+               int dest_index,
+               const struct buffer *src,
+               int src_index,
+               int src_len)
+{
+    if (src_index < 0
+        || src_len < 0
+        || src_index + src_len > src->len
+        || dest_index < 0
+        || dest->offset + dest_index + src_len > dest->capacity)
+    {
+        return false;
+    }
+    memcpy(dest->data + dest->offset + dest_index, src->data + src->offset + src_index, src_len);
+    if (dest_index + src_len > dest->len)
+    {
+        dest->len = dest_index + src_len;
+    }
+    return true;
+}
+
+/* truncate src to len, copy excess data beyond len to dest */
+static inline bool
+buf_copy_excess(struct buffer *dest,
+                struct buffer *src,
+                int len)
+{
+    if (len < 0)
+    {
+        return false;
+    }
+    if (src->len > len)
+    {
+        struct buffer b = *src;
+        src->len = len;
+        if (!buf_advance(&b, len))
+        {
+            return false;
+        }
+        return buf_copy(dest, &b);
+    }
+    else
+    {
+        return true;
+    }
+}
+
+static inline bool
+buf_read(struct buffer *src, void *dest, int size)
+{
+    uint8_t *cp = buf_read_alloc(src, size);
+    if (!cp)
+    {
+        return false;
+    }
+    memcpy(dest, cp, size);
+    return true;
+}
+
+static inline int
+buf_read_u8(struct buffer *buf)
+{
+    int ret;
+    if (BLEN(buf) < 1)
+    {
+        return -1;
+    }
+    ret = *BPTR(buf);
+    buf_advance(buf, 1);
+    return ret;
+}
+
+static inline int
+buf_read_u16(struct buffer *buf)
+{
+    uint16_t ret;
+    if (!buf_read(buf, &ret, sizeof(uint16_t)))
+    {
+        return -1;
+    }
+    return ntohs(ret);
+}
+
+static inline uint32_t
+buf_read_u32(struct buffer *buf, bool *good)
+{
+    uint32_t ret;
+    if (!buf_read(buf, &ret, sizeof(uint32_t)))
+    {
+        if (good)
+        {
+            *good = false;
+        }
+        return 0;
+    }
+    else
+    {
+        if (good)
+        {
+            *good = true;
+        }
+        return ntohl(ret);
+    }
+}
+
+/**
+ * Compare src buffer contents with match.
+ * *NOT* constant time. Do not use when comparing HMACs.
+ */
+static inline bool
+buf_string_match(const struct buffer *src, const void *match, int size)
+{
+    if (size != src->len)
+    {
+        return false;
+    }
+    return memcmp(BPTR(src), match, size) == 0;
+}
+
+/**
+ * Compare first size bytes of src buffer contents with match.
+ * *NOT* constant time. Do not use when comparing HMACs.
+ */
+static inline bool
+buf_string_match_head(const struct buffer *src, const void *match, int size)
+{
+    if (size < 0 || size > src->len)
+    {
+        return false;
+    }
+    return memcmp(BPTR(src), match, size) == 0;
+}
+
+bool buf_string_match_head_str(const struct buffer *src, const char *match);
+
+bool buf_string_compare_advance(struct buffer *src, const char *match);
+
+int buf_substring_len(const struct buffer *buf, int delim);
+
+/*
+ * Print a string which might be NULL
+ */
+const char *np(const char *str);
+
+/*#define CHARACTER_CLASS_DEBUG*/
+
+/* character classes */
+
+#define CC_ANY                (1<<0)
+#define CC_NULL               (1<<1)
+
+#define CC_ALNUM              (1<<2)
+#define CC_ALPHA              (1<<3)
+#define CC_ASCII              (1<<4)
+#define CC_CNTRL              (1<<5)
+#define CC_DIGIT              (1<<6)
+#define CC_PRINT              (1<<7)
+#define CC_PUNCT              (1<<8)
+#define CC_SPACE              (1<<9)
+#define CC_XDIGIT             (1<<10)
+
+#define CC_BLANK              (1<<11)
+#define CC_NEWLINE            (1<<12)
+#define CC_CR                 (1<<13)
+
+#define CC_BACKSLASH          (1<<14)
+#define CC_UNDERBAR           (1<<15)
+#define CC_DASH               (1<<16)
+#define CC_DOT                (1<<17)
+#define CC_COMMA              (1<<18)
+#define CC_COLON              (1<<19)
+#define CC_SLASH              (1<<20)
+#define CC_SINGLE_QUOTE       (1<<21)
+#define CC_DOUBLE_QUOTE       (1<<22)
+#define CC_REVERSE_QUOTE      (1<<23)
+#define CC_AT                 (1<<24)
+#define CC_EQUAL              (1<<25)
+#define CC_LESS_THAN          (1<<26)
+#define CC_GREATER_THAN       (1<<27)
+#define CC_PIPE               (1<<28)
+#define CC_QUESTION_MARK      (1<<29)
+#define CC_ASTERISK           (1<<30)
+
+/* macro classes */
+#define CC_NAME               (CC_ALNUM|CC_UNDERBAR)
+#define CC_CRLF               (CC_CR|CC_NEWLINE)
+
+bool char_class(const unsigned char c, const unsigned int flags);
+
+bool string_class(const char *str, const unsigned int inclusive, const unsigned int exclusive);
+
+bool string_mod(char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace);
+
+const char *string_mod_const(const char *str,
+                             const unsigned int inclusive,
+                             const unsigned int exclusive,
+                             const char replace,
+                             struct gc_arena *gc);
+
+void string_replace_leading(char *str, const char match, const char replace);
+
+/** Return true iff str starts with prefix */
+static inline bool
+strprefix(const char *str, const char *prefix)
+{
+    return 0 == strncmp(str, prefix, strlen(prefix));
+}
+
+
+#ifdef CHARACTER_CLASS_DEBUG
+void character_class_debug(void);
+
+#endif
+
+/*
+ * Verify that a pointer is correctly aligned
+ */
+#ifdef VERIFY_ALIGNMENT
+void valign4(const struct buffer *buf, const char *file, const int line);
+
+#define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__)
+#else
+#define verify_align_4(ptr)
+#endif
+
+/*
+ * Very basic garbage collection, mostly for routines that return
+ * char ptrs to malloced strings.
+ */
+
+void gc_transfer(struct gc_arena *dest, struct gc_arena *src);
+
+void x_gc_free(struct gc_arena *a);
+
+void x_gc_freespecial(struct gc_arena *a);
+
+static inline bool
+gc_defined(struct gc_arena *a)
+{
+    return a->list != NULL;
+}
+
+static inline void
+gc_init(struct gc_arena *a)
+{
+    a->list = NULL;
+    a->list_special = NULL;
+}
+
+static inline void
+gc_detach(struct gc_arena *a)
+{
+    gc_init(a);
+}
+
+static inline struct gc_arena
+gc_new(void)
+{
+    struct gc_arena ret;
+    gc_init(&ret);
+    return ret;
+}
+
+static inline void
+gc_free(struct gc_arena *a)
+{
+    if (a->list)
+    {
+        x_gc_free(a);
+    }
+    if (a->list_special)
+    {
+        x_gc_freespecial(a);
+    }
+}
+
+static inline void
+gc_reset(struct gc_arena *a)
+{
+    gc_free(a);
+}
+
+/*
+ * Allocate memory to hold a structure
+ */
+
+#define ALLOC_OBJ(dptr, type) \
+    { \
+        check_malloc_return((dptr) = (type *) malloc(sizeof(type))); \
+    }
+
+#define ALLOC_OBJ_CLEAR(dptr, type) \
+    { \
+        ALLOC_OBJ(dptr, type); \
+        memset((dptr), 0, sizeof(type)); \
+    }
+
+#define ALLOC_ARRAY(dptr, type, n) \
+    { \
+        check_malloc_return((dptr) = (type *) malloc(array_mult_safe(sizeof(type), (n), 0))); \
+    }
+
+#define ALLOC_ARRAY_GC(dptr, type, n, gc) \
+    { \
+        (dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), false, (gc)); \
+    }
+
+#define ALLOC_ARRAY_CLEAR(dptr, type, n) \
+    { \
+        ALLOC_ARRAY(dptr, type, n); \
+        memset((dptr), 0, (array_mult_safe(sizeof(type), (n), 0))); \
+    }
+
+#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \
+    { \
+        (dptr) = (type *) gc_malloc(array_mult_safe(sizeof(type), (n), 0), true, (gc)); \
+    }
+
+#define ALLOC_VAR_ARRAY_CLEAR_GC(dptr, type, atype, n, gc)      \
+    { \
+        (dptr) = (type *) gc_malloc(array_mult_safe(sizeof(atype), (n), sizeof(type)), true, (gc)); \
+    }
+
+#define ALLOC_OBJ_GC(dptr, type, gc) \
+    { \
+        (dptr) = (type *) gc_malloc(sizeof(type), false, (gc)); \
+    }
+
+#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \
+    { \
+        (dptr) = (type *) gc_malloc(sizeof(type), true, (gc)); \
+    }
+
+static inline void
+check_malloc_return(const void *p)
+{
+    if (!p)
+    {
+        out_of_memory();
+    }
+}
+
+/*
+ * Manage lists of buffers
+ */
+struct buffer_entry
+{
+    struct buffer buf;
+    struct buffer_entry *next;
+};
+
+struct buffer_list
+{
+    struct buffer_entry *head; /* next item to pop/peek */
+    struct buffer_entry *tail; /* last item pushed */
+    int size;                /* current number of entries */
+    int max_size;            /* maximum size list should grow to */
+};
+
+/**
+ * Allocate an empty buffer list of capacity \c max_size.
+ *
+ * @param max_size  the capacity of the list to allocate
+ *
+ * @return the new list
+ */
+struct buffer_list *buffer_list_new(const int max_size);
+
+/**
+ * Frees a buffer list and all the buffers in it.
+ *
+ * @param ol    the list to free
+ */
+void buffer_list_free(struct buffer_list *ol);
+
+/**
+ * Checks if the list is valid and non-empty
+ *
+ * @param ol    the list to check
+ *
+ * @return true iff \c ol is not NULL and contains at least one buffer
+ */
+bool buffer_list_defined(const struct buffer_list *ol);
+
+/**
+ * Empty the list \c ol and frees all the contained buffers
+ *
+ * @param ol    the list to reset
+ */
+void buffer_list_reset(struct buffer_list *ol);
+
+/**
+ * Allocates and appends a new buffer containing \c str as data to \c ol
+ *
+ * @param ol    the list to append the new buffer to
+ * @param str   the string to copy into the new buffer
+ */
+void buffer_list_push(struct buffer_list *ol, const char *str);
+
+/**
+ * Allocates and appends a new buffer containing \c data of length \c size.
+ *
+ * @param ol    the list to append the new buffer to
+ * @param data  the data to copy into the new buffer
+ * @param size  the length of \c data to copy into the buffer
+ *
+ * @return the new buffer
+ */
+struct buffer_entry *buffer_list_push_data(struct buffer_list *ol, const void *data, size_t size);
+
+/**
+ * Retrieve the head buffer
+ *
+ * @param ol    the list to retrieve the buffer from
+ *
+ * @return a pointer to the head buffer or NULL if the list is empty
+ */
+struct buffer *buffer_list_peek(struct buffer_list *ol);
+
+void buffer_list_advance(struct buffer_list *ol, int n);
+
+void buffer_list_pop(struct buffer_list *ol);
+
+/**
+ * Aggregates as many buffers as possible from \c bl in a new buffer of maximum
+ * length \c max_len .
+ * All the aggregated buffers are removed from the list and replaced by the new
+ * one, followed by any additional (non-aggregated) data.
+ *
+ * @param bl    the list of buffer to aggregate
+ * @param max   the maximum length of the aggregated buffer
+ */
+void buffer_list_aggregate(struct buffer_list *bl, const size_t max);
+
+/**
+ * Aggregates as many buffers as possible from \c bl in a new buffer
+ * of maximum length \c max_len . \c sep is written after
+ * each copied buffer (also after the last one). All the aggregated buffers are
+ * removed from the list and replaced by the new one, followed by any additional
+ * (non-aggregated) data.
+ * Nothing happens if \c max_len is not enough to aggregate at least 2 buffers.
+ *
+ * @param bl        the list of buffer to aggregate
+ * @param max_len   the maximum length of the aggregated buffer
+ * @param sep       the separator to put between buffers during aggregation
+ */
+void buffer_list_aggregate_separator(struct buffer_list *bl,
+                                     const size_t max_len, const char *sep);
+
+struct buffer_list *buffer_list_file(const char *fn, int max_line_len);
+
+#endif /* BUFFER_H */
diff --git a/src/openvpn/circ_list.h b/src/openvpn/circ_list.h
new file mode 100644
index 0000000..23b42d2
--- /dev/null
+++ b/src/openvpn/circ_list.h
@@ -0,0 +1,77 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 CIRC_LIST_H
+#define CIRC_LIST_H
+
+#include "basic.h"
+#include "integer.h"
+#include "error.h"
+
+#define CIRC_LIST(name, type) \
+    struct name { \
+        int x_head; \
+        int x_size; \
+        int x_cap; \
+        int x_sizeof; \
+        type x_list[EMPTY_ARRAY_SIZE]; \
+    }
+
+#define CIRC_LIST_PUSH(obj, item) \
+    { \
+        (obj)->x_head = modulo_add((obj)->x_head, -1, (obj)->x_cap); \
+        (obj)->x_list[(obj)->x_head] = (item); \
+        (obj)->x_size = min_int((obj)->x_size + 1, (obj)->x_cap); \
+    }
+
+#define CIRC_LIST_SIZE(obj) \
+    ((obj)->x_size)
+
+#define CIRC_LIST_INDEX(obj, index) \
+    modulo_add((obj)->x_head, \
+               index_verify((index), (obj)->x_size, __FILE__, __LINE__), \
+               (obj)->x_cap)
+
+#define CIRC_LIST_ITEM(obj, index) \
+    ((obj)->x_list[CIRC_LIST_INDEX((obj), (index))])
+
+#define CIRC_LIST_RESET(obj) \
+    { \
+        (obj)->x_head = 0; \
+        (obj)->x_size = 0; \
+    }
+
+#define CIRC_LIST_ALLOC(dest, list_type, size) \
+    { \
+        const int so = sizeof(list_type) + sizeof((dest)->x_list[0]) * (size); \
+        (dest) = (list_type *) malloc(so); \
+        check_malloc_return(dest); \
+        memset((dest), 0, so); \
+        (dest)->x_cap = size; \
+        (dest)->x_sizeof = so; \
+    }
+
+#define CIRC_LIST_FREE(dest) \
+    free(dest)
+
+#endif /* ifndef CIRC_LIST_H */
diff --git a/src/openvpn/clinat.c b/src/openvpn/clinat.c
new file mode 100644
index 0000000..b08fd54
--- /dev/null
+++ b/src/openvpn/clinat.c
@@ -0,0 +1,278 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 "syshead.h"
+
+#include "clinat.h"
+#include "proto.h"
+#include "socket.h"
+#include "memdbg.h"
+
+static bool
+add_entry(struct client_nat_option_list *dest,
+          const struct client_nat_entry *e)
+{
+    if (dest->n >= MAX_CLIENT_NAT)
+    {
+        msg(M_WARN, "WARNING: client-nat table overflow (max %d entries)", MAX_CLIENT_NAT);
+        return false;
+    }
+    else
+    {
+        dest->entries[dest->n++] = *e;
+        return true;
+    }
+}
+
+void
+print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
+{
+    struct gc_arena gc = gc_new();
+    int i;
+
+    msg(msglevel, "*** CNAT list");
+    if (list)
+    {
+        for (i = 0; i < list->n; ++i)
+        {
+            const struct client_nat_entry *e = &list->entries[i];
+            msg(msglevel, "  CNAT[%d] t=%d %s/%s/%s",
+                i,
+                e->type,
+                print_in_addr_t(e->network, IA_NET_ORDER, &gc),
+                print_in_addr_t(e->netmask, IA_NET_ORDER, &gc),
+                print_in_addr_t(e->foreign_network, IA_NET_ORDER, &gc));
+        }
+    }
+    gc_free(&gc);
+}
+
+struct client_nat_option_list *
+new_client_nat_list(struct gc_arena *gc)
+{
+    struct client_nat_option_list *ret;
+    ALLOC_OBJ_CLEAR_GC(ret, struct client_nat_option_list, gc);
+    return ret;
+}
+
+struct client_nat_option_list *
+clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc)
+{
+    struct client_nat_option_list *ret;
+    ALLOC_OBJ_GC(ret, struct client_nat_option_list, gc);
+    *ret = *src;
+    return ret;
+}
+
+void
+copy_client_nat_option_list(struct client_nat_option_list *dest,
+                            const struct client_nat_option_list *src)
+{
+    int i;
+    for (i = 0; i < src->n; ++i)
+    {
+        if (!add_entry(dest, &src->entries[i]))
+        {
+            break;
+        }
+    }
+}
+
+void
+add_client_nat_to_option_list(struct client_nat_option_list *dest,
+                              const char *type,
+                              const char *network,
+                              const char *netmask,
+                              const char *foreign_network,
+                              int msglevel)
+{
+    struct client_nat_entry e;
+    bool ok;
+
+    if (!strcmp(type, "snat"))
+    {
+        e.type = CN_SNAT;
+    }
+    else if (!strcmp(type, "dnat"))
+    {
+        e.type = CN_DNAT;
+    }
+    else
+    {
+        msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
+        return;
+    }
+
+    e.network = getaddr(0, network, 0, &ok, NULL);
+    if (!ok)
+    {
+        msg(msglevel, "client-nat: bad network: %s", network);
+        return;
+    }
+    e.netmask = getaddr(0, netmask, 0, &ok, NULL);
+    if (!ok)
+    {
+        msg(msglevel, "client-nat: bad netmask: %s", netmask);
+        return;
+    }
+    e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
+    if (!ok)
+    {
+        msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
+        return;
+    }
+
+    add_entry(dest, &e);
+}
+
+#if 0
+static void
+print_checksum(struct openvpn_iphdr *iph, const char *prefix)
+{
+    uint16_t *sptr;
+    unsigned int sum = 0;
+    int i = 0;
+    for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++)
+    {
+        i += 1;
+        sum += *sptr;
+    }
+    msg(M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum);
+}
+#endif
+
+static void
+print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
+{
+    struct gc_arena gc = gc_new();
+
+    char *dirstr = "???";
+    if (direction == CN_OUTGOING)
+    {
+        dirstr = "OUT";
+    }
+    else if (direction == CN_INCOMING)
+    {
+        dirstr = "IN";
+    }
+
+    msg(msglevel, "** CNAT %s %s %s -> %s",
+        dirstr,
+        prefix,
+        print_in_addr_t(iph->saddr, IA_NET_ORDER, &gc),
+        print_in_addr_t(iph->daddr, IA_NET_ORDER, &gc));
+
+    gc_free(&gc);
+}
+
+void
+client_nat_transform(const struct client_nat_option_list *list,
+                     struct buffer *ipbuf,
+                     const int direction)
+{
+    struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *) BPTR(ipbuf);
+    int i;
+    uint32_t addr, *addr_ptr;
+    const uint32_t *from, *to;
+    int accumulate = 0;
+    unsigned int amask;
+    unsigned int alog = 0;
+
+    if (check_debug_level(D_CLIENT_NAT))
+    {
+        print_pkt(&h->ip, "BEFORE", direction, D_CLIENT_NAT);
+    }
+
+    for (i = 0; i < list->n; ++i)
+    {
+        const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
+        if (e->type ^ direction)
+        {
+            addr = *(addr_ptr = &h->ip.daddr);
+            amask = 2;
+        }
+        else
+        {
+            addr = *(addr_ptr = &h->ip.saddr);
+            amask = 1;
+        }
+        if (direction)
+        {
+            from = &e->foreign_network;
+            to = &e->network;
+        }
+        else
+        {
+            from = &e->network;
+            to = &e->foreign_network;
+        }
+
+        if (((addr & e->netmask) == *from) && !(amask & alog))
+        {
+            /* pre-adjust IP checksum */
+            ADD_CHECKSUM_32(accumulate, addr);
+
+            /* do NAT transform */
+            addr = (addr & ~e->netmask) | *to;
+
+            /* post-adjust IP checksum */
+            SUB_CHECKSUM_32(accumulate, addr);
+
+            /* write the modified address to packet */
+            *addr_ptr = addr;
+
+            /* mark as modified */
+            alog |= amask;
+        }
+    }
+    if (alog)
+    {
+        if (check_debug_level(D_CLIENT_NAT))
+        {
+            print_pkt(&h->ip, "AFTER", direction, D_CLIENT_NAT);
+        }
+
+        ADJUST_CHECKSUM(accumulate, h->ip.check);
+
+        if (h->ip.protocol == OPENVPN_IPPROTO_TCP)
+        {
+            if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
+            {
+                ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
+            }
+        }
+        else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
+        {
+            if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
+            {
+                ADJUST_CHECKSUM(accumulate, h->u.udp.check);
+            }
+        }
+    }
+}
diff --git a/src/openvpn/clinat.h b/src/openvpn/clinat.h
new file mode 100644
index 0000000..eec7a03
--- /dev/null
+++ b/src/openvpn/clinat.h
@@ -0,0 +1,67 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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.
+ */
+
+#if !defined(CLINAT_H)
+#define CLINAT_H
+
+#include "buffer.h"
+
+#define MAX_CLIENT_NAT 64
+
+#define CN_OUTGOING 0
+#define CN_INCOMING 1
+
+struct client_nat_entry {
+#define CN_SNAT 0
+#define CN_DNAT 1
+    int type;
+    in_addr_t network;
+    in_addr_t netmask;
+    in_addr_t foreign_network;
+};
+
+struct client_nat_option_list {
+    int n;
+    struct client_nat_entry entries[MAX_CLIENT_NAT];
+};
+
+struct client_nat_option_list *new_client_nat_list(struct gc_arena *gc);
+
+struct client_nat_option_list *clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc);
+
+void copy_client_nat_option_list(struct client_nat_option_list *dest, const struct client_nat_option_list *src);
+
+void print_client_nat_list(const struct client_nat_option_list *list, int msglevel);
+
+void add_client_nat_to_option_list(struct client_nat_option_list *dest,
+                                   const char *type,
+                                   const char *network,
+                                   const char *netmask,
+                                   const char *foreign_network,
+                                   int msglevel);
+
+void client_nat_transform(const struct client_nat_option_list *list,
+                          struct buffer *ipbuf,
+                          const int direction);
+
+#endif /* if !defined(CLINAT_H) */
diff --git a/src/openvpn/common.h b/src/openvpn/common.h
new file mode 100644
index 0000000..0f73200
--- /dev/null
+++ b/src/openvpn/common.h
@@ -0,0 +1,104 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 COMMON_H
+#define COMMON_H
+
+/*
+ * Statistics counters and associated printf formats.
+ */
+#ifdef USE_64_BIT_COUNTERS
+typedef unsigned long long int counter_type;
+#ifdef _WIN32
+#define counter_format  "%I64u"
+#else
+#define counter_format  "%llu"
+#endif
+#else  /* ifdef USE_64_BIT_COUNTERS */
+typedef unsigned int counter_type;
+#define counter_format   "%u"
+#endif
+
+/*
+ * Time intervals
+ */
+typedef int interval_t;
+
+/*
+ * Used as an upper bound for timeouts.
+ */
+#define BIG_TIMEOUT  (60*60*24*7)  /* one week (in seconds) */
+
+/*
+ * Printf formats for special types
+ */
+#ifdef _WIN64
+#define ptr_format              "0x%I64x"
+#else
+#define ptr_format              "0x%08lx"
+#endif
+#define time_format             "%lu"
+#define fragment_header_format  "0x%08x"
+
+/* these are used to cast the arguments
+ * and MUST match the formats above */
+typedef unsigned long time_type;
+#ifdef _WIN64
+typedef unsigned long long ptr_type;
+#else
+typedef unsigned long ptr_type;
+#endif
+
+/* the --client-config-dir default file */
+#define CCD_DEFAULT "DEFAULT"
+
+/*
+ * This parameter controls the TLS channel buffer size and the
+ * maximum size of a single TLS message (cleartext).
+ * This parameter must be >= PUSH_BUNDLE_SIZE
+ */
+#define TLS_CHANNEL_BUF_SIZE 2048
+
+/*
+ * This parameter controls the maximum size of a bundle
+ * of pushed options.
+ */
+#define PUSH_BUNDLE_SIZE 1024
+
+/*
+ * In how many seconds does client re-send PUSH_REQUEST if we haven't yet received a reply
+ */
+#define PUSH_REQUEST_INTERVAL 5
+
+/*
+ * A sort of pseudo-filename for data provided inline within
+ * the configuration file.
+ */
+#define INLINE_FILE_TAG "[[INLINE]]"
+
+/*
+ * Script security warning
+ */
+#define SCRIPT_SECURITY_WARNING "WARNING: External program may not be called unless '--script-security 2' or higher is enabled. See --help text or man page for detailed info."
+
+#endif /* ifndef COMMON_H */
diff --git a/src/openvpn/comp-lz4.c b/src/openvpn/comp-lz4.c
new file mode 100644
index 0000000..f2916bd
--- /dev/null
+++ b/src/openvpn/comp-lz4.c
@@ -0,0 +1,322 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *  Copyright (C) 2013-2018 Gert Doering <gert@greenie.muc.de>
+ *
+ *  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 "syshead.h"
+
+#if defined(ENABLE_LZ4)
+
+#if defined(NEED_COMPAT_LZ4)
+#include "compat-lz4.h"
+#else
+#include "lz4.h"
+#endif
+
+#include "comp.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+
+static void
+lz4_compress_init(struct compress_context *compctx)
+{
+    msg(D_INIT_MEDIUM, "LZ4 compression initializing");
+    ASSERT(compctx->flags & COMP_F_SWAP);
+}
+
+static void
+lz4v2_compress_init(struct compress_context *compctx)
+{
+    msg(D_INIT_MEDIUM, "LZ4v2 compression initializing");
+}
+
+static void
+lz4_compress_uninit(struct compress_context *compctx)
+{
+}
+
+static bool
+do_lz4_compress(struct buffer *buf,
+                struct buffer *work,
+                struct compress_context *compctx,
+                const struct frame *frame)
+{
+    /*
+     * In order to attempt compression, length must be at least COMPRESS_THRESHOLD.
+     */
+    if (buf->len >= COMPRESS_THRESHOLD)
+    {
+        const size_t ps = PAYLOAD_SIZE(frame);
+        int zlen_max = ps + COMP_EXTRA_BUFFER(ps);
+        int zlen;
+
+        ASSERT(buf_init(work, FRAME_HEADROOM(frame)));
+        ASSERT(buf_safe(work, zlen_max));
+
+        if (buf->len > ps)
+        {
+            dmsg(D_COMP_ERRORS, "LZ4 compression buffer overflow");
+            buf->len = 0;
+            return false;
+        }
+
+        zlen = LZ4_compress_default((const char *)BPTR(buf), (char *)BPTR(work), BLEN(buf), zlen_max);
+
+        if (zlen <= 0)
+        {
+            dmsg(D_COMP_ERRORS, "LZ4 compression error");
+            buf->len = 0;
+            return false;
+        }
+
+        ASSERT(buf_safe(work, zlen));
+        work->len = zlen;
+
+
+        dmsg(D_COMP, "LZ4 compress %d -> %d", buf->len, work->len);
+        compctx->pre_compress += buf->len;
+        compctx->post_compress += work->len;
+        return true;
+    }
+    return false;
+}
+
+
+static void
+lz4_compress(struct buffer *buf, struct buffer work,
+             struct compress_context *compctx,
+             const struct frame *frame)
+{
+    bool compressed;
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    compressed = do_lz4_compress(buf, &work, compctx, frame);
+
+    /* On error do_lz4_compress sets buf len to zero, just return */
+    if (buf->len == 0)
+    {
+        return;
+    }
+
+    /* did compression save us anything? */
+    {
+        uint8_t comp_head_byte = NO_COMPRESS_BYTE_SWAP;
+        if (compressed && work.len < buf->len)
+        {
+            *buf = work;
+            comp_head_byte = LZ4_COMPRESS_BYTE;
+        }
+
+        {
+            uint8_t *head = BPTR(buf);
+            uint8_t *tail  = BEND(buf);
+            ASSERT(buf_safe(buf, 1));
+            ++buf->len;
+
+            /* move head byte of payload to tail */
+            *tail = *head;
+            *head = comp_head_byte;
+        }
+    }
+}
+
+
+static void
+lz4v2_compress(struct buffer *buf, struct buffer work,
+               struct compress_context *compctx,
+               const struct frame *frame)
+{
+    bool compressed;
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    compressed = do_lz4_compress(buf, &work, compctx, frame);
+
+    /* On Error just return */
+    if (buf->len == 0)
+    {
+        return;
+    }
+
+    /* did compression save us anything?  Include 2 byte compression header
+     * in calculation */
+    if (compressed && work.len + 2 < buf->len)
+    {
+        ASSERT(buf_prepend(&work, 2));
+        uint8_t *head = BPTR(&work);
+        head[0] = COMP_ALGV2_INDICATOR_BYTE;
+        head[1] = COMP_ALGV2_LZ4_BYTE;
+        *buf = work;
+    }
+    else
+    {
+        compv2_escape_data_ifneeded(buf);
+    }
+}
+
+static void
+do_lz4_decompress(size_t zlen_max,
+                  struct buffer *work,
+                  struct buffer *buf,
+                  struct compress_context *compctx)
+{
+    int uncomp_len;
+    ASSERT(buf_safe(work, zlen_max));
+    uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(work), (size_t)BLEN(buf), zlen_max);
+    if (uncomp_len <= 0)
+    {
+        dmsg(D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
+        buf->len = 0;
+        return;
+    }
+
+    ASSERT(buf_safe(work, uncomp_len));
+    work->len = uncomp_len;
+
+    dmsg(D_COMP, "LZ4 decompress %d -> %d", buf->len, work->len);
+    compctx->pre_decompress += buf->len;
+    compctx->post_decompress += work->len;
+
+    *buf = *work;
+}
+
+static void
+lz4_decompress(struct buffer *buf, struct buffer work,
+               struct compress_context *compctx,
+               const struct frame *frame)
+{
+    size_t zlen_max = EXPANDED_SIZE(frame);
+    uint8_t c;          /* flag indicating whether or not our peer compressed */
+
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
+
+    /* do unframing/swap (assumes buf->len > 0) */
+    {
+        uint8_t *head = BPTR(buf);
+        c = *head;
+        --buf->len;
+        *head = *BEND(buf);
+    }
+
+    if (c == LZ4_COMPRESS_BYTE) /* packet was compressed */
+    {
+        do_lz4_decompress(zlen_max, &work, buf, compctx);
+    }
+    else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */
+    {
+    }
+    else
+    {
+        dmsg(D_COMP_ERRORS, "Bad LZ4 decompression header byte: %d", c);
+        buf->len = 0;
+    }
+}
+
+static void
+lz4v2_decompress(struct buffer *buf, struct buffer work,
+                 struct compress_context *compctx,
+                 const struct frame *frame)
+{
+    size_t zlen_max = EXPANDED_SIZE(frame);
+    uint8_t c;          /* flag indicating whether or not our peer compressed */
+
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    ASSERT(buf_init(&work, FRAME_HEADROOM(frame)));
+
+    /* do unframing/swap (assumes buf->len > 0) */
+    uint8_t *head = BPTR(buf);
+    c = *head;
+
+    /* Not compressed */
+    if (c != COMP_ALGV2_INDICATOR_BYTE)
+    {
+        return;
+    }
+
+    /* Packet to short to make sense */
+    if (buf->len <= 1)
+    {
+        buf->len = 0;
+        return;
+    }
+
+    c = head[1];
+    if (c == COMP_ALGV2_LZ4_BYTE) /* packet was compressed */
+    {
+        buf_advance(buf,2);
+        do_lz4_decompress(zlen_max, &work, buf, compctx);
+    }
+    else if (c == COMP_ALGV2_UNCOMPRESSED_BYTE)
+    {
+        buf_advance(buf,2);
+    }
+    else
+    {
+        dmsg(D_COMP_ERRORS, "Bad LZ4v2 decompression header byte: %d", c);
+        buf->len = 0;
+    }
+}
+
+const struct compress_alg lz4_alg = {
+    "lz4",
+    lz4_compress_init,
+    lz4_compress_uninit,
+    lz4_compress,
+    lz4_decompress
+};
+
+const struct compress_alg lz4v2_alg = {
+    "lz4v2",
+    lz4v2_compress_init,
+    lz4_compress_uninit,
+    lz4v2_compress,
+    lz4v2_decompress
+};
+
+#else  /* if defined(ENABLE_LZ4) */
+static void
+dummy(void)
+{
+}
+#endif /* ENABLE_LZ4 */
diff --git a/src/openvpn/comp-lz4.h b/src/openvpn/comp-lz4.h
new file mode 100644
index 0000000..8c1ca3a
--- /dev/null
+++ b/src/openvpn/comp-lz4.h
@@ -0,0 +1,41 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *  Copyright (C) 2013-2018 Gert Doering <gert@greenie.muc.de>
+ *
+ *  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 OPENVPN_COMP_LZ4_H
+#define OPENVPN_COMP_LZ4_H
+
+#if defined(ENABLE_LZ4)
+
+#include "buffer.h"
+
+extern const struct compress_alg lz4_alg;
+extern const struct compress_alg lz4v2_alg;
+
+struct lz4_workspace
+{
+    int dummy;
+};
+
+#endif /* ENABLE_LZ4 */
+#endif
diff --git a/src/openvpn/comp.c b/src/openvpn/comp.c
new file mode 100644
index 0000000..a945913
--- /dev/null
+++ b/src/openvpn/comp.c
@@ -0,0 +1,176 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 "syshead.h"
+
+#ifdef USE_COMP
+
+#include "comp.h"
+#include "error.h"
+#include "otime.h"
+
+#include "memdbg.h"
+
+struct compress_context *
+comp_init(const struct compress_options *opt)
+{
+    struct compress_context *compctx = NULL;
+    switch (opt->alg)
+    {
+        case COMP_ALG_STUB:
+            ALLOC_OBJ_CLEAR(compctx, struct compress_context);
+            compctx->flags = opt->flags;
+            compctx->alg = comp_stub_alg;
+            break;
+
+        case COMP_ALGV2_UNCOMPRESSED:
+            ALLOC_OBJ_CLEAR(compctx, struct compress_context);
+            compctx->flags = opt->flags;
+            compctx->alg = compv2_stub_alg;
+            break;
+
+#ifdef ENABLE_LZO
+        case COMP_ALG_LZO:
+            ALLOC_OBJ_CLEAR(compctx, struct compress_context);
+            compctx->flags = opt->flags;
+            compctx->alg = lzo_alg;
+            break;
+
+#endif
+#ifdef ENABLE_LZ4
+        case COMP_ALG_LZ4:
+            ALLOC_OBJ_CLEAR(compctx, struct compress_context);
+            compctx->flags = opt->flags;
+            compctx->alg = lz4_alg;
+            break;
+
+        case COMP_ALGV2_LZ4:
+            ALLOC_OBJ_CLEAR(compctx, struct compress_context);
+            compctx->flags = opt->flags;
+            compctx->alg = lz4v2_alg;
+            break;
+#endif
+    }
+    if (compctx)
+    {
+        (*compctx->alg.compress_init)(compctx);
+    }
+
+    return compctx;
+}
+
+/* In the v2 compression schemes, an uncompressed packet has
+ * has no opcode in front, unless the first byte is 0x50. In this
+ * case the packet needs to be escaped */
+void
+compv2_escape_data_ifneeded(struct buffer *buf)
+{
+    uint8_t *head = BPTR(buf);
+    if (head[0] != COMP_ALGV2_INDICATOR_BYTE)
+    {
+        return;
+    }
+
+    /* Header is 0x50 */
+    ASSERT(buf_prepend(buf, 2));
+
+    head = BPTR(buf);
+    head[0] = COMP_ALGV2_INDICATOR_BYTE;
+    head[1] = COMP_ALGV2_UNCOMPRESSED;
+}
+
+
+void
+comp_uninit(struct compress_context *compctx)
+{
+    if (compctx)
+    {
+        (*compctx->alg.compress_uninit)(compctx);
+        free(compctx);
+    }
+}
+
+void
+comp_add_to_extra_frame(struct frame *frame)
+{
+    /* Leave room for our one-byte compressed/didn't-compress prefix byte. */
+    frame_add_to_extra_frame(frame, COMP_PREFIX_LEN);
+}
+
+void
+comp_add_to_extra_buffer(struct frame *frame)
+{
+    /* Leave room for compression buffer to expand in worst case scenario
+     * where data is totally uncompressible */
+    frame_add_to_extra_buffer(frame, COMP_EXTRA_BUFFER(EXPANDED_SIZE(frame)));
+}
+
+void
+comp_print_stats(const struct compress_context *compctx, struct status_output *so)
+{
+    if (compctx)
+    {
+        status_printf(so, "pre-compress bytes," counter_format, compctx->pre_compress);
+        status_printf(so, "post-compress bytes," counter_format, compctx->post_compress);
+        status_printf(so, "pre-decompress bytes," counter_format, compctx->pre_decompress);
+        status_printf(so, "post-decompress bytes," counter_format, compctx->post_decompress);
+    }
+}
+
+/*
+ * Tell our peer which compression algorithms we support.
+ */
+void
+comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out)
+{
+    if (opt)
+    {
+        bool lzo_avail = false;
+        if (!(opt->flags & COMP_F_ADVERTISE_STUBS_ONLY))
+        {
+#if defined(ENABLE_LZ4)
+            buf_printf(out, "IV_LZ4=1\n");
+            buf_printf(out, "IV_LZ4v2=1\n");
+#endif
+#if defined(ENABLE_LZO)
+            buf_printf(out, "IV_LZO=1\n");
+            lzo_avail = true;
+#endif
+        }
+        if (!lzo_avail)
+        {
+            buf_printf(out, "IV_LZO_STUB=1\n");
+        }
+        buf_printf(out, "IV_COMP_STUB=1\n");
+        buf_printf(out, "IV_COMP_STUBv2=1\n");
+        buf_printf(out, "IV_TCPNL=1\n");
+    }
+}
+
+#endif /* USE_COMP */
diff --git a/src/openvpn/comp.h b/src/openvpn/comp.h
new file mode 100644
index 0000000..0dadd1e
--- /dev/null
+++ b/src/openvpn/comp.h
@@ -0,0 +1,198 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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.
+ */
+
+/*
+ * Generic compression support.  Currently we support
+ * LZO 2 and LZ4.
+ */
+#ifndef OPENVPN_COMP_H
+#define OPENVPN_COMP_H
+
+#ifdef USE_COMP
+
+#include "buffer.h"
+#include "mtu.h"
+#include "common.h"
+#include "status.h"
+
+/* algorithms */
+#define COMP_ALG_UNDEF  0
+#define COMP_ALG_STUB   1 /* support compression command byte and framing without actual compression */
+#define COMP_ALG_LZO    2 /* LZO algorithm */
+#define COMP_ALG_SNAPPY 3 /* Snappy algorithm (no longer supported) */
+#define COMP_ALG_LZ4    4 /* LZ4 algorithm */
+
+
+/* algorithm v2 */
+#define COMP_ALGV2_UNCOMPRESSED 10
+#define COMP_ALGV2_LZ4      11
+/*
+ #define COMP_ALGV2_LZO     12
+ #define COMP_ALGV2_SNAPPY   13
+ */
+
+/* Compression flags */
+#define COMP_F_ADAPTIVE   (1<<0) /* COMP_ALG_LZO only */
+#define COMP_F_ASYM       (1<<1) /* only downlink is compressed, not uplink */
+#define COMP_F_SWAP       (1<<2) /* initial command byte is swapped with last byte in buffer to preserve payload alignment */
+#define COMP_F_ADVERTISE_STUBS_ONLY (1<<3) /* tell server that we only support compression stubs */
+
+
+/*
+ * Length of prepended prefix on compressed packets
+ */
+#define COMP_PREFIX_LEN 1
+
+/*
+ * Prefix bytes
+ */
+
+/* V1 on wire codes */
+/* Initial command byte to tell our peer if we compressed */
+#define LZO_COMPRESS_BYTE 0x66
+#define LZ4_COMPRESS_BYTE 0x69
+#define NO_COMPRESS_BYTE      0xFA
+#define NO_COMPRESS_BYTE_SWAP 0xFB /* to maintain payload alignment, replace this byte with last byte of packet */
+
+/* V2 on wire code */
+#define COMP_ALGV2_INDICATOR_BYTE       0x50
+#define COMP_ALGV2_UNCOMPRESSED_BYTE    0
+#define COMP_ALGV2_LZ4_BYTE             1
+#define COMP_ALGV2_LZO_BYTE             2
+#define COMP_ALGV2_SNAPPY_BYTE          3
+
+/*
+ * Compress worst case size expansion (for any algorithm)
+ *
+ * LZO:    len + len/8 + 128 + 3
+ * Snappy: len + len/6 + 32
+ * LZ4:    len + len/255 + 16  (LZ4_COMPRESSBOUND(len))
+ */
+#define COMP_EXTRA_BUFFER(len) ((len)/6 + 128 + 3 + COMP_PREFIX_LEN)
+
+/*
+ * Don't try to compress any packet smaller than this.
+ */
+#define COMPRESS_THRESHOLD 100
+
+/* Forward declaration of compression context */
+struct compress_context;
+
+/*
+ * Virtual methods and other static info for each compression algorithm
+ */
+struct compress_alg
+{
+    const char *name;
+    void (*compress_init)(struct compress_context *compctx);
+    void (*compress_uninit)(struct compress_context *compctx);
+    void (*compress)(struct buffer *buf, struct buffer work,
+                     struct compress_context *compctx,
+                     const struct frame *frame);
+
+    void (*decompress)(struct buffer *buf, struct buffer work,
+                       struct compress_context *compctx,
+                       const struct frame *frame);
+};
+
+/*
+ * Headers for each compression implementation
+ */
+#ifdef ENABLE_LZO
+#include "lzo.h"
+#endif
+
+#ifdef ENABLE_LZ4
+#include "comp-lz4.h"
+#endif
+
+/*
+ * Information that basically identifies a compression
+ * algorithm and related flags.
+ */
+struct compress_options
+{
+    int alg;
+    unsigned int flags;
+};
+
+/*
+ * Workspace union of all supported compression algorithms
+ */
+union compress_workspace_union
+{
+#ifdef ENABLE_LZO
+    struct lzo_compress_workspace lzo;
+#endif
+#ifdef ENABLE_LZ4
+    struct lz4_workspace lz4;
+#endif
+};
+
+/*
+ * Context for active compression session
+ */
+struct compress_context
+{
+    unsigned int flags;
+    struct compress_alg alg;
+    union compress_workspace_union wu;
+
+    /* statistics */
+    counter_type pre_decompress;
+    counter_type post_decompress;
+    counter_type pre_compress;
+    counter_type post_compress;
+};
+
+extern const struct compress_alg comp_stub_alg;
+extern const struct compress_alg compv2_stub_alg;
+
+struct compress_context *comp_init(const struct compress_options *opt);
+
+void comp_uninit(struct compress_context *compctx);
+
+void comp_add_to_extra_frame(struct frame *frame);
+
+void comp_add_to_extra_buffer(struct frame *frame);
+
+void comp_print_stats(const struct compress_context *compctx, struct status_output *so);
+
+void comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out);
+
+void compv2_escape_data_ifneeded(struct buffer *buf);
+
+static inline bool
+comp_enabled(const struct compress_options *info)
+{
+    return info->alg != COMP_ALG_UNDEF;
+}
+
+static inline bool
+comp_unswapped_prefix(const struct compress_options *info)
+{
+    return !(info->flags & COMP_F_SWAP);
+}
+
+#endif /* USE_COMP */
+#endif /* ifndef OPENVPN_COMP_H */
diff --git a/src/openvpn/compstub.c b/src/openvpn/compstub.c
new file mode 100644
index 0000000..9123541
--- /dev/null
+++ b/src/openvpn/compstub.c
@@ -0,0 +1,184 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *
+ *  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 "syshead.h"
+
+#if defined(USE_COMP)
+
+#include "comp.h"
+#include "error.h"
+#include "otime.h"
+
+#include "memdbg.h"
+
+static void
+stub_compress_init(struct compress_context *compctx)
+{
+}
+
+static void
+stub_compress_uninit(struct compress_context *compctx)
+{
+}
+
+static void
+stub_compress(struct buffer *buf, struct buffer work,
+              struct compress_context *compctx,
+              const struct frame *frame)
+{
+    if (buf->len <= 0)
+    {
+        return;
+    }
+    if (compctx->flags & COMP_F_SWAP)
+    {
+        uint8_t *head = BPTR(buf);
+        uint8_t *tail  = BEND(buf);
+        ASSERT(buf_safe(buf, 1));
+        ++buf->len;
+
+        /* move head byte of payload to tail */
+        *tail = *head;
+        *head = NO_COMPRESS_BYTE_SWAP;
+    }
+    else
+    {
+        uint8_t *header = buf_prepend(buf, 1);
+        *header = NO_COMPRESS_BYTE;
+    }
+}
+
+static void
+stub_decompress(struct buffer *buf, struct buffer work,
+                struct compress_context *compctx,
+                const struct frame *frame)
+{
+    uint8_t c;
+    if (buf->len <= 0)
+    {
+        return;
+    }
+    if (compctx->flags & COMP_F_SWAP)
+    {
+        uint8_t *head = BPTR(buf);
+        c = *head;
+        --buf->len;
+        *head = *BEND(buf);
+        if (c != NO_COMPRESS_BYTE_SWAP)
+        {
+            dmsg(D_COMP_ERRORS, "Bad compression stub (swap) decompression header byte: %d", c);
+            buf->len = 0;
+        }
+    }
+    else
+    {
+        c = *BPTR(buf);
+        ASSERT(buf_advance(buf, 1));
+        if (c != NO_COMPRESS_BYTE)
+        {
+            dmsg(D_COMP_ERRORS, "Bad compression stub decompression header byte: %d", c);
+            buf->len = 0;
+        }
+    }
+}
+
+
+static void
+stubv2_compress(struct buffer *buf, struct buffer work,
+                struct compress_context *compctx,
+                const struct frame *frame)
+{
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    compv2_escape_data_ifneeded(buf);
+}
+
+static void
+stubv2_decompress(struct buffer *buf, struct buffer work,
+                  struct compress_context *compctx,
+                  const struct frame *frame)
+{
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    uint8_t *head = BPTR(buf);
+
+    /* no compression or packet to short*/
+    if (head[0] != COMP_ALGV2_INDICATOR_BYTE)
+    {
+        return;
+    }
+
+    /* compression header (0x50) is present */
+    buf_advance(buf, 1);
+
+    /* Packet buffer too short (only 1 byte) */
+    if (buf->len <= 0)
+    {
+        return;
+    }
+
+    head = BPTR(buf);
+    buf_advance(buf, 1);
+
+    if (head[0] != COMP_ALGV2_UNCOMPRESSED_BYTE)
+    {
+        dmsg(D_COMP_ERRORS, "Bad compression stubv2 decompression header byte: %d", *head);
+        buf->len = 0;
+        return;
+    }
+}
+
+const struct compress_alg compv2_stub_alg = {
+    "stubv2",
+    stub_compress_init,
+    stub_compress_uninit,
+    stubv2_compress,
+    stubv2_decompress
+};
+
+const struct compress_alg comp_stub_alg = {
+    "stub",
+    stub_compress_init,
+    stub_compress_uninit,
+    stub_compress,
+    stub_decompress
+};
+
+#else  /* if defined(USE_COMP) */
+static void
+dummy(void)
+{
+}
+#endif /* USE_STUB */
diff --git a/src/openvpn/console.c b/src/openvpn/console.c
new file mode 100644
index 0000000..4d49722
--- /dev/null
+++ b/src/openvpn/console.c
@@ -0,0 +1,86 @@
+/*
+ *  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) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ *  Copyright (C) 2014-2015 David Sommerseth <davids@redhat.com>
+ *  Copyright (C) 2016-2018 David Sommerseth <davids@openvpn.net>
+ *
+ *  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 "syshead.h"
+#include "console.h"
+#include "error.h"
+#include "buffer.h"
+#include "misc.h"
+
+#ifdef ENABLE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+
+struct _query_user query_user[QUERY_USER_NUMSLOTS];  /* GLOBAL */
+
+
+void
+query_user_clear(void)
+{
+    int i;
+
+    for (i = 0; i < QUERY_USER_NUMSLOTS; i++)
+    {
+        CLEAR(query_user[i]);
+    }
+}
+
+
+void
+query_user_add(char *prompt, size_t prompt_len,
+               char *resp, size_t resp_len,
+               bool echo)
+{
+    int i;
+
+    /* Ensure input is sane.  All these must be present otherwise it is
+     * a programming error.
+     */
+    ASSERT( prompt_len > 0 && prompt != NULL && resp_len > 0 && resp != NULL );
+
+    /* Seek to the last unused slot */
+    for (i = 0; i < QUERY_USER_NUMSLOTS; i++)
+    {
+        if (query_user[i].prompt == NULL)
+        {
+            break;
+        }
+    }
+    ASSERT( i < QUERY_USER_NUMSLOTS );  /* Unlikely, but we want to panic if it happens */
+
+    /* Save the information needed for the user interaction */
+    query_user[i].prompt = prompt;
+    query_user[i].prompt_len = prompt_len;
+    query_user[i].response = resp;
+    query_user[i].response_len = resp_len;
+    query_user[i].echo = echo;
+}
diff --git a/src/openvpn/console.h b/src/openvpn/console.h