summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlberto Gonzalez Iniesta <agi@inittab.org>2012-02-21 15:53:40 +0100
committerAlberto Gonzalez Iniesta <agi@inittab.org>2012-02-21 15:53:40 +0100
commit349cfa7acb95abe865209a28e417ec74b56f9bba (patch)
treead65334821b587c4ecdd461be84c94305ffdb888
Imported Upstream version 2.2.1upstream/2.2.1
-rw-r--r--AUTHORS1
-rw-r--r--COPYING217
-rw-r--r--COPYRIGHT.GPL339
-rw-r--r--ChangeLog3483
-rw-r--r--INSTALL343
-rw-r--r--INSTALL-win32.txt23
-rw-r--r--Makefile.am164
-rw-r--r--Makefile.in1243
-rw-r--r--NEWS0
-rw-r--r--PORTS94
-rw-r--r--README75
-rw-r--r--acinclude.m4127
-rw-r--r--aclocal.m4964
-rw-r--r--base64.c140
-rw-r--r--base64.h44
-rw-r--r--basic.h49
-rw-r--r--buffer.c1060
-rw-r--r--buffer.h864
-rw-r--r--circ_list.h78
-rw-r--r--common.h102
-rwxr-xr-xconfig.guess1501
-rw-r--r--config.h.in622
-rwxr-xr-xconfig.sub1705
-rwxr-xr-xconfigure9168
-rw-r--r--configure.ac916
-rw-r--r--configure_h.awk39
-rw-r--r--configure_log.awk33
-rw-r--r--contrib/OCSP_check/OCSP_check.sh111
-rw-r--r--contrib/README2
-rw-r--r--contrib/multilevel-init.patch79
-rw-r--r--contrib/openvpn-fwmarkroute-1.00/README44
-rwxr-xr-xcontrib/openvpn-fwmarkroute-1.00/fwmarkroute.down22
-rwxr-xr-xcontrib/openvpn-fwmarkroute-1.00/fwmarkroute.up49
-rw-r--r--contrib/pull-resolv-conf/client.down47
-rw-r--r--contrib/pull-resolv-conf/client.up101
-rw-r--r--crypto.c1841
-rw-r--r--crypto.h421
-rw-r--r--cryptoapi.c476
-rw-r--r--cryptoapi.h7
-rw-r--r--debug/valgrind-suppress1182
-rwxr-xr-xdepcomp630
-rw-r--r--dhcp.c183
-rw-r--r--dhcp.h87
-rwxr-xr-xdoclean73
-rw-r--r--domake-win138
-rw-r--r--easy-rsa/1.0/README161
-rwxr-xr-xeasy-rsa/1.0/build-ca13
-rwxr-xr-xeasy-rsa/1.0/build-dh12
-rwxr-xr-xeasy-rsa/1.0/build-inter19
-rwxr-xr-xeasy-rsa/1.0/build-key20
-rwxr-xr-xeasy-rsa/1.0/build-key-pass20
-rwxr-xr-xeasy-rsa/1.0/build-key-pkcs1221
-rwxr-xr-xeasy-rsa/1.0/build-key-server22
-rwxr-xr-xeasy-rsa/1.0/build-req18
-rwxr-xr-xeasy-rsa/1.0/build-req-pass18
-rwxr-xr-xeasy-rsa/1.0/clean-all19
-rw-r--r--easy-rsa/1.0/list-crl18
-rw-r--r--easy-rsa/1.0/make-crl18
-rw-r--r--easy-rsa/1.0/openssl.cnf255
-rw-r--r--easy-rsa/1.0/revoke-crt18
-rwxr-xr-xeasy-rsa/1.0/revoke-full29
-rwxr-xr-xeasy-rsa/1.0/sign-req18
-rw-r--r--easy-rsa/1.0/vars49
-rw-r--r--easy-rsa/2.0/Makefile13
-rw-r--r--easy-rsa/2.0/README229
-rwxr-xr-xeasy-rsa/2.0/build-ca8
-rwxr-xr-xeasy-rsa/2.0/build-dh11
-rwxr-xr-xeasy-rsa/2.0/build-inter7
-rwxr-xr-xeasy-rsa/2.0/build-key7
-rwxr-xr-xeasy-rsa/2.0/build-key-pass7
-rwxr-xr-xeasy-rsa/2.0/build-key-pkcs128
-rwxr-xr-xeasy-rsa/2.0/build-key-server10
-rwxr-xr-xeasy-rsa/2.0/build-req7
-rwxr-xr-xeasy-rsa/2.0/build-req-pass7
-rwxr-xr-xeasy-rsa/2.0/clean-all16
-rwxr-xr-xeasy-rsa/2.0/inherit-inter39
-rwxr-xr-xeasy-rsa/2.0/list-crl13
-rwxr-xr-xeasy-rsa/2.0/openssl-0.9.6.cnf265
-rwxr-xr-xeasy-rsa/2.0/openssl-0.9.8.cnf290
-rwxr-xr-xeasy-rsa/2.0/openssl-1.0.0.cnf285
-rw-r--r--easy-rsa/2.0/openssl-1.0.0.cnf-old-copy285
-rwxr-xr-xeasy-rsa/2.0/pkitool379
-rwxr-xr-xeasy-rsa/2.0/revoke-full40
-rwxr-xr-xeasy-rsa/2.0/sign-req7
-rw-r--r--easy-rsa/2.0/tmp/README229
-rwxr-xr-xeasy-rsa/2.0/tmp/build-ca8
-rwxr-xr-xeasy-rsa/2.0/tmp/build-dh11
-rwxr-xr-xeasy-rsa/2.0/tmp/build-inter7
-rwxr-xr-xeasy-rsa/2.0/tmp/build-key7
-rwxr-xr-xeasy-rsa/2.0/tmp/build-key-pass7
-rwxr-xr-xeasy-rsa/2.0/tmp/build-key-pkcs128
-rwxr-xr-xeasy-rsa/2.0/tmp/build-key-server10
-rwxr-xr-xeasy-rsa/2.0/tmp/build-req7
-rwxr-xr-xeasy-rsa/2.0/tmp/build-req-pass7
-rwxr-xr-xeasy-rsa/2.0/tmp/clean-all16
-rw-r--r--easy-rsa/2.0/tmp/file1
-rwxr-xr-xeasy-rsa/2.0/tmp/inherit-inter39
-rwxr-xr-xeasy-rsa/2.0/tmp/list-crl13
-rw-r--r--easy-rsa/2.0/tmp/openssl-0.9.6.cnf265
-rw-r--r--easy-rsa/2.0/tmp/openssl-1.0.0.cnf285
-rwxr-xr-xeasy-rsa/2.0/tmp/pkitool379
-rwxr-xr-xeasy-rsa/2.0/tmp/revoke-full40
-rwxr-xr-xeasy-rsa/2.0/tmp/sign-req7
-rw-r--r--easy-rsa/2.0/tmp/vars74
-rwxr-xr-xeasy-rsa/2.0/tmp/whichopensslcnf23
-rwxr-xr-xeasy-rsa/2.0/vars74
-rwxr-xr-xeasy-rsa/2.0/whichopensslcnf26
-rw-r--r--easy-rsa/Windows/README.txt44
-rw-r--r--easy-rsa/Windows/build-ca-pass.bat8
-rw-r--r--easy-rsa/Windows/build-ca.bat4
-rw-r--r--easy-rsa/Windows/build-dh.bat4
-rw-r--r--easy-rsa/Windows/build-key-pass.bat8
-rw-r--r--easy-rsa/Windows/build-key-pkcs12.bat10
-rw-r--r--easy-rsa/Windows/build-key-server-pass.bat8
-rw-r--r--easy-rsa/Windows/build-key-server.bat8
-rw-r--r--easy-rsa/Windows/build-key.bat8
-rw-r--r--easy-rsa/Windows/clean-all.bat13
-rw-r--r--easy-rsa/Windows/index.txt.start0
-rwxr-xr-xeasy-rsa/Windows/init-config.bat1
-rw-r--r--easy-rsa/Windows/revoke-full.bat13
-rw-r--r--easy-rsa/Windows/serial.start1
-rw-r--r--easy-rsa/Windows/vars.bat.sample40
-rw-r--r--errlevel.h175
-rw-r--r--error.c863
-rw-r--r--error.h348
-rw-r--r--event.c1055
-rw-r--r--event.h158
-rw-r--r--fdmisc.c72
-rw-r--r--fdmisc.h31
-rw-r--r--forward-inline.h294
-rw-r--r--forward.c1531
-rw-r--r--forward.h86
-rw-r--r--fragment.c408
-rw-r--r--fragment.h188
-rw-r--r--gremlin.c215
-rw-r--r--gremlin.h72
-rw-r--r--helper.c480
-rw-r--r--helper.h38
-rw-r--r--httpdigest.c143
-rw-r--r--httpdigest.h60
-rw-r--r--ieproxy.c146
-rw-r--r--ieproxy.h24
-rw-r--r--images/Makefile.am41
-rw-r--r--images/Makefile.in418
-rwxr-xr-ximages/icon.icobin0 -> 22486 bytes
-rwxr-xr-ximages/install-whirl.bmpbin0 -> 25820 bytes
-rw-r--r--init.c3554
-rw-r--r--init.h127
-rwxr-xr-xinstall-sh520
-rw-r--r--install-win32/GetWindowsVersion.nsi109
-rw-r--r--install-win32/Makefile.am97
-rw-r--r--install-win32/Makefile.in553
-rw-r--r--install-win32/build-pkcs11-helper.sh24
-rw-r--r--install-win32/buildinstaller14
-rw-r--r--install-win32/ddk-common2
-rw-r--r--install-win32/doclean6
-rw-r--r--install-win32/dosname.pl9
-rw-r--r--install-win32/getgui19
-rw-r--r--install-win32/getopenssl19
-rw-r--r--install-win32/getpkcs11helper17
-rw-r--r--install-win32/getprebuilt10
-rw-r--r--install-win32/getxgui28
-rw-r--r--install-win32/ifdef.pl53
-rw-r--r--install-win32/m4todef.pl15
-rw-r--r--install-win32/macro.pl61
-rw-r--r--install-win32/makeopenvpn37
-rw-r--r--install-win32/maketap17
-rw-r--r--install-win32/maketapinstall15
-rw-r--r--install-win32/maketext59
-rw-r--r--install-win32/openssl/README.txt21
-rw-r--r--install-win32/openssl/openssl097.patch68
-rw-r--r--install-win32/openssl/openssl098.patch56
-rwxr-xr-xinstall-win32/openvpn.nsi886
-rwxr-xr-xinstall-win32/sample.ovpn103
-rwxr-xr-xinstall-win32/setpath.nsi231
-rw-r--r--install-win32/settings.in71
-rw-r--r--install-win32/trans.pl97
-rwxr-xr-xinstall-win32/u2d.c20
-rw-r--r--install-win32/winconfig18
-rw-r--r--integer.h114
-rw-r--r--interval.c77
-rw-r--r--interval.h247
-rw-r--r--list.c643
-rw-r--r--list.h196
-rw-r--r--lladdr.c61
-rw-r--r--lladdr.h8
-rw-r--r--lzo.c275
-rw-r--r--lzo.h136
-rw-r--r--manage.c3160
-rw-r--r--manage.h516
-rw-r--r--management/management-notes.txt838
-rw-r--r--mbuf.c169
-rw-r--r--mbuf.h109
-rw-r--r--memcmp.c43
-rw-r--r--memdbg.h114
-rw-r--r--misc.c2378
-rw-r--r--misc.h422
-rwxr-xr-xmissing376
-rw-r--r--mroute.c429
-rw-r--r--mroute.h212
-rw-r--r--mss.c114
-rw-r--r--mss.h34
-rw-r--r--mtcp.c718
-rw-r--r--mtcp.h68
-rw-r--r--mtu.c290
-rw-r--r--mtu.h304
-rw-r--r--mudp.c277
-rw-r--r--mudp.h42
-rw-r--r--multi.c2692
-rw-r--r--multi.h450
-rw-r--r--ntlm.c353
-rw-r--r--ntlm.h11
-rw-r--r--occ-inline.h85
-rw-r--r--occ.c393
-rw-r--r--occ.h95
-rw-r--r--openvpn-plugin.h461
-rw-r--r--openvpn.85967
-rw-r--r--openvpn.c250
-rw-r--r--openvpn.h550
-rw-r--r--openvpn.spec281
-rw-r--r--openvpn.spec.in281
-rw-r--r--options.c6020
-rw-r--r--options.h759
-rw-r--r--otime.c273
-rw-r--r--otime.h271
-rw-r--r--packet_id.c486
-rw-r--r--packet_id.h335
-rw-r--r--perf.c293
-rw-r--r--perf.h82
-rw-r--r--pf-inline.h59
-rw-r--r--pf.c711
-rw-r--r--pf.h102
-rw-r--r--ping-inline.h59
-rw-r--r--ping.c92
-rw-r--r--ping.h47
-rw-r--r--pkcs11.c990
-rw-r--r--pkcs11.h80
-rw-r--r--plugin.c747
-rw-r--r--plugin.h180
-rw-r--r--plugin/README47
-rw-r--r--plugin/auth-pam/.svnignore1
-rwxr-xr-xplugin/auth-pam/Makefile30
-rw-r--r--plugin/auth-pam/README74
-rw-r--r--plugin/auth-pam/auth-pam.c797
-rw-r--r--plugin/auth-pam/pamdl.c180
-rw-r--r--plugin/auth-pam/pamdl.h7
-rw-r--r--plugin/defer/README16
-rwxr-xr-xplugin/defer/build14
-rw-r--r--plugin/defer/simple.c305
-rwxr-xr-xplugin/defer/simple.def6
-rwxr-xr-xplugin/defer/winbuild18
-rwxr-xr-xplugin/down-root/Makefile17
-rw-r--r--plugin/down-root/README29
-rw-r--r--plugin/down-root/down-root.c553
-rw-r--r--plugin/examples/README16
-rwxr-xr-xplugin/examples/build14
-rw-r--r--plugin/examples/log.c184
-rw-r--r--plugin/examples/simple.c120
-rwxr-xr-xplugin/examples/simple.def6
-rwxr-xr-xplugin/examples/winbuild18
-rw-r--r--pool.c526
-rw-r--r--pool.h88
-rw-r--r--proto.c122
-rw-r--r--proto.h202
-rw-r--r--proxy.c1129
-rw-r--r--proxy.h114
-rw-r--r--ps.c880
-rw-r--r--ps.h57
-rw-r--r--push.c458
-rw-r--r--push.h73
-rw-r--r--pushlist.h42
-rw-r--r--reliable.c751
-rw-r--r--reliable.h163
-rw-r--r--route.c2404
-rw-r--r--route.h220
-rw-r--r--sample-config-files/README6
-rw-r--r--sample-config-files/client.conf123
-rwxr-xr-xsample-config-files/firewall.sh108
-rwxr-xr-xsample-config-files/home.up2
-rw-r--r--sample-config-files/loopback-client25
-rw-r--r--sample-config-files/loopback-server26
-rwxr-xr-xsample-config-files/office.up2
-rwxr-xr-xsample-config-files/openvpn-shutdown.sh5
-rwxr-xr-xsample-config-files/openvpn-startup.sh34
-rw-r--r--sample-config-files/server.conf299
-rw-r--r--sample-config-files/static-home.conf72
-rw-r--r--sample-config-files/static-office.conf69
-rw-r--r--sample-config-files/tls-home.conf83
-rw-r--r--sample-config-files/tls-office.conf83
-rw-r--r--sample-config-files/xinetd-client-config11
-rw-r--r--sample-config-files/xinetd-server-config25
-rw-r--r--sample-keys/README14
-rw-r--r--sample-keys/ca.crt19
-rw-r--r--sample-keys/ca.key15
-rw-r--r--sample-keys/client.crt65
-rw-r--r--sample-keys/client.key15
-rw-r--r--sample-keys/dh1024.pem5
-rw-r--r--sample-keys/pass.crt65
-rw-r--r--sample-keys/pass.key18
-rw-r--r--sample-keys/pkcs12.p12bin0 -> 2685 bytes
-rw-r--r--sample-keys/server.crt67
-rw-r--r--sample-keys/server.key15
-rw-r--r--sample-keys/ta.key21
-rwxr-xr-xsample-scripts/auth-pam.pl97
-rwxr-xr-xsample-scripts/bridge-start39
-rwxr-xr-xsample-scripts/bridge-stop18
-rwxr-xr-xsample-scripts/openvpn.init244
-rwxr-xr-xsample-scripts/ucn.pl11
-rwxr-xr-xsample-scripts/verify-cn64
-rw-r--r--schedule.c653
-rw-r--r--schedule.h132
-rw-r--r--service-win32/Makefile.am41
-rw-r--r--service-win32/Makefile.in509
-rw-r--r--service-win32/msvc.mak30
-rwxr-xr-xservice-win32/openvpnserv.c530
-rw-r--r--service-win32/service.c695
-rw-r--r--service-win32/service.h141
-rw-r--r--session_id.c61
-rw-r--r--session_id.h86
-rw-r--r--shaper.c95
-rw-r--r--shaper.h178
-rw-r--r--sig.c368
-rw-r--r--sig.h102
-rw-r--r--socket.c2808
-rw-r--r--socket.h944
-rw-r--r--socks.c551
-rw-r--r--socks.h78
-rw-r--r--ssl.c5385
-rw-r--r--ssl.h849
-rw-r--r--status.c301
-rw-r--r--status.h95
-rw-r--r--suse/openvpn.init264
-rw-r--r--syshead.h658
-rwxr-xr-xt_client.sh298
-rwxr-xr-xt_client.sh.in298
-rwxr-xr-xt_cltsrv-down.sh2
-rwxr-xr-xt_cltsrv.sh87
-rwxr-xr-xt_lpback.sh31
-rwxr-xr-xtap-win32/MAKEFILE6
-rwxr-xr-xtap-win32/SOURCES.in64
-rwxr-xr-xtap-win32/common.h82
-rwxr-xr-xtap-win32/constants.h52
-rwxr-xr-xtap-win32/dhcp.c599
-rwxr-xr-xtap-win32/dhcp.h164
-rwxr-xr-xtap-win32/endian.h35
-rwxr-xr-xtap-win32/error.c378
-rwxr-xr-xtap-win32/error.h88
-rwxr-xr-xtap-win32/hexdump.c69
-rwxr-xr-xtap-win32/hexdump.h63
-rwxr-xr-xtap-win32/i386/OemWin2k.inf.in195
-rwxr-xr-xtap-win32/instance.c241
-rwxr-xr-xtap-win32/lock.h75
-rwxr-xr-xtap-win32/macinfo.c154
-rwxr-xr-xtap-win32/macinfo.h38
-rwxr-xr-xtap-win32/mem.c186
-rwxr-xr-xtap-win32/proto.h224
-rwxr-xr-xtap-win32/prototypes.h260
-rwxr-xr-xtap-win32/resource.rc58
-rwxr-xr-xtap-win32/tapdrvr.c3145
-rwxr-xr-xtap-win32/types.h178
-rw-r--r--tun.c4557
-rw-r--r--tun.h459
-rw-r--r--version.m46
-rw-r--r--win/__init__.py0
-rw-r--r--win/autodefs.h.in31
-rw-r--r--win/build.py23
-rw-r--r--win/build_all.py69
-rw-r--r--win/build_ddk.py55
-rw-r--r--win/build_exe.py15
-rw-r--r--win/config.h.in323
-rw-r--r--win/config.py21
-rw-r--r--win/config_all.py13
-rw-r--r--win/config_tap.py35
-rw-r--r--win/config_ti.py18
-rw-r--r--win/js.py10
-rw-r--r--win/make_dist.py107
-rw-r--r--win/msvc.mak.in57
-rwxr-xr-xwin/openvpn.nsi822
-rwxr-xr-xwin/openvpn.nsi.orig822
-rwxr-xr-xwin/setpath.nsi231
-rw-r--r--win/settings.in87
-rw-r--r--win/show.py9
-rw-r--r--win/sign.py19
-rw-r--r--win/tap_span.py129
-rw-r--r--win/wb.py322
-rw-r--r--win32.c1115
-rw-r--r--win32.h277
387 files changed, 126590 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0f90aa2
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+James Yonan <jim@yonan.net>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f6e5c43
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,217 @@
+OpenVPN (TM) -- An Open Source VPN daemon
+
+Copyright (C) 2002-2010 OpenVPN Technologies, 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 Technologies, 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 Technologies, 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-2010 OpenVPN Technologies, 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.
+
diff --git a/COPYRIGHT.GPL b/COPYRIGHT.GPL
new file mode 100644
index 0000000..ff8a7f0
--- /dev/null
+++ b/COPYRIGHT.GPL
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f187d4b
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3483 @@
+OpenVPN Change Log
+Copyright (C) 2002-2011 OpenVPN Technologies, Inc. <sales@openvpn.net>
+
+2011.07.01 -- Versoin 2.2.1
+David Sommerseth (4):
+ Don't define ENABLE_PUSH_PEER_INFO if SSL is not available
+ Fix compiling issues with pkcs11 when --disable-management is configured
+ Remove support for Linux 2.2 configuration fallback
+ Revert "Add new openssl.cnf to easy-rsa/Windows"
+ Prepared for releasing OpenVPN 2.2.1
+
+ Gustavo Zacarias (1):
+ Fix compile issues when using --enable-small and --disable-ssl/--disable-crypto
+
+ Matthew L. Creech (1):
+ Fix 2.2.0 build failure when management interface disabled
+
+ Robert Fischer (2):
+ Added info about --show-proxy-settings
+ Documented --x509-username-field option
+
+ Samuli Seppänen (5):
+ Fix a build-ca issue on Windows
+ Add new openssl.cnf to easy-rsa/Windows
+ Updated "easy-rsa" for OpenSSL 1.0.0
+ Made domake-win builds to use easy-rsa/2.0/openssl-1.0.0.cnf
+ Fixes to easy-rsa/2.0
+
+ Simon Matter (1):
+ Fix issues with some older GCC compilers
+
+2011.04.21 -- Version 2.2.0
+David Sommerseth (4):
+ Fix the --client-cert-not-required feature
+ Change the default --tmp-dir path to a more suitable path
+ Improve the mysprintf() issue in openvpnserv.c
+ Add a simple comment regarding openvpn_snprintf() is duplicated
+
+Gert Doering (1):
+ Add more detailed explanation regarding the function of "--rdns-internal"
+
+Gisle Vanem (1):
+ Avoid re-defining uint32_t when using mingw compiler
+
+James Yonan (1):
+ Fixed bug in port-share that could cause port share process to crash with output like this:
+
+Robert Fischer / rf (4):
+ Update man page with info about --capath
+ Update man page with info about --connect-timeout
+ Update man page with info about --remote-random-hostname
+ Added man page entry for --management-client
+
+Samuli Seppänen (6):
+ Add man page entry for --redirect-private
+ Change all CRLF linefeeds to LF linefeeds
+ Fix a bug in devcon source code handling
+ Removed Win2k from supported platforms list in INSTALL and win/openvpn.nsi
+ Fixed copying of tapinstall.exe to dist/bin when using prebuilt TAP-drivers
+ Fixed a bug with GUI icon deletion on upgrade from 2.2-RC or earlier
+
+chantra (1):
+ Clarify --tmp-dir option
+
+2011.03.24 -- Version 2.2-RC2
+Alon Bar-Lev (1):
+ Windows cross-compile cleanup
+
+David Sommerseth (2):
+ Open log files as text files on Windows
+ Clarify default value for the --inactive option.
+
+Gert Doering (1):
+ Implement IPv6 in TUN mode for Windows TAP driver.
+
+Samuli Seppänen (6):
+ Added support for prebuilt TAP-drivers. Automated embedding manifests.
+ Fixes to win/openvpn.nsi
+ Replaced config-win32.h with win/config.h.in
+ Updated INSTALL-win32.txt
+ Fixes to Makefile.am
+ Clarified --client-config-dir section on the man-page.
+
+Ville Skyttä (1):
+ Fix line continuation in chkconfig init script description.
+
+2011.02.28 -- Version 2.2-RC
+David Sommerseth (3):
+ Make the --x509-username-field feature an opt-in feature
+ Fix compiler warning when compiling against OpenSSL 1.0.0
+ Fix packaging of config-win32.h and service-win32/msvc.mak
+
+James Yonan (1):
+ Minor addition of logging info before and after execution of Windows net commands.
+
+Matthias Andree (1):
+ Change variadic macros to C99 style.
+
+Samuli Seppänen (15):
+ Added ENABLE_PASSWORD_SAVE to config-win32.h
+ Added a nmake makefile for openvpnserv.exe building
+ Moved TAP-driver version info to version.m4. Cleaned up win/settings.in.
+ Added helper functionality to win/wb.py
+ Added support for viewing config-win32.h paramters to win/show.py
+ Added comments and made small modifications to win/msvc.mak.in
+ Added command-line switch to win/build_all.py to skip TAP driver building
+ Added configure.h and version.m4 variable parsing to win/config.py
+ Added openvpnserv.exe building to win/build.py
+ Added comments to win/build_ddk.py
+ Several modifications to win/make_dist.py to allow building the NSI installer
+ Copied install-win32/setpath.nsi to win/setpath.nsi
+ Added first version of NSI installer script to win/openvpn.nsi
+ Changes to buildsystem patchset
+ Temporary snprintf-related fix to service-win32/openvpnserv.c
+
+2010.11.25 -- Version 2.2-beta5
+
+Samuli Seppänen (1):
+ Fixed an issue causing a build failure with MS Visual Studio 2008.
+
+2010.11.18 -- Version 2.2-beta4
+
+David Sommerseth (10):
+ Clarified --explicit-exit-notify man page entry
+ Clean-up: Remove pthread and mutex locking code
+ Clean-up: Remove more dead and inactive code paths
+ Clean-up: Removing useless code - hash related functions
+ Use stricter snprintf() formatting in socks_username_password_auth() (v3)
+ Fix compiler warnings about not used dummy() functions
+ Fixed potential misinterpretation of boolean logic
+ Only add some functions when really needed
+ Removed functions not being used anywhere
+ Merged add_bypass_address() and add_host_route_if_nonlocal()
+
+Gert Doering (3):
+ Integrate support for TAP mode on Solaris, written by Kazuyoshi Aizawa <admin2@whiteboard.ne.jp>.
+ Make "topology subnet" work on Solaris
+ Improved man page entry for script_type
+
+James Yonan (5):
+ Fixed initialization bug in route_list_add_default_gateway (Gert Doering).
+ Implement challenge/response authentication support in client mode
+ Make base64.h have the same conditional compilation expression as base64.c.
+ Fixed compiling issues when using --disable-crypto
+ In verify_callback, the subject var should be freed by OPENSSL_free, not free
+
+Jesse Young (1):
+ Remove hardcoded path to resolvconf
+
+Lars Hupel (1):
+ Add HTTP/1.1 Host header
+
+Pierre Bourdon (1):
+ Adding support for SOCKS plain text authentication
+
+Samuli Seppänen (2):
+ Added check for variable CONFIGURE_DEFINES into options.c
+ Added command-line option parser and an unsigned build option to build_all.py
+
+2010.08.21 -- Version 2.2-beta3
+
+* Attempt to fix issue where domake-win build system was not properly
+ signing drivers and .exe files.
+
+ Added win/tap_span.py for building multiple versions of the TAP driver
+ and tapinstall binaries using different DDK versions to span from Win2K
+ to Win7 and beyond.
+
+* Community patches
+ David Sommerseth (2):
+ Test framework improvment - Do not FAIL if t_client.rc is missing
+ More t_client.sh updates - exit with SKIP when we want to skip
+
+ Gert Doering (4):
+ Fix compile problems on NetBSD and OpenBSD
+ Fix <net/if.h> compile time problems on OpenBSD for good
+ full "VPN client connect" test framework for OpenVPN
+ Build t_client.sh by configure at run-time.
+
+ chantra (1):
+ Fixes openssl-1.0.0 compilation warning
+
+2010.08.16 -- Version 2.2-beta2
+
+* Windows security issue:
+ Fixed potential local privilege escalation vulnerability in
+ Windows service. The Windows service did not properly quote the
+ executable filename passed to CreateService. A local attacker
+ with write access to the root directory C:\ could create an
+ executable that would be run with the same privilege level as
+ the OpenVPN Windows service. However, since non-Administrative
+ users normally lack write permission on C:\, this vulnerability
+ is generally not exploitable except on older versions of Windows
+ (such as Win2K) where the default permissions on C:\ would allow
+ any user to create files there.
+ Credit: Scott Laurie, MWR InfoSecurity
+
+* Added Python-based based alternative build system for Windows using
+ Visual Studio 2008 (in win directory).
+
+* When aborting in a non-graceful way, try to execute do_close_tun in
+ init.c prior to daemon exit to ensure that the tun/tap interface is
+ closed and any added routes are deleted.
+
+* Fixed an issue where AUTH_FAILED was not being properly delivered
+ to the client when a bad password is given for mid-session reauth,
+ causing the connection to fail without an error indication.
+
+* Don't advance to the next connection profile on AUTH_FAILED errors.
+
+* Fixed an issue in the Management Interface that could cause
+ a process hang with 100% CPU utilization in --management-client
+ mode if the management interface client disconnected at the
+ point where credentials are queried.
+
+* Fixed an issue where if reneg-sec was set to 0 on the client,
+ so that the server-side value would take precedence,
+ the auth_deferred_expire_window function would incorrectly
+ return a window period of 0 seconds. In this case, the
+ correct window period should be the handshake window
+ period.
+
+* Modified ">PASSWORD:Verification Failed" management interface
+ notification to include a client reason string:
+
+ >PASSWORD:Verification Failed: 'AUTH_TYPE' ['REASON_STRING']
+
+* Enable exponential backoff in reliability layer
+ retransmits.
+
+* Set socket buffers (SO_SNDBUF and SO_RCVBUF) immediately after
+ socket is created rather than waiting until after connect/listen.
+
+* Management interface performance optimizations:
+
+ 1. Added env-filter MI command to perform filtering on env vars
+ passed through as a part of --management-client-auth
+
+ 2. man_write will now try to aggregate output into larger blocks
+ (up to 1024 bytes) for more efficient i/o
+
+* Fixed minor issue in Windows TAP driver DEBUG builds
+ where non-null-terminated unicode strings were being
+ printed incorrectly.
+
+* Fixed issue on Windows with MSVC compiler, where TCP_NODELAY support
+ was not being compiled in.
+
+* Proxy improvements:
+
+ Improved the ability of http-auth "auto" flag to dynamically detect
+ the auth method required by the proxy.
+
+ Added http-auth "auto-nct" flag to reject weak proxy auth methods.
+
+ Added HTTP proxy digest authentication method.
+
+ Removed extraneous openvpn_sleep calls from proxy.c.
+
+* Implemented http-proxy-override and http-proxy-fallback directives to make it
+ easier for OpenVPN client UIs to start a pre-existing client config file with
+ proxy options, or to adaptively fall back to a proxy connection if a direct
+ connection fails.
+
+* Implemented a key/value auth channel from client to server.
+
+* Fixed issue where bad creds provided by the management interface
+ for HTTP Proxy Basic Authentication would go into an infinite
+ retry-fail loop instead of requerying the management interface for
+ new creds.
+
+* Added support for MSVC debugging of openvpn.exe in settings.in:
+
+ # Build debugging version of openvpn.exe
+ !define PRODUCT_OPENVPN_DEBUG
+
+* Implemented multi-address DNS expansion on the network field of route
+ commands.
+
+ When only a single IP address is desired from a multi-address DNS
+ expansion, use the first address rather than a random selection.
+
+* Added --register-dns option for Windows.
+
+ Fixed some issues on Windows with --log, subprocess creation
+ for command execution, and stdout/stderr redirection.
+
+* Fixed an issue where application payload transmissions on the
+ TLS control channel (such as AUTH_FAILED) that occur during
+ or immediately after a TLS renegotiation might be dropped.
+
+* Added warning about tls-remote option in man page.
+
+2009.12.11 -- Version 2.1.1
+
+* Fixed some breakage in openvpn.spec (which is required to build an
+ RPM distribution) where it was referencing a non-existent
+ subdirectory in the tarball, causing it to fail (patch from
+ David Sommerseth).
+
+2009.12.11 -- Version 2.1.0
+
+* Fixed a couple issues in sample plugins auth-pam.c and down-root.c.
+ (1) Fail gracefully rather than segfault if calloc returns NULL.
+ (2) The openvpn_plugin_abort_v1 function can potentially be called
+ with handle == NULL. Add code to detect this case, and if so, avoid
+ dereferencing pointers derived from handle (Thanks to David
+ Sommerseth for finding this bug).
+
+* Documented "multihome" option in the man page.
+
+2009.11.20 -- Version 2.1_rc22
+
+* Fixed a client-side bug on Windows that occurred when the
+ "dhcp-pre-release" or "dhcp-renew" options were combined with
+ "route-gateway dhcp". The release/renew would not occur
+ because the Windows DHCP renew function is blocking and
+ therefore must be called from another process or thread
+ so as not to stall the tunnel.
+
+* Added a hard failure when peer provides a certificate chain
+ with depth > 16. Previously, a warning was issued.
+
+2009.11.12 -- Version 2.1_rc21
+
+* Rebuilt OpenVPN Windows installer with OpenSSL 0.9.8l to address
+ CVE-2009-3555. Note that OpenVPN has never relied on the session
+ renegotiation capabilities that are built into the SSL/TLS protocol,
+ therefore the fix in OpenSSL 0.9.8l (disable SSL/TLS renegotiation
+ completely) will not adversely affect OpenVPN mid-session SSL/TLS
+ renegotation or any other OpenVPN capabilities.
+
+* Added additional session renegotiation hardening. OpenVPN has always
+ required that mid-session renegotiations build up a new SSL/TLS
+ session from scratch. While the client certificate common name is
+ already locked against changes in mid-session TLS renegotiations, we
+ now extend this locking to the auth-user-pass username as well as all
+ certificate content in the full client certificate chain.
+
+2009.10.01 -- Version 2.1_rc20
+
+* Fixed a bug introduced in 2.1_rc17 (svn r4436) where using the
+ redirect-gateway option by itself, without any extra parameters,
+ would cause the option to be ignored.
+
+* Fixed build problem when ./configure --disable-server is used.
+
+* Fixed ifconfig command for "topology subnet" on FreeBSD (Stefan Bethke).
+
+* Added --remote-random-hostname option.
+
+* Added "load-stats" management interface command to get global server
+ load statistics.
+
+* Added new ./configure flags:
+
+ --disable-def-auth Disable deferred authentication
+ --disable-pf Disable internal packet filter
+
+* Added "setcon" directive for interoperability with SELinux (Sebastien
+ Raveau).
+
+* Optimized PUSH_REQUEST handshake sequence to shave several seconds
+ off of a typical client connection initiation.
+
+* The maximum number of "route" directives (specified in the config
+ file or pulled from a server) can now be configured via the new
+ "max-routes" directive.
+
+* Eliminated the limitation on the number of options that can be pushed
+ to clients, including routes. Previously, all pushed options needed
+ to fit within a 1024 byte options string.
+
+* Added --server-poll-timeout option : when polling possible remote
+ servers to connect to in a round-robin fashion, spend no more than
+ n seconds waiting for a response before trying the next server.
+
+* Added the ability for the server to provide a custom reason string
+ when an AUTH_FAILED message is returned to the client. This
+ string can be set by the server-side managment interface and read
+ by the client-side management interface.
+
+* client-kill management interface command, when issued on server, will
+ now send a RESTART message to client.
+ This feature is intended to make UDP clients respond the same as TCP
+ clients in the case where the server issues a RESTART message in
+ order to force the client to reconnect and pull a new options/route
+ list.
+
+2009.07.16 -- Version 2.1_rc19
+
+* In Windows TAP driver, refactor DHCP/ARP packet injection code to
+ use a DPC (deferred procedure call) to defer packet injection until
+ IRQL < DISPATCH_LEVEL, rather than calling NdisMEthIndicateReceive
+ in the context of AdapterTransmit. This is an attempt to reduce kernel
+ stack usage, and prevent EXCEPTION_DOUBLE_FAULT BSODs that have been
+ observed on Vista. Updated TAP driver version number to 9.6.
+
+* In configure.ac, use datadir instead of datarootdir for compatibility
+ with <autoconf-2.60.
+
+2009.06.07 -- Version 2.1_rc18
+
+* Fixed compile error on ./configure --enable-small
+
+* Fixed issue introduced in r4475 (2.1-rc17) where cryptoapi.c change
+ does not build on Windows on non-MINGW32.
+
+2009.05.30 -- Version 2.1_rc17
+
+* Reduce the debug level (--verb) at which received management interface
+ commands are echoed from 7 to 3. Passwords will be filtered.
+
+* Fixed race condition in management interface recv code on
+ Windows, where sending a set of several commands to the
+ management interface in quick succession might cause the
+ latter commands in the set to be ignored.
+
+* Increased management interface input command buffer size
+ from 256 to 1024 bytes.
+
+* Minor tweaks to Windows build system.
+
+* Added "redirect-private" option which allows private subnets
+ to be pushed to the client in such a way that they don't accidently
+ obscure critical local addresses such as the DHCP server address and
+ DNS server addresses.
+
+* Added new 'autolocal' redirect-gateway flag. When enabled, the OpenVPN
+ client will examine the routing table and determine whether (a) the
+ OpenVPN server is reachable via a locally connected interface, or (b)
+ traffic to the server must be forwarded through the default router.
+ Only add a special bypass route for the OpenVPN server if (b) is true.
+ If (a) is true, behave as if the 'local' flag is specified, and do not
+ add a bypass route.
+
+ The new 'autolocal' flag depends on the non-portable test_local_addr()
+ function in route.c, which is currently only implemented for Windows.
+ The 'autolocal' flag will act as a no-op on platforms that have not
+ yet defined a test_local_addr() function.
+
+* Increased TLS_CHANNEL_BUF_SIZE to 2048 from 1024 (this will allow for
+ more option content to be pushed from server to client).
+
+* Raised D_MULTI_DROPPED debug level to 4 from 3 to filter out (at debug
+ levels <=3) a common and usually innocuous warning.
+
+* Fixed issue of symbol conflicts interfering with Windows CryptoAPI
+ functionality (Alon Bar-Lev).
+
+* Fixed bug where the remote_X environmental variables were not being
+ set correctly when the 'local' option is specifed.
+
+2009.05.17 -- Version 2.1_rc16
+
+* Windows installer changes:
+
+ 1. ifdefed out the check Windows version code which is causing
+ problems on Windows 7
+
+ 2. don't define SF_SELECTED if it is already defined
+
+ 3. Use LZMA instead of BZIP2 compression for better compression
+
+ 4. Upgraded OpenSSL to 0.9.8k
+
+* Added the ability to read the configuration file
+ from stdin, when "stdin" is given as the config
+ file name.
+
+* Allow "management-client" directive to be used
+ with unix domain sockets.
+
+* Added errors-to-stderr option. When enabled, fatal errors
+ that result in the termination of the daemon will be written
+ to stderr.
+
+* Added optional "nogw" (no gateway) flag to --server-bridge
+ to inhibit the pushing of the route-gateway parameter to
+ clients.
+
+* Added new management interface command "pid" to show the
+ process ID of the current OpenVPN process (Angelo Laub).
+
+* Fixed issue where SIGUSR1 restarts would fail if private
+ key was specified as an inline file.
+
+* Added daemon_start_time and daemon_pid environmental variables.
+
+* In management interface, added new ">CLIENT:ESTABLISHED" notification.
+
+* Build fixes:
+
+ 1. Fixed some issues with C++ style comments that leaked into the code.
+
+ 2. Updated configure.ac to work on MinGW64.
+
+ 3. Updated common.h types for _WIN64.
+
+ 4. Fixed issue involving an #ifdef in a macro reference that breaks early gcc
+ compilers.
+
+ 5. In cryptoapi.c, renamed CryptAcquireCertificatePrivateKey to
+ OpenVPNCryptAcquireCertificatePrivateKey to work around
+ a symbol conflict in MinGW-5.1.4.
+
+2008.11.19 -- Version 2.1_rc15
+
+* Fixed issue introduced in 2.1_rc14 that may cause a
+ segfault when a --plugin module is used.
+
+* Added server-side --opt-verify option: clients that connect
+ with options that are incompatible with those of the server
+ will be disconnected (without this option, incompatible
+ clients would trigger a warning message in the server log
+ but would not be disconnected).
+
+* Added --tcp-nodelay option: Macro that sets TCP_NODELAY socket
+ flag on the server as well as pushes it to connecting clients.
+
+* Minor options check fix: --no-name-remapping is a
+ server-only option and should therefore generate an
+ error when used on the client.
+
+* Added --prng option to control PRNG (pseudo-random
+ number generator) parameters. In previous OpenVPN
+ versions, the PRNG was hardcoded to use the SHA1
+ hash. Now any OpenSSL hash may be used. This is
+ part of an effort to remove hardcoded references to
+ a specific cipher or cryptographic hash algorithm.
+
+* Cleaned up man page synopsis.
+
+2008.11.16 -- Version 2.1_rc14
+
+* Added AC_GNU_SOURCE to configure.ac to enable struct ucred,
+ with the goal of fixing a build issue on Fedora 9 that was
+ introduced in 2.1_rc13.
+
+* Added additional method parameter to --script-security to preserve
+ backward compatibility with system() call semantics used in OpenVPN
+ 2.1_rc8 and earlier. To preserve backward compatibility use:
+
+ script-security 3 system
+
+* Added additional warning messages about --script-security 2
+ or higher being required to execute user-defined scripts or
+ executables.
+
+* Windows build system changes:
+
+ Modified Windows domake-win build system to write all openvpn.nsi
+ input files to gen, so that gen can be disconnected from
+ the rest of the source tree and makensis openvpn.nsi will
+ still function correctly.
+
+ Added additional SAMPCONF_(CA|CRT|KEY) macros to settings.in
+ (commented out by default).
+
+ Added optional files SAMPCONF_CONF2 (second sample configuration
+ file) and SAMPCONF_DH (Diffie-Helman parameters) to Windows
+ build system, and may be defined in settings.in.
+
+* Extended Management Interface "bytecount" command
+ to work when OpenVPN is running as a server.
+ Documented Management Interface "bytecount" command in
+ management/management-notes.txt.
+
+* Fixed informational message in ssl.c to properly indicate
+ deferred authentication.
+
+* Added server-side --auth-user-pass-optional directive, to allow
+ connections by clients that do not specify a username/password, when a
+ user-defined authentication script/module is in place (via
+ --auth-user-pass-verify, --management-client-auth, or a plugin module).
+
+* Changes to easy-rsa/2.0/pkitool and related openssl.cnf:
+
+ Calling scripts can set the KEY_NAME environmental variable to set
+ the "name" X509 subject field in generated certificates.
+
+ Modified pkitool to allow flexibility in separating the Common Name
+ convention from the cert/key filename convention.
+
+ For example:
+
+ KEY_CN="James's Laptop" KEY_NAME="james" ./pkitool james
+
+ will create a client certificate/key pair of james.crt/james.key
+ having a Common Name of "James's Laptop" and a Name of "james".
+
+* Added --no-name-remapping option to allow Common Name, X509 Subject,
+ and username strings to include any printable character including
+ space, but excluding control characters such as tab, newline, and
+ carriage-return (this is important for compatibility with external
+ authentication systems).
+
+ As a related change, added --status-version 3 format (and "status 3"
+ in the management interface) which uses the version 2 format except
+ that tabs are used as delimiters instead of commas so that there
+ is no ambiguity when parsing a Common Name that contains a comma.
+
+ Also, save X509 Subject fields to environment, using the naming
+ convention:
+
+ X509_{cert_depth}_{name}={value}
+
+ This is to avoid ambiguities when parsing out the X509 subject string
+ since "/" characters could potentially be used in the common name.
+
+* Fixed some ifconfig-pool issues that precluded it from being combined
+ with --server directive.
+
+ Now, for example, we can configure thusly:
+
+ server 10.8.0.0 255.255.255.0 nopool
+ ifconfig-pool 10.8.0.2 10.8.0.99 255.255.255.0
+
+ to have ifconfig-pool manage only a subset
+ of the VPN subnet.
+
+* Added config file option "setenv FORWARD_COMPATIBLE 1" to relax
+ config file syntax checking to allow directives for future OpenVPN
+ versions to be ignored.
+
+2008.10.07 -- Version 2.1_rc13
+
+* Bundled OpenSSL 0.9.8i with Windows installer.
+
+* Management interface can now listen on a unix
+ domain socket, for example:
+
+ management /tmp/openvpn unix
+
+ Also added management-client-user and management-client-group
+ directives to control which processes are allowed to connect
+ to the socket.
+
+* Copyright change to OpenVPN Technologies, Inc.
+
+2008.09.23 -- Version 2.1_rc12
+
+* Patched Makefile.am so that the new t_cltsrv-down.sh script becomes
+ part of the tarball (Matthias Andree).
+
+* Fixed --lladdr bug introduced in 2.1-rc9 where input validation code
+ was incorrectly expecting the lladdr parameter to be an IP address
+ when it is actually a MAC address (HoverHell).
+
+2008.09.14 -- Version 2.1_rc11
+
+* Fixed a bug that can cause SSL/TLS negotiations in UDP mode
+ to fail if UDP packets are dropped.
+
+2008.09.10 -- Version 2.1_rc10
+
+* Added "--server-bridge" (without parameters) to enable
+ DHCP proxy mode: Configure server mode for ethernet
+ bridging using a DHCP-proxy, where clients talk to the
+ OpenVPN server-side DHCP server to receive their IP address
+ allocation and DNS server addresses.
+
+* Added "--route-gateway dhcp", to enable the extraction
+ of the gateway address from a DHCP negotiation with the
+ OpenVPN server-side LAN.
+
+* Fixed minor issue with --redirect-gateway bypass-dhcp or bypass-dns
+ on Windows. If the bypass IP address is 0.0.0.0 or 255.255.255.255,
+ ignore it.
+
+* Warn when ethernet bridging that the IP address of the bridge adapter
+ is probably not the same address that the LAN adapter was set to
+ previously.
+
+* When running as a server, warn if the LAN network address is
+ the all-popular 192.168.[0|1].x, since this condition commonly
+ leads to subnet conflicts down the road.
+
+* Primarily on the client, check for subnet conflicts between
+ the local LAN and the VPN subnet.
+
+* Added a 'netmask' parameter to get_default_gateway, to return
+ the netmask of the adapter containing the default gateway.
+ Only implemented on Windows so far. Other platforms will
+ return 255.255.255.0. Currently the netmask information is
+ only used to warn about subnet conflicts.
+
+* Minor fix to cryptoapi.c to not compile itself unless USE_CRYPTO
+ and USE_SSL flags are enabled (Alon Bar-Lev).
+
+* Updated openvpn/t_cltsrv.sh (used by "make check") to conform to new
+ --script-security rules. Also adds retrying if the addresses are in
+ use (Matthias Andree).
+
+* Fixed build issue with ./configure --disable-socks --disable-http.
+
+* Fixed separate compile errors in options.c and ntlm.c that occur
+ on strict C compilers (such as old versions of gcc) that require
+ that C variable declarations occur at the start of a {} block,
+ not in the middle.
+
+* Workaround bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8, which
+ the new implementation of extract_x509_field_ssl depends on.
+
+* LZO compression buffer overflow errors will now invalidate
+ the packet rather than trigger a fatal assertion.
+
+* Fixed minor compile issue in ntlm.c (mid-block declaration).
+
+* Added --allow-pull-fqdn option which allows client to pull DNS names
+ from server (rather than only IP address) for --ifconfig, --route, and
+ --route-gateway. OpenVPN versions 2.1_rc7 and earlier allowed DNS names
+ for these options to be pulled and translated to IP addresses by default.
+ Now --allow-pull-fqdn will be explicitly required on the client to enable
+ DNS-name-to-IP-address translation of pulled options.
+
+* 2.1_rc8 and earlier did implicit shell expansion on script
+ arguments since all scripts were called by system().
+ The security hardening changes made to 2.1_rc9 no longer
+ use system(), but rather use the safer execve or CreateProcess
+ system calls. The security hardening also introduced a
+ backward incompatibility with 2.1_rc8 and earlier in that
+ script parameters were no longer shell-expanded, so
+ for example:
+
+ client-connect "docc CLIENT-CONNECT"
+
+ would fail to work because execve would try to execute
+ a script called "docc CLIENT-CONNECT" instead of "docc"
+ with "CLIENT-CONNECT" as the first argument.
+
+ This patch fixes the issue, bringing the script argument
+ semantics back to pre 2.1_rc9 behavior in order to preserve
+ backward compatibility while still using execve or CreateProcess
+ to execute the script/executable.
+
+* Modified ip_or_dns_addr_safe, which validates pulled DNS names,
+ to more closely conform to RFC 3696:
+
+ (1) DNS name length must not exceed 255 characters
+
+ (2) DNS name characters must be limited to alphanumeric,
+ dash ('-'), and dot ('.')
+
+* Fixed bug in intra-session TLS key rollover that was introduced with
+ deferred authentication features in 2.1_rc8.
+
+2008.07.31 -- Version 2.1_rc9
+
+* Security Fix -- affects non-Windows OpenVPN clients running
+ OpenVPN 2.1-beta14 through 2.1-rc8 (OpenVPN 2.0.x clients are NOT
+ vulnerable nor are any versions of the OpenVPN server vulnerable).
+ An OpenVPN client connecting to a malicious or compromised
+ server could potentially receive an "lladdr" or "iproute" configuration
+ directive from the server which could cause arbitrary code execution on
+ the client. A successful attack requires that (a) the client has agreed
+ to allow the server to push configuration directives to it by including
+ "pull" or the macro "client" in its configuration file, (b) the client
+ successfully authenticates the server, (c) the server is malicious or has
+ been compromised and is under the control of the attacker, and (d) the
+ client is running a non-Windows OS. Credit: David Wagner.
+ CVE-2008-3459
+
+* Miscellaneous defensive programming changes to multiple
+ areas of the code. In particular, use of the system() call
+ for calling executables such as ifconfig, route, and
+ user-defined scripts has been completely revamped in favor
+ of execve() on unix and CreateProcess() on Windows.
+
+* In Windows build, package a statically linked openssl.exe to work around
+ observed instabilities in the dynamic build since the migration to
+ OpenSSL 0.9.8h.
+
+2008.06.11 -- Version 2.1_rc8
+
+* Added client authentication and packet filtering capability
+ to management interface. In addition, allow OpenVPN plugins
+ to take advantage of deferred authentication and packet
+ filtering capability.
+
+* Added support for client-side connection profiles.
+
+* Fixed unbounded memory growth bug in environmental variable
+ code that could have caused long-running OpenVPN sessions
+ with many TLS renegotiations to incrementally
+ increase memory usage over time.
+
+* Windows release now packages openssl-0.9.8h.
+
+* Build system changes -- allow building on Windows using
+ autoconf/automake scripts (Alon Bar-Lev).
+
+* Changes to Windows build system to make it easier to do
+ partial builds, with a reduced set of prerequisites,
+ where only a subset of OpenVPN installer
+ components are built. See ./domake-win comments.
+
+* Cleanup IP address for persistence interfaces for tap and also
+ using ifconfig, gentoo#209055 (Alon Bar-Lev).
+
+* Fall back to old version of extract_x509_field for OpenSSL 0.9.6.
+
+* Clarified tcp-queue-limit man page entry (Matti Linnanvuori).
+
+* Added new OpenVPN icon and installer graphic.
+
+* Minor pkitool changes.
+
+* Added --pkcs11-id-management option, which will cause OpenVPN to
+ query the management interface via the new NEED-STR asynchronous
+ notification query to get additional PKCS#11 options (Alon Bar-Lev).
+
+* Added NEED-STR management interface asynchronous query and
+ "needstr" management interface command to respond to the query
+ (Alon Bar-Lev).
+
+* Added Dragonfly BSD support (Francis-Gudin).
+
+* Quote device names before passing to up/down script (Josh Cepek).
+
+* Bracketed struct openvpn_pktinfo with #pragma pack(1) to
+ prevent structure padding from causing an incorrect length
+ to be returned by sizeof (struct openvpn_pktinfo) on 64-bit
+ platforms.
+
+* On systems that support res_init, always call it
+ before calling gethostbyname to ensure that
+ resolver configuration state is current.
+
+* Added NTLMv2 proxy support (Miroslav Zajic).
+
+* Fixed an issue in extract_x509_field_ssl where the extraction
+ would fail on the first field of the subject name, such as
+ the common name in: /CN=foo/emailAddress=foo@bar.com
+
+* Made "Linux ip addr del failed" error nonfatal.
+
+* Amplified --client-cert-not-required warning.
+
+* Added #pragma pack to proto.h.
+
+2008.01.29 -- Version 2.1_rc7
+
+* Added a few extra files that exist in the svn repo but were
+ not being copied into the tarball by make dist.
+
+* Fixup null interface on close, don't use ip addr flush (Alon Bar-Lev).
+
+2008.01.24 -- Version 2.1_rc6
+
+* Fixed options checking bug introduced in rc5 where legitimate configuration
+ files might elicit the error: "Options error: Parameter pkcs11_private_mode
+ can only be specified in TLS-mode, i.e. where --tls-server or --tls-client
+ is also specified."
+
+2008.01.23 -- Version 2.1_rc5
+
+* Fixed Win2K TAP driver bug that was introduced by Vista fixes,
+ incremented driver version to 9.4.
+
+* Windows build system changes:
+
+ Incremented included OpenSSL version to openssl-0.9.7m.
+
+ Updated openssl.patch for openssl-0.9.7m and added some
+ brief usage comments to the head of the patch.
+
+ Added build-pkcs11-helper.sh for building the pkcs11-helper
+ library.
+
+ Integrated inclusion of pkcs11-helper into Windows build
+ system.
+
+ Upgraded TAP build scripts to use WDK 6001.17121
+ (Windows 2008 Server pre-RTM).
+
+* Windows installer changes:
+
+ Clean up the start menu folder.
+
+ Allow for a site-specific sample configuration file and keys
+ to be included in a custom installer (see SAMPCONF macros
+ in settings.in).
+
+ New icon (temporary).
+
+* Added "forget-passwords" command to the management interface
+ (Alon Bar-Lev).
+
+* Added --management-signal option to signal SIGUSR1 when the
+ management interface disconnects (Alon Bar-Lev).
+
+* Modified command line and config file parser to allow
+ quoted strings using single quotes ('') (Alon Bar-Lev).
+
+* Use pkcs11-helper as external library, can be downloaded from
+ https://www.opensc-project.org/pkcs11-helper (Alon Bar-Lev).
+
+* Fixed interim memory growth issue in TCP connect loop where
+ "TCP: connect to %s failed, will try again in %d seconds: %s"
+ is output.
+
+* Fixed bug in epoll driver in event.c, where the lack of a
+ handler for EPOLLHUP could cause 99% CPU usage.
+
+* Defined ALLOW_NON_CBC_CIPHERS for people who don't
+ want to use a CBC cipher for OpenVPN's data channel.
+
+* Added PLUGIN_LIBDIR preprocessor string to prepend a default
+ plugin directory to the dlopen search list when the user
+ specifies the basename of the plugin only (Marius Tomaschewski).
+
+* Rewrote extract_x509_field and modified COMMON_NAME_CHAR_CLASS
+ to allow forward slash characters ("/") in the X509 common name
+ (Pavel Shramov).
+
+* Allow OpenVPN to run completely unprivileged under Linux
+ by allowing openvpn --mktun to be used with --user and --group
+ to set the UID/GID of the tun device node. Also added --iproute
+ option to allow an alternative command to be executed in place
+ of the default iproute2 command (Alon Bar-Lev).
+
+* Fixed --disable-iproute2 in ./configure to actually disable
+ iproute2 usage (Alon Bar-Lev).
+
+* Added --management-forget-disconnect option -- forget
+ passwords when management session disconnects (Alon Bar-Lev).
+
+2007.04.25 -- Version 2.1_rc4
+
+* Worked out remaining issues with TAP driver signing
+ on Vista x64. OpenVPN will now run on Vista x64
+ with driver signing enforcement enabled.
+
+* Fixed 64-bit portability bug in time_string function
+ (Thomas Habets).
+
+2007.04.22 -- Version 2.1_rc3
+
+* Additional fixes to TAP driver for Windows x64. Driver
+ now runs successfully on Vista x64 if driver signing
+ enforcement is disabled.
+
+* The Windows Installer and TAP driver are now signed by
+ OpenVPN Solutions LLC (in addition to the usual GnuPG
+ signatures).
+
+* Added OpenVPN GUI (Mathias Sundman version) as install
+ option in Windows installer.
+
+* Clean up configure on FreeBSD for recent autotool versions
+ that require that all .h files have to be compiled.
+ Also, FreeBSD install does not support GNU long options
+ which the Makefile in easy-rsa/2.0 uses (not checked the
+ others as we don't install those on Gentoo) (Roy Marples).
+
+* Added additional scripts to easy-rsa/Windows for working
+ with password-protected keys; also add -extensions server
+ option when generating server cert via
+ build-key-server-pass.bat (Daniel Zauft).
+
+2007.02.27 -- Version 2.1_rc2
+
+* auth-pam change: link with -lpam rather
+ than dlopen (Roy Marples).
+
+* Prevent SIGUSR1 or SIGHUP from causing program
+ exit from initial management hold.
+
+* SO_REUSEADDR should not be set on Windows TCP sockets
+ because it will cause bind to succeed on port conflicts.
+
+* Added time_ascii, time_duration, and time_unix
+ environmental variables for plugins and callback
+ scripts.
+
+* Fixed issue where OpenVPN does not apply the --txqueuelen option
+ to persistent interfaces made with --mktun (Roy Marples).
+
+* Attempt at rational signal handling when in the
+ management hold state. During management hold, ignore
+ SIGUSR1/SIGHUP signals thrown with the "signal" command.
+ Also, "signal" command will now apply remapping as
+ specified with the --remap-usr1 option.
+ When a signal entered using the "signal" command from a management
+ hold is ignored, output: >HOLD:Waiting for hold release
+
+* Fixed issue where struct env_set methods that
+ change the value of an existing name=value pair
+ would delay the freeing of the memory held by
+ the previous name=value pair until the underlying
+ client instance object is closed.
+ This could cause a server that handles long-term
+ client connections, resulting in many periodic calls
+ to verify_callback, to needlessly grow the env_set
+ memory allocation until the underlying client instance
+ object is closed.
+
+* Renamed TAP-Win32 driver from tap0801.sys to tap0901.sys
+ to reflect the fact that Vista has blacklisted the tap0801.sys
+ file name due to previous compatibility issues which have now
+ been resolved. TAP-Win32 major/minor version number is now 9/1.
+
+* Windows installer will delete a previously installed
+ tap0801.sys TAP driver before installing tap0901.sys.
+
+* Added code to Windows installer to fail gracefully on 64 bit
+ installs until 64-bit TAP driver issues can be resolved.
+
+* Added code to Windows installer to fail gracefully on
+ versions of Windows which are not explicitly supported.
+
+* The Windows version will now use a default route-delay
+ of 5 seconds to deal with an apparent routing table race
+ condition on Vista.
+
+* Worked around an incompatibility in the Windows Vista
+ version of CreateIpForwardEntry as described in
+ http://www.nynaeve.net/?p=59
+ This issue would cause route additions using the
+ IP Helper API to fail on Vista.
+
+* On Windows, revert to "ip-win32 dynamic" as the default.
+
+2006.10.31 -- Version 2.1_rc1
+
+* Support recovery (return to hold) from signal at
+ management password prompt.
+
+* Added workaround for OpenSC PKCS#11 bug#108
+ (Alon Bar-Lev).
+
+2006.10.01 -- Version 2.1-beta16
+
+* Windows installer updated with OpenSSL 0.9.7l DLLs to fix
+ published vulnerabilities.
+
+* Fixed TAP-Win32 bug that caused BSOD on Windows Vista
+ (Henry Nestler).
+
+* Autodetect 32/64 bit Windows in installer and install
+ appropriate TAP driver (Mathias Sundman, Hypherion).
+
+* Fixed bug in loopback self-test introduced
+ in 2.1-beta15 where self test as invoked by
+ "make check" would not properly exit after
+ 2 minutes (Paul Howarth).
+
+2006.09.12 -- Version 2.1-beta15
+
+* Windows installer updated with OpenSSL 0.9.7k DLLs to fix
+ RSA Signature Forgery (CVE-2006-4339).
+
+* Fixed bug introduced with the --port-share directive
+ (back in 2.1-beta9 which causes TLS soft resets
+ (1 per hour by default) in TCP server mode to force
+ a blockage of tunnel packets and later time-out and
+ restart the connection.
+
+* easy-rsa update (Alon Bar-Lev)
+ Makefile (install) is now available so that
+ distribs will be able to install it safely.
+
+* PKCS#11 changes: (Alon Bar-Lev)
+ - Modified ssl.c to not FATAL and return to init.c
+ so auth-retry will work.
+ - Modifed pkcs11-helper.c to fix some problem with
+ multiple providers.
+ - Added retry counter to PKCS#11 PIN hook.
+ - Modified PKCS#11 PIN retry loop to return correct error
+ code when PIN is incorrect.
+ - Fix handling (ignoring) zero sized attributes.
+ - Fix gcc-2 issues.
+ - Fix openssl 0.9.6 (first version) issues.
+
+* Minor fixes of lladdr (Alon Bar-Lev)
+ Updated makefile.w32-vc to include lladdr.*, updated
+ linkage libraries.
+ Modified lladdr.c to be compiled under visual C.
+
+* Added two new management states:
+ OPENVPN_STATE_RESOLVE -- DNS lookup
+ OPENVPN_STATE_TCP_CONNECT -- Connecting to TCP server
+
+* Echo management state change to log.
+
+* Minor syshead.h change for NetBSD to allow
+ TCP_NODELAY flag to work.
+
+* Modified --port-share code to remove the assumption that
+ CMSG_SPACE always evaluates to a constant, to enable
+ compilation on NetBSD and possibly other BSDs as well.
+
+* Eliminated gcc 3.3.3 warnings on NetBSD
+ when ./configure --enable-strict is used.
+
+* Added optional minimum-number-of-bytes parameter
+ to --inactive directive.
+
+2006.04.13 -- Version 2.1-beta14
+
+* Fixed Windows server bug in time backtrack handling code which
+ could cause TLS negotiation failures on legitimate clients.
+
+* Rewrote gettimeofday function for Windows to be
+ simpler and more efficient.
+
+* Merged PKCS#11 extensions to easy-rsa/2.0 (Alon Bar-Lev).
+
+* Added --route-metric option to set a default route metric
+ for --route (Roy Marples).
+
+* Added --lladdr option to specify the link layer (MAC) address
+ for the tap interface on non-Windows platforms (Roy Marples).
+
+2006.04.12 -- Version 2.1-beta13
+
+* Code added in 2.1-beta7 and 2.0.6-rc1 to extend byte counters
+ to 64 bits caused a bug in the Windows version which has now
+ been fixed. The bug could cause intermittent crashes.
+
+2006.04.05 -- Version 2.1-beta12
+
+* Security Vulnerability -- An OpenVPN client connecting to a
+ malicious or compromised server could potentially receive
+ "setenv" configuration directives from the server which could
+ cause arbitrary code execution on the client via a LD_PRELOAD
+ attack. A successful attack appears to require that (a) the
+ client has agreed to allow the server to push configuration
+ directives to it by including "pull" or the macro "client" in
+ its configuration file, (b) the client configuration file uses
+ a scripting directive such as "up" or "down", (c) the client
+ succesfully authenticates the server, (d) the server is
+ malicious or has been compromised and is under the control of
+ the attacker, and (e) the attacker has at least some level of
+ pre-existing control over files on the client (this might be
+ accomplished by having the server respond to a client web request
+ with a specially crafted file). Credit: Hendrik Weimer.
+ CVE-2006-1629.
+
+ The fix is to disallow "setenv" to be pushed to clients from
+ the server, and to add a new directive "setenv-safe" which is
+ pushable from the server, but which appends "OPENVPN_" to the
+ name of each remotely set environmental variable.
+
+* "topology subnet" fix for FreeBSD (Benoit Bourdin).
+
+* PKCS11 fixes (Alon Bar-Lev). For full description:
+ svn log -r990 http://svn.openvpn.net/projects/openvpn/branches/BETA21
+
+* When deleting routes under Linux, use the route metric
+ as a differentiator to ensure that the route teardown
+ process only deletes the identical route which was originally
+ added via the "route" directive (Roy Marples).
+
+* Fix the t_cltsrv.sh file in FreeBSD 4 jails
+ (Matthias Andree, Dirk Meyer, Vasil Dimov).
+
+* Extended tun device configure code to support ethernet
+ bridging on NetBSD (Emmanuel Kasper).
+
+2006.02.19 -- Version 2.1-beta11
+
+* Fixed --port-share bug that caused premature closing
+ of proxied sessions.
+
+2006.02.17 -- Version 2.1-beta10
+
+* Fixed --port-share breakage introduced in 2.1-beta9.
+
+2006.02.16 -- Version 2.1-beta9
+
+* Added --port-share option for allowing OpenVPN and HTTPS
+ server to share the same port number.
+* Added --management-client option to connect as a client
+ to management GUI app rather than be connected to as a
+ server.
+* Added "bytecount" command to management interface.
+* --remote-cert-tls fixes (Alon Bar-Lev).
+
+2006.01.03 -- Version 2.1-beta8
+
+* --remap-usr1 will now also remap signals thrown during
+ initialization.
+* Added --connect-timeout option to control the timeout
+ on TCP client connection attempts (doesn't work on all
+ OSes). This patch also makes OpenVPN signalable during
+ TCP connection attempts.
+* Fixed bug in acinclude.m4 where capability of compiler
+ to handle zero-length arrays in structs is tested
+ (David Stipp).
+* Fixed typo in manage.c where inline function declaration
+ was declared without the "static" keyword (David Stipp).
+* Patch to support --topology subnet on Mac OS X (Mathias Sundman).
+* Added --auto-proxy directive to auto-detect HTTP or SOCKS
+ proxy settings (currently Windows only).
+* Removed redundant base64 code.
+* Better sanity checking of --server and --server-bridge
+ IP pool ranges, so as not to hit the assertion at
+ pool.c:119 (2.0.5).
+* Fixed bug where --daemon and --management-query-passwords
+ used together would cause OpenVPN to block prior to
+ daemonization.
+* Fixed client/server race condition which could occur
+ when --auth-retry interact is set and the initially
+ provided auth-user-pass credentials are incorrect,
+ forcing a username/password re-query.
+* Fixed bug where if --daemon and --management-hold are
+ used together, --user or --group options would be ignored.
+* --ip-win32 adaptive is now the default.
+* --ip-win32 netsh (or --ip-win32 adaptive when in netsh
+ mode) can now set DNS/WINS addresses on the TAP-Win32
+ adapter.
+* Added new option --route-method adaptive (Win32)
+ which tries IP helper API first, then falls back to
+ route.exe.
+* Made --route-method adaptive the default.
+
+2005.11.12 -- Version 2.1-beta7
+
+* Allow blank passwords to be passed via the management
+ interface.
+* Fixed bug where "make check" inside a FreeBSD "jail"
+ would never complete (Matthias Andree).
+* Fixed bug where --server directive in --dev tap mode
+ claimed that it would support subnets of /30 or less
+ but actually would only accept /29 or less.
+* Extend byte counters to 64 bits (M. van Cuijk).
+* Fixed bug in Linux get_default_gateway function
+ introduced in 2.0.4, which would cause redirect-gateway
+ on Linux clients to fail.
+* Moved easy-rsa 2.0 scripts to easy-rsa/2.0 to
+ be compatible with 2.0.x distribution.
+* Documented --route-nopull.
+* Documented --ip-win32 adaptive.
+* Windows build now linked with LZO2.
+* Allow ca, cert, key, and dh files to be specified
+ inline via XML-like syntax without needing to
+ reference an explicit file.
+ For example:
+ <ca>
+ data here...
+ </ca>
+* Allow plugin and push directives to have multi-line
+ parameter lists such as:
+ <plugin>
+ my-plugin.so
+ parm1
+ parm2
+ </plugin>
+* Added connect-retry-max option (Alon Bar-Lev).
+* Fixed problems where signals thrown during initialization
+ were not returning to a management-hold state.
+* Added a backtrack-hardened system time algorithm.
+* Added --remote-cert-ku, --remote-cert-eku, and
+ --remote-cert-tls options for verifying certificate
+ attributes (Alon Bar-Lev).
+* For Windows, reverted --ip-win32 default back to "dynamic".
+ To use new adaptive mode, set explicitly.
+
+2005.11.01 -- Version 2.1-beta6
+
+* Security fix (merged from 2.0.4) -- Affects non-Windows
+ OpenVPN clients of version 2.0 or higher which connect to
+ a malicious or compromised server. A format string
+ vulnerability in the foreign_option function in options.c
+ could potentially allow a malicious or compromised server
+ to execute arbitrary code on the client. Only
+ non-Windows clients are affected. The vulnerability
+ only exists if (a) the client's TLS negotiation with
+ the server succeeds, (b) the server is malicious or
+ has been compromised such that it is configured to
+ push a maliciously crafted options string to the client,
+ and (c) the client indicates its willingness to accept
+ pushed options from the server by having "pull" or
+ "client" in its configuration file (Credit: Vade79).
+ CVE-2005-3393
+* Security fix -- (merged from 2.0.4) Potential DoS
+ vulnerability on the server in TCP mode. If the TCP
+ server accept() call returns an error status, the resulting
+ exception handler may attempt to indirect through a NULL
+ pointer, causing a segfault. Affects all OpenVPN 2.0 versions.
+ CVE-2005-3409
+* Fix attempt of assertion at multi.c:1586 (note that
+ this precise line number will vary across different
+ versions of OpenVPN).
+* Windows reliability changes:
+ (a) Added code to make sure that the local PATH environmental
+ variable points to the Windows system32 directory.
+ (b) Added new --ip-win32 adaptive mode which tries 'dynamic'
+ and then fails over to 'netsh' if the DHCP negotiation fails.
+ (c) Made --ip-win32 adaptive the default.
+* More PKCS#11 additions/changes (Alon Bar-Lev).
+* Added ".PHONY: plugin" to Makefile.am to work around
+ "make dist" issue.
+* Fixed double fork issue that occurs when --management-hold
+ is used.
+* Moved TUN/TAP read/write log messages from --verb 8 to 6.
+* Warn when multiple clients having the same common name or
+ username usurp each other when --duplicate-cn is not used.
+* Modified Windows and Linux versions of get_default_gateway
+ to return the route with the smallest metric
+ if multiple 0.0.0.0/0.0.0.0 entries are present.
+* Added ">NEED-OK" alert and "needok" command to management
+ interface to provide a general interface for sending
+ alerts to the end-user. Used by the PKCS#11 code
+ to send Token Insertion Requests to the user.
+* Added actual remote address used to the ">STATE" alert
+ in the management interface (Rolf Fokkens).
+
+2005.10.17 -- Version 2.1-beta4
+
+* Fixed bug introduced in 2.1-beta3 where management
+ socket bind would fail.
+* --capath fix in ssl.c (Zhuang Yuyao).
+* Added ".PHONY: plugin" to Makefile.am, reverted
+ location of "plugin" directory (thanks to
+ Matthias Andree for figuring this out).
+
+2005.10.16 -- Version 2.1-beta3
+
+* Added PKCS#11 support (Alon Bar-Lev).
+* Enable the use of --ca together with --pkcs12. If --ca is
+ used at the same time as --pkcs12, the CA certificate is loaded
+ from the file specified by --ca regardless if the pkcs12 file
+ contains a CA cert or not (Mathias Sundman).
+* Merged --capath patch (Thomas Noel).
+* Merged --multihome patch.
+* Added --bind option for TCP client connections (Ewan Bhamrah
+ Harley).
+* Moved "plugin" directory to "plugins" to deal with strange
+ automake problem that ended up being also fixable with
+ ".PHONY: plugin" in Makefile.am.
+
+2005.10.13 -- Version 2.1-beta2
+
+* Made --sndbuf and --rcvbuf pushable.
+
+2005.10.01 -- Version 2.1-beta1
+
+* Made LZO setting pushable.
+* Renamed sample-keys/tmp-ca.crt to ca.crt.
+* Fixed bug where remove_iroutes_from_push_route_list
+ was missing routes if those routes had
+ an implied netmask (by omission) of 255.255.255.255.
+* Merged with 2.0.3-rc1
+* easy-rsa/2.0 moved to easy-rsa
+* old easy-rsa moved to easy-rsa/1.0
+
+2005.09.23 -- Version 2.0.2-TO4
+
+* Added feature to TAP-Win32 adapter to allow it to be
+ opened from non-administrator mode. This feature
+ is enabled by default, and can be enabled/disabled
+ in the adapter advanced properties dialog.
+* Added --allow-nonadmin standalone option for Windows to
+ set TAP adapter to allow non-admin access. This
+ is a user-mode version of the code, and duplicates
+ the same feature as the above entry.
+* Added fix that attempts to solve corner case of tunnel not
+ forwarding packets when system clock is reset to an earlier time.
+* Added --redirect-gateway bypass-dns option. (Developers:
+ To add bypass-dhcp or bypass-dns support to other OSes,
+ add a get_bypass_addresses function to route.c for
+ your OS.)
+* Added OPENVPN_PLUGIN_CLIENT_CONNECT_V2 plugin callback, which
+ allows a client-connect plugin to return configuration text
+ in memory, rather than via a file.
+* Fixed a bug where --mode server --proto tcp-server --cipher none
+ operation could cause tunnel packet truncation.
+* openvpn --version will show [LZO1] or [LZO2], depending on
+ version that was linked.
+
+2005.09.07 -- Version 2.0.2-TO1
+
+* Added --topology directive. See man page.
+* Added --redirect-gateway bypass-dhcp option to add a route
+ allowing DHCP packets to bypass the tunnel, when the
+ DHCP server is non-local. Currently only implemented
+ on Windows clients.
+* Modified OpenVPN Service on Windows to declare the DHCP
+ client service as a dependency.
+* Extended the plugin interface to allow plugins to declare
+ per-client constructor and destructor functions, to make
+ it simpler for plugins to maintain per-client state.
+
+2005.09.25 -- Version 2.0.3-rc1
+
+* openvpn_plugin_abort_v1 function wasn't being properly
+ registered on Windows.
+* Fixed a bug where --mode server --proto tcp-server --cipher none
+ operation could cause tunnel packet truncation.
+
+2005.08.25 -- Version 2.0.2
+
+* No change from 2.0.2-rc1.
+
+2005.08.24 -- Version 2.0.2-rc1
+
+* Fixed regression bug in Win32 installer, introduced in 2.0.1,
+ which incorrectly set OpenVPN service to autostart.
+* Don't package source code zip file in Windows installer
+ in order to reduce the size of the installer. The source
+ zip file can always be downloaded separately if needed.
+* Fixed bug in route.c in FreeBSD, Darwin, OpenBSD and NetBSD
+ version of get_default_gateway. Allocated socket for route
+ manipulation is never freed so number of mbufs continuously
+ grow and exhaust system resources after a while (Jaroslav Klaus).
+* Fixed bug where "--proto tcp-server --mode p2p --management
+ host port" would cause the management port to not respond until
+ the OpenVPN peer connects.
+* Modified pkitool script to be /bin/sh compatible (Johnny Lam).
+
+2005.08.16 -- Version 2.0.1
+
+* Security Fix -- DoS attack against server when run with "verb 0" and
+ without "tls-auth". If a client connection to the server fails
+ certificate verification, the OpenSSL error queue is not properly
+ flushed, which can result in another unrelated client instance on the
+ server seeing the error and responding to it, resulting in disconnection
+ of the unrelated client (CAN-2005-2531).
+* Security Fix -- DoS attack against server by authenticated client.
+ This bug presents a potential DoS attack vector against the server
+ which can only be initiated by a connected and authenticated client.
+ If the client sends a packet which fails to decrypt on the server,
+ the OpenSSL error queue is not properly flushed, which can result in
+ another unrelated client instance on the server seeing the error and
+ responding to it, resulting in disconnection of the unrelated client
+ (CAN-2005-2532). Credit: Mike Ireton.
+* Security Fix -- DoS attack against server by authenticated client.
+ A malicious client in "dev tap" ethernet bridging mode could
+ theoretically flood the server with packets appearing to come from
+ hundreds of thousands of different MAC addresses, causing the OpenVPN
+ process to deplete system virtual memory as it expands its internal
+ routing table. A --max-routes-per-client directive has been added
+ (default=256) to limit the maximum number of routes in OpenVPN's
+ internal routing table which can be associated with a given client
+ (CAN-2005-2533).
+* Security Fix -- DoS attack against server by authenticated client.
+ If two or more client machines try to connect to the server at the
+ same time via TCP, using the same client certificate, and when
+ --duplicate-cn is not enabled on the server, a race condition can
+ crash the server with "Assertion failed at mtcp.c:411"
+ (CAN-2005-2534).
+* Fixed server bug where under certain circumstances, the client instance
+ object deletion function would try to delete iroutes which had never been
+ added in the first place, triggering "Assertion failed at mroute.c:349".
+* Added --auth-retry option to prevent auth errors from being fatal
+ on the client side, and to permit username/password requeries in case
+ of error. Also controllable via new "auth-retry" management interface
+ command. See man page for more info.
+* Added easy-rsa 2.0 scripts to the tarball in easy-rsa/2.0
+* Fixed bug in openvpn.spec where rpmbuild --define 'without_pam 1'
+ would fail to build.
+* Implement "make check" to perform loopback tests (Matthias Andree).
+
+2005.07.21 -- Version 2.0.1-rc7
+
+* Support LZO 2.01 which renamed its library to lzo2 (Matthias Andree).
+* Include linux/types.h before checking for linux/errqueue.h (Matthias
+ Andree).
+
+2005.07.15 -- Version 2.0.1-rc6
+
+* Commented out "user nobody" and "group nobody" in sample
+ client/server config files.
+* Allow '@' character to be used in --client-config-dir
+ file names.
+
+2005.07.04 -- Version 2.0.1-rc5
+
+* Windows version will log a for-further-info URL when
+ initialization sequence is completed with errors.
+* Added DLOPEN_PAM parameter to plugin/auth-pam/Makefile
+ to control whether auth-pam plugin links to PAM via
+ dlopen or -lpam. By default, DLOPEN_PAM=1 so pre-existing
+ behavior should be preserved. DLOPEN_PAM=0 is the preferred
+ setting to link via -lpam, but DLOPEN_PAM=1 works around
+ a bug in SuSE 9.1 (and possibly other distros as well)
+ where the PAM modules are not linked with -lpam. See
+ thread on openvpn-devel for more discussion about this
+ patch (Simon Perreault).
+
+2005.06.15 -- Version 2.0.1-rc4
+
+* Support LZO 2.00, including changes to configure script to
+ autodetect LZO version.
+
+2005.06.12 -- Version 2.0.1-rc3
+
+* Fixed a bug which caused standard file handles to not be closed
+ after daemonization when --plugin and --daemon are used together,
+ and if the plugin initialization function forks (as does auth-pam
+ and down-root) (Simon Perreault).
+* Added client-side up/down scripts in contrib/pull-resolv-conf
+ for accepting server-pushed "dhcp-option DOMAIN" and "dhcp-option DNS"
+ on Linux/Unix systems (Jesse Adelman).
+* Fixed bug where if client-connect scripts/plugins were cascaded,
+ and one (but not all) of them returned an error status, there might
+ be cases where for an individual script/plugin, client-connect was
+ called but not client-disconnect. The goal of this fix is to
+ ensure that if client-connect is called on a given client instance,
+ then client-disconnect will definitely be called. A potential
+ complication of this fix is that when client-connect functions are
+ cascaded, it's possible that the client-disconnect function would
+ be called in cases where the related client-connect function returned
+ an error status. This fix should not alter OpenVPN behavior when
+ scripts/plugins are not cascaded.
+* Changed the hard-to-reproduce "Assertion failed at fragment.c:312"
+ fatal error to a warning: "FRAG: outgoing buffer is not empty".
+ Need more info on how to reproduce this one.
+* When --duplicate-cn is used, the --ifconfig-pool allocation
+ algorithm will now allocate the first available IP address.
+* When --daemon and --management-hold are used together,
+ OpenVPN will daemonize before it enters the management hold state.
+
+2005.05.16 -- Version 2.0.1-rc2
+
+* Modified vendor test in openvpn.spec file to match against
+ "Mandrakesoft" in addition to "MandrakeSoft".
+* Using --iroute in a --client-config-dir file while in --dev tap
+ mode is not currently supported and will produce a warning
+ message. Fixed bug where in certain cases, in addition to
+ generating a warning message, this combination of options
+ would also produce a fatal assertion in mroute.c.
+* Pass --auth-user-pass username to server-side plugin without
+ performing any string remapping (plugins, unlike scripts,
+ don't get any security benefit from string remapping).
+ This is intended to fix an issue with openvpn-auth-pam/pam_winbind
+ where backslash characters in a username ('\') were being remapped
+ to underscore ('_').
+* Updated OpenSSL DLLs in Windows build to 0.9.7g.
+* Documented --explicit-exit-notify in man page.
+* --explicit-exit-notify seconds parameter defaults to 1 if
+ unspecified.
+
+2005.04.30 -- Version 2.0.1-rc1
+
+* Fixed bug where certain kinds of fatal errors after
+ initialization (such as port in use) would leave plugin
+ processes (such as openvpn-auth-pam) still running.
+* Added optional openvpn_plugin_abort_v1 plugin function for
+ closing initialized plugin objects in the event of a fatal
+ error by main OpenVPN process.
+* When the --remote list is > 1, and --resolv-retry is not
+ specified (meaning that it defaults to "infinite"), apply the
+ infinite timeout to the --remote list as a whole, but try each
+ list item only once before moving on to the next item.
+* Added new --syslog directive which redirects output
+ to syslog without requiring the use of the --daemon or --inetd
+ directives.
+* Added openvpn.spec option to allow RPM to be built with support
+ for passwords read from a file:
+ rpmbuild -tb [openvpn.x.tar.gz] --define 'with_password_save 1'
+
+2005.04.17 -- Version 2.0
+
+* Fixed minor options string typo in options.c.
+
+2005.04.10 -- Version 2.0-rc21
+
+* Change license description from "GPL Version 2 or (at your
+ option) any later version" to just "GPL Version 2".
+
+2005.04.04 -- Version 2.0-rc20
+
+* Dag Wieers has put together an OpenVPN/LZO binary RPM set with
+ excellent distro/version coverage for RH/EL/Fedora, though
+ using his own SPEC. I modified openvpn.spec to follow some of
+ the same conventions such as putting sample scripts and doc
+ files in %doc rather than /usr/share/openvpn.
+* Minor change to init scripts to run the user-defined script
+ /etc/openvpn/openvpn-startup (if it exists) before any OpenVPN
+ configs are started, and to run /etc/openvpn/openvpn-shutdown
+ after all OpenVPN configs have been stopped. The
+ openvpn-startup script can be used for stuff like
+ insmod tun.o, setting up firewall rules, or starting
+ ethernet bridges.
+
+2005.03.29 -- Version 2.0-rc19
+
+* Omit additions of routes where the network and
+ gateway are equal and the netmask is 255.255.255.255.
+ This can come up if you are using both
+ server/ifconfig-pool and client-config-dir with
+ ifconfig-push static addresses for some subset of clients
+ which directly reference the server IP address as the
+ remote endpoint.
+
+2005.03.28 -- Version 2.0-rc18
+
+* Packaged Windows installer with OpenSSL 0.9.7f.
+* Built Windows installer with NSIS 2.06.
+
+2005.03.12 -- Version 2.0-rc17
+
+* "MANAGEMENT: CMD" log file output will now only occur
+ at --verb 7 or greater.
+* Added an optional name/value configuration list to
+ the openvpn-auth-pam plugin module argument list. See
+ plugin/auth-pam/README for documentation. This is necessary
+ in order for openvpn-auth-pam to work with queries generated
+ by arbitrary PAM modules.
+* In both auth-pam and down-root plugins, in the forked process,
+ a read error on the parent process socket is no longer fatal.
+* MandrakeSoft liblzo1 RPM only Provides for a 'liblzo1'.
+ A conditional test of the vendor has been added to
+ Require the appropriately named 'lzo' (liblzo1 / lzo).
+ (Tom Walsh - http://openhardware.net)
+
+
+2005.02.20 -- Version 2.0-rc16
+
+* Fixed bug introduced in rc13 where Windows service wrapper
+ would be installed with a startup type of Automatic.
+ This fix restores the previous behavior of installing
+ with a startup type of Manual.
+
+2005.02.19 -- Version 2.0-rc15
+
+* Added warning when --keepalive is not used in a server
+ configuration.
+* Don't include OpenSSL md4.h file if we are not building
+ NTLM proxy support (Waldemar Brodkorb).
+* Added easy-rsa/build-key-pkcs12 and
+ easy-rsa/Windows/build-key-pkcs12.bat scripts
+ (Mathias Sundman).
+
+2005.02.16 -- Version 2.0-rc14
+
+* Fixed small memory leak that occurs when --crl-verify
+ is used.
+* Upgraded Windows installer and .nsi script to NSIS 2.05
+ (Mathias Sundman).
+* Changed #include backslash usage in cryptoapi.c to use
+ forward slashes instead (Gisle Vanem).
+* Created easy-rsa/revoke-full to handle revocations in
+ a single step: (a) revoke crt, (b) regenerate CRL, and
+ (c) verify that revocation succeeded.
+* Renamed easy-rsa/Windows/revoke-key to revoke-full so
+ that both *nix and Windows scripts are equivalent.
+
+2005.02.11 -- Version 2.0-rc13
+
+* Improve human-readability of local/remote options
+ diff, when inconsistencies are present.
+* For Windows easy-rsa, distribute vars.bat.sample and
+ openssl.cnf.sample, then copy them to their normal
+ filenames (without the .sample) when init-config.bat
+ is run. This is to prevent OpenVPN upgrades from
+ wiping out vars.bat and openssl.cnf edits.
+* Modified service wrapper (Windows) to use a
+ case-insensitive search when scanning for .ovpn files
+ in \Program Files\OpenVPN\config. Prior versions
+ required an all-lower-case .ovpn file extension.
+* Miscellaneous service wrapper code cleanup.
+* If --user/--group is used on Windows, treat it
+ as a no-op with a warning (this makes it easier to
+ distribute the same client config file to Windows
+ and *nix users).
+* Warn if --ifconfig-pool-persist is used with
+ --duplicate-cn.
+
+2005.02.05 -- Version 2.0-rc12
+
+* Removed some debugging code inadvertently included
+ in rc11 which would print the --auth-user-pass
+ username/password provided by clients in the server
+ logfile.
+* Client code for cycling through --remote list will
+ retry the last address which successfully authenticated
+ before moving on through the list.
+* Windows installer will now install sample configuration
+ files in \Program Files\OpenVPN\sample-configs as well
+ as generate a start menu shortcut to this directory.
+* Minor type change in buffer.[ch] to work around char-type
+ ambiguity bug. Caused management interface lock-ups on
+ ARM when building with armv4b-hardhat-linux-gcc 2.95.3.
+
+2005.02.03 -- Version 2.0-rc11
+
+* Windows installer will now install easy-rsa directory
+ in \Program Files\OpenVPN
+* Allow syslog facility to be controlled at compile time,
+ e.g. -DLOG_OPENVPN=LOG_LOCAL6 (P Kern).
+* Changed certain shell scripts in distribution to use
+ #!/bin/sh rather than #!/bin/bash for better portability.
+* If --ifconfig-pool-persist seconds parameter is 0, treat
+ persist file as an allocation of fixed IP addresses
+ (previous versions took IP-to-common-name associations
+ from this list as hints, not mandatory static allocations).
+* Fixed bug on *nix where if --auth-user-pass and --log
+ were used together, the username prompt would be sent to
+ the log file rather than /dev/tty.
+* Spurious text in openvpn.8 detected by doclifter
+ (Eric S. Raymond).
+* Call closelog later on daemon kill so that process
+ exit message is written to syslog.
+
+2005.01.27 -- Version 2.0-rc10
+
+* When ./configure is run with plugins enabled (the default),
+ check whether or not dlopen exists in libc before testing
+ for libdl. This is to fix an issue on FreeBSD and possibly
+ other OSes which bundle libdl functions in libc.
+* On Windows, filter initial WSAEINVAL warning which occurs
+ on the initial read attempt of an unbound socket.
+* The easy-rsa scripts build-key, build-key-pass, and
+ build-key-server will now chmod the .key file
+ to 0600. This is in addition to the fact the generated
+ keys directory has always been similarly protected
+ (Pete Harlan).
+
+2005.01.23 -- Version 2.0-rc9
+
+* Fixed error "ROUTE: route addition failed using
+ CreateIpForwardEntry ..." on Windows when --redirect-gateway
+ is used over a RRAS internet link.
+* When using --route-method exe on Windows, include the
+ gateway parameter on route delete commands (Mathias Sundman).
+* Try not to do a hard reset (i.e. SIGHUP) when two
+ SIGUSR1 signals are received in close succession.
+* If the push list tries to grow beyond its buffer capacity,
+ the resulting error will be non-fatal.
+* To increase the push list capacity (must be done on both
+ client and server), increase TLS_CHANNEL_BUF_SIZE in
+ common.h (default=1024).
+
+2005.01.15 -- Version 2.0-rc8
+
+* Fixed bug introduced in rc7 where options error
+ "--auth-user-pass requires --pull" might occur even
+ if --pull was correctly specified.
+* Changed management interface code to bind once
+ to TCP socket, rather than rebinding after every
+ client disconnect.
+* Added "disable" directive for client-config-dir
+ files.
+* Windows binary install is now distributed with
+ OpenSSL 0.9.7e.
+* Query the management interface for --http-proxy
+ username/password if authfile is set to "stdin".
+* Added current OpenVPN version number to "Unrecognized
+ option or missing parameter" error message.
+* Added "-extensions server" to "openssl req" command
+ in easy-rsa/build-key-server (Nir Yeffet).
+
+2005.01.10 -- Version 2.0-rc7
+
+* Fixed bug in management interface which could cause
+ 100% CPU utilization in --proto tcp-server mode
+ on all *nix OSes except for Linux 2.6.
+* --ifconfig-push now accepts DNS names as well as
+ IP addresses.
+* Added sanity check errors when --pull or
+ --auth-user-pass is used in an incorrect mode.
+* Updated man page entries for --client-connect and
+ --ifconfig-push.
+* Added "String Types and Remapping" section to man
+ page to consisely document the way which OpenVPN
+ may convert certain types of characters in strings
+ to ('_').
+* Modified bridging description in HOWTO to emphasize
+ the fact that bridging allows Windows file and print
+ sharing without a WINS server (Charles Duffy).
+
+2004.12.20 -- Version 2.0-rc6
+
+* Improved checking for epoll support in ./configure
+ to fix false positive on RH9 (Jan Just Keijser).
+* Made the "MULTI TCP: I/O wait required blocking in
+ multi_tcp_action, action=7" error nonfatal and replaced
+ with "MULTI: Outgoing TUN queue full, dropped packet".
+ So far the issue only seems to occur on Linux 2.2
+ in --mode server --proto tcp mode. It occurs when
+ the TUN/TAP driver locks up and refuses to accept
+ new packet writes for a second or more.
+* Fixed bug where if a --client-config-dir file tried
+ to include another file using "config", and if that
+ include failed, OpenVPN would abort with a fatal
+ error. Now such inclusion failures will be logged
+ but are no longer fatal.
+* Global changes to the way that packet buffer alignment
+ is handled. Previously we didn't care about alignment
+ and took care, when handling 16 and 32 bit words
+ in buffers, to always use alignment-safe transfers.
+ This approach appears to be inadequate on some
+ architectures such as alpha. The new approach is
+ to initialize packet buffers in a way that anticipates
+ how component structures will be allocated within
+ them, to maintain correct alignment.
+* Added --dhcp-option DISABLE-NBT to disable NetBIOS
+ over TCP (Jan Just Keijser).
+* Added --http-proxy-option directive for controlling
+ miscellaneous HTTP proxy options.
+* Management state will no longer transition to "WAIT"
+ during TLS renegotiations.
+
+2004.12.16 -- Version 2.0-rc5
+
+* The --client-config-dir option will now try to open
+ a default file called "DEFAULT" if no file matching
+ the common name of the incoming client was found.
+* The --client-connect script/plugin can now veto client
+ authentication by returning a failure code.
+* The --learn-address script/plugin can now prevent a
+ client-instance/address association from being learned
+ by returning a failure code.
+* Changed RPM group in .spec file to Applications/Internet.
+
+2004.12.14 -- Version 2.0-rc4
+
+* SuSE only -- Fixed interaction between openvpn.spec and
+ suse/openvpn.init where the .spec file was writing the
+ OpenVPN binary to a different location than where the
+ .init script was referencing it (Stefan Engel).
+* Solaris only -- Split Solaris ifconfig command into two
+ parts (Jan Just Keijser).
+* Some cleanup in add_option().
+* Better error checking on input dotted quad IP addresses.
+* Verify that --push argument is quoted, if there is
+ more than one.
+* More miscellaneous option sanity checks.
+
+2004.12.13 -- Version 2.0-rc3
+
+* On Windows, when --log or --log-append is used,
+ save the original stderr for username and password
+ prompts.
+* Fixed a bug introduced in the late 2.0 betas where
+ if a "verb" parameter >= 16 was used, it would be
+ ignored and the actual verb level would remain at 1.
+* Fixed a bug mostly seen on OS X where --management-hold
+ or --management-query-passwords would cause the management
+ interface to be unresponsive to incoming client connections.
+* Trigger an options error if one of the management-modifying
+ options is used without "management" itself.
+
+2004.12.12 -- Version 2.0-rc2
+
+* Amplified warnings in documentation about possible
+ man-in-the-middle attack when clients do not properly
+ verify server certificate. Changes to easy-rsa README,
+ FAQ, HOWTO, man page, and sample client config file.
+* Added a warning message if --tls-client or --client
+ is used without also specifying one of either
+ --ns-cert-type, --tls-remote, or --tls-verify.
+* status_open() fixes for MSVC builds (Blaine Fleming).
+* Fix attempt of "ntlm.c:55: error: `des_cblock' undeclared"
+ compiler error which has been reported on some platforms.
+* The openvpn.spec file for rpmbuild has several
+ new build-time options. See comments in the file.
+* Plugins are now built and packaged in the RPM and
+ will be saved in /usr/share/openvpn/plugin/lib.
+* Added --management-hold directive to start OpenVPN
+ in a hibernating state until released by the
+ management interface. Also added "hold" command
+ to the management interface.
+
+2004.12.07 -- Version 2.0-rc1
+
+* openvpn.spec workaround for SuSE confusion regarding
+ /etc/init.d vs. /etc/rc.d/init.d (Stefan Engel).
+
+2004.12.05 -- Version 2.0-beta20
+
+* The ability to read --askpass and --auth-user-pass
+ passwords from a file has been disabled by default.
+ To re-enable, use ./configure --enable-password-save.
+* Added additional pre-connected states to management
+ interface. See management/management-notes.txt
+ for more info.
+* State history is now recorded by the management
+ interface, and the "state" command now works like
+ the log or echo commands.
+* State history and real-time state change notifications
+ are now prepended with an integer unix timestamp.
+* Added --http-proxy-timeout option, previously
+ the timeout was hardcoded to 5 seconds.
+
+2004.12.02 -- Version 2.0-beta19
+
+* Fixed bug in management interface line termination
+ where output lines incorrectly contained a \00 char
+ after the customary \0d \0a.
+* Fixed bug introduced in beta18 where Windows version
+ would segfault on options errors.
+* Fixed bug in management interface where an empty
+ quoted string ("") entered as a parameter would cause
+ a segfault.
+* Fixed bug where --resolv-retry was not working
+ properly with multiple --remote hosts.
+* Added additional ./configure options to reduce
+ executable size for embedded applications.
+ See ./configure --help.
+
+2004.11.28 -- Version 2.0-beta18
+
+* Added management interface. See new --management-*
+ options or the full management interface documentation
+ in management/management-notes.txt in the tarball.
+ Management interface inclusion can be disabled by
+ ./configure --disable-management.
+* Added two new plugin modules: auth-pam and down-root.
+ Auth-pam supports pam-based authentication using a
+ split privilege execution model, while down-root enables
+ a down script to be executed with root privileges, even
+ when --user/--group is used to drop root privileges.
+ See the plugin directory in the tarball for READMEs,
+ source code, and Makefiles.
+* Plugin developers should note that some changes were
+ made to the plugin interface since beta17. See
+ openvpn-plugin.h for details.
+ Plugin interface inclusion can be disabled with
+ ./configure --disable-plugins
+* Added easy-rsa/build-key-server script which will
+ build a certificate with with nsCertType=server.
+* Added --ns-cert-type option for verification
+ of nsCertType field in peer certificate.
+* If --fragment n is specified and --mssfix is specified
+ without a parameter, default --mssfix to n. This restores
+ the 1.6 behavior when using --mssfix without a parameter.
+* Fixed SSL context initialization bug introduced in beta14
+ where this error might occur on restarts: "Cannot load
+ certificate chain ... PEM_read_bio:no start line".
+
+2004.11.11 -- Version 2.0-beta17
+
+* Changed default port number to 1194 per IANA official
+ port number assignment.
+* Added --plugin directive which allows compiled
+ modules to intercept script callbacks. See
+ plugin folder in tarball for more info.
+* Fixed bug introduced in beta12 where --key-method 1
+ authentications which should have succeeded would fail.
+* Ignore SIGUSR1 during DNS resolution.
+* Added SuSE support to openvpn.spec (Umberto Nicoletti).
+* Fixed --cryptoapicert SUBJ: parsing bug (Peter 'Luna'
+ Runestig).
+
+2004.11.07 -- Version 2.0-beta16
+
+* Modified sample-scripts/auth-pam.pl to get username
+ and password from OpenVPN via a file rather than
+ via environmental variables.
+* Added bytes_sent and bytes_received environmental
+ variables to be set prior to client-disconnect script.
+* Changed client virtual IP derivation precedence:
+ (1) use --ifconfig-push directive from --client-connect
+ script, (2) use --ifconfig-push directive from
+ --client-config-dir, and (3) use --ifconfig-pool
+ address.
+* If a --client-config-dir file specifies --ifconfig-push,
+ it will be visible to the --client-connect-script in
+ the ifconfig_pool_remote_ip environmental variable.
+* For tun-style tunnels, the ifconfig_pool_local_ip
+ environmental variable will be set, while for
+ tap-style tunnels, the ifconfig_pool_netmask variable
+ will be set.
+* Added intelligence to autoconf script to test
+ compiler for the accepted form of zero-length arrays.
+* Fixed a bug introduced in beta12 where --ip-win32
+ netsh would fail if --dev-node was not explicitly
+ specified.
+* --ip-win32 netsh will now work on hidden adapters.
+* Fix attempt of "Assertion failed at crypto.c:149".
+ This assertion has also been reported on 1.x with a
+ slightly different line number. The fix is twofold:
+ (1) In previous releases, --mtu-test may trigger this
+ assertion -- this bug has been fixed. (2) If something
+ else causes the assertion to be thrown, don't panic,
+ just output a nonfatal warning to the log and drop
+ the packet which generated the error.
+* Support TAP interfaces on Mac OS X (Waldemar Brodkorb).
+* Added --echo directive.
+* Added --auth-nocache directive.
+
+2004.10.28 -- Version 2.0-beta15
+
+* Changed environmental variable character classes
+ so that names must consist of alphanumeric or
+ underbar chars and values must consist of printable
+ characters. Illegal chars will be deleted.
+ Versions prior to 2.0-beta12 were more restrictive
+ and would map spaces to '.'.
+* On Windows, when the TAP adapter fails to
+ initialize with the correct IP address, output
+ "Initialization Sequence Completed with Errors"
+ to the console or log file.
+* Added a warning when user/group/chroot is used
+ without persist-tun and persist-key.
+* Added cryptoapi.[ch] to tarball and source zip.
+* --tls-remote option now works with common name
+ prefixes as well as with the full X509 subject
+ string. This is a useful alternative to using
+ a CRL on the client.
+* common names associated with a static
+ --ifconfig-push setting will no longer leave
+ any state in the --ifconfig-pool-persist file.
+* Hard TLS errors (TLS handshake failed) will now
+ trigger either a SIGUSR1 signal by default
+ or SIGTERM (if --tls-exit is specified). In TCP
+ mode, all TLS errors are considered to be hard.
+ In server mode, the signal will be local to the
+ client instance.
+* Added method parameter to --auth-user-pass-verify
+ directive to select whether username/password
+ is passed to script via environment or a temporary
+ file.
+* Added --status-version option to control format
+ of --status file. The --mode server
+ --status-version 2 format now includes a line
+ type token, the virtual IP address is shown
+ in the client list (even in --dev tap mode),
+ and the integer time_t value is shown anywhere
+ an ascii-formatted time/date is also shown.
+* Added --remap-usr1 directive which can be used
+ to control whether internally or externally
+ generated SIGUSR1 signals are remapped to
+ SIGHUP (restart without persisting state) or
+ SIGTERM (exit).
+* When running as a Windows service (using
+ --service option), check the exit event before
+ and after reading one line of input from
+ stdin, when reading username/password info.
+* For developers: Extended the --gremlin function
+ to better stress-test the new 2.0 features,
+ added Valgrind support on Linux and Dmalloc
+ support on Windows.
+
+2004.10.19 -- Version 2.0-beta14
+
+* Fixed a bug introduced in Beta12 that would occur
+ if you use a --client-connect script without also
+ defining --tmp-dir.
+* Fixed a bug introduced in Beta12 where a learn-address
+ script might segfault on the delete method.
+* Added Crypto API support in Windows version via
+ the --cryptoapicert option (Peter 'Luna' Runestig).
+
+2004.10.18 -- Version 2.0-beta13
+
+* Fixed an issue introduced in Beta12 where the private
+ key password would not be prompted for unless --askpass
+ was explicitly specified in the config.
+
+2004.10.17 -- Version 2.0-beta12
+
+* Added support for username/password-based authentication.
+ Clients can now authentication themselves with the server
+ using either a certificate, a username/password, or both.
+ New directives: --auth-user-pass, --auth-user-pass-verify,
+ --client-cert-not-required, and --username-as-common-name.
+* Added NTLM proxy patch (William Preston).
+* Added --ifconfig-pool-linear server flag to allocate
+ individual tun addresses for clients rather than /30
+ subnets (won't work with Windows clients).
+* Modified --http-proxy code to cache username/password
+ across restarts.
+* Modified --http-proxy code to read username/password
+ from the console when the auth file is given as "stdin".
+* Modified --askpass to take an optional filename argument.
+* --persist-tun and --persist-key now work in client mode
+ and can be pushed to clients as well.
+* Added --ifconfig-pool-persist directive, to maintain
+ ifconfig-pool info in a file which is persistent across
+ daemon instantiations.
+* --user and --group privilege downgrades as well as
+ --chroot now also work in client mode (the
+ dowgrade/chroot will be delayed until the initialization
+ sequence is completed).
+* Added --show-engines standalone directive to show
+ available OpenSSL crypto accelerator engine support.
+* --engine directive now accepts an optional engine-ID
+ parameter to control which engine is used.
+* "Connection reset, restarting" log message now shows
+ which client is being reset.
+* Added --dhcp-pre-release directive in Windows version.
+* Second parm to --ip-win32 can be "default", e.g.
+ --ip-win32 dynamic default 60.
+* Fixed documentation bug regarding environmental
+ variable settings for --ifconfig-pool IP addresses.
+ The correct environmental variable names are:
+ ifconfig_pool_local_ip and ifconfig_pool_remote_ip.
+* ifconfig_pool_local_ip and ifconfig_pool_remote_ip
+ environmental variables are now passed to the
+ client-disconnect script.
+* In server mode, environmental variables are now scoped
+ according to the client they are associated with,
+ to solve the problem of "crosstalk" between different
+ client's environmental variable sets.
+* Added --down-pre flag to cause --down script to be
+ called before TUN/TAP close (rather than after).
+* Added --tls-exit flag which will cause OpenVPN
+ to exit on any TLS errors.
+* Don't push a route to a client if it exactly
+ matches an iroute (this lets you push routes to
+ all clients, and OpenVPN will automatically remove
+ the route from the route push list only for that client
+ which the route actually belongs to).
+* Made '--resolv-retry infinite' the default.
+ --resolv-retry can be disabled by using a parameter of 0.
+* For clients which plan to pull config info from server,
+ set an initial default ping-restart of 60 seconds.
+* Optimized mute code to lessen the load on the processor
+ when messages are being muted at a higher frequency.
+* Made route log messages non-mutable.
+* Silence the Linux "No buffer space available" message.
+* Added miscellaneous additional option sanity checks.
+* Added Windows version of easy-rsa scripts in
+ easy-rsa/Windows directory (Andrew J. Richardson).
+* Added NetBSD route patch (Ed Ravin).
+* Added OpenBSD patch for TAP + --redirect-gateway
+ (Waldemar Brodkorb).
+* Directives which prompt for a username and/or password
+ will now work with --daemon (OpenVPN will prompt
+ before forking).
+* Warn if CRL is from a different issuer than the
+ issuer of the peer certificate (Bernhard Weisshuhn).
+* Changed init script chkconfig parameters to start
+ OpenVPN daemon(s) before NFS.
+* Bug fix attempt of "too many I/O wait events" which occurs
+ on OSes which prefer select() over poll() such as Mac OS X.
+* Added --ccd-exclusive flag. This flag will require, as a
+ condition of authentication, that a connecting client has
+ a --client-config-dir file.
+* TAP-Win32 open code will attempt to open a free adapter
+ if --dev-node is not specified (Mathias Sundman).
+* Resequenced --nice and --chroot ordering so that --nice
+ occurs first.
+* Added --suppress-timestamps flag (Charles Duffy).
+* Source code changes to allow compilation by MSVC
+ (Peter 'Luna' Runestig).
+* Added experimental --fast-io flag which optimizes
+ TUN/TAP/UDP writes on non-Windows systems.
+
+2004.08.18 -- Version 2.0-beta11
+
+* Added --server, --server-bridge, --client, and
+ --keepalive helper directives. See client.conf
+ and server.conf in sample-config-files for sample
+ configurations which use the new directives.
+* On Windows, added --route-method to control
+ whether IP Helper API or route.exe is used
+ to add/delete routes.
+* On Windows, added a second parameter to
+ --route-delay to control the maximum time period
+ to wait for the TAP-Win32 adapter to come up
+ before adding routes.
+* Fixed bug in Windows version where configurations
+ which omit --ifconfig might fail to recognize when
+ the TAP adapter is up.
+* Proxy connection failures will now retry according
+ to the --connect-retry parameter.
+* Fixed --dev null handling on Windows so that TLS
+ loopback test described in INSTALL file works
+ correctly on Windows.
+* Added "Initialization Sequence Completed" message
+ after all initialization steps have been completed
+ and the VPN can be considered "up".
+* Better sanity-checking on --ifconfig-pool parameters.
+* Added --tcp-queue-limit option to control
+ TUN/TAP -> TCP socket overflow.
+* --ifconfig-nowarn flag will now silence general
+ warnings about possible --ifconfig address
+ conflicts, including the warning about --ifconfig
+ and --remote addresses being in same /24 subnet.
+* Fixed case where server mode did not correctly
+ identify certain types of ethernet multicast packets
+ (Marcel de Kogel).
+* Added --explicit-exit-notify option (experimental).
+
+2004.08.02 -- Version 2.0-beta10
+
+* Fixed possible reference after free of option strings
+ after a restart, bug was introduced in beta8.
+* Fixed segfault at route.c:919 in the beta9
+ Windows version that was being caused by indirection
+ through a NULL pointer.
+* Mistakenly built debug version of TAP-Win32 driver
+ for beta9. Beta10 has correct release build.
+
+2004.07.30 -- Version 2.0-beta9
+
+* Fixed --route issue on Windows that was introduced with
+ the new beta8 route implementation based on the
+ IP Helper API.
+
+2004.07.27 -- Version 2.0-beta8
+
+* Added TCP support in server mode.
+* Added PKCS #12 support (Mathias Sundman).
+* Added patch to make revoke-crt and make-crl work
+ seamlessly within the easy-rsa environment (Jan Kiszka).
+* Modified --mode server ethernet bridge code to forward
+ special IEEE 802.1d MAC Groups, i.e. 01:80:C2:XX:XX:XX.
+* Added --dhcp-renew and --dhcp-release flags to Windows
+ version. Normally DHCP renewal and release on the TAP
+ adapter occurs automatically under Windows, however
+ if you set the TAP-Win32 adapter Media Status property
+ to "Always Connected", you may need these flags.
+* Added --show-net standalone flag to Windows version to
+ show OpenVPN's view of the system adapter and routing
+ tables.
+* Added --show-net-up flag to Windows version to output
+ the system routing table and network adapter list to
+ the log file after the TAP-Win32 adapter has been brought
+ up and any routes have been added.
+* Modified Windows version to add routes using the IP Helper
+ API rather than by calling route.exe.
+* Fixed bug where --route-up script was not being called
+ if no --route options were specified.
+* Added --mute-replay-warnings to suppress packet replay
+ warnings. This is a common false alarm on WiFi nets.
+* Added "def1" flag to --redirect-gateway option to override
+ the default gateway by using 0.0.0.0/1 and 128.0.0.0/1
+ rather than 0.0.0.0/0. This has the benefit of overriding
+ but not wiping out the original default gateway.
+ (Thanks to Jim Carter for pointing out this idea).
+* You can now run OpenVPN with a single config file argument.
+ For example, you can now say "openvpn config.conf"
+ rather than "openvpn --config config.conf".
+* On Windows, made --route and --route-delay more adaptive
+ with respect to waiting for interfaces referenced by the
+ route destination to come up. Routes added by --route
+ should now be added as soon as the interface comes up,
+ rather than after an obligatory 10 second delay. The
+ way this works internally is that --route-delay now
+ defaults to 0 on Windows. Previous versions would
+ wait for --route-delay seconds then add the routes.
+ This version will wait --route-delay seconds and then
+ test the routing table at one second intervals for the
+ next 30 seconds and will not add the routes until they
+ can be added without errors.
+* On Windows, don't setsockopt SO_SNDBUF or SO_RCVBUF by
+ default on TCP/UDP socket in light of reports that this
+ action can have undesirable global side effects on the
+ MTU settings of other adapters. These parameters can
+ still be set, but you need to explicitly specify
+ --sndbuf and/or --rcvbuf.
+* Added --max-clients option to limit the maximum number
+ of simultaneously connected clients in server mode.
+* Added error message to illuminate shell escape gotcha when
+ single backslashes are used in Windows path names.
+* Added optional netmask parm to --ifconfig-pool.
+* Fixed bug where http-proxy connect retry attempts were
+ incorrectly going to the remote OpenVPN server,
+ not to the HTTP proxy server.
+
+2004.06.29 -- Version 2.0-beta7
+
+* Fixed bug in link_socket_verify_incoming_addr() which
+ under certain circumstances could have caused --float
+ behavior even if --float was not specified.
+* --tls-auth option now works with --mode server.
+ All clients and the server should use the same
+ --tls-auth key when operating in client/server mode.
+* Added --engine option to make use of OpenSSL-supported
+ crypto acceleration hardware.
+* Fixed some high verbosity print format size issues
+ in event.c for 64 bit platforms (Janne Johansson).
+* Made failure to open --log or --log-append file
+ a non-fatal error.
+
+2004.06.23 -- Version 2.0-beta6
+
+* Fixed Windows installer to intelligently put
+ up a reboot dialog only if tapinstall tells
+ us that it's really necessary.
+* Fixed "Assertion failed at fragment.c:309"
+ bug when --mode server and --fragment are used
+ together.
+* Ignore HUP, USR1, and USR2 signals during
+ initialization. Prior versions would abort.
+* Fixed bug on OS X: "Assertion failed at event.c:406".
+* Added --service option to Windows version, for use
+ when OpenVPN is being programmatically instantiated
+ by another process (see man page for info).
+* --log and --log-append options now work on Windows.
+* Update OpenBSD INSTALL notes (Janne Johansson).
+* Enable multicast on tun interface when running on
+ OpenBSD (Pavlin Radoslavov).
+* Fixed recent --test-crypto breakage, where options
+ such as --cipher were not being parsed correctly.
+* Modified options compatibility string by removing
+ ifconfig substring if it is empty. Incremented
+ options compatibility string version number to 4.
+* Fixed typo in --tls-timeout option parsing
+ (Mikael Lonnroth).
+
+2004.06.13 -- Version 2.0-beta5
+
+* Fixed rare --mode server crash that could occur
+ if data was being routed to a client at
+ high bandwidth at the precise moment that the
+ client instance object on the server was being
+ deleted.
+* Fixed issue on machines which have epoll.h and
+ the epoll_create glibc call defined, but which
+ don't actually implement epoll in the kernel.
+ OpenVPN will now gracefully fall back to the
+ poll API in this case.
+* Fixed Windows bug which would cause the following
+ error in a --mode server --dev tap configuration:
+ "resource limit WSA_MAXIMUM_WAIT_EVENTS has been
+ exceeded".
+* Added CRL (certificate revocation list) management
+ scripts to easy-rsa directory (Jon Bendtsen).
+* Do a better job of getting the ifconfig component
+ of the options consistency check to work correctly
+ when --up-delay is used.
+* De-inlined some functions which were too complex
+ to be inlined anyway with gcc.
+* If a --dhcp-option option is pushed to a non-windows
+ client, the option will be saved in the client's
+ environment before the --up script is called, under
+ the name "foreign_option_{n}".
+* Added --learn-address script (see man page) which
+ allows for firewall access through the VPN to be
+ controlled based on the client common name.
+* In mode --server mode, when a client connects to
+ the server, the server will disconnect any
+ still-active clients which use the same common
+ name. Use --duplicate-cn flag to revert to
+ previous behavior of allowing multiple clients
+ to concurrently connect with the same common name.
+
+2004.06.08 -- Version 2.0-beta4
+
+* Fixed issue with beta3 where Win32 service wrapper
+ was keying off of old TAP HWID as a dependency. To
+ ensure that the new service wrapper is correctly
+ installed, the Windows install script will uninstall
+ the old wrapper before installing the new one,
+ causing a reset of service properties.
+* Fixed permissions issue on --status output file,
+ with default access permissions of owner read/write
+ only (default permissions can be changed of course with
+ chmod).
+
+2004.06.05 -- Version 2.0-beta3
+
+* More changes to TAP-Win32 driver's INF file which
+ affects the placement of the driver in the Windows
+ device namespace. This is done to work around an
+ apparent bug in Windows when short HWIDs are used,
+ and will also ease the upgrade from 1.x to 2.0 by
+ reducing the chances that a reboot will be needed
+ on upgrade. Like beta2, this upgrade will
+ delete existing TAP-Win32 interfaces, and reinstall
+ a single new interface with default properties.
+* Major rewrite of I/O event wait layer in the style
+ of libevent. This is a precursor to TCP support
+ in --mode server.
+* New feature: --status. Outputs a SIGUSR2-like
+ status summary to a given file, updated once
+ per n seconds. The status file is comma delimited
+ for easy machine parsing.
+* --ifconfig-pool now remembers common names and
+ will try to assign a consistent IP to a given
+ common name. Still to do: persist --ifconfig-pool
+ memory across restarts by saving state in file.
+* Fixed bug in event timer queue which could cause
+ recurring timer events such as --ping to not
+ correctly schedule again after firing. This in
+ turn would cause spurrious ping restarts and possible
+ connection outages. Thanks to Denis Vlasenko for
+ tracking this down.
+* Possible fix to reported bug where --daemon argument
+ was not printing to syslog correctly after restart.
+* Fixed bug where pulling --route or --dhcp-option
+ directives from a server would problematically
+ interact with --persist-tun on the client.
+* Updated contrib/multilevel-init.patch (Farkas Levente).
+* Added RPM build option to .spec and .spec.in files
+ to optionally disable LZO inclusion (Ian Pilcher).
+* The latest MingW runtime and headers define
+ 'ssize_t', so a patch is needed (Gisle Vanem).
+
+2004.05.14 -- Version 2.0-beta2
+
+* Fixed signal handling bug in --mode server, where
+ SIGHUP and SIGUSR1 were treated as SIGTERM.
+* Changed the TAP-Win32 HWID from "TAP" to "TAPDEV".
+ Apparently the larger string may work around
+ a problem where the TAP adapter is sometimes missing
+ from the network connections panel, especially under
+ XP SP2. Also note that installing this upgrade will
+ uninstall any pre-existing TAP-Win32 adapters, and then
+ install a single new adapter, meaning that old adapter
+ properties will be lost. Thanks to Md5Chap for solving
+ this one.
+* For --mode server --dev tap, the options --ifconfig and
+ --ifconfig-pool are now optional. This allows address
+ assignment via DHCP or use of a TAP VPN without
+ IP support, as has always been possible with 1.x.
+* Fixed bug where --ifconfig may not work correctly on
+ Linux 2.2.
+* Added 'local' flag to --redirect-gateway for use on
+ networks where both OpenVPN daemons are connected
+ to a shared subnet, such as wireless.
+
+2004.05.09 -- Version 2.0-beta1
+
+* Unchanged from test29 except for version number
+ upgrade.
+
+2004.05.08 -- Version 2.0-test29
+
+* Modified --dev-node on Windows to accept a TAP-Win32
+ GUID name. In addition, --show-adapters will now
+ display the high-level name and GUID of each adapter.
+ This is an attempt to work around an issue in Windows
+ where sometimes the TAP-Win32 adapter installs correctly
+ but has no icon in the network connections control
+ panel. In such cases, being able to specify
+ --dev-node {TAP-GUID} can work around the missing icon.
+
+2004.05.07 -- Version 2.0-test28
+
+* Fixed bug which could cause segfault on program
+ shutdown if --route and --persist-tun are used
+ together.
+
+2004.05.06 -- Version 2.0-test27
+
+* Fixed bug in close_instance() which might cause
+ memory to be accessed after it had already been freed.
+* Fixed bug in verify_callback() that might have
+ caused uninitialized data to be referenced.
+* --iroute now allows full CIDR subnet routing.
+* In "--mode server --dev tun" usage, source addresses
+ on VPN packets coming from a particular client must
+ be associated with that client in the OpenVPN internal
+ routing table.
+
+2004.04.28 -- Version 2.0-test26
+
+* Optimized broadcast path in multi-client mode.
+* Added socket buffer size options --rcvbuf & --sndbuf.
+* Configure Linux tun/tap driver to use a more sensible
+ txqueuelen default. Also allow explicit setting
+ via --txqueuelen option (Harald Roelle).
+* The --remote option now allows the port number
+ to be specified as the second parameter. If
+ unspecified, the port number defaults to the
+ --rport value.
+* Multiple --remote options on the client can now be
+ specified for load balancing and failover. The
+ --remote-random flag can be used to initially randomize
+ the --remote list for basic load balancing.
+* If a remote DNS name resolves to multiple DNS addresses,
+ one will be chosen by random as a kind of basic
+ load-balancing feature if --remote-random is used.
+* Added --connect-freq option to control maximum
+ new connection frequency in multi-client mode.
+* In multi-client mode, all syslog messages associated
+ with a specific client now include a client-ID prefix.
+* For Windows, use a gettimeofday() function based
+ on QueryPerformanceCounter (Derek Burdick).
+* Fixed bug in interaction between --key-method 2
+ and DES ciphers, where dynamic keys would be generated
+ with bad parity and then be rejected.
+
+2004.04.17 -- Version 2.0-test24
+
+* Reworked multi-client broadcast handling.
+
+2004.04.13 -- Version 2.0-test23
+
+* Fixed bug in --dev tun --client-to-client routing.
+* Fixed a potential deadlock in --pull.
+* Fixed a problem with select() usage which could
+ cause a repeating sequence of "select : Invalid
+ argument (code=22)"
+
+2004.04.11 -- Version 2.0-test22
+
+* Fixed bug where --mode server + --daemon was
+ prematurely closing syslog connection.
+* Added support for --redirect-gateway on Mac OS X
+ (Jeremy Apple).
+* Minor changes to TAP-Win32 driver based on feedback
+ from the NDISTest tool.
+
+2004.04.11 -- Version 2.0-test21
+
+* Optimizations in multi-client server event loop.
+
+2004.04.10 -- Version 2.0-test20
+
+* --mode server capability now works with either tun
+ or tap interfaces. When used with tap interfaces,
+ OpenVPN will internally bridge all client tap
+ interfaces with the server tap interface.
+* Connecting clients can now have a client-specific
+ configuration on the server, based on the client
+ common name embedded in the client certificate.
+ See --client-config-dir and --client-connect.
+ These options can be used to configure client-specific
+ routes.
+* Added an option --client-to-client that enables
+ internal client-to-client routing or bridging.
+ Otherwise, clients will only "see" the server,
+ not other connected clients.
+* Fixed bug in route scheduling which would have caused
+ --mode server to not work on Windows in test18
+ and test19 with the sample config file.
+* Man page is up to date with all new options.
+* OpenVPN 2.0 release notes on web site updated
+ with tap-style tunnel examples.
+
+2004.04.02 -- Version 2.0-test19
+
+* Fixed bug where routes pushed from server were
+ not working correctly on Windows clients.
+* Added Mac OS X route patch (Jeremy Apple).
+
+2004.03.30 -- Version 2.0-test18
+
+* Minor fixes + Windows self-install modified
+ to use OpenSSL 0.9.7d.
+
+2004.03.29 -- Version 2.0-test17
+
+* Fixed some bugs related to instance timeout and deletion.
+* Extended --push/--pull option to support additional
+ option classes.
+
+2004.03.28 -- Version 2.0-test16
+
+* Successful test of --mode udp-server, --push,
+ --pull, and --ifconfig-pool with server on
+ Linux 2.4 and clients on Linux and Windows.
+
+2004.03.25 -- Version 2.0-test15
+
+* Implemented hash-table lookup of client instances
+ based either on remote UDP address/port or remote
+ ifconfig endpoint.
+* Implemented a randomized binary tree based
+ scheduler for scalably scheduling a large number
+ of client instance events. Uses the treap
+ data structure and node rotation algorithm
+ to keep the tree balanced.
+* Initial implementation of ifconfig-pool.
+* Made --key-method 2 the default.
+
+2004.03.20 -- Version 2.0-test14
+
+* Implemented --push and --pull.
+
+2004.03.20 -- Version 2.0-test13
+
+* Reduced struct tls_multi and --single-session
+ memory footprint.
+* Modified --single-session flag to be used
+ in multi-client UDP server client instances.
+
+2004.03.19 -- Version 2.0-test12
+
+* Added the key multi-client UDP server options,
+ --mode, --push, --pull, and --ifconfig-pool.
+* Revamped GC (garbage collection) code to not rely
+ on any global data.
+* Modifications to thread.[ch] to allow a more
+ flexible thread model.
+
+2004.03.16 -- Version 2.0-test11
+
+* Moved all timer code to interval.h, added new file
+ interval.c.
+* Fixed missing include.
+
+2004.03.16 -- Version 2.0-test10
+
+* More TAP-Win32 fixes.
+* Initial debugging and testing of multi.[ch].
+
+2004.03.14 -- Version 2.0-test9
+
+* Branch merge with 1.6-rc3
+* More point-to-multipoint work in multi.[ch].
+* Major TAP-Win32 driver restructuring to use
+ NdisMRegisterDevice instead of
+ IoCreateDevice/IoCreateSymbolicLink.
+* Changed TAP-Win32 symbolic links to use \DosDevices\Global\
+ pathname prefix.
+* In the majority of cases, TAP-Win32 should now be
+ able to install and uninstall on Win2K without requiring
+ a reboot.
+* TAP-Win32 MAC address can now be explicitly set in the
+ adapter advanced properties page.
+
+2004.03.04 -- Version 2.0-test8
+
+* Branch merge with 1.6-rc2.
+
+2004.03.03 -- Version 2.0-test7
+
+* Branch merge with 1.6-rc1.2.
+
+2004.03.02 -- Version 2.0-test6
+
+* Branch merge with 1.6-rc1.
+
+2004.03.02 -- Version 2.0-test5
+
+* Move Socks5 UDP header append/remove to socks.c, and is
+ called from forward.c.
+* Moved verify statics from ssl.c into struct tls_session.
+* Wrote multi.[ch] to handle top level of point-to-multipoint
+ mode.
+* Wrote some code to allow a struct link_socket in a child context
+ to be slaved to the parent context.
+* Broke up packet read and process functions in forward.c
+ (from socket or tuntap) into separate functions for read
+ and process, so that point-to-point and point-to-multipoint can
+ share the same code.
+* Expand TLS control channel to allow the passing of configuration
+ commands.
+* Wrote mroute.[ch] to handle internal packet routing for
+ point-to-multipoint mode.
+
+2004.02.22 -- Version 2.0-test3
+
+* Initial work on UDP multi-client server.
+* Branch merge of 1.6-beta7
+
+2004.02.14 -- Version 2.0-test2
+
+* Refactorization of openvpn.c into openvpn.[ch]
+ init.[ch] forward.[ch] forward-inline.h
+ occ.[ch] occ-inline.h ping.[ch] ping-inline.h
+ sig.[ch]. Created a master per-tunnel
+ struct context in openvpn.h.
+* Branch merge of 1.6-beta6.2
+
+2003.11.06 -- Version 2.0-test1
+
+* Initial testbed for 2.0.
+
+2004.05.09 -- Version 1.6.0
+
+* Unchanged from 1.6-rc4 except for version number
+ upgrade.
+
+2004.04.01 -- Version 1.6-rc4
+
+* Made minor customizations to devcon and
+ renamed as tapinstall.exe for Windows version.
+* Fixed "storage size of `iv' isn't known" build
+ problem on FreeBSD.
+* OpenSSL 0.9.7d bundled with Windows self-install.
+
+2004.03.13 -- Version 1.6-rc3
+
+* Minor Windows fixes for --ip-win32 dynamic, relating to
+ the way the TAP-Win32 driver responds to a DHCP request
+ from the Windows DHCP client.
+* The net_gateway environmental variable wasn't being
+ set correctly for called scripts (Paul Zuber).
+* Added code to determine the default gateway on FreeBSD,
+ allowing the --redirect-gateway option to work
+ (Juan Rodriguez Hervella).
+
+2004.03.04 -- Version 1.6-rc2
+
+* Fixed bug in Windows version where the NetBIOS node-type
+ DHCP option might have been passed even if it was not
+ specified.
+* Fixed bug in Windows version introduced in 1.6-rc1, where
+ DHCP timeout would be set to 0 seconds if --ifconfig option
+ was used and --ip-win32 option was not explicitly specified.
+* Added some new --dhcp-option types for Windows version.
+
+2004.03.02 -- Version 1.6-rc1
+
+* For Windows, make "--ip-win32 dynamic" the default.
+* For Windows, make "--route-delay 10" the default
+ unless --ip-win32 dynamic is not used or --route-delay
+ is explicitly specified.
+* L_TLS mutex could have been left in a locked state
+ for certain kinds of TLS errors.
+
+2004.02.22 -- Version 1.6-beta7
+
+* Allow scheduling priority increase (--nice) together
+ with UID/GID downgrade (--user/--group).
+* Code that causes SIGUSR1 restart on TLS errors in TCP
+ mode was not activated in pthread builds.
+* Save the certificate serial number in an environmental
+ variable called tls_serial_{n} prior to calling the
+ --tls-verify script. n is the current cert chain level.
+* Added NetBSD IPv6 tunnel capability (also requires
+ a kernel patch) (Horst Laschinsky).
+* Fixed bug in checking the return value of the nice()
+ function (Ian Pilcher).
+* Bug fix in new FreeBSD IPv6 over TUN code which was
+ originally added in 1.6-beta5 (Nathanael Rensen).
+* More Socks5 fixes -- extended the struct frame
+ infrastructure to accomodate proxy-based encapsulation
+ overhead.
+* Added --dhcp-option to Windows version for setting
+ adapter properties such as WINS & DNS servers.
+* Use a default route-delay of 5 seconds when
+ --ip-win32 dynamic is specified (only applicable when
+ --route-delay is not explicitly specified).
+* Added "log_append" registry variable to control
+ whether the OpenVPN service wrapper on Windows
+ opens log files in append (log_append="1") or
+ truncate (log_append="0") mode. The default
+ is truncate.
+
+2004.02.05 -- Version 1.6-beta6
+
+* UDP over Socks5 fix to accomodate Socks5 encapsulation
+ overhead (Christof Meerwald).
+* Minor --ip-win32 dynamic tweaks (use long lease time,
+ invalidate existing lease with DHCPNAK).
+
+2004.02.01 -- Version 1.6-beta5
+
+* Added Socks5 proxy support (Christof Meerwald).
+* IPv6 tun support for FreeBSD (Thomas Glanzmann).
+* Special TAP-Win32 debug mode for Windows self-install that was
+ enabled in beta4 is now turned off.
+* Added some new Solaris notes to INSTALL (Koen Maris).
+* More work on --ip-win32 dynamic.
+
+2004.01.27 -- Version 1.6-beta4
+
+* For this beta, the Windows self-install is a debug version
+ and will run slower -- use only for testing.
+* Reverted the --ip-win32 default back to 'ipapi'
+ from 'dynamic'.
+* Added the offset parameter to '--ip-win32 dynamic' which
+ can be used to control the address of the masqueraded
+ DHCP server which replies to Windows DHCP requests.
+* Added a wait/nowait option to --inetd (nowait can only
+ be used with TCP sockets, TLS authentication, and over
+ a bridged configuration -- see FAQ for more info)
+ (Stefan `Sec` Zehl).
+* Added a build-time capability where TAP-Win32 driver
+ debug messages can be output by OpenVPN at --verb 6
+ or higher.
+
+2004.01.20 -- Version 1.6-beta2
+
+* Added ./configure --enable-iproute2 flag which
+ uses iproute2 instead of route + ifconfig --
+ this is necessary for the LEAF Linux distro
+ (Martin Hejl).
+* Added renewal-time and rebind-time to set of
+ DHCP options returned by the TAP-Win32 driver when
+ "--ip-win32 dynamic" is used.
+
+2004.01.14 -- Version 1.6-beta1
+
+* Fixed --proxy bug that sometimes caused plaintext
+ control info generated by the proxy prior to http
+ CONNECT method establishment to be incorrectly
+ parsed as OpenVPN data.
+* For Windows version, implemented the
+ "--ip-win32 dynamic" method and made it the default.
+ This method sets the TAP-Win32 adapter IP address
+ and netmask by replying to the kernel's DHCP queries.
+ See the man page for more detailed info.
+* Added --connect-retry parameter which controls
+ the time interval (in seconds) between connect()
+ retries when --proto tcp-client is used. Previously,
+ this value was hardcoded to 5 seconds, and still
+ defaults as such.
+* --resolv-retry can now be used with a parameter
+ of "infinite" to retry indefinitely.
+* Added SSL_CTX_use_certificate_chain_file() to ssl.c
+ for support of multi-level certificate chains
+ (Sten Kalenda).
+* Fixed --tls-auth incompatibility with 1.4.x and earlier
+ versions of OpenVPN when the passphrase file is an
+ OpenVPN static key file (as generated by --genkey).
+* Added shell-escape support in config files using
+ the backslash character ("\") so that (for example)
+ double quotes can be passed to the shell.
+* Added "contrib" subdirectory on tarball, source zip,
+ and CVS containing user-submitted contributions.
+* Added an optional patch to the Redhat init script to
+ allow the configuration file directory to be a
+ multi-level directory hierarchy (Farkas Levente).
+ See contrib/multilevel-init.patch
+* Added some scripts and documentation on using
+ Linux "fwmark" iptables rules to enable
+ fine-grained routing control over the VPN
+ (Sean Reifschneider, <jafo@tummy.com>).
+ See contrib/openvpn-fwmarkroute-1.00
+
+2003.11.20 -- Version 1.5.0
+
+* Minor documentation changes.
+
+2003.11.04 -- Version 1.5-beta14
+
+* Fixed build problem with ./configure --disable-ssl
+ that was reported on Debian woody.
+* Fixed bug where --redirect-gateway could not be used
+ together with --resolv-retry.
+
+2003.11.03 -- Version 1.5-beta13
+
+* Added CRL (certificate revocation list) capability using
+ --crl-verify option (Stefano Bracalenti).
+* Added --replay-window option for variable replay-protection
+ window sizes.
+* Fixed --fragment bug which might have caused certain large
+ packets to be sent unfragmented.
+* Modified --secret and --tls-auth to permit different cipher and
+ HMAC keys to be used for each data flow direction. Also
+ increased static key file size generated by --genkey from
+ 1024 to 2048 bits, where 512 bits each are reserved for
+ send-HMAC, encrypt, receive-HMAC, and decrypt. Key file forward
+ and backward compatibility is maintained. See --secret option
+ documentation on the man page for more info.
+* Added --tls-remote option (Teemu Kiviniemi).
+* Fixed --tls-cipher documention regarding correct delimiter
+ usage (Teemu Kiviniemi).
+* Added --key-method option for selecting alternative data
+ channel key negotiation methods. Method 1 is the default.
+ Method 2 has been added (see man page for more info).
+* Added French translation of HOWTO to web site
+ (Guillaume Lehmann).
+* Fixed problem caused by late resolver library load on
+ certain platforms when --resolv-retry and --chroot are
+ used together (Teemu Kiviniemi).
+* In TCP mode, all decryption or TLS errors will abort the current
+ connection (this is not done in UDP mode because UDP is
+ "connectionless").
+* Fixed a TCP client reconnect bug that only occurs on the
+ BSDs, where connect() fails with an invalid argument. This
+ bug was partially (but not completely) fixed in beta7.
+* Added "route_net_gateway" environmental variable which contains
+ the pre-existing default gateway address from the routing table
+ (there's no standard API for getting the default gateway, so
+ right now this feature only works on Windows or Linux).
+* Renamed the "route_default_gateway" enviromental variable to
+ "route_vpn_gateway" -- this is the remote VPN endpoint.
+* The special keywords vpn_gateway, net_gateway, and remote_host
+ can now be used for the network or gateway components of the
+ --route option. See the man page for more info.
+* Added the --redirect-gateway option to configure the VPN
+ as the default gateway (implemented on Linux and Windows only).
+* Added the --http-proxy option with basic authentication
+ support for use in TCP client mode. Successfully tested
+ using Squid as the HTTP proxy, with and without authentication.
+
+2003.10.12 -- Version 1.5-beta12
+
+* Fixed Linux-only bug in --mktun and --rmtun which was
+ introduced around beta8 or so, which would cause
+ an error such as "I don't recognize device tun0 as a
+ tun or tap device1".
+* Added --ifconfig-nowarn option to disable options
+ consistency warnings about --ifconfig parameters.
+* Don't allow any kind of sequence number backtracking or
+ message reordering when in TCP mode.
+* Changed beta naming convention to use '_' (underscore)
+ rather than '-' (dash) to pacify rpmbuild.
+
+2003.10.08 -- Version 1.5-beta11
+
+* Modified code in the Windows version which sets the IP address
+ and netmask of the TAP-Win32 adapter using the IP Helper API.
+ Most of the changes involve better error recovery when
+ the IP Helper API returns an error status. See the
+ manual page entry on --ip-win32 for more info.
+
+2003.10.08 -- Version 1.5-beta10
+
+* Added getpass() function for Windows version so that --askpass
+ option works correctly (Stefano Bracalenti).
+* Added reboot advisory to end of Win32 install script.
+* Changed crypto code to use pseudo-random IVs rather than
+ carrying forward the IV state from the previous packet.
+ This is in response to item 2 in the following document:
+ http://www.openssl.org/~bodo/tls-cbc.txt which points
+ out weaknesses in TLS's use of the same IV carryforward
+ approach. This change does not break protocol compatibility
+ with previous versions of OpenVPN.
+* Made a change to the crypto replay protection code to also
+ protect against certain kinds of packet reordering attacks.
+ This change does not break protocol compatibility with
+ previous versions of OpenVPN.
+* Added --ip-win32 option to provide several choices for
+ setting the IP address on the TAP-Win32 adapter.
+* #ifdefed out non-CBC crypto modes by default.
+* Added --up-delay option to delay TUN/TAP open and --up script
+ execution until after connection establishment. This option
+ replaces the earlier windows-only option --tap-delay.
+
+2003.10.01 -- Version 1.5-beta9
+
+* Fixed --route-noexec bug where option was not parsed correctly.
+* Complain if --dev tun is specified without --ifconfig on Windows.
+* Fixed bug where TCP connections on windows would sometimes cause
+ an assertion failure.
+* Added a new flag to TAP-Win32 advanced properties that allows one
+ to set the adapter to be always "connected" even when an OpenVPN
+ process doesn't have it open. The default behavior is to report
+ a media status of connected only when an OpenVPN process has the
+ adapter open.
+* Rebuilt the Windows self-install distribution with OpenSSL 0.9.7c
+ DLLs in response to an OpenSSL security advisory.
+
+2003.09.30 -- Version 1.5-beta8
+
+* Extended the --ifconfig option to work on tap devices as well
+ as tun devices.
+* Implemented the --ifconfig option for Windows, by calling the
+ netsh tool.
+* By default, do an "arp -d *" on Windows after TAP-Win32 open to
+ refresh the MAC cache. This behaviour can be disabled with
+ --no-arp-del.
+* On Windows, allow the --dev-node parameter (which specifies
+ the name of the TAP-Win32 adapter) to be omitted in cases where
+ there is a single TAP-Win32 adapter on the system which can be
+ assumed to be the default.
+* Modified the diagnostic --verb 5 debugging level to print 'R'
+ for TCP/UDP read, 'W' for TCP/UDP write, 'r' for TUN/TAP read,
+ and 'w' for TUN/TAP write.
+* Conditionalize OpenBSD read_tun and write_tun based on tun or tap
+ mode.
+* Added IPv6 tun support to OpenBSD (Thomas Glanzmann).
+* Make the --enable-mtu-dynamic ./configure option enabled by
+ default.
+* Deprecated the --mtu-dynamic run-time option, in favor of
+ --fragment.
+* DNS names can now be used as --ifconfig parameters.
+* Significant work on TAP-Win32 driver to bring up to SMP standards.
+* On Windows, fixed dangling IRP problem if TAP-Win32 driver is
+ unloaded or disabled, while a user-space process has it open.
+* On Windows, if --tun-mtu is not specified, it will be read from
+ the TAP-Win32 driver via ioctl.
+* On Windows, added TAP-Win32 driver status info to "F2" keyboard
+ signal (only when run from a console window).
+* Added --mssfix option to control TCP MSS size (YANO Hirokuni).
+* Renamed --mtu-dynamic option to --fragment to more accurately
+ reflect its function. Fragment accepts a single parameter which
+ is the upper limit on acceptable UDP packet size.
+* Changed default --tun-mtu-extra parameter to 32 from 64.
+* Eliminated reference to malloc.o in configure.ac.
+* Added tun device emulation to the TAP-Win32 driver.
+* Added --route and related options.
+* Added init script for SuSE Linux (Frank Plohmann).
+* Extended option consistency check between peers to function
+ in all crypto modes, including static-key and cleartext modes.
+ Previously only TLS mode was supported. Disable with
+ --disable-occ.
+* Overall, increased the amount of configuration option sanity
+ checking, especially of networking parameters.
+* Added --mtu-test option for empirical MTU measurement.
+* Added Windows-only option --tap-delay to not set the TAP-Win32
+ adapter media state to 'connected' until TCP/UDP connection
+ establishment with peer.
+* Slightly modified --route/--route-delay semantics so that when
+ --route is given without --route-delay, routes are added
+ immediately after tun/tap device open. When --route-delay is
+ specified, routes will be added n seconds after connection
+ initiation, where n is the --route-delay parameter (which
+ can be set to 0).
+* Made TCP framing error into a non-fatal error that triggers a
+ connection reset.
+
+2003.08.28 -- Version 1.5-beta7
+
+* Fixed bug that caused OpenVPN not to respond to exit/restart
+ signals when --resolv-retry is used and a local or remote DNS
+ name cannot be resolved.
+* Exported a series of environmental variables with useful
+ info for scripts. See man page for more info. Based
+ on a suggestion by Anthony Ciaravalo.
+* Moved TCP/UDP socket bind to a point in the initialization
+ before the --up script gets called. This is desirable
+ because (a) a socket bind failure will happen before
+ daemonization, allowing an error status code to be returned
+ to the shell and (b) the possibility is eliminated of a
+ socket bind failure causing the --up script to be run
+ but not the --down script. This change has a side effect
+ that --resolv-retry will no longer work with --local.
+* Fixed bug where if an OpenVPN TCP server went down and back
+ up again, Solaris or FreeBSD clients would fail to reconnect
+ to it.
+* Fixed bug that prevented OpenVPN from being run by
+ inetd/xinetd in TCP mode.
+* Added --log and --log-append options for logging messages to
+ a file.
+* On Windows, check that the current user is a member of the
+ Administrator group before attempting install or uninstall.
+
+2003.08.16 -- Version 1.5-beta6
+
+* Fixed TAP-Win32 driver to properly increment the Rx/Tx count.
+
+2003.08.14 -- Version 1.5-beta5
+
+* Added user-configurability of the TAP-Win32 adapter MTU
+ through the adapter advanced properties page.
+* Added Windows Service support.
+* On Windows, added file association and right-clickability
+ for .ovpn files (OpenVPN config files).
+
+2003.08.05 -- Version 1.5-beta4
+
+* Extra refinements and error checking added to Windows
+ NSIS install script.
+
+2003.08.05 -- Version 1.5-beta3
+
+* Added md5.h include to crypto.c to fix build problem on
+ OpenBSD.
+* Created a Win32 installer using NSIS.
+* Removed DelService command from TAP-Win32 INF file. It appears
+ to be not necessary and it interfered with the ability to
+ uninstall and reinstall the driver without needing to reboot.
+* On Windows version, added "addtap" and "deltapall" batch
+ files to add and delete TAP-Win32 adapter instances.
+
+2003.07.31 -- Version 1.5-beta2
+
+* Renamed INSTALL.w32 to INSTALL-win32.txt and reformatted
+ in Windows ASCII so it's easier to click and view.
+* Added postscript and PDF versions of the HOWTO to the web
+ site (C R Zamana).
+* Merged Michael Clarke's stability patch into TAP-Win32
+ driver which appears to fix the suspend/resume driver bug
+ and significantly improve driver stability.
+* Added Christof Meerwald's Media Status patch to the
+ TAP-Win32 driver which shows the TAP adapter to be
+ disconnected when OpenVPN is not running.
+* Moved socket connect and TCP server listen code to a later
+ point in openvpn() function so that the TCP server listen
+ state is entered after daemonization.
+* Added keyboard shortcuts to simulate signals in the Windows
+ version, see the window title bar for descriptions.
+
+2003.07.24 -- Version 1.5-beta1
+
+* Added TCP support via the new --proto option.
+* Renamed udp-centric options such as --udp-mtu to
+ --link-mtu (old option names preserved for compatibility).
+* Ported to Windows 2000 + XP using mingw and a TAP driver
+ derived from the Cipe-Win32 project by Damion K. Wilson.
+* Added --show-adapters flag for windows version.
+* Reworked the SSL/TLS packet acknowledge code to better
+ handle certain corner cases.
+* Turned off the default enabling of IP forwarding in the
+ sample-scripts/openvpn.init script for Redhat.
+ Forwarding can be enabled by users in their --up scripts
+ or firewall config.
+* Added --up-restart option based on suggestion from Sean
+ Reifschneider.
+* If --dev tap or --dev-type tap is specified, --tun-mtu
+ defaults to 1500 and --tun-mtu-extra defaults to 64.
+* Enabled --verb 5 debugging mode that prints 'R' and 'W'
+ for each packet read or write on the TCP/UDP socket.
+
+2003.08.04 -- Version 1.4.3
+
+* Added md5.h include to crypto.c
+ to fix build problem on OpenBSD.
+
+2003.07.15 -- Version 1.4.2
+
+* Removed adaptive bandwidth from
+ --mtu-dynamic -- its absence appears
+ to work better than its existence (1.4.1.2).
+* Minor changes to --shaper to fix long
+ retransmit timeouts at low bandwidth
+ (1.4.1.2).
+* Added LOG_RW flag to openvpn.h for
+ debugging (1.4.1.2).
+* Silenced spurious configure warnings (1.4.1.2).
+* Backed out --dev-name patch, modified --dev
+ to offer equivalent functionality (1.4.1.4).
+* Added an optional parameter to --daemon and
+ --inetd to support the passing of a custom
+ program name to the system logger (1.4.1.5).
+* Add compiled-in options to the program title
+ (1.4.1.5).
+* Coded the beginnings of a WIN32 port (1.4.1.5).
+* Succeeded in porting to Win32 Mingw environment
+ and running loopback tests (1.4.1.6). Still
+ need a kernel driver for full Win32
+ functionality.
+* Fixed a bug in error.h where
+ HAVE_CPP_VARARG_MACRO_GCC was misspelled.
+ This would have caused a significant slowdown
+ of OpenVPN when built by compilers that
+ lack ISO C99 vararg macros (1.4.1.6).
+* Created an init script for Gentoo Linux
+ in ./gentoo directory (1.4.1.6).
+
+2003.05.15 -- Version 1.4.1
+
+* Modified the Linux 2.4 TUN/TAP open code to
+ fall back to the 2.2 TUN/TAP interface if the
+ open or ioctl fails.
+* Fixed bug when --verb is set to 0 and non-fatal
+ socket errors occur, causing 100% CPU utilization.
+ Occurs on platorms where
+ EXTENDED_SOCKET_ERROR_CAPABILITY is defined,
+ such as Linux 2.4.
+* Fixed typo in tun.c that was preventing
+ OpenBSD build.
+* Added --enable-mtu-dynamic configure option
+ to enable --mtu-dynamic experimental option.
+
+2003.05.07 -- Version 1.4.0
+
+* Added --replay-persist feature to allow replay
+ protection across sessions.
+* Fixed bug where --ifconfig could not be used
+ with --tun-mtu.
+* Added --tun-mtu-extra parameter to deal with
+ the situation where a read on a TUN/TAP device
+ returns more data than the device's MTU size.
+* Fixed bug where some IPv6 support code for
+ Linux was not being properly ifdefed out for
+ Linux 2.2, causing compile errors.
+* Added OPENVPN_EXIT_STATUS_x codes to
+ openvpn.h to control which status value
+ openvpn returns to its caller (such as
+ a shell or inetd/xinetd) for various conditions.
+* Added OPENVPN_DEBUG_COMMAND_LINE flag to
+ openvpn.h to allow debugging in situations
+ where stdout, stderr, and syslog cannot be used
+ for message output, such as when OpenVPN is
+ instantiated by inetd/xinetd.
+* Removed owner-execute permission from file
+ created by static key generator (Herbert Xu
+ and Alberto Gonzalez Iniesta).
+* Added --passtos option to allow IPv4 TOS bits
+ to be passed from TUN/TAP input packets to
+ the outgoing UDP socket (Craig Knox).
+* Added code to prevent open socket file descriptors
+ from being accessible to called scripts.
+* Added --dev-name option (Christian Lademann).
+* Added --mtu-disc option for manual control
+ over MTU options.
+* Show OS MTU value on UDP socket write failures
+ (linux only).
+* Numerous build system and portability
+ fixes (Matthias Andree).
+* Added better sensing of compiler support for
+ variable argument macros, including (a) gcc
+ style, (b) ISO C 1999 style, and (c) no support.
+* Removed generated files from CVS. Note INSTALL
+ file for new CVS build commands.
+* Changed certain internal symbol names
+ for C standards compliance.
+* Added TUN/TAP open code to cycle dynamically
+ through unit numbers until it finds a free
+ unit (based on code from Thomas Gielfeldt
+ and VTun).
+* Added dynamic MTU and fragmenting infrastructure
+ (Experimental). Rebuild with FRAGMENT_ENABLE
+ defined to enable.
+* Minor changes to SSL/TLS negotiation, use
+ exponential backoff on retransmits, and use
+ a smaller MTU size (note that no protocol
+ changes have been made which would break
+ compatibility with 1.3.x).
+* Added --enable-strict-options flag
+ to ./configure. This option will cause
+ a more strict check for options compatibility
+ between peers when SSL/TLS negotiation is used,
+ but should only be used when both OpenVPN peers
+ are of the same version.
+* Reorganization of debugging levels.
+* Added a workaround in configure.ac for
+ default SSL header location on Linux
+ to fix RH9 build problem.
+* Fixed potential deadlock when pthread support
+ is used on OSes that allocate a small socketpair()
+ message buffer.
+* Fixed openvpn.init to be sh compliant
+ (Bishop Clark).
+* Changed --daemon to wait until all
+ initialization is finished before becoming a
+ daemon, for the benefit of initialization
+ scripts that want a useful return status from
+ the openvpn command.
+* Made openvpn.init script more robust, including
+ positive indication of initialization errors
+ in the openvpn daemon and better sanity checks.
+* Changed --chroot to wait until initialization
+ is finished before calling chroot(), and allow
+ the use of --user and --group with --chroot.
+* When syslog logging is enabled (--daemon or
+ --inetd), set stdin/stdout/stderr to point
+ to /dev/null.
+* For inetd instantiations, dup socket descriptor
+ to a >2 value.
+* Fixed bug in verify-cn script, where test would
+ incorrectly fail if CN=x was the last component
+ of the X509 composite string (Anonymous).
+* Added Markus F.X.J. Oberhumer's special
+ license exception to COPYING.
+
+2002.10.23 -- Version 1.3.2
+
+* Added SSL_CTX_set_client_CA_list call
+ to follow the canonical form for TLS initialization
+ recommended by the OpenSSL docs. This change allows
+ better support for intermediate CAs and has no impact
+ on security.
+* Added build-inter script to easy-rsa package, to
+ facilitate the generation of intermediate CAs.
+* Ported to NetBSD (Dimitri Goldin).
+* Fixed minor bug in easy-rsa/sign-req. It refers to
+ openssl.cnf file, instead of $KEY_CONFIG, like all
+ other scripts (Ernesto Baschny).
+* Added --days 3650 to the root CA generation command
+ in the HOWTO to override the woefully small 30 day
+ default (Dominik 'Aeneas' Schnitzer).
+* Fixed bug where --ping-restart would sometimes
+ not re-resolve remote DNS hostname.
+* Added --tun-ipv6 option and related infrastructure
+ support for IPv6 over tun.
+* Added IPv6 over tun support for Linux (Aaron Sethman).
+* Added FreeBSD 4.1.1+ TUN/TAP driver notes to
+ INSTALL (Matthias Andree).
+* Added inetd/xinetd support (--inetd) including
+ documentation in the HOWTO.
+* Added "Important Note on the use of commercial certificate
+ authorities (CAs) with OpenVPN" to HOWTO based on
+ issues raised on the openvpn-users list.
+
+2002.07.10 -- Version 1.3.1
+
+* Fixed bug in openvpn.spec and openvpn.init
+ which caused RPM upgrade to fail.
+
+2002.07.10 -- Version 1.3.0
+
+* Added --dev-node option to allow explicit selection of
+ tun/tap device node.
+* Removed mlockall call from child thread, as it doesn't
+ appear to be necessary (child thread inherits mlockall
+ state from parent).
+* Added --ping-timer-rem which causes timer for --ping-exit
+ and --ping-restart not to run unless we have a remote IP
+ address.
+* Added condrestart to openvpn.init and openvpn.spec
+ (Bishop Clark).
+* Added --ifconfig case for FreeBSD (Matthias Andree).
+* Call openlog with facility=LOG_DAEMON (Matthias Andree).
+* Changed LOG_INFO messages to LOG_NOTICE.
+* Added warning when key files are group/others accessible.
+* Added --single-session flag for TLS mode.
+* Fixed bug where --writepid would segfault if used with
+ an invalid filename.
+* Fixed bug where --ipchange status message was formatted
+ incorrectly.
+* Print more concise error message when system() call
+ fails.
+* Added --disable-occ option.
+* Added --local, --remote, and --ifconfig options sanity
+ check.
+* Changed default UDP MTU to 1300 and TUN/TAP MTU to
+ 1300.
+* Successfully tested with OpenSSL 0.9.7 Beta 2.
+* Broke out debug level definitions to errlevel.h
+* Minor documentation and web site changes.
+* All changes maintain protocol compatibility
+ with OpenVPN versions since 1.1.0, however default
+ MTU changes will require setting the MTU explicitly
+ by command line option, if you want 1.3.0 to
+ communicate with previous versions.
+
+2002.06.12 -- Version 1.2.1
+
+* Added --ping-restart option to restart
+ connection on ping timeout using SIGUSR1
+ logic (Matthias Andree).
+* Added --persist-tun, --persist-key,
+ --persist-local-ip, and --persist-remote-ip
+ options for finer-grained control over SIGUSR1
+ and --ping-restart restarts. To
+ replicate previous SIGUSR1 functionality,
+ use --persist-remote-ip.
+* Changed residual IV fetching code to take
+ IV from tail of ciphertext.
+* Added check to make sure that CFB or OFB
+ cipher modes are only used with SSL/TLS
+ authentication mode, and added a caveat
+ to INSTALL.
+* Changed signal handling during initialization
+ (including re-initialization during restarts)
+ to exit on SIGTERM or SIGINT and ignore other
+ signals which would ordinarily be caught.
+* Added --resolv-retry option to allow
+ retries on hostname resolution.
+* Expanded the --float option to also
+ allow dynamic changes in source port number
+ on incoming datagrams.
+* Added --mute option to limit repetitive
+ logging of similar message types.
+* Added --group option to downgrade GID
+ after initialization.
+* Try to set ifconfig path automatically
+ in configure.
+* Added --ifconfig code for Mac OS X
+ (Christoph Pfisterer).
+* Moved "Peer Connection Initiated" message
+ to --verb level 1.
+* Successfully tested with
+ OpenSSL 0.9.7 Beta 1 and AES cipher.
+* Added RPM notes to INSTALL.
+* Added ACX_PTHREAD (from the autoconf
+ macro archive) to configure.ac
+ to figure out the right pthread
+ options for a given platform.
+* Broke out macro definitions from
+ configure.ac to acinclude.m4.
+* Minor changes to docs and HOWTO.
+* All changes maintain protocol compatibility
+ with OpenVPN versions since 1.1.0.
+
+2002.05.22 -- Version 1.2.0
+
+* Added configuration file support via
+ the --config option.
+* Added pthread support to improve latency.
+ With pthread support, OpenVPN
+ will offload CPU-intensive tasks such as RSA
+ key number crunching to a background thread
+ to improve tunnel packet forwarding
+ latency. pthread support can be enabled
+ with the --enable-pthread configure option.
+ Pthread support is currently available
+ only for Linux and Solaris.
+* Added --dev-type option so that tun/tap
+ device names don't need to begin with
+ "tun" or "tap".
+* Added --writepid option to write main
+ process ID to a file.
+* Numerous portability fixes to ease
+ porting to other OSes including changing
+ all network types to uint8_t and uint32_t,
+ and not assuming that time_t is 32 bits.
+* Backported to OpenSSL 0.9.5.
+* Ported to Solaris.
+* Finished OpenBSD port except for
+ pthread support.
+* Added initialization script:
+ sample-scripts/openvpn.init
+ (Douglas Keller)
+* Ported to Mac OS X (Christoph Pfisterer).
+* Improved resilience to DoS attacks when
+ TLS mode is used without --remote or
+ --tls-auth, or when --float is used
+ with --remote. Note however that the best
+ defense against DoS attacks in TLS mode
+ is to use --tls-auth.
+* Eliminated automake/autoconf dependency
+ for non-developers.
+* Ported configure.in to configure.ac
+ and autoconf 2.50+.
+* SIGHUP signal now causes OpenVPN to restart
+ and re-read command line and or config file,
+ in conformance with canonical daemon behaviour.
+* SIGUSR1 now does what SIGHUP did in
+ version 1.1.1 and earlier -- close and reopen
+ the UDP socket for use when DHCP changes
+ host's IP address and preserve most recently
+ authenticated peer address without rereading
+ config file.
+* SIGUSR2 added -- outputs current statistics,
+ including compression statistics.
+* All changes maintain protocol compatibility
+ with 1.1.1 and 1.1.0.
+
+2002.04.22 -- Version 1.1.1
+
+* Added --ifconfig option to automatically configure
+ TUN device.
+* Added inactivity disconnect (--inactive
+ and --ping-exit options).
+* Added --ping option to keep stateful firewalls
+ from timing out.
+* Added sanity check to command line parser to
+ err if any TLS options are used in non-TLS mode.
+* Fixed build problem with compiler environments that
+ define printf as a macro.
+* Fixed build problem on linux systems that have
+ an integrated TUN/TAP driver but lack the persistent
+ tunnel feature (TUNSETPERSIST). Some linux kernels
+ >= 2.4.0 and < 2.4.7 fall into this category.
+* Changed all calls to EVP_CipherInit to use explicit
+ encrypt/decrypt mode in order to fix problem with
+ IDEA-CBC and AES-256-CBC ciphers.
+* Minor changes to control channel transmit limiter
+ algorithm to fix problem where TLS control channel
+ might not renegotiate within the default 60 second window.
+* Simplified man page examples by taking advantage
+ of the new --ifconfig option.
+* Minor changes to configure.in to check more
+ rigourously for OpenSSL 0.9.6 or greater.
+* Put back openvpn.spec, eliminated
+ openvpn.spec.in.
+* Modified openvpn.spec to reflect new automake-based
+ build environment (Bishop Clark).
+* Other documentation changes.
+* Added --test-crypto option for debugging.
+* Added "missing" and "mkinstalldirs" automake
+ support files.
+
+
+2002.04.09 -- Version 1.1.0
+
+* Strengthened replay protection and IV handling,
+ extending it fully to both static key and
+ TLS dynamic key exchange modes.
+* Added --mlock option to disable paging and ensure that key
+ material and tunnel data is never paged to disk.
+* Added optional traffic shaping feature to cap the maximum
+ data rate of the tunnel.
+* Converted to automake (The Platypus Brothers 2002-04-01).
+* Ported to OpenBSD by Janne Johansson.
+* Added --tun-af-inet option to work around an incompatibility
+ between Linux and BSD tun drivers.
+* Sequence number-based replay protection using the
+ IPSec sliding window model is now the default,
+ disable with --no-replay.
+* Explicit IV is now the default, disable with --no-iv.
+* Disabled all cipher modes except CBC, CFB, and OFB.
+* In CBC mode, use explicit IV and carry forward residuals,
+ using IPSec model.
+* In CFB/OFB mode, IV is timestamp, sequence number.
+* Eliminated --packet-id, --timestamp, and max-delta parameter to
+ the --tls-auth option as they are now supplanted by improved
+ replay code which is enabled by default.
+* Eliminated --rand-iv as it is now obsolete with improved
+ IV code.
+* Eliminated --reneg-err option as it increases vulnerability
+ to DoS attacks.
+* Added weak key check for DES ciphers.
+* --tls-freq option is no longer specified on the command line,
+ instead it now inherits its parameter from the
+ --tls-timeout option.
+* Fixed bug that would try to free memory on exit that was
+ never malloced if --comp-lzo was not specified.
+* Errata fixed in the man page examples: "test-ca" should be
+ "tmp-ca".
+* Updated manual page.
+* Preliminary work in porting to OpenSSL 0.9.7.
+* Changed license to allowing linking with OpenSSL.
+
+2002.03.29 -- Version 1.0.3
+
+* Fixed a problem in configure with library ordering on the
+ command line.
+
+2002.03.28 -- Version 1.0.2
+
+* Improved the efficiency of the inner event loop.
+* Fixed a minor bug with timeout handling.
+* Improved the build system to build on RH 6.2 through 7.2.
+* Added an openvpn.spec file for RPM builders (Bishop Clark).
+
+2002.03.23 -- Version 1.0
+
+* Added TLS-based authentication and key exchange.
+* Added gremlin mode to stress test.
+* Wrote man page.
+
+2001.12.26 -- Version 0.91
+
+* Added any choice of cipher or HMAC digest.
+
+2001.5.13 -- Version 0.90
+
+* Initial release.
+* IP tunnel over UDP, with blowfish cipher and SHA1 HMAC signature.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ab0d671
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,343 @@
+Installation instructions for OpenVPN, a Secure Tunneling Daemon
+
+Copyright (C) 2002-2010 OpenVPN Technologies, 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
+
+ Windows MinGW, using MSYS bash shell:
+ ./domake-win (see comments in the script for more info)
+
+ Windows Visual Studio:
+ python win\build_all.py
+
+*************************************************************************
+
+To download OpenVPN, go to:
+
+ http://openvpn.net/download.html
+
+For step-by-step installation instructions with real-world
+examples see:
+
+ http://openvpn.net/howto.html
+
+For examples see:
+
+ http://openvpn.net/examples.html
+
+*************************************************************************
+
+SUPPORTED PLATFORMS:
+ (1) Linux 2.2+
+ (2) Solaris
+ (3) OpenBSD 3.0+ (Comes with OpenSSL and TUN devices by default)
+ (4) Mac OS X Darwin
+ (5) FreeBSD
+ (6) NetBSD
+ (7) Windows (WinXP and higher)
+
+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.5 or higher
+ required, available from http://www.openssl.org/
+ (2) 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 "--with-lzo-headers" and "--with-lzo-lib" directives to
+ "configure", pointing to /usr/local/include and /usr/local/lib
+ respectively since gcc will not find them otherwise.
+ (3) Pthread library.
+
+OPTIONAL (for developers only):
+ (1) Autoconf 2.50 or higher + Automake 1.5 or higher
+ -- available from http://www.gnu.org/software/software.html
+ (2) Dmalloc library
+ -- available from http://dmalloc.com/
+
+*************************************************************************
+
+CHECK OUT SOURCE FROM SUBVERSION REPOSITORY:
+
+ Check out stable version:
+
+ svn checkout http://svn.openvpn.net/projects/openvpn/trunk/openvpn openvpn
+
+ Check out beta21 branch:
+
+ svn checkout http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn openvpn
+
+*************************************************************************
+
+BUILD COMMANDS FROM TARBALL:
+
+ ./configure
+ make
+ make install
+
+*************************************************************************
+
+BUILD COMMANDS FROM SUBVERSION REPOSITORY CHECKOUT:
+
+ autoreconf -i -v
+ ./configure
+ make
+ make install
+
+*************************************************************************
+
+BUILD A TARBALL FROM SUBVERSION REPOSITORY CHECKOUT:
+
+ autoreconf -i -v
+ ./configure
+ make dist
+
+*************************************************************************
+
+LOOPBACK 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-config-files/loopback-client (In one window)
+./openvpn --config sample-config-files/loopback-server (Simultaneously in another window)
+
+*************************************************************************
+
+OPTIONS for ./configure:
+
+ --enable-pthread Compile pthread support for
+ improved latency during SSL/TLS key
+ negotiations (Linux or Solaris only)
+
+ --disable-lzo Do not compile LZO compression support
+ --disable-crypto Do not compile OpenSSL crypto support
+ --disable-ssl Do not compile OpenSSL SSL support for
+ TLS-based key exchange
+
+ --with-ssl-headers=DIR Crypto/SSL Include files location
+ --with-ssl-lib=DIR Crypto/SSL Library location
+ --with-lzo-headers=DIR LZO Include files location
+ --with-lzo-lib=DIR LZO Library location
+
+ --with-ifconfig-path=PATH Path to ifconfig tool (only need to
+ specify if in a non-standard location)
+
+ --with-leak-check=TYPE Build with memory leak checking
+ TYPE = dmalloc or ssl
+
+ --enable-strict Enable strict compiler warnings
+
+ --enable-strict-options Enable strict options check between peers
+
+*************************************************************************
+
+BUILDING ON LINUX 2.4+ FROM RPM
+
+You can build a binary RPM directly from the OpenVPN tarball file:
+
+ rpmbuild -tb [tarball]
+
+This command will build a binary RPM file and place it in the system
+RPM directory. You can then install the RPM with the standard RPM
+install command:
+
+ rpm -ivh [binary-rpm]
+
+When you install the binary RPM, it will install
+sample-scripts/openvpn.init, which can be used to
+automatically start or stop one or more OpenVPN tunnels on system
+startup or shutdown, based on OpenVPN .conf files in /etc/openvpn.
+See the comments in openvpn.init for more information.
+
+Installing the RPM will also configure the TUN/TAP device node
+for linux 2.4.
+
+Note that the current openvpn.spec file, which instructs the rpm tool
+how to build a package, will build OpenVPN with all options enabled,
+including OpenSSL, LZO, and pthread linkage. Therefore all of
+these packages will need to be present prior to the RPM build, unless
+you edit the openvpn.spec file.
+
+*************************************************************************
+
+TUN/TAP Driver Configuration:
+
+* Linux 2.4 or higher (with integrated TUN/TAP driver):
+
+ (1) make device node: mknod /dev/net/tun c 10 200
+ (2a) add to /etc/modules.conf: alias char-major-10-200 tun
+ (2b) load driver: modprobe tun
+ (3) enable routing: echo 1 > /proc/sys/net/ipv4/ip_forward
+
+ Note that either of steps (2a) or (2b) is sufficient. While (2a)
+ only needs to be done once per install, (2b) 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.
+
+* Linux 2.2 or Solaris:
+
+ You should obtain
+ version 1.1 of the TUN/TAP driver from
+ http://vtun.sourceforge.net/tun/
+ and follow the installation instructions.
+
+ If you use OpenVPN on Linux 2.2 or 2.4 or Solaris, you may be
+ suffering from a bug which causes connections to hang under heavy load.
+ The symptoms are very similar to the MTU problems discussed frequently
+ in the OpenVPN mailing lists. But it turns out that this bug is not caused by
+ MTU problems. It's a bug in the tun/tap driver. A patch is provided here:
+
+ http://openvpn.net/patch/tun-sb.patch
+
+* Solaris
+
+ For 64 bit, I used the tun-1.1.tar.gz source and compiled it.
+
+ Of course there is a but :)
+ In the tun-1-1\solaris\Makefile I changed a line so it compiles with 64 bit
+
+ CFLAGS = $(DEFS) -m64 -O2 -Wall -D_KERNEL -I.
+
+ I just added -m64 and it worked.
+
+ The tun driver works fine as said previously, however we noticed there is a
+ minor problem when creating multiple tunnels on Solaris.
+ Mr Tycho Fruru changed the code in tun.c file where he locked the tun device
+ number to -1. This way it is impossible to specify the name of the tun device
+ but it is still possible to have multiple devices.
+ The modification will increment automatically meaning starting from tun0 --->
+ tunX I know you are not responsible for the tun coding but if you think the
+ modification can be useful for you feel free to use it.
+
+ http://openvpn.net/solaris/tun.c
+
+* FreeBSD 4.1.1+:
+
+ 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 ships with tun0 and tun1 installed by default on pre-3.5 systems,
+ while 3.5 and later have 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.
+
+* Mac OS X:
+
+ 2005.02.13: Angelo Laub has developed a GUI for OS X:
+
+ http://rechenknecht.net/OpenVPN-GUI/
+
+ 2004.10.26: Mattias Nissler has developed a new TUN/TAP driver for
+ MAC OS X:
+
+ http://www-user.rhrk.uni-kl.de/~nissler/tuntap/
+
+ Christoph Pfisterer's old TUN driver can be obtained at
+ http://chrisp.de/en/projects/tunnel.html -- note that it
+ is no longer being maintained.
+
+* Solaris9 Sparc/64
+
+ The kernel module for solaris
+ can be generated by adding the -m64 switch to a modern
+ gcc compiler (I'm using 3.2) The resulting kernel driver
+ needs to be manually copied to /kernel/drv/sparcv9/ and then a
+ reconfiguration reboot. (boot -r).
+
+* Windows XP/2003/Vista
+
+ See domake-win for building instructions.
+ See INSTALL-win32.txt for usage info.
+
+ See the man page for more information, usage examples, and
+ information on firewall configuration.
+
+*************************************************************************
+
+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.
+
+******************************************************************************
+
+Subject: [Openvpn-users] Re: Windows XP 64 bit
+From: Hypherion
+Date: Thu, 14 Apr 2005 07:01:17 +0000 (UTC)
+
+Well I managed to build a Windows XP 64 bit driver myself and it's working
+great, I can connect to my server again :)
+
+I had to use the WinDDK for Windows 2003 Service Pack 1 and just built the
+driver in the Windows 2003 AMD64 environment. I had to comment out the
+MAPINFO:FIXUPS directive in the SOURCES file.
+
+Then I copied and renamed (devcon.exe/tapinstall.exe) from
+C:\WINDDK\3790.1830\tools\devcon\amd64.
+
+I had to edit the file OemWin2k.inf and change the Manufactured + Product
+Section to:
+
+[Manufacturer]
+ %Provider% = tap0901, NTamd64
+
+[tap0901.NTamd64]
+ %DeviceDescription% = tap0901.ndi, tap0901
diff --git a/INSTALL-win32.txt b/INSTALL-win32.txt
new file mode 100644
index 0000000..5a0f3a9
--- /dev/null
+++ b/INSTALL-win32.txt
@@ -0,0 +1,23 @@
+IMPORTANT NOTE FOR WINDOWS VISTA/7 USERS
+
+Note that on Windows Vista, you will need to run the OpenVPN
+GUI with administrator privileges, so that it can add routes
+to the routing table that are pulled from the OpenVPN server.
+You can do this by right-clicking on the OpenVPN GUI
+desktop icon, and selecting "Run as administrator".
+
+GENERAL QUICKSTART FOR WINDOWS
+
+The OpenVPN Client requires a configuration file
+and key/certificate files. You should obtain
+these and save them to OpenVPN's configuration
+directory, usually C:\Program Files\OpenVPN\config.
+
+You can run OpenVPN as a Windows system service or by using
+the client GUI. To use the OpenVPN GUI, double click on the
+desktop icon or start menu icon. The OpenVPN GUI is a
+system-tray applet, so an icon for the GUI will appear in
+the lower-right corner of the screen. Right click on the
+system tray icon, and a menu should appear showing the names
+of your OpenVPN configuration files, and giving you the
+option to connect.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..f311837
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,164 @@
+#
+# 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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+LDADD = @LIBOBJS@
+.PHONY: plugin
+
+# This option prevents autoreconf from overriding our COPYING and
+# INSTALL targets:
+AUTOMAKE_OPTIONS = foreign
+
+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)/depcomp $(srcdir)/aclocal.m4 \
+ $(srcdir)/config.guess $(srcdir)/config.sub \
+ $(srcdir)/openvpn.spec
+CLEANFILES = openvpn.8.html configure.h
+
+EXTRA_DIST = \
+ easy-rsa \
+ sample-config-files \
+ sample-keys \
+ sample-scripts \
+ suse \
+ tap-win32 \
+ contrib \
+ debug \
+ plugin \
+ win
+
+SUBDIRS = \
+ images \
+ service-win32 \
+ install-win32
+
+TESTS = t_client.sh t_lpback.sh t_cltsrv.sh
+sbin_PROGRAMS = openvpn
+
+dist_doc_DATA = \
+ management/management-notes.txt
+
+dist_noinst_SCRIPTS = \
+ $(TESTS) \
+ doclean \
+ domake-win \
+ t_cltsrv-down.sh \
+ configure_h.awk configure_log.awk
+
+dist_noinst_DATA = \
+ openvpn.spec \
+ COPYRIGHT.GPL \
+ PORTS \
+ INSTALL-win32.txt \
+ service-win32/msvc.mak
+
+openvpn_SOURCES = \
+ base64.c base64.h \
+ basic.h \
+ buffer.c buffer.h \
+ circ_list.h \
+ common.h \
+ crypto.c crypto.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 \
+ mroute.c mroute.h \
+ mss.c mss.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 \
+ pkcs11.c pkcs11.h \
+ openvpn.c openvpn.h \
+ openvpn-plugin.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 \
+ ieproxy.h ieproxy.c \
+ 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 \
+ status.c status.h \
+ syshead.h \
+ tun.c tun.h \
+ win32.h win32.c \
+ cryptoapi.h cryptoapi.c
+
+nodist_openvpn_SOURCES = configure.h
+options.$(OBJEXT): configure.h
+
+configure.h: Makefile
+ awk -f $(srcdir)/configure_h.awk config.h > $@
+ awk -f $(srcdir)/configure_log.awk config.log >> $@
+
+dist-hook:
+ cd $(distdir) && for i in $(EXTRA_DIST) $(SUBDIRS) ; do find $$i -name .svn -type d -prune -exec rm -rf '{}' ';' ; rm -f `find $$i -type f | grep -E '(^|\/)\.?\#|\~$$|\.s?o$$'` ; done
+
+if WIN32
+dist_noinst_DATA += openvpn.8
+nodist_html_DATA = openvpn.8.html
+openvpn.8.html: $(srcdir)/openvpn.8
+ $(MAN2HTML) < $(srcdir)/openvpn.8 > openvpn.8.html
+else
+dist_man_MANS = openvpn.8
+endif
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..87cabd4
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,1243 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# 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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+# Copyright (C) 2010 David Sommerseth <dazo@users.sourceforge.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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = openvpn$(EXEEXT)
+@WIN32_TRUE@am__append_1 = openvpn.8
+subdir = .
+DIST_COMMON = README $(am__configure_deps) \
+ $(am__dist_noinst_DATA_DIST) $(dist_doc_DATA) $(dist_man_MANS) \
+ $(dist_noinst_SCRIPTS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+ $(srcdir)/openvpn.spec.in $(srcdir)/t_client.sh.in \
+ $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
+ config.guess config.sub depcomp install-sh memcmp.c missing
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = t_client.sh openvpn.spec
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(docdir)" "$(DESTDIR)$(htmldir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am_openvpn_OBJECTS = base64.$(OBJEXT) buffer.$(OBJEXT) \
+ crypto.$(OBJEXT) dhcp.$(OBJEXT) error.$(OBJEXT) \
+ event.$(OBJEXT) fdmisc.$(OBJEXT) forward.$(OBJEXT) \
+ fragment.$(OBJEXT) gremlin.$(OBJEXT) helper.$(OBJEXT) \
+ httpdigest.$(OBJEXT) lladdr.$(OBJEXT) init.$(OBJEXT) \
+ interval.$(OBJEXT) list.$(OBJEXT) lzo.$(OBJEXT) \
+ manage.$(OBJEXT) mbuf.$(OBJEXT) misc.$(OBJEXT) \
+ mroute.$(OBJEXT) mss.$(OBJEXT) mtcp.$(OBJEXT) mtu.$(OBJEXT) \
+ mudp.$(OBJEXT) multi.$(OBJEXT) ntlm.$(OBJEXT) occ.$(OBJEXT) \
+ pkcs11.$(OBJEXT) openvpn.$(OBJEXT) options.$(OBJEXT) \
+ otime.$(OBJEXT) packet_id.$(OBJEXT) perf.$(OBJEXT) \
+ pf.$(OBJEXT) ping.$(OBJEXT) plugin.$(OBJEXT) pool.$(OBJEXT) \
+ proto.$(OBJEXT) proxy.$(OBJEXT) ieproxy.$(OBJEXT) ps.$(OBJEXT) \
+ push.$(OBJEXT) reliable.$(OBJEXT) route.$(OBJEXT) \
+ schedule.$(OBJEXT) session_id.$(OBJEXT) shaper.$(OBJEXT) \
+ sig.$(OBJEXT) socket.$(OBJEXT) socks.$(OBJEXT) ssl.$(OBJEXT) \
+ status.$(OBJEXT) tun.$(OBJEXT) win32.$(OBJEXT) \
+ cryptoapi.$(OBJEXT)
+nodist_openvpn_OBJECTS =
+openvpn_OBJECTS = $(am_openvpn_OBJECTS) $(nodist_openvpn_OBJECTS)
+openvpn_LDADD = $(LDADD)
+openvpn_DEPENDENCIES = @LIBOBJS@
+SCRIPTS = $(dist_noinst_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(openvpn_SOURCES) $(nodist_openvpn_SOURCES)
+DIST_SOURCES = $(openvpn_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS)
+am__dist_noinst_DATA_DIST = openvpn.spec COPYRIGHT.GPL PORTS \
+ INSTALL-win32.txt service-win32/msvc.mak openvpn.8
+DATA = $(dist_doc_DATA) $(dist_noinst_DATA) $(nodist_html_DATA)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir dist dist-all distcheck
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d "$(distdir)" \
+ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr "$(distdir)"; }; }
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+IFCONFIG = @IFCONFIG@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPROUTE = @IPROUTE@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAN2HTML = @MAN2HTML@
+MKDIR_P = @MKDIR_P@
+NETSTAT = @NETSTAT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+ROUTE = @ROUTE@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAP_ID = @TAP_ID@
+TAP_WIN32_MIN_MAJOR = @TAP_WIN32_MIN_MAJOR@
+TAP_WIN32_MIN_MINOR = @TAP_WIN32_MIN_MINOR@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+win32datadir = @win32datadir@
+LDADD = @LIBOBJS@
+
+# This option prevents autoreconf from overriding our COPYING and
+# INSTALL targets:
+AUTOMAKE_OPTIONS = foreign
+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)/depcomp $(srcdir)/aclocal.m4 \
+ $(srcdir)/config.guess $(srcdir)/config.sub \
+ $(srcdir)/openvpn.spec
+
+CLEANFILES = openvpn.8.html configure.h
+EXTRA_DIST = \
+ easy-rsa \
+ sample-config-files \
+ sample-keys \
+ sample-scripts \
+ suse \
+ tap-win32 \
+ contrib \
+ debug \
+ plugin \
+ win
+
+SUBDIRS = \
+ images \
+ service-win32 \
+ install-win32
+
+TESTS = t_client.sh t_lpback.sh t_cltsrv.sh
+dist_doc_DATA = \
+ management/management-notes.txt
+
+dist_noinst_SCRIPTS = \
+ $(TESTS) \
+ doclean \
+ domake-win \
+ t_cltsrv-down.sh \
+ configure_h.awk configure_log.awk
+
+dist_noinst_DATA = openvpn.spec COPYRIGHT.GPL PORTS INSTALL-win32.txt \
+ service-win32/msvc.mak $(am__append_1)
+openvpn_SOURCES = \
+ base64.c base64.h \
+ basic.h \
+ buffer.c buffer.h \
+ circ_list.h \
+ common.h \
+ crypto.c crypto.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 \
+ mroute.c mroute.h \
+ mss.c mss.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 \
+ pkcs11.c pkcs11.h \
+ openvpn.c openvpn.h \
+ openvpn-plugin.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 \
+ ieproxy.h ieproxy.c \
+ 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 \
+ status.c status.h \
+ syshead.h \
+ tun.c tun.h \
+ win32.h win32.c \
+ cryptoapi.h cryptoapi.c
+
+nodist_openvpn_SOURCES = configure.h
+@WIN32_TRUE@nodist_html_DATA = openvpn.8.html
+@WIN32_FALSE@dist_man_MANS = openvpn.8
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+t_client.sh: $(top_builddir)/config.status $(srcdir)/t_client.sh.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+openvpn.spec: $(top_builddir)/config.status $(srcdir)/openvpn.spec.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+openvpn$(EXEEXT): $(openvpn_OBJECTS) $(openvpn_DEPENDENCIES)
+ @rm -f openvpn$(EXEEXT)
+ $(LINK) $(openvpn_OBJECTS) $(openvpn_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memcmp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cryptoapi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdmisc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/forward.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fragment.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gremlin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpdigest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ieproxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interval.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lladdr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lzo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbuf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mroute.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mss.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mtcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mtu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mudp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/multi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ntlm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/occ.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_id.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ping.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkcs11.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ps.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/push.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reliable.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/schedule.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session_id.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shaper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win32.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+install-man8: $(dist_man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+install-dist_docDATA: $(dist_doc_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)"
+ @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \
+ done
+
+uninstall-dist_docDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(docdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(docdir)" && rm -f $$files
+install-nodist_htmlDATA: $(nodist_html_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(htmldir)" || $(MKDIR_P) "$(DESTDIR)$(htmldir)"
+ @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(htmldir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(htmldir)" || exit $$?; \
+ done
+
+uninstall-nodist_htmlDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_html_DATA)'; test -n "$(htmldir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(htmldir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(htmldir)" && rm -f $$files
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ echo "$$grn$$dashes"; \
+ else \
+ echo "$$red$$dashes"; \
+ fi; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes$$std"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ $(am__remove_distdir)
+ test -d "$(distdir)" || mkdir "$(distdir)"
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+ -test -n "$(am__skip_mode_fix)" \
+ || find "$(distdir)" -type d ! -perm -755 \
+ -exec chmod u+rwx,go+rx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-xz: distdir
+ tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.xz*) \
+ xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ test -d $(distdir)/_build || exit 0; \
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && am__cwd=`pwd` \
+ && $(am__cd) $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+ && cd "$$am__cwd" \
+ || exit 1
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @$(am__cd) '$(distuninstallcheck_dir)' \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(MANS) $(DATA) config.h
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(htmldir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-dist_docDATA install-man \
+ install-nodist_htmlDATA
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man: install-man8
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-dist_docDATA uninstall-man \
+ uninstall-nodist_htmlDATA uninstall-sbinPROGRAMS
+
+uninstall-man: uninstall-man8
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check-am \
+ ctags-recursive install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am am--refresh check check-TESTS check-am clean \
+ clean-generic clean-sbinPROGRAMS ctags ctags-recursive dist \
+ dist-all dist-bzip2 dist-gzip dist-hook dist-lzma dist-shar \
+ dist-tarZ dist-xz dist-zip distcheck distclean \
+ distclean-compile distclean-generic distclean-hdr \
+ distclean-tags distcleancheck distdir distuninstallcheck dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dist_docDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man8 install-nodist_htmlDATA install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
+ ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-dist_docDATA uninstall-man uninstall-man8 \
+ uninstall-nodist_htmlDATA uninstall-sbinPROGRAMS
+
+.PHONY: plugin
+options.$(OBJEXT): configure.h
+
+configure.h: Makefile
+ awk -f $(srcdir)/configure_h.awk config.h > $@
+ awk -f $(srcdir)/configure_log.awk config.log >> $@
+
+dist-hook:
+ cd $(distdir) && for i in $(EXTRA_DIST) $(SUBDIRS) ; do find $$i -name .svn -type d -prune -exec rm -rf '{}' ';' ; rm -f `find $$i -type f | grep -E '(^|\/)\.?\#|\~$$|\.s?o$$'` ; done
+@WIN32_TRUE@openvpn.8.html: $(srcdir)/openvpn.8
+@WIN32_TRUE@ $(MAN2HTML) < $(srcdir)/openvpn.8 > openvpn.8.html
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/PORTS b/PORTS
new file mode 100644
index 0000000..735493c
--- /dev/null
+++ b/PORTS
@@ -0,0 +1,94 @@
+OpenVPN
+Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+
+ OpenVPN has been written to try to avoid features
+ that are not standardized well across different
+ OSes, so porting OpenVPN itself will probably be
+ straightforward if a tun or tap driver already exists.
+
+ Where special OS features are used, they are usually
+ bracketed with #ifdef HAVE_SOME_FUNCTION.
+
+PLATFORM STATUS:
+
+ * Linux 2.2+ (supported)
+ * Solaris (supported)
+ * OpenBSD 3.0 (supported but pthreads are broken)
+ * Max OS X Darwin
+ * FreeBSD
+ * NetBSD
+ * Windows
+ * 64 bit platforms -- I have heard reports that
+ OpenVPN runs on Alpha Linux and FreeBSD.
+ * ARM -- I have heard of at least one case
+ where OpenVPN was successfully built and
+ run on the ARM architecture.
+
+PORTING NOTES:
+
+ * Make sure that OpenSSL will build on your
+ platform.
+ * Make sure that a tun or tap virtual device
+ driver exists for your platform. See
+ http://vtun.sourceforge.net/tun/ for examples
+ of tun and tap drivers that have been written
+ for Linux, Solaris, and FreeBSD.
+ * Make sure you have autoconf 2.50+ and
+ automake 1.6+.
+ * Edit configure.ac, adding platform specific
+ config code, and a TARGET_YOUROS define.
+ * Add platform-specific includes to syshead.h.
+ * Add an #ifdef TARGET_YOUROS to the do_ifconfig()
+ function in tun.c to generate a correct "ifconfig"
+ command for your platform. Note that OpenVPN
+ determines the ifconfig path at ./configure time.
+ * Add an ifconfig_order() variant for your OS so
+ openvpn knows whether to call ifconfig before
+ or after tun/tap dev open.
+ * Add an #ifdef TARGET_YOUROS block in tun.c and define
+ the open_tun, close_tun, read_tun, and write_tun
+ functions. If your tun/tap virtual device is
+ sufficiently generic, you may be able to use the
+ default case.
+ * Add appropriate code to route.c to handle
+ the route command on your platform. This
+ is necessary for the --route option to
+ work correctly.
+ * After you successfully build OpenVPN, run
+ the loopback tests as described in INSTALL.
+ * For the next test, confirm that the UDP socket
+ functionality is working independently of the
+ tun device, by doing something like:
+ ./openvpn --remote localhost --verb 9 --ping 1 --dev null
+ * Now try with --remote [a real host]
+ * Now try with a real tun/tap device, you will
+ need to figure out the appropriate ifconfig
+ command to use once openvpn has opened the tun/tap
+ device.
+ * Once you have simple tests working on the tun device,
+ try more complex tests such as using TLS mode.
+ * Stress test the link by doing ping -f across it.
+ * Make sure that packet fragmenting is happening
+ correctly by doing a ping -s 2000 or higher.
+ * Ensure that OpenVPN on your platform will talk
+ to OpenVPN on other platforms such as Linux.
+ Some tun/tap driver implementations will prepend
+ unnecessary stuff onto the datagram that must be
+ disabled with an explicit ioctl call if cross-platform
+ compatibility is to be preserved. You can see some
+ examples of this in tun.c.
+ * If your system supports pthreads, try building
+ with ./configure --enable-pthread and do a stress
+ test in TLS mode.
+ * Try the ultimate stress test which is --gremlin
+ --reneg-sec 10 in TLS mode (preferably with pthreads
+ enabled), then do a flood ping across the tunnel
+ (ping -f remote-endpoint) in both directions and let
+ it run overnight. --gremlin will induce massive
+ corruption and packet loss, but you win if you
+ wake up the next morning and both peers are still
+ running and occasionally even succeeding in their
+ attempted once-per-10-seconds TLS handshake.
+ * When it's working, submit your patch to
+ <openvpn-devel@lists.sourceforge.net>
+ and rejoice :)
diff --git a/README b/README
new file mode 100644
index 0000000..2c5e6f6
--- /dev/null
+++ b/README
@@ -0,0 +1,75 @@
+OpenVPN -- A Secure tunneling daemon
+
+Copyright (C) 2002-2010 OpenVPN Technologies, 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.
+
+*************************************************************************
+
+For the latest version of OpenVPN, go to:
+
+ http://openvpn.net/
+
+To Build and Install,
+
+ ./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
+
+For a description of OpenVPN's underlying protocol,
+ see the file ssl.h included in the source distribution.
+
+*************************************************************************
+
+Other Files & Directories:
+
+* INSTALL-win32.txt -- installation instructions
+ for Windows
+
+* configure.ac -- script to rebuild our configure
+ script and makefile.
+
+* openvpn.spec -- RPM Spec file
+ To build an OpenVPN binary RPM, use the command:
+
+ rpmbuild -tb [tarball]
+
+ When you install the binary RPM, it will automatically
+ install sample-scripts/openvpn.init (see below)
+
+* sample-scripts/openvpn.init
+
+ A sample init script for OpenVPN. See the file for
+ comments and additional information.
+
+* 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-keys/
+
+ Sample RSA keys and certificates. DON'T USE THESE FILES
+ FOR ANYTHING OTHER THAN TESTING BECAUSE THEY ARE TOTALLY INSECURE.
+
+* sample-config-files/
+
+ A collection of OpenVPN config files and scripts from
+ the HOWTO at http://openvpn.net/howto.html
+
+* easy-rsa/
+
+ A simple guide to RSA key management, scripts included.
+ Also see http://openvpn.net/easyrsa.html
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..185907f
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,127 @@
+dnl Special Autoconf Macros for OpenVPN
+
+dnl OPENVPN_ADD_LIBS(LIB)
+AC_DEFUN([OPENVPN_ADD_LIBS], [
+ LIBS="$1 $LIBS"
+])
+
+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], [
+ AC_MSG_RESULT([checking for C compiler empty array support])
+ AC_COMPILE_IFELSE(
+ [
+ struct { int foo; int bar[[0]]; } mystruct;
+ ], [
+ AC_DEFINE_UNQUOTED(EMPTY_ARRAY_SIZE, 0, [Dimension to use for empty array declaration])
+ ], [
+ AC_COMPILE_IFELSE(
+ [
+ struct { int foo; int bar[[]]; } mystruct;
+ ], [
+ AC_DEFINE_UNQUOTED(EMPTY_ARRAY_SIZE,, [Dimension to use for empty array declaration])
+ ], [
+ AC_MSG_ERROR([C compiler is unable to creaty empty arrays])
+ ])
+ ])
+ ]
+)
+
+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, [dnl
+ AC_COMPILE_IFELSE([
+ #define macro(a, b...) func(a, b)
+ int func(int a, int b, int c);
+ int test() { return macro(1, 2, 3); }
+ ], [ VAR=yes ], [VAR=no])])
+ if test $VAR = yes ; then
+ AC_DEFINE([HAVE_CPP_VARARG_MACRO_GCC], 1,
+ [Define to 1 if your compiler supports GNU GCC-style variadic macros])
+ fi
+ 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, [dnl
+ AC_COMPILE_IFELSE([
+#define macro(a, ...) func(a, __VA_ARGS__)
+ int func(int a, int b, int c);
+ int test() { return macro(1, 2, 3); }
+ ], [ VAR=yes ], [VAR=no])])
+ if test $VAR = yes ; then
+ AC_DEFINE([HAVE_CPP_VARARG_MACRO_ISO], 1,
+ [Define to 1 if your compiler supports ISO C99 variadic macros])
+ fi
+ AS_VAR_POPDEF([VAR])dnl
+])
+
+dnl -- The following is taken from 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([TYPE_SOCKLEN_T],
+[
+ AC_CHECK_TYPE([socklen_t], ,[
+ AC_MSG_CHECKING([for socklen_t equivalent])
+ AC_CACHE_VAL([curl_cv_socklen_t_equiv],
+ [
+ case "$host" in
+ *-mingw*) curl_cv_socklen_t_equiv=int ;;
+ *)
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ curl_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+ ],[
+ $t len;
+ getpeername(0,0,&len);
+ ],[
+ curl_cv_socklen_t_equiv="$t"
+ break
+ ])
+ done
+ done
+ ;;
+ esac
+
+ if test "x$curl_cv_socklen_t_equiv" = x; then
+ AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+ fi
+ ])
+ AC_MSG_RESULT($curl_cv_socklen_t_equiv)
+ AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
+ [type to use in place of socklen_t if not defined])],
+ [#include <sys/types.h>
+#include <sys/socket.h>])
+])
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..c7a777e
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,964 @@
+# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.66],,
+[m4_warning([this file was generated for autoconf 2.66.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 10
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS.
+AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([acinclude.m4])
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..3449ae5
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+#include "syshead.h"
+
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
+
+#include "base64.h"
+
+#include "memdbg.h"
+
+static char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+base64_encode(const void *data, int size, char **str)
+{
+ char *s, *p;
+ int i;
+ int c;
+ const unsigned char *q;
+
+ 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 (strlen(token) < 4)
+ 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;
+}
+
+int
+base64_decode(const char *str, void *data)
+{
+ const char *p;
+ unsigned char *q;
+
+ q = data;
+ 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;
+ *q++ = (val >> 16) & 0xff;
+ if (marker < 2)
+ *q++ = (val >> 8) & 0xff;
+ if (marker < 1)
+ *q++ = val & 0xff;
+ }
+ return q - (unsigned char *) data;
+}
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_HTTP_PROXY, ENABLE_PKCS11, ENABLE_CLIENT_CR */
diff --git a/base64.h b/base64.h
new file mode 100644
index 0000000..968d18d
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,44 @@
+/*
+ * 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_
+
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
+
+int base64_encode(const void *data, int size, char **str);
+int base64_decode(const char *str, void *data);
+
+#endif
+
+#endif
diff --git a/basic.h b/basic.h
new file mode 100644
index 0000000..88539e2
--- /dev/null
+++ b/basic.h
@@ -0,0 +1,49 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BASIC_H
+#define BASIC_H
+
+/* bool definitions */
+#ifndef bool
+#define bool int
+#endif
+
+#ifndef true
+#define true 1
+#endif
+
+#ifndef false
+#define false 0
+#endif
+
+#define BOOL_CAST(x) ((x) ? (true) : (false))
+
+/* size of an array */
+#define SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/* clear an object */
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+#endif
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..e0a9a96
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,1060 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "common.h"
+#include "buffer.h"
+#include "error.h"
+#include "mtu.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
+{
+#ifdef DMALLOC
+ return alloc_buf_gc_debug (size, NULL, file, line);
+#else
+ return alloc_buf_gc (size, NULL);
+#endif
+}
+
+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
+
+#define buf_debug_line(buf) 0
+#define buf_debug_file(buf) "[UNDEF]"
+
+#endif
+
+void
+buf_clear (struct buffer *buf)
+{
+ if (buf->capacity > 0)
+ memset (buf->data, 0, 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 ()
+{
+ 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;
+}
+
+/*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+ *
+ * This function is duplicated into service-win32/openvpnserv.c
+ * Any modifications here should be done to the other place as well.
+ */
+
+int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list arglist;
+ int ret = 0;
+ if (size > 0)
+ {
+ va_start (arglist, format);
+ ret = vsnprintf (str, size, format, arglist);
+ va_end (arglist);
+ str[size - 1] = 0;
+ }
+ return ret;
+}
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * 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,
+ int space_break, const char* separator,
+ struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (maxoutput ? maxoutput :
+ ((size * 2) + (size / space_break) * (int) strlen (separator) + 2),
+ gc);
+ int i;
+ for (i = 0; i < size; ++i)
+ {
+ if (separator && i && !(i % space_break))
+ buf_printf (&out, "%s", separator);
+ 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;
+
+#ifdef DMALLOC
+ ret = (char *) gc_malloc_debug (n, false, gc, file, line);
+#else
+ ret = (char *) gc_malloc (n, false, gc);
+#endif
+ memcpy (ret, str, n);
+ return ret;
+ }
+ else
+ return NULL;
+}
+
+/*
+ * Erase all characters in a string
+ */
+void
+string_clear (char *str)
+{
+ if (str)
+ {
+ const int len = strlen (str);
+ if (len > 0)
+ memset (str, 0, len);
+ }
+}
+
+/*
+ * 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) && isprint (c))
+ 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;
+
+ 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
+
+/*
+ * struct buffer_list
+ */
+
+#ifdef ENABLE_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->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 unsigned 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 uint8_t *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 (struct buffer_list *bl, const size_t max)
+{
+ if (bl->head)
+ {
+ struct buffer_entry *more = bl->head;
+ size_t size = 0;
+ int count = 0;
+ for (count = 0; more && size <= max; ++count)
+ {
+ size += BLEN(&more->buf);
+ more = more->next;
+ }
+
+ if (count >= 2)
+ {
+ int i;
+ struct buffer_entry *e = bl->head, *f;
+
+ ALLOC_OBJ_CLEAR (f, struct buffer_entry);
+ f->buf.data = malloc (size);
+ check_malloc_return (f->buf.data);
+ f->buf.capacity = size;
+ for (i = 0; e && i < count; ++i)
+ {
+ struct buffer_entry *next = e->next;
+ buf_copy (&f->buf, &e->buf);
+ free_buf (&e->buf);
+ free (e);
+ e = next;
+ }
+ bl->head = f;
+ f->next = more;
+ if (!more)
+ bl->tail = f;
+ }
+ }
+}
+
+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 = 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, (unsigned char *)line);
+ free (line);
+ }
+ fclose (fp);
+ }
+ return bl;
+}
+
+#endif
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..3b24d09
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,864 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include "basic.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
+
+/* basic buffer class for OpenVPN */
+
+struct buffer
+{
+ int capacity; /* size of buffer allocated by malloc */
+ int offset; /* data starts at data + offset, offset > 0 to allow for efficient prepending */
+ int len; /* length of data that starts at data + offset */
+ uint8_t *data;
+
+#ifdef BUF_INIT_TRACKING
+ const char *debug_file;
+ int debug_line;
+#endif
+};
+
+/* for garbage collection */
+
+struct gc_entry
+{
+ struct gc_entry *next;
+};
+
+struct gc_arena
+{
+ struct gc_entry *list;
+};
+
+#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
+
+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 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 */
+
+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;
+}
+
+/*
+ * printf append to a buffer with overflow check
+ */
+bool buf_printf (struct buffer *buf, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+
+/*
+ * Like snprintf but guarantees null termination for size > 0
+ */
+int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#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.
+ */
+char *
+format_hex_ex (const uint8_t *data, int size, int maxoutput,
+ int space_break, 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);
+ }
+}
+
+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;
+}
+
+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);
+
+/*
+ * Bitwise operations
+ */
+static inline void
+xor (uint8_t *dest, const uint8_t *src, int len)
+{
+ while (len-- > 0)
+ *dest++ ^= *src++;
+}
+
+/*
+ * 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)
+
+/* 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);
+
+#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);
+
+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;
+}
+
+static inline void
+gc_detach (struct gc_arena *a)
+{
+ gc_init (a);
+}
+
+static inline struct gc_arena
+gc_new (void)
+{
+ struct gc_arena ret;
+ ret.list = NULL;
+ return ret;
+}
+
+static inline void
+gc_free (struct gc_arena *a)
+{
+ if (a->list)
+ x_gc_free (a);
+}
+
+static inline void
+gc_reset (struct gc_arena *a)
+{
+ gc_free (a);
+}
+
+/*
+ * Allocate memory to hold a structure
+ */
+
+void out_of_memory (void);
+
+#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 (void *p)
+{
+ void out_of_memory (void);
+ if (!p)
+ out_of_memory ();
+}
+
+/*
+ * Manage lists of buffers
+ */
+
+#ifdef ENABLE_BUFFER_LIST
+
+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 */
+};
+
+struct buffer_list *buffer_list_new (const int max_size);
+void buffer_list_free (struct buffer_list *ol);
+
+bool buffer_list_defined (const struct buffer_list *ol);
+void buffer_list_reset (struct buffer_list *ol);
+
+void buffer_list_push (struct buffer_list *ol, const unsigned char *str);
+struct buffer_entry *buffer_list_push_data (struct buffer_list *ol, const uint8_t *data, size_t size);
+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);
+
+void buffer_list_aggregate (struct buffer_list *bl, const size_t max);
+
+struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
+
+#endif
+
+#endif /* BUFFER_H */
diff --git a/circ_list.h b/circ_list.h
new file mode 100644
index 0000000..583701a
--- /dev/null
+++ b/circ_list.h
@@ -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) 2002-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..ff3a0d5
--- /dev/null
+++ b/common.h
@@ -0,0 +1,102 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
+ 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
+
+/*
+ * A sort of pseudo-filename for data provided inline within
+ * the configuration file.
+ */
+#if ENABLE_INLINE_FILES
+#define INLINE_FILE_TAG "[[INLINE]]"
+#endif
+
+/*
+ * Script security warning
+ */
+#define SCRIPT_SECURITY_WARNING "WARNING: External program may not be called unless '--script-security 2' or higher is enabled. Use '--script-security 3 system' for backward compatibility with 2.1_rc8 and earlier. See --help text or man page for detailed info."
+
+#endif
diff --git a/config.guess b/config.guess
new file mode 100755
index 0000000..dc84c68
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1501 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file 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.
+
+
+# Originally written by Per Bothner. Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[456])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ 8664:Windows_NT:*)
+ echo x86_64-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-unknown-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..8b04c43
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,622 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Enable deferred authentication */
+#undef CONFIGURE_DEF_AUTH
+
+/* Enable internal packet filter */
+#undef CONFIGURE_PF
+
+/* enable iproute2 support */
+#undef CONFIG_FEATURE_IPROUTE
+
+/* Use memory debugging function in OpenSSL */
+#undef CRYPTO_MDEBUG
+
+/* Use dmalloc memory debugging library */
+#undef DMALLOC
+
+/* Dimension to use for empty array declaration */
+#undef EMPTY_ARRAY_SIZE
+
+/* Enable client capability only */
+#undef ENABLE_CLIENT_ONLY
+
+/* Enable client/server capability */
+#undef ENABLE_CLIENT_SERVER
+
+/* Enable debugging support */
+#undef ENABLE_DEBUG
+
+/* Enable support for the eurephia plug-in */
+#undef ENABLE_EUREPHIA
+
+/* Enable internal fragmentation support */
+#undef ENABLE_FRAGMENT
+
+/* Enable HTTP proxy support */
+#undef ENABLE_HTTP_PROXY
+
+/* Enable management server capability */
+#undef ENABLE_MANAGEMENT
+
+/* Enable multi-homed UDP server capability */
+#undef ENABLE_MULTIHOME
+
+/* Allow --askpass and --auth-user-pass passwords to be read from a file */
+#undef ENABLE_PASSWORD_SAVE
+
+/* Enable TCP Server port sharing */
+#undef ENABLE_PORT_SHARE
+
+/* Enable smaller executable size */
+#undef ENABLE_SMALL
+
+/* Enable Socks proxy support */
+#undef ENABLE_SOCKS
+
+/* Enable --x509-username-field feature */
+#undef ENABLE_X509ALTUSERNAME
+
+/* Define to 1 if you have the `accept' function. */
+#undef HAVE_ACCEPT
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the `bind' function. */
+#undef HAVE_BIND
+
+/* Define to 1 if you have the `chdir' function. */
+#undef HAVE_CHDIR
+
+/* Define to 1 if you have the `chroot' function. */
+#undef HAVE_CHROOT
+
+/* Define to 1 if you have the `chsize' function. */
+#undef HAVE_CHSIZE
+
+/* struct cmsghdr needed for extended socket error support */
+#undef HAVE_CMSGHDR
+
+/* Define to 1 if you have the `connect' function. */
+#undef HAVE_CONNECT
+
+/* Define to 1 if your compiler supports GNU GCC-style variadic macros */
+#undef HAVE_CPP_VARARG_MACRO_GCC
+
+/* Define to 1 if your compiler supports ISO C99 variadic macros */
+#undef HAVE_CPP_VARARG_MACRO_ISO
+
+/* Define to 1 if you have the `ctime' function. */
+#undef HAVE_CTIME
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define to 1 if you have the `daemon' function. */
+#undef HAVE_DAEMON
+
+/* Define to 1 if you have the `dup' function. */
+#undef HAVE_DUP
+
+/* Define to 1 if you have the `dup2' function. */
+#undef HAVE_DUP2
+
+/* 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. */
+#undef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
+
+/* Define to 1 if you have the `ENGINE_register_all_complete' function. */
+#undef HAVE_ENGINE_REGISTER_ALL_COMPLETE
+
+/* epoll_create function is defined */
+#undef HAVE_EPOLL_CREATE
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <err.h> header file. */
+#undef HAVE_ERR_H
+
+/* Define to 1 if you have the `EVP_CIPHER_CTX_set_key_length' function. */
+#undef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
+
+/* Define to 1 if you have the `execve' function. */
+#undef HAVE_EXECVE
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `flock' function. */
+#undef HAVE_FLOCK
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `getgrnam' function. */
+#undef HAVE_GETGRNAM
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if you have the `getpass' function. */
+#undef HAVE_GETPASS
+
+/* Define to 1 if you have the `getpeereid' function. */
+#undef HAVE_GETPEEREID
+
+/* Define to 1 if you have the `getpeername' function. */
+#undef HAVE_GETPEERNAME
+
+/* Define to 1 if you have the `getpid' function. */
+#undef HAVE_GETPID
+
+/* Define to 1 if you have the `getpwnam' function. */
+#undef HAVE_GETPWNAM
+
+/* Define to 1 if you have the `getsockname' function. */
+#undef HAVE_GETSOCKNAME
+
+/* Define to 1 if you have the `getsockopt' function. */
+#undef HAVE_GETSOCKOPT
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#undef HAVE_INET_NTOA
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* struct in_pktinfo needed for IP_PKTINFO support */
+#undef HAVE_IN_PKTINFO
+
+/* struct iovec needed for IPv6 support */
+#undef HAVE_IOVEC
+
+/* struct iphdr needed for IPv6 support */
+#undef HAVE_IPHDR
+
+/* Define to 1 if you have the <linux/errqueue.h> header file. */
+#undef HAVE_LINUX_ERRQUEUE_H
+
+/* Define to 1 if you have the <linux/if_tun.h> header file. */
+#undef HAVE_LINUX_IF_TUN_H
+
+/* Define to 1 if you have the <linux/sockios.h> header file. */
+#undef HAVE_LINUX_SOCKIOS_H
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#undef HAVE_LINUX_TYPES_H
+
+/* Define to 1 if you have the `listen' function. */
+#undef HAVE_LISTEN
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the `mlockall' function. */
+#undef HAVE_MLOCKALL
+
+/* struct msghdr needed for extended socket error support */
+#undef HAVE_MSGHDR
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+#undef HAVE_NETINET_IF_ETHER_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+#undef HAVE_NETINET_IN_SYSTM_H
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+#undef HAVE_NETINET_IP_H
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#undef HAVE_NETINET_TCP_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* 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/tun/if_tun.h> header file. */
+#undef HAVE_NET_TUN_IF_TUN_H
+
+/* Define to 1 if you have the `nice' function. */
+#undef HAVE_NICE
+
+/* Define to 1 if you have the `openlog' function. */
+#undef HAVE_OPENLOG
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+#undef HAVE_OPENSSL_ENGINE_H
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define to 1 if you have the `readv' function. */
+#undef HAVE_READV
+
+/* Define to 1 if you have the `recv' function. */
+#undef HAVE_RECV
+
+/* Define to 1 if you have the `recvfrom' function. */
+#undef HAVE_RECVFROM
+
+/* Define to 1 if you have the `recvmsg' function. */
+#undef HAVE_RECVMSG
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#undef HAVE_RESOLV_H
+
+/* Indicates if res_init is available */
+#undef HAVE_RES_INIT
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `send' function. */
+#undef HAVE_SEND
+
+/* Define to 1 if you have the `sendmsg' function. */
+#undef HAVE_SENDMSG
+
+/* Define to 1 if you have the `sendto' function. */
+#undef HAVE_SENDTO
+
+/* SELinux support */
+#undef HAVE_SETCON
+
+/* Define to 1 if you have the `setgid' function. */
+#undef HAVE_SETGID
+
+/* Define to 1 if you have the `setgroups' function. */
+#undef HAVE_SETGROUPS
+
+/* Define to 1 if you have the `setsid' function. */
+#undef HAVE_SETSID
+
+/* Define to 1 if you have the `setsockopt' function. */
+#undef HAVE_SETSOCKOPT
+
+/* Define to 1 if you have the `setuid' function. */
+#undef HAVE_SETUID
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the `socket' function. */
+#undef HAVE_SOCKET
+
+/* struct sock_extended_err needed for extended socket error support */
+#undef HAVE_SOCK_EXTENDED_ERR
+
+/* Define to 1 if you have the `stat' function. */
+#undef HAVE_STAT
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <stropts.h> header file. */
+#undef HAVE_STROPTS_H
+
+/* Define to 1 if you have the `syslog' function. */
+#undef HAVE_SYSLOG
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the `system' function. */
+#undef HAVE_SYSTEM
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#undef HAVE_SYS_MMAN_H
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* 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. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the `time' function. */
+#undef HAVE_TIME
+
+/* struct tun_pi needed for IPv6 support */
+#undef HAVE_TUN_PI
+
+/* Define to 1 if you have the `umask' function. */
+#undef HAVE_UMASK
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `unlink' function. */
+#undef HAVE_UNLINK
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Define to 1 if you have the `writev' function. */
+#undef HAVE_WRITEV
+
+/* Path to ifconfig tool */
+#undef IFCONFIG_PATH
+
+/* Path to iproute tool */
+#undef IPROUTE_PATH
+
+/* Use lzo/ directory prefix for LZO header files (for LZO 2.0) */
+#undef LZO_HEADER_DIR
+
+/* LZO version number */
+#undef LZO_VERSION_NUM
+
+/* Path to netstat tool */
+#undef NETSTAT_PATH
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Path to route tool */
+#undef ROUTE_PATH
+
+/* The size of `unsigned int', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_INT
+
+/* The size of `unsigned long', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_LONG
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Enable strict options check between peers */
+#undef STRICT_OPTIONS_CHECK
+
+/* The TAP-Win32 id defined in tap-win32/SOURCES */
+#undef TAP_ID
+
+/* The TAP-Win32 version number is defined in tap-win32/SOURCES */
+#undef TAP_WIN32_MIN_MAJOR
+
+/* The TAP-Win32 version number is defined in tap-win32/SOURCES */
+#undef TAP_WIN32_MIN_MINOR
+
+/* A string representing our host */
+#undef TARGET_ALIAS
+
+/* 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? */
+#undef TARGET_LINUX
+
+/* Are we running NetBSD? */
+#undef TARGET_NETBSD
+
+/* Are we running on OpenBSD? */
+#undef TARGET_OPENBSD
+
+/* Are we running on Solaris? */
+#undef TARGET_SOLARIS
+
+/* Are we running WIN32? */
+#undef TARGET_WIN32
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Use OpenSSL crypto library */
+#undef USE_CRYPTO
+
+/* Use libdl for dynamic library loading */
+#undef USE_LIBDL
+
+/* Use LoadLibrary to load DLLs on Windows */
+#undef USE_LOAD_LIBRARY
+
+/* Use LZO compression library */
+#undef USE_LZO
+
+/* Enable PKCS11 capability */
+#undef USE_PKCS11
+
+/* Use OpenSSL SSL library */
+#undef USE_SSL
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Use valgrind memory debugging library */
+#undef USE_VALGRIND
+
+/* Version number of package */
+#undef VERSION
+
+/* 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 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
+
+/* Some systems don't define in_addr_t */
+#undef in_addr_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 `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
+
+/* 16-bit unsigned type */
+#undef uint16_t
+
+/* 32-bit unsigned type */
+#undef uint32_t
+
+/* 8-bit unsigned type */
+#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/config.sub b/config.sub
new file mode 100755
index 0000000..2a55a50
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1705 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray | -microblaze)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tile*)
+ basic_machine=tile-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -cnk*|-aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..3c772e3
--- /dev/null
+++ b/configure
@@ -0,0 +1,9168 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.66 for OpenVPN 2.2.1.
+#
+# Report bugs to <openvpn-users@lists.sourceforge.net>.
+#
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+# Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ # We cannot yet assume a decent shell, so we have to provide a
+ # neutralization value for shells without unset; and this also
+ # works around shells that cannot unset nonexistent variables.
+ BASH_ENV=/dev/null
+ ENV=/dev/null
+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: openvpn-users@lists.sourceforge.net about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in #(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='OpenVPN'
+PACKAGE_TARNAME='openvpn'
+PACKAGE_VERSION='2.2.1'
+PACKAGE_STRING='OpenVPN 2.2.1'
+PACKAGE_BUGREPORT='openvpn-users@lists.sourceforge.net'
+PACKAGE_URL=''
+
+ac_unique_file="syshead.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+WIN32_FALSE
+WIN32_TRUE
+win32datadir
+TAP_WIN32_MIN_MINOR
+TAP_WIN32_MIN_MAJOR
+TAP_ID
+LIBOBJS
+MAN2HTML
+EGREP
+GREP
+CPP
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+NETSTAT
+ROUTE
+IPROUTE
+IFCONFIG
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_cygwin_native
+enable_lzo
+enable_crypto
+enable_ssl
+enable_x509_alt_username
+enable_multi
+enable_server
+enable_plugins
+enable_eurephia
+enable_management
+enable_pkcs11
+enable_socks
+enable_http
+enable_fragment
+enable_multihome
+enable_port_share
+enable_debug
+enable_small
+enable_password_save
+enable_iproute2
+enable_def_auth
+enable_pf
+enable_strict
+enable_pedantic
+enable_profiling
+enable_strict_options
+enable_selinux
+with_ssl_headers
+with_ssl_lib
+with_lzo_headers
+with_lzo_lib
+with_pkcs11_helper_headers
+with_pkcs11_helper_lib
+with_ifconfig_path
+with_iproute_path
+with_route_path
+with_netstat_path
+with_mem_check
+enable_dependency_tracking
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+MAN2HTML'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used" >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures OpenVPN 2.2.1 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/openvpn]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of OpenVPN 2.2.1:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --disable-lzo Disable LZO compression support
+ --disable-crypto Disable OpenSSL crypto support
+ --disable-ssl Disable OpenSSL SSL support for TLS-based key exchange
+ --enable-x509-alt-username Enable the --x509-username-field feature
+ --disable-multi Disable client/server support (--mode server + client mode)
+ --disable-server Disable server support only (but retain client support)
+ --disable-plugins Disable plug-in support
+ --disable-eurephia Disable support for the eurephia plug-in
+ --disable-management Disable management server support
+ --disable-pkcs11 Disable pkcs11 support
+ --disable-socks Disable Socks support
+ --disable-http Disable HTTP proxy support
+ --disable-fragment Disable internal fragmentation support (--fragment)
+ --disable-multihome Disable multi-homed UDP server support (--multihome)
+ --disable-port-share Disable TCP server port-share support (--port-share)
+ --disable-debug Disable debugging support (disable gremlin and verb 7+ messages)
+ --enable-small Enable smaller executable size (disable OCC, usage message, and verb 4 parm list)
+ --enable-password-save Allow --askpass and --auth-user-pass passwords to be read from a file
+ --enable-iproute2 Enable support for iproute2
+ --disable-def-auth Disable deferred authentication
+ --disable-pf Disable internal packet filter
+ --enable-strict Enable strict compiler warnings (debugging option)
+ --enable-pedantic Enable pedantic compiler warnings, will not generate a working executable (debugging option)
+ --enable-profiling Enable profiling (debugging option)
+ --enable-strict-options Enable strict options check between peers (debugging option)
+ --disable-selinux Disable SELinux support
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-cygwin-native Compile native win32
+ --with-ssl-headers=DIR Crypto/SSL Include files location
+ --with-ssl-lib=DIR Crypto/SSL Library location
+ --with-lzo-headers=DIR LZO Include files location
+ --with-lzo-lib=DIR LZO Library location
+ --with-pkcs11-helper-headers=DIR pkcs11-helper Include files location
+ --with-pkcs11-helper-lib=DIR pkcs11-helper Library location
+ --with-ifconfig-path=PATH Path to ifconfig tool
+ --with-iproute-path=PATH Path to iproute tool
+ --with-route-path=PATH Path to route tool
+ --with-netstat-path=PATH Path to netstat tool
+ --with-mem-check=TYPE Build with debug memory checking, TYPE = dmalloc or valgrind
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+ MAN2HTML man2html utility
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <openvpn-users@lists.sourceforge.net>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+OpenVPN configure 2.2.1
+generated by GNU Autoconf 2.66
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } >/dev/null && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval "test \"\${$3+set}\"" = set; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval "test \"\${$3+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## -------------------------------------------------- ##
+## Report this to openvpn-users@lists.sourceforge.net ##
+## -------------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval "test \"\${$3+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval "test \"\${$3+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval "test \"\${$3+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval "test \"\${$3+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+
+} # ac_fn_c_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by OpenVPN $as_me 2.2.1, which was
+generated by GNU Autoconf 2.66. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if test "${ac_cv_build+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if test "${ac_cv_host+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+am__api_version='1.11'
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_STRIP+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if test "${ac_cv_path_mkdir+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_AWK+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE=openvpn
+ VERSION=$PACKAGE_VERSION
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+
+# Check whether --with-cygwin-native was given.
+if test "${with_cygwin_native+set}" = set; then :
+ withval=$with_cygwin_native; CYGWIN_NATIVE="${withval}"
+else
+ CYGWIN_NATIVE="no"
+
+fi
+
+
+WIN32="no"
+CYGWIN="no"
+case "${host}" in
+ *-mingw*)
+ WIN32="yes"
+ cross_compiling="yes"
+ ;;
+ *-*-cygwin*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking cygwin mode to use" >&5
+$as_echo_n "checking cygwin mode to use... " >&6; }
+ if test "${CYGWIN_NATIVE}" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using native win32" >&5
+$as_echo "Using native win32" >&6; }
+ CFLAGS="${CFLAGS} -mno-cygwin"
+ CYGWIN="yes"
+ WIN32="yes"
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using cygwin" >&5
+$as_echo "Using cygwin" >&6; }
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+# Check whether --enable-lzo was given.
+if test "${enable_lzo+set}" = set; then :
+ enableval=$enable_lzo; LZO="$enableval"
+else
+ LZO="yes"
+
+fi
+
+
+# Check whether --enable-crypto was given.
+if test "${enable_crypto+set}" = set; then :
+ enableval=$enable_crypto; CRYPTO="$enableval"
+else
+ CRYPTO="yes"
+
+fi
+
+
+# Check whether --enable-ssl was given.
+if test "${enable_ssl+set}" = set; then :
+ enableval=$enable_ssl; SSL="$enableval"
+else
+ SSL="yes"
+
+fi
+
+
+# Check whether --enable-x509-alt-username was given.
+if test "${enable_x509_alt_username+set}" = set; then :
+ enableval=$enable_x509_alt_username; X509ALTUSERNAME="$enableval"
+else
+ X509ALTUSERNAME="no"
+
+fi
+
+
+# Check whether --enable-multi was given.
+if test "${enable_multi+set}" = set; then :
+ enableval=$enable_multi; MULTI="$enableval"
+else
+ MULTI="yes"
+
+fi
+
+
+# Check whether --enable-server was given.
+if test "${enable_server+set}" = set; then :
+ enableval=$enable_server; MULTI_SERVER="$enableval"
+else
+ MULTI_SERVER="yes"
+
+fi
+
+
+# Check whether --enable-plugins was given.
+if test "${enable_plugins+set}" = set; then :
+ enableval=$enable_plugins; PLUGINS="$enableval"
+else
+ PLUGINS="yes"
+
+fi
+
+
+# Check whether --enable-eurephia was given.
+if test "${enable_eurephia+set}" = set; then :
+ enableval=$enable_eurephia; EUREPHIA="$enableval"
+else
+ EUREPHIA="yes"
+
+fi
+
+
+# Check whether --enable-management was given.
+if test "${enable_management+set}" = set; then :
+ enableval=$enable_management; MANAGEMENT="$enableval"
+else
+ MANAGEMENT="yes"
+
+fi
+
+
+# Check whether --enable-pkcs11 was given.
+if test "${enable_pkcs11+set}" = set; then :
+ enableval=$enable_pkcs11; PKCS11="$enableval"
+else
+ PKCS11="yes"
+
+fi
+
+
+# Check whether --enable-socks was given.
+if test "${enable_socks+set}" = set; then :
+ enableval=$enable_socks; SOCKS="$enableval"
+else
+ SOCKS="yes"
+
+fi
+
+
+# Check whether --enable-http was given.
+if test "${enable_http+set}" = set; then :
+ enableval=$enable_http; HTTP_PROXY="$enableval"
+else
+ HTTP_PROXY="yes"
+
+fi
+
+
+# Check whether --enable-fragment was given.
+if test "${enable_fragment+set}" = set; then :
+ enableval=$enable_fragment; FRAGMENT="$enableval"
+else
+ FRAGMENT="yes"
+
+fi
+
+
+# Check whether --enable-multihome was given.
+if test "${enable_multihome+set}" = set; then :
+ enableval=$enable_multihome; MULTIHOME="$enableval"
+else
+ MULTIHOME="yes"
+
+fi
+
+
+# Check whether --enable-port-share was given.
+if test "${enable_port_share+set}" = set; then :
+ enableval=$enable_port_share; PORT_SHARE="$enableval"
+else
+ PORT_SHARE="yes"
+
+fi
+
+
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug; DEBUG="$enableval"
+else
+ DEBUG="yes"
+
+fi
+
+
+# Check whether --enable-small was given.
+if test "${enable_small+set}" = set; then :
+ enableval=$enable_small; SMALL="$enableval"
+else
+ SMALL="no"
+
+fi
+
+
+# Check whether --enable-password-save was given.
+if test "${enable_password_save+set}" = set; then :
+ enableval=$enable_password_save; PASSWORD_SAVE="$enableval"
+else
+ PASSWORD_SAVE="no"
+
+fi
+
+
+# Check whether --enable-iproute2 was given.
+if test "${enable_iproute2+set}" = set; then :
+ enableval=$enable_iproute2; test $enableval = "yes" &&
+$as_echo "#define CONFIG_FEATURE_IPROUTE 1" >>confdefs.h
+
+
+fi
+
+
+# Check whether --enable-def-auth was given.
+if test "${enable_def_auth+set}" = set; then :
+ enableval=$enable_def_auth; DEF_AUTH="$enableval"
+else
+ DEF_AUTH="yes"
+
+fi
+
+
+# Check whether --enable-pf was given.
+if test "${enable_pf+set}" = set; then :
+ enableval=$enable_pf; PF="$enableval"
+else
+ PF="yes"
+
+fi
+
+
+# Check whether --enable-strict was given.
+if test "${enable_strict+set}" = set; then :
+ enableval=$enable_strict; STRICT="$enableval"
+else
+ STRICT="no"
+
+fi
+
+
+# Check whether --enable-pedantic was given.
+if test "${enable_pedantic+set}" = set; then :
+ enableval=$enable_pedantic; PEDANTIC="$enableval"
+else
+ PEDANTIC="no"
+
+fi
+
+
+# Check whether --enable-profiling was given.
+if test "${enable_profiling+set}" = set; then :
+ enableval=$enable_profiling; PROFILE="$enableval"
+else
+ PROFILE="no"
+
+fi
+
+
+# Check whether --enable-strict-options was given.
+if test "${enable_strict_options+set}" = set; then :
+ enableval=$enable_strict_options; STRICT_OPTIONS="$enableval"
+else
+ STRICT_OPTIONS="no"
+
+fi
+
+
+# Check whether --enable-selinux was given.
+if test "${enable_selinux+set}" = set; then :
+ enableval=$enable_selinux; SELINUX="$enableval"
+else
+ SELINUX="yes"
+
+fi
+
+
+
+# Check whether --with-ssl-headers was given.
+if test "${with_ssl_headers+set}" = set; then :
+ withval=$with_ssl_headers; CS_HDR_DIR="$withval"
+ CPPFLAGS="$CPPFLAGS -I$withval"
+
+fi
+
+
+
+# Check whether --with-ssl-lib was given.
+if test "${with_ssl_lib+set}" = set; then :
+ withval=$with_ssl_lib; LDFLAGS="$LDFLAGS -L$withval"
+
+fi
+
+
+
+# Check whether --with-lzo-headers was given.
+if test "${with_lzo_headers+set}" = set; then :
+ withval=$with_lzo_headers; LZO_HDR_DIR="$withval"
+ CPPFLAGS="$CPPFLAGS -I$withval"
+
+fi
+
+
+
+# Check whether --with-lzo-lib was given.
+if test "${with_lzo_lib+set}" = set; then :
+ withval=$with_lzo_lib; LDFLAGS="$LDFLAGS -L$withval"
+
+fi
+
+
+
+# Check whether --with-pkcs11-helper-headers was given.
+if test "${with_pkcs11_helper_headers+set}" = set; then :
+ withval=$with_pkcs11_helper_headers; PKCS11_HELPER_HDR_DIR="$withval"
+ CPPFLAGS="$CPPFLAGS -I$withval"
+
+fi
+
+
+
+# Check whether --with-pkcs11-helper-lib was given.
+if test "${with_pkcs11_helper_lib+set}" = set; then :
+ withval=$with_pkcs11_helper_lib; LDFLAGS="$LDFLAGS -L$withval"
+
+fi
+
+
+
+# Check whether --with-ifconfig-path was given.
+if test "${with_ifconfig_path+set}" = set; then :
+ withval=$with_ifconfig_path; IFCONFIG="$withval"
+else
+ # Extract the first word of "ifconfig", so it can be a program name with args.
+set dummy ifconfig; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_IFCONFIG+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $IFCONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_IFCONFIG="$IFCONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/local/sbin:/usr/sbin:/sbin"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_IFCONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_IFCONFIG" && ac_cv_path_IFCONFIG="ifconfig"
+ ;;
+esac
+fi
+IFCONFIG=$ac_cv_path_IFCONFIG
+if test -n "$IFCONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $IFCONFIG" >&5
+$as_echo "$IFCONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define IFCONFIG_PATH "$IFCONFIG"
+_ACEOF
+
+
+
+# Check whether --with-iproute-path was given.
+if test "${with_iproute_path+set}" = set; then :
+ withval=$with_iproute_path; IPROUTE="$withval"
+else
+ # Extract the first word of "ip", so it can be a program name with args.
+set dummy ip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_IPROUTE+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $IPROUTE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_IPROUTE="$IPROUTE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/local/sbin:/usr/sbin:/sbin"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_IPROUTE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_IPROUTE" && ac_cv_path_IPROUTE="ip"
+ ;;
+esac
+fi
+IPROUTE=$ac_cv_path_IPROUTE
+if test -n "$IPROUTE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $IPROUTE" >&5
+$as_echo "$IPROUTE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define IPROUTE_PATH "$IPROUTE"
+_ACEOF
+
+
+
+
+# Check whether --with-route-path was given.
+if test "${with_route_path+set}" = set; then :
+ withval=$with_route_path; ROUTE="$withval"
+else
+ # Extract the first word of "route", so it can be a program name with args.
+set dummy route; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_ROUTE+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ROUTE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ROUTE="$ROUTE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/local/sbin:/usr/sbin:/sbin"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_ROUTE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_ROUTE" && ac_cv_path_ROUTE="route"
+ ;;
+esac
+fi
+ROUTE=$ac_cv_path_ROUTE
+if test -n "$ROUTE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ROUTE" >&5
+$as_echo "$ROUTE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define ROUTE_PATH "$ROUTE"
+_ACEOF
+
+
+
+# Check whether --with-netstat-path was given.
+if test "${with_netstat_path+set}" = set; then :
+ withval=$with_netstat_path; NETSTAT="$withval"
+else
+ # Extract the first word of "netstat", so it can be a program name with args.
+set dummy netstat; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_path_NETSTAT+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NETSTAT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NETSTAT="$NETSTAT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/local/sbin:/usr/sbin:/sbin:/etc"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NETSTAT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NETSTAT" && ac_cv_path_NETSTAT="netstat"
+ ;;
+esac
+fi
+NETSTAT=$ac_cv_path_NETSTAT
+if test -n "$NETSTAT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NETSTAT" >&5
+$as_echo "$NETSTAT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define NETSTAT_PATH "$NETSTAT"
+_ACEOF
+
+
+
+# Check whether --with-mem-check was given.
+if test "${with_mem_check+set}" = set; then :
+ withval=$with_mem_check; MEMCHECK="$withval"
+
+fi
+
+
+CPPFLAGS="$CPPFLAGS -I${srcdir}"
+
+openvpn_host=$host
+if test $host_alias; then
+ openvpn_host=$host_alias
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define TARGET_ALIAS "$openvpn_host"
+_ACEOF
+
+case "$host" in
+*-*-linux*)
+
+$as_echo "#define TARGET_LINUX 1" >>confdefs.h
+
+ if test -z $CS_HDR_DIR && test "$CRYPTO" = "yes"; then
+ CPPFLAGS="$CPPFLAGS $(pkg-config --cflags openssl 2>/dev/null)"
+ fi
+ ;;
+*-*-solaris*)
+
+$as_echo "#define TARGET_SOLARIS 1" >>confdefs.h
+
+ ;;
+*-*-openbsd*)
+
+$as_echo "#define TARGET_OPENBSD 1" >>confdefs.h
+
+ ;;
+*-*-freebsd*)
+
+$as_echo "#define TARGET_FREEBSD 1" >>confdefs.h
+
+ ;;
+*-*-netbsd*)
+
+$as_echo "#define TARGET_NETBSD 1" >>confdefs.h
+
+ ;;
+*-*-darwin*)
+
+$as_echo "#define TARGET_DARWIN 1" >>confdefs.h
+
+ CPPFLAGS="$CPPFLAGS -no-cpp-precomp"
+ ;;
+*-mingw*)
+
+$as_echo "#define TARGET_WIN32 1" >>confdefs.h
+
+ CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN"
+
+ LIBS="-lgdi32 $LIBS"
+
+
+ LIBS="-lws2_32 $LIBS"
+
+
+ LIBS="-lwininet $LIBS"
+
+
+ LIBS="-lcrypt32 $LIBS"
+
+
+ LIBS="-liphlpapi $LIBS"
+
+
+ LIBS="-lwinmm $LIBS"
+
+ ;;
+*-*-dragonfly*)
+
+$as_echo "#define TARGET_DRAGONFLY 1" >>confdefs.h
+
+ ;;
+
+esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if test "${ac_cv_objext+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if test "${ac_cv_c_compiler_gnu+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if test "${ac_cv_prog_cc_g+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if test "${ac_cv_prog_cc_c89+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if test "${ac_cv_path_GREP+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if test "${ac_cv_path_EGREP+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+if test $ac_cv_c_compiler_gnu = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5
+$as_echo_n "checking whether $CC needs -traditional... " >&6; }
+if test "${ac_cv_prog_gcc_traditional+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_pattern="Autoconf.*'x'"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then :
+ ac_cv_prog_gcc_traditional=yes
+else
+ ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+ if test $ac_cv_prog_gcc_traditional = no; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "$ac_pattern" >/dev/null 2>&1; then :
+ ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5
+$as_echo "$ac_cv_prog_gcc_traditional" >&6; }
+ if test $ac_cv_prog_gcc_traditional = yes; then
+ CC="$CC -traditional"
+ fi
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+ ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
+if test "x$ac_cv_header_minix_config_h" = x""yes; then :
+ MINIX=yes
+else
+ MINIX=
+fi
+
+
+ if test "$MINIX" = yes; then
+
+$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
+
+
+$as_echo "#define _MINIX 1" >>confdefs.h
+
+ fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if test "${ac_cv_safe_to_define___extensions__+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# define __EXTENSIONS__ 1
+ $ac_includes_default
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_safe_to_define___extensions__=yes
+else
+ ac_cv_safe_to_define___extensions__=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
+ test $ac_cv_safe_to_define___extensions__ = yes &&
+ $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
+
+ $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
+
+ $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
+
+ $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+ $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+
+
+
+
+if test "${WIN32}" = "yes"; then
+
+ for ac_prog in man2html
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if test "${ac_cv_prog_MAN2HTML+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$MAN2HTML"; then
+ ac_cv_prog_MAN2HTML="$MAN2HTML" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_MAN2HTML="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+MAN2HTML=$ac_cv_prog_MAN2HTML
+if test -n "$MAN2HTML"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAN2HTML" >&5
+$as_echo "$MAN2HTML" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$MAN2HTML" && break
+done
+
+ test -z "${MAN2HTML}" && as_fn_error $? "man2html is required for win32" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if test "${ac_cv_header_stdc+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
+$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
+if test "${ac_cv_c_const+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+/* FIXME: Include the comments suggested by Paul. */
+#ifndef __cplusplus
+ /* Ultrix mips cc rejects this. */
+ typedef int charset[2];
+ const charset cs;
+ /* SunOS 4.1.1 cc rejects this. */
+ char const *const *pcpcc;
+ char **ppc;
+ /* NEC SVR4.0.2 mips cc rejects this. */
+ struct point {int x, y;};
+ static struct point const zero = {0,0};
+ /* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in
+ an arm of an if-expression whose if-part is not a constant
+ expression */
+ const char *g = "string";
+ pcpcc = &g + (g ? g-g : 0);
+ /* HPUX 7.0 cc rejects these. */
+ ++pcpcc;
+ ppc = (char**) pcpcc;
+ pcpcc = (char const *const *) ppc;
+ { /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+ if (s) return 0;
+ }
+ { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+ }
+ { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+ }
+ { /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+ }
+ { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+ if (!foo) return 0;
+ }
+ return !cs[0] && !zero.x;
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_const=yes
+else
+ ac_cv_c_const=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
+$as_echo "$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+$as_echo "#define const /**/" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
+$as_echo_n "checking for inline... " >&6; }
+if test "${ac_cv_c_inline+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_inline=$ac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
+$as_echo "$ac_cv_c_inline" >&6; }
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5
+$as_echo_n "checking for working volatile... " >&6; }
+if test "${ac_cv_c_volatile+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+volatile int x;
+int * volatile y = (int *) 0;
+return !x && !y;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_volatile=yes
+else
+ ac_cv_c_volatile=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5
+$as_echo "$ac_cv_c_volatile" >&6; }
+if test $ac_cv_c_volatile = no; then
+
+$as_echo "#define volatile /**/" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
+if test "x$ac_cv_type_off_t" = x""yes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = x""yes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = x""yes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if test "${ac_cv_type_uid_t+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "#define uid_t int" >>confdefs.h
+
+
+$as_echo "#define gid_t int" >>confdefs.h
+
+fi
+
+
+ ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "#include <sys/types.h>
+#include <sys/socket.h>
+"
+if test "x$ac_cv_type_socklen_t" = x""yes; then :
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socklen_t equivalent" >&5
+$as_echo_n "checking for socklen_t equivalent... " >&6; }
+ if test "${curl_cv_socklen_t_equiv+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ case "$host" in
+ *-mingw*) curl_cv_socklen_t_equiv=int ;;
+ *)
+ # Systems have either "struct sockaddr *" or
+ # "void *" as the second argument to getpeername
+ curl_cv_socklen_t_equiv=
+ for arg2 in "struct sockaddr" void; do
+ for t in int size_t unsigned long "unsigned long"; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <sys/types.h>
+ #include <sys/socket.h>
+
+ int getpeername (int, $arg2 *, $t *);
+
+int
+main ()
+{
+
+ $t len;
+ getpeername(0,0,&len);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+ curl_cv_socklen_t_equiv="$t"
+ break
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+ ;;
+ esac
+
+ if test "x$curl_cv_socklen_t_equiv" = x; then
+ as_fn_error $? "Cannot find a type to use in place of socklen_t" "$LINENO" 5
+ fi
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $curl_cv_socklen_t_equiv" >&5
+$as_echo "$curl_cv_socklen_t_equiv" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define socklen_t $curl_cv_socklen_t_equiv
+_ACEOF
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if test "${ac_cv_header_time+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ISO C 1999 vararg macro support" >&5
+$as_echo_n "checking for ISO C 1999 vararg macro support... " >&6; }
+if test "${ax_cv_cpp_vararg_macro_iso+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#define macro(a, ...) func(a, __VA_ARGS__)
+ int func(int a, int b, int c);
+ int test() { return macro(1, 2, 3); }
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ax_cv_cpp_vararg_macro_iso=yes
+else
+ ax_cv_cpp_vararg_macro_iso=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cpp_vararg_macro_iso" >&5
+$as_echo "$ax_cv_cpp_vararg_macro_iso" >&6; }
+ if test $ax_cv_cpp_vararg_macro_iso = yes ; then
+
+$as_echo "#define HAVE_CPP_VARARG_MACRO_ISO 1" >>confdefs.h
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU GCC vararg macro support" >&5
+$as_echo_n "checking for GNU GCC vararg macro support... " >&6; }
+if test "${ax_cv_cpp_vararg_macro_gcc+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #define macro(a, b...) func(a, b)
+ int func(int a, int b, int c);
+ int test() { return macro(1, 2, 3); }
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ax_cv_cpp_vararg_macro_gcc=yes
+else
+ ax_cv_cpp_vararg_macro_gcc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cpp_vararg_macro_gcc" >&5
+$as_echo "$ax_cv_cpp_vararg_macro_gcc" >&6; }
+ if test $ax_cv_cpp_vararg_macro_gcc = yes ; then
+
+$as_echo "#define HAVE_CPP_VARARG_MACRO_GCC 1" >>confdefs.h
+
+ fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: checking for C compiler empty array support" >&5
+$as_echo "checking for C compiler empty array support" >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ struct { int foo; int bar[0]; } mystruct;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+
+cat >>confdefs.h <<_ACEOF
+#define EMPTY_ARRAY_SIZE 0
+_ACEOF
+
+
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ struct { int foo; int bar[]; } mystruct;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+
+cat >>confdefs.h <<_ACEOF
+#define EMPTY_ARRAY_SIZE /**/
+_ACEOF
+
+
+else
+
+ as_fn_error $? "C compiler is unable to creaty empty arrays" "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+
+for ac_header in fcntl.h stdlib.h stdarg.h stdio.h string.h strings.h ctype.h errno.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${WIN32}" != "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test "${ac_cv_header_sys_wait_h+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+ for ac_header in sys/time.h sys/socket.h sys/un.h sys/ioctl.h sys/stat.h sys/mman.h fcntl.h sys/file.h stdlib.h stdint.h stdarg.h unistd.h signal.h stdio.h string.h strings.h ctype.h errno.h syslog.h pwd.h grp.h net/if_tun.h net/tun/if_tun.h stropts.h sys/sockio.h netinet/in.h netinet/in_systm.h netinet/tcp.h arpa/inet.h netdb.h sys/uio.h linux/if_tun.h linux/sockios.h linux/types.h sys/poll.h sys/epoll.h err.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in net/if.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_SYS_SOCKET_H
+ # include <sys/socket.h>
+ #endif
+
+"
+if test "x$ac_cv_header_net_if_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_NET_IF_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in netinet/ip.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "netinet/ip.h" "ac_cv_header_netinet_ip_h" "#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_SYSTM_H
+ # include <netinet/in_systm.h>
+ #endif
+
+"
+if test "x$ac_cv_header_netinet_ip_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_NETINET_IP_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in netinet/if_ether.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "netinet/if_ether.h" "ac_cv_header_netinet_if_ether_h" "#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_SYS_SOCKET_H
+ # include <sys/socket.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+
+"
+if test "x$ac_cv_header_netinet_if_ether_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_NETINET_IF_ETHER_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in resolv.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "resolv.h" "ac_cv_header_resolv_h" "#ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+
+"
+if test "x$ac_cv_header_resolv_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_RESOLV_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in linux/errqueue.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "linux/errqueue.h" "ac_cv_header_linux_errqueue_h" "#ifdef HAVE_LINUX_TYPES_H
+ # include <linux/types.h>
+ #endif
+
+"
+if test "x$ac_cv_header_linux_errqueue_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LINUX_ERRQUEUE_H 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+ac_fn_c_check_type "$LINENO" "in_addr_t" "ac_cv_type_in_addr_t" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_in_addr_t" = x""yes; then :
+
+else
+
+$as_echo "#define in_addr_t uint32_t" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "uint8_t" "ac_cv_type_uint8_t" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_uint8_t" = x""yes; then :
+
+else
+
+$as_echo "#define uint8_t unsigned char" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "uint16_t" "ac_cv_type_uint16_t" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_uint16_t" = x""yes; then :
+
+else
+
+$as_echo "#define uint16_t unsigned char" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_uint32_t" = x""yes; then :
+
+else
+
+$as_echo "#define uint32_t unsigned long" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "struct tun_pi" "ac_cv_type_struct_tun_pi" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_tun_pi" = x""yes; then :
+
+$as_echo "#define HAVE_TUN_PI 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "struct iphdr" "ac_cv_type_struct_iphdr" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_iphdr" = x""yes; then :
+
+$as_echo "#define HAVE_IPHDR 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "struct iovec" "ac_cv_type_struct_iovec" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_iovec" = x""yes; then :
+
+$as_echo "#define HAVE_IOVEC 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "struct sock_extended_err" "ac_cv_type_struct_sock_extended_err" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_sock_extended_err" = x""yes; then :
+
+$as_echo "#define HAVE_SOCK_EXTENDED_ERR 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "struct msghdr" "ac_cv_type_struct_msghdr" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_msghdr" = x""yes; then :
+
+$as_echo "#define HAVE_MSGHDR 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "struct cmsghdr" "ac_cv_type_struct_cmsghdr" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_cmsghdr" = x""yes; then :
+
+$as_echo "#define HAVE_CMSGHDR 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "struct in_pktinfo" "ac_cv_type_struct_in_pktinfo" "#include \"syshead.h\"
+"
+if test "x$ac_cv_type_struct_in_pktinfo" = x""yes; then :
+
+$as_echo "#define HAVE_IN_PKTINFO 1" >>confdefs.h
+
+fi
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if test "${ac_cv_sizeof_unsigned_int+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if test "${ac_cv_sizeof_unsigned_long+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+for ac_func in ctime memset vsnprintf strdup
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ as_fn_error $? "Required library function not found" "$LINENO" 5
+fi
+done
+
+for ac_func in daemon chroot getpwnam setuid nice system getpid dup dup2 getpass strerror syslog openlog mlockall getgrnam setgid setgroups stat flock readv writev time setsid chdir putenv getpeername unlink chsize ftruncate execve getpeereid umask
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+# Windows use stdcall for winsock so we cannot auto detect these
+
+
+
+if test "${WIN32}" = "yes"; then
+
+
+$as_echo "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h
+
+
+
+$as_echo "#define HAVE_SOCKET 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_RECV 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_RECVFROM 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_SEND 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_SENDTO 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_LISTEN 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_ACCEPT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_CONNECT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_BIND 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_SELECT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_GETHOSTBYNAME 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_INET_NTOA 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_SETSOCKOPT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_GETSOCKOPT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_GETSOCKNAME 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_POLL 1" >>confdefs.h
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if test "${ac_cv_type_signal+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
+$as_echo_n "checking for library containing socket... " >&6; }
+if test "${ac_cv_search_socket+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_socket=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if test "${ac_cv_search_socket+set}" = set; then :
+ break
+fi
+done
+if test "${ac_cv_search_socket+set}" = set; then :
+
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
+$as_echo "$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing inet_ntoa" >&5
+$as_echo_n "checking for library containing inet_ntoa... " >&6; }
+if test "${ac_cv_search_inet_ntoa+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_ntoa ();
+int
+main ()
+{
+return inet_ntoa ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_inet_ntoa=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if test "${ac_cv_search_inet_ntoa+set}" = set; then :
+ break
+fi
+done
+if test "${ac_cv_search_inet_ntoa+set}" = set; then :
+
+else
+ ac_cv_search_inet_ntoa=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_ntoa" >&5
+$as_echo "$ac_cv_search_inet_ntoa" >&6; }
+ac_res=$ac_cv_search_inet_ntoa
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5
+$as_echo_n "checking for library containing gethostbyname... " >&6; }
+if test "${ac_cv_search_gethostbyname+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostbyname ();
+int
+main ()
+{
+return gethostbyname ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' resolv nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gethostbyname=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if test "${ac_cv_search_gethostbyname+set}" = set; then :
+ break
+fi
+done
+if test "${ac_cv_search_gethostbyname+set}" = set; then :
+
+else
+ ac_cv_search_gethostbyname=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5
+$as_echo "$ac_cv_search_gethostbyname" >&6; }
+ac_res=$ac_cv_search_gethostbyname
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+ for ac_header in vfork.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
+if test "x$ac_cv_header_vfork_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_VFORK_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in fork vfork
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if test "${ac_cv_func_fork_works+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_fork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_fork_works=yes
+else
+ ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if test "${ac_cv_func_vfork_works+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_vfork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_vfork_works=yes
+else
+ ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h
+
+else
+
+$as_echo "#define vfork fork" >>confdefs.h
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
+
+fi
+
+
+ for ac_func in gettimeofday
+do :
+ ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday"
+if test "x$ac_cv_func_gettimeofday" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_GETTIMEOFDAY 1
+_ACEOF
+
+fi
+done
+
+
+ for ac_func in socket recv recvfrom send sendto listen accept connect bind select gethostbyname inet_ntoa
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+else
+ as_fn_error $? "Required library function not found" "$LINENO" 5
+fi
+done
+
+ for ac_func in setsockopt getsockopt getsockname poll sendmsg recvmsg
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5
+$as_echo_n "checking for working memcmp... " >&6; }
+if test "${ac_cv_func_memcmp_working+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_memcmp_working=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Some versions of memcmp are not 8-bit clean. */
+ char c0 = '\100', c1 = '\200', c2 = '\201';
+ if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+ return 1;
+
+ /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+ or more and with at least one buffer not starting on a 4-byte boundary.
+ William Lewis provided this test program. */
+ {
+ char foo[21];
+ char bar[21];
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ char *a = foo + i;
+ char *b = bar + i;
+ strcpy (a, "--------01111111");
+ strcpy (b, "--------10000000");
+ if (memcmp (a, b, 16) >= 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_memcmp_working=yes
+else
+ ac_cv_func_memcmp_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5
+$as_echo "$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+ *" memcmp.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <resolv.h>
+
+int
+main ()
+{
+
+ res_init ();
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: res_init DEFINED" >&5
+$as_echo "res_init DEFINED" >&6; }
+
+$as_echo "#define HAVE_RES_INIT 1" >>confdefs.h
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: res_init UNDEFINED" >&5
+$as_echo "res_init UNDEFINED" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working epoll implementation..." >&5
+$as_echo "$as_me: checking for working epoll implementation..." >&6;}
+OLDLDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
+ac_fn_c_check_func "$LINENO" "epoll_create" "ac_cv_func_epoll_create"
+if test "x$ac_cv_func_epoll_create" = x""yes; then :
+
+$as_echo "#define HAVE_EPOLL_CREATE 1" >>confdefs.h
+
+fi
+
+LDFLAGS="$OLDLDFLAGS"
+
+
+if test "$MEMCHECK" = "valgrind"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for valgrind tool and Header files..." >&5
+$as_echo "$as_me: checking for valgrind tool and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "valgrind/memcheck.h" "ac_cv_header_valgrind_memcheck_h" "$ac_includes_default"
+if test "x$ac_cv_header_valgrind_memcheck_h" = x""yes; then :
+
+
+$as_echo "#define USE_VALGRIND 1" >>confdefs.h
+
+ CFLAGS="-g -fno-inline"
+
+else
+ as_fn_error $? "valgrind headers not found." "$LINENO" 5
+
+fi
+
+
+fi
+
+
+if test "$MEMCHECK" = "dmalloc"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dmalloc Library and Header files..." >&5
+$as_echo "$as_me: checking for dmalloc Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "dmalloc.h" "ac_cv_header_dmalloc_h" "$ac_includes_default"
+if test "x$ac_cv_header_dmalloc_h" = x""yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for malloc in -ldmalloc" >&5
+$as_echo_n "checking for malloc in -ldmalloc... " >&6; }
+if test "${ac_cv_lib_dmalloc_malloc+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldmalloc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char malloc ();
+int
+main ()
+{
+return malloc ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dmalloc_malloc=yes
+else
+ ac_cv_lib_dmalloc_malloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dmalloc_malloc" >&5
+$as_echo "$ac_cv_lib_dmalloc_malloc" >&6; }
+if test "x$ac_cv_lib_dmalloc_malloc" = x""yes; then :
+
+
+ LIBS="-ldmalloc $LIBS"
+
+
+$as_echo "#define DMALLOC 1" >>confdefs.h
+
+
+else
+ as_fn_error $? "dmalloc library not found." "$LINENO" 5
+
+fi
+
+else
+ as_fn_error $? "dmalloc headers not found." "$LINENO" 5
+
+fi
+
+
+fi
+
+if test "${WIN32}" != "yes"; then
+ if test "$PLUGINS" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libdl Library and Header files..." >&5
+$as_echo "$as_me: checking for libdl Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default"
+if test "x$ac_cv_header_dlfcn_h" = x""yes; then :
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = x""yes; then :
+
+$as_echo "#define USE_LIBDL 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if test "${ac_cv_lib_dl_dlopen+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = x""yes; then :
+
+
+ LIBS="-ldl $LIBS"
+
+
+$as_echo "#define USE_LIBDL 1" >>confdefs.h
+
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: libdl library not found." >&5
+$as_echo "libdl library not found." >&6; }
+
+fi
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: libdl headers not found." >&5
+$as_echo "libdl headers not found." >&6; }
+
+fi
+
+
+ if test "$EUREPHIA" = "yes"; then
+
+$as_echo "#define ENABLE_EUREPHIA 1" >>confdefs.h
+
+ fi
+ fi
+fi
+
+if test "${WIN32}" = "yes"; then
+ if test "$PLUGINS" = "yes"; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <windows.h>
+
+int
+main ()
+{
+
+ LoadLibrary (NULL);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LoadLibrary DEFINED" >&5
+$as_echo "LoadLibrary DEFINED" >&6; }
+
+$as_echo "#define USE_LOAD_LIBRARY 1" >>confdefs.h
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LoadLibrary UNDEFINED" >&5
+$as_echo "LoadLibrary UNDEFINED" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+fi
+
+
+if test "$LZO" = "yes"; then
+ LZO_H=""
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LZO Library and Header files..." >&5
+$as_echo "$as_me: checking for LZO Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "lzo/lzo1x.h" "ac_cv_header_lzo_lzo1x_h" "$ac_includes_default"
+if test "x$ac_cv_header_lzo_lzo1x_h" = x""yes; then :
+ LZO_H="2"
+ lzolibs="lzo2 lzo"
+
+$as_echo "#define LZO_HEADER_DIR 1" >>confdefs.h
+
+
+else
+ ac_fn_c_check_header_mongrel "$LINENO" "lzo1x.h" "ac_cv_header_lzo1x_h" "$ac_includes_default"
+if test "x$ac_cv_header_lzo1x_h" = x""yes; then :
+ LZO_H="1" ; lzolibs=lzo
+fi
+
+
+
+fi
+
+
+
+ if test -n "$LZO_H"; then
+ havelzolib=0
+ for i in $lzolibs ; do
+ if test $havelzolib = 1 ; then break ; fi
+ as_ac_Lib=`$as_echo "ac_cv_lib_$i''_lzo1x_1_15_compress" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for lzo1x_1_15_compress in -l$i" >&5
+$as_echo_n "checking for lzo1x_1_15_compress in -l$i... " >&6; }
+if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$i $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char lzo1x_1_15_compress ();
+int
+main ()
+{
+return lzo1x_1_15_compress ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+
+
+ LIBS="-l$i $LIBS"
+
+
+$as_echo "#define USE_LZO 1" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define LZO_VERSION_NUM "$LZO_H"
+_ACEOF
+
+ havelzolib=1
+
+
+fi
+
+ done
+ if test $havelzolib = 0 ; then
+ as_fn_error $? "LZO headers were found but LZO library was not found" "$LINENO" 5
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO headers were not found" >&5
+$as_echo "LZO headers were not found" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: LZO library available from http://www.oberhumer.com/opensource/lzo/" >&5
+$as_echo "LZO library available from http://www.oberhumer.com/opensource/lzo/" >&6; }
+ as_fn_error $? "Or try ./configure --disable-lzo" "$LINENO" 5
+ fi
+fi
+
+
+if test "$CRYPTO" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL Crypto Library and Header files..." >&5
+$as_echo "$as_me: checking for OpenSSL Crypto Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/evp.h" "ac_cv_header_openssl_evp_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_evp_h" = x""yes; then :
+
+else
+ as_fn_error $? "OpenSSL Crypto headers not found." "$LINENO" 5
+fi
+
+
+
+ for lib in crypto eay32; do
+ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_EVP_CIPHER_CTX_init" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_init in -l$lib" >&5
+$as_echo_n "checking for EVP_CIPHER_CTX_init in -l$lib... " >&6; }
+if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char EVP_CIPHER_CTX_init ();
+int
+main ()
+{
+return EVP_CIPHER_CTX_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+
+ cryptofound=1
+
+ LIBS="-l$lib $LIBS"
+
+
+
+fi
+
+ done
+
+ test -n "$cryptofound" || as_fn_error $? "OpenSSL Crypto library not found." "$LINENO" 5
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking that OpenSSL Library is at least version 0.9.6" >&5
+$as_echo_n "checking that OpenSSL Library is at least version 0.9.6... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <openssl/evp.h>
+ #if SSLEAY_VERSION_NUMBER >= 0x00906000L
+ yes
+ #endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define USE_CRYPTO 1" >>confdefs.h
+
+ for ac_func in EVP_CIPHER_CTX_set_key_length
+do :
+ ac_fn_c_check_func "$LINENO" "EVP_CIPHER_CTX_set_key_length" "ac_cv_func_EVP_CIPHER_CTX_set_key_length"
+if test "x$ac_cv_func_EVP_CIPHER_CTX_set_key_length" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH 1
+_ACEOF
+
+fi
+done
+
+
+ for ac_header in openssl/engine.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/engine.h" "ac_cv_header_openssl_engine_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_engine_h" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_ENGINE_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_func in ENGINE_load_builtin_engines
+do :
+ ac_fn_c_check_func "$LINENO" "ENGINE_load_builtin_engines" "ac_cv_func_ENGINE_load_builtin_engines"
+if test "x$ac_cv_func_ENGINE_load_builtin_engines" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_ENGINE_LOAD_BUILTIN_ENGINES 1
+_ACEOF
+
+fi
+done
+
+ for ac_func in ENGINE_register_all_complete
+do :
+ ac_fn_c_check_func "$LINENO" "ENGINE_register_all_complete" "ac_cv_func_ENGINE_register_all_complete"
+if test "x$ac_cv_func_ENGINE_register_all_complete" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_ENGINE_REGISTER_ALL_COMPLETE 1
+_ACEOF
+
+fi
+done
+
+ for ac_func in ENGINE_cleanup
+do :
+ ac_fn_c_check_func "$LINENO" "ENGINE_cleanup" "ac_cv_func_ENGINE_cleanup"
+if test "x$ac_cv_func_ENGINE_cleanup" = x""yes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_ENGINE_CLEANUP 1
+_ACEOF
+
+fi
+done
+
+
+else
+ as_fn_error $? "OpenSSL crypto Library is too old." "$LINENO" 5
+
+fi
+rm -f conftest*
+
+
+
+ if test "$SSL" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL SSL Library and Header files..." >&5
+$as_echo "$as_me: checking for OpenSSL SSL Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_ssl_h" = x""yes; then :
+
+else
+ as_fn_error $? "OpenSSL SSL headers not found." "$LINENO" 5
+
+fi
+
+
+
+ for lib in ssl ssl32; do
+ as_ac_Lib=`$as_echo "ac_cv_lib_$lib''_SSL_CTX_new" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -l$lib" >&5
+$as_echo_n "checking for SSL_CTX_new in -l$lib... " >&6; }
+if eval "test \"\${$as_ac_Lib+set}\"" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_CTX_new ();
+int
+main ()
+{
+return SSL_CTX_new ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+
+ sslfound=1
+
+ LIBS="-l$lib $LIBS"
+
+
+
+fi
+
+ done
+
+ test -n "${sslfound}" || as_fn_error $? "OpenSSL SSL library not found." "$LINENO" 5
+
+ if test "$MEMCHECK" = "ssl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Memory Debugging Capabilities in OpenSSL Library..." >&5
+$as_echo "$as_me: checking for Memory Debugging Capabilities in OpenSSL Library..." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CRYPTO_mem_ctrl in -lssl" >&5
+$as_echo_n "checking for CRYPTO_mem_ctrl in -lssl... " >&6; }
+if test "${ac_cv_lib_ssl_CRYPTO_mem_ctrl+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char CRYPTO_mem_ctrl ();
+int
+main ()
+{
+return CRYPTO_mem_ctrl ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ssl_CRYPTO_mem_ctrl=yes
+else
+ ac_cv_lib_ssl_CRYPTO_mem_ctrl=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_CRYPTO_mem_ctrl" >&5
+$as_echo "$ac_cv_lib_ssl_CRYPTO_mem_ctrl" >&6; }
+if test "x$ac_cv_lib_ssl_CRYPTO_mem_ctrl" = x""yes; then :
+
+
+$as_echo "#define CRYPTO_MDEBUG 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG" >&5
+$as_echo "NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG" >&6; }
+
+else
+ as_fn_error $? "Memory Debugging function in OpenSSL library not found." "$LINENO" 5
+
+fi
+
+ fi
+
+
+$as_echo "#define USE_SSL 1" >>confdefs.h
+
+ fi
+fi
+
+if test "$X509ALTUSERNAME" = "yes"; then
+
+$as_echo "#define ENABLE_X509ALTUSERNAME 1" >>confdefs.h
+
+fi
+
+if test "$PKCS11" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pkcs11-helper Library and Header files..." >&5
+$as_echo "$as_me: checking for pkcs11-helper Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "pkcs11-helper-1.0/pkcs11h-core.h" "ac_cv_header_pkcs11_helper_1_0_pkcs11h_core_h" "$ac_includes_default"
+if test "x$ac_cv_header_pkcs11_helper_1_0_pkcs11h_core_h" = x""yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pkcs11h_initialize in -lpkcs11-helper" >&5
+$as_echo_n "checking for pkcs11h_initialize in -lpkcs11-helper... " >&6; }
+if test "${ac_cv_lib_pkcs11_helper_pkcs11h_initialize+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpkcs11-helper $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pkcs11h_initialize ();
+int
+main ()
+{
+return pkcs11h_initialize ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pkcs11_helper_pkcs11h_initialize=yes
+else
+ ac_cv_lib_pkcs11_helper_pkcs11h_initialize=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pkcs11_helper_pkcs11h_initialize" >&5
+$as_echo "$ac_cv_lib_pkcs11_helper_pkcs11h_initialize" >&6; }
+if test "x$ac_cv_lib_pkcs11_helper_pkcs11h_initialize" = x""yes; then :
+
+
+$as_echo "#define USE_PKCS11 1" >>confdefs.h
+
+
+ LIBS="-lpkcs11-helper $LIBS"
+
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: pkcs11-helper library not found." >&5
+$as_echo "pkcs11-helper library not found." >&6; }
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: pkcs11-helper headers not found." >&5
+$as_echo "pkcs11-helper headers not found." >&6; }
+
+fi
+
+
+fi
+
+if test "$MULTI" = "yes"; then
+
+$as_echo "#define ENABLE_CLIENT_SERVER 1" >>confdefs.h
+
+fi
+
+if test "$MULTI_SERVER" = "no"; then
+
+$as_echo "#define ENABLE_CLIENT_ONLY 1" >>confdefs.h
+
+fi
+
+if test "$MANAGEMENT" = "yes"; then
+
+$as_echo "#define ENABLE_MANAGEMENT 1" >>confdefs.h
+
+fi
+
+if test "$SOCKS" = "yes"; then
+
+$as_echo "#define ENABLE_SOCKS 1" >>confdefs.h
+
+fi
+
+if test "$HTTP_PROXY" = "yes"; then
+
+$as_echo "#define ENABLE_HTTP_PROXY 1" >>confdefs.h
+
+fi
+
+if test "$MULTIHOME" = "yes"; then
+
+$as_echo "#define ENABLE_MULTIHOME 1" >>confdefs.h
+
+fi
+
+if test "$DEBUG" = "yes"; then
+
+$as_echo "#define ENABLE_DEBUG 1" >>confdefs.h
+
+fi
+
+if test "$SMALL" = "yes"; then
+
+$as_echo "#define ENABLE_SMALL 1" >>confdefs.h
+
+fi
+
+if test "$FRAGMENT" = "yes"; then
+
+$as_echo "#define ENABLE_FRAGMENT 1" >>confdefs.h
+
+fi
+
+if test "$PORT_SHARE" = "yes"; then
+
+$as_echo "#define ENABLE_PORT_SHARE 1" >>confdefs.h
+
+fi
+
+if test "$DEF_AUTH" = "yes"; then
+
+$as_echo "#define CONFIGURE_DEF_AUTH 1" >>confdefs.h
+
+fi
+
+if test "$PF" = "yes"; then
+
+$as_echo "#define CONFIGURE_PF 1" >>confdefs.h
+
+fi
+
+if test "$STRICT" = "yes"; then
+ CFLAGS="$CFLAGS -Wall -Wno-unused-parameter -Wno-unused-function"
+fi
+
+if test "$PEDANTIC" = "yes"; then
+ CFLAGS="$CFLAGS -ansi -pedantic"
+fi
+
+if test "$PROFILE" = "yes"; then
+ CFLAGS="$CFLAGS -pg -DENABLE_PROFILING"
+fi
+
+if test "$STRICT_OPTIONS" = "yes"; then
+
+$as_echo "#define STRICT_OPTIONS_CHECK 1" >>confdefs.h
+
+fi
+
+if test "$PASSWORD_SAVE" = "yes"; then
+
+$as_echo "#define ENABLE_PASSWORD_SAVE 1" >>confdefs.h
+
+fi
+
+if test "$SELINUX" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libselinux Library and Header files..." >&5
+$as_echo "$as_me: checking for libselinux Library and Header files..." >&6;}
+ ac_fn_c_check_header_mongrel "$LINENO" "selinux/selinux.h" "ac_cv_header_selinux_selinux_h" "$ac_includes_default"
+if test "x$ac_cv_header_selinux_selinux_h" = x""yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setcon in -lselinux" >&5
+$as_echo_n "checking for setcon in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_setcon+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setcon ();
+int
+main ()
+{
+return setcon ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_selinux_setcon=yes
+else
+ ac_cv_lib_selinux_setcon=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_selinux_setcon" >&5
+$as_echo "$ac_cv_lib_selinux_setcon" >&6; }
+if test "x$ac_cv_lib_selinux_setcon" = x""yes; then :
+
+
+ LIBS="-lselinux $LIBS"
+
+
+$as_echo "#define HAVE_SETCON 1" >>confdefs.h
+
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: SELinux library not found." >&5
+$as_echo "SELinux library not found." >&6; }
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: SELinux headers not found." >&5
+$as_echo "SELinux headers not found." >&6; }
+
+fi
+
+
+fi
+
+TAP_ID="tap0901"
+TAP_WIN32_MIN_MAJOR="9"
+TAP_WIN32_MIN_MINOR="8"
+
+cat >>confdefs.h <<_ACEOF
+#define TAP_ID "${TAP_ID}"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define TAP_WIN32_MIN_MAJOR ${TAP_WIN32_MIN_MAJOR}
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define TAP_WIN32_MIN_MINOR ${TAP_WIN32_MIN_MINOR}
+_ACEOF
+
+
+
+
+
+win32datadir="\${datadir}/${PACKAGE}-win32"
+
+ if test "${WIN32}" = "yes"; then
+ WIN32_TRUE=
+ WIN32_FALSE='#'
+else
+ WIN32_TRUE='#'
+ WIN32_FALSE=
+fi
+
+
+# workaround for <autoconf-2.60
+if test -z "${docdir}"; then
+ docdir="\$(datadir)/doc/\$(PACKAGE_NAME)"
+
+fi
+if test -z "${htmldir}"; then
+ htmldir="\$(docdir)"
+
+fi
+# end workaround
+
+ac_config_files="$ac_config_files t_client.sh"
+
+ac_config_files="$ac_config_files Makefile openvpn.spec images/Makefile service-win32/Makefile install-win32/Makefile install-win32/settings"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ test "x$cache_file" != "x/dev/null" &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ cat confcache >$cache_file
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then
+ as_fn_error $? "conditional \"WIN32\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in #(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by OpenVPN $as_me 2.2.1, which was
+generated by GNU Autoconf 2.66. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <openvpn-users@lists.sourceforge.net>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+OpenVPN config.status 2.2.1
+configured by $0, generated by GNU Autoconf 2.66,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "t_client.sh") CONFIG_FILES="$CONFIG_FILES t_client.sh" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "openvpn.spec") CONFIG_FILES="$CONFIG_FILES openvpn.spec" ;;
+ "images/Makefile") CONFIG_FILES="$CONFIG_FILES images/Makefile" ;;
+ "service-win32/Makefile") CONFIG_FILES="$CONFIG_FILES service-win32/Makefile" ;;
+ "install-win32/Makefile") CONFIG_FILES="$CONFIG_FILES install-win32/Makefile" ;;
+ "install-win32/settings") CONFIG_FILES="$CONFIG_FILES install-win32/settings" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp=
+ trap 'exit_status=$?
+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_t=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_t"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$tmp/stdin"
+ case $ac_file in
+ -) cat "$tmp/out" && rm -f "$tmp/out";;
+ *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
+ } >"$tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "t_client.sh":F) chmod +x t_client.sh ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..e0847bc
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,916 @@
+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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+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
+dnl along with this program (see the file COPYING included with this
+dnl distribution); if not, write to the Free Software Foundation, Inc.,
+dnl 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.50)
+
+m4_include(version.m4)
+AC_INIT([OpenVPN], [PRODUCT_VERSION], [openvpn-users@lists.sourceforge.net], [openvpn])
+AM_CONFIG_HEADER(config.h)
+AC_CONFIG_SRCDIR(syshead.h)
+
+dnl Guess host type.
+AC_CANONICAL_HOST
+AM_INIT_AUTOMAKE(openvpn, [$PACKAGE_VERSION])
+
+AC_ARG_WITH(cygwin-native,
+ [ --with-cygwin-native Compile native win32],
+ [CYGWIN_NATIVE="${withval}"],
+ [CYGWIN_NATIVE="no"]
+)
+
+WIN32="no"
+CYGWIN="no"
+case "${host}" in
+ *-mingw*)
+ WIN32="yes"
+ cross_compiling="yes"
+ ;;
+ *-*-cygwin*)
+ AC_MSG_CHECKING([cygwin mode to use])
+ if test "${CYGWIN_NATIVE}" = "yes"; then
+ AC_MSG_RESULT([Using native win32])
+ CFLAGS="${CFLAGS} -mno-cygwin"
+ CYGWIN="yes"
+ WIN32="yes"
+ else
+ AC_MSG_RESULT([Using cygwin])
+ fi
+ ;;
+ *)
+ ;;
+esac
+
+AC_ARG_ENABLE(lzo,
+ [ --disable-lzo Disable LZO compression support],
+ [LZO="$enableval"],
+ [LZO="yes"]
+)
+
+AC_ARG_ENABLE(crypto,
+ [ --disable-crypto Disable OpenSSL crypto support],
+ [CRYPTO="$enableval"],
+ [CRYPTO="yes"]
+)
+
+AC_ARG_ENABLE(ssl,
+ [ --disable-ssl Disable OpenSSL SSL support for TLS-based key exchange],
+ [SSL="$enableval"],
+ [SSL="yes"]
+)
+
+AC_ARG_ENABLE(x509-alt-username,
+ [ --enable-x509-alt-username Enable the --x509-username-field feature],
+ [X509ALTUSERNAME="$enableval"],
+ [X509ALTUSERNAME="no"]
+)
+
+AC_ARG_ENABLE(multi,
+ [ --disable-multi Disable client/server support (--mode server + client mode)],
+ [MULTI="$enableval"],
+ [MULTI="yes"]
+)
+
+AC_ARG_ENABLE(server,
+ [ --disable-server Disable server support only (but retain client support)],
+ [MULTI_SERVER="$enableval"],
+ [MULTI_SERVER="yes"]
+)
+
+AC_ARG_ENABLE(plugins,
+ [ --disable-plugins Disable plug-in support],
+ [PLUGINS="$enableval"],
+ [PLUGINS="yes"]
+)
+
+AC_ARG_ENABLE(eurephia,
+ [ --disable-eurephia Disable support for the eurephia plug-in],
+ [EUREPHIA="$enableval"],
+ [EUREPHIA="yes"]
+)
+
+AC_ARG_ENABLE(management,
+ [ --disable-management Disable management server support],
+ [MANAGEMENT="$enableval"],
+ [MANAGEMENT="yes"]
+)
+
+AC_ARG_ENABLE(pkcs11,
+ [ --disable-pkcs11 Disable pkcs11 support],
+ [PKCS11="$enableval"],
+ [PKCS11="yes"]
+)
+
+AC_ARG_ENABLE(socks,
+ [ --disable-socks Disable Socks support],
+ [SOCKS="$enableval"],
+ [SOCKS="yes"]
+)
+
+AC_ARG_ENABLE(http,
+ [ --disable-http Disable HTTP proxy support],
+ [HTTP_PROXY="$enableval"],
+ [HTTP_PROXY="yes"]
+)
+
+AC_ARG_ENABLE(fragment,
+ [ --disable-fragment Disable internal fragmentation support (--fragment)],
+ [FRAGMENT="$enableval"],
+ [FRAGMENT="yes"]
+)
+
+AC_ARG_ENABLE(multihome,
+ [ --disable-multihome Disable multi-homed UDP server support (--multihome)],
+ [MULTIHOME="$enableval"],
+ [MULTIHOME="yes"]
+)
+
+AC_ARG_ENABLE(port-share,
+ [ --disable-port-share Disable TCP server port-share support (--port-share)],
+ [PORT_SHARE="$enableval"],
+ [PORT_SHARE="yes"]
+)
+
+AC_ARG_ENABLE(debug,
+ [ --disable-debug Disable debugging support (disable gremlin and verb 7+ messages)],
+ [DEBUG="$enableval"],
+ [DEBUG="yes"]
+)
+
+AC_ARG_ENABLE(small,
+ [ --enable-small Enable smaller executable size (disable OCC, usage message, and verb 4 parm list)],
+ [SMALL="$enableval"],
+ [SMALL="no"]
+)
+
+AC_ARG_ENABLE(password-save,
+ [ --enable-password-save Allow --askpass and --auth-user-pass passwords to be read from a file],
+ [PASSWORD_SAVE="$enableval"],
+ [PASSWORD_SAVE="no"]
+)
+
+AC_ARG_ENABLE(iproute2,
+ [ --enable-iproute2 Enable support for iproute2],
+ test $enableval = "yes" && AC_DEFINE(CONFIG_FEATURE_IPROUTE, 1, [enable iproute2 support])
+)
+
+AC_ARG_ENABLE(def-auth,
+ [ --disable-def-auth Disable deferred authentication],
+ [DEF_AUTH="$enableval"],
+ [DEF_AUTH="yes"]
+)
+
+AC_ARG_ENABLE(pf,
+ [ --disable-pf Disable internal packet filter],
+ [PF="$enableval"],
+ [PF="yes"]
+)
+
+AC_ARG_ENABLE(strict,
+ [ --enable-strict Enable strict compiler warnings (debugging option)],
+ [STRICT="$enableval"],
+ [STRICT="no"]
+)
+
+AC_ARG_ENABLE(pedantic,
+ [ --enable-pedantic Enable pedantic compiler warnings, will not generate a working executable (debugging option)],
+ [PEDANTIC="$enableval"],
+ [PEDANTIC="no"]
+)
+
+AC_ARG_ENABLE(profiling,
+ [ --enable-profiling Enable profiling (debugging option)],
+ [PROFILE="$enableval"],
+ [PROFILE="no"]
+)
+
+AC_ARG_ENABLE(strict-options,
+ [ --enable-strict-options Enable strict options check between peers (debugging option)],
+ [STRICT_OPTIONS="$enableval"],
+ [STRICT_OPTIONS="no"]
+)
+
+AC_ARG_ENABLE(selinux,
+ [ --disable-selinux Disable SELinux support],
+ [SELINUX="$enableval"],
+ [SELINUX="yes"]
+)
+
+AC_ARG_WITH(ssl-headers,
+ [ --with-ssl-headers=DIR Crypto/SSL Include files location],
+ [CS_HDR_DIR="$withval"]
+ [CPPFLAGS="$CPPFLAGS -I$withval"]
+)
+
+AC_ARG_WITH(ssl-lib,
+ [ --with-ssl-lib=DIR Crypto/SSL Library location],
+ [LDFLAGS="$LDFLAGS -L$withval"]
+)
+
+AC_ARG_WITH(lzo-headers,
+ [ --with-lzo-headers=DIR LZO Include files location],
+ [LZO_HDR_DIR="$withval"]
+ [CPPFLAGS="$CPPFLAGS -I$withval"]
+)
+
+AC_ARG_WITH(lzo-lib,
+ [ --with-lzo-lib=DIR LZO Library location],
+ [LDFLAGS="$LDFLAGS -L$withval"]
+)
+
+AC_ARG_WITH(pkcs11-helper-headers,
+ [ --with-pkcs11-helper-headers=DIR pkcs11-helper Include files location],
+ [PKCS11_HELPER_HDR_DIR="$withval"]
+ [CPPFLAGS="$CPPFLAGS -I$withval"]
+)
+
+AC_ARG_WITH(pkcs11-helper-lib,
+ [ --with-pkcs11-helper-lib=DIR pkcs11-helper Library location],
+ [LDFLAGS="$LDFLAGS -L$withval"]
+)
+
+AC_ARG_WITH(ifconfig-path,
+ [ --with-ifconfig-path=PATH Path to ifconfig tool],
+ [IFCONFIG="$withval"],
+ [AC_PATH_PROG([IFCONFIG], [ifconfig], [ifconfig], [$PATH:/usr/local/sbin:/usr/sbin:/sbin])]
+)
+AC_DEFINE_UNQUOTED(IFCONFIG_PATH, "$IFCONFIG", [Path to ifconfig tool])
+
+AC_ARG_WITH(iproute-path,
+ [ --with-iproute-path=PATH Path to iproute tool],
+ [IPROUTE="$withval"],
+ [AC_PATH_PROG([IPROUTE], [ip], [ip], [$PATH:/usr/local/sbin:/usr/sbin:/sbin])]
+)
+AC_DEFINE_UNQUOTED(IPROUTE_PATH, "$IPROUTE", [Path to iproute tool])
+
+
+AC_ARG_WITH(route-path,
+ [ --with-route-path=PATH Path to route tool],
+ [ROUTE="$withval"],
+ [AC_PATH_PROG([ROUTE], [route], [route], [$PATH:/usr/local/sbin:/usr/sbin:/sbin])]
+)
+AC_DEFINE_UNQUOTED(ROUTE_PATH, "$ROUTE", [Path to route tool])
+
+AC_ARG_WITH(netstat-path,
+ [ --with-netstat-path=PATH Path to netstat tool],
+ [NETSTAT="$withval"],
+ [AC_PATH_PROG([NETSTAT], [netstat], [netstat], [$PATH:/usr/local/sbin:/usr/sbin:/sbin:/etc])]
+)
+AC_DEFINE_UNQUOTED(NETSTAT_PATH, "$NETSTAT", [Path to netstat tool])
+
+AC_ARG_WITH(mem-check,
+ [ --with-mem-check=TYPE Build with debug memory checking, TYPE = dmalloc or valgrind],
+ [MEMCHECK="$withval"]
+)
+
+dnl fix search path, to allow compilers to find syshead.h
+CPPFLAGS="$CPPFLAGS -I${srcdir}"
+
+dnl check host OS
+openvpn_host=$host
+if test $host_alias; then
+ openvpn_host=$host_alias
+fi
+AC_DEFINE_UNQUOTED(TARGET_ALIAS, "$openvpn_host", [A string representing our host])
+case "$host" in
+*-*-linux*)
+ AC_DEFINE(TARGET_LINUX, 1, [Are we running on Linux?])
+ dnl RH9 SSL headers workaround
+ if test -z $CS_HDR_DIR && test "$CRYPTO" = "yes"; then
+ CPPFLAGS="$CPPFLAGS $(pkg-config --cflags openssl 2>/dev/null)"
+ fi
+ ;;
+*-*-solaris*)
+ AC_DEFINE(TARGET_SOLARIS, 1, [Are we running on Solaris?])
+ ;;
+*-*-openbsd*)
+ AC_DEFINE(TARGET_OPENBSD, 1, [Are we running on OpenBSD?])
+ ;;
+*-*-freebsd*)
+ AC_DEFINE(TARGET_FREEBSD, 1, [Are we running on FreeBSD?])
+ ;;
+*-*-netbsd*)
+ AC_DEFINE(TARGET_NETBSD, 1, [Are we running NetBSD?])
+ ;;
+*-*-darwin*)
+ dnl some Mac OS X tendering (we use vararg macros...)
+ AC_DEFINE(TARGET_DARWIN, 1, [Are we running on Mac OS X?])
+ CPPFLAGS="$CPPFLAGS -no-cpp-precomp"
+ ;;
+*-mingw*)
+ AC_DEFINE(TARGET_WIN32, 1, [Are we running WIN32?])
+ CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN"
+ OPENVPN_ADD_LIBS(-lgdi32)
+ OPENVPN_ADD_LIBS(-lws2_32)
+ OPENVPN_ADD_LIBS(-lwininet)
+ OPENVPN_ADD_LIBS(-lcrypt32)
+ OPENVPN_ADD_LIBS(-liphlpapi)
+ OPENVPN_ADD_LIBS(-lwinmm)
+ ;;
+*-*-dragonfly*)
+ AC_DEFINE(TARGET_DRAGONFLY, 1, [Are we running on DragonFlyBSD?])
+ ;;
+
+esac
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_GCC_TRADITIONAL
+AC_GNU_SOURCE
+
+if test "${WIN32}" = "yes"; then
+ AC_ARG_VAR([MAN2HTML], [man2html utility])
+ AC_CHECK_PROGS([MAN2HTML], [man2html])
+ test -z "${MAN2HTML}" && AC_MSG_ERROR([man2html is required for win32])
+fi
+
+dnl Checks for header files.
+AC_HEADER_STDC
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+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
+TYPE_SOCKLEN_T
+AC_HEADER_TIME
+AX_CPP_VARARG_MACRO_ISO
+AX_CPP_VARARG_MACRO_GCC
+AX_EMPTY_ARRAY
+
+dnl Check for more header files.
+AC_CHECK_HEADERS(fcntl.h stdlib.h dnl
+ stdarg.h stdio.h string.h dnl
+ strings.h ctype.h errno.h dnl
+)
+
+if test "${WIN32}" != "yes"; then
+ AC_HEADER_SYS_WAIT
+ AC_CHECK_HEADERS(sys/time.h sys/socket.h sys/un.h sys/ioctl.h sys/stat.h dnl
+ sys/mman.h fcntl.h sys/file.h stdlib.h stdint.h dnl
+ stdarg.h unistd.h signal.h stdio.h string.h dnl
+ strings.h ctype.h errno.h syslog.h pwd.h grp.h dnl
+ net/if_tun.h net/tun/if_tun.h stropts.h sys/sockio.h dnl
+ netinet/in.h netinet/in_systm.h dnl
+ netinet/tcp.h arpa/inet.h dnl
+ netdb.h sys/uio.h linux/if_tun.h linux/sockios.h dnl
+ linux/types.h sys/poll.h sys/epoll.h err.h dnl
+ )
+ AC_CHECK_HEADERS(net/if.h,,,
+ [#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_SYS_SOCKET_H
+ # include <sys/socket.h>
+ #endif
+ ])
+ AC_CHECK_HEADERS(netinet/ip.h,,,
+ [#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_SYSTM_H
+ # include <netinet/in_systm.h>
+ #endif
+ ])
+ AC_CHECK_HEADERS(netinet/if_ether.h,,,
+ [#ifdef HAVE_SYS_TYPES_H
+ # include <sys/types.h>
+ #endif
+ #ifdef HAVE_SYS_SOCKET_H
+ # include <sys/socket.h>
+ #endif
+ #ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+ ])
+ AC_CHECK_HEADERS(resolv.h,,,
+ [#ifdef HAVE_NETINET_IN_H
+ # include <netinet/in.h>
+ #endif
+ ])
+ AC_CHECK_HEADERS(linux/errqueue.h,,,
+ [#ifdef HAVE_LINUX_TYPES_H
+ # include <linux/types.h>
+ #endif
+ ])
+fi
+
+AC_CACHE_SAVE
+
+dnl check that in_addr_t is defined
+AC_CHECK_TYPE(
+ [in_addr_t],
+ [],
+ [AC_DEFINE(in_addr_t, uint32_t, [Some systems don't define in_addr_t])],
+ [#include "syshead.h"])
+
+dnl check for basic types
+AC_CHECK_TYPE(
+ [uint8_t],
+ [],
+ [AC_DEFINE(uint8_t, unsigned char, [8-bit unsigned type])],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [uint16_t],
+ [],
+ [AC_DEFINE(uint16_t, unsigned char, [16-bit unsigned type])],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [uint32_t],
+ [],
+ [AC_DEFINE(uint32_t, unsigned long, [32-bit unsigned type])],
+ [#include "syshead.h"])
+
+dnl check for IPv6 types
+AC_CHECK_TYPE(
+ [struct tun_pi],
+ [AC_DEFINE(HAVE_TUN_PI, 1, [struct tun_pi needed for IPv6 support])],
+ [],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [struct iphdr],
+ [AC_DEFINE(HAVE_IPHDR, 1, [struct iphdr needed for IPv6 support])],
+ [],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [struct iovec],
+ [AC_DEFINE(HAVE_IOVEC, 1, [struct iovec needed for IPv6 support])],
+ [],
+ [#include "syshead.h"])
+
+dnl check for extended socket error types
+AC_CHECK_TYPE(
+ [struct sock_extended_err],
+ [AC_DEFINE(HAVE_SOCK_EXTENDED_ERR, 1, [struct sock_extended_err needed for extended socket error support])],
+ [],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [struct msghdr],
+ [AC_DEFINE(HAVE_MSGHDR, 1, [struct msghdr needed for extended socket error support])],
+ [],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [struct cmsghdr],
+ [AC_DEFINE(HAVE_CMSGHDR, 1, [struct cmsghdr needed for extended socket error support])],
+ [],
+ [#include "syshead.h"])
+AC_CHECK_TYPE(
+ [struct in_pktinfo],
+ [AC_DEFINE(HAVE_IN_PKTINFO, 1, [struct in_pktinfo needed for IP_PKTINFO support])],
+ [],
+ [#include "syshead.h"])
+
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned long)
+
+AC_CACHE_SAVE
+
+AC_CHECK_FUNCS([ctime memset vsnprintf strdup], ,
+ [AC_MSG_ERROR([Required library function not found])])
+AC_CHECK_FUNCS(daemon chroot getpwnam setuid nice system getpid dup dup2 dnl
+ getpass strerror syslog openlog mlockall getgrnam setgid dnl
+ setgroups stat flock readv writev time dnl
+ setsid chdir putenv getpeername unlink dnl
+ chsize ftruncate execve getpeereid umask)
+
+# 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])
+m4_define([SOCKET_OPT_FUNCS], [setsockopt getsockopt getsockname poll])
+
+if test "${WIN32}" = "yes"; then
+
+ AC_DEFINE([HAVE_GETTIMEOFDAY], [1], [We fake gettimeofday for win32 at otime.c])
+ 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
+
+ dnl check for other types
+ AC_TYPE_SIGNAL
+
+ dnl Check for libsocket
+ AC_SEARCH_LIBS(socket, socket)
+
+ dnl Check for libnsl
+ AC_SEARCH_LIBS(inet_ntoa, nsl)
+
+ dnl Check for libresolv
+ AC_SEARCH_LIBS(gethostbyname, resolv nsl)
+
+ dnl optional library functions
+ AC_FUNC_FORK
+
+ AC_CHECK_FUNCS(gettimeofday)
+
+ AC_CHECK_FUNCS(SOCKET_FUNCS, ,
+ [AC_MSG_ERROR([Required library function not found])])
+ AC_CHECK_FUNCS(SOCKET_OPT_FUNCS sendmsg recvmsg)
+
+fi
+
+dnl Required library functions
+AC_FUNC_MEMCMP
+
+dnl
+dnl Check for res_init
+dnl
+AC_TRY_LINK([
+ #include <resolv.h>
+ ], [
+ res_init ();
+ ], [
+ AC_MSG_RESULT([res_init DEFINED])
+ AC_DEFINE([HAVE_RES_INIT], 1, [Indicates if res_init is available])
+ ], [
+ AC_MSG_RESULT([res_init UNDEFINED])
+ ])
+
+dnl
+dnl check libraries
+dnl
+
+dnl Checking for a working epoll
+AC_CHECKING([for working epoll implementation])
+OLDLDFLAGS="$LDFLAGS"
+LDFLAGS="$LDFLAGS -Wl,--fatal-warnings"
+AC_CHECK_FUNC(epoll_create, AC_DEFINE(HAVE_EPOLL_CREATE, 1, [epoll_create function is defined]))
+LDFLAGS="$OLDLDFLAGS"
+
+dnl
+dnl check for valgrind tool
+dnl
+
+if test "$MEMCHECK" = "valgrind"; then
+ AC_CHECKING([for valgrind tool and Header files])
+ AC_CHECK_HEADER(valgrind/memcheck.h,
+ [
+ AC_DEFINE(USE_VALGRIND, 1, [Use valgrind memory debugging library])
+ CFLAGS="-g -fno-inline"
+ ],
+ [AC_MSG_ERROR([valgrind headers not found.])]
+ )
+fi
+
+dnl
+dnl check for dmalloc library
+dnl
+
+if test "$MEMCHECK" = "dmalloc"; then
+ AC_CHECKING([for dmalloc Library and Header files])
+ AC_CHECK_HEADER(dmalloc.h,
+ [AC_CHECK_LIB(dmalloc, malloc,
+ [
+ OPENVPN_ADD_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.])]
+ )
+fi
+
+dnl
+dnl Check for dlopen -- first try libc then libdl.
+dnl
+if test "${WIN32}" != "yes"; then
+ if test "$PLUGINS" = "yes"; then
+ AC_CHECKING([for libdl Library and Header files])
+ AC_CHECK_HEADER(dlfcn.h,
+ [AC_CHECK_FUNC(dlopen,
+ [AC_DEFINE(USE_LIBDL, 1, [Use libdl for dynamic library loading])],
+ [AC_CHECK_LIB(dl, dlopen,
+ [
+ OPENVPN_ADD_LIBS(-ldl)
+ AC_DEFINE(USE_LIBDL, 1, [Use libdl for dynamic library loading])
+ ],
+ [AC_MSG_RESULT([libdl library not found.])]
+ )],
+ )],
+ [AC_MSG_RESULT([libdl headers not found.])]
+ )
+ if test "$EUREPHIA" = "yes"; then
+ AC_DEFINE(ENABLE_EUREPHIA, 1, [Enable support for the eurephia plug-in])
+ fi
+ fi
+fi
+
+dnl
+dnl Check if LoadLibrary exists on Windows
+dnl
+if test "${WIN32}" = "yes"; then
+ if test "$PLUGINS" = "yes"; then
+ AC_TRY_LINK([
+ #include <windows.h>
+ ], [
+ LoadLibrary (NULL);
+ ], [
+ AC_MSG_RESULT([LoadLibrary DEFINED])
+ AC_DEFINE(USE_LOAD_LIBRARY, 1, [Use LoadLibrary to load DLLs on Windows])
+ ], [
+ AC_MSG_RESULT([LoadLibrary UNDEFINED])
+ ])
+ fi
+fi
+
+dnl
+dnl check for LZO library
+dnl
+
+if test "$LZO" = "yes"; then
+ LZO_H=""
+ AC_CHECKING([for LZO Library and Header files])
+ AC_CHECK_HEADER(lzo/lzo1x.h,
+ [ LZO_H="2"
+ lzolibs="lzo2 lzo"
+ AC_DEFINE(LZO_HEADER_DIR, 1, [Use lzo/ directory prefix for LZO header files (for LZO 2.0)])
+ ],
+ [ AC_CHECK_HEADER(lzo1x.h, [ LZO_H="1" ; lzolibs=lzo ]) ]
+ )
+
+ if test -n "$LZO_H"; then
+ havelzolib=0
+ for i in $lzolibs ; do
+ if test $havelzolib = 1 ; then break ; fi
+ AC_CHECK_LIB($i, lzo1x_1_15_compress,
+ [
+ OPENVPN_ADD_LIBS(-l$i)
+ AC_DEFINE(USE_LZO, 1, [Use LZO compression library])
+ AC_DEFINE_UNQUOTED(LZO_VERSION_NUM, "$LZO_H", [LZO version number])
+ havelzolib=1
+ ]
+ )
+ done
+ if test $havelzolib = 0 ; then
+ AC_MSG_ERROR([LZO headers were found but LZO library was not found])
+ fi
+ else
+ AC_MSG_RESULT([LZO headers were not found])
+ AC_MSG_RESULT([LZO library available from http://www.oberhumer.com/opensource/lzo/])
+ AC_MSG_ERROR([Or try ./configure --disable-lzo])
+ fi
+fi
+
+dnl
+dnl check for OpenSSL-crypto library
+dnl
+
+if test "$CRYPTO" = "yes"; then
+ AC_CHECKING([for OpenSSL Crypto Library and Header files])
+ AC_CHECK_HEADER(openssl/evp.h,,
+ [AC_MSG_ERROR([OpenSSL Crypto headers not found.])])
+
+ for lib in crypto eay32; do
+ AC_CHECK_LIB($lib, EVP_CIPHER_CTX_init,
+ [
+ cryptofound=1
+ OPENVPN_ADD_LIBS(-l$lib)
+ ]
+ )
+ done
+
+ test -n "$cryptofound" || AC_MSG_ERROR([OpenSSL Crypto library not found.])
+
+ AC_MSG_CHECKING([that OpenSSL Library is at least version 0.9.6])
+ AC_EGREP_CPP(yes,
+ [
+ #include <openssl/evp.h>
+ #if SSLEAY_VERSION_NUMBER >= 0x00906000L
+ yes
+ #endif
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(USE_CRYPTO, 1, [Use OpenSSL crypto library])
+ AC_CHECK_FUNCS(EVP_CIPHER_CTX_set_key_length)
+
+ dnl check for OpenSSL crypto acceleration capability
+ AC_CHECK_HEADERS(openssl/engine.h)
+ AC_CHECK_FUNCS(ENGINE_load_builtin_engines)
+ AC_CHECK_FUNCS(ENGINE_register_all_complete)
+ AC_CHECK_FUNCS(ENGINE_cleanup)
+ ],
+ [AC_MSG_ERROR([OpenSSL crypto Library is too old.])]
+ )
+
+dnl
+dnl check for OpenSSL-SSL library
+dnl
+
+ if test "$SSL" = "yes"; then
+ AC_CHECKING([for OpenSSL SSL Library and Header files])
+ AC_CHECK_HEADER(openssl/ssl.h,,
+ [AC_MSG_ERROR([OpenSSL SSL headers not found.])]
+ )
+
+ for lib in ssl ssl32; do
+ AC_CHECK_LIB($lib, SSL_CTX_new,
+ [
+ sslfound=1
+ OPENVPN_ADD_LIBS(-l$lib)
+ ]
+ )
+ done
+
+ test -n "${sslfound}" || AC_MSG_ERROR([OpenSSL SSL library not found.])
+
+ if test "$MEMCHECK" = "ssl"; then
+ AC_CHECKING([for Memory Debugging Capabilities in OpenSSL Library])
+ AC_CHECK_LIB(ssl, CRYPTO_mem_ctrl,
+ [
+ AC_DEFINE(CRYPTO_MDEBUG, 1, [Use memory debugging function in OpenSSL])
+ AC_MSG_RESULT([NOTE: OpenSSL library must be compiled with CRYPTO_MDEBUG])
+ ],
+ [AC_MSG_ERROR([Memory Debugging function in OpenSSL library not found.])]
+ )
+ fi
+
+ AC_DEFINE(USE_SSL, 1, [Use OpenSSL SSL library])
+ fi
+fi
+
+dnl enable --x509-username-field feature if requested
+if test "$X509ALTUSERNAME" = "yes"; then
+ AC_DEFINE(ENABLE_X509ALTUSERNAME, 1, [Enable --x509-username-field feature])
+fi
+
+dnl enable pkcs11 capability
+if test "$PKCS11" = "yes"; then
+ AC_CHECKING([for pkcs11-helper Library and Header files])
+ AC_CHECK_HEADER(pkcs11-helper-1.0/pkcs11h-core.h,
+ [AC_CHECK_LIB(pkcs11-helper, pkcs11h_initialize,
+ [
+ AC_DEFINE(USE_PKCS11, 1, [Enable PKCS11 capability])
+ OPENVPN_ADD_LIBS(-lpkcs11-helper)
+ ],
+ [AC_MSG_RESULT([pkcs11-helper library not found.])]
+ )],
+ [AC_MSG_RESULT([pkcs11-helper headers not found.])]
+ )
+fi
+
+dnl enable multi-client mode
+if test "$MULTI" = "yes"; then
+ AC_DEFINE(ENABLE_CLIENT_SERVER, 1, [Enable client/server capability])
+fi
+
+dnl enable client mode only, not server
+if test "$MULTI_SERVER" = "no"; then
+ AC_DEFINE(ENABLE_CLIENT_ONLY, 1, [Enable client capability only])
+fi
+
+dnl enable management server capability
+if test "$MANAGEMENT" = "yes"; then
+ AC_DEFINE(ENABLE_MANAGEMENT, 1, [Enable management server capability])
+fi
+
+dnl enable socks
+if test "$SOCKS" = "yes"; then
+ AC_DEFINE(ENABLE_SOCKS, 1, [Enable Socks proxy support])
+fi
+
+dnl enable HTTP proxy
+if test "$HTTP_PROXY" = "yes"; then
+ AC_DEFINE(ENABLE_HTTP_PROXY, 1, [Enable HTTP proxy support])
+fi
+
+dnl compile --multihome option
+if test "$MULTIHOME" = "yes"; then
+ AC_DEFINE(ENABLE_MULTIHOME, 1, [Enable multi-homed UDP server capability])
+fi
+
+dnl enable debugging
+if test "$DEBUG" = "yes"; then
+ AC_DEFINE(ENABLE_DEBUG, 1, [Enable debugging support])
+fi
+
+dnl enable small size optimizations
+if test "$SMALL" = "yes"; then
+ AC_DEFINE(ENABLE_SMALL, 1, [Enable smaller executable size])
+fi
+
+dnl enable --fragment
+if test "$FRAGMENT" = "yes"; then
+ AC_DEFINE(ENABLE_FRAGMENT, 1, [Enable internal fragmentation support])
+fi
+
+dnl enable --port-share
+if test "$PORT_SHARE" = "yes"; then
+ AC_DEFINE(ENABLE_PORT_SHARE, 1, [Enable TCP Server port sharing])
+fi
+
+dnl enable deferred auth
+if test "$DEF_AUTH" = "yes"; then
+ AC_DEFINE(CONFIGURE_DEF_AUTH, 1, [Enable deferred authentication])
+fi
+
+dnl enable internal packet filter
+if test "$PF" = "yes"; then
+ AC_DEFINE(CONFIGURE_PF, 1, [Enable internal packet filter])
+fi
+
+dnl enable strict compiler warnings
+if test "$STRICT" = "yes"; then
+ CFLAGS="$CFLAGS -Wall -Wno-unused-parameter -Wno-unused-function"
+fi
+
+dnl enable pedantic compiler warnings
+if test "$PEDANTIC" = "yes"; then
+ CFLAGS="$CFLAGS -ansi -pedantic"
+fi
+
+dnl enable profiling
+if test "$PROFILE" = "yes"; then
+ CFLAGS="$CFLAGS -pg -DENABLE_PROFILING"
+fi
+
+dnl enable strict options check between peers
+if test "$STRICT_OPTIONS" = "yes"; then
+ AC_DEFINE(STRICT_OPTIONS_CHECK, 1, [Enable strict options check between peers])
+fi
+
+dnl enable password save
+if test "$PASSWORD_SAVE" = "yes"; then
+ AC_DEFINE(ENABLE_PASSWORD_SAVE, 1, [Allow --askpass and --auth-user-pass passwords to be read from a file])
+fi
+
+dnl
+dnl check for SELinux library and headers
+dnl
+if test "$SELINUX" = "yes"; then
+ AC_CHECKING([for libselinux Library and Header files])
+ AC_CHECK_HEADER(selinux/selinux.h,
+ [AC_CHECK_LIB(selinux, setcon,
+ [
+ OPENVPN_ADD_LIBS(-lselinux)
+ AC_DEFINE(HAVE_SETCON, 1, [SELinux support])
+ ],
+ [AC_MSG_RESULT([SELinux library not found.])]
+ )],
+ [AC_MSG_RESULT([SELinux headers not found.])]
+ )
+fi
+
+TAP_ID="PRODUCT_TAP_ID"
+TAP_WIN32_MIN_MAJOR="PRODUCT_TAP_WIN32_MIN_MAJOR"
+TAP_WIN32_MIN_MINOR="PRODUCT_TAP_WIN32_MIN_MINOR"
+AC_DEFINE_UNQUOTED(TAP_ID, "${TAP_ID}", [The TAP-Win32 id defined in tap-win32/SOURCES])
+AC_DEFINE_UNQUOTED(TAP_WIN32_MIN_MAJOR, ${TAP_WIN32_MIN_MAJOR}, [The TAP-Win32 version number is defined in tap-win32/SOURCES])
+AC_DEFINE_UNQUOTED(TAP_WIN32_MIN_MINOR, ${TAP_WIN32_MIN_MINOR}, [The TAP-Win32 version number is defined in tap-win32/SOURCES])
+AC_SUBST(TAP_ID)
+AC_SUBST(TAP_WIN32_MIN_MAJOR)
+AC_SUBST(TAP_WIN32_MIN_MINOR)
+
+win32datadir="\${datadir}/${PACKAGE}-win32"
+AC_SUBST(win32datadir)
+AM_CONDITIONAL(WIN32, test "${WIN32}" = "yes")
+
+# workaround for <autoconf-2.60
+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
+# end workaround
+
+AC_CONFIG_FILES([t_client.sh], [chmod +x t_client.sh])
+AC_OUTPUT([
+ Makefile
+ openvpn.spec
+ images/Makefile
+ service-win32/Makefile
+ install-win32/Makefile
+ install-win32/settings
+])
diff --git a/configure_h.awk b/configure_h.awk
new file mode 100644
index 0000000..672e745
--- /dev/null
+++ b/configure_h.awk
@@ -0,0 +1,39 @@
+#
+# 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) 2010 David Sommerseth <dazo@users.sourceforge.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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+#
+# This script will build up a line which can be included into a C program.
+# The line will contain all interesting #define statements from f.ex. ./config.h
+#
+
+BEGIN {
+ printf ("#define CONFIGURE_DEFINES \"")
+}
+
+/^#define (ENABLE|DISABLE|DEPRECATED|USE)_/ {
+ printf (" %s", $2)
+}
+
+END {
+ printf ("\"\n")
+}
diff --git a/configure_log.awk b/configure_log.awk
new file mode 100644
index 0000000..099e5c4
--- /dev/null
+++ b/configure_log.awk
@@ -0,0 +1,33 @@
+#
+# 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) 2010 David Sommerseth <dazo@users.sourceforge.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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+#
+# This script will build up a line which can be included into a C program.
+# The line will only contain the first entry of the ./configure line from
+# ./config.log.
+#
+
+/\$ (.*)\/configure/ {
+ printf ("#define CONFIGURE_CALL \"%s\"\n", $0)
+ exit 0
+}
diff --git a/contrib/OCSP_check/OCSP_check.sh b/contrib/OCSP_check/OCSP_check.sh
new file mode 100644
index 0000000..847be45
--- /dev/null
+++ b/contrib/OCSP_check/OCSP_check.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+
+# Sample script to perform OCSP queries with OpenSSL
+# given a certificate serial number.
+
+# If you run your own CA, you can set up a very simple
+# OCSP server using the -port option to "openssl ocsp".
+
+# Full documentation and examples:
+# http://www.openssl.org/docs/apps/ocsp.html
+
+
+# Edit the following values to suit your needs
+
+# OCSP responder URL (mandatory)
+# YOU MUST UNCOMMENT ONE OF THESE AND SET IT TO A VALID SERVER
+#ocsp_url="http://ocsp.example.com/"
+#ocsp_url="https://ocsp.secure.example.com/"
+
+# Path to issuer certificate (mandatory)
+# YOU MUST SET THIS TO THE PATH TO THE CA CERTIFICATE
+issuer="/path/to/CAcert.crt"
+
+# use a nonce in the query, set to "-no_nonce" to not use it
+nonce="-nonce"
+
+# Verify the response
+# YOU MUST SET THIS TO THE PATH TO THE RESPONSE VERIFICATION CERT
+verify="/path/to/CAcert.crt"
+
+# Depth in the certificate chain where the cert to verify is.
+# Set to -1 to run the verification at every level (NOTE that
+# in that case you need a more complex script as the various
+# parameters for the query will likely be different at each level)
+# "0" is the usual value here, where the client certificate is
+check_depth=0
+
+cur_depth=$1 # this is the *CURRENT* depth
+common_name=$2 # CN in case you need it
+
+# minimal sanity checks
+
+err=0
+if [ -z "$issuer" ] || [ ! -e "$issuer" ]; then
+ echo "Error: issuer certificate undefined or not found!" >&2
+ err=1
+fi
+
+if [ -z "$verify" ] || [ ! -e "$verify" ]; then
+ echo "Error: verification certificate undefined or not found!" >&2
+ err=1
+fi
+
+if [ -z "$ocsp_url" ]; then
+ echo "Error: OCSP server URL not defined!" >&2
+ err=1
+fi
+
+if [ $err -eq 1 ]; then
+ echo "Did you forget to customize the variables in the script?" >&2
+ exit 1
+fi
+
+# begin
+if [ $check_depth -eq -1 ] || [ $cur_depth -eq $check_depth ]; then
+
+ eval serial="\$tls_serial_${cur_depth}"
+
+ # To successfully complete, the following must happen:
+ #
+ # - The serial number must not be empty
+ # - The exit status of "openssl ocsp" must be zero
+ # - The output of the above command must contain the line
+ # "0x${serial}: good"
+ #
+ # Everything else fails with exit status 1.
+
+ if [ -n "$serial" ]; then
+
+ # This is only an example; you are encouraged to run this command (without
+ # redirections) manually against your or your CA's OCSP server to see how
+ # it responds, and adapt accordingly.
+ # Sample output that is assumed here:
+ #
+ # Response verify OK
+ # 0x428740A5: good
+ # This Update: Apr 24 19:38:49 2010 GMT
+ # Next Update: May 2 14:23:42 2010 GMT
+ #
+ # NOTE: It is needed to check the exit code of OpenSSL explicitly. OpenSSL
+ # can in some circumstances give a "good" result if it could not
+ # reach the the OSCP server. In this case, the exit code will indicate
+ # if OpenSSL itself failed or not. If OpenSSL's exit code is not 0,
+ # don't trust the OpenSSL status.
+
+ status=$(openssl ocsp -issuer "$issuer" \
+ "$nonce" \
+ -CAfile "$verify" \
+ -url "$ocsp_url" \
+ -serial "0x${serial}" 2>/dev/null)
+
+ if [ $? -eq 0 ]; then
+ # check that it's good
+ if echo "$status" | grep -Fq "0x${serial}: good"; then
+ exit 0
+ fi
+ fi
+ fi
+ # if we get here, something was wrong
+ exit 1
+fi
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..e1a57d0
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,2 @@
+This directory contains scripts and patches contributed
+by users.
diff --git a/contrib/multilevel-init.patch b/contrib/multilevel-init.patch
new file mode 100644
index 0000000..8d48fbc
--- /dev/null
+++ b/contrib/multilevel-init.patch
@@ -0,0 +1,79 @@
+--- /etc/init.d/openvpn 2004-05-12 20:30:06.000000000 +0200
++++ openvpn 2004-05-12 20:34:33.000000000 +0200
+@@ -58,13 +58,13 @@
+ # returning success or failure status to caller (James Yonan).
+
+ # Location of openvpn binary
+-openvpn="/usr/sbin/openvpn"
++openvpn=/usr/sbin/openvpn
+
+ # Lockfile
+-lock="/var/lock/subsys/openvpn"
++lock=/var/lock/subsys/openvpn
+
+ # PID directory
+-piddir="/var/run/openvpn"
++piddir=/var/run/openvpn
+
+ # Our working directory
+ work=/etc/openvpn
+@@ -106,7 +106,7 @@
+
+ if [ -f $lock ]; then
+ # we were not shut down correctly
+- for pidf in `/bin/ls $piddir/*.pid $piddir/*/*.pid 2>/dev/null`; do
++ for pidf in `find $piddir -name "*.pid" 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+@@ -116,12 +116,12 @@
+ sleep 2
+ fi
+
+- rm -f $piddir/*.pid $piddir/*/*.pid
++ find $piddir -name "*.pid"|xargs rm -f
+
+ # Start every .conf in $work and run .sh if exists
+ errors=0
+ successes=0
+- for c in `/bin/ls *.conf */*.conf 2>/dev/null`; do
++ for c in `find * -name "*.conf" 2>/dev/null`; do
+ bn=${c%%.conf}
+ if [ -f "$bn.sh" ]; then
+ . $bn.sh
+@@ -147,7 +147,7 @@
+ ;;
+ stop)
+ echo -n $"Shutting down openvpn: "
+- for pidf in `/bin/ls $piddir/*.pid $piddir/*/*.pid 2>/dev/null`; do
++ for pidf in `find $piddir -name "*.pid" 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+@@ -163,7 +163,7 @@
+ ;;
+ reload)
+ if [ -f $lock ]; then
+- for pidf in `/bin/ls $piddir/*.pid $piddir/*/*.pid 2>/dev/null`; do
++ for pidf in `find $piddir -name "*.pid" 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -HUP `cat $pidf` >/dev/null 2>&1
+ fi
+@@ -175,7 +175,7 @@
+ ;;
+ reopen)
+ if [ -f $lock ]; then
+- for pidf in `/bin/ls $piddir/*.pid $piddir/*/*.pid 2>/dev/null`; do
++ for pidf in `find $piddir -name "*.pid" 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR1 `cat $pidf` >/dev/null 2>&1
+ fi
+@@ -195,7 +195,7 @@
+ ;;
+ status)
+ if [ -f $lock ]; then
+- for pidf in `/bin/ls $piddir/*.pid $piddir/*/*.pid 2>/dev/null`; do
++ for pidf in `find $piddir -name "*.pid" 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR2 `cat $pidf` >/dev/null 2>&1
+ fi
diff --git a/contrib/openvpn-fwmarkroute-1.00/README b/contrib/openvpn-fwmarkroute-1.00/README
new file mode 100644
index 0000000..66fe61a
--- /dev/null
+++ b/contrib/openvpn-fwmarkroute-1.00/README
@@ -0,0 +1,44 @@
+OpenVPN fwmark Routing
+Sean Reifschneider, <jafo@tummy.com>
+Thursday November 27, 2003
+==========================
+
+These scripts can be used with OpenVPN up and down scripts to set up
+routing on a Linux system such that the VPN traffic is sent via normal
+network connectivity, but other traffic to that network runs over the VPN.
+The idea is to allow encryption of data to the network the remote host is
+on, without interfering with the VPN traffic. You can't simply add a route
+to the remote network, becaues that will cause the VPN traffic to also try
+to run over the VPN, and breaks the VPN.
+
+These scripts use the Linux "fwmark" iptables rules to specify routing
+based not only on IP address, but also by port and protocol. This allows
+you to effectively say "if the packet is to this IP address on this port
+using this protocol, then use the normal default gateway, otherwise use the
+VPN gateway.
+
+This is set up on the client VPN system, not the VPN server. These scripts
+also set up all ICMP echo-responses to run across the VPN. You can
+comment the lines in the scripts to disable this, but I find this useful
+at coffee shops which have networks that block ICMP.
+
+To configure this, you need to set up these scripts as your up and down
+scripts in the config file. You will need to set these values in the
+config file:
+
+ up /etc/openvpn/fwmarkroute.up
+ down /etc/openvpn/fwmarkroute.down
+ up-restart
+ up-delay
+
+ setenv remote_netmask_bits 24
+
+Note: For this to work, you can't set the "user" or "group" config options,
+because then the scripts will not run as root.
+
+The last setting allows you to control the size of the network the remote
+system is on. The remote end has to be set up to route, probably with
+masquerading or NAT. The network this netmask relates to is calculated
+using the value of "remote" in the conf file.
+
+Sean
diff --git a/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.down b/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.down
new file mode 100755
index 0000000..87d67d4
--- /dev/null
+++ b/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.down
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Bring down vpn routing.
+
+# calculate the network address
+remote_network=`ipcalc -n "$remote"/"$remote_netmask_bits"`
+remote_network="${remote_network#*=}"
+
+# clear routing via VPN
+ip route del "$remote_network"/"$remote_netmask_bits" via "$5" table vpn.out
+ip route del table vpnonly.out via "$5"
+iptables -D OUTPUT -t mangle -p "$proto" \
+ -d "$remote_network"/"$remote_netmask_bits" \
+ --dport "$remote_port" -j ACCEPT
+iptables -D OUTPUT -t mangle -d "$remote" -j MARK --set-mark 2
+
+# undo the ICMP ping tunneling
+iptables -D OUTPUT -t mangle --protocol icmp --icmp-type echo-request \
+ -j MARK --set-mark 3
+
+# flush route cache
+ip route flush cache
diff --git a/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.up b/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.up
new file mode 100755
index 0000000..661ec31
--- /dev/null
+++ b/contrib/openvpn-fwmarkroute-1.00/fwmarkroute.up
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Bring up vpn routing.
+
+# calculate the network address
+remote_network=`ipcalc -n "$remote"/"$remote_netmask_bits"`
+remote_network="${remote_network#*=}"
+
+# add the stuff that doesn't change if it's not already there
+grep -q '^202 ' /etc/iproute2/rt_tables
+if [ "$?" -ne 0 ]
+then
+ echo 202 vpn.out >> /etc/iproute2/rt_tables
+fi
+grep -q '^203 ' /etc/iproute2/rt_tables
+if [ "$?" -ne 0 ]
+then
+ echo 203 vpnonly.out >> /etc/iproute2/rt_tables
+fi
+ip rule ls | grep -q 'lookup vpn.out *$'
+if [ "$?" -ne 0 ]
+then
+ ip rule add fwmark 2 table vpn.out
+fi
+ip rule ls | grep -q 'lookup vpnonly.out *$'
+if [ "$?" -ne 0 ]
+then
+ ip rule add fwmark 3 table vpnonly.out
+fi
+
+# route VPN traffic using the normal table
+iptables -A OUTPUT -t mangle -p "$proto" -d "$remote" --dport "$remote_port" \
+ -j ACCEPT
+
+# route all other traffic to that host via VPN
+iptables -A OUTPUT -t mangle -d "$remote_network"/"$remote_netmask_bits" \
+ -j MARK --set-mark 2
+
+# route all ICMP pings over the VPN
+iptables -A OUTPUT -t mangle --protocol icmp --icmp-type echo-request \
+ -j MARK --set-mark 3
+
+# NAT traffic going over the VPN, so it doesn't have an unknown address
+iptables -t nat -A POSTROUTING -o "$1" -j SNAT --to-source "$4"
+
+# add routing commands
+ip route add "$remote_network"/"$remote_netmask_bits" via "$5" table vpn.out
+ip route add table vpnonly.out via "$5"
+ip route flush cache
diff --git a/contrib/pull-resolv-conf/client.down b/contrib/pull-resolv-conf/client.down
new file mode 100644
index 0000000..05f2d4d
--- /dev/null
+++ b/contrib/pull-resolv-conf/client.down
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# Copyright (c) 2005-2010 OpenVPN Technologies, Inc.
+# Licensed under the GPL version 2
+
+# First version by Jesse Adelman
+# someone at boldandbusted dink com
+# http://www.boldandbusted.com/
+
+# PURPOSE: This script automatically removes the /etc/resolv.conf entries previously
+# set by the companion script "client.up".
+
+# INSTALL NOTES:
+# Place this in /etc/openvpn/client.down
+# Then, add the following to your /etc/openvpn/<clientconfig>.conf:
+# client
+# up /etc/openvpn/client.up
+# down /etc/openvpn/client.down
+# Next, "chmod a+x /etc/openvpn/client.down"
+
+# USAGE NOTES:
+# Note that this script is best served with the companion "client.up"
+# script.
+
+# Tested under Debian lenny with OpenVPN 2.1_rc11
+# It should work with any UNIX with a POSIX sh, /etc/resolv.conf or resolvconf
+
+# This runs with the context of the OpenVPN UID/GID
+# at the time of execution. This generally means that
+# the client "up" script will run fine, but the "down" script
+# will require the use of the OpenVPN "down-root" plugin
+# which is in the plugins/ directory of the OpenVPN source tree
+
+# A horrid work around, from a security perspective,
+# is to run OpenVPN as root. THIS IS NOT RECOMMENDED. You have
+# been WARNED.
+PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
+
+if type resolvconf >/dev/null 2>&1; then
+ resolvconf -d "${1}" -f
+elif [ -e /etc/resolv.conf.ovpnsave ] ; then
+ # cp + rm rather than mv in case it's a symlink
+ cp /etc/resolv.conf.ovpnsave /etc/resolv.conf
+ rm -f /etc/resolv.conf.ovpnsave
+fi
+
+exit 0
diff --git a/contrib/pull-resolv-conf/client.up b/contrib/pull-resolv-conf/client.up
new file mode 100644
index 0000000..b28d4d1
--- /dev/null
+++ b/contrib/pull-resolv-conf/client.up
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+# Copyright (c) 2005-2010 OpenVPN Technologies, Inc.
+# Licensed under the GPL version 2
+
+# First version by Jesse Adelman
+# someone at boldandbusted dink com
+# http://www.boldandbusted.com/
+
+# PURPOSE: This script automatically sets the proper /etc/resolv.conf entries
+# as pulled down from an OpenVPN server.
+
+# INSTALL NOTES:
+# Place this in /etc/openvpn/client.up
+# Then, add the following to your /etc/openvpn/<clientconfig>.conf:
+# client
+# up /etc/openvpn/client.up
+# Next, "chmod a+x /etc/openvpn/client.up"
+
+# USAGE NOTES:
+# Note that this script is best served with the companion "client.down"
+# script.
+
+# Tested under Debian lenny with OpenVPN 2.1_rc11
+# It should work with any UNIX with a POSIX sh, /etc/resolv.conf or resolvconf
+
+# This runs with the context of the OpenVPN UID/GID
+# at the time of execution. This generally means that
+# the client "up" script will run fine, but the "down" script
+# will require the use of the OpenVPN "down-root" plugin
+# which is in the plugins/ directory of the OpenVPN source tree
+
+# A horrid work around, from a security perspective,
+# is to run OpenVPN as root. THIS IS NOT RECOMMENDED. You have
+# been WARNED.
+PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
+
+# init variables
+
+i=1
+domains=
+fopt=
+ndoms=0
+nns=0
+nl='
+'
+
+# $foreign_option_<n> is something like
+# "dhcp-option DOMAIN example.com" (multiple allowed)
+# or
+# "dhcp-option DNS 10.10.10.10" (multiple allowed)
+
+# each DNS option becomes a "nameserver" option in resolv.con
+# if we get one DOMAIN, that becomes "domain" in resolv.conf
+# if we get multiple DOMAINS, those become "search" lines in resolv.conf
+
+while true; do
+ eval fopt=\$foreign_option_${i}
+ [ -z "${fopt}" ] && break
+
+ case ${fopt} in
+ dhcp-option\ DOMAIN\ *)
+ ndoms=$((ndoms + 1))
+ domains="${domains} ${fopt#dhcp-option DOMAIN }"
+ ;;
+ dhcp-option\ DNS\ *)
+ nns=$((nns + 1))
+ if [ $nns -le 3 ]; then
+ dns="${dns}${dns:+$nl}nameserver ${fopt#dhcp-option DNS }"
+ else
+ printf "%s\n" "Too many nameservers - ignoring after third" >&2
+ fi
+ ;;
+ *)
+ printf "%s\n" "Unknown option \"${fopt}\" - ignored" >&2
+ ;;
+ esac
+ i=$((i + 1))
+done
+
+ds=domain
+if [ $ndoms -gt 1 ]; then
+ ds=search
+fi
+
+# This is the complete file - "$domains" has a leading space already
+out="# resolv.conf autogenerated by ${0} (${1})${nl}${dns}${nl}${ds}${domains}"
+
+# use resolvconf if it's available
+if type resolvconf >/dev/null 2>&1; then
+ printf "%s\n" "${out}" | resolvconf -p -a "${1}"
+else
+ # Preserve the existing resolv.conf
+ if [ -e /etc/resolv.conf ] ; then
+ cp /etc/resolv.conf /etc/resolv.conf.ovpnsave
+ fi
+ printf "%s\n" "${out}" > /etc/resolv.conf
+ chmod 644 /etc/resolv.conf
+fi
+
+exit 0
diff --git a/crypto.c b/crypto.c
new file mode 100644
index 0000000..5cfc34a
--- /dev/null
+++ b/crypto.c
@@ -0,0 +1,1841 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef USE_CRYPTO
+
+#include "crypto.h"
+#include "error.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+/*
+ * Check for key size creepage.
+ */
+
+#if MAX_CIPHER_KEY_LENGTH < EVP_MAX_KEY_LENGTH
+#warning Some OpenSSL EVP ciphers now support key lengths greater than MAX_CIPHER_KEY_LENGTH -- consider increasing MAX_CIPHER_KEY_LENGTH
+#endif
+
+#if MAX_HMAC_KEY_LENGTH < EVP_MAX_MD_SIZE
+#warning Some OpenSSL HMAC message digests now support key lengths greater than MAX_HMAC_KEY_LENGTH -- consider increasing MAX_HMAC_KEY_LENGTH
+#endif
+
+/*
+ * Encryption and Compression Routines.
+ *
+ * On entry, buf contains the input data and length.
+ * On exit, it should be set to the output data and length.
+ *
+ * If buf->len is <= 0 we should return
+ * If buf->len is set to 0 on exit it tells the caller to ignore the packet.
+ *
+ * work is a workspace buffer we are given of size BUF_SIZE.
+ * work may be used to return output data, or the input buffer
+ * may be modified and returned as output. If output data is
+ * returned in work, the data should start after FRAME_HEADROOM bytes
+ * of padding to leave room for downstream routines to prepend.
+ *
+ * Up to a total of FRAME_HEADROOM bytes may be prepended to the input buf
+ * by all routines (encryption, decryption, compression, and decompression).
+ *
+ * Note that the buf_prepend return will assert if we try to
+ * make a header bigger than FRAME_HEADROOM. This should not
+ * happen unless the frame parameters are wrong.
+ */
+
+#define CRYPT_ERROR(format) \
+ do { msg (D_CRYPT_ERRORS, "%s: " format, error_prefix); goto error_exit; } while (false)
+
+void
+openvpn_encrypt (struct buffer *buf, struct buffer work,
+ const struct crypto_options *opt,
+ const struct frame* frame)
+{
+ struct gc_arena gc;
+ gc_init (&gc);
+
+ if (buf->len > 0 && opt->key_ctx_bi)
+ {
+ struct key_ctx *ctx = &opt->key_ctx_bi->encrypt;
+
+ /* Do Encrypt from buf -> work */
+ if (ctx->cipher)
+ {
+ uint8_t iv_buf[EVP_MAX_IV_LENGTH];
+ const int iv_size = EVP_CIPHER_CTX_iv_length (ctx->cipher);
+ const unsigned int mode = EVP_CIPHER_CTX_mode (ctx->cipher);
+ int outlen;
+
+ if (mode == EVP_CIPH_CBC_MODE)
+ {
+ CLEAR (iv_buf);
+
+ /* generate pseudo-random IV */
+ if (opt->flags & CO_USE_IV)
+ prng_bytes (iv_buf, iv_size);
+
+ /* Put packet ID in plaintext buffer or IV, depending on cipher mode */
+ if (opt->packet_id)
+ {
+ struct packet_id_net pin;
+ packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
+ ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
+ }
+ }
+ else if (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE)
+ {
+ struct packet_id_net pin;
+ struct buffer b;
+
+ ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */
+ ASSERT (opt->packet_id); /* for this mode. */
+
+ packet_id_alloc_outgoing (&opt->packet_id->send, &pin, true);
+ memset (iv_buf, 0, iv_size);
+ buf_set_write (&b, iv_buf, iv_size);
+ ASSERT (packet_id_write (&pin, &b, true, false));
+ }
+ else /* We only support CBC, CFB, or OFB modes right now */
+ {
+ ASSERT (0);
+ }
+
+ /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
+ ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+ /* set the IV pseudo-randomly */
+ if (opt->flags & CO_USE_IV)
+ dmsg (D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));
+
+ dmsg (D_PACKET_CONTENT, "ENCRYPT FROM: %s",
+ format_hex (BPTR (buf), BLEN (buf), 80, &gc));
+
+ /* cipher_ctx was already initialized with key & keylen */
+ ASSERT (EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv_buf, DO_ENCRYPT));
+
+ /* Buffer overflow check */
+ if (!buf_safe (&work, buf->len + EVP_CIPHER_CTX_block_size (ctx->cipher)))
+ {
+ msg (D_CRYPT_ERRORS, "ENCRYPT: buffer size error, bc=%d bo=%d bl=%d wc=%d wo=%d wl=%d cbs=%d",
+ buf->capacity,
+ buf->offset,
+ buf->len,
+ work.capacity,
+ work.offset,
+ work.len,
+ EVP_CIPHER_CTX_block_size (ctx->cipher));
+ goto err;
+ }
+
+ /* Encrypt packet ID, payload */
+ ASSERT (EVP_CipherUpdate_ov (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf)));
+ work.len += outlen;
+
+ /* Flush the encryption buffer */
+ ASSERT (EVP_CipherFinal (ctx->cipher, BPTR (&work) + outlen, &outlen));
+ work.len += outlen;
+ ASSERT (outlen == iv_size);
+
+ /* prepend the IV to the ciphertext */
+ if (opt->flags & CO_USE_IV)
+ {
+ uint8_t *output = buf_prepend (&work, iv_size);
+ ASSERT (output);
+ memcpy (output, iv_buf, iv_size);
+ }
+
+ dmsg (D_PACKET_CONTENT, "ENCRYPT TO: %s",
+ format_hex (BPTR (&work), BLEN (&work), 80, &gc));
+ }
+ else /* No Encryption */
+ {
+ if (opt->packet_id)
+ {
+ struct packet_id_net pin;
+ packet_id_alloc_outgoing (&opt->packet_id->send, &pin, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM));
+ ASSERT (packet_id_write (&pin, buf, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM), true));
+ }
+ work = *buf;
+ }
+
+ /* HMAC the ciphertext (or plaintext if !cipher) */
+ if (ctx->hmac)
+ {
+ int hmac_len;
+ uint8_t *output;
+
+ HMAC_Init_ex (ctx->hmac, NULL, 0, NULL, NULL);
+ HMAC_Update (ctx->hmac, BPTR (&work), BLEN (&work));
+ output = buf_prepend (&work, HMAC_size (ctx->hmac));
+ ASSERT (output);
+ HMAC_Final (ctx->hmac, output, (unsigned int *)&hmac_len);
+ ASSERT (hmac_len == HMAC_size (ctx->hmac));
+ }
+
+ *buf = work;
+ }
+
+ gc_free (&gc);
+ return;
+
+ err:
+ ERR_clear_error ();
+ buf->len = 0;
+ gc_free (&gc);
+ return;
+}
+
+/*
+ * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet.
+ *
+ * Set buf->len to 0 and return false on decrypt error.
+ *
+ * On success, buf is set to point to plaintext, true
+ * is returned.
+ */
+bool
+openvpn_decrypt (struct buffer *buf, struct buffer work,
+ const struct crypto_options *opt,
+ const struct frame* frame)
+{
+ static const char error_prefix[] = "Authenticate/Decrypt packet error";
+ struct gc_arena gc;
+ gc_init (&gc);
+
+ if (buf->len > 0 && opt->key_ctx_bi)
+ {
+ struct key_ctx *ctx = &opt->key_ctx_bi->decrypt;
+ struct packet_id_net pin;
+ bool have_pin = false;
+
+ /* Verify the HMAC */
+ if (ctx->hmac)
+ {
+ int hmac_len;
+ uint8_t local_hmac[MAX_HMAC_KEY_LENGTH]; /* HMAC of ciphertext computed locally */
+ int in_hmac_len;
+
+ HMAC_Init_ex (ctx->hmac, NULL, 0, NULL, NULL);
+
+ /* Assume the length of the input HMAC */
+ hmac_len = HMAC_size (ctx->hmac);
+
+ /* Authentication fails if insufficient data in packet for HMAC */
+ if (buf->len < hmac_len)
+ CRYPT_ERROR ("missing authentication info");
+
+ HMAC_Update (ctx->hmac, BPTR (buf) + hmac_len,
+ BLEN (buf) - hmac_len);
+ HMAC_Final (ctx->hmac, local_hmac, (unsigned int *)&in_hmac_len);
+ ASSERT (hmac_len == in_hmac_len);
+
+ /* Compare locally computed HMAC with packet HMAC */
+ if (memcmp (local_hmac, BPTR (buf), hmac_len))
+ CRYPT_ERROR ("packet HMAC authentication failed");
+
+ ASSERT (buf_advance (buf, hmac_len));
+ }
+
+ /* Decrypt packet ID + payload */
+
+ if (ctx->cipher)
+ {
+ const unsigned int mode = EVP_CIPHER_CTX_mode (ctx->cipher);
+ const int iv_size = EVP_CIPHER_CTX_iv_length (ctx->cipher);
+ uint8_t iv_buf[EVP_MAX_IV_LENGTH];
+ int outlen;
+
+ /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */
+ ASSERT (buf_init (&work, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_DECRYPT)));
+
+ /* use IV if user requested it */
+ CLEAR (iv_buf);
+ if (opt->flags & CO_USE_IV)
+ {
+ if (buf->len < iv_size)
+ CRYPT_ERROR ("missing IV info");
+ memcpy (iv_buf, BPTR (buf), iv_size);
+ ASSERT (buf_advance (buf, iv_size));
+ }
+
+ /* show the IV's initial state */
+ if (opt->flags & CO_USE_IV)
+ dmsg (D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex (iv_buf, iv_size, 0, &gc));
+
+ if (buf->len < 1)
+ CRYPT_ERROR ("missing payload");
+
+ /* ctx->cipher was already initialized with key & keylen */
+ if (!EVP_CipherInit_ov (ctx->cipher, NULL, NULL, iv_buf, DO_DECRYPT))
+ CRYPT_ERROR ("cipher init failed");
+
+ /* Buffer overflow check (should never happen) */
+ if (!buf_safe (&work, buf->len))
+ CRYPT_ERROR ("buffer overflow");
+
+ /* Decrypt packet ID, payload */
+ if (!EVP_CipherUpdate_ov (ctx->cipher, BPTR (&work), &outlen, BPTR (buf), BLEN (buf)))
+ CRYPT_ERROR ("cipher update failed");
+ work.len += outlen;
+
+ /* Flush the decryption buffer */
+ if (!EVP_CipherFinal (ctx->cipher, BPTR (&work) + outlen, &outlen))
+ CRYPT_ERROR ("cipher final failed");
+ work.len += outlen;
+
+ dmsg (D_PACKET_CONTENT, "DECRYPT TO: %s",
+ format_hex (BPTR (&work), BLEN (&work), 80, &gc));
+
+ /* Get packet ID from plaintext buffer or IV, depending on cipher mode */
+ {
+ if (mode == EVP_CIPH_CBC_MODE)
+ {
+ if (opt->packet_id)
+ {
+ if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
+ CRYPT_ERROR ("error reading CBC packet-id");
+ have_pin = true;
+ }
+ }
+ else if (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE)
+ {
+ struct buffer b;
+
+ ASSERT (opt->flags & CO_USE_IV); /* IV and packet-ID required */
+ ASSERT (opt->packet_id); /* for this mode. */
+
+ buf_set_read (&b, iv_buf, iv_size);
+ if (!packet_id_read (&pin, &b, true))
+ CRYPT_ERROR ("error reading CFB/OFB packet-id");
+ have_pin = true;
+ }
+ else /* We only support CBC, CFB, or OFB modes right now */
+ {
+ ASSERT (0);
+ }
+ }
+ }
+ else
+ {
+ work = *buf;
+ if (opt->packet_id)
+ {
+ if (!packet_id_read (&pin, &work, BOOL_CAST (opt->flags & CO_PACKET_ID_LONG_FORM)))
+ CRYPT_ERROR ("error reading packet-id");
+ have_pin = !BOOL_CAST (opt->flags & CO_IGNORE_PACKET_ID);
+ }
+ }
+
+ if (have_pin)
+ {
+ packet_id_reap_test (&opt->packet_id->rec);
+ if (packet_id_test (&opt->packet_id->rec, &pin))
+ {
+ packet_id_add (&opt->packet_id->rec, &pin);
+ if (opt->pid_persist && (opt->flags & CO_PACKET_ID_LONG_FORM))
+ packet_id_persist_save_obj (opt->pid_persist, opt->packet_id);
+ }
+ else
+ {
+ if (!(opt->flags & CO_MUTE_REPLAY_WARNINGS))
+ msg (D_REPLAY_ERRORS, "%s: bad packet ID (may be a replay): %s -- see the man page entry for --no-replay and --replay-window for more info or silence this warning with --mute-replay-warnings",
+ error_prefix, packet_id_net_print (&pin, true, &gc));
+ goto error_exit;
+ }
+ }
+ *buf = work;
+ }
+
+ gc_free (&gc);
+ return true;
+
+ error_exit:
+ ERR_clear_error ();
+ buf->len = 0;
+ gc_free (&gc);
+ return false;
+}
+
+/*
+ * How many bytes will we add to frame buffer for a given
+ * set of crypto options?
+ */
+void
+crypto_adjust_frame_parameters(struct frame *frame,
+ const struct key_type* kt,
+ bool cipher_defined,
+ bool use_iv,
+ bool packet_id,
+ bool packet_id_long_form)
+{
+ frame_add_to_extra_frame (frame,
+ (packet_id ? packet_id_size (packet_id_long_form) : 0) +
+ ((cipher_defined && use_iv) ? EVP_CIPHER_iv_length (kt->cipher) : 0) +
+ (cipher_defined ? EVP_CIPHER_block_size (kt->cipher) : 0) + /* worst case padding expansion */
+ kt->hmac_length);
+}
+
+static const EVP_CIPHER *
+get_cipher (const char *ciphername)
+{
+ const EVP_CIPHER *cipher = NULL;
+ ASSERT (ciphername);
+ cipher = EVP_get_cipherbyname (ciphername);
+ if ( !(cipher && cipher_ok (OBJ_nid2sn (EVP_CIPHER_nid (cipher)))))
+ msg (M_SSLERR, "Cipher algorithm '%s' not found", ciphername);
+ if (EVP_CIPHER_key_length (cipher) > MAX_CIPHER_KEY_LENGTH)
+ msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)",
+ ciphername,
+ EVP_CIPHER_key_length (cipher),
+ MAX_CIPHER_KEY_LENGTH);
+ return cipher;
+}
+
+static const EVP_MD *
+get_md (const char *digest)
+{
+ const EVP_MD *md = NULL;
+ ASSERT (digest);
+ md = EVP_get_digestbyname (digest);
+ if (!md)
+ msg (M_SSLERR, "Message hash algorithm '%s' not found", digest);
+ if (EVP_MD_size (md) > MAX_HMAC_KEY_LENGTH)
+ msg (M_FATAL, "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum hash size (%d bytes)",
+ digest,
+ EVP_MD_size (md),
+ MAX_HMAC_KEY_LENGTH);
+ return md;
+}
+
+static void
+init_cipher (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ struct key *key, const struct key_type *kt, int enc,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ EVP_CIPHER_CTX_init (ctx);
+ if (!EVP_CipherInit_ov (ctx, cipher, NULL, NULL, enc))
+ msg (M_SSLERR, "EVP cipher init #1");
+#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
+ if (!EVP_CIPHER_CTX_set_key_length (ctx, kt->cipher_length))
+ msg (M_SSLERR, "EVP set key size");
+#endif
+ if (!EVP_CipherInit_ov (ctx, NULL, key->cipher, NULL, enc))
+ msg (M_SSLERR, "EVP cipher init #2");
+
+ msg (D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key",
+ prefix,
+ OBJ_nid2sn (EVP_CIPHER_CTX_nid (ctx)),
+ EVP_CIPHER_CTX_key_length (ctx) * 8);
+
+ /* make sure we used a big enough key */
+ ASSERT (EVP_CIPHER_CTX_key_length (ctx) <= kt->cipher_length);
+
+ dmsg (D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix,
+ format_hex (key->cipher, kt->cipher_length, 0, &gc));
+ dmsg (D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d",
+ prefix,
+ EVP_CIPHER_CTX_block_size (ctx),
+ EVP_CIPHER_CTX_iv_length (ctx));
+
+ gc_free (&gc);
+}
+
+static void
+init_hmac (HMAC_CTX *ctx, const EVP_MD *digest,
+ struct key *key, const struct key_type *kt, const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ HMAC_CTX_init (ctx);
+ HMAC_Init_ex (ctx, key->hmac, kt->hmac_length, digest, NULL);
+ msg (D_HANDSHAKE,
+ "%s: Using %d bit message hash '%s' for HMAC authentication",
+ prefix, HMAC_size (ctx) * 8, OBJ_nid2sn (EVP_MD_type (digest)));
+
+ /* make sure we used a big enough key */
+ ASSERT (HMAC_size (ctx) <= kt->hmac_length);
+
+ dmsg (D_SHOW_KEYS, "%s: HMAC KEY: %s", prefix,
+ format_hex (key->hmac, kt->hmac_length, 0, &gc));
+ dmsg (D_CRYPTO_DEBUG, "%s: HMAC size=%d block_size=%d",
+ prefix,
+ EVP_MD_size (digest),
+ EVP_MD_block_size (digest));
+
+ gc_free (&gc);
+}
+
+/*
+ * Build a struct key_type.
+ */
+void
+init_key_type (struct key_type *kt, const char *ciphername,
+ bool ciphername_defined, const char *authname,
+ bool authname_defined, int keysize,
+ bool cfb_ofb_allowed, bool warn)
+{
+ CLEAR (*kt);
+ if (ciphername && ciphername_defined)
+ {
+ kt->cipher = get_cipher (ciphername);
+ kt->cipher_length = EVP_CIPHER_key_length (kt->cipher);
+ if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH)
+ kt->cipher_length = keysize;
+
+ /* check legal cipher mode */
+ {
+ const unsigned int mode = EVP_CIPHER_mode (kt->cipher);
+ if (!(mode == EVP_CIPH_CBC_MODE
+#ifdef ALLOW_NON_CBC_CIPHERS
+ || (cfb_ofb_allowed && (mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE))
+#endif
+ ))
+#ifdef ENABLE_SMALL
+ msg (M_FATAL, "Cipher '%s' mode not supported", ciphername);
+#else
+ msg (M_FATAL, "Cipher '%s' uses a mode not supported by " PACKAGE_NAME " in your current configuration. CBC mode is always supported, while CFB and OFB modes are supported only when using SSL/TLS authentication and key exchange mode, and when " PACKAGE_NAME " has been built with ALLOW_NON_CBC_CIPHERS.", ciphername);
+#endif
+ }
+ }
+ else
+ {
+ if (warn)
+ msg (M_WARN, "******* WARNING *******: null cipher specified, no encryption will be used");
+ }
+ if (authname && authname_defined)
+ {
+ kt->digest = get_md (authname);
+ kt->hmac_length = EVP_MD_size (kt->digest);
+ }
+ else
+ {
+ if (warn)
+ msg (M_WARN, "******* WARNING *******: null MAC specified, no authentication will be used");
+ }
+}
+
+const char *
+kt_cipher_name (const struct key_type *kt)
+{
+ if (kt->cipher)
+ return EVP_CIPHER_name (kt->cipher);
+ else
+ return "[null-cipher]";
+}
+
+const char *
+kt_digest_name (const struct key_type *kt)
+{
+ if (kt->digest)
+ return EVP_MD_name (kt->digest);
+ else
+ return "[null-digest]";
+}
+
+int
+kt_key_size (const struct key_type *kt)
+{
+ if (kt->cipher_length)
+ return kt->cipher_length * 8;
+ else if (kt->cipher)
+ return EVP_CIPHER_key_length (kt->cipher) * 8;
+ else
+ return 0;
+}
+
+/* given a key and key_type, build a key_ctx */
+void
+init_key_ctx (struct key_ctx *ctx, struct key *key,
+ const struct key_type *kt, int enc,
+ const char *prefix)
+{
+ CLEAR (*ctx);
+ if (kt->cipher && kt->cipher_length > 0)
+ {
+ ALLOC_OBJ (ctx->cipher, EVP_CIPHER_CTX);
+ init_cipher (ctx->cipher, kt->cipher, key, kt, enc, prefix);
+ }
+ if (kt->digest && kt->hmac_length > 0)
+ {
+ ALLOC_OBJ (ctx->hmac, HMAC_CTX);
+ init_hmac (ctx->hmac, kt->digest, key, kt, prefix);
+ }
+}
+
+void
+free_key_ctx (struct key_ctx *ctx)
+{
+ if (ctx->cipher)
+ {
+ EVP_CIPHER_CTX_cleanup (ctx->cipher);
+ free (ctx->cipher);
+ ctx->cipher = NULL;
+ }
+ if (ctx->hmac)
+ {
+ HMAC_CTX_cleanup (ctx->hmac);
+ free (ctx->hmac);
+ ctx->hmac = NULL;
+ }
+}
+
+void
+free_key_ctx_bi (struct key_ctx_bi *ctx)
+{
+ free_key_ctx(&ctx->encrypt);
+ free_key_ctx(&ctx->decrypt);
+}
+
+/*
+ * Return number of DES cblocks for the current
+ * key type or 0 if not a DES cipher.
+ */
+static int
+n_DES_cblocks (const struct key_type *kt)
+{
+ int ret = 0;
+ const char *name = OBJ_nid2sn (EVP_CIPHER_nid (kt->cipher));
+ if (name)
+ {
+ if (!strncmp (name, "DES-", 4))
+ {
+ ret = EVP_CIPHER_key_length (kt->cipher) / sizeof (DES_cblock);
+ }
+ else if (!strncmp (name, "DESX-", 5))
+ {
+ ret = 1;
+ }
+ }
+ dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: n_DES_cblocks=%d", ret);
+ return ret;
+}
+
+static bool
+check_key_DES (struct key *key, const struct key_type *kt, int ndc)
+{
+ int i;
+ struct buffer b;
+
+ buf_set_read (&b, key->cipher, kt->cipher_length);
+
+ for (i = 0; i < ndc; ++i)
+ {
+ DES_cblock *dc = (DES_cblock*) buf_read_alloc (&b, sizeof (DES_cblock));
+ if (!dc)
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: insufficient key material");
+ goto err;
+ }
+ if (DES_is_weak_key(dc))
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: weak key detected");
+ goto err;
+ }
+ if (!DES_check_key_parity (dc))
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: check_key_DES: bad parity detected");
+ goto err;
+ }
+ }
+ return true;
+
+ err:
+ ERR_clear_error ();
+ return false;
+}
+
+static void
+fixup_key_DES (struct key *key, const struct key_type *kt, int ndc)
+{
+ int i;
+ struct buffer b;
+
+ buf_set_read (&b, key->cipher, kt->cipher_length);
+ for (i = 0; i < ndc; ++i)
+ {
+ DES_cblock *dc = (DES_cblock*) buf_read_alloc(&b, sizeof(DES_cblock));
+ if (!dc)
+ {
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: fixup_key_DES: insufficient key material");
+ ERR_clear_error ();
+ return;
+ }
+ DES_set_odd_parity (dc);
+ }
+}
+
+static bool
+key_is_zero (struct key *key, const struct key_type *kt)
+{
+ int i;
+ for (i = 0; i < kt->cipher_length; ++i)
+ if (key->cipher[i])
+ return false;
+ msg (D_CRYPT_ERRORS, "CRYPTO INFO: WARNING: zero key detected");
+ return true;
+}
+
+/*
+ * Make sure that cipher key is a valid key for current key_type.
+ */
+bool
+check_key (struct key *key, const struct key_type *kt)
+{
+ if (kt->cipher)
+ {
+ /*
+ * Check for zero key
+ */
+ if (key_is_zero(key, kt))
+ return false;
+
+ /*
+ * Check for weak or semi-weak DES keys.
+ */
+ {
+ const int ndc = n_DES_cblocks (kt);
+ if (ndc)
+ return check_key_DES (key, kt, ndc);
+ else
+ return true;
+ }
+ }
+ return true;
+}
+
+/*
+ * Make safe mutations to key to ensure it is valid,
+ * such as ensuring correct parity on DES keys.
+ *
+ * This routine cannot guarantee it will generate a good
+ * key. You must always call check_key after this routine
+ * to make sure.
+ */
+void
+fixup_key (struct key *key, const struct key_type *kt)
+{
+ struct gc_arena gc = gc_new ();
+ if (kt->cipher)
+ {
+#ifdef ENABLE_DEBUG
+ const struct key orig = *key;
+#endif
+ const int ndc = n_DES_cblocks (kt);
+
+ if (ndc)
+ fixup_key_DES (key, kt, ndc);
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_CRYPTO_DEBUG))
+ {
+ if (memcmp (orig.cipher, key->cipher, kt->cipher_length))
+ dmsg (D_CRYPTO_DEBUG, "CRYPTO INFO: fixup_key: before=%s after=%s",
+ format_hex (orig.cipher, kt->cipher_length, 0, &gc),
+ format_hex (key->cipher, kt->cipher_length, 0, &gc));
+ }
+#endif
+ }
+ gc_free (&gc);
+}
+
+void
+check_replay_iv_consistency (const struct key_type *kt, bool packet_id, bool use_iv)
+{
+ if (cfb_ofb_mode (kt) && !(packet_id && use_iv))
+ msg (M_FATAL, "--no-replay or --no-iv cannot be used with a CFB or OFB mode cipher");
+}
+
+bool
+cfb_ofb_mode (const struct key_type* kt)
+{
+ if (kt->cipher) {
+ const unsigned int mode = EVP_CIPHER_mode (kt->cipher);
+ return mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE;
+ } else
+ return false;
+}
+
+/*
+ * Generate a random key. If key_type is provided, make
+ * sure generated key is valid for key_type.
+ */
+void
+generate_key_random (struct key *key, const struct key_type *kt)
+{
+ int cipher_len = MAX_CIPHER_KEY_LENGTH;
+ int hmac_len = MAX_HMAC_KEY_LENGTH;
+
+ struct gc_arena gc = gc_new ();
+
+ do {
+ CLEAR (*key);
+ if (kt)
+ {
+ if (kt->cipher && kt->cipher_length > 0 && kt->cipher_length <= cipher_len)
+ cipher_len = kt->cipher_length;
+
+ if (kt->digest && kt->hmac_length > 0 && kt->hmac_length <= hmac_len)
+ hmac_len = kt->hmac_length;
+ }
+ if (!RAND_bytes (key->cipher, cipher_len)
+ || !RAND_bytes (key->hmac, hmac_len))
+ msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for key generation");
+
+ dmsg (D_SHOW_KEY_SOURCE, "Cipher source entropy: %s", format_hex (key->cipher, cipher_len, 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE, "HMAC source entropy: %s", format_hex (key->hmac, hmac_len, 0, &gc));
+
+ if (kt)
+ fixup_key (key, kt);
+ } while (kt && !check_key (key, kt));
+
+ gc_free (&gc);
+}
+
+/*
+ * Print key material
+ */
+void
+key2_print (const struct key2* k,
+ const struct key_type *kt,
+ const char* prefix0,
+ const char* prefix1)
+{
+ struct gc_arena gc = gc_new ();
+ ASSERT (k->n == 2);
+ dmsg (D_SHOW_KEY_SOURCE, "%s (cipher): %s",
+ prefix0,
+ format_hex (k->keys[0].cipher, kt->cipher_length, 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE, "%s (hmac): %s",
+ prefix0,
+ format_hex (k->keys[0].hmac, kt->hmac_length, 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE, "%s (cipher): %s",
+ prefix1,
+ format_hex (k->keys[1].cipher, kt->cipher_length, 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE, "%s (hmac): %s",
+ prefix1,
+ format_hex (k->keys[1].hmac, kt->hmac_length, 0, &gc));
+ gc_free (&gc);
+}
+
+void
+test_crypto (const struct crypto_options *co, struct frame* frame)
+{
+ int i, j;
+ struct gc_arena gc = gc_new ();
+ struct buffer src = alloc_buf_gc (TUN_MTU_SIZE (frame), &gc);
+ struct buffer work = alloc_buf_gc (BUF_SIZE (frame), &gc);
+ struct buffer encrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc);
+ struct buffer decrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc);
+ struct buffer buf = clear_buf();
+
+ /* init work */
+ ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+ msg (M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode.");
+ for (i = 1; i <= TUN_MTU_SIZE (frame); ++i)
+ {
+ update_time ();
+
+ msg (M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i);
+
+ /*
+ * Load src with random data.
+ */
+ ASSERT (buf_init (&src, 0));
+ ASSERT (i <= src.capacity);
+ src.len = i;
+ ASSERT (RAND_pseudo_bytes (BPTR (&src), BLEN (&src)));
+
+ /* copy source to input buf */
+ buf = work;
+ memcpy (buf_write_alloc (&buf, BLEN (&src)), BPTR (&src), BLEN (&src));
+
+ /* encrypt */
+ openvpn_encrypt (&buf, encrypt_workspace, co, frame);
+
+ /* decrypt */
+ openvpn_decrypt (&buf, decrypt_workspace, co, frame);
+
+ /* compare */
+ if (buf.len != src.len)
+ msg (M_FATAL, "SELF TEST FAILED, src.len=%d buf.len=%d", src.len, buf.len);
+ for (j = 0; j < i; ++j)
+ {
+ const uint8_t in = *(BPTR (&src) + j);
+ const uint8_t out = *(BPTR (&buf) + j);
+ if (in != out)
+ msg (M_FATAL, "SELF TEST FAILED, pos=%d in=%d out=%d", j, in, out);
+ }
+ }
+ msg (M_INFO, PACKAGE_NAME " crypto self-test mode SUCCEEDED.");
+ gc_free (&gc);
+}
+
+#ifdef USE_SSL
+
+void
+get_tls_handshake_key (const struct key_type *key_type,
+ struct key_ctx_bi *ctx,
+ const char *passphrase_file,
+ const int key_direction,
+ const unsigned int flags)
+{
+ if (passphrase_file && key_type->hmac_length)
+ {
+ struct key2 key2;
+ struct key_type kt = *key_type;
+ struct key_direction_state kds;
+
+ /* for control channel we are only authenticating, not encrypting */
+ kt.cipher_length = 0;
+ kt.cipher = NULL;
+
+#if ENABLE_INLINE_FILES
+ if (flags & GHK_INLINE)
+ {
+ /* key was specified inline, key text is in passphrase_file */
+ read_key_file (&key2, passphrase_file, RKF_INLINE|RKF_MUST_SUCCEED);
+
+ /* succeeded? */
+ if (key2.n == 2)
+ msg (M_INFO, "Control Channel Authentication: tls-auth using INLINE static key file");
+ else
+ msg (M_FATAL, "INLINE tls-auth file lacks the requisite 2 keys");
+ }
+ else
+#endif
+ {
+ /* first try to parse as an OpenVPN static key file */
+ read_key_file (&key2, passphrase_file, 0);
+
+ /* succeeded? */
+ if (key2.n == 2)
+ {
+ msg (M_INFO,
+ "Control Channel Authentication: using '%s' as a " PACKAGE_NAME " static key file",
+ passphrase_file);
+ }
+ else
+ {
+ int hash_size;
+
+ CLEAR (key2);
+
+ /* failed, now try to get hash from a freeform file */
+ hash_size = read_passphrase_hash (passphrase_file,
+ kt.digest,
+ key2.keys[0].hmac,
+ MAX_HMAC_KEY_LENGTH);
+ ASSERT (hash_size == kt.hmac_length);
+
+ /* suceeded */
+ key2.n = 1;
+
+ msg (M_INFO,
+ "Control Channel Authentication: using '%s' as a free-form passphrase file",
+ passphrase_file);
+ }
+ }
+ /* handle key direction */
+
+ key_direction_state_init (&kds, key_direction);
+ must_have_n_keys (passphrase_file, "tls-auth", &key2, kds.need_keys);
+
+ /* initialize hmac key in both directions */
+
+ init_key_ctx (&ctx->encrypt, &key2.keys[kds.out_key], &kt, DO_ENCRYPT,
+ "Outgoing Control Channel Authentication");
+ init_key_ctx (&ctx->decrypt, &key2.keys[kds.in_key], &kt, DO_DECRYPT,
+ "Incoming Control Channel Authentication");
+
+ CLEAR (key2);
+ }
+ else
+ {
+ CLEAR (*ctx);
+ }
+}
+#endif
+
+/* header and footer for static key file */
+static const char static_key_head[] = "-----BEGIN OpenVPN Static key V1-----";
+static const char static_key_foot[] = "-----END OpenVPN Static key V1-----";
+
+static const char printable_char_fmt[] =
+ "Non-Hex character ('%c') found at line %d in key file '%s' (%d/%d/%d bytes found/min/max)";
+
+static const char unprintable_char_fmt[] =
+ "Non-Hex, unprintable character (0x%02x) found at line %d in key file '%s' (%d/%d/%d bytes found/min/max)";
+
+/* read key from file */
+
+void
+read_key_file (struct key2 *key2, const char *file, const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer in;
+ int fd, size;
+ uint8_t hex_byte[3] = {0, 0, 0};
+ const char *error_filename = file;
+
+ /* parse info */
+ const unsigned char *cp;
+ int hb_index = 0;
+ int line_num = 1;
+ int line_index = 0;
+ int match = 0;
+
+ /* output */
+ uint8_t* out = (uint8_t*) &key2->keys;
+ const int keylen = sizeof (key2->keys);
+ int count = 0;
+
+ /* parse states */
+# define PARSE_INITIAL 0
+# define PARSE_HEAD 1
+# define PARSE_DATA 2
+# define PARSE_DATA_COMPLETE 3
+# define PARSE_FOOT 4
+# define PARSE_FINISHED 5
+ int state = PARSE_INITIAL;
+
+ /* constants */
+ const int hlen = strlen (static_key_head);
+ const int flen = strlen (static_key_foot);
+ const int onekeylen = sizeof (key2->keys[0]);
+
+ CLEAR (*key2);
+
+ /*
+ * Key can be provided as a filename in 'file' or if RKF_INLINE
+ * is set, the actual key data itself in ascii form.
+ */
+#if ENABLE_INLINE_FILES
+ if (flags & RKF_INLINE) /* 'file' is a string containing ascii representation of key */
+ {
+ size = strlen (file) + 1;
+ buf_set_read (&in, (const uint8_t *)file, size);
+ error_filename = INLINE_FILE_TAG;
+ }
+ else /* 'file' is a filename which refers to a file containing the ascii key */
+#endif
+ {
+ in = alloc_buf_gc (2048, &gc);
+ fd = open (file, O_RDONLY);
+ if (fd == -1)
+ msg (M_ERR, "Cannot open file key file '%s'", file);
+ size = read (fd, in.data, in.capacity);
+ if (size < 0)
+ msg (M_FATAL, "Read error on key file ('%s')", file);
+ if (size == in.capacity)
+ msg (M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity);
+ close (fd);
+ }
+
+ cp = (unsigned char *)in.data;
+ while (size > 0)
+ {
+ const unsigned char c = *cp;
+
+#if 0
+ msg (M_INFO, "char='%c'[%d] s=%d ln=%d li=%d m=%d c=%d",
+ c, (int)c, state, line_num, line_index, match, count);
+#endif
+
+ if (c == '\n')
+ {
+ line_index = match = 0;
+ ++line_num;
+ }
+ else
+ {
+ /* first char of new line */
+ if (!line_index)
+ {
+ /* first char of line after header line? */
+ if (state == PARSE_HEAD)
+ state = PARSE_DATA;
+
+ /* first char of footer */
+ if ((state == PARSE_DATA || state == PARSE_DATA_COMPLETE) && c == '-')
+ state = PARSE_FOOT;
+ }
+
+ /* compare read chars with header line */
+ if (state == PARSE_INITIAL)
+ {
+ if (line_index < hlen && c == static_key_head[line_index])
+ {
+ if (++match == hlen)
+ state = PARSE_HEAD;
+ }
+ }
+
+ /* compare read chars with footer line */
+ if (state == PARSE_FOOT)
+ {
+ if (line_index < flen && c == static_key_foot[line_index])
+ {
+ if (++match == flen)
+ state = PARSE_FINISHED;
+ }
+ }
+
+ /* reading key */
+ if (state == PARSE_DATA)
+ {
+ if (isxdigit(c))
+ {
+ ASSERT (hb_index >= 0 && hb_index < 2);
+ hex_byte[hb_index++] = c;
+ if (hb_index == 2)
+ {
+ unsigned int u;
+ ASSERT(sscanf((const char *)hex_byte, "%x", &u) == 1);
+ *out++ = u;
+ hb_index = 0;
+ if (++count == keylen)
+ state = PARSE_DATA_COMPLETE;
+ }
+ }
+ else if (isspace(c))
+ ;
+ else
+ {
+ msg (M_FATAL,
+ (isprint (c) ? printable_char_fmt : unprintable_char_fmt),
+ c, line_num, error_filename, count, onekeylen, keylen);
+ }
+ }
+ ++line_index;
+ }
+ ++cp;
+ --size;
+ }
+
+ /*
+ * Normally we will read either 1 or 2 keys from file.
+ */
+ key2->n = count / onekeylen;
+
+ ASSERT (key2->n >= 0 && key2->n <= (int) SIZE (key2->keys));
+
+ if (flags & RKF_MUST_SUCCEED)
+ {
+ if (!key2->n)
+ msg (M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)",
+ error_filename, count, onekeylen, keylen);
+
+ if (state != PARSE_FINISHED)
+ msg (M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)",
+ error_filename, count, onekeylen, keylen);
+ }
+
+ /* zero file read buffer if not an inline file */
+#if ENABLE_INLINE_FILES
+ if (!(flags & RKF_INLINE))
+#endif
+ buf_clear (&in);
+
+ if (key2->n)
+ warn_if_group_others_accessible (error_filename);
+
+#if 0
+ /* DEBUGGING */
+ {
+ int i;
+ printf ("KEY READ, n=%d\n", key2->n);
+ for (i = 0; i < (int) SIZE (key2->keys); ++i)
+ {
+ /* format key as ascii */
+ const char *fmt = format_hex_ex ((const uint8_t*)&key2->keys[i],
+ sizeof (key2->keys[i]),
+ 0,
+ 16,
+ "\n",
+ &gc);
+ printf ("[%d]\n%s\n\n", i, fmt);
+ }
+ }
+#endif
+
+ /* pop our garbage collection level */
+ gc_free (&gc);
+}
+
+int
+read_passphrase_hash (const char *passphrase_file,
+ const EVP_MD *digest,
+ uint8_t *output,
+ int len)
+{
+ unsigned int outlen = 0;
+ EVP_MD_CTX md;
+
+ ASSERT (len >= EVP_MD_size (digest));
+ memset (output, 0, len);
+
+ EVP_DigestInit (&md, digest);
+
+ /* read passphrase file */
+ {
+ const int min_passphrase_size = 8;
+ uint8_t buf[64];
+ int total_size = 0;
+ int fd = open (passphrase_file, O_RDONLY);
+
+ if (fd == -1)
+ msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file);
+
+ for (;;)
+ {
+ int size = read (fd, buf, sizeof (buf));
+ if (size == 0)
+ break;
+ if (size == -1)
+ msg (M_ERR, "Read error on passphrase file: '%s'",
+ passphrase_file);
+ EVP_DigestUpdate (&md, buf, size);
+ total_size += size;
+ }
+ close (fd);
+
+ warn_if_group_others_accessible (passphrase_file);
+
+ if (total_size < min_passphrase_size)
+ msg (M_FATAL,
+ "Passphrase file '%s' is too small (must have at least %d characters)",
+ passphrase_file, min_passphrase_size);
+ }
+
+ EVP_DigestFinal (&md, output, &outlen);
+ EVP_MD_CTX_cleanup (&md);
+ return outlen;
+}
+
+/*
+ * Write key to file, return number of random bits
+ * written.
+ */
+int
+write_key_file (const int nkeys, const char *filename)
+{
+ struct gc_arena gc = gc_new ();
+
+ int fd, i;
+ int nbits = 0;
+
+ /* must be large enough to hold full key file */
+ struct buffer out = alloc_buf_gc (2048, &gc);
+ struct buffer nbits_head_text = alloc_buf_gc (128, &gc);
+
+ /* how to format the ascii file representation of key */
+ const int bytes_per_line = 16;
+
+ /* open key file */
+ fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR);
+
+ if (fd == -1)
+ msg (M_ERR, "Cannot open shared secret file '%s' for write", filename);
+
+ buf_printf (&out, "%s\n", static_key_head);
+
+ for (i = 0; i < nkeys; ++i)
+ {
+ struct key key;
+ char* fmt;
+
+ /* generate random bits */
+ generate_key_random (&key, NULL);
+
+ /* format key as ascii */
+ fmt = format_hex_ex ((const uint8_t*)&key,
+ sizeof (key),
+ 0,
+ bytes_per_line,
+ "\n",
+ &gc);
+
+ /* increment random bits counter */
+ nbits += sizeof (key) * 8;
+
+ /* write to holding buffer */
+ buf_printf (&out, "%s\n", fmt);
+
+ /* zero memory which held key component (will be freed by GC) */
+ memset (fmt, 0, strlen(fmt));
+ CLEAR (key);
+ }
+
+ buf_printf (&out, "%s\n", static_key_foot);
+
+ /* write number of bits */
+ buf_printf (&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits);
+ buf_write_string_file (&nbits_head_text, filename, fd);
+
+ /* write key file, now formatted in out, to file */
+ buf_write_string_file (&out, filename, fd);
+
+ if (close (fd))
+ msg (M_ERR, "Close error on shared secret file %s", filename);
+
+ /* zero memory which held file content (memory will be freed by GC) */
+ buf_clear (&out);
+
+ /* pop our garbage collection level */
+ gc_free (&gc);
+
+ return nbits;
+}
+
+void
+must_have_n_keys (const char *filename, const char *option, const struct key2 *key2, int n)
+{
+ if (key2->n < n)
+ {
+#ifdef ENABLE_SMALL
+ msg (M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d]", filename, option, key2->n, n);
+#else
+ msg (M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey --secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option);
+#endif
+ }
+}
+
+int
+ascii2keydirection (int msglevel, const char *str)
+{
+ if (!str)
+ return KEY_DIRECTION_BIDIRECTIONAL;
+ else if (!strcmp (str, "0"))
+ return KEY_DIRECTION_NORMAL;
+ else if (!strcmp (str, "1"))
+ return KEY_DIRECTION_INVERSE;
+ else
+ {
+ msg (msglevel, "Unknown key direction '%s' -- must be '0' or '1'", str);
+ return -1;
+ }
+ return KEY_DIRECTION_BIDIRECTIONAL; /* NOTREACHED */
+}
+
+const char *
+keydirection2ascii (int kd, bool remote)
+{
+ if (kd == KEY_DIRECTION_BIDIRECTIONAL)
+ return NULL;
+ else if (kd == KEY_DIRECTION_NORMAL)
+ return remote ? "1" : "0";
+ else if (kd == KEY_DIRECTION_INVERSE)
+ return remote ? "0" : "1";
+ else
+ {
+ ASSERT (0);
+ }
+ return NULL; /* NOTREACHED */
+}
+
+void
+key_direction_state_init (struct key_direction_state *kds, int key_direction)
+{
+ CLEAR (*kds);
+ switch (key_direction)
+ {
+ case KEY_DIRECTION_NORMAL:
+ kds->out_key = 0;
+ kds->in_key = 1;
+ kds->need_keys = 2;
+ break;
+ case KEY_DIRECTION_INVERSE:
+ kds->out_key = 1;
+ kds->in_key = 0;
+ kds->need_keys = 2;
+ break;
+ case KEY_DIRECTION_BIDIRECTIONAL:
+ kds->out_key = 0;
+ kds->in_key = 0;
+ kds->need_keys = 1;
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+void
+verify_fix_key2 (struct key2 *key2, const struct key_type *kt, const char *shared_secret_file)
+{
+ int i;
+
+ for (i = 0; i < key2->n; ++i)
+ {
+ /* Fix parity for DES keys and make sure not a weak key */
+ fixup_key (&key2->keys[i], kt);
+
+ /* This should be a very improbable failure */
+ if (!check_key (&key2->keys[i], kt))
+ msg (M_FATAL, "Key #%d in '%s' is bad. Try making a new key with --genkey.",
+ i+1, shared_secret_file);
+ }
+}
+
+/* given a key and key_type, write key to buffer */
+bool
+write_key (const struct key *key, const struct key_type *kt,
+ struct buffer *buf)
+{
+ ASSERT (kt->cipher_length <= MAX_CIPHER_KEY_LENGTH
+ && kt->hmac_length <= MAX_HMAC_KEY_LENGTH);
+
+ if (!buf_write (buf, &kt->cipher_length, 1))
+ return false;
+ if (!buf_write (buf, &kt->hmac_length, 1))
+ return false;
+ if (!buf_write (buf, key->cipher, kt->cipher_length))
+ return false;
+ if (!buf_write (buf, key->hmac, kt->hmac_length))
+ return false;
+
+ return true;
+}
+
+/*
+ * Given a key_type and buffer, read key from buffer.
+ * Return: 1 on success
+ * -1 read failure
+ * 0 on key length mismatch
+ */
+int
+read_key (struct key *key, const struct key_type *kt, struct buffer *buf)
+{
+ uint8_t cipher_length;
+ uint8_t hmac_length;
+
+ CLEAR (*key);
+ if (!buf_read (buf, &cipher_length, 1))
+ goto read_err;
+ if (!buf_read (buf, &hmac_length, 1))
+ goto read_err;
+
+ if (!buf_read (buf, key->cipher, cipher_length))
+ goto read_err;
+ if (!buf_read (buf, key->hmac, hmac_length))
+ goto read_err;
+
+ if (cipher_length != kt->cipher_length || hmac_length != kt->hmac_length)
+ goto key_len_err;
+
+ return 1;
+
+read_err:
+ msg (D_TLS_ERRORS, "TLS Error: error reading key from remote");
+ return -1;
+
+key_len_err:
+ msg (D_TLS_ERRORS,
+ "TLS Error: key length mismatch, local cipher/hmac %d/%d, remote cipher/hmac %d/%d",
+ kt->cipher_length, kt->hmac_length, cipher_length, hmac_length);
+ return 0;
+}
+
+void
+show_available_ciphers ()
+{
+ int nid;
+
+
+#ifndef ENABLE_SMALL
+ printf ("The following ciphers and cipher modes are available\n"
+ "for use with " PACKAGE_NAME ". Each cipher shown below may be\n"
+ "used as a parameter to the --cipher option. The default\n"
+ "key size is shown as well as whether or not it can be\n"
+ "changed with the --keysize directive. Using a CBC mode\n"
+ "is recommended.\n\n");
+#endif
+
+ for (nid = 0; nid < 10000; ++nid) /* is there a better way to get the size of the nid list? */
+ {
+ const EVP_CIPHER *cipher = EVP_get_cipherbynid (nid);
+ if (cipher && cipher_ok (OBJ_nid2sn (nid)))
+ {
+ const unsigned int mode = EVP_CIPHER_mode (cipher);
+ if (mode == EVP_CIPH_CBC_MODE
+#ifdef ALLOW_NON_CBC_CIPHERS
+ || mode == EVP_CIPH_CFB_MODE || mode == EVP_CIPH_OFB_MODE
+#endif
+ )
+ printf ("%s %d bit default key (%s)\n",
+ OBJ_nid2sn (nid),
+ EVP_CIPHER_key_length (cipher) * 8,
+ ((EVP_CIPHER_flags (cipher) & EVP_CIPH_VARIABLE_LENGTH) ?
+ "variable" : "fixed"));
+ }
+ }
+ printf ("\n");
+}
+
+void
+show_available_digests ()
+{
+ int nid;
+
+#ifndef ENABLE_SMALL
+ printf ("The following message digests are available for use with\n"
+ PACKAGE_NAME ". A message digest is used in conjunction with\n"
+ "the HMAC function, to authenticate received packets.\n"
+ "You can specify a message digest as parameter to\n"
+ "the --auth option.\n\n");
+#endif
+
+ for (nid = 0; nid < 10000; ++nid)
+ {
+ const EVP_MD *digest = EVP_get_digestbynid (nid);
+ if (digest)
+ {
+ printf ("%s %d bit digest size\n",
+ OBJ_nid2sn (nid), EVP_MD_size (digest) * 8);
+ }
+ }
+ printf ("\n");
+}
+
+void
+show_available_engines ()
+{
+#if CRYPTO_ENGINE
+ ENGINE *e;
+
+ printf ("OpenSSL Crypto Engines\n\n");
+
+ ENGINE_load_builtin_engines ();
+
+ e = ENGINE_get_first ();
+ while (e)
+ {
+ printf ("%s [%s]\n",
+ ENGINE_get_name (e),
+ ENGINE_get_id (e));
+ e = ENGINE_get_next (e);
+ }
+ ENGINE_cleanup ();
+#else
+ printf ("Sorry, OpenSSL hardware crypto engine functionality is not available.\n");
+#endif
+}
+
+/*
+ * Enable crypto acceleration, if available
+ */
+
+static bool engine_initialized = false; /* GLOBAL */
+
+#if CRYPTO_ENGINE
+
+static ENGINE *engine_persist = NULL; /* GLOBAL */
+
+/* Try to load an engine in a shareable library */
+static ENGINE *
+try_load_engine (const char *engine)
+{
+ ENGINE *e = ENGINE_by_id ("dynamic");
+ if (e)
+ {
+ if (!ENGINE_ctrl_cmd_string (e, "SO_PATH", engine, 0)
+ || !ENGINE_ctrl_cmd_string (e, "LOAD", NULL, 0))
+ {
+ ENGINE_free (e);
+ e = NULL;
+ }
+ }
+ return e;
+}
+
+static ENGINE *
+setup_engine (const char *engine)
+{
+ ENGINE *e = NULL;
+
+ ENGINE_load_builtin_engines ();
+
+ if (engine)
+ {
+ if (strcmp (engine, "auto") == 0)
+ {
+ msg (M_INFO, "Initializing OpenSSL auto engine support");
+ ENGINE_register_all_complete ();
+ return NULL;
+ }
+ if ((e = ENGINE_by_id (engine)) == NULL
+ && (e = try_load_engine (engine)) == NULL)
+ {
+ msg (M_FATAL, "OpenSSL error: cannot load engine '%s'", engine);
+ }
+
+ if (!ENGINE_set_default (e, ENGINE_METHOD_ALL))
+ {
+ msg (M_FATAL, "OpenSSL error: ENGINE_set_default failed on engine '%s'",
+ engine);
+ }
+
+ msg (M_INFO, "Initializing OpenSSL support for engine '%s'",
+ ENGINE_get_id (e));
+ }
+ return e;
+}
+#endif
+
+void
+init_crypto_lib_engine (const char *engine_name)
+{
+ if (!engine_initialized)
+ {
+#if CRYPTO_ENGINE
+ ASSERT (engine_name);
+ ASSERT (!engine_persist);
+ engine_persist = setup_engine (engine_name);
+#else
+ msg (M_WARN, "Note: OpenSSL hardware crypto engine functionality is not available");
+#endif
+ engine_initialized = true;
+ }
+}
+
+/*
+ * This routine should have additional OpenSSL crypto library initialisations
+ * used by both crypto and ssl components of OpenVPN.
+ */
+void init_crypto_lib ()
+{
+}
+
+void uninit_crypto_lib ()
+{
+#if CRYPTO_ENGINE
+ if (engine_initialized)
+ {
+ ENGINE_cleanup ();
+ engine_persist = NULL;
+ engine_initialized = false;
+ }
+#endif
+ prng_uninit ();
+}
+
+/*
+ * Random number functions, used in cases where we want
+ * reasonably strong cryptographic random number generation
+ * without depleting our entropy pool. Used for random
+ * IV values and a number of other miscellaneous tasks.
+ */
+
+static uint8_t *nonce_data; /* GLOBAL */
+static const EVP_MD *nonce_md = NULL; /* GLOBAL */
+static int nonce_secret_len; /* GLOBAL */
+
+void
+prng_init (const char *md_name, const int nonce_secret_len_parm)
+{
+ prng_uninit ();
+ nonce_md = md_name ? get_md (md_name) : NULL;
+ if (nonce_md)
+ {
+ ASSERT (nonce_secret_len_parm >= NONCE_SECRET_LEN_MIN && nonce_secret_len_parm <= NONCE_SECRET_LEN_MAX);
+ nonce_secret_len = nonce_secret_len_parm;
+ {
+ const int size = EVP_MD_size (nonce_md) + nonce_secret_len;
+ dmsg (D_CRYPTO_DEBUG, "PRNG init md=%s size=%d", EVP_MD_name (nonce_md), size);
+ nonce_data = (uint8_t*) malloc (size);
+ check_malloc_return (nonce_data);
+#if 1 /* Must be 1 for real usage */
+ if (!RAND_bytes (nonce_data, size))
+ msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for PRNG");
+#else
+ /* Only for testing -- will cause a predictable PRNG sequence */
+ {
+ int i;
+ for (i = 0; i < size; ++i)
+ nonce_data[i] = (uint8_t) i;
+ }
+#endif
+ }
+ }
+}
+
+void
+prng_uninit (void)
+{
+ free (nonce_data);
+ nonce_data = NULL;
+ nonce_md = NULL;
+ nonce_secret_len = 0;
+}
+
+void
+prng_bytes (uint8_t *output, int len)
+{
+ if (nonce_md)
+ {
+ EVP_MD_CTX ctx;
+ const int md_size = EVP_MD_size (nonce_md);
+ while (len > 0)
+ {
+ unsigned int outlen = 0;
+ const int blen = min_int (len, md_size);
+ EVP_DigestInit (&ctx, nonce_md);
+ EVP_DigestUpdate (&ctx, nonce_data, md_size + nonce_secret_len);
+ EVP_DigestFinal (&ctx, nonce_data, &outlen);
+ ASSERT (outlen == md_size);
+ EVP_MD_CTX_cleanup (&ctx);
+ memcpy (output, nonce_data, blen);
+ output += blen;
+ len -= blen;
+ }
+ }
+ else
+ RAND_bytes (output, len);
+}
+
+/* an analogue to the random() function, but use prng_bytes */
+long int
+get_random()
+{
+ long int l;
+ prng_bytes ((unsigned char *)&l, sizeof(l));
+ if (l < 0)
+ l = -l;
+ return l;
+}
+
+const char *
+md5sum (uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc)
+{
+ uint8_t digest[MD5_DIGEST_LENGTH];
+ MD5 (buf, len, digest);
+ return format_hex (digest, MD5_DIGEST_LENGTH, n_print_chars, gc);
+}
+
+/*
+ * OpenSSL memory debugging. If dmalloc debugging is enabled, tell
+ * OpenSSL to use our private malloc/realloc/free functions so that
+ * we can dispatch them to dmalloc.
+ */
+
+#ifdef DMALLOC
+
+static void *
+crypto_malloc (size_t size, const char *file, int line)
+{
+ return dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0);
+}
+
+static void *
+crypto_realloc (void *ptr, size_t size, const char *file, int line)
+{
+ return dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0);
+}
+
+static void
+crypto_free (void *ptr)
+{
+ dmalloc_free (__FILE__, __LINE__, ptr, DMALLOC_FUNC_FREE);
+}
+
+void
+openssl_dmalloc_init (void)
+{
+ CRYPTO_set_mem_ex_functions (crypto_malloc,
+ crypto_realloc,
+ crypto_free);
+}
+
+#endif /* DMALLOC */
+
+#ifndef USE_SSL
+
+void
+init_ssl_lib (void)
+{
+ ERR_load_crypto_strings ();
+ OpenSSL_add_all_algorithms ();
+ init_crypto_lib ();
+}
+
+void
+free_ssl_lib (void)
+{
+ uninit_crypto_lib ();
+ EVP_cleanup ();
+ ERR_free_strings ();
+}
+
+#endif /* USE_SSL */
+
+/*
+ * md5 functions
+ */
+
+void
+md5_state_init (struct md5_state *s)
+{
+ MD5_Init (&s->ctx);
+}
+
+void
+md5_state_update (struct md5_state *s, void *data, size_t len)
+{
+ MD5_Update (&s->ctx, data, len);
+}
+
+void
+md5_state_final (struct md5_state *s, struct md5_digest *out)
+{
+ MD5_Final (out->digest, &s->ctx);
+}
+
+void
+md5_digest_clear (struct md5_digest *digest)
+{
+ CLEAR (*digest);
+}
+
+bool
+md5_digest_defined (const struct md5_digest *digest)
+{
+ int i;
+ for (i = 0; i < MD5_DIGEST_LENGTH; ++i)
+ if (digest->digest[i])
+ return true;
+ return false;
+}
+
+bool
+md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2)
+{
+ return memcmp(d1->digest, d2->digest, MD5_DIGEST_LENGTH) == 0;
+}
+
+#endif /* USE_CRYPTO */
diff --git a/crypto.h b/crypto.h
new file mode 100644
index 0000000..fcd4661
--- /dev/null
+++ b/crypto.h
@@ -0,0 +1,421 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+#ifdef USE_CRYPTO
+
+#define ALLOW_NON_CBC_CIPHERS
+
+/*
+ * Does our OpenSSL library support crypto hardware acceleration?
+ */
+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_LOAD_BUILTIN_ENGINES) && defined(HAVE_ENGINE_REGISTER_ALL_COMPLETE) && defined(HAVE_ENGINE_CLEANUP)
+#define CRYPTO_ENGINE 1
+#else
+#define CRYPTO_ENGINE 0
+#endif
+
+#include <openssl/objects.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/des.h>
+#include <openssl/md5.h>
+#if NTLM
+#include <openssl/md4.h>
+#endif
+#include <openssl/sha.h>
+#include <openssl/err.h>
+
+#if CRYPTO_ENGINE
+#include <openssl/engine.h>
+#endif
+
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+#include <openssl/des_old.h>
+#endif
+
+#include "basic.h"
+#include "buffer.h"
+#include "packet_id.h"
+#include "mtu.h"
+
+/*
+ * Workarounds for incompatibilites between OpenSSL libraries.
+ * Right now we accept OpenSSL libraries from 0.9.5 to 0.9.7.
+ */
+
+#if SSLEAY_VERSION_NUMBER < 0x00907000L
+
+/* Workaround: EVP_CIPHER_mode is defined wrong in OpenSSL 0.9.6 but is fixed in 0.9.7 */
+#undef EVP_CIPHER_mode
+#define EVP_CIPHER_mode(e) (((e)->flags) & EVP_CIPH_MODE)
+
+#define DES_cblock des_cblock
+#define DES_is_weak_key des_is_weak_key
+#define DES_check_key_parity des_check_key_parity
+#define DES_set_odd_parity des_set_odd_parity
+
+#define HMAC_CTX_init(ctx) CLEAR (*ctx)
+#define HMAC_Init_ex(ctx,sec,len,md,impl) HMAC_Init(ctx, sec, len, md)
+#define HMAC_CTX_cleanup(ctx) HMAC_cleanup(ctx)
+#define EVP_MD_CTX_cleanup(md) CLEAR (*md)
+
+#define INFO_CALLBACK_SSL_CONST
+
+#endif
+
+#ifndef INFO_CALLBACK_SSL_CONST
+#define INFO_CALLBACK_SSL_CONST const
+#endif
+
+#if SSLEAY_VERSION_NUMBER < 0x00906000
+
+#undef EVP_CIPHER_mode
+#define EVP_CIPHER_mode(x) 1
+#define EVP_CIPHER_CTX_mode(x) 1
+#define EVP_CIPHER_flags(x) 0
+
+#define EVP_CIPH_CBC_MODE 1
+#define EVP_CIPH_CFB_MODE 0
+#define EVP_CIPH_OFB_MODE 0
+#define EVP_CIPH_VARIABLE_LENGTH 0
+
+#define OPENSSL_malloc(x) malloc(x)
+#define OPENSSL_free(x) free(x)
+
+static inline int
+EVP_CipherInit_ov (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, uint8_t *key, uint8_t *iv, int enc)
+{
+ EVP_CipherInit (ctx, type, key, iv, enc);
+ return 1;
+}
+
+static inline int
+EVP_CipherUpdate_ov (EVP_CIPHER_CTX *ctx, uint8_t *out, int *outl, uint8_t *in, int inl)
+{
+ EVP_CipherUpdate (ctx, out, outl, in, inl);
+ return 1;
+}
+
+static inline bool
+cipher_ok (const char* name)
+{
+ const int i = strlen (name) - 4;
+ if (i >= 0)
+ return !strcmp (name + i, "-CBC");
+ else
+ return false;
+}
+
+#else
+
+static inline int
+EVP_CipherInit_ov (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, uint8_t *key, uint8_t *iv, int enc)
+{
+ return EVP_CipherInit (ctx, type, key, iv, enc);
+}
+
+static inline int
+EVP_CipherUpdate_ov (EVP_CIPHER_CTX *ctx, uint8_t *out, int *outl, uint8_t *in, int inl)
+{
+ return EVP_CipherUpdate (ctx, out, outl, in, inl);
+}
+
+static inline bool
+cipher_ok (const char* name)
+{
+ return true;
+}
+
+#endif
+
+#if SSLEAY_VERSION_NUMBER < 0x0090581f
+#undef DES_check_key_parity
+#define DES_check_key_parity(x) 1
+#endif
+
+#ifndef EVP_CIPHER_name
+#define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e))
+#endif
+
+#ifndef EVP_MD_name
+#define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_type(e))
+#endif
+
+/*
+ * Max size in bytes of any cipher key that might conceivably be used.
+ *
+ * This value is checked at compile time in crypto.c to make sure
+ * it is always at least EVP_MAX_KEY_LENGTH.
+ *
+ * We define our own value, since this parameter
+ * is used to control the size of static key files.
+ * If the OpenSSL library increases EVP_MAX_KEY_LENGTH,
+ * we don't want our key files to be suddenly rendered
+ * unusable.
+ */
+#define MAX_CIPHER_KEY_LENGTH 64
+
+/*
+ * Max size in bytes of any HMAC key that might conceivably be used.
+ *
+ * This value is checked at compile time in crypto.c to make sure
+ * it is always at least EVP_MAX_MD_SIZE. We define our own value
+ * for the same reason as above.
+ */
+#define MAX_HMAC_KEY_LENGTH 64
+
+/*
+ * Defines a key type and key length for both cipher and HMAC.
+ */
+struct key_type
+{
+ uint8_t cipher_length;
+ uint8_t hmac_length;
+ const EVP_CIPHER *cipher;
+ const EVP_MD *digest;
+};
+
+/*
+ * A random key.
+ */
+struct key
+{
+ uint8_t cipher[MAX_CIPHER_KEY_LENGTH];
+ uint8_t hmac[MAX_HMAC_KEY_LENGTH];
+};
+
+#define KEY_DIRECTION_BIDIRECTIONAL 0 /* same keys for both directions */
+#define KEY_DIRECTION_NORMAL 1 /* encrypt with keys[0], decrypt with keys[1] */
+#define KEY_DIRECTION_INVERSE 2 /* encrypt with keys[1], decrypt with keys[0] */
+
+/*
+ * Dual random keys (for encrypt/decrypt)
+ */
+struct key2
+{
+ int n;
+ struct key keys[2];
+};
+
+/*
+ * Used for controlling bidirectional keys
+ * vs. a separate key for each direction.
+ */
+struct key_direction_state
+{
+ int out_key;
+ int in_key;
+ int need_keys;
+};
+
+/*
+ * A key context for cipher and/or HMAC.
+ */
+struct key_ctx
+{
+ EVP_CIPHER_CTX *cipher;
+ HMAC_CTX *hmac;
+};
+
+/*
+ * Cipher/HMAC key context for both sending and receiving
+ * directions.
+ */
+struct key_ctx_bi
+{
+ struct key_ctx encrypt;
+ struct key_ctx decrypt;
+};
+
+/*
+ * Options for encrypt/decrypt.
+ */
+struct crypto_options
+{
+ struct key_ctx_bi *key_ctx_bi;
+ struct packet_id *packet_id;
+ struct packet_id_persist *pid_persist;
+
+# define CO_PACKET_ID_LONG_FORM (1<<0)
+# define CO_USE_IV (1<<1)
+# define CO_IGNORE_PACKET_ID (1<<2)
+# define CO_MUTE_REPLAY_WARNINGS (1<<3)
+ unsigned int flags;
+};
+
+void init_key_type (struct key_type *kt, const char *ciphername,
+ bool ciphername_defined, const char *authname,
+ bool authname_defined, int keysize,
+ bool cfb_ofb_allowed, bool warn);
+
+#define RKF_MUST_SUCCEED (1<<0)
+#define RKF_INLINE (1<<1)
+void read_key_file (struct key2 *key2, const char *file, const unsigned int flags);
+
+int write_key_file (const int nkeys, const char *filename);
+
+int read_passphrase_hash (const char *passphrase_file,
+ const EVP_MD *digest,
+ uint8_t *output,
+ int len);
+
+void generate_key_random (struct key *key, const struct key_type *kt);
+
+void check_replay_iv_consistency(const struct key_type *kt, bool packet_id, bool use_iv);
+
+bool check_key (struct key *key, const struct key_type *kt);
+
+void fixup_key (struct key *key, const struct key_type *kt);
+
+bool write_key (const struct key *key, const struct key_type *kt,
+ struct buffer *buf);
+
+int read_key (struct key *key, const struct key_type *kt, struct buffer *buf);
+
+bool cfb_ofb_mode (const struct key_type* kt);
+
+const char *kt_cipher_name (const struct key_type *kt);
+const char *kt_digest_name (const struct key_type *kt);
+int kt_key_size (const struct key_type *kt);
+
+/* enc parameter in init_key_ctx */
+#define DO_ENCRYPT 1
+#define DO_DECRYPT 0
+
+void init_key_ctx (struct key_ctx *ctx, struct key *key,
+ const struct key_type *kt, int enc,
+ const char *prefix);
+
+void free_key_ctx (struct key_ctx *ctx);
+void free_key_ctx_bi (struct key_ctx_bi *ctx);
+
+void openvpn_encrypt (struct buffer *buf, struct buffer work,
+ const struct crypto_options *opt,
+ const struct frame* frame);
+
+bool openvpn_decrypt (struct buffer *buf, struct buffer work,
+ const struct crypto_options *opt,
+ const struct frame* frame);
+
+
+void crypto_adjust_frame_parameters(struct frame *frame,
+ const struct key_type* kt,
+ bool cipher_defined,
+ bool use_iv,
+ bool packet_id,
+ bool packet_id_long_form);
+
+#define NONCE_SECRET_LEN_MIN 16
+#define NONCE_SECRET_LEN_MAX 64
+void prng_init (const char *md_name, const int nonce_secret_len_parm);
+void prng_bytes (uint8_t *output, int len);
+void prng_uninit ();
+
+void test_crypto (const struct crypto_options *co, struct frame* f);
+
+const char *md5sum(uint8_t *buf, int len, int n_print_chars, struct gc_arena *gc);
+
+void show_available_ciphers (void);
+
+void show_available_digests (void);
+
+void show_available_engines (void);
+
+void init_crypto_lib_engine (const char *engine_name);
+
+void init_crypto_lib (void);
+
+void uninit_crypto_lib (void);
+
+/* key direction functions */
+
+void key_direction_state_init (struct key_direction_state *kds, int key_direction);
+
+void verify_fix_key2 (struct key2 *key2, const struct key_type *kt, const char *shared_secret_file);
+
+void must_have_n_keys (const char *filename, const char *option, const struct key2 *key2, int n);
+
+int ascii2keydirection (int msglevel, const char *str);
+
+const char *keydirection2ascii (int kd, bool remote);
+
+/* print keys */
+void key2_print (const struct key2* k,
+ const struct key_type *kt,
+ const char* prefix0,
+ const char* prefix1);
+
+/* memory debugging */
+void openssl_dmalloc_init (void);
+
+#ifdef USE_SSL
+
+#define GHK_INLINE (1<<0)
+void get_tls_handshake_key (const struct key_type *key_type,
+ struct key_ctx_bi *ctx,
+ const char *passphrase_file,
+ const int key_direction,
+ const unsigned int flags);
+
+#else
+
+void init_ssl_lib (void);
+void free_ssl_lib (void);
+
+#endif /* USE_SSL */
+
+/*
+ * Inline functions
+ */
+
+static inline bool
+key_ctx_bi_defined(const struct key_ctx_bi* key)
+{
+ return key->encrypt.cipher || key->encrypt.hmac || key->decrypt.cipher || key->decrypt.hmac;
+}
+
+/*
+ * md5 functions
+ */
+
+struct md5_state {
+ MD5_CTX ctx;
+};
+
+struct md5_digest {
+ uint8_t digest [MD5_DIGEST_LENGTH];
+};
+
+void md5_state_init (struct md5_state *s);
+void md5_state_update (struct md5_state *s, void *data, size_t len);
+void md5_state_final (struct md5_state *s, struct md5_digest *out);
+void md5_digest_clear (struct md5_digest *digest);
+bool md5_digest_defined (const struct md5_digest *digest);
+bool md5_digest_equal (const struct md5_digest *d1, const struct md5_digest *d2);
+
+#endif /* USE_CRYPTO */
+#endif /* CRYPTO_H */
diff --git a/cryptoapi.c b/cryptoapi.c
new file mode 100644
index 0000000..3365cd7
--- /dev/null
+++ b/cryptoapi.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2004 Peter 'Luna' Runestig <peter@runestig.com>
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include "syshead.h"
+
+#if defined(WIN32) && defined(USE_CRYPTO) && defined(USE_SSL)
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <windows.h>
+#include <wincrypt.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <assert.h>
+
+#ifdef __MINGW32_VERSION
+/* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1
+ * anyway. This is a hack around that problem. */
+#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5)
+#define CERT_SYSTEM_STORE_LOCATION_SHIFT 16
+#define CERT_SYSTEM_STORE_CURRENT_USER_ID 1
+#define CERT_SYSTEM_STORE_CURRENT_USER (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004
+static HINSTANCE crypt32dll = NULL;
+static BOOL WINAPI (*OpenVPNCryptAcquireCertificatePrivateKey) (PCCERT_CONTEXT pCert, DWORD dwFlags,
+ void *pvReserved, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) = NULL;
+#else
+#define OpenVPNCryptAcquireCertificatePrivateKey CryptAcquireCertificatePrivateKey
+#endif
+
+/* Size of an SSL signature: MD5+SHA1 */
+#define SSL_SIG_LENGTH 36
+
+/* try to funnel any Windows/CryptoAPI error messages to OpenSSL ERR_... */
+#define ERR_LIB_CRYPTOAPI (ERR_LIB_USER + 69) /* 69 is just a number... */
+#define CRYPTOAPIerr(f) err_put_ms_error(GetLastError(), (f), __FILE__, __LINE__)
+#define CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE 100
+#define CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE 101
+#define CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY 102
+#define CRYPTOAPI_F_CRYPT_CREATE_HASH 103
+#define CRYPTOAPI_F_CRYPT_GET_HASH_PARAM 104
+#define CRYPTOAPI_F_CRYPT_SET_HASH_PARAM 105
+#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106
+#define CRYPTOAPI_F_LOAD_LIBRARY 107
+#define CRYPTOAPI_F_GET_PROC_ADDRESS 108
+
+static ERR_STRING_DATA CRYPTOAPI_str_functs[] = {
+ { ERR_PACK(ERR_LIB_CRYPTOAPI, 0, 0), "microsoft cryptoapi"},
+ { ERR_PACK(0, CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE, 0), "CertOpenSystemStore" },
+ { ERR_PACK(0, CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE, 0), "CertFindCertificateInStore" },
+ { ERR_PACK(0, CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY, 0), "CryptAcquireCertificatePrivateKey" },
+ { ERR_PACK(0, CRYPTOAPI_F_CRYPT_CREATE_HASH, 0), "CryptCreateHash" },
+ { ERR_PACK(0, CRYPTOAPI_F_CRYPT_GET_HASH_PARAM, 0), "CryptGetHashParam" },
+ { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SET_HASH_PARAM, 0), "CryptSetHashParam" },
+ { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" },
+ { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" },
+ { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" },
+ { 0, NULL }
+};
+
+typedef struct _CAPI_DATA {
+ const CERT_CONTEXT *cert_context;
+ HCRYPTPROV crypt_prov;
+ DWORD key_spec;
+ BOOL free_crypt_prov;
+} CAPI_DATA;
+
+static char *ms_error_text(DWORD ms_err)
+{
+ LPVOID lpMsgBuf = NULL;
+ char *rv = NULL;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, ms_err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+ if (lpMsgBuf) {
+ char *p;
+ rv = strdup(lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ /* trim to the left */
+ if (rv)
+ for (p = rv + strlen(rv) - 1; p >= rv; p--) {
+ if (isspace(*p))
+ *p = '\0';
+ else
+ break;
+ }
+ }
+ return rv;
+}
+
+static void err_put_ms_error(DWORD ms_err, int func, const char *file, int line)
+{
+ static int init = 0;
+# define ERR_MAP_SZ 16
+ static struct {
+ int err;
+ DWORD ms_err; /* I don't think we get more than 16 *different* errors */
+ } err_map[ERR_MAP_SZ]; /* in here, before we give up the whole thing... */
+ int i;
+
+ if (ms_err == 0)
+ /* 0 is not an error */
+ return;
+ if (!init) {
+ ERR_load_strings(ERR_LIB_CRYPTOAPI, CRYPTOAPI_str_functs);
+ memset(&err_map, 0, sizeof(err_map));
+ init++;
+ }
+ /* since MS error codes are 32 bit, and the ones in the ERR_... system is
+ * only 12, we must have a mapping table between them. */
+ for (i = 0; i < ERR_MAP_SZ; i++) {
+ if (err_map[i].ms_err == ms_err) {
+ ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
+ break;
+ } else if (err_map[i].ms_err == 0 ) {
+ /* end of table, add new entry */
+ ERR_STRING_DATA *esd = calloc(2, sizeof(*esd));
+ if (esd == NULL)
+ break;
+ err_map[i].ms_err = ms_err;
+ err_map[i].err = esd->error = i + 100;
+ esd->string = ms_error_text(ms_err);
+ ERR_load_strings(ERR_LIB_CRYPTOAPI, esd);
+ ERR_PUT_error(ERR_LIB_CRYPTOAPI, func, err_map[i].err, file, line);
+ break;
+ }
+ }
+}
+
+/* encrypt */
+static int rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ /* I haven't been able to trigger this one, but I want to know if it happens... */
+ assert(0);
+
+ return 0;
+}
+
+/* verify arbitrary data */
+static int rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ /* I haven't been able to trigger this one, but I want to know if it happens... */
+ assert(0);
+
+ return 0;
+}
+
+/* sign arbitrary data */
+static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data;
+ HCRYPTHASH hash;
+ DWORD hash_size, len, i;
+ unsigned char *buf;
+
+ if (cd == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ if (padding != RSA_PKCS1_PADDING) {
+ /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
+ return 0;
+ }
+ /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would
+ * be way to straightforward for M$, I guess... So we have to do it this
+ * tricky way instead, by creating a "Hash", and load the already-made hash
+ * from 'from' into it. */
+ /* For now, we only support NID_md5_sha1 */
+ if (flen != SSL_SIG_LENGTH) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
+ return 0;
+ }
+ if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH);
+ return 0;
+ }
+ len = sizeof(hash_size);
+ if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM);
+ CryptDestroyHash(hash);
+ return 0;
+ }
+ if ((int) hash_size != flen) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH);
+ CryptDestroyHash(hash);
+ return 0;
+ }
+ if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM);
+ CryptDestroyHash(hash);
+ return 0;
+ }
+
+ len = RSA_size(rsa);
+ buf = malloc(len);
+ if (buf == NULL) {
+ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+ CryptDestroyHash(hash);
+ return 0;
+ }
+ if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len)) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH);
+ CryptDestroyHash(hash);
+ free(buf);
+ return 0;
+ }
+ /* and now, we have to reverse the byte-order in the result from CryptSignHash()... */
+ for (i = 0; i < len; i++)
+ to[i] = buf[len - i - 1];
+ free(buf);
+
+ CryptDestroyHash(hash);
+ return len;
+}
+
+/* decrypt */
+static int rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
+{
+ /* I haven't been able to trigger this one, but I want to know if it happens... */
+ assert(0);
+
+ return 0;
+}
+
+/* called at RSA_new */
+static int init(RSA *rsa)
+{
+
+ return 0;
+}
+
+/* called at RSA_free */
+static int finish(RSA *rsa)
+{
+ CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data;
+
+ if (cd == NULL)
+ return 0;
+ if (cd->crypt_prov && cd->free_crypt_prov)
+ CryptReleaseContext(cd->crypt_prov, 0);
+ if (cd->cert_context)
+ CertFreeCertificateContext(cd->cert_context);
+ free(rsa->meth->app_data);
+ free((char *) rsa->meth);
+ rsa->meth = NULL;
+ return 1;
+}
+
+static const CERT_CONTEXT *find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store)
+{
+ /* Find, and use, the desired certificate from the store. The
+ * 'cert_prop' certificate search string can look like this:
+ * SUBJ:<certificate substring to match>
+ * THUMB:<certificate thumbprint hex value>, e.g.
+ * THUMB:f6 49 24 41 01 b4 fb 44 0c ce f4 36 ae d0 c4 c9 df 7a b6 28
+ */
+ const CERT_CONTEXT *rv = NULL;
+
+ if (!strncmp(cert_prop, "SUBJ:", 5)) {
+ /* skip the tag */
+ cert_prop += 5;
+ rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_SUBJECT_STR_A, cert_prop, NULL);
+
+ } else if (!strncmp(cert_prop, "THUMB:", 6)) {
+ unsigned char hash[255];
+ char *p;
+ int i, x = 0;
+ CRYPT_HASH_BLOB blob;
+
+ /* skip the tag */
+ cert_prop += 6;
+ for (p = (char *) cert_prop, i = 0; *p && i < sizeof(hash); i++) {
+ if (*p >= '0' && *p <= '9')
+ x = (*p - '0') << 4;
+ else if (*p >= 'A' && *p <= 'F')
+ x = (*p - 'A' + 10) << 4;
+ else if (*p >= 'a' && *p <= 'f')
+ x = (*p - 'a' + 10) << 4;
+ if (!*++p) /* unexpected end of string */
+ break;
+ if (*p >= '0' && *p <= '9')
+ x += *p - '0';
+ else if (*p >= 'A' && *p <= 'F')
+ x += *p - 'A' + 10;
+ else if (*p >= 'a' && *p <= 'f')
+ x += *p - 'a' + 10;
+ hash[i] = x;
+ /* skip any space(s) between hex numbers */
+ for (p++; *p && *p == ' '; p++);
+ }
+ blob.cbData = i;
+ blob.pbData = (unsigned char *) &hash;
+ rv = CertFindCertificateInStore(cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_HASH, &blob, NULL);
+
+ }
+
+ return rv;
+}
+
+int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
+{
+ HCERTSTORE cs;
+ X509 *cert = NULL;
+ RSA *rsa = NULL, *pub_rsa;
+ CAPI_DATA *cd = calloc(1, sizeof(*cd));
+ RSA_METHOD *my_rsa_method = calloc(1, sizeof(*my_rsa_method));
+
+ if (cd == NULL || my_rsa_method == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ /* search CURRENT_USER first, then LOCAL_MACHINE */
+ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER |
+ CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
+ if (cs == NULL) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE);
+ goto err;
+ }
+ cd->cert_context = find_certificate_in_store(cert_prop, cs);
+ CertCloseStore(cs, 0);
+ if (!cd->cert_context) {
+ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE |
+ CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY");
+ if (cs == NULL) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE);
+ goto err;
+ }
+ cd->cert_context = find_certificate_in_store(cert_prop, cs);
+ CertCloseStore(cs, 0);
+ if (cd->cert_context == NULL) {
+ CRYPTOAPIerr(CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE);
+ goto err;
+ }
+ }
+
+ /* cert_context->pbCertEncoded is the cert X509 DER encoded. */
+ cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded,
+ cd->cert_context->cbCertEncoded);
+ if (cert == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB);
+ goto err;
+ }
+
+ /* set up stuff to use the private key */
+#ifdef __MINGW32_VERSION
+ /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1
+ * anyway. This is a hack around that problem. */
+ if (crypt32dll == NULL) {
+ crypt32dll = LoadLibrary("crypt32");
+ if (crypt32dll == NULL) {
+ CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY);
+ goto err;
+ }
+ }
+ if (OpenVPNCryptAcquireCertificatePrivateKey == NULL) {
+ OpenVPNCryptAcquireCertificatePrivateKey = GetProcAddress(crypt32dll,
+ "CryptAcquireCertificatePrivateKey");
+ if (OpenVPNCryptAcquireCertificatePrivateKey == NULL) {
+ CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS);
+ goto err;
+ }
+ }
+#endif
+ if (!OpenVPNCryptAcquireCertificatePrivateKey(cd->cert_context, CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+ NULL, &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) {
+ /* if we don't have a smart card reader here, and we try to access a
+ * smart card certificate, we get:
+ * "Error 1223: The operation was canceled by the user." */
+ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY);
+ goto err;
+ }
+ /* here we don't need to do CryptGetUserKey() or anything; all necessary key
+ * info is in cd->cert_context, and then, in cd->crypt_prov. */
+
+ my_rsa_method->name = "Microsoft CryptoAPI RSA Method";
+ my_rsa_method->rsa_pub_enc = rsa_pub_enc;
+ my_rsa_method->rsa_pub_dec = rsa_pub_dec;
+ my_rsa_method->rsa_priv_enc = rsa_priv_enc;
+ my_rsa_method->rsa_priv_dec = rsa_priv_dec;
+ /* my_rsa_method->init = init; */
+ my_rsa_method->finish = finish;
+ my_rsa_method->flags = RSA_METHOD_FLAG_NO_CHECK;
+ my_rsa_method->app_data = (char *) cd;
+
+ rsa = RSA_new();
+ if (rsa == NULL) {
+ SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(),
+ * so we do it here then... */
+ if (!SSL_CTX_use_certificate(ssl_ctx, cert))
+ goto err;
+ /* the public key */
+ pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+ /* SSL_CTX_use_certificate() increased the reference count in 'cert', so
+ * we decrease it here with X509_free(), or it will never be cleaned up. */
+ X509_free(cert);
+ cert = NULL;
+
+ /* I'm not sure about what we have to fill in in the RSA, trying out stuff... */
+ /* rsa->n indicates the key size */
+ rsa->n = BN_dup(pub_rsa->n);
+ rsa->flags |= RSA_FLAG_EXT_PKEY;
+ if (!RSA_set_method(rsa, my_rsa_method))
+ goto err;
+
+ if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
+ goto err;
+ /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
+ * we decrease it here with RSA_free(), or it will never be cleaned up. */
+ RSA_free(rsa);
+ return 1;
+
+ err:
+ if (cert)
+ X509_free(cert);
+ if (rsa)
+ RSA_free(rsa);
+ else {
+ if (my_rsa_method)
+ free(my_rsa_method);
+ if (cd) {
+ if (cd->free_crypt_prov && cd->crypt_prov)
+ CryptReleaseContext(cd->crypt_prov, 0);
+ if (cd->cert_context)
+ CertFreeCertificateContext(cd->cert_context);
+ free(cd);
+ }
+ }
+ return 0;
+}
+
+#else
+#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
+static void dummy (void) {}
+#endif
+#endif /* WIN32 */
diff --git a/cryptoapi.h b/cryptoapi.h
new file mode 100644
index 0000000..8ac6db3
--- /dev/null
+++ b/cryptoapi.h
@@ -0,0 +1,7 @@
+#ifndef _CRYPTOAPI_H_
+#define _CRYPTOAPI_H_
+
+int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop);
+
+
+#endif /* !_CRYPTOAPI_H_ */
diff --git a/debug/valgrind-suppress b/debug/valgrind-suppress
new file mode 100644
index 0000000..d7de12f
--- /dev/null
+++ b/debug/valgrind-suppress
@@ -0,0 +1,1182 @@
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ fun:__nss_next
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlopen
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlopen
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlsym
+ fun:libdl_resolve_symbol
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlopen
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/libc-2.7.so
+ obj:/lib/ld-2.7.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.7.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/libc-2.7.so
+ obj:/lib/ld-2.7.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.7.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/libc-2.7.so
+ obj:/lib/ld-2.7.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.7.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Addr8
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/ld-2.7.so
+ obj:/lib/libc-2.7.so
+ obj:/lib/ld-2.7.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.7.so
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_verify
+ fun:EVP_VerifyFinal
+ fun:ASN1_item_verify
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:X509_verify_cert
+ fun:ssl_verify_cert_chain
+ fun:ssl3_get_client_certificate
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_sign
+ fun:ssl3_send_server_key_exchange
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_MONT_CTX_set
+ fun:BN_mod_exp_mont
+ fun:BN_BLINDING_create_param
+ fun:RSA_setup_blinding
+ obj:/usr/lib/libcrypto.so.0.9.8
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_sign
+ fun:ssl3_send_server_key_exchange
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_nnmod
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+ fun:BN_nnmod
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_verify
+ fun:EVP_VerifyFinal
+ fun:ASN1_item_verify
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:X509_verify_cert
+ fun:ssl_verify_cert_chain
+ fun:ssl3_get_client_certificate
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:ssl3_ctx_ctrl
+ fun:init_ssl
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_MONT_CTX_set_locked
+ obj:/usr/lib/libcrypto.so.0.9.8
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_sign
+ fun:ssl3_send_server_key_exchange
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+ fun:BN_MONT_CTX_set
+ fun:BN_mod_exp_mont
+ fun:BN_BLINDING_create_param
+ fun:RSA_setup_blinding
+ obj:/usr/lib/libcrypto.so.0.9.8
+ obj:/usr/lib/libcrypto.so.0.9.8
+ fun:RSA_sign
+ fun:ssl3_send_server_key_exchange
+ fun:ssl3_accept
+ fun:ssl3_read_bytes
+ fun:ssl3_read
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:BIO_read
+ fun:bio_read
+ fun:tls_process
+ fun:tls_multi_process
+ fun:check_tls_dowork
+ fun:pre_select
+ fun:multi_process_post
+ fun:multi_process_incoming_link
+ fun:multi_tcp_action
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:*
+ obj:*
+ obj:*
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ fun:__nss_next
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ fun:__nss_next
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ fun:__nss_next
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:gethostbyname_r
+ fun:gethostbyname
+ fun:getaddr
+ fun:resolve_remote
+ fun:link_socket_init_phase1
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/ld-2.5.so
+ fun:__libc_dlopen_mode
+ fun:__nss_lookup_function
+ obj:/lib/libc-2.5.so
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libc-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlsym
+ fun:libdl_resolve_symbol
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ obj:/lib/ld-2.5.so
+ obj:/lib/libdl-2.5.so
+ fun:dlopen
+ fun:plugin_list_init
+ fun:init_plugins
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:CRYPTO_malloc
+ fun:sk_new
+ obj:/usr/lib/libssl.so.0.9.8
+ fun:SSL_COMP_get_compression_methods
+ fun:SSL_library_init
+ fun:init_ssl_lib
+ fun:init_static
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:getdelim
+ fun:getpass
+ fun:get_console_input
+ fun:get_user_pass
+ fun:context_init_1
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+ obj:*
+ obj:*
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.5.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.5.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.5.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.5.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.7.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_tcp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.7.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getgrnam_r
+ fun:getgrnam
+ fun:get_group
+ fun:do_init_first_time
+ fun:init_instance
+ fun:init_instance_handle_signals
+ fun:tunnel_server_udp
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:malloc
+ obj:/lib/libc-2.7.so
+ fun:__nss_database_lookup
+ obj:*
+ obj:*
+ fun:getpwnam_r
+ fun:getpwnam
+ fun:get_user
+ fun:management_open
+ fun:open_management
+ fun:main
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_mod_inverse
+}
+
+{
+ <insert a suppression name here>
+ Memcheck:Cond
+ fun:BN_div
+}
diff --git a/depcomp b/depcomp
new file mode 100755
index 0000000..df8eea7
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,630 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
+# Software Foundation, Inc.
+
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# 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.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u="sed s,\\\\\\\\,/,g"
+ depmode=msvisualcpp
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> "$depfile"
+ echo >> "$depfile"
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add `dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mechanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/dhcp.c b/dhcp.c
new file mode 100644
index 0000000..3474a17
--- /dev/null
+++ b/dhcp.c
@@ -0,0 +1,183 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "dhcp.h"
+#include "socket.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+static int
+get_dhcp_message_type (const struct dhcp *dhcp, const int optlen)
+{
+ const uint8_t *p = (uint8_t *) (dhcp + 1);
+ int i;
+
+ for (i = 0; i < optlen; ++i)
+ {
+ const uint8_t type = p[i];
+ const int room = optlen - i;
+ if (type == DHCP_END) /* didn't find what we were looking for */
+ return -1;
+ else if (type == DHCP_PAD) /* no-operation */
+ ;
+ else if (type == DHCP_MSG_TYPE) /* what we are looking for */
+ {
+ if (room >= 3)
+ {
+ if (p[i+1] == 1) /* option length should be 1 */
+ return p[i+2]; /* return message type */
+ }
+ return -1;
+ }
+ else /* some other option */
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ }
+ return -1;
+}
+
+static in_addr_t
+do_extract (struct dhcp *dhcp, const int optlen)
+{
+ uint8_t *p = (uint8_t *) (dhcp + 1);
+ int i;
+ in_addr_t ret = 0;
+
+ for (i = 0; i < optlen; ++i)
+ {
+ const uint8_t type = p[i];
+ const int room = optlen - i;
+ if (type == DHCP_END)
+ break;
+ else if (type == DHCP_PAD)
+ ;
+ else if (type == DHCP_ROUTER)
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ if (len <= (room-2))
+ {
+ if (!ret && len >= 4 && (len & 3) == 0)
+ {
+ memcpy (&ret, p+i+2, 4); /* get router IP address */
+ ret = ntohl (ret);
+ }
+ memset (p+i, DHCP_PAD, len+2); /* delete the router option by padding it out */
+ }
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ else /* some other option */
+ {
+ if (room >= 2)
+ {
+ const int len = p[i+1]; /* get option length */
+ i += (len + 1); /* advance to next option */
+ }
+ }
+ }
+ return ret;
+}
+
+static uint16_t
+udp_checksum (const uint8_t *buf,
+ const int len_udp,
+ const uint8_t *src_addr,
+ const uint8_t *dest_addr)
+{
+ uint16_t word16;
+ uint32_t sum = 0;
+ int i;
+
+ /* make 16 bit words out of every two adjacent 8 bit words and */
+ /* calculate the sum of all 16 bit words */
+ for (i = 0; i < len_udp; i += 2){
+ word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
+ sum += word16;
+ }
+
+ /* add the UDP pseudo header which contains the IP source and destination addresses */
+ for (i = 0; i < 4; i += 2){
+ word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+ for (i = 0; i < 4; i += 2){
+ word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+
+ /* the protocol number and the length of the UDP packet */
+ sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp;
+
+ /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ /* Take the one's complement of sum */
+ return ((uint16_t) ~sum);
+}
+
+in_addr_t
+dhcp_extract_router_msg (struct buffer *ipbuf)
+{
+ struct dhcp_full *df = (struct dhcp_full *) BPTR (ipbuf);
+ const int optlen = BLEN (ipbuf) - (sizeof (struct openvpn_iphdr) + sizeof (struct openvpn_udphdr) + sizeof (struct dhcp));
+
+ if (optlen >= 0
+ && df->ip.protocol == OPENVPN_IPPROTO_UDP
+ && df->udp.source == htons (BOOTPS_PORT)
+ && df->udp.dest == htons (BOOTPC_PORT)
+ && df->dhcp.op == BOOTREPLY
+ && get_dhcp_message_type (&df->dhcp, optlen) == DHCPACK)
+ {
+ /* get the router IP address while padding out all DHCP router options */
+ const in_addr_t ret = do_extract (&df->dhcp, optlen);
+
+ /* recompute the UDP checksum */
+ df->udp.check = htons (udp_checksum ((uint8_t *) &df->udp,
+ sizeof (struct openvpn_udphdr) + sizeof (struct dhcp) + optlen,
+ (uint8_t *)&df->ip.saddr,
+ (uint8_t *)&df->ip.daddr));
+
+ if (ret)
+ {
+ struct gc_arena gc = gc_new ();
+ msg (D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t (ret, 0, &gc));
+ gc_free (&gc);
+ }
+
+ return ret;
+ }
+ else
+ return 0;
+}
diff --git a/dhcp.h b/dhcp.h
new file mode 100644
index 0000000..e823a4a
--- /dev/null
+++ b/dhcp.h
@@ -0,0 +1,87 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include "common.h"
+#include "buffer.h"
+#include "proto.h"
+
+#pragma pack(1)
+
+/* DHCP Option types */
+#define DHCP_PAD 0
+#define DHCP_ROUTER 3
+#define DHCP_MSG_TYPE 53 /* message type (u8) */
+#define DHCP_END 255
+
+/* DHCP Messages types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+/* DHCP UDP port numbers */
+#define BOOTPS_PORT 67
+#define BOOTPC_PORT 68
+
+struct dhcp {
+# define BOOTREQUEST 1
+# define BOOTREPLY 2
+ uint8_t op; /* message op */
+
+ uint8_t htype; /* hardware address type (e.g. '1' = 10Mb Ethernet) */
+ uint8_t hlen; /* hardware address length (e.g. '6' for 10Mb Ethernet) */
+ uint8_t hops; /* client sets to 0, may be used by relay agents */
+ uint32_t xid; /* transaction ID, chosen by client */
+ uint16_t secs; /* seconds since request process began, set by client */
+ uint16_t flags;
+ uint32_t ciaddr; /* client IP address, client sets if known */
+ uint32_t yiaddr; /* 'your' IP address -- server's response to client */
+ uint32_t siaddr; /* server IP address */
+ uint32_t giaddr; /* relay agent IP address */
+ uint8_t chaddr[16]; /* client hardware address */
+ uint8_t sname[64]; /* optional server host name */
+ uint8_t file[128]; /* boot file name */
+ uint32_t magic; /* must be 0x63825363 (network order) */
+};
+
+struct dhcp_full {
+ struct openvpn_iphdr ip;
+ struct openvpn_udphdr udp;
+ struct dhcp dhcp;
+# define DHCP_OPTIONS_BUFFER_SIZE 256
+ uint8_t options[DHCP_OPTIONS_BUFFER_SIZE];
+};
+
+#pragma pack()
+
+in_addr_t dhcp_extract_router_msg (struct buffer *ipbuf);
+
+#endif
diff --git a/doclean b/doclean
new file mode 100755
index 0000000..8b35dd6
--- /dev/null
+++ b/doclean
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Let's have a fresh start. Remove all
+# generated files.
+#
+# Run this script, then:
+# autoreconf -i -v
+# ./configure
+# make
+# make install
+
+if ! [ "$KEEPAUTODEFS" = "yes" ]; then
+ rm -rf autodefs
+fi
+
+rm -f \
+ *.o \
+ service-win32/*.o \
+ service-win32/*.exe \
+ *.exe \
+ openvpn \
+ config.cache \
+ configure \
+ Makefile \
+ Makefile.in \
+ stamp-h* \
+ config.guess \
+ config.sub \
+ depcomp \
+ missing \
+ mkinstalldirs \
+ config.log \
+ config.status \
+ config.h \
+ config.h.in \
+ aclocal.m4 \
+ openvpn.spec \
+ install-sh \
+ openvpn.8.html \
+ install-win32/*.exe \
+ install-win32/makensis.log \
+ install-win32/settings \
+ install-win32/Makefile \
+ install-win32/Makefile.in \
+ images/Makefile \
+ images/Makefile.in \
+ service-win32/Makefile \
+ service-win32/Makefile.in
+
+rm -rf \
+ autom4te*.cache \
+ .deps \
+ */.deps \
+ windest \
+ gen \
+ tapinstall \
+ install-win32/tmp
+
+rm -rf \
+ tap-win32/objfre_w2k_x86 \
+ tap-win32/dist \
+ tap-win32/SOURCES \
+ tap-win32/tapdrvr.cod \
+ tap-win32/buildfre_wnet_amd64.wrn \
+ tap-win32/buildfre_w2k_x86.wrn \
+ tap-win32/objfre_wnet_amd64 \
+ tap-win32/buildfre_wnet_amd64.log \
+ tap-win32/buildfre_w2k_x86.log \
+ tap-win32/amd64 \
+ tap-win32/i386/tap0901.pdb \
+ tap-win32/i386/OemWin2k.inf \
+ tap-win32/i386/tap0901.map \
+ tap-win32/i386/tap0901.sys
diff --git a/domake-win b/domake-win
new file mode 100644
index 0000000..bd730e0
--- /dev/null
+++ b/domake-win
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+# This is the master OpenVPN build script for Windows.
+# This script will build OpenVPN, the TAP driver, and
+# the installer from source, targeting x86 on Windows
+# 2000 and higher, and x64 on Windows 2003 and higher.
+# For quick start options, see pre-built notes below.
+#
+# Note that if you are only looking to build the
+# openvpn user-space binaries (openvpn.exe
+# and openvpnserv.exe) you can use the
+# provided autoconf/automake build environment.
+#
+# If you are building from an expanded .tar.gz file,
+# make sure to run "./doclean" before "./domake-win".
+#
+# See top-level build configuration and settings in:
+#
+# version.m4
+# install-win32/settings.in
+#
+# Mandatory prerequisites:
+#
+# MinGW -- for GNU C compiler
+# MSYS -- for bash
+# msysDTK -- for perl
+# NSIS -- for building installer
+#
+# The following additional prerequisites may be omitted
+# when building in pre-built mode (see note below).
+#
+# svn -- for checking out source code (or TortoiseSVN)
+# Windows Driver Kit (6001_17121_HyperV_WDK.iso) -- for building
+# TAP driver + tapinstall
+#
+# Required libraries (must be prebuilt)
+#
+# OpenSSL -- define OPENSSL_DIR in settings.in
+# LZO -- define LZO_DIR in settings.in
+# PKCS11-HELPER -- define PKCS11_HELPER_DIR
+#
+# Optional OpenVPN GUI binary (prebuilt)
+# -- define OPENVPN_GUI_DIR and OPENVPN_GUI in settings.in
+#
+# Required source code not included in OpenVPN SVN repository
+# because of MS licensing restrictions:
+#
+# ../tapinstall -- This is based on 'devcon' which is found in the
+# Windows Driver Kit (formerly known as DDK).
+# Copy the 'devcon' source tree to ../tapinstall
+# Edit 'sources' and modify TARGETNAME=tapinstall
+
+# Note that all variables referenced here such as GENOUT,
+# GENOUT_PREBUILT, and CLEAN are defined in install-win32/settings.in
+
+# SPECIAL NOTES ON PRE-BUILT MODE
+# Setting up a complete tool chain to build OpenVPN and all
+# dependencies on Windows can be an onerous task, so the capability
+# is provided to reference a directory of pre-built components during
+# the build process. When dependencies are missing to build a given
+# component (such as the TAP driver), the build script will auto-detect
+# this and use the pre-built version instead. This would allow you, for
+# example, to build an OpenVPN installer with custom edits to
+# install-win32/settings.in, but then avoid needing to build all other
+# components (such as OpenSSL, LZO, Pkcs11-helper, TAP driver, Windows
+# service, etc.). The procedure is as follows. First Download and expand
+# the pre-built binaries from:
+#
+# http://openvpn.net/prebuilt/ (choose the most recent -prebuilt .tbz file)
+#
+# After expanding the .tbz file, cd to the top level directory and
+# expand an OpenVPN source distribution taken from either the subversion
+# repository or a source .tar.gz file. It's best to use an OpenVPN source
+# version that is the same or slightly later than the pre-built binaries
+# file. So now you have a directory containing something that looks like
+# this:
+#
+# gen-prebuilt -> from prebuilt .tbz file
+# lzo-2.02 -> from prebuilt .tbz file
+# openssl-0.9.8i -> from prebuilt .tbz file
+# pkcs11-helper -> from prebuilt .tbz file
+# openvpn-2.1_rc13.tar.gz -> downloaded from openvpn.net
+# openvpn-2.1_rc13 -> directory expanded from above file
+#
+# Now cd to your expanded source tree (openvpn-2.1_rc13 in the
+# example above), make edits to install-win32/settings.in (or even
+# patch the OpenVPN source code directly), and run this script:
+#
+# ./domake-win
+#
+# If everything runs correctly, you should have a custom installer
+# written to ./gen/install
+
+# First build the autodefs directory, containing C, sh, and NSIS versions
+# of global settings, using install-win32/settings.in as source.
+# These settings will then drive the rest of the build process.
+install-win32/winconfig
+
+# clean all generated files
+install-win32/doclean
+
+# Load a pre-built GENOUT directory if GENOUT_PREBUILT is defined
+# and the GENOUT directory is non-existing
+install-win32/getprebuilt
+
+# Each of the scripts below build, get, and/or possibly sign a different
+# OpenVPN component, placing the generated files in GENOUT. Each of these
+# steps is fully indepedent, and can be executed in any order or omitted.
+# The exception is the last script which gathers together all files from
+# GENOUT and builds the installer.
+
+# Make the OpenVPN user-space components (OpenVPN and service)
+install-win32/makeopenvpn
+
+# Make the OpenVPN TAP driver
+install-win32/maketap
+
+# Make the tapinstall utility, used to install the TAP driver
+install-win32/maketapinstall
+
+# Get the OpenSSL libraries from a pre-build OpenSSL tree
+install-win32/getopenssl
+
+# Get the PKCS-11 helper library from a pre-built OpenSSL tree
+install-win32/getpkcs11helper
+
+# Get the OpenVPN GUI (must be prebuilt)
+install-win32/getgui
+
+# Get the OpenVPN XML-based GUI (must be prebuilt)
+install-win32/getxgui
+
+# Produce the license text, install README, and sample config files
+install-win32/maketext
+
+# This final step builds the OpenVPN installer using generated
+# files from GENOUT
+install-win32/buildinstaller
diff --git a/easy-rsa/1.0/README b/easy-rsa/1.0/README
new file mode 100644
index 0000000..fd424ef
--- /dev/null
+++ b/easy-rsa/1.0/README
@@ -0,0 +1,161 @@
+This is a small RSA key management package,
+based on the openssl command line tool, that
+can be found in the easy-rsa subdirectory
+of the OpenVPN distribution.
+
+These are reference notes. For step
+by step instructions, see the HOWTO:
+
+http://openvpn.net/howto.html
+
+INSTALL
+
+1. Edit vars.
+2. Set KEY_CONFIG to point to the openssl.cnf file
+ included in this distribution.
+3. Set KEY_DIR to point to a directory which will
+ contain all keys, certificates, etc. This
+ directory need not exist, and if it does,
+ it will be deleted with rm -rf, so BE
+ CAREFUL how you set KEY_DIR.
+4. (Optional) Edit other fields in vars
+ per your site data. You may want to
+ increase KEY_SIZE to 2048 if you are
+ paranoid and don't mind slower key
+ processing, but certainly 1024 is
+ fine for testing purposes. KEY_SIZE
+ must be compatible across both peers
+ participating in a secure SSL/TLS
+ connection.
+5 . vars
+6. ./clean-all
+7. As you create certificates, keys, and
+ certificate signing requests, understand that
+ only .key files should be kept confidential.
+ .crt and .csr files can be sent over insecure
+ channels such as plaintext email.
+8. You should never need to copy a .key file
+ between computers. Normally each computer
+ will have its own certificate/key pair.
+
+BUILD YOUR OWN ROOT CERTIFICATE AUTHORITY (CA) CERTIFICATE/KEY
+
+1. ./build-ca
+2. ca.crt and ca.key will be built in your KEY_DIR
+ directory
+
+BUILD AN INTERMEDIATE CERTIFICATE AUTHORITY CERTIFICATE/KEY (optional)
+
+1. ./build-inter inter
+2. inter.crt and inter.key will be built in your KEY_DIR
+ directory and signed with your root certificate.
+
+BUILD DIFFIE-HELLMAN PARAMETERS (necessary for
+the server end of a SSL/TLS connection).
+
+1. ./build-dh
+
+BUILD A CERTIFICATE SIGNING REQUEST (If
+you want to sign your certificate with a root
+certificate controlled by another individual
+or organization, or residing on a different machine).
+
+1. Get ca.crt (the root certificate) from your
+ certificate authority. Though this
+ transfer can be over an insecure channel, to prevent
+ man-in-the-middle attacks you must confirm that
+ ca.crt was not tampered with. Large CAs solve this
+ problem by hardwiring their root certificates into
+ popular web browsers. A simple way to verify a root
+ CA is to call the issuer on the telephone and confirm
+ that the md5sum or sha1sum signatures on the ca.crt
+ files match (such as with the command: "md5sum ca.crt").
+2. Choose a name for your certificate such as your computer
+ name. In our example we will use "mycert".
+3. ./build-req mycert
+4. You can ignore most of the fields, but set
+ "Common Name" to something unique such as your
+ computer's host name. Leave all password
+ fields blank, unless you want your private key
+ to be protected by password. Using a password
+ is not required -- it will make your key more secure
+ but also more inconvenient to use, because you will
+ need to supply your password anytime the key is used.
+ NOTE: if you are using a password, use ./build-req-pass
+ instead of ./build-req
+5. Your key will be written to $KEY_DIR/mycert.key
+6. Your certificate signing request will be written to
+ to $KEY_DIR/mycert.csr
+7. Email mycert.csr to the individual or organization
+ which controls the root certificate. This can be
+ done over an insecure channel.
+8. After the .csr file is signed by the root certificate
+ authority, you will receive a file mycert.crt
+ (your certificate). Place mycert.crt in your
+ KEY_DIR directory.
+9. The combined files of mycert.crt, mycert.key,
+ and ca.crt can now be used to secure one end of
+ an SSL/TLS connection.
+
+SIGN A CERTIFICATE SIGNING REQUEST
+
+1. ./sign-req mycert
+2. mycert.crt will be built in your KEY_DIR
+ directory using mycert.csr and your root CA
+ file as input.
+
+BUILD AND SIGN A CERTIFICATE SIGNING REQUEST
+USING A LOCALLY INSTALLED ROOT CERTIFICATE/KEY -- this
+script generates and signs a certificate in one step,
+but it requires that the generated certificate and private
+key files be copied to the destination host over a
+secure channel.
+
+1. ./build-key mycert (no password protection)
+2. OR ./build-key-pass mycert (with password protection)
+3. OR ./build-key-pkcs12 mycert (PKCS #12 format)
+4. OR ./build-key-server mycert (with nsCertType=server)
+5. mycert.crt and mycert.key will be built in your
+ KEY_DIR directory, and mycert.crt will be signed
+ by your root CA. If ./build-key-pkcs12 was used a
+ mycert.p12 file will also be created including the
+ private key, certificate and the ca certificate.
+
+IMPORTANT
+
+To avoid a possible Man-in-the-Middle attack where an authorized
+client tries to connect to another client by impersonating the
+server, make sure to enforce some kind of server certificate
+verification by clients. There are currently four different ways
+of accomplishing this, listed in the order of preference:
+
+(1) Build your server certificates with the build-key-server
+ script. This will designate the certificate as a
+ server-only certificate by setting nsCertType=server.
+ Now add the following line to your client configuration:
+
+ ns-cert-type server
+
+ This will block clients from connecting to any
+ server which lacks the nsCertType=server designation
+ in its certificate, even if the certificate has been
+ signed by the CA which is cited in the OpenVPN configuration
+ file (--ca directive).
+
+(2) Use the --tls-remote directive on the client to
+ accept/reject the server connection based on the common
+ name of the server certificate.
+
+(3) Use a --tls-verify script or plugin to accept/reject the
+ server connection based on a custom test of the server
+ certificate's embedded X509 subject details.
+
+(4) Sign server certificates with one CA and client certificates
+ with a different CA. The client config "ca" directive should
+ reference the server-signing CA while the server config "ca"
+ directive should reference the client-signing CA.
+
+NOTES
+
+Show certificate fields:
+ openssl x509 -in cert.crt -text
diff --git a/easy-rsa/1.0/build-ca b/easy-rsa/1.0/build-ca
new file mode 100755
index 0000000..5ad59cc
--- /dev/null
+++ b/easy-rsa/1.0/build-ca
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+#
+# Build a root certificate
+#
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -x509 -keyout ca.key -out ca.crt -config $KEY_CONFIG && \
+ chmod 0600 ca.key
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-dh b/easy-rsa/1.0/build-dh
new file mode 100755
index 0000000..6de4baf
--- /dev/null
+++ b/easy-rsa/1.0/build-dh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+#
+# Build Diffie-Hellman parameters for the server side
+# of an SSL/TLS connection.
+#
+
+if test $KEY_DIR; then
+ openssl dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE}
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-inter b/easy-rsa/1.0/build-inter
new file mode 100755
index 0000000..8b3a6b2
--- /dev/null
+++ b/easy-rsa/1.0/build-inter
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+#
+# Make an intermediate CA certificate/private key pair using a locally generated
+# root certificate.
+#
+
+if test $# -ne 1; then
+ echo "usage: build-inter <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \
+ openssl ca -extensions v3_ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-key b/easy-rsa/1.0/build-key
new file mode 100755
index 0000000..3159d2b
--- /dev/null
+++ b/easy-rsa/1.0/build-key
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+#
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+#
+
+if test $# -ne 1; then
+ echo "usage: build-key <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \
+ openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \
+ chmod 0600 $1.key
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-key-pass b/easy-rsa/1.0/build-key-pass
new file mode 100755
index 0000000..03ab304
--- /dev/null
+++ b/easy-rsa/1.0/build-key-pass
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+#
+# Similar to build-key, but protect the private key
+# with a password.
+#
+
+if test $# -ne 1; then
+ echo "usage: build-key-pass <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \
+ openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \
+ chmod 0600 $1.key
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-key-pkcs12 b/easy-rsa/1.0/build-key-pkcs12
new file mode 100755
index 0000000..f8a057b
--- /dev/null
+++ b/easy-rsa/1.0/build-key-pkcs12
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+#
+# Make a certificate/private key pair using a locally generated
+# root certificate and convert it to a PKCS #12 file including the
+# the CA certificate as well.
+
+if test $# -ne 1; then
+ echo "usage: build-key-pkcs12 <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG && \
+ openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG && \
+ openssl pkcs12 -export -inkey $1.key -in $1.crt -certfile ca.crt -out $1.p12 && \
+ chmod 0600 $1.key $1.p12
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-key-server b/easy-rsa/1.0/build-key-server
new file mode 100755
index 0000000..30dc41e
--- /dev/null
+++ b/easy-rsa/1.0/build-key-server
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+#
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+#
+# Explicitly set nsCertType to server using the "server"
+# extension in the openssl.cnf file.
+
+if test $# -ne 1; then
+ echo "usage: build-key-server <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -extensions server -config $KEY_CONFIG && \
+ openssl ca -days 3650 -out $1.crt -in $1.csr -extensions server -config $KEY_CONFIG && \
+ chmod 0600 $1.key
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-req b/easy-rsa/1.0/build-req
new file mode 100755
index 0000000..30f62f5
--- /dev/null
+++ b/easy-rsa/1.0/build-req
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# Build a certificate signing request and private key. Use this
+# when your root certificate and key is not available locally.
+#
+
+if test $# -ne 1; then
+ echo "usage: build-req <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -nodes -new -keyout $1.key -out $1.csr -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/build-req-pass b/easy-rsa/1.0/build-req-pass
new file mode 100755
index 0000000..829b286
--- /dev/null
+++ b/easy-rsa/1.0/build-req-pass
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# Like build-req, but protect your private key
+# with a password.
+#
+
+if test $# -ne 1; then
+ echo "usage: build-req-pass <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl req -days 3650 -new -keyout $1.key -out $1.csr -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/clean-all b/easy-rsa/1.0/clean-all
new file mode 100755
index 0000000..d10aef5
--- /dev/null
+++ b/easy-rsa/1.0/clean-all
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+#
+# Initialize the $KEY_DIR directory.
+# Note that this script does a
+# rm -rf on $KEY_DIR so be careful!
+#
+
+d=$KEY_DIR
+
+if test $d; then
+ rm -rf $d
+ mkdir $d && \
+ chmod go-rwx $d && \
+ touch $d/index.txt && \
+ echo 01 >$d/serial
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/list-crl b/easy-rsa/1.0/list-crl
new file mode 100644
index 0000000..b214dbd
--- /dev/null
+++ b/easy-rsa/1.0/list-crl
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# list revoked certificates
+#
+#
+
+if test $# -ne 1; then
+ echo "usage: list-crl <crlfile.pem>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl crl -text -noout -in $1
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/make-crl b/easy-rsa/1.0/make-crl
new file mode 100644
index 0000000..62fe6c1
--- /dev/null
+++ b/easy-rsa/1.0/make-crl
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# generate a CRL
+#
+#
+
+if test $# -ne 1; then
+ echo "usage: make-crl <crlfile.pem>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl ca -gencrl -out $1 -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/openssl.cnf b/easy-rsa/1.0/openssl.cnf
new file mode 100644
index 0000000..270b069
--- /dev/null
+++ b/easy-rsa/1.0/openssl.cnf
@@ -0,0 +1,255 @@
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "OpenSSL Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "OpenSSL Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
diff --git a/easy-rsa/1.0/revoke-crt b/easy-rsa/1.0/revoke-crt
new file mode 100644
index 0000000..35b071a
--- /dev/null
+++ b/easy-rsa/1.0/revoke-crt
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# revoke a certificate
+#
+#
+
+if test $# -ne 1; then
+ echo "usage: revoke-crt <file.crt>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl ca -revoke $1 -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/revoke-full b/easy-rsa/1.0/revoke-full
new file mode 100755
index 0000000..66ea03f
--- /dev/null
+++ b/easy-rsa/1.0/revoke-full
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# revoke a certificate, regenerate CRL,
+# and verify revocation
+
+CRL=crl.pem
+RT=revoke-test.pem
+
+if test $# -ne 1; then
+ echo "usage: revoke-full <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR
+ rm -f $RT
+
+ # revoke key and generate a new CRL
+ openssl ca -revoke $1.crt -config $KEY_CONFIG
+
+ # generate a new CRL
+ openssl ca -gencrl -out $CRL -config $KEY_CONFIG
+ cat ca.crt $CRL >$RT
+
+ # verify the revocation
+ openssl verify -CAfile $RT -crl_check $1.crt
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/sign-req b/easy-rsa/1.0/sign-req
new file mode 100755
index 0000000..59edc42
--- /dev/null
+++ b/easy-rsa/1.0/sign-req
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#
+# Sign a certificate signing request (a .csr file)
+# with a local root certificate and key.
+#
+
+if test $# -ne 1; then
+ echo "usage: sign-req <name>";
+ exit 1
+fi
+
+if test $KEY_DIR; then
+ cd $KEY_DIR && \
+ openssl ca -days 3650 -out $1.crt -in $1.csr -config $KEY_CONFIG
+else
+ echo you must define KEY_DIR
+fi
diff --git a/easy-rsa/1.0/vars b/easy-rsa/1.0/vars
new file mode 100644
index 0000000..da89cd2
--- /dev/null
+++ b/easy-rsa/1.0/vars
@@ -0,0 +1,49 @@
+# easy-rsa parameter settings
+
+# NOTE: If you installed from an RPM,
+# don't edit this file in place in
+# /usr/share/openvpn/easy-rsa --
+# instead, you should copy the whole
+# easy-rsa directory to another location
+# (such as /etc/openvpn) so that your
+# edits will not be wiped out by a future
+# OpenVPN package upgrade.
+
+# This variable should point to
+# the top level of the easy-rsa
+# tree.
+export D=`pwd`
+
+# This variable should point to
+# the openssl.cnf file included
+# with easy-rsa.
+export KEY_CONFIG=$D/openssl.cnf
+
+# Edit this variable to point to
+# your soon-to-be-created key
+# directory.
+#
+# WARNING: clean-all will do
+# a rm -rf on this directory
+# so make sure you define
+# it correctly!
+export KEY_DIR=$D/keys
+
+# Issue rm -rf warning
+echo NOTE: when you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
+
+# Increase this to 2048 if you
+# are paranoid. This will slow
+# down TLS negotiation performance
+# as well as the one-time DH parms
+# generation process.
+export KEY_SIZE=1024
+
+# These are the default values for fields
+# which will be placed in the certificate.
+# Don't leave any of these fields blank.
+export KEY_COUNTRY=KG
+export KEY_PROVINCE=NA
+export KEY_CITY=BISHKEK
+export KEY_ORG="OpenVPN-TEST"
+export KEY_EMAIL="me@myhost.mydomain"
diff --git a/easy-rsa/2.0/Makefile b/easy-rsa/2.0/Makefile
new file mode 100644
index 0000000..8000cc5
--- /dev/null
+++ b/easy-rsa/2.0/Makefile
@@ -0,0 +1,13 @@
+
+DESTDIR=
+PREFIX=
+
+all:
+ echo "All done."
+ echo "Run make install DESTDIR=/usr/share/somewhere"
+
+install:
+ install -d "${DESTDIR}/${PREFIX}"
+ install -m 0755 build-* "${DESTDIR}/${PREFIX}"
+ install -m 0755 clean-all list-crl inherit-inter pkitool revoke-full sign-req whichopensslcnf "${DESTDIR}/${PREFIX}"
+ install -m 0644 openssl-0.9.6.cnf openssl-0.9.8.cnf openssl-1.0.0.cnf README vars "${DESTDIR}/${PREFIX}"
diff --git a/easy-rsa/2.0/README b/easy-rsa/2.0/README
new file mode 100644
index 0000000..6f5395c
--- /dev/null
+++ b/easy-rsa/2.0/README
@@ -0,0 +1,229 @@
+EASY-RSA Version 2.0-rc1
+
+This is a small RSA key management package, based on the openssl
+command line tool, that can be found in the easy-rsa subdirectory
+of the OpenVPN distribution. While this tool is primary concerned
+with key management for the SSL VPN application space, it can also
+be used for building web certificates.
+
+These are reference notes. For step-by-step instructions, see the
+HOWTO:
+
+http://openvpn.net/howto.html
+
+This package is based on the ./pkitool script. Run ./pkitool
+without arguments for a detailed help message (which is also pasted
+below).
+
+Release Notes for easy-rsa-2.0
+
+* Most functionality has been consolidated into the pkitool
+ script. For compatibility, all previous scripts from 1.0 such
+ as build-key and build-key-server are provided as stubs
+ which call pkitool to do the real work.
+
+* pkitool has a --batch flag (enabled by default) which generates
+ keys/certs without needing any interactive input. pkitool
+ can still generate certs/keys using interactive prompting by
+ using the --interact flag.
+
+* The inherit-inter script has been provided for creating
+ a new PKI rooted on an intermediate certificate built within a
+ higher-level PKI. See comments in the inherit-inter script
+ for more info.
+
+* The openssl.cnf file has been modified. pkitool will not
+ work with the openssl.cnf file included with previous
+ easy-rsa releases.
+
+* The vars file has been modified -- the following extra
+ variables have been added: EASY_RSA, CA_EXPIRE,
+ KEY_EXPIRE.
+
+* The make-crl and revoke-crt scripts have been removed and
+ are replaced by the revoke-full script.
+
+* The "Organizational Unit" X509 field can be set using
+ the KEY_OU environmental variable before calling pkitool.
+
+* This release only affects the Linux/Unix version of easy-rsa.
+ The Windows version (written to use the Windows shell) is unchanged.
+
+* Use the revoke-full script to revoke a certificate, and generate
+ (or update) the crl.pem file in the keys directory (as set by the
+ vars script). Then use "crl-verify crl.pem" in your OpenVPN server
+ config file, so that OpenVPN can reject any connections coming from
+ clients which present a revoked certificate. Usage for the script is:
+
+ revoke-full <common-name>
+
+ Note this this procedure is primarily designed to revoke client
+ certificates. You could theoretically use this method to revoke
+ server certificates as well, but then you would need to propagate
+ the crl.pem file to all clients as well, and have them include
+ "crl-verify crl.pem" in their configuration files.
+
+* PKCS#11 support was added.
+
+* For those interested in using this tool to generate web certificates,
+ A variant of the easy-rsa package that allows the creation of multi-domain
+ certificates with subjectAltName can be obtained from here:
+
+ http://www.bisente.com/proyectos/easy-rsa-subjectaltname/
+
+INSTALL easy-rsa
+
+1. Edit vars.
+2. Set KEY_CONFIG to point to the correct openssl-<version>.cnf
+ file included in this distribution.
+3. Set KEY_DIR to point to a directory which will
+ contain all keys, certificates, etc. This
+ directory need not exist, and if it does,
+ it will be deleted with rm -rf, so BE
+ CAREFUL how you set KEY_DIR.
+4. (Optional) Edit other fields in vars
+ per your site data. You may want to
+ increase KEY_SIZE to 2048 if you are
+ paranoid and don't mind slower key
+ processing, but certainly 1024 is
+ fine for testing purposes. KEY_SIZE
+ must be compatible across both peers
+ participating in a secure SSL/TLS
+ connection.
+5. (Optional) If you intend to use PKCS#11,
+ install openssl >= 0.9.7, install the
+ following components from www.opensc.org:
+ - opensc >= 0.10.0
+ - engine_pkcs11 >= 0.1.3
+ Update the openssl.cnf to load the engine:
+ - Uncomment pkcs11 under engine_section.
+ - Validate path at dynamic_path under pkcs11_section.
+6. . vars
+7. ./clean-all
+8. As you create certificates, keys, and
+ certificate signing requests, understand that
+ only .key files should be kept confidential.
+ .crt and .csr files can be sent over insecure
+ channels such as plaintext email.
+
+IMPORTANT
+
+To avoid a possible Man-in-the-Middle attack where an authorized
+client tries to connect to another client by impersonating the
+server, make sure to enforce some kind of server certificate
+verification by clients. There are currently four different ways
+of accomplishing this, listed in the order of preference:
+
+(1) Build your server certificates with specific key usage and
+ extended key usage. The RFC3280 determine that the following
+ attributes should be provided for TLS connections:
+
+ Mode Key usage Extended key usage
+ ---------------------------------------------------------------------------
+ Client digitalSignature TLS Web Client Authentication
+ keyAgreement
+ digitalSignature, keyAgreement
+
+ Server digitalSignature, keyEncipherment TLS Web Server Authentication
+ digitalSignature, keyAgreement
+
+ Now add the following line to your client configuration:
+
+ remote-cert-tls server
+
+ This will block clients from connecting to any
+ server which lacks the required extension designation
+ in its certificate, even if the certificate has been
+ signed by the CA which is cited in the OpenVPN configuration
+ file (--ca directive).
+
+(3) Use the --tls-remote directive on the client to
+ accept/reject the server connection based on the common
+ name of the server certificate.
+
+(3) Use a --tls-verify script or plugin to accept/reject the
+ server connection based on a custom test of the server
+ certificate's embedded X509 subject details.
+
+(4) Sign server certificates with one CA and client certificates
+ with a different CA. The client config "ca" directive should
+ reference the server-signing CA while the server config "ca"
+ directive should reference the client-signing CA.
+
+NOTES
+
+Show certificate fields:
+ openssl x509 -in cert.crt -text
+
+PKITOOL documentation
+
+pkitool 2.0
+Usage: pkitool [options...] [common-name]
+Options:
+ --batch : batch mode (default)
+ --keysize : Set keysize
+ size : size (default=1024)
+ --interact : interactive mode
+ --server : build server cert
+ --initca : build root CA
+ --inter : build intermediate CA
+ --pass : encrypt private key with password
+ --csr : only generate a CSR, do not sign
+ --sign : sign an existing CSR
+ --pkcs12 : generate a combined PKCS#12 file
+ --pkcs11 : generate certificate on PKCS#11 token
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ id : PKCS#11 object id (hex string)
+ label : PKCS#11 object label
+Standalone options:
+ --pkcs11-slots : list PKCS#11 slots
+ lib : PKCS#11 library
+ --pkcs11-objects : list PKCS#11 token objects
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ --pkcs11-init : initialize PKCS#11 token DANGEROUS!!!
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ label : PKCS#11 token label
+Notes:
+ Please edit the vars script to reflect your configuration,
+ then source it with "source ./vars".
+ Next, to start with a fresh PKI configuration and to delete any
+ previous certificates and keys, run "./clean-all".
+ Finally, you can run this tool (pkitool) to build certificates/keys.
+ In order to use PKCS#11 interface you must have opensc-0.10.0 or higher.
+Generated files and corresponding OpenVPN directives:
+(Files will be placed in the $KEY_DIR directory, defined in ./vars)
+ ca.crt -> root certificate (--ca)
+ ca.key -> root key, keep secure (not directly used by OpenVPN)
+ .crt files -> client/server certificates (--cert)
+ .key files -> private keys, keep secure (--key)
+ .csr files -> certificate signing request (not directly used by OpenVPN)
+ dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh)
+Examples:
+ pkitool --initca -> Build root certificate
+ pkitool --initca --pass -> Build root certificate with password-protected key
+ pkitool --server server1 -> Build "server1" certificate/key
+ pkitool client1 -> Build "client1" certificate/key
+ pkitool --pass client2 -> Build password-protected "client2" certificate/key
+ pkitool --pkcs12 client3 -> Build "client3" certificate/key in PKCS#12 format
+ pkitool --csr client4 -> Build "client4" CSR to be signed by another CA
+ pkitool --sign client4 -> Sign "client4" CSR
+ pkitool --inter interca -> Build an intermediate key-signing certificate/key
+ Also see ./inherit-inter script.
+ pkitool --pkcs11 /usr/lib/pkcs11/lib1 0 010203 "client5 id" client5
+ -> Build "client5" certificate/key in PKCS#11 token
+Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys.
+Protect client2 key with a password. Build DH parms. Generated files in ./keys :
+ [edit vars with your site-specific info]
+ source ./vars
+ ./clean-all
+ ./build-dh -> takes a long time, consider backgrounding
+ ./pkitool --initca
+ ./pkitool --server myserver
+ ./pkitool client1
+ ./pkitool --pass client2
+Typical usage for adding client cert to existing PKI:
+ source ./vars
+ ./pkitool client-new
diff --git a/easy-rsa/2.0/build-ca b/easy-rsa/2.0/build-ca
new file mode 100755
index 0000000..bce29a6
--- /dev/null
+++ b/easy-rsa/2.0/build-ca
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+#
+# Build a root certificate
+#
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --initca $*
diff --git a/easy-rsa/2.0/build-dh b/easy-rsa/2.0/build-dh
new file mode 100755
index 0000000..4beb127
--- /dev/null
+++ b/easy-rsa/2.0/build-dh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Build Diffie-Hellman parameters for the server side
+# of an SSL/TLS connection.
+
+if [ -d $KEY_DIR ] && [ $KEY_SIZE ]; then
+ $OPENSSL dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE}
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/build-inter b/easy-rsa/2.0/build-inter
new file mode 100755
index 0000000..87bf98d
--- /dev/null
+++ b/easy-rsa/2.0/build-inter
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Make an intermediate CA certificate/private key pair using a locally generated
+# root certificate.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --inter $*
diff --git a/easy-rsa/2.0/build-key b/easy-rsa/2.0/build-key
new file mode 100755
index 0000000..6c0fed8
--- /dev/null
+++ b/easy-rsa/2.0/build-key
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact $*
diff --git a/easy-rsa/2.0/build-key-pass b/easy-rsa/2.0/build-key-pass
new file mode 100755
index 0000000..8ef8307
--- /dev/null
+++ b/easy-rsa/2.0/build-key-pass
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Similar to build-key, but protect the private key
+# with a password.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --pass $*
diff --git a/easy-rsa/2.0/build-key-pkcs12 b/easy-rsa/2.0/build-key-pkcs12
new file mode 100755
index 0000000..ba90e6a
--- /dev/null
+++ b/easy-rsa/2.0/build-key-pkcs12
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate and convert it to a PKCS #12 file including the
+# the CA certificate as well.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --pkcs12 $*
diff --git a/easy-rsa/2.0/build-key-server b/easy-rsa/2.0/build-key-server
new file mode 100755
index 0000000..fee0194
--- /dev/null
+++ b/easy-rsa/2.0/build-key-server
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+#
+# Explicitly set nsCertType to server using the "server"
+# extension in the openssl.cnf file.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --server $*
diff --git a/easy-rsa/2.0/build-req b/easy-rsa/2.0/build-req
new file mode 100755
index 0000000..559d512
--- /dev/null
+++ b/easy-rsa/2.0/build-req
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Build a certificate signing request and private key. Use this
+# when your root certificate and key is not available locally.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --csr $*
diff --git a/easy-rsa/2.0/build-req-pass b/easy-rsa/2.0/build-req-pass
new file mode 100755
index 0000000..b73ee1b
--- /dev/null
+++ b/easy-rsa/2.0/build-req-pass
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Like build-req, but protect your private key
+# with a password.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --csr --pass $*
diff --git a/easy-rsa/2.0/clean-all b/easy-rsa/2.0/clean-all
new file mode 100755
index 0000000..cc6e3b2
--- /dev/null
+++ b/easy-rsa/2.0/clean-all
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Initialize the $KEY_DIR directory.
+# Note that this script does a
+# rm -rf on $KEY_DIR so be careful!
+
+if [ "$KEY_DIR" ]; then
+ rm -rf "$KEY_DIR"
+ mkdir "$KEY_DIR" && \
+ chmod go-rwx "$KEY_DIR" && \
+ touch "$KEY_DIR/index.txt" && \
+ echo 01 >"$KEY_DIR/serial"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/inherit-inter b/easy-rsa/2.0/inherit-inter
new file mode 100755
index 0000000..aaa5168
--- /dev/null
+++ b/easy-rsa/2.0/inherit-inter
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Build a new PKI which is rooted on an intermediate certificate generated
+# by ./build-inter or ./pkitool --inter from a parent PKI. The new PKI should
+# have independent vars settings, and must use a different KEY_DIR directory
+# from the parent. This tool can be used to generate arbitrary depth
+# certificate chains.
+#
+# To build an intermediate CA, follow the same steps for a regular PKI but
+# replace ./build-key or ./pkitool --initca with this script.
+
+# The EXPORT_CA file will contain the CA certificate chain and should be
+# referenced by the OpenVPN "ca" directive in config files. The ca.crt file
+# will only contain the local intermediate CA -- it's needed by the easy-rsa
+# scripts but not by OpenVPN directly.
+EXPORT_CA="export-ca.crt"
+
+if [ $# -ne 2 ]; then
+ echo "usage: $0 <parent-key-dir> <common-name>"
+ echo "parent-key-dir: the KEY_DIR directory of the parent PKI"
+ echo "common-name: the common name of the intermediate certificate in the parent PKI"
+ exit 1;
+fi
+
+if [ "$KEY_DIR" ]; then
+ cp "$1/$2.crt" "$KEY_DIR/ca.crt"
+ cp "$1/$2.key" "$KEY_DIR/ca.key"
+
+ if [ -e "$1/$EXPORT_CA" ]; then
+ PARENT_CA="$1/$EXPORT_CA"
+ else
+ PARENT_CA="$1/ca.crt"
+ fi
+ cp "$PARENT_CA" "$KEY_DIR/$EXPORT_CA"
+ cat "$KEY_DIR/ca.crt" >> "$KEY_DIR/$EXPORT_CA"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/list-crl b/easy-rsa/2.0/list-crl
new file mode 100755
index 0000000..d1d8a69
--- /dev/null
+++ b/easy-rsa/2.0/list-crl
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# list revoked certificates
+
+CRL="${1:-crl.pem}"
+
+if [ "$KEY_DIR" ]; then
+ cd "$KEY_DIR" && \
+ $OPENSSL crl -text -noout -in "$CRL"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/openssl-0.9.6.cnf b/easy-rsa/2.0/openssl-0.9.6.cnf
new file mode 100755
index 0000000..d28341d
--- /dev/null
+++ b/easy-rsa/2.0/openssl-0.9.6.cnf
@@ -0,0 +1,265 @@
+# For use with easy-rsa version 2.0
+
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
diff --git a/easy-rsa/2.0/openssl-0.9.8.cnf b/easy-rsa/2.0/openssl-0.9.8.cnf
new file mode 100755
index 0000000..340b8af
--- /dev/null
+++ b/easy-rsa/2.0/openssl-0.9.8.cnf
@@ -0,0 +1,290 @@
+# For use with easy-rsa version 2.0
+
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/easy-rsa/2.0/openssl-1.0.0.cnf b/easy-rsa/2.0/openssl-1.0.0.cnf
new file mode 100755
index 0000000..fa258a5
--- /dev/null
+++ b/easy-rsa/2.0/openssl-1.0.0.cnf
@@ -0,0 +1,285 @@
+# For use with easy-rsa version 2.0 and OpenSSL 1.0.0*
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation after 2004).
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/easy-rsa/2.0/openssl-1.0.0.cnf-old-copy b/easy-rsa/2.0/openssl-1.0.0.cnf-old-copy
new file mode 100644
index 0000000..da425aa
--- /dev/null
+++ b/easy-rsa/2.0/openssl-1.0.0.cnf-old-copy
@@ -0,0 +1,285 @@
+# For use with easy-rsa version 2.0 and OpenSSL 1.0.0*
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation after 2004).
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/easy-rsa/2.0/pkitool b/easy-rsa/2.0/pkitool
new file mode 100755
index 0000000..49588f5
--- /dev/null
+++ b/easy-rsa/2.0/pkitool
@@ -0,0 +1,379 @@
+#!/bin/sh
+
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# pkitool is a front-end for the openssl tool.
+
+# Calling scripts can set the certificate organizational
+# unit with the KEY_OU environmental variable.
+
+# Calling scripts can also set the KEY_NAME environmental
+# variable to set the "name" X509 subject field.
+
+PROGNAME=pkitool
+VERSION=2.0
+DEBUG=0
+
+die()
+{
+ local m="$1"
+
+ echo "$m" >&2
+ exit 1
+}
+
+need_vars()
+{
+ echo ' Please edit the vars script to reflect your configuration,'
+ echo ' then source it with "source ./vars".'
+ echo ' Next, to start with a fresh PKI configuration and to delete any'
+ echo ' previous certificates and keys, run "./clean-all".'
+ echo " Finally, you can run this tool ($PROGNAME) to build certificates/keys."
+}
+
+usage()
+{
+ echo "$PROGNAME $VERSION"
+ echo "Usage: $PROGNAME [options...] [common-name]"
+ echo "Options:"
+ echo " --batch : batch mode (default)"
+ echo " --keysize : Set keysize"
+ echo " size : size (default=1024)"
+ echo " --interact : interactive mode"
+ echo " --server : build server cert"
+ echo " --initca : build root CA"
+ echo " --inter : build intermediate CA"
+ echo " --pass : encrypt private key with password"
+ echo " --csr : only generate a CSR, do not sign"
+ echo " --sign : sign an existing CSR"
+ echo " --pkcs12 : generate a combined PKCS#12 file"
+ echo " --pkcs11 : generate certificate on PKCS#11 token"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " id : PKCS#11 object id (hex string)"
+ echo " label : PKCS#11 object label"
+ echo "Standalone options:"
+ echo " --pkcs11-slots : list PKCS#11 slots"
+ echo " lib : PKCS#11 library"
+ echo " --pkcs11-objects : list PKCS#11 token objects"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " --pkcs11-init : initialize PKCS#11 token DANGEROUS!!!"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " label : PKCS#11 token label"
+ echo "Notes:"
+ need_vars
+ echo " In order to use PKCS#11 interface you must have opensc-0.10.0 or higher."
+ echo "Generated files and corresponding OpenVPN directives:"
+ echo '(Files will be placed in the $KEY_DIR directory, defined in ./vars)'
+ echo " ca.crt -> root certificate (--ca)"
+ echo " ca.key -> root key, keep secure (not directly used by OpenVPN)"
+ echo " .crt files -> client/server certificates (--cert)"
+ echo " .key files -> private keys, keep secure (--key)"
+ echo " .csr files -> certificate signing request (not directly used by OpenVPN)"
+ echo " dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh)"
+ echo "Examples:"
+ echo " $PROGNAME --initca -> Build root certificate"
+ echo " $PROGNAME --initca --pass -> Build root certificate with password-protected key"
+ echo " $PROGNAME --server server1 -> Build \"server1\" certificate/key"
+ echo " $PROGNAME client1 -> Build \"client1\" certificate/key"
+ echo " $PROGNAME --pass client2 -> Build password-protected \"client2\" certificate/key"
+ echo " $PROGNAME --pkcs12 client3 -> Build \"client3\" certificate/key in PKCS#12 format"
+ echo " $PROGNAME --csr client4 -> Build \"client4\" CSR to be signed by another CA"
+ echo " $PROGNAME --sign client4 -> Sign \"client4\" CSR"
+ echo " $PROGNAME --inter interca -> Build an intermediate key-signing certificate/key"
+ echo " Also see ./inherit-inter script."
+ echo " $PROGNAME --pkcs11 /usr/lib/pkcs11/lib1 0 010203 \"client5 id\" client5"
+ echo " -> Build \"client5\" certificate/key in PKCS#11 token"
+ echo "Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys."
+ echo "Protect client2 key with a password. Build DH parms. Generated files in ./keys :"
+ echo " [edit vars with your site-specific info]"
+ echo " source ./vars"
+ echo " ./clean-all"
+ echo " ./build-dh -> takes a long time, consider backgrounding"
+ echo " ./$PROGNAME --initca"
+ echo " ./$PROGNAME --server myserver"
+ echo " ./$PROGNAME client1"
+ echo " ./$PROGNAME --pass client2"
+ echo "Typical usage for adding client cert to existing PKI:"
+ echo " source ./vars"
+ echo " ./$PROGNAME client-new"
+}
+
+# Set tool defaults
+[ -n "$OPENSSL" ] || export OPENSSL="openssl"
+[ -n "$PKCS11TOOL" ] || export PKCS11TOOL="pkcs11-tool"
+[ -n "$GREP" ] || export GREP="grep"
+
+# Set defaults
+DO_REQ="1"
+REQ_EXT=""
+DO_CA="1"
+CA_EXT=""
+DO_P12="0"
+DO_P11="0"
+DO_ROOT="0"
+NODES_REQ="-nodes"
+NODES_P12=""
+BATCH="-batch"
+CA="ca"
+# must be set or errors of openssl.cnf
+PKCS11_MODULE_PATH="dummy"
+PKCS11_PIN="dummy"
+
+# Process options
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --keysize ) KEY_SIZE=$2
+ shift;;
+ --server ) REQ_EXT="$REQ_EXT -extensions server"
+ CA_EXT="$CA_EXT -extensions server" ;;
+ --batch ) BATCH="-batch" ;;
+ --interact ) BATCH="" ;;
+ --inter ) CA_EXT="$CA_EXT -extensions v3_ca" ;;
+ --initca ) DO_ROOT="1" ;;
+ --pass ) NODES_REQ="" ;;
+ --csr ) DO_CA="0" ;;
+ --sign ) DO_REQ="0" ;;
+ --pkcs12 ) DO_P12="1" ;;
+ --pkcs11 ) DO_P11="1"
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ PKCS11_ID="$4"
+ PKCS11_LABEL="$5"
+ shift 4;;
+
+ # standalone
+ --pkcs11-init)
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ PKCS11_LABEL="$4"
+ if [ -z "$PKCS11_LABEL" ]; then
+ die "Please specify library name, slot and label"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --init-token --slot "$PKCS11_SLOT" \
+ --label "$PKCS11_LABEL" &&
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --init-pin --slot "$PKCS11_SLOT"
+ exit $?;;
+ --pkcs11-slots)
+ PKCS11_MODULE_PATH="$2"
+ if [ -z "$PKCS11_MODULE_PATH" ]; then
+ die "Please specify library name"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --list-slots
+ exit 0;;
+ --pkcs11-objects)
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ if [ -z "$PKCS11_SLOT" ]; then
+ die "Please specify library name and slot"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --list-objects --login --slot "$PKCS11_SLOT"
+ exit 0;;
+
+ --help|--usage)
+ usage
+ exit ;;
+ --version)
+ echo "$PROGNAME $VERSION"
+ exit ;;
+ # errors
+ --* ) die "$PROGNAME: unknown option: $1" ;;
+ * ) break ;;
+ esac
+ shift
+done
+
+if ! [ -z "$BATCH" ]; then
+ if $OPENSSL version | grep 0.9.6 > /dev/null; then
+ die "Batch mode is unsupported in openssl<0.9.7"
+ fi
+fi
+
+if [ $DO_P12 -eq 1 -a $DO_P11 -eq 1 ]; then
+ die "PKCS#11 and PKCS#12 cannot be specified together"
+fi
+
+if [ $DO_P11 -eq 1 ]; then
+ if ! grep "^pkcs11.*=" "$KEY_CONFIG" > /dev/null; then
+ die "Please edit $KEY_CONFIG and setup PKCS#11 engine"
+ fi
+fi
+
+# If we are generating pkcs12, only encrypt the final step
+if [ $DO_P12 -eq 1 ]; then
+ NODES_P12="$NODES_REQ"
+ NODES_REQ="-nodes"
+fi
+
+if [ $DO_P11 -eq 1 ]; then
+ if [ -z "$PKCS11_LABEL" ]; then
+ die "PKCS#11 arguments incomplete"
+ fi
+fi
+
+# If undefined, set default key expiration intervals
+if [ -z "$KEY_EXPIRE" ]; then
+ KEY_EXPIRE=3650
+fi
+if [ -z "$CA_EXPIRE" ]; then
+ CA_EXPIRE=3650
+fi
+
+# Set organizational unit to empty string if undefined
+if [ -z "$KEY_OU" ]; then
+ KEY_OU=""
+fi
+
+# Set X509 Name string to empty string if undefined
+if [ -z "$KEY_NAME" ]; then
+ KEY_NAME=""
+fi
+
+# Set KEY_CN, FN
+if [ $DO_ROOT -eq 1 ]; then
+ if [ -z "$KEY_CN" ]; then
+ if [ "$1" ]; then
+ KEY_CN="$1"
+ elif [ "$KEY_ORG" ]; then
+ KEY_CN="$KEY_ORG CA"
+ fi
+ fi
+ if [ $BATCH ] && [ "$KEY_CN" ]; then
+ echo "Using CA Common Name:" "$KEY_CN"
+ fi
+ FN="$KEY_CN"
+elif [ $BATCH ] && [ "$KEY_CN" ]; then
+ echo "Using Common Name:" "$KEY_CN"
+ FN="$KEY_CN"
+ if [ "$1" ]; then
+ FN="$1"
+ fi
+else
+ if [ $# -ne 1 ]; then
+ usage
+ exit 1
+ else
+ KEY_CN="$1"
+ fi
+ FN="$KEY_CN"
+fi
+
+export CA_EXPIRE KEY_EXPIRE KEY_OU KEY_NAME KEY_CN PKCS11_MODULE_PATH PKCS11_PIN
+
+# Show parameters (debugging)
+if [ $DEBUG -eq 1 ]; then
+ echo DO_REQ $DO_REQ
+ echo REQ_EXT $REQ_EXT
+ echo DO_CA $DO_CA
+ echo CA_EXT $CA_EXT
+ echo NODES_REQ $NODES_REQ
+ echo NODES_P12 $NODES_P12
+ echo DO_P12 $DO_P12
+ echo KEY_CN $KEY_CN
+ echo BATCH $BATCH
+ echo DO_ROOT $DO_ROOT
+ echo KEY_EXPIRE $KEY_EXPIRE
+ echo CA_EXPIRE $CA_EXPIRE
+ echo KEY_OU $KEY_OU
+ echo KEY_NAME $KEY_NAME
+ echo DO_P11 $DO_P11
+ echo PKCS11_MODULE_PATH $PKCS11_MODULE_PATH
+ echo PKCS11_SLOT $PKCS11_SLOT
+ echo PKCS11_ID $PKCS11_ID
+ echo PKCS11_LABEL $PKCS11_LABEL
+fi
+
+# Make sure ./vars was sourced beforehand
+if [ -d "$KEY_DIR" ] && [ "$KEY_CONFIG" ]; then
+ cd "$KEY_DIR"
+
+ # Make sure $KEY_CONFIG points to the correct version
+ # of openssl.cnf
+ if $GREP -i 'easy-rsa version 2\.[0-9]' "$KEY_CONFIG" >/dev/null; then
+ :
+ else
+ echo "$PROGNAME: KEY_CONFIG (set by the ./vars script) is pointing to the wrong"
+ echo "version of openssl.cnf: $KEY_CONFIG"
+ echo "The correct version should have a comment that says: easy-rsa version 2.x";
+ exit 1;
+ fi
+
+ # Build root CA
+ if [ $DO_ROOT -eq 1 ]; then
+ $OPENSSL req $BATCH -days $CA_EXPIRE $NODES_REQ -new -newkey rsa:$KEY_SIZE -sha1 \
+ -x509 -keyout "$CA.key" -out "$CA.crt" -config "$KEY_CONFIG" && \
+ chmod 0600 "$CA.key"
+ else
+ # Make sure CA key/cert is available
+ if [ $DO_CA -eq 1 ] || [ $DO_P12 -eq 1 ]; then
+ if [ ! -r "$CA.crt" ] || [ ! -r "$CA.key" ]; then
+ echo "$PROGNAME: Need a readable $CA.crt and $CA.key in $KEY_DIR"
+ echo "Try $PROGNAME --initca to build a root certificate/key."
+ exit 1
+ fi
+ fi
+
+ # Generate key for PKCS#11 token
+ PKCS11_ARGS=
+ if [ $DO_P11 -eq 1 ]; then
+ stty -echo
+ echo -n "User PIN: "
+ read -r PKCS11_PIN
+ stty echo
+ export PKCS11_PIN
+
+ echo "Generating key pair on PKCS#11 token..."
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --keypairgen \
+ --login --pin "$PKCS11_PIN" \
+ --key-type rsa:1024 \
+ --slot "$PKCS11_SLOT" --id "$PKCS11_ID" --label "$PKCS11_LABEL" || exit 1
+ PKCS11_ARGS="-engine pkcs11 -keyform engine -key $PKCS11_SLOT:$PKCS11_ID"
+ fi
+
+ # Build cert/key
+ ( [ $DO_REQ -eq 0 ] || $OPENSSL req $BATCH -days $KEY_EXPIRE $NODES_REQ -new -newkey rsa:$KEY_SIZE \
+ -keyout "$FN.key" -out "$FN.csr" $REQ_EXT -config "$KEY_CONFIG" $PKCS11_ARGS ) && \
+ ( [ $DO_CA -eq 0 ] || $OPENSSL ca $BATCH -days $KEY_EXPIRE -out "$FN.crt" \
+ -in "$FN.csr" $CA_EXT -md sha1 -config "$KEY_CONFIG" ) && \
+ ( [ $DO_P12 -eq 0 ] || $OPENSSL pkcs12 -export -inkey "$FN.key" \
+ -in "$FN.crt" -certfile "$CA.crt" -out "$FN.p12" $NODES_P12 ) && \
+ ( [ $DO_CA -eq 0 -o $DO_P11 -eq 1 ] || chmod 0600 "$FN.key" ) && \
+ ( [ $DO_P12 -eq 0 ] || chmod 0600 "$FN.p12" )
+
+ # Load certificate into PKCS#11 token
+ if [ $DO_P11 -eq 1 ]; then
+ $OPENSSL x509 -in "$FN.crt" -inform PEM -out "$FN.crt.der" -outform DER && \
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --write-object "$FN.crt.der" --type cert \
+ --login --pin "$PKCS11_PIN" \
+ --slot "$PKCS11_SLOT" --id "$PKCS11_ID" --label "$PKCS11_LABEL"
+ [ -e "$FN.crt.der" ]; rm "$FN.crt.der"
+ fi
+
+ fi
+
+# Need definitions
+else
+ need_vars
+fi
diff --git a/easy-rsa/2.0/revoke-full b/easy-rsa/2.0/revoke-full
new file mode 100755
index 0000000..4169c4c
--- /dev/null
+++ b/easy-rsa/2.0/revoke-full
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# revoke a certificate, regenerate CRL,
+# and verify revocation
+
+CRL="crl.pem"
+RT="revoke-test.pem"
+
+if [ $# -ne 1 ]; then
+ echo "usage: revoke-full <cert-name-base>";
+ exit 1
+fi
+
+if [ "$KEY_DIR" ]; then
+ cd "$KEY_DIR"
+ rm -f "$RT"
+
+ # set defaults
+ export KEY_CN=""
+ export KEY_OU=""
+ export KEY_NAME=""
+
+ # revoke key and generate a new CRL
+ $OPENSSL ca -revoke "$1.crt" -config "$KEY_CONFIG"
+
+ # generate a new CRL -- try to be compatible with
+ # intermediate PKIs
+ $OPENSSL ca -gencrl -out "$CRL" -config "$KEY_CONFIG"
+ if [ -e export-ca.crt ]; then
+ cat export-ca.crt "$CRL" >"$RT"
+ else
+ cat ca.crt "$CRL" >"$RT"
+ fi
+
+ # verify the revocation
+ $OPENSSL verify -CAfile "$RT" -crl_check "$1.crt"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/sign-req b/easy-rsa/2.0/sign-req
new file mode 100755
index 0000000..6cae7b4
--- /dev/null
+++ b/easy-rsa/2.0/sign-req
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Sign a certificate signing request (a .csr file)
+# with a local root certificate and key.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --sign $*
diff --git a/easy-rsa/2.0/tmp/README b/easy-rsa/2.0/tmp/README
new file mode 100644
index 0000000..6f5395c
--- /dev/null
+++ b/easy-rsa/2.0/tmp/README
@@ -0,0 +1,229 @@
+EASY-RSA Version 2.0-rc1
+
+This is a small RSA key management package, based on the openssl
+command line tool, that can be found in the easy-rsa subdirectory
+of the OpenVPN distribution. While this tool is primary concerned
+with key management for the SSL VPN application space, it can also
+be used for building web certificates.
+
+These are reference notes. For step-by-step instructions, see the
+HOWTO:
+
+http://openvpn.net/howto.html
+
+This package is based on the ./pkitool script. Run ./pkitool
+without arguments for a detailed help message (which is also pasted
+below).
+
+Release Notes for easy-rsa-2.0
+
+* Most functionality has been consolidated into the pkitool
+ script. For compatibility, all previous scripts from 1.0 such
+ as build-key and build-key-server are provided as stubs
+ which call pkitool to do the real work.
+
+* pkitool has a --batch flag (enabled by default) which generates
+ keys/certs without needing any interactive input. pkitool
+ can still generate certs/keys using interactive prompting by
+ using the --interact flag.
+
+* The inherit-inter script has been provided for creating
+ a new PKI rooted on an intermediate certificate built within a
+ higher-level PKI. See comments in the inherit-inter script
+ for more info.
+
+* The openssl.cnf file has been modified. pkitool will not
+ work with the openssl.cnf file included with previous
+ easy-rsa releases.
+
+* The vars file has been modified -- the following extra
+ variables have been added: EASY_RSA, CA_EXPIRE,
+ KEY_EXPIRE.
+
+* The make-crl and revoke-crt scripts have been removed and
+ are replaced by the revoke-full script.
+
+* The "Organizational Unit" X509 field can be set using
+ the KEY_OU environmental variable before calling pkitool.
+
+* This release only affects the Linux/Unix version of easy-rsa.
+ The Windows version (written to use the Windows shell) is unchanged.
+
+* Use the revoke-full script to revoke a certificate, and generate
+ (or update) the crl.pem file in the keys directory (as set by the
+ vars script). Then use "crl-verify crl.pem" in your OpenVPN server
+ config file, so that OpenVPN can reject any connections coming from
+ clients which present a revoked certificate. Usage for the script is:
+
+ revoke-full <common-name>
+
+ Note this this procedure is primarily designed to revoke client
+ certificates. You could theoretically use this method to revoke
+ server certificates as well, but then you would need to propagate
+ the crl.pem file to all clients as well, and have them include
+ "crl-verify crl.pem" in their configuration files.
+
+* PKCS#11 support was added.
+
+* For those interested in using this tool to generate web certificates,
+ A variant of the easy-rsa package that allows the creation of multi-domain
+ certificates with subjectAltName can be obtained from here:
+
+ http://www.bisente.com/proyectos/easy-rsa-subjectaltname/
+
+INSTALL easy-rsa
+
+1. Edit vars.
+2. Set KEY_CONFIG to point to the correct openssl-<version>.cnf
+ file included in this distribution.
+3. Set KEY_DIR to point to a directory which will
+ contain all keys, certificates, etc. This
+ directory need not exist, and if it does,
+ it will be deleted with rm -rf, so BE
+ CAREFUL how you set KEY_DIR.
+4. (Optional) Edit other fields in vars
+ per your site data. You may want to
+ increase KEY_SIZE to 2048 if you are
+ paranoid and don't mind slower key
+ processing, but certainly 1024 is
+ fine for testing purposes. KEY_SIZE
+ must be compatible across both peers
+ participating in a secure SSL/TLS
+ connection.
+5. (Optional) If you intend to use PKCS#11,
+ install openssl >= 0.9.7, install the
+ following components from www.opensc.org:
+ - opensc >= 0.10.0
+ - engine_pkcs11 >= 0.1.3
+ Update the openssl.cnf to load the engine:
+ - Uncomment pkcs11 under engine_section.
+ - Validate path at dynamic_path under pkcs11_section.
+6. . vars
+7. ./clean-all
+8. As you create certificates, keys, and
+ certificate signing requests, understand that
+ only .key files should be kept confidential.
+ .crt and .csr files can be sent over insecure
+ channels such as plaintext email.
+
+IMPORTANT
+
+To avoid a possible Man-in-the-Middle attack where an authorized
+client tries to connect to another client by impersonating the
+server, make sure to enforce some kind of server certificate
+verification by clients. There are currently four different ways
+of accomplishing this, listed in the order of preference:
+
+(1) Build your server certificates with specific key usage and
+ extended key usage. The RFC3280 determine that the following
+ attributes should be provided for TLS connections:
+
+ Mode Key usage Extended key usage
+ ---------------------------------------------------------------------------
+ Client digitalSignature TLS Web Client Authentication
+ keyAgreement
+ digitalSignature, keyAgreement
+
+ Server digitalSignature, keyEncipherment TLS Web Server Authentication
+ digitalSignature, keyAgreement
+
+ Now add the following line to your client configuration:
+
+ remote-cert-tls server
+
+ This will block clients from connecting to any
+ server which lacks the required extension designation
+ in its certificate, even if the certificate has been
+ signed by the CA which is cited in the OpenVPN configuration
+ file (--ca directive).
+
+(3) Use the --tls-remote directive on the client to
+ accept/reject the server connection based on the common
+ name of the server certificate.
+
+(3) Use a --tls-verify script or plugin to accept/reject the
+ server connection based on a custom test of the server
+ certificate's embedded X509 subject details.
+
+(4) Sign server certificates with one CA and client certificates
+ with a different CA. The client config "ca" directive should
+ reference the server-signing CA while the server config "ca"
+ directive should reference the client-signing CA.
+
+NOTES
+
+Show certificate fields:
+ openssl x509 -in cert.crt -text
+
+PKITOOL documentation
+
+pkitool 2.0
+Usage: pkitool [options...] [common-name]
+Options:
+ --batch : batch mode (default)
+ --keysize : Set keysize
+ size : size (default=1024)
+ --interact : interactive mode
+ --server : build server cert
+ --initca : build root CA
+ --inter : build intermediate CA
+ --pass : encrypt private key with password
+ --csr : only generate a CSR, do not sign
+ --sign : sign an existing CSR
+ --pkcs12 : generate a combined PKCS#12 file
+ --pkcs11 : generate certificate on PKCS#11 token
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ id : PKCS#11 object id (hex string)
+ label : PKCS#11 object label
+Standalone options:
+ --pkcs11-slots : list PKCS#11 slots
+ lib : PKCS#11 library
+ --pkcs11-objects : list PKCS#11 token objects
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ --pkcs11-init : initialize PKCS#11 token DANGEROUS!!!
+ lib : PKCS#11 library
+ slot : PKCS#11 slot
+ label : PKCS#11 token label
+Notes:
+ Please edit the vars script to reflect your configuration,
+ then source it with "source ./vars".
+ Next, to start with a fresh PKI configuration and to delete any
+ previous certificates and keys, run "./clean-all".
+ Finally, you can run this tool (pkitool) to build certificates/keys.
+ In order to use PKCS#11 interface you must have opensc-0.10.0 or higher.
+Generated files and corresponding OpenVPN directives:
+(Files will be placed in the $KEY_DIR directory, defined in ./vars)
+ ca.crt -> root certificate (--ca)
+ ca.key -> root key, keep secure (not directly used by OpenVPN)
+ .crt files -> client/server certificates (--cert)
+ .key files -> private keys, keep secure (--key)
+ .csr files -> certificate signing request (not directly used by OpenVPN)
+ dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh)
+Examples:
+ pkitool --initca -> Build root certificate
+ pkitool --initca --pass -> Build root certificate with password-protected key
+ pkitool --server server1 -> Build "server1" certificate/key
+ pkitool client1 -> Build "client1" certificate/key
+ pkitool --pass client2 -> Build password-protected "client2" certificate/key
+ pkitool --pkcs12 client3 -> Build "client3" certificate/key in PKCS#12 format
+ pkitool --csr client4 -> Build "client4" CSR to be signed by another CA
+ pkitool --sign client4 -> Sign "client4" CSR
+ pkitool --inter interca -> Build an intermediate key-signing certificate/key
+ Also see ./inherit-inter script.
+ pkitool --pkcs11 /usr/lib/pkcs11/lib1 0 010203 "client5 id" client5
+ -> Build "client5" certificate/key in PKCS#11 token
+Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys.
+Protect client2 key with a password. Build DH parms. Generated files in ./keys :
+ [edit vars with your site-specific info]
+ source ./vars
+ ./clean-all
+ ./build-dh -> takes a long time, consider backgrounding
+ ./pkitool --initca
+ ./pkitool --server myserver
+ ./pkitool client1
+ ./pkitool --pass client2
+Typical usage for adding client cert to existing PKI:
+ source ./vars
+ ./pkitool client-new
diff --git a/easy-rsa/2.0/tmp/build-ca b/easy-rsa/2.0/tmp/build-ca
new file mode 100755
index 0000000..bce29a6
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-ca
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+#
+# Build a root certificate
+#
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --initca $*
diff --git a/easy-rsa/2.0/tmp/build-dh b/easy-rsa/2.0/tmp/build-dh
new file mode 100755
index 0000000..4beb127
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-dh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Build Diffie-Hellman parameters for the server side
+# of an SSL/TLS connection.
+
+if [ -d $KEY_DIR ] && [ $KEY_SIZE ]; then
+ $OPENSSL dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE}
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/tmp/build-inter b/easy-rsa/2.0/tmp/build-inter
new file mode 100755
index 0000000..87bf98d
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-inter
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Make an intermediate CA certificate/private key pair using a locally generated
+# root certificate.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --inter $*
diff --git a/easy-rsa/2.0/tmp/build-key b/easy-rsa/2.0/tmp/build-key
new file mode 100755
index 0000000..6c0fed8
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-key
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact $*
diff --git a/easy-rsa/2.0/tmp/build-key-pass b/easy-rsa/2.0/tmp/build-key-pass
new file mode 100755
index 0000000..8ef8307
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-key-pass
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Similar to build-key, but protect the private key
+# with a password.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --pass $*
diff --git a/easy-rsa/2.0/tmp/build-key-pkcs12 b/easy-rsa/2.0/tmp/build-key-pkcs12
new file mode 100755
index 0000000..ba90e6a
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-key-pkcs12
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate and convert it to a PKCS #12 file including the
+# the CA certificate as well.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --pkcs12 $*
diff --git a/easy-rsa/2.0/tmp/build-key-server b/easy-rsa/2.0/tmp/build-key-server
new file mode 100755
index 0000000..fee0194
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-key-server
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Make a certificate/private key pair using a locally generated
+# root certificate.
+#
+# Explicitly set nsCertType to server using the "server"
+# extension in the openssl.cnf file.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --server $*
diff --git a/easy-rsa/2.0/tmp/build-req b/easy-rsa/2.0/tmp/build-req
new file mode 100755
index 0000000..559d512
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-req
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Build a certificate signing request and private key. Use this
+# when your root certificate and key is not available locally.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --csr $*
diff --git a/easy-rsa/2.0/tmp/build-req-pass b/easy-rsa/2.0/tmp/build-req-pass
new file mode 100755
index 0000000..b73ee1b
--- /dev/null
+++ b/easy-rsa/2.0/tmp/build-req-pass
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Like build-req, but protect your private key
+# with a password.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --csr --pass $*
diff --git a/easy-rsa/2.0/tmp/clean-all b/easy-rsa/2.0/tmp/clean-all
new file mode 100755
index 0000000..cc6e3b2
--- /dev/null
+++ b/easy-rsa/2.0/tmp/clean-all
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# Initialize the $KEY_DIR directory.
+# Note that this script does a
+# rm -rf on $KEY_DIR so be careful!
+
+if [ "$KEY_DIR" ]; then
+ rm -rf "$KEY_DIR"
+ mkdir "$KEY_DIR" && \
+ chmod go-rwx "$KEY_DIR" && \
+ touch "$KEY_DIR/index.txt" && \
+ echo 01 >"$KEY_DIR/serial"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/tmp/file b/easy-rsa/2.0/tmp/file
new file mode 100644
index 0000000..1987bd7
--- /dev/null
+++ b/easy-rsa/2.0/tmp/file
@@ -0,0 +1 @@
+./openssl-1.0.0.cnf
diff --git a/easy-rsa/2.0/tmp/inherit-inter b/easy-rsa/2.0/tmp/inherit-inter
new file mode 100755
index 0000000..aaa5168
--- /dev/null
+++ b/easy-rsa/2.0/tmp/inherit-inter
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Build a new PKI which is rooted on an intermediate certificate generated
+# by ./build-inter or ./pkitool --inter from a parent PKI. The new PKI should
+# have independent vars settings, and must use a different KEY_DIR directory
+# from the parent. This tool can be used to generate arbitrary depth
+# certificate chains.
+#
+# To build an intermediate CA, follow the same steps for a regular PKI but
+# replace ./build-key or ./pkitool --initca with this script.
+
+# The EXPORT_CA file will contain the CA certificate chain and should be
+# referenced by the OpenVPN "ca" directive in config files. The ca.crt file
+# will only contain the local intermediate CA -- it's needed by the easy-rsa
+# scripts but not by OpenVPN directly.
+EXPORT_CA="export-ca.crt"
+
+if [ $# -ne 2 ]; then
+ echo "usage: $0 <parent-key-dir> <common-name>"
+ echo "parent-key-dir: the KEY_DIR directory of the parent PKI"
+ echo "common-name: the common name of the intermediate certificate in the parent PKI"
+ exit 1;
+fi
+
+if [ "$KEY_DIR" ]; then
+ cp "$1/$2.crt" "$KEY_DIR/ca.crt"
+ cp "$1/$2.key" "$KEY_DIR/ca.key"
+
+ if [ -e "$1/$EXPORT_CA" ]; then
+ PARENT_CA="$1/$EXPORT_CA"
+ else
+ PARENT_CA="$1/ca.crt"
+ fi
+ cp "$PARENT_CA" "$KEY_DIR/$EXPORT_CA"
+ cat "$KEY_DIR/ca.crt" >> "$KEY_DIR/$EXPORT_CA"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/tmp/list-crl b/easy-rsa/2.0/tmp/list-crl
new file mode 100755
index 0000000..d1d8a69
--- /dev/null
+++ b/easy-rsa/2.0/tmp/list-crl
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# list revoked certificates
+
+CRL="${1:-crl.pem}"
+
+if [ "$KEY_DIR" ]; then
+ cd "$KEY_DIR" && \
+ $OPENSSL crl -text -noout -in "$CRL"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/tmp/openssl-0.9.6.cnf b/easy-rsa/2.0/tmp/openssl-0.9.6.cnf
new file mode 100644
index 0000000..d28341d
--- /dev/null
+++ b/easy-rsa/2.0/tmp/openssl-0.9.6.cnf
@@ -0,0 +1,265 @@
+# For use with easy-rsa version 2.0
+
+#
+# OpenSSL example configuration file.
+# This is mostly being used for generation of certificate requests.
+#
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # which md to use.
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
diff --git a/easy-rsa/2.0/tmp/openssl-1.0.0.cnf b/easy-rsa/2.0/tmp/openssl-1.0.0.cnf
new file mode 100644
index 0000000..da425aa
--- /dev/null
+++ b/easy-rsa/2.0/tmp/openssl-1.0.0.cnf
@@ -0,0 +1,285 @@
+# For use with easy-rsa version 2.0 and OpenSSL 1.0.0*
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+openssl_conf = openssl_init
+
+[ openssl_init ]
+# Extra OBJECT IDENTIFIER info:
+#oid_file = $ENV::HOME/.oid
+oid_section = new_oids
+engines = engine_section
+
+# To use this configuration file with the "-extfile" option of the
+# "openssl x509" utility, name here the section containing the
+# X.509v3 extensions to use:
+# extensions =
+# (Alternatively, use a configuration file that has only
+# X.509v3 extensions in its main [= default] section.)
+
+[ new_oids ]
+
+# We can add new OIDs in here for use by 'ca' and 'req'.
+# Add a simple OID like this:
+# testoid1=1.2.3.4
+# Or use config file substitution like this:
+# testoid2=${testoid1}.5.6
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = $ENV::KEY_DIR # Where everything is kept
+certs = $dir # Where the issued certs are kept
+crl_dir = $dir # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir # default place for new certs.
+
+certificate = $dir/ca.crt # The CA certificate
+serial = $dir/serial # The current serial number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/ca.key # The private key
+RANDFILE = $dir/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crl_extensions = crl_ext
+
+default_days = 3650 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = md5 # use public key default MD
+preserve = no # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_anything
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+name = optional
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = $ENV::KEY_SIZE
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation after 2004).
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+string_mask = nombstr
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = $ENV::KEY_COUNTRY
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = $ENV::KEY_PROVINCE
+
+localityName = Locality Name (eg, city)
+localityName_default = $ENV::KEY_CITY
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = $ENV::KEY_ORG
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+
+commonName = Common Name (eg, your name or your server\'s hostname)
+commonName_max = 64
+
+name = Name
+name_max = 64
+
+emailAddress = Email Address
+emailAddress_default = $ENV::KEY_EMAIL
+emailAddress_max = 40
+
+# JY -- added for batch mode
+organizationalUnitName_default = $ENV::KEY_OU
+commonName_default = $ENV::KEY_CN
+name_default = $ENV::KEY_NAME
+
+
+# SET-ex3 = SET extension number 3
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+
+# This is OK for an SSL server.
+# nsCertType = server
+
+# For an object signing certificate this would be used.
+# nsCertType = objsign
+
+# For normal client use this is typical
+# nsCertType = client, email
+
+# and for everything including object signing:
+# nsCertType = client, email, objsign
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# This will be displayed in Netscape's comment listbox.
+nsComment = "Easy-RSA Generated Certificate"
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=clientAuth
+keyUsage = digitalSignature
+
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ server ]
+
+# JY ADDED -- Make a cert with nsCertType set to "server"
+basicConstraints=CA:FALSE
+nsCertType = server
+nsComment = "Easy-RSA Generated Server Certificate"
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+extendedKeyUsage=serverAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+
+# PKIX recommendation.
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
+
+[ engine_section ]
+#
+# If you are using PKCS#11
+# Install engine_pkcs11 of opensc (www.opensc.org)
+# And uncomment the following
+# verify that dynamic_path points to the correct location
+#
+#pkcs11 = pkcs11_section
+
+[ pkcs11_section ]
+engine_id = pkcs11
+dynamic_path = /usr/lib/engines/engine_pkcs11.so
+MODULE_PATH = $ENV::PKCS11_MODULE_PATH
+PIN = $ENV::PKCS11_PIN
+init = 0
diff --git a/easy-rsa/2.0/tmp/pkitool b/easy-rsa/2.0/tmp/pkitool
new file mode 100755
index 0000000..49588f5
--- /dev/null
+++ b/easy-rsa/2.0/tmp/pkitool
@@ -0,0 +1,379 @@
+#!/bin/sh
+
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# pkitool is a front-end for the openssl tool.
+
+# Calling scripts can set the certificate organizational
+# unit with the KEY_OU environmental variable.
+
+# Calling scripts can also set the KEY_NAME environmental
+# variable to set the "name" X509 subject field.
+
+PROGNAME=pkitool
+VERSION=2.0
+DEBUG=0
+
+die()
+{
+ local m="$1"
+
+ echo "$m" >&2
+ exit 1
+}
+
+need_vars()
+{
+ echo ' Please edit the vars script to reflect your configuration,'
+ echo ' then source it with "source ./vars".'
+ echo ' Next, to start with a fresh PKI configuration and to delete any'
+ echo ' previous certificates and keys, run "./clean-all".'
+ echo " Finally, you can run this tool ($PROGNAME) to build certificates/keys."
+}
+
+usage()
+{
+ echo "$PROGNAME $VERSION"
+ echo "Usage: $PROGNAME [options...] [common-name]"
+ echo "Options:"
+ echo " --batch : batch mode (default)"
+ echo " --keysize : Set keysize"
+ echo " size : size (default=1024)"
+ echo " --interact : interactive mode"
+ echo " --server : build server cert"
+ echo " --initca : build root CA"
+ echo " --inter : build intermediate CA"
+ echo " --pass : encrypt private key with password"
+ echo " --csr : only generate a CSR, do not sign"
+ echo " --sign : sign an existing CSR"
+ echo " --pkcs12 : generate a combined PKCS#12 file"
+ echo " --pkcs11 : generate certificate on PKCS#11 token"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " id : PKCS#11 object id (hex string)"
+ echo " label : PKCS#11 object label"
+ echo "Standalone options:"
+ echo " --pkcs11-slots : list PKCS#11 slots"
+ echo " lib : PKCS#11 library"
+ echo " --pkcs11-objects : list PKCS#11 token objects"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " --pkcs11-init : initialize PKCS#11 token DANGEROUS!!!"
+ echo " lib : PKCS#11 library"
+ echo " slot : PKCS#11 slot"
+ echo " label : PKCS#11 token label"
+ echo "Notes:"
+ need_vars
+ echo " In order to use PKCS#11 interface you must have opensc-0.10.0 or higher."
+ echo "Generated files and corresponding OpenVPN directives:"
+ echo '(Files will be placed in the $KEY_DIR directory, defined in ./vars)'
+ echo " ca.crt -> root certificate (--ca)"
+ echo " ca.key -> root key, keep secure (not directly used by OpenVPN)"
+ echo " .crt files -> client/server certificates (--cert)"
+ echo " .key files -> private keys, keep secure (--key)"
+ echo " .csr files -> certificate signing request (not directly used by OpenVPN)"
+ echo " dh1024.pem or dh2048.pem -> Diffie Hellman parameters (--dh)"
+ echo "Examples:"
+ echo " $PROGNAME --initca -> Build root certificate"
+ echo " $PROGNAME --initca --pass -> Build root certificate with password-protected key"
+ echo " $PROGNAME --server server1 -> Build \"server1\" certificate/key"
+ echo " $PROGNAME client1 -> Build \"client1\" certificate/key"
+ echo " $PROGNAME --pass client2 -> Build password-protected \"client2\" certificate/key"
+ echo " $PROGNAME --pkcs12 client3 -> Build \"client3\" certificate/key in PKCS#12 format"
+ echo " $PROGNAME --csr client4 -> Build \"client4\" CSR to be signed by another CA"
+ echo " $PROGNAME --sign client4 -> Sign \"client4\" CSR"
+ echo " $PROGNAME --inter interca -> Build an intermediate key-signing certificate/key"
+ echo " Also see ./inherit-inter script."
+ echo " $PROGNAME --pkcs11 /usr/lib/pkcs11/lib1 0 010203 \"client5 id\" client5"
+ echo " -> Build \"client5\" certificate/key in PKCS#11 token"
+ echo "Typical usage for initial PKI setup. Build myserver, client1, and client2 cert/keys."
+ echo "Protect client2 key with a password. Build DH parms. Generated files in ./keys :"
+ echo " [edit vars with your site-specific info]"
+ echo " source ./vars"
+ echo " ./clean-all"
+ echo " ./build-dh -> takes a long time, consider backgrounding"
+ echo " ./$PROGNAME --initca"
+ echo " ./$PROGNAME --server myserver"
+ echo " ./$PROGNAME client1"
+ echo " ./$PROGNAME --pass client2"
+ echo "Typical usage for adding client cert to existing PKI:"
+ echo " source ./vars"
+ echo " ./$PROGNAME client-new"
+}
+
+# Set tool defaults
+[ -n "$OPENSSL" ] || export OPENSSL="openssl"
+[ -n "$PKCS11TOOL" ] || export PKCS11TOOL="pkcs11-tool"
+[ -n "$GREP" ] || export GREP="grep"
+
+# Set defaults
+DO_REQ="1"
+REQ_EXT=""
+DO_CA="1"
+CA_EXT=""
+DO_P12="0"
+DO_P11="0"
+DO_ROOT="0"
+NODES_REQ="-nodes"
+NODES_P12=""
+BATCH="-batch"
+CA="ca"
+# must be set or errors of openssl.cnf
+PKCS11_MODULE_PATH="dummy"
+PKCS11_PIN="dummy"
+
+# Process options
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --keysize ) KEY_SIZE=$2
+ shift;;
+ --server ) REQ_EXT="$REQ_EXT -extensions server"
+ CA_EXT="$CA_EXT -extensions server" ;;
+ --batch ) BATCH="-batch" ;;
+ --interact ) BATCH="" ;;
+ --inter ) CA_EXT="$CA_EXT -extensions v3_ca" ;;
+ --initca ) DO_ROOT="1" ;;
+ --pass ) NODES_REQ="" ;;
+ --csr ) DO_CA="0" ;;
+ --sign ) DO_REQ="0" ;;
+ --pkcs12 ) DO_P12="1" ;;
+ --pkcs11 ) DO_P11="1"
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ PKCS11_ID="$4"
+ PKCS11_LABEL="$5"
+ shift 4;;
+
+ # standalone
+ --pkcs11-init)
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ PKCS11_LABEL="$4"
+ if [ -z "$PKCS11_LABEL" ]; then
+ die "Please specify library name, slot and label"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --init-token --slot "$PKCS11_SLOT" \
+ --label "$PKCS11_LABEL" &&
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --init-pin --slot "$PKCS11_SLOT"
+ exit $?;;
+ --pkcs11-slots)
+ PKCS11_MODULE_PATH="$2"
+ if [ -z "$PKCS11_MODULE_PATH" ]; then
+ die "Please specify library name"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --list-slots
+ exit 0;;
+ --pkcs11-objects)
+ PKCS11_MODULE_PATH="$2"
+ PKCS11_SLOT="$3"
+ if [ -z "$PKCS11_SLOT" ]; then
+ die "Please specify library name and slot"
+ fi
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --list-objects --login --slot "$PKCS11_SLOT"
+ exit 0;;
+
+ --help|--usage)
+ usage
+ exit ;;
+ --version)
+ echo "$PROGNAME $VERSION"
+ exit ;;
+ # errors
+ --* ) die "$PROGNAME: unknown option: $1" ;;
+ * ) break ;;
+ esac
+ shift
+done
+
+if ! [ -z "$BATCH" ]; then
+ if $OPENSSL version | grep 0.9.6 > /dev/null; then
+ die "Batch mode is unsupported in openssl<0.9.7"
+ fi
+fi
+
+if [ $DO_P12 -eq 1 -a $DO_P11 -eq 1 ]; then
+ die "PKCS#11 and PKCS#12 cannot be specified together"
+fi
+
+if [ $DO_P11 -eq 1 ]; then
+ if ! grep "^pkcs11.*=" "$KEY_CONFIG" > /dev/null; then
+ die "Please edit $KEY_CONFIG and setup PKCS#11 engine"
+ fi
+fi
+
+# If we are generating pkcs12, only encrypt the final step
+if [ $DO_P12 -eq 1 ]; then
+ NODES_P12="$NODES_REQ"
+ NODES_REQ="-nodes"
+fi
+
+if [ $DO_P11 -eq 1 ]; then
+ if [ -z "$PKCS11_LABEL" ]; then
+ die "PKCS#11 arguments incomplete"
+ fi
+fi
+
+# If undefined, set default key expiration intervals
+if [ -z "$KEY_EXPIRE" ]; then
+ KEY_EXPIRE=3650
+fi
+if [ -z "$CA_EXPIRE" ]; then
+ CA_EXPIRE=3650
+fi
+
+# Set organizational unit to empty string if undefined
+if [ -z "$KEY_OU" ]; then
+ KEY_OU=""
+fi
+
+# Set X509 Name string to empty string if undefined
+if [ -z "$KEY_NAME" ]; then
+ KEY_NAME=""
+fi
+
+# Set KEY_CN, FN
+if [ $DO_ROOT -eq 1 ]; then
+ if [ -z "$KEY_CN" ]; then
+ if [ "$1" ]; then
+ KEY_CN="$1"
+ elif [ "$KEY_ORG" ]; then
+ KEY_CN="$KEY_ORG CA"
+ fi
+ fi
+ if [ $BATCH ] && [ "$KEY_CN" ]; then
+ echo "Using CA Common Name:" "$KEY_CN"
+ fi
+ FN="$KEY_CN"
+elif [ $BATCH ] && [ "$KEY_CN" ]; then
+ echo "Using Common Name:" "$KEY_CN"
+ FN="$KEY_CN"
+ if [ "$1" ]; then
+ FN="$1"
+ fi
+else
+ if [ $# -ne 1 ]; then
+ usage
+ exit 1
+ else
+ KEY_CN="$1"
+ fi
+ FN="$KEY_CN"
+fi
+
+export CA_EXPIRE KEY_EXPIRE KEY_OU KEY_NAME KEY_CN PKCS11_MODULE_PATH PKCS11_PIN
+
+# Show parameters (debugging)
+if [ $DEBUG -eq 1 ]; then
+ echo DO_REQ $DO_REQ
+ echo REQ_EXT $REQ_EXT
+ echo DO_CA $DO_CA
+ echo CA_EXT $CA_EXT
+ echo NODES_REQ $NODES_REQ
+ echo NODES_P12 $NODES_P12
+ echo DO_P12 $DO_P12
+ echo KEY_CN $KEY_CN
+ echo BATCH $BATCH
+ echo DO_ROOT $DO_ROOT
+ echo KEY_EXPIRE $KEY_EXPIRE
+ echo CA_EXPIRE $CA_EXPIRE
+ echo KEY_OU $KEY_OU
+ echo KEY_NAME $KEY_NAME
+ echo DO_P11 $DO_P11
+ echo PKCS11_MODULE_PATH $PKCS11_MODULE_PATH
+ echo PKCS11_SLOT $PKCS11_SLOT
+ echo PKCS11_ID $PKCS11_ID
+ echo PKCS11_LABEL $PKCS11_LABEL
+fi
+
+# Make sure ./vars was sourced beforehand
+if [ -d "$KEY_DIR" ] && [ "$KEY_CONFIG" ]; then
+ cd "$KEY_DIR"
+
+ # Make sure $KEY_CONFIG points to the correct version
+ # of openssl.cnf
+ if $GREP -i 'easy-rsa version 2\.[0-9]' "$KEY_CONFIG" >/dev/null; then
+ :
+ else
+ echo "$PROGNAME: KEY_CONFIG (set by the ./vars script) is pointing to the wrong"
+ echo "version of openssl.cnf: $KEY_CONFIG"
+ echo "The correct version should have a comment that says: easy-rsa version 2.x";
+ exit 1;
+ fi
+
+ # Build root CA
+ if [ $DO_ROOT -eq 1 ]; then
+ $OPENSSL req $BATCH -days $CA_EXPIRE $NODES_REQ -new -newkey rsa:$KEY_SIZE -sha1 \
+ -x509 -keyout "$CA.key" -out "$CA.crt" -config "$KEY_CONFIG" && \
+ chmod 0600 "$CA.key"
+ else
+ # Make sure CA key/cert is available
+ if [ $DO_CA -eq 1 ] || [ $DO_P12 -eq 1 ]; then
+ if [ ! -r "$CA.crt" ] || [ ! -r "$CA.key" ]; then
+ echo "$PROGNAME: Need a readable $CA.crt and $CA.key in $KEY_DIR"
+ echo "Try $PROGNAME --initca to build a root certificate/key."
+ exit 1
+ fi
+ fi
+
+ # Generate key for PKCS#11 token
+ PKCS11_ARGS=
+ if [ $DO_P11 -eq 1 ]; then
+ stty -echo
+ echo -n "User PIN: "
+ read -r PKCS11_PIN
+ stty echo
+ export PKCS11_PIN
+
+ echo "Generating key pair on PKCS#11 token..."
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --keypairgen \
+ --login --pin "$PKCS11_PIN" \
+ --key-type rsa:1024 \
+ --slot "$PKCS11_SLOT" --id "$PKCS11_ID" --label "$PKCS11_LABEL" || exit 1
+ PKCS11_ARGS="-engine pkcs11 -keyform engine -key $PKCS11_SLOT:$PKCS11_ID"
+ fi
+
+ # Build cert/key
+ ( [ $DO_REQ -eq 0 ] || $OPENSSL req $BATCH -days $KEY_EXPIRE $NODES_REQ -new -newkey rsa:$KEY_SIZE \
+ -keyout "$FN.key" -out "$FN.csr" $REQ_EXT -config "$KEY_CONFIG" $PKCS11_ARGS ) && \
+ ( [ $DO_CA -eq 0 ] || $OPENSSL ca $BATCH -days $KEY_EXPIRE -out "$FN.crt" \
+ -in "$FN.csr" $CA_EXT -md sha1 -config "$KEY_CONFIG" ) && \
+ ( [ $DO_P12 -eq 0 ] || $OPENSSL pkcs12 -export -inkey "$FN.key" \
+ -in "$FN.crt" -certfile "$CA.crt" -out "$FN.p12" $NODES_P12 ) && \
+ ( [ $DO_CA -eq 0 -o $DO_P11 -eq 1 ] || chmod 0600 "$FN.key" ) && \
+ ( [ $DO_P12 -eq 0 ] || chmod 0600 "$FN.p12" )
+
+ # Load certificate into PKCS#11 token
+ if [ $DO_P11 -eq 1 ]; then
+ $OPENSSL x509 -in "$FN.crt" -inform PEM -out "$FN.crt.der" -outform DER && \
+ $PKCS11TOOL --module "$PKCS11_MODULE_PATH" --write-object "$FN.crt.der" --type cert \
+ --login --pin "$PKCS11_PIN" \
+ --slot "$PKCS11_SLOT" --id "$PKCS11_ID" --label "$PKCS11_LABEL"
+ [ -e "$FN.crt.der" ]; rm "$FN.crt.der"
+ fi
+
+ fi
+
+# Need definitions
+else
+ need_vars
+fi
diff --git a/easy-rsa/2.0/tmp/revoke-full b/easy-rsa/2.0/tmp/revoke-full
new file mode 100755
index 0000000..4169c4c
--- /dev/null
+++ b/easy-rsa/2.0/tmp/revoke-full
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# revoke a certificate, regenerate CRL,
+# and verify revocation
+
+CRL="crl.pem"
+RT="revoke-test.pem"
+
+if [ $# -ne 1 ]; then
+ echo "usage: revoke-full <cert-name-base>";
+ exit 1
+fi
+
+if [ "$KEY_DIR" ]; then
+ cd "$KEY_DIR"
+ rm -f "$RT"
+
+ # set defaults
+ export KEY_CN=""
+ export KEY_OU=""
+ export KEY_NAME=""
+
+ # revoke key and generate a new CRL
+ $OPENSSL ca -revoke "$1.crt" -config "$KEY_CONFIG"
+
+ # generate a new CRL -- try to be compatible with
+ # intermediate PKIs
+ $OPENSSL ca -gencrl -out "$CRL" -config "$KEY_CONFIG"
+ if [ -e export-ca.crt ]; then
+ cat export-ca.crt "$CRL" >"$RT"
+ else
+ cat ca.crt "$CRL" >"$RT"
+ fi
+
+ # verify the revocation
+ $OPENSSL verify -CAfile "$RT" -crl_check "$1.crt"
+else
+ echo 'Please source the vars script first (i.e. "source ./vars")'
+ echo 'Make sure you have edited it to reflect your configuration.'
+fi
diff --git a/easy-rsa/2.0/tmp/sign-req b/easy-rsa/2.0/tmp/sign-req
new file mode 100755
index 0000000..6cae7b4
--- /dev/null
+++ b/easy-rsa/2.0/tmp/sign-req
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+# Sign a certificate signing request (a .csr file)
+# with a local root certificate and key.
+
+export EASY_RSA="${EASY_RSA:-.}"
+"$EASY_RSA/pkitool" --interact --sign $*
diff --git a/easy-rsa/2.0/tmp/vars b/easy-rsa/2.0/tmp/vars
new file mode 100644
index 0000000..2ea1ced
--- /dev/null
+++ b/easy-rsa/2.0/tmp/vars
@@ -0,0 +1,74 @@
+# easy-rsa parameter settings
+
+# NOTE: If you installed from an RPM,
+# don't edit this file in place in
+# /usr/share/openvpn/easy-rsa --
+# instead, you should copy the whole
+# easy-rsa directory to another location
+# (such as /etc/openvpn) so that your
+# edits will not be wiped out by a future
+# OpenVPN package upgrade.
+
+# This variable should point to
+# the top level of the easy-rsa
+# tree.
+export EASY_RSA="`pwd`"
+
+#
+# This variable should point to
+# the requested executables
+#
+export OPENSSL="openssl"
+export PKCS11TOOL="pkcs11-tool"
+export GREP="grep"
+
+
+# This variable should point to
+# the openssl.cnf file included
+# with easy-rsa.
+export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
+
+# Edit this variable to point to
+# your soon-to-be-created key
+# directory.
+#
+# WARNING: clean-all will do
+# a rm -rf on this directory
+# so make sure you define
+# it correctly!
+export KEY_DIR="$EASY_RSA/keys"
+
+# Issue rm -rf warning
+echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
+
+# PKCS11 fixes
+export PKCS11_MODULE_PATH="dummy"
+export PKCS11_PIN="dummy"
+
+# Increase this to 2048 if you
+# are paranoid. This will slow
+# down TLS negotiation performance
+# as well as the one-time DH parms
+# generation process.
+export KEY_SIZE=1024
+
+# In how many days should the root CA key expire?
+export CA_EXPIRE=3650
+
+# In how many days should certificates expire?
+export KEY_EXPIRE=3650
+
+# These are the default values for fields
+# which will be placed in the certificate.
+# Don't leave any of these fields blank.
+export KEY_COUNTRY="US"
+export KEY_PROVINCE="CA"
+export KEY_CITY="SanFrancisco"
+export KEY_ORG="Fort-Funston"
+export KEY_EMAIL="me@myhost.mydomain"
+export KEY_EMAIL=mail@host.domain
+export KEY_CN=changeme
+export KEY_NAME=changeme
+export KEY_OU=changeme
+export PKCS11_MODULE_PATH=changeme
+export PKCS11_PIN=1234
diff --git a/easy-rsa/2.0/tmp/whichopensslcnf b/easy-rsa/2.0/tmp/whichopensslcnf
new file mode 100755
index 0000000..94225cb
--- /dev/null
+++ b/easy-rsa/2.0/tmp/whichopensslcnf
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+cnf="$1/openssl.cnf"
+if [ "$OPENSSL" ]; then
+ if $OPENSSL version | grep 0.9.6 > /dev/null; then
+ cnf="$1/openssl-0.9.6.cnf"
+ elif $OPENSSL version | grep -E "1\.0\.([[:digit:]][[:alnum:]])" > /dev/null; then
+ cnf="$1/openssl-1.0.0.cnf"
+ else
+ cnf="$1/openssl.cnf"
+ fi
+fi
+
+echo $cnf
+
+if [ ! -r $cnf ]; then
+ echo "**************************************************************" >&2
+ echo " No $cnf file could be found" >&2
+ echo " Further invocations will fail" >&2
+ echo "**************************************************************" >&2
+fi
+
+exit 0
diff --git a/easy-rsa/2.0/vars b/easy-rsa/2.0/vars
new file mode 100755
index 0000000..2ea1ced
--- /dev/null
+++ b/easy-rsa/2.0/vars
@@ -0,0 +1,74 @@
+# easy-rsa parameter settings
+
+# NOTE: If you installed from an RPM,
+# don't edit this file in place in
+# /usr/share/openvpn/easy-rsa --
+# instead, you should copy the whole
+# easy-rsa directory to another location
+# (such as /etc/openvpn) so that your
+# edits will not be wiped out by a future
+# OpenVPN package upgrade.
+
+# This variable should point to
+# the top level of the easy-rsa
+# tree.
+export EASY_RSA="`pwd`"
+
+#
+# This variable should point to
+# the requested executables
+#
+export OPENSSL="openssl"
+export PKCS11TOOL="pkcs11-tool"
+export GREP="grep"
+
+
+# This variable should point to
+# the openssl.cnf file included
+# with easy-rsa.
+export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
+
+# Edit this variable to point to
+# your soon-to-be-created key
+# directory.
+#
+# WARNING: clean-all will do
+# a rm -rf on this directory
+# so make sure you define
+# it correctly!
+export KEY_DIR="$EASY_RSA/keys"
+
+# Issue rm -rf warning
+echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
+
+# PKCS11 fixes
+export PKCS11_MODULE_PATH="dummy"
+export PKCS11_PIN="dummy"
+
+# Increase this to 2048 if you
+# are paranoid. This will slow
+# down TLS negotiation performance
+# as well as the one-time DH parms
+# generation process.
+export KEY_SIZE=1024
+
+# In how many days should the root CA key expire?
+export CA_EXPIRE=3650
+
+# In how many days should certificates expire?
+export KEY_EXPIRE=3650
+
+# These are the default values for fields
+# which will be placed in the certificate.
+# Don't leave any of these fields blank.
+export KEY_COUNTRY="US"
+export KEY_PROVINCE="CA"
+export KEY_CITY="SanFrancisco"
+export KEY_ORG="Fort-Funston"
+export KEY_EMAIL="me@myhost.mydomain"
+export KEY_EMAIL=mail@host.domain
+export KEY_CN=changeme
+export KEY_NAME=changeme
+export KEY_OU=changeme
+export PKCS11_MODULE_PATH=changeme
+export PKCS11_PIN=1234
diff --git a/easy-rsa/2.0/whichopensslcnf b/easy-rsa/2.0/whichopensslcnf
new file mode 100755
index 0000000..2226a8e
--- /dev/null
+++ b/easy-rsa/2.0/whichopensslcnf
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+cnf="$1/openssl.cnf"
+
+if [ "$OPENSSL" ]; then
+ if $OPENSSL version | grep -E "0\.9\.6[[:alnum:]]" > /dev/null; then
+ cnf="$1/openssl-0.9.6.cnf"
+ elif $OPENSSL version | grep -E "0\.9\.8[[:alnum:]]" > /dev/null; then
+ cnf="$1/openssl-0.9.8.cnf"
+ elif $OPENSSL version | grep -E "1\.0\.([[:digit:]][[:alnum:]])" > /dev/null; then
+ cnf="$1/openssl-1.0.0.cnf"
+ else
+ cnf="$1/openssl.cnf"
+ fi
+fi
+
+echo $cnf
+
+if [ ! -r $cnf ]; then
+ echo "**************************************************************" >&2
+ echo " No $cnf file could be found" >&2
+ echo " Further invocations will fail" >&2
+ echo "**************************************************************" >&2
+fi
+
+exit 0
diff --git a/easy-rsa/Windows/README.txt b/easy-rsa/Windows/README.txt
new file mode 100644
index 0000000..2ede7b1
--- /dev/null
+++ b/easy-rsa/Windows/README.txt
@@ -0,0 +1,44 @@
+Extract all zip'd files to the OpenVPN home directory,
+including the openssl.cnf file from the top-level
+"easy-rsa" directory.
+
+First run init-config.bat
+
+Next, edit vars.bat to adapt it to your environment, and
+create the directory that will hold your key files.
+
+To generate TLS keys:
+
+Create new empty index and serial files (once only)
+1. vars
+2. clean-all
+
+Build a CA key (once only)
+1. vars
+2. build-ca
+
+Build a DH file (for server side, once only)
+1. vars
+2. build-dh
+
+Build a private key/certficate for the openvpn server
+1. vars
+2. build-key-server <machine-name>
+
+Build key files in PEM format (for each client machine)
+1. vars
+2. build-key <machine-name>
+ (use <machine name> for specific name within script)
+
+or
+
+Build key files in PKCS #12 format (for each client machine)
+1. vars
+2. build-key-pkcs12 <machine-name>
+ (use <machine name> for specific name within script)
+
+To revoke a TLS certificate and generate a CRL file:
+1. vars
+2. revoke-full <machine-name>
+3. verify last line of output confirms revokation
+4. copy crl.pem to server directory and ensure config file uses "crl-verify <crl filename>"
diff --git a/easy-rsa/Windows/build-ca-pass.bat b/easy-rsa/Windows/build-ca-pass.bat
new file mode 100644
index 0000000..ab0b2a4
--- /dev/null
+++ b/easy-rsa/Windows/build-ca-pass.bat
@@ -0,0 +1,8 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/build-ca.bat b/easy-rsa/Windows/build-ca.bat
new file mode 100644
index 0000000..a3f234b
--- /dev/null
+++ b/easy-rsa/Windows/build-ca.bat
@@ -0,0 +1,4 @@
+@echo off
+cd %HOME%
+rem build a cert authority valid for ten years, starting now
+openssl req -days 3650 -nodes -new -x509 -keyout %KEY_DIR%\ca.key -out %KEY_DIR%\ca.crt -config %KEY_CONFIG%
diff --git a/easy-rsa/Windows/build-dh.bat b/easy-rsa/Windows/build-dh.bat
new file mode 100644
index 0000000..74bc603
--- /dev/null
+++ b/easy-rsa/Windows/build-dh.bat
@@ -0,0 +1,4 @@
+@echo off
+cd %HOME%
+rem build a dh file for the server side
+openssl dhparam -out %KEY_DIR%/dh%KEY_SIZE%.pem %KEY_SIZE%
diff --git a/easy-rsa/Windows/build-key-pass.bat b/easy-rsa/Windows/build-key-pass.bat
new file mode 100644
index 0000000..ab0b2a4
--- /dev/null
+++ b/easy-rsa/Windows/build-key-pass.bat
@@ -0,0 +1,8 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/build-key-pkcs12.bat b/easy-rsa/Windows/build-key-pkcs12.bat
new file mode 100644
index 0000000..1fc083e
--- /dev/null
+++ b/easy-rsa/Windows/build-key-pkcs12.bat
@@ -0,0 +1,10 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -nodes -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem convert the key/cert and embed the ca cert into a pkcs12 file.
+openssl pkcs12 -export -inkey %KEY_DIR%\%1.key -in %KEY_DIR%\%1.crt -certfile %KEY_DIR%\ca.crt -out %KEY_DIR%\%1.p12
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/build-key-server-pass.bat b/easy-rsa/Windows/build-key-server-pass.bat
new file mode 100644
index 0000000..99ed4d3
--- /dev/null
+++ b/easy-rsa/Windows/build-key-server-pass.bat
@@ -0,0 +1,8 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -extensions server -config %KEY_CONFIG%
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/build-key-server.bat b/easy-rsa/Windows/build-key-server.bat
new file mode 100644
index 0000000..20e3605
--- /dev/null
+++ b/easy-rsa/Windows/build-key-server.bat
@@ -0,0 +1,8 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -nodes -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -extensions server -config %KEY_CONFIG%
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/build-key.bat b/easy-rsa/Windows/build-key.bat
new file mode 100644
index 0000000..c040904
--- /dev/null
+++ b/easy-rsa/Windows/build-key.bat
@@ -0,0 +1,8 @@
+@echo off
+cd %HOME%
+rem build a request for a cert that will be valid for ten years
+openssl req -days 3650 -nodes -new -keyout %KEY_DIR%\%1.key -out %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem sign the cert request with our ca, creating a cert/key pair
+openssl ca -days 3650 -out %KEY_DIR%\%1.crt -in %KEY_DIR%\%1.csr -config %KEY_CONFIG%
+rem delete any .old files created in this process, to avoid future file creation errors
+del /q %KEY_DIR%\*.old
diff --git a/easy-rsa/Windows/clean-all.bat b/easy-rsa/Windows/clean-all.bat
new file mode 100644
index 0000000..71cbf4d
--- /dev/null
+++ b/easy-rsa/Windows/clean-all.bat
@@ -0,0 +1,13 @@
+@echo off
+rem move to the HOME directory specified in VARS script
+cd %HOME%
+rem set a temporary KEY_DIR variable
+set d=%KEY_DIR%
+rem delete the KEY_DIR and any subdirs quietly
+rmdir /s /q %d%
+rem make a new KEY_DIR
+mkdir %d%
+rem copy in a fesh index file so we begin with an empty database
+copy index.txt.start %d%\index.txt
+rem copy in a fresh serial file so we begin generating keys at index 01
+copy serial.start %d%\serial.
diff --git a/easy-rsa/Windows/index.txt.start b/easy-rsa/Windows/index.txt.start
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/easy-rsa/Windows/index.txt.start
diff --git a/easy-rsa/Windows/init-config.bat b/easy-rsa/Windows/init-config.bat
new file mode 100755
index 0000000..12e6d78
--- /dev/null
+++ b/easy-rsa/Windows/init-config.bat
@@ -0,0 +1 @@
+copy vars.bat.sample vars.bat
diff --git a/easy-rsa/Windows/revoke-full.bat b/easy-rsa/Windows/revoke-full.bat
new file mode 100644
index 0000000..ef2e4b5
--- /dev/null
+++ b/easy-rsa/Windows/revoke-full.bat
@@ -0,0 +1,13 @@
+@echo off
+cd %HOME%
+rem revoke cert
+openssl ca -revoke %KEY_DIR%\%1.crt -config %KEY_CONFIG%
+rem generate new crl
+openssl ca -gencrl -out %KEY_DIR%\crl.pem -config %KEY_CONFIG%
+rem test revocation
+rem first concatinate ca cert with newly generated crl
+copy %KEY_DIR%\ca.crt+%KEY_DIR%\crl.pem %KEY_DIR%\revoke_test_file.pem
+rem now verify the revocation
+openssl verify -CAfile %KEY_DIR%\revoke_test_file.pem -crl_check %KEY_DIR%\%1.crt
+rem delete temporary test file
+del /q %KEY_DIR%\revoke_test_file.pem
diff --git a/easy-rsa/Windows/serial.start b/easy-rsa/Windows/serial.start
new file mode 100644
index 0000000..8a0f05e
--- /dev/null
+++ b/easy-rsa/Windows/serial.start
@@ -0,0 +1 @@
+01
diff --git a/easy-rsa/Windows/vars.bat.sample b/easy-rsa/Windows/vars.bat.sample
new file mode 100644
index 0000000..36e6f71
--- /dev/null
+++ b/easy-rsa/Windows/vars.bat.sample
@@ -0,0 +1,40 @@
+@echo off
+rem Edit this variable to point to
+rem the openssl.cnf file included
+rem with easy-rsa.
+
+set HOME=%ProgramFiles%\OpenVPN\easy-rsa
+set KEY_CONFIG=openssl-1.0.0.cnf
+
+rem Edit this variable to point to
+rem your soon-to-be-created key
+rem directory.
+rem
+rem WARNING: clean-all will do
+rem a rm -rf on this directory
+rem so make sure you define
+rem it correctly!
+set KEY_DIR=keys
+
+rem Increase this to 2048 if you
+rem are paranoid. This will slow
+rem down TLS negotiation performance
+rem as well as the one-time DH parms
+rem generation process.
+set KEY_SIZE=1024
+
+rem These are the default values for fields
+rem which will be placed in the certificate.
+rem Change these to reflect your site.
+rem Don't leave any of these parms blank.
+
+set KEY_COUNTRY=US
+set KEY_PROVINCE=CA
+set KEY_CITY=SanFrancisco
+set KEY_ORG=OpenVPN
+set KEY_EMAIL=mail@host.domain
+set KEY_CN=changeme
+set KEY_NAME=changeme
+set KEY_OU=changeme
+set PKCS11_MODULE_PATH=changeme
+set PKCS11_PIN=1234
diff --git a/errlevel.h b/errlevel.h
new file mode 100644
index 0000000..75c3194
--- /dev/null
+++ b/errlevel.h
@@ -0,0 +1,175 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ERRLEVEL_H
+#define ERRLEVEL_H
+
+#include "error.h"
+
+/*
+ * Debug level at and above where we
+ * display time to microsecond resolution.
+ */
+#define DEBUG_LEVEL_USEC_TIME 4
+
+/*
+ * In non-server modes, delay n milliseconds after certain kinds
+ * of non-fatal network errors to avoid a barrage of errors.
+ *
+ * To disable all delays, set to 0.
+ */
+#define P2P_ERROR_DELAY_MS 0
+
+/*
+ * Enable D_LOG_RW
+ */
+#define LOG_RW
+
+/*
+ * Debugging levels for various kinds
+ * of output.
+ */
+
+#define M_VERB0 LOGLEV(0, 0, 0) /* Messages displayed even at --verb 0 (fatal errors only) */
+
+#define M_INFO LOGLEV(1, 0, 0) /* default informational messages */
+
+#define D_LINK_ERRORS LOGLEV(1, 1, M_NONFATAL) /* show link errors from main event loop */
+#define D_CRYPT_ERRORS LOGLEV(1, 2, M_NONFATAL) /* show errors from encrypt/decrypt */
+#define D_TLS_ERRORS LOGLEV(1, 3, M_NONFATAL) /* show TLS control channel errors */
+#define D_RESOLVE_ERRORS LOGLEV(1, 4, M_NONFATAL) /* show hostname resolve errors */
+#define D_COMP_ERRORS LOGLEV(1, 5, M_NONFATAL) /* show compression errors */
+#define D_REPLAY_ERRORS LOGLEV(1, 6, M_NONFATAL) /* show packet replay errors */
+#define D_STREAM_ERRORS LOGLEV(1, 7, M_NONFATAL) /* TCP stream error requiring restart */
+#define D_IMPORT_ERRORS LOGLEV(1, 8, M_NONFATAL) /* show server import option errors */
+#define D_MULTI_ERRORS LOGLEV(1, 9, M_NONFATAL) /* show multi-client server errors */
+#define D_EVENT_ERRORS LOGLEV(1, 10, M_NONFATAL) /* show event.[ch] errors */
+#define D_PUSH_ERRORS LOGLEV(1, 11, M_NONFATAL) /* show push/pull errors */
+#define D_PID_PERSIST LOGLEV(1, 12, M_NONFATAL) /* show packet_id persist errors */
+#define D_FRAG_ERRORS LOGLEV(1, 13, M_NONFATAL) /* show fragmentation errors */
+#define D_ALIGN_ERRORS LOGLEV(1, 14, M_NONFATAL) /* show bad struct alignments */
+
+#define D_HANDSHAKE LOGLEV(2, 20, 0) /* show data & control channel handshakes */
+#define D_MTU_INFO LOGLEV(2, 21, 0) /* show terse MTU info */
+#define D_CLOSE LOGLEV(2, 22, 0) /* show socket and TUN/TAP close */
+#define D_SHOW_OCC_HASH LOGLEV(2, 23, 0) /* show MD5 hash of option compatibility string */
+#define D_PROXY LOGLEV(2, 24, 0) /* show http proxy control packets */
+#define D_ARGV LOGLEV(2, 25, 0) /* show struct argv errors */
+
+#define D_TLS_DEBUG_LOW LOGLEV(3, 20, 0) /* low frequency info from tls_session routines */
+#define D_GREMLIN LOGLEV(3, 30, 0) /* show simulated outage info from gremlin module */
+#define D_GENKEY LOGLEV(3, 31, 0) /* print message after key generation */
+#define D_ROUTE LOGLEV(3, 0, 0) /* show routes added and deleted (don't mute) */
+#define D_TUNTAP_INFO LOGLEV(3, 32, 0) /* show debugging info from TUN/TAP driver */
+#define D_RESTART LOGLEV(3, 33, 0) /* show certain restart messages */
+#define D_PUSH LOGLEV(3, 34, 0) /* show push/pull info */
+#define D_IFCONFIG_POOL LOGLEV(3, 35, 0) /* show ifconfig pool info */
+#define D_BACKTRACK LOGLEV(3, 36, 0) /* show replay backtracks */
+#define D_AUTH LOGLEV(3, 37, 0) /* show user/pass auth info */
+#define D_MULTI_LOW LOGLEV(3, 38, 0) /* show point-to-multipoint low-freq debug info */
+#define D_PLUGIN LOGLEV(3, 39, 0) /* show plugin calls */
+#define D_MANAGEMENT LOGLEV(3, 40, 0) /* show --management info */
+#define D_SCHED_EXIT LOGLEV(3, 41, 0) /* show arming of scheduled exit */
+#define D_ROUTE_QUOTA LOGLEV(3, 42, 0) /* show route quota exceeded messages */
+#define D_OSBUF LOGLEV(3, 43, 0) /* show socket/tun/tap buffer sizes */
+#define D_PS_PROXY LOGLEV(3, 44, 0) /* messages related to --port-share option */
+#define D_PF_INFO LOGLEV(3, 45, 0) /* packet filter informational messages */
+
+#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
+#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */
+#define D_LOW LOGLEV(4, 52, 0) /* miscellaneous low-frequency debug info */
+#define D_DHCP_OPT LOGLEV(4, 53, 0) /* show DHCP options binary string */
+#define D_MBUF LOGLEV(4, 54, 0) /* mbuf.[ch] routines */
+#define D_PACKET_TRUNC_ERR LOGLEV(4, 55, 0) /* PACKET_TRUNCATION_CHECK */
+#define D_PF_DROPPED LOGLEV(4, 56, 0) /* packet filter dropped a packet */
+#define D_MULTI_DROPPED LOGLEV(4, 57, 0) /* show point-to-multipoint packet drops */
+
+#define D_LOG_RW LOGLEV(5, 0, 0) /* Print 'R' or 'W' to stdout for read/write */
+
+#define D_LINK_RW LOGLEV(6, 60, M_DEBUG) /* show TCP/UDP reads/writes (terse) */
+#define D_TUN_RW LOGLEV(6, 60, M_DEBUG) /* show TUN/TAP reads/writes */
+#define D_TAP_WIN32_DEBUG LOGLEV(6, 60, M_DEBUG) /* show TAP-Win32 driver debug info */
+
+#define D_SHOW_KEYS LOGLEV(7, 70, M_DEBUG) /* show data channel encryption keys */
+#define D_SHOW_KEY_SOURCE LOGLEV(7, 70, M_DEBUG) /* show data channel key source entropy */
+#define D_REL_LOW LOGLEV(7, 70, M_DEBUG) /* show low frequency info from reliable layer */
+#define D_FRAG_DEBUG LOGLEV(7, 70, M_DEBUG) /* show fragment debugging info */
+#define D_WIN32_IO_LOW LOGLEV(7, 70, M_DEBUG) /* low freq win32 I/O debugging info */
+#define D_MTU_DEBUG LOGLEV(7, 70, M_DEBUG) /* show MTU debugging info */
+#define D_PID_DEBUG_LOW LOGLEV(7, 70, M_DEBUG) /* show low-freq packet-id debugging info */
+#define D_MULTI_DEBUG LOGLEV(7, 70, M_DEBUG) /* show medium-freq multi debugging info */
+#define D_MSS LOGLEV(7, 70, M_DEBUG) /* show MSS adjustments */
+#define D_COMP_LOW LOGLEV(7, 70, M_DEBUG) /* show adaptive compression state changes */
+#define D_CONNECTION_LIST LOGLEV(7, 70, M_DEBUG) /* show <connection> list info */
+#define D_SCRIPT LOGLEV(7, 70, M_DEBUG) /* show parms & env vars passed to scripts */
+#define D_SHOW_NET LOGLEV(7, 70, M_DEBUG) /* show routing table and adapter list */
+#define D_ROUTE_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose route.[ch] output */
+#define D_TLS_STATE_ERRORS LOGLEV(7, 70, M_DEBUG) /* no TLS state for client */
+#define D_SEMAPHORE_LOW LOGLEV(7, 70, M_DEBUG) /* show Win32 semaphore waits (low freq) */
+#define D_SEMAPHORE LOGLEV(7, 70, M_DEBUG) /* show Win32 semaphore waits */
+#define D_TEST_FILE LOGLEV(7, 70, M_DEBUG) /* show test_file() calls */
+#define D_MANAGEMENT_DEBUG LOGLEV(3, 70, M_DEBUG) /* show --management debug info */
+#define D_PLUGIN_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose plugin calls */
+#define D_SOCKET_DEBUG LOGLEV(7, 70, M_DEBUG) /* show socket.[ch] debugging info */
+#define D_SHOW_PKCS11 LOGLEV(7, 70, M_DEBUG) /* show PKCS#11 actions */
+#define D_ALIGN_DEBUG LOGLEV(7, 70, M_DEBUG) /* show verbose struct alignment info */
+#define D_PACKET_TRUNC_DEBUG LOGLEV(7, 70, M_DEBUG) /* PACKET_TRUNCATION_CHECK verbose */
+#define D_PING LOGLEV(7, 70, M_DEBUG) /* PING send/receive messages */
+#define D_PS_PROXY_DEBUG LOGLEV(7, 70, M_DEBUG) /* port share proxy debug */
+#define D_AUTO_USERID LOGLEV(7, 70, M_DEBUG) /* AUTO_USERID debugging */
+#define D_TLS_KEYSELECT LOGLEV(7, 70, M_DEBUG) /* show information on key selection for data channel */
+#define D_ARGV_PARSE_CMD LOGLEV(7, 70, M_DEBUG) /* show parse_line() errors in argv_printf %sc */
+#define D_CRYPTO_DEBUG LOGLEV(7, 70, M_DEBUG) /* show detailed info from crypto.c routines */
+#define D_PF_DROPPED_BCAST LOGLEV(7, 71, M_DEBUG) /* packet filter dropped a broadcast packet */
+#define D_PF_DEBUG LOGLEV(7, 72, M_DEBUG) /* packet filter debugging, must also define PF_DEBUG in pf.h */
+
+#define D_HANDSHAKE_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show detailed description of each handshake */
+#define D_TLS_DEBUG_MED LOGLEV(8, 70, M_DEBUG) /* limited info from tls_session routines */
+#define D_INTERVAL LOGLEV(8, 70, M_DEBUG) /* show interval.h debugging info */
+#define D_SCHEDULER LOGLEV(8, 70, M_DEBUG) /* show scheduler debugging info */
+#define D_GREMLIN_VERBOSE LOGLEV(8, 70, M_DEBUG) /* show verbose info from gremlin module */
+#define D_REL_DEBUG LOGLEV(8, 70, M_DEBUG) /* show detailed info from reliable routines */
+#define D_EVENT_WAIT LOGLEV(8, 70, M_DEBUG) /* show detailed info from event waits */
+#define D_MULTI_TCP LOGLEV(8, 70, M_DEBUG) /* show debug info from mtcp.c */
+
+#define D_TLS_DEBUG LOGLEV(9, 70, M_DEBUG) /* show detailed info from TLS routines */
+#define D_COMP LOGLEV(9, 70, M_DEBUG) /* show compression info */
+#define D_READ_WRITE LOGLEV(9, 70, M_DEBUG) /* show all tun/tcp/udp reads/writes/opens */
+#define D_PACKET_CONTENT LOGLEV(9, 70, M_DEBUG) /* show before/after encryption packet content */
+#define D_TLS_NO_SEND_KEY LOGLEV(9, 70, M_DEBUG) /* show when no data channel send-key exists */
+#define D_PID_DEBUG LOGLEV(9, 70, M_DEBUG) /* show packet-id debugging info */
+#define D_PID_PERSIST_DEBUG LOGLEV(9, 70, M_DEBUG) /* show packet-id persist debugging info */
+#define D_LINK_RW_VERBOSE LOGLEV(9, 70, M_DEBUG) /* show link reads/writes with greater verbosity */
+#define D_STREAM_DEBUG LOGLEV(9, 70, M_DEBUG) /* show TCP stream debug info */
+#define D_WIN32_IO LOGLEV(9, 70, M_DEBUG) /* win32 I/O debugging info */
+#define D_PKCS11_DEBUG LOGLEV(9, 70, M_DEBUG) /* show PKCS#11 debugging */
+
+#define D_SHAPER_DEBUG LOGLEV(10, 70, M_DEBUG) /* show traffic shaper info */
+
+#define D_REGISTRY LOGLEV(11, 70, M_DEBUG) /* win32 registry debugging info */
+#define D_OPENSSL_LOCK LOGLEV(11, 70, M_DEBUG) /* show OpenSSL locks */
+
+/*#define D_THREAD_DEBUG LOGLEV(4, 70, M_DEBUG)*/ /* show pthread debug information */
+
+#endif
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..9754464
--- /dev/null
+++ b/error.c
@@ -0,0 +1,863 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "error.h"
+#include "buffer.h"
+#include "misc.h"
+#include "win32.h"
+#include "socket.h"
+#include "tun.h"
+#include "otime.h"
+#include "perf.h"
+#include "status.h"
+#include "integer.h"
+#include "ps.h"
+
+#ifdef USE_CRYPTO
+#include <openssl/err.h>
+#endif
+
+#include "memdbg.h"
+
+#if SYSLOG_CAPABILITY
+#ifndef LOG_OPENVPN
+#define LOG_OPENVPN LOG_DAEMON
+#endif
+#endif
+
+/* Globals */
+unsigned int x_debug_level; /* GLOBAL */
+
+/* Mute state */
+static int mute_cutoff; /* GLOBAL */
+static int mute_count; /* GLOBAL */
+static int mute_category; /* GLOBAL */
+
+/*
+ * Output mode priorities are as follows:
+ *
+ * (1) --log-x overrides everything
+ * (2) syslog is used if --daemon or --inetd is defined and not --log-x
+ * (3) if OPENVPN_DEBUG_COMMAND_LINE is defined, output
+ * to constant logfile name.
+ * (4) Output to stdout.
+ */
+
+/* If true, indicates that stdin/stdout/stderr
+ have been redirected due to --log */
+static bool std_redir; /* GLOBAL */
+
+/* Should messages be written to the syslog? */
+static bool use_syslog; /* GLOBAL */
+
+/* Should timestamps be included on messages to stdout/stderr? */
+static bool suppress_timestamps; /* GLOBAL */
+
+/* The program name passed to syslog */
+#if SYSLOG_CAPABILITY
+static char *pgmname_syslog; /* GLOBAL */
+#endif
+
+/* If non-null, messages should be written here (used for debugging only) */
+static FILE *msgfp; /* GLOBAL */
+
+/* If true, we forked from main OpenVPN process */
+static bool forked; /* GLOBAL */
+
+/* our default output targets */
+static FILE *default_out; /* GLOBAL */
+static FILE *default_err; /* GLOBAL */
+
+void
+msg_forked (void)
+{
+ forked = true;
+}
+
+bool
+set_debug_level (const int level, const unsigned int flags)
+{
+ const int ceiling = 15;
+
+ if (level >= 0 && level <= ceiling)
+ {
+ x_debug_level = level;
+ return true;
+ }
+ else if (flags & SDL_CONSTRAIN)
+ {
+ x_debug_level = constrain_int (level, 0, ceiling);
+ return true;
+ }
+ return false;
+}
+
+bool
+set_mute_cutoff (const int cutoff)
+{
+ if (cutoff >= 0)
+ {
+ mute_cutoff = cutoff;
+ return true;
+ }
+ else
+ return false;
+}
+
+int
+get_debug_level (void)
+{
+ return x_debug_level;
+}
+
+int
+get_mute_cutoff (void)
+{
+ return mute_cutoff;
+}
+
+void
+set_suppress_timestamps (bool suppressed)
+{
+ suppress_timestamps = suppressed;
+}
+
+void
+error_reset ()
+{
+ use_syslog = std_redir = false;
+ suppress_timestamps = false;
+ x_debug_level = 1;
+ mute_cutoff = 0;
+ mute_count = 0;
+ mute_category = 0;
+ default_out = OPENVPN_MSG_FP;
+ default_err = OPENVPN_MSG_FP;
+
+#ifdef OPENVPN_DEBUG_COMMAND_LINE
+ msgfp = fopen (OPENVPN_DEBUG_FILE, "w");
+ if (!msgfp)
+ openvpn_exit (OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE); /* exit point */
+#else
+ msgfp = NULL;
+#endif
+}
+
+void
+errors_to_stderr (void)
+{
+ default_err = OPENVPN_ERROR_FP;
+}
+
+/*
+ * Return a file to print messages to before syslog is opened.
+ */
+FILE *
+msg_fp(const unsigned int flags)
+{
+ FILE *fp = msgfp;
+ if (!fp)
+ fp = (flags & (M_FATAL|M_USAGE_SMALL)) ? default_err : default_out;
+ if (!fp)
+ openvpn_exit (OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE); /* exit point */
+ return fp;
+}
+
+#define SWAP { tmp = m1; m1 = m2; m2 = tmp; }
+
+int x_msg_line_num; /* GLOBAL */
+
+void x_msg (const unsigned int flags, const char *format, ...)
+{
+ struct gc_arena gc;
+ va_list arglist;
+#if SYSLOG_CAPABILITY
+ int level;
+#endif
+ char *m1;
+ char *m2;
+ char *tmp;
+ int e;
+ const char *prefix;
+ const char *prefix_sep;
+
+ void usage_small (void);
+
+#ifndef HAVE_VARARG_MACROS
+ /* the macro has checked this otherwise */
+ if (!MSG_TEST (flags))
+ return;
+#endif
+
+ if (flags & M_ERRNO_SOCK)
+ e = openvpn_errno_socket ();
+ else
+ e = openvpn_errno ();
+
+ /*
+ * Apply muting filter.
+ */
+#ifndef HAVE_VARARG_MACROS
+ /* the macro has checked this otherwise */
+ if (!dont_mute (flags))
+ return;
+#endif
+
+ gc_init (&gc);
+
+ m1 = (char *) gc_malloc (ERR_BUF_SIZE, false, &gc);
+ m2 = (char *) gc_malloc (ERR_BUF_SIZE, false, &gc);
+
+ va_start (arglist, format);
+ vsnprintf (m1, ERR_BUF_SIZE, format, arglist);
+ va_end (arglist);
+ m1[ERR_BUF_SIZE - 1] = 0; /* windows vsnprintf needs this */
+
+ if ((flags & (M_ERRNO|M_ERRNO_SOCK)) && e)
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "%s: %s (errno=%d)",
+ m1, strerror_ts (e, &gc), e);
+ SWAP;
+ }
+
+#ifdef USE_CRYPTO
+ if (flags & M_SSL)
+ {
+ int nerrs = 0;
+ int err;
+ while ((err = ERR_get_error ()))
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "%s: %s",
+ m1, ERR_error_string (err, NULL));
+ SWAP;
+ ++nerrs;
+ }
+ if (!nerrs)
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "%s (OpenSSL)", m1);
+ SWAP;
+ }
+ }
+#endif
+
+ if (flags & M_OPTERR)
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "Options error: %s", m1);
+ SWAP;
+ }
+
+#if SYSLOG_CAPABILITY
+ if (flags & (M_FATAL|M_NONFATAL|M_USAGE_SMALL))
+ level = LOG_ERR;
+ else if (flags & M_WARN)
+ level = LOG_WARNING;
+ else
+ level = LOG_NOTICE;
+#endif
+
+ /* set up client prefix */
+ if (flags & M_NOIPREFIX)
+ prefix = NULL;
+ else
+ prefix = msg_get_prefix ();
+ prefix_sep = " ";
+ if (!prefix)
+ prefix_sep = prefix = "";
+
+ /* virtual output capability used to copy output to management subsystem */
+ if (!forked)
+ {
+ const struct virtual_output *vo = msg_get_virtual_output ();
+ if (vo)
+ {
+ openvpn_snprintf (m2, ERR_BUF_SIZE, "%s%s%s",
+ prefix,
+ prefix_sep,
+ m1);
+ virtual_output_print (vo, flags, m2);
+ }
+ }
+
+ if (!(flags & M_MSG_VIRT_OUT))
+ {
+ if (use_syslog && !std_redir && !forked)
+ {
+#if SYSLOG_CAPABILITY
+ syslog (level, "%s%s%s",
+ prefix,
+ prefix_sep,
+ m1);
+#endif
+ }
+ else
+ {
+ FILE *fp = msg_fp(flags);
+ const bool show_usec = check_debug_level (DEBUG_LEVEL_USEC_TIME);
+
+ if ((flags & M_NOPREFIX) || suppress_timestamps)
+ {
+ fprintf (fp, "%s%s%s%s",
+ prefix,
+ prefix_sep,
+ m1,
+ (flags&M_NOLF) ? "" : "\n");
+ }
+ else
+ {
+ fprintf (fp, "%s %s%s%s%s",
+ time_string (0, 0, show_usec, &gc),
+ prefix,
+ prefix_sep,
+ m1,
+ (flags&M_NOLF) ? "" : "\n");
+ }
+ fflush(fp);
+ ++x_msg_line_num;
+ }
+ }
+
+ if (flags & M_FATAL)
+ msg (M_INFO, "Exiting");
+
+ if (flags & M_FATAL)
+ openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
+
+ if (flags & M_USAGE_SMALL)
+ usage_small ();
+
+ gc_free (&gc);
+}
+
+/*
+ * Apply muting filter.
+ */
+bool
+dont_mute (unsigned int flags)
+{
+ bool ret = true;
+ if (mute_cutoff > 0 && !(flags & M_NOMUTE))
+ {
+ const int mute_level = DECODE_MUTE_LEVEL (flags);
+ if (mute_level > 0 && mute_level == mute_category)
+ {
+ if (mute_count == mute_cutoff)
+ msg (M_INFO | M_NOMUTE, "NOTE: --mute triggered...");
+ if (++mute_count > mute_cutoff)
+ ret = false;
+ }
+ else
+ {
+ const int suppressed = mute_count - mute_cutoff;
+ if (suppressed > 0)
+ msg (M_INFO | M_NOMUTE,
+ "%d variation(s) on previous %d message(s) suppressed by --mute",
+ suppressed,
+ mute_cutoff);
+ mute_count = 1;
+ mute_category = mute_level;
+ }
+ }
+ return ret;
+}
+
+void
+assert_failed (const char *filename, int line)
+{
+ msg (M_FATAL, "Assertion failed at %s:%d", filename, line);
+}
+
+/*
+ * Fail memory allocation. Don't use msg() because it tries
+ * to allocate memory as part of its operation.
+ */
+void
+out_of_memory (void)
+{
+ fprintf (stderr, PACKAGE_NAME ": Out of Memory\n");
+ exit (1);
+}
+
+void
+open_syslog (const char *pgmname, bool stdio_to_null)
+{
+#if SYSLOG_CAPABILITY
+ if (!msgfp && !std_redir)
+ {
+ if (!use_syslog)
+ {
+ pgmname_syslog = string_alloc (pgmname ? pgmname : PACKAGE, NULL);
+ openlog (pgmname_syslog, LOG_PID, LOG_OPENVPN);
+ use_syslog = true;
+
+ /* Better idea: somehow pipe stdout/stderr output to msg() */
+ if (stdio_to_null)
+ set_std_files_to_null (false);
+ }
+ }
+#else
+ msg (M_WARN, "Warning on use of --daemon/--inetd: this operating system lacks daemon logging features, therefore when I become a daemon, I won't be able to log status or error messages");
+#endif
+}
+
+void
+close_syslog ()
+{
+#if SYSLOG_CAPABILITY
+ if (use_syslog)
+ {
+ closelog();
+ use_syslog = false;
+ if (pgmname_syslog)
+ {
+ free (pgmname_syslog);
+ pgmname_syslog = NULL;
+ }
+ }
+#endif
+}
+
+#ifdef WIN32
+
+static HANDLE orig_stderr;
+
+HANDLE
+get_orig_stderr (void)
+{
+ if (orig_stderr)
+ return orig_stderr;
+ else
+ return GetStdHandle (STD_ERROR_HANDLE);
+}
+
+#endif
+
+void
+redirect_stdout_stderr (const char *file, bool append)
+{
+#if defined(WIN32)
+ if (!std_redir)
+ {
+ HANDLE log_handle;
+ int log_fd;
+
+ SECURITY_ATTRIBUTES saAttr;
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ log_handle = CreateFile (file,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ &saAttr,
+ append ? OPEN_ALWAYS : CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (log_handle == INVALID_HANDLE_VALUE)
+ {
+ msg (M_WARN|M_ERRNO, "Warning: cannot open --log file: %s", file);
+ return;
+ }
+
+ /* append to logfile? */
+ if (append)
+ {
+ if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ msg (M_ERR, "Error: cannot seek to end of --log file: %s", file);
+ }
+
+ /* save original stderr for password prompts */
+ orig_stderr = GetStdHandle (STD_ERROR_HANDLE);
+
+#if 0 /* seems not be necessary with stdout/stderr redirection below*/
+ /* set up for redirection */
+ if (!SetStdHandle (STD_OUTPUT_HANDLE, log_handle)
+ || !SetStdHandle (STD_ERROR_HANDLE, log_handle))
+ msg (M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file);
+#endif
+
+ /* direct stdout/stderr to point to log_handle */
+ log_fd = _open_osfhandle ((intptr_t)log_handle, _O_TEXT);
+ if (log_fd == -1)
+ msg (M_ERR, "Error: --log redirect failed due to _open_osfhandle failure");
+
+ /* open log_handle as FILE stream */
+ ASSERT (msgfp == NULL);
+ msgfp = _fdopen (log_fd, "wt");
+ if (msgfp == NULL)
+ msg (M_ERR, "Error: --log redirect failed due to _fdopen");
+
+ /* redirect C-library stdout/stderr to log file */
+ if (_dup2 (log_fd, 1) == -1 || _dup2 (log_fd, 2) == -1)
+ msg (M_WARN, "Error: --log redirect of stdout/stderr failed");
+
+ std_redir = true;
+ }
+#elif defined(HAVE_DUP2)
+ if (!std_redir)
+ {
+ int out = open (file,
+ O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC),
+ S_IRUSR | S_IWUSR);
+
+ if (out < 0)
+ {
+ msg (M_WARN|M_ERRNO, "Warning: Error redirecting stdout/stderr to --log file: %s", file);
+ return;
+ }
+
+ if (dup2 (out, 1) == -1)
+ msg (M_ERR, "--log file redirection error on stdout");
+ if (dup2 (out, 2) == -1)
+ msg (M_ERR, "--log file redirection error on stderr");
+
+ if (out > 2)
+ close (out);
+
+ std_redir = true;
+ }
+
+#else
+ msg (M_WARN, "WARNING: The --log option is not supported on this OS because it lacks the dup2 function");
+#endif
+}
+
+/*
+ * Functions used to check return status
+ * of I/O operations.
+ */
+
+unsigned int x_cs_info_level; /* GLOBAL */
+unsigned int x_cs_verbose_level; /* GLOBAL */
+unsigned int x_cs_err_delay_ms; /* GLOBAL */
+
+void
+reset_check_status ()
+{
+ x_cs_info_level = 0;
+ x_cs_verbose_level = 0;
+}
+
+void
+set_check_status (unsigned int info_level, unsigned int verbose_level)
+{
+ x_cs_info_level = info_level;
+ x_cs_verbose_level = verbose_level;
+}
+
+/*
+ * Called after most socket or tun/tap operations, via the inline
+ * function check_status().
+ *
+ * Decide if we should print an error message, and see if we can
+ * extract any useful info from the error, such as a Path MTU hint
+ * from the OS.
+ */
+void
+x_check_status (int status,
+ const char *description,
+ struct link_socket *sock,
+ struct tuntap *tt)
+{
+ const int my_errno = (sock ? openvpn_errno_socket () : (int)openvpn_errno ());
+ const char *extended_msg = NULL;
+
+ msg (x_cs_verbose_level, "%s %s returned %d",
+ sock ? proto2ascii (sock->info.proto, true) : "",
+ description,
+ status);
+
+ if (status < 0)
+ {
+ struct gc_arena gc = gc_new ();
+#if EXTENDED_SOCKET_ERROR_CAPABILITY
+ /* get extended socket error message and possible PMTU hint from OS */
+ if (sock)
+ {
+ int mtu;
+ extended_msg = format_extended_socket_error (sock->sd, &mtu, &gc);
+ if (mtu > 0 && sock->mtu != mtu)
+ {
+ sock->mtu = mtu;
+ sock->info.mtu_changed = true;
+ }
+ }
+#elif defined(WIN32)
+ /* get possible driver error from TAP-Win32 driver */
+ extended_msg = tap_win32_getinfo (tt, &gc);
+#endif
+ if (!ignore_sys_error (my_errno))
+ {
+ if (extended_msg)
+ msg (x_cs_info_level, "%s %s [%s]: %s (code=%d)",
+ description,
+ sock ? proto2ascii (sock->info.proto, true) : "",
+ extended_msg,
+ strerror_ts (my_errno, &gc),
+ my_errno);
+ else
+ msg (x_cs_info_level, "%s %s: %s (code=%d)",
+ description,
+ sock ? proto2ascii (sock->info.proto, true) : "",
+ strerror_ts (my_errno, &gc),
+ my_errno);
+
+ if (x_cs_err_delay_ms)
+ sleep_milliseconds (x_cs_err_delay_ms);
+ }
+ gc_free (&gc);
+ }
+}
+
+/*
+ * In multiclient mode, put a client-specific prefix
+ * before each message.
+ */
+const char *x_msg_prefix; /* GLOBAL */
+
+/*
+ * Allow MSG to be redirected through a virtual_output object
+ */
+
+const struct virtual_output *x_msg_virtual_output; /* GLOBAL */
+
+/*
+ * Exiting.
+ */
+
+void
+openvpn_exit (const int status)
+{
+ void tun_abort();
+#ifdef ENABLE_PLUGIN
+ void plugin_abort (void);
+#endif
+
+ tun_abort();
+
+#ifdef WIN32
+ uninit_win32 ();
+#endif
+
+ close_syslog ();
+
+#ifdef ENABLE_PLUGIN
+ plugin_abort ();
+#endif
+
+#if PORT_SHARE
+ if (port_share)
+ port_share_abort (port_share);
+#endif
+
+#ifdef ABORT_ON_ERROR
+ if (status == OPENVPN_EXIT_STATUS_ERROR)
+ abort ();
+#endif
+
+ if (status == OPENVPN_EXIT_STATUS_GOOD)
+ perf_output_results ();
+
+ exit (status);
+}
+
+/*
+ * Translate msg flags into a string
+ */
+const char *
+msg_flags_string (const unsigned int flags, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (16, gc);
+ if (flags == M_INFO)
+ buf_printf (&out, "I");
+ if (flags & M_FATAL)
+ buf_printf (&out, "F");
+ if (flags & M_NONFATAL)
+ buf_printf (&out, "N");
+ if (flags & M_WARN)
+ buf_printf (&out, "W");
+ if (flags & M_DEBUG)
+ buf_printf (&out, "D");
+ return BSTR (&out);
+}
+
+#ifdef ENABLE_DEBUG
+void
+crash (void)
+{
+ char *null = NULL;
+ *null = 0;
+}
+#endif
+
+#ifdef WIN32
+
+const char *
+strerror_win32 (DWORD errnum, struct gc_arena *gc)
+{
+ /*
+ * This code can be omitted, though often the Windows
+ * WSA error messages are less informative than the
+ * Posix equivalents.
+ */
+#if 1
+ switch (errnum) {
+ /*
+ * When the TAP-Win32 driver returns STATUS_UNSUCCESSFUL, this code
+ * gets returned to user space.
+ */
+ case ERROR_GEN_FAILURE:
+ return "General failure (ERROR_GEN_FAILURE)";
+ case ERROR_IO_PENDING:
+ return "I/O Operation in progress (ERROR_IO_PENDING)";
+ case WSA_IO_INCOMPLETE:
+ return "I/O Operation in progress (WSA_IO_INCOMPLETE)";
+ case WSAEINTR:
+ return "Interrupted system call (WSAEINTR)";
+ case WSAEBADF:
+ return "Bad file number (WSAEBADF)";
+ case WSAEACCES:
+ return "Permission denied (WSAEACCES)";
+ case WSAEFAULT:
+ return "Bad address (WSAEFAULT)";
+ case WSAEINVAL:
+ return "Invalid argument (WSAEINVAL)";
+ case WSAEMFILE:
+ return "Too many open files (WSAEMFILE)";
+ case WSAEWOULDBLOCK:
+ return "Operation would block (WSAEWOULDBLOCK)";
+ case WSAEINPROGRESS:
+ return "Operation now in progress (WSAEINPROGRESS)";
+ case WSAEALREADY:
+ return "Operation already in progress (WSAEALREADY)";
+ case WSAEDESTADDRREQ:
+ return "Destination address required (WSAEDESTADDRREQ)";
+ case WSAEMSGSIZE:
+ return "Message too long (WSAEMSGSIZE)";
+ case WSAEPROTOTYPE:
+ return "Protocol wrong type for socket (WSAEPROTOTYPE)";
+ case WSAENOPROTOOPT:
+ return "Bad protocol option (WSAENOPROTOOPT)";
+ case WSAEPROTONOSUPPORT:
+ return "Protocol not supported (WSAEPROTONOSUPPORT)";
+ case WSAESOCKTNOSUPPORT:
+ return "Socket type not supported (WSAESOCKTNOSUPPORT)";
+ case WSAEOPNOTSUPP:
+ return "Operation not supported on socket (WSAEOPNOTSUPP)";
+ case WSAEPFNOSUPPORT:
+ return "Protocol family not supported (WSAEPFNOSUPPORT)";
+ case WSAEAFNOSUPPORT:
+ return "Address family not supported by protocol family (WSAEAFNOSUPPORT)";
+ case WSAEADDRINUSE:
+ return "Address already in use (WSAEADDRINUSE)";
+ case WSAENETDOWN:
+ return "Network is down (WSAENETDOWN)";
+ case WSAENETUNREACH:
+ return "Network is unreachable (WSAENETUNREACH)";
+ case WSAENETRESET:
+ return "Net dropped connection or reset (WSAENETRESET)";
+ case WSAECONNABORTED:
+ return "Software caused connection abort (WSAECONNABORTED)";
+ case WSAECONNRESET:
+ return "Connection reset by peer (WSAECONNRESET)";
+ case WSAENOBUFS:
+ return "No buffer space available (WSAENOBUFS)";
+ case WSAEISCONN:
+ return "Socket is already connected (WSAEISCONN)";
+ case WSAENOTCONN:
+ return "Socket is not connected (WSAENOTCONN)";
+ case WSAETIMEDOUT:
+ return "Connection timed out (WSAETIMEDOUT)";
+ case WSAECONNREFUSED:
+ return "Connection refused (WSAECONNREFUSED)";
+ case WSAELOOP:
+ return "Too many levels of symbolic links (WSAELOOP)";
+ case WSAENAMETOOLONG:
+ return "File name too long (WSAENAMETOOLONG)";
+ case WSAEHOSTDOWN:
+ return "Host is down (WSAEHOSTDOWN)";
+ case WSAEHOSTUNREACH:
+ return "No Route to Host (WSAEHOSTUNREACH)";
+ case WSAENOTEMPTY:
+ return "Directory not empty (WSAENOTEMPTY)";
+ case WSAEPROCLIM:
+ return "Too many processes (WSAEPROCLIM)";
+ case WSAEUSERS:
+ return "Too many users (WSAEUSERS)";
+ case WSAEDQUOT:
+ return "Disc Quota Exceeded (WSAEDQUOT)";
+ case WSAESTALE:
+ return "Stale NFS file handle (WSAESTALE)";
+ case WSASYSNOTREADY:
+ return "Network SubSystem is unavailable (WSASYSNOTREADY)";
+ case WSAVERNOTSUPPORTED:
+ return "WINSOCK DLL Version out of range (WSAVERNOTSUPPORTED)";
+ case WSANOTINITIALISED:
+ return "Successful WSASTARTUP not yet performed (WSANOTINITIALISED)";
+ case WSAEREMOTE:
+ return "Too many levels of remote in path (WSAEREMOTE)";
+ case WSAHOST_NOT_FOUND:
+ return "Host not found (WSAHOST_NOT_FOUND)";
+ default:
+ break;
+ }
+#endif
+
+ /* format a windows error message */
+ {
+ char message[256];
+ struct buffer out = alloc_buf_gc (256, gc);
+ const int status = FormatMessage (
+ FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL,
+ errnum,
+ 0,
+ message,
+ sizeof (message),
+ NULL);
+ if (!status)
+ {
+ buf_printf (&out, "[Unknown Win32 Error]");
+ }
+ else
+ {
+ char *cp;
+ for (cp = message; *cp != '\0'; ++cp)
+ {
+ if (*cp == '\n' || *cp == '\r')
+ *cp = ' ';
+ }
+
+ buf_printf(&out, "%s", message);
+ }
+
+ return BSTR (&out);
+ }
+}
+
+#endif
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..4be3268
--- /dev/null
+++ b/error.h
@@ -0,0 +1,348 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef ERROR_H
+#define ERROR_H
+
+#include "basic.h"
+
+/* #define ABORT_ON_ERROR */
+
+#ifdef ENABLE_PKCS11
+#define ERR_BUF_SIZE 8192
+#else
+#define ERR_BUF_SIZE 1280
+#endif
+
+struct gc_arena;
+
+/*
+ * Where should messages be printed before syslog is opened?
+ * Not used if OPENVPN_DEBUG_COMMAND_LINE is defined.
+ */
+#define OPENVPN_MSG_FP stdout
+#define OPENVPN_ERROR_FP stderr
+
+/*
+ * Exit status codes
+ */
+
+#define OPENVPN_EXIT_STATUS_GOOD 0
+#define OPENVPN_EXIT_STATUS_ERROR 1
+#define OPENVPN_EXIT_STATUS_USAGE 1
+#define OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE 1
+
+/*
+ * Special command line debugging mode.
+ * If OPENVPN_DEBUG_COMMAND_LINE
+ * is defined, contents of argc/argv will
+ * be dumped to OPENVPN_DEBUG_FILE as well
+ * as all other OpenVPN messages.
+ */
+
+/* #define OPENVPN_DEBUG_COMMAND_LINE */
+#define OPENVPN_DEBUG_FILE PACKAGE ".log"
+
+/* String and Error functions */
+
+#ifdef WIN32
+# define openvpn_errno() GetLastError()
+# define openvpn_errno_socket() WSAGetLastError()
+# define openvpn_strerror(e, gc) strerror_win32(e, gc)
+ const char *strerror_win32 (DWORD errnum, struct gc_arena *gc);
+#else
+# define openvpn_errno() errno
+# define openvpn_errno_socket() errno
+# define openvpn_strerror(x, gc) strerror(x)
+#endif
+
+/*
+ * These globals should not be accessed directly,
+ * but rather through macros or inline functions defined below.
+ */
+extern unsigned int x_debug_level;
+extern int x_msg_line_num;
+
+/* msg() flags */
+
+#define M_DEBUG_LEVEL (0x0F) /* debug level mask */
+
+#define M_FATAL (1<<4) /* exit program */
+#define M_NONFATAL (1<<5) /* non-fatal error */
+#define M_WARN (1<<6) /* call syslog with LOG_WARNING */
+#define M_DEBUG (1<<7)
+
+#define M_ERRNO (1<<8) /* show errno description */
+#define M_ERRNO_SOCK (1<<9) /* show socket errno description */
+#define M_SSL (1<<10) /* show SSL error */
+#define M_NOMUTE (1<<11) /* don't do mute processing */
+#define M_NOPREFIX (1<<12) /* don't show date/time prefix */
+#define M_USAGE_SMALL (1<<13) /* fatal options error, call usage_small */
+#define M_MSG_VIRT_OUT (1<<14) /* output message through msg_status_output callback */
+#define M_OPTERR (1<<15) /* print "Options error:" prefix */
+#define M_NOLF (1<<16) /* don't print new line */
+#define M_NOIPREFIX (1<<17) /* don't print instance prefix */
+
+/* flag combinations which are frequently used */
+#define M_ERR (M_FATAL | M_ERRNO)
+#define M_SOCKERR (M_FATAL | M_ERRNO_SOCK)
+#define M_SSLERR (M_FATAL | M_SSL)
+#define M_USAGE (M_USAGE_SMALL | M_NOPREFIX | M_OPTERR)
+#define M_CLIENT (M_MSG_VIRT_OUT | M_NOMUTE | M_NOIPREFIX)
+
+/*
+ * Mute levels are designed to avoid large numbers of
+ * mostly similar messages clogging the log file.
+ *
+ * A mute level of 0 is always printed.
+ */
+#define MUTE_LEVEL_SHIFT 24
+#define MUTE_LEVEL_MASK 0xFF
+
+#define ENCODE_MUTE_LEVEL(mute_level) (((mute_level) & MUTE_LEVEL_MASK) << MUTE_LEVEL_SHIFT)
+#define DECODE_MUTE_LEVEL(flags) (((flags) >> MUTE_LEVEL_SHIFT) & MUTE_LEVEL_MASK)
+
+/*
+ * log_level: verbosity level n (--verb n) must be >= log_level to print.
+ * mute_level: don't print more than n (--mute n) consecutive messages at
+ * a given mute level, or if 0 disable muting and print everything.
+ *
+ * Mask map:
+ * Bits 0-3: log level
+ * Bits 4-23: M_x flags
+ * Bits 24-31: mute level
+ */
+#define LOGLEV(log_level, mute_level, other) ((log_level) | ENCODE_MUTE_LEVEL(mute_level) | other)
+
+/*
+ * If compiler supports variable arguments in macros, define
+ * msg() as a macro for optimization win.
+ */
+
+bool dont_mute (unsigned int flags); /* check muting filter */
+
+#define MSG_TEST(flags) (unlikely((((unsigned int)flags) & M_DEBUG_LEVEL) <= x_debug_level) && dont_mute (flags))
+
+#if defined(HAVE_CPP_VARARG_MACRO_ISO) && !defined(__LCLINT__)
+# define HAVE_VARARG_MACROS
+# define msg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); } while (false)
+# ifdef ENABLE_DEBUG
+# define dmsg(flags, ...) do { if (MSG_TEST(flags)) x_msg((flags), __VA_ARGS__); } while (false)
+# else
+# define dmsg(flags, ...)
+# endif
+#elif defined(HAVE_CPP_VARARG_MACRO_GCC) && !defined(__LCLINT__)
+# define HAVE_VARARG_MACROS
+# define msg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); } while (false)
+# ifdef ENABLE_DEBUG
+# define dmsg(flags, args...) do { if (MSG_TEST(flags)) x_msg((flags), args); } while (false)
+# else
+# define dmsg(flags, args...)
+# endif
+#else
+# if !PEDANTIC
+# ifdef _MSC_VER
+# pragma message("this compiler appears to lack vararg macros which will cause a significant degradation in efficiency")
+# else
+# warning this compiler appears to lack vararg macros which will cause a significant degradation in efficiency (you can ignore this warning if you are using LCLINT)
+# endif
+# endif
+# define msg x_msg
+# define dmsg x_msg
+#endif
+
+void x_msg (const unsigned int flags, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ; /* should be called via msg above */
+
+/*
+ * Function prototypes
+ */
+
+void error_reset (void);
+
+/* route errors to stderr that would normally go to stdout */
+void errors_to_stderr (void);
+
+void set_suppress_timestamps (bool suppressed);
+
+#define SDL_CONSTRAIN (1<<0)
+bool set_debug_level (const int level, const unsigned int flags);
+
+bool set_mute_cutoff (const int cutoff);
+
+int get_debug_level (void);
+int get_mute_cutoff (void);
+
+const char *msg_flags_string (const unsigned int flags, struct gc_arena *gc);
+
+/*
+ * File to print messages to before syslog is opened.
+ */
+FILE *msg_fp(const unsigned int flags);
+
+/* Fatal logic errors */
+#define ASSERT(x) do { if (!(x)) assert_failed(__FILE__, __LINE__); } while (false)
+
+void assert_failed (const char *filename, int line);
+
+#ifdef ENABLE_DEBUG
+void crash (void); /* force a segfault (debugging only) */
+#endif
+
+/* Inline functions */
+
+static inline bool
+check_debug_level (unsigned int level)
+{
+ return (level & M_DEBUG_LEVEL) <= x_debug_level;
+}
+
+/* Call if we forked */
+void msg_forked (void);
+
+/* syslog output */
+
+void open_syslog (const char *pgmname, bool stdio_to_null);
+void close_syslog ();
+
+/* log file output */
+void redirect_stdout_stderr (const char *file, bool append);
+
+#ifdef WIN32
+/* get original stderr handle, even if redirected by --log/--log-append */
+HANDLE get_orig_stderr (void);
+#endif
+
+/* exit program */
+void openvpn_exit (const int status);
+
+/*
+ * Check the return status of read/write routines.
+ */
+
+struct link_socket;
+struct tuntap;
+
+extern unsigned int x_cs_info_level;
+extern unsigned int x_cs_verbose_level;
+extern unsigned int x_cs_err_delay_ms;
+
+void reset_check_status (void);
+void set_check_status (unsigned int info_level, unsigned int verbose_level);
+
+void x_check_status (int status,
+ const char *description,
+ struct link_socket *sock,
+ struct tuntap *tt);
+
+static inline void
+check_status (int status, const char *description, struct link_socket *sock, struct tuntap *tt)
+{
+ if (status < 0 || check_debug_level (x_cs_verbose_level))
+ x_check_status (status, description, sock, tt);
+}
+
+static inline void
+set_check_status_error_delay (unsigned int milliseconds)
+{
+ x_cs_err_delay_ms = milliseconds;
+}
+
+/*
+ * In multiclient mode, put a client-specific prefix
+ * before each message.
+ *
+ * TODO: x_msg_prefix should be thread-local
+ */
+
+extern const char *x_msg_prefix;
+
+void msg_thread_init (void);
+void msg_thread_uninit (void);
+
+static inline void
+msg_set_prefix (const char *prefix)
+{
+ x_msg_prefix = prefix;
+}
+
+static inline const char *
+msg_get_prefix (void)
+{
+ return x_msg_prefix;
+}
+
+/*
+ * Allow MSG to be redirected through a virtual_output object
+ */
+
+struct virtual_output;
+
+extern const struct virtual_output *x_msg_virtual_output;
+
+static inline void
+msg_set_virtual_output (const struct virtual_output *vo)
+{
+ x_msg_virtual_output = vo;
+}
+
+static inline const struct virtual_output *
+msg_get_virtual_output (void)
+{
+ return x_msg_virtual_output;
+}
+
+/*
+ * Return true if this is a system error
+ * which can be safely ignored.
+ */
+static inline bool
+ignore_sys_error (const int err)
+{
+ /* I/O operation pending */
+#ifdef WIN32
+ if (err == WSAEWOULDBLOCK || err == WSAEINVAL)
+ return true;
+#else
+ if (err == EAGAIN)
+ return true;
+#endif
+
+#if 0 /* if enabled, suppress ENOBUFS errors */
+#ifdef ENOBUFS
+ /* No buffer space available */
+ if (err == ENOBUFS)
+ return true;
+#endif
+#endif
+
+ return false;
+}
+
+#include "errlevel.h"
+
+#endif
diff --git a/event.c b/event.c
new file mode 100644
index 0000000..6a9161b
--- /dev/null
+++ b/event.c
@@ -0,0 +1,1055 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "error.h"
+#include "integer.h"
+#include "event.h"
+
+#include "memdbg.h"
+
+/*
+ * Some OSes will prefer select() over poll()
+ * when both are available.
+ */
+#if defined(TARGET_DARWIN)
+#define SELECT_PREFERRED_OVER_POLL
+#endif
+
+/*
+ * All non-windows OSes are assumed to have select()
+ */
+#ifdef WIN32
+#define SELECT 0
+#else
+#define SELECT 1
+#endif
+
+/*
+ * This should be set to the highest file descriptor
+ * which can be used in one of the FD_ macros.
+ */
+#ifdef FD_SETSIZE
+#define SELECT_MAX_FDS FD_SETSIZE
+#else
+#define SELECT_MAX_FDS 256
+#endif
+
+static inline int
+tv_to_ms_timeout (const struct timeval *tv)
+{
+ if (tv->tv_sec == 0 && tv->tv_usec == 0)
+ return 0;
+ else
+ return max_int (tv->tv_sec * 1000 + (tv->tv_usec + 500) / 1000, 1);
+}
+
+#ifdef WIN32
+
+struct we_set
+{
+ struct event_set_functions func;
+ bool fast;
+ HANDLE *events;
+ struct event_set_return *esr;
+ int n_events;
+ int capacity;
+};
+
+static inline void
+we_set_event (struct we_set *wes, int i, event_t event, unsigned int rwflags, void *arg)
+{
+ ASSERT (i >= 0 && i < wes->capacity);
+
+ if (rwflags == EVENT_READ)
+ {
+ ASSERT (event->read != NULL);
+ wes->events[i] = event->read;
+ }
+ else if (rwflags == EVENT_WRITE)
+ {
+ ASSERT (event->write != NULL);
+ wes->events[i] = event->write;
+ }
+ else
+ msg (M_FATAL, "fatal error in we_set_events: rwflags=%d", rwflags);
+
+ wes->esr[i].rwflags = rwflags;
+ wes->esr[i].arg = arg;
+}
+
+static inline bool
+we_append_event (struct we_set *wes, event_t event, unsigned int rwflags, void *arg)
+{
+ if (rwflags & EVENT_WRITE)
+ {
+ if (wes->n_events < wes->capacity)
+ {
+ we_set_event (wes, wes->n_events, event, EVENT_WRITE, arg);
+ ++wes->n_events;
+ }
+ else
+ return false;
+ }
+ if (rwflags & EVENT_READ)
+ {
+ if (wes->n_events < wes->capacity)
+ {
+ we_set_event (wes, wes->n_events, event, EVENT_READ, arg);
+ ++wes->n_events;
+ }
+ else
+ return false;
+ }
+ return true;
+}
+
+static void
+we_del_event (struct we_set *wes, event_t event)
+{
+ int i, j = 0;
+ const int len = wes->n_events;
+
+ for (i = 0; i < len; ++i)
+ {
+ const HANDLE h = wes->events[i];
+ if (h == event->read || h == event->write)
+ --wes->n_events;
+ else
+ {
+ if (i != j)
+ {
+ wes->events[j] = wes->events[i];
+ wes->esr[j] = wes->esr[i];
+ }
+ ++j;
+ }
+ }
+}
+
+static void
+we_del_index (struct we_set *wes, int index)
+{
+ int i;
+ ASSERT (index >= 0 && index < wes->n_events);
+ for (i = index; i < wes->n_events - 1; ++i)
+ {
+ wes->events[i] = wes->events[i+1];
+ wes->esr[i] = wes->esr[i+1];
+ }
+ --wes->n_events;
+}
+
+static void
+we_get_rw_indices (struct we_set *wes, event_t event, int *ri, int *wi)
+{
+ int i;
+ *ri = *wi = -1;
+ for (i = 0; i < wes->n_events; ++i)
+ {
+ const HANDLE h = wes->events[i];
+ if (h == event->read)
+ {
+ ASSERT (*ri == -1);
+ *ri = i;
+ }
+ else if (h == event->write)
+ {
+ ASSERT (*wi == -1);
+ *wi = i;
+ }
+ }
+}
+
+static void
+we_free (struct event_set *es)
+{
+ struct we_set *wes = (struct we_set *) es;
+ free (wes->events);
+ free (wes->esr);
+ free (wes);
+}
+
+static void
+we_reset (struct event_set *es)
+{
+ struct we_set *wes = (struct we_set *) es;
+ ASSERT (wes->fast);
+ wes->n_events = 0;
+}
+
+static void
+we_del (struct event_set *es, event_t event)
+{
+ struct we_set *wes = (struct we_set *) es;
+ ASSERT (!wes->fast);
+ we_del_event (wes, event);
+}
+
+static void
+we_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
+{
+ struct we_set *wes = (struct we_set *) es;
+
+ dmsg (D_EVENT_WAIT, "WE_CTL n=%d ev=0x%08x rwflags=0x%04x arg=" ptr_format,
+ wes->n_events,
+ (unsigned int)event,
+ rwflags,
+ (ptr_type)arg);
+
+ if (wes->fast)
+ {
+ if (!we_append_event (wes, event, rwflags, arg))
+ goto err;
+ }
+ else
+ {
+ int ri, wi;
+ int one = -1;
+ int n = 0;
+
+ we_get_rw_indices (wes, event, &ri, &wi);
+ if (wi >= 0)
+ {
+ one = wi;
+ ++n;
+ }
+ if (ri >= 0)
+ {
+ one = ri;
+ ++n;
+ }
+ switch (rwflags)
+ {
+ case 0:
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ we_del_index (wes, one);
+ break;
+ case 2:
+ we_del_event (wes, event);
+ break;
+ default:
+ ASSERT (0);
+ }
+ break;
+ case EVENT_READ:
+ switch (n)
+ {
+ case 0:
+ if (!we_append_event (wes, event, EVENT_READ, arg))
+ goto err;
+ break;
+ case 1:
+ we_set_event (wes, one, event, EVENT_READ, arg);
+ break;
+ case 2:
+ we_del_index (wes, wi);
+ break;
+ default:
+ ASSERT (0);
+ }
+ break;
+ case EVENT_WRITE:
+ switch (n)
+ {
+ case 0:
+ if (!we_append_event (wes, event, EVENT_WRITE, arg))
+ goto err;
+ break;
+ case 1:
+ we_set_event (wes, one, event, EVENT_WRITE, arg);
+ break;
+ case 2:
+ we_del_index (wes, ri);
+ break;
+ default:
+ ASSERT (0);
+ }
+ break;
+ case EVENT_READ|EVENT_WRITE:
+ switch (n)
+ {
+ case 0:
+ if (!we_append_event (wes, event, EVENT_READ|EVENT_WRITE, arg))
+ goto err;
+ break;
+ case 1:
+ if (ri == -1)
+ {
+ ASSERT (wi != -1);
+ if (!we_append_event (wes, event, EVENT_READ, arg))
+ goto err;
+ }
+ else if (wi == -1)
+ {
+ if (!we_append_event (wes, event, EVENT_WRITE, arg))
+ goto err;
+ }
+ else
+ ASSERT (0);
+ break;
+ case 2:
+ break;
+ default:
+ ASSERT (0);
+ }
+ break;
+ default:
+ msg (M_FATAL, "fatal error in we_ctl: rwflags=%d", rwflags);
+ }
+ }
+ return;
+
+ err:
+ msg (D_EVENT_ERRORS, "Error: Windows resource limit WSA_MAXIMUM_WAIT_EVENTS (%d) has been exceeded", WSA_MAXIMUM_WAIT_EVENTS);
+}
+
+static int
+we_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ struct we_set *wes = (struct we_set *) es;
+ const int timeout = tv_to_ms_timeout (tv);
+ DWORD status;
+
+ dmsg (D_EVENT_WAIT, "WE_WAIT enter n=%d to=%d", wes->n_events, timeout);
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_EVENT_WAIT)) {
+ int i;
+ for (i = 0; i < wes->n_events; ++i)
+ dmsg (D_EVENT_WAIT, "[%d] ev=0x%08x rwflags=0x%04x arg=" ptr_format,
+ i,
+ (unsigned int)wes->events[i],
+ wes->esr[i].rwflags,
+ (ptr_type)wes->esr[i].arg);
+ }
+#endif
+
+ /*
+ * First poll our event list with 0 timeout
+ */
+ status = WSAWaitForMultipleEvents(
+ (DWORD) wes->n_events,
+ wes->events,
+ FALSE,
+ (DWORD) 0,
+ FALSE);
+
+ /*
+ * If at least one event is already set, we must
+ * individually poll the whole list.
+ */
+ if (status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events)
+ {
+ int i;
+ int j = 0;
+ for (i = 0; i < wes->n_events; ++i)
+ {
+ if (j >= outlen)
+ break;
+ if (WaitForSingleObject (wes->events[i], 0) == WAIT_OBJECT_0)
+ {
+ *out = wes->esr[i];
+ dmsg (D_EVENT_WAIT, "WE_WAIT leave [%d,%d] rwflags=0x%04x arg=" ptr_format,
+ i, j, out->rwflags, (ptr_type)out->arg);
+ ++j;
+ ++out;
+ }
+ }
+ return j;
+ }
+ else
+ {
+ /*
+ * If caller specified timeout > 0, we know at this point
+ * that no events are set, so wait only for the first event
+ * (or timeout) and return at most one event_set_return object.
+ *
+ * If caller specified timeout == 0, the second call to
+ * WSAWaitForMultipleEvents would be redundant -- just
+ * return 0 indicating timeout.
+ */
+ if (timeout > 0)
+ status = WSAWaitForMultipleEvents(
+ (DWORD) wes->n_events,
+ wes->events,
+ FALSE,
+ (DWORD) timeout,
+ FALSE);
+
+ if (outlen >= 1 && status >= WSA_WAIT_EVENT_0 && status < WSA_WAIT_EVENT_0 + (DWORD) wes->n_events)
+ {
+ *out = wes->esr[status - WSA_WAIT_EVENT_0];
+ dmsg (D_EVENT_WAIT, "WE_WAIT leave rwflags=0x%04x arg=" ptr_format,
+ out->rwflags, (ptr_type)out->arg);
+ return 1;
+ }
+ else if (status == WSA_WAIT_TIMEOUT)
+ return 0;
+ else
+ return -1;
+ }
+}
+
+static struct event_set *
+we_init (int *maxevents, unsigned int flags)
+{
+ struct we_set *wes;
+
+ dmsg (D_EVENT_WAIT, "WE_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
+
+ ALLOC_OBJ_CLEAR (wes, struct we_set);
+
+ /* set dispatch functions */
+ wes->func.free = we_free;
+ wes->func.reset = we_reset;
+ wes->func.del = we_del;
+ wes->func.ctl = we_ctl;
+ wes->func.wait = we_wait;
+
+ if (flags & EVENT_METHOD_FAST)
+ wes->fast = true;
+ wes->n_events = 0;
+
+ /* Figure our event capacity */
+ ASSERT (*maxevents > 0);
+ wes->capacity = min_int (*maxevents * 2, WSA_MAXIMUM_WAIT_EVENTS);
+ *maxevents = min_int (*maxevents, WSA_MAXIMUM_WAIT_EVENTS);
+
+ /* Allocate space for Win32 event handles */
+ ALLOC_ARRAY_CLEAR (wes->events, HANDLE, wes->capacity);
+
+ /* Allocate space for event_set_return objects */
+ ALLOC_ARRAY_CLEAR (wes->esr, struct event_set_return, wes->capacity);
+
+ dmsg (D_EVENT_WAIT, "WE_INIT maxevents=%d capacity=%d",
+ *maxevents, wes->capacity);
+
+ return (struct event_set *) wes;
+}
+
+#endif /* WIN32 */
+
+#if EPOLL
+
+struct ep_set
+{
+ struct event_set_functions func;
+ bool fast;
+ int epfd;
+ int maxevents;
+ struct epoll_event *events;
+};
+
+static void
+ep_free (struct event_set *es)
+{
+ struct ep_set *eps = (struct ep_set *) es;
+ close (eps->epfd);
+ free (eps->events);
+ free (eps);
+}
+
+static void
+ep_reset (struct event_set *es)
+{
+ const struct ep_set *eps = (struct ep_set *) es;
+ ASSERT (eps->fast);
+}
+
+static void
+ep_del (struct event_set *es, event_t event)
+{
+ struct epoll_event ev;
+ struct ep_set *eps = (struct ep_set *) es;
+
+ dmsg (D_EVENT_WAIT, "EP_DEL ev=%d", (int)event);
+
+ ASSERT (!eps->fast);
+ CLEAR (ev);
+ epoll_ctl (eps->epfd, EPOLL_CTL_DEL, event, &ev);
+}
+
+static void
+ep_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
+{
+ struct ep_set *eps = (struct ep_set *) es;
+ struct epoll_event ev;
+
+ CLEAR (ev);
+
+ ev.data.ptr = arg;
+ if (rwflags & EVENT_READ)
+ ev.events |= EPOLLIN;
+ if (rwflags & EVENT_WRITE)
+ ev.events |= EPOLLOUT;
+
+ dmsg (D_EVENT_WAIT, "EP_CTL fd=%d rwflags=0x%04x ev=0x%08x arg=" ptr_format,
+ (int)event,
+ rwflags,
+ (unsigned int)ev.events,
+ (ptr_type)ev.data.ptr);
+
+ if (epoll_ctl (eps->epfd, EPOLL_CTL_MOD, event, &ev) < 0)
+ {
+ if (errno == ENOENT)
+ {
+ if (epoll_ctl (eps->epfd, EPOLL_CTL_ADD, event, &ev) < 0)
+ msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_ADD failed");
+ }
+ else
+ msg (M_ERR, "EVENT: epoll_ctl EPOLL_CTL_MOD failed");
+ }
+}
+
+static int
+ep_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ struct ep_set *eps = (struct ep_set *) es;
+ int stat;
+
+ if (outlen > eps->maxevents)
+ outlen = eps->maxevents;
+
+ stat = epoll_wait (eps->epfd, eps->events, outlen, tv_to_ms_timeout (tv));
+ ASSERT (stat <= outlen);
+
+ if (stat > 0)
+ {
+ int i;
+ const struct epoll_event *ev = eps->events;
+ struct event_set_return *esr = out;
+ for (i = 0; i < stat; ++i)
+ {
+ esr->rwflags = 0;
+ if (ev->events & (EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP))
+ esr->rwflags |= EVENT_READ;
+ if (ev->events & EPOLLOUT)
+ esr->rwflags |= EVENT_WRITE;
+ esr->arg = ev->data.ptr;
+ dmsg (D_EVENT_WAIT, "EP_WAIT[%d] rwflags=0x%04x ev=0x%08x arg=" ptr_format,
+ i, esr->rwflags, ev->events, (ptr_type)ev->data.ptr);
+ ++ev;
+ ++esr;
+ }
+ }
+ return stat;
+}
+
+static struct event_set *
+ep_init (int *maxevents, unsigned int flags)
+{
+ struct ep_set *eps;
+ int fd;
+
+ dmsg (D_EVENT_WAIT, "EP_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
+
+ /* open epoll file descriptor */
+ fd = epoll_create (*maxevents);
+ if (fd < 0)
+ return NULL;
+
+ ALLOC_OBJ_CLEAR (eps, struct ep_set);
+
+ /* set dispatch functions */
+ eps->func.free = ep_free;
+ eps->func.reset = ep_reset;
+ eps->func.del = ep_del;
+ eps->func.ctl = ep_ctl;
+ eps->func.wait = ep_wait;
+
+ /* fast method ("sort of") corresponds to epoll one-shot */
+ if (flags & EVENT_METHOD_FAST)
+ eps->fast = true;
+
+ /* allocate space for epoll_wait return */
+ ASSERT (*maxevents > 0);
+ eps->maxevents = *maxevents;
+ ALLOC_ARRAY_CLEAR (eps->events, struct epoll_event, eps->maxevents);
+
+ /* set epoll control fd */
+ eps->epfd = fd;
+
+ return (struct event_set *) eps;
+}
+#endif /* EPOLL */
+
+#if POLL
+
+struct po_set
+{
+ struct event_set_functions func;
+ bool fast;
+ struct pollfd *events;
+ void **args;
+ int n_events;
+ int capacity;
+};
+
+static void
+po_free (struct event_set *es)
+{
+ struct po_set *pos = (struct po_set *) es;
+ free (pos->events);
+ free (pos->args);
+ free (pos);
+}
+
+static void
+po_reset (struct event_set *es)
+{
+ struct po_set *pos = (struct po_set *) es;
+ ASSERT (pos->fast);
+ pos->n_events = 0;
+}
+
+static void
+po_del (struct event_set *es, event_t event)
+{
+ struct po_set *pos = (struct po_set *) es;
+ int i;
+
+ dmsg (D_EVENT_WAIT, "PO_DEL ev=%d", (int)event);
+
+ ASSERT (!pos->fast);
+ for (i = 0; i < pos->n_events; ++i)
+ {
+ if (pos->events[i].fd == event)
+ {
+ int j;
+ for (j = i; j < pos->n_events - 1; ++j)
+ {
+ pos->events[j] = pos->events[j+1];
+ pos->args[j] = pos->args[j+1];
+ }
+ --pos->n_events;
+ break;
+ }
+ }
+}
+
+static inline void
+po_set_pollfd_events (struct pollfd *pfdp, unsigned int rwflags)
+{
+ pfdp->events = 0;
+ if (rwflags & EVENT_WRITE)
+ pfdp->events |= POLLOUT;
+ if (rwflags & EVENT_READ)
+ pfdp->events |= (POLLIN|POLLPRI);
+}
+
+static inline bool
+po_append_event (struct po_set *pos, event_t event, unsigned int rwflags, void *arg)
+{
+ if (pos->n_events < pos->capacity)
+ {
+ struct pollfd *pfdp = &pos->events[pos->n_events];
+ pfdp->fd = event;
+ pos->args[pos->n_events] = arg;
+ po_set_pollfd_events (pfdp, rwflags);
+ ++pos->n_events;
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+po_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
+{
+ struct po_set *pos = (struct po_set *) es;
+
+ dmsg (D_EVENT_WAIT, "PO_CTL rwflags=0x%04x ev=%d arg=" ptr_format,
+ rwflags, (int)event, (ptr_type)arg);
+
+ if (pos->fast)
+ {
+ if (!po_append_event (pos, event, rwflags, arg))
+ goto err;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i < pos->n_events; ++i)
+ {
+ struct pollfd *pfdp = &pos->events[i];
+ if (pfdp->fd == event)
+ {
+ pos->args[i] = arg;
+ po_set_pollfd_events (pfdp, rwflags);
+ goto done;
+ }
+ }
+ if (!po_append_event (pos, event, rwflags, arg))
+ goto err;
+ }
+
+ done:
+ return;
+
+ err:
+ msg (D_EVENT_ERRORS, "Error: poll: too many I/O wait events");
+}
+
+static int
+po_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ struct po_set *pos = (struct po_set *) es;
+ int stat;
+
+ stat = poll (pos->events, pos->n_events, tv_to_ms_timeout (tv));
+
+ ASSERT (stat <= pos->n_events);
+
+ if (stat > 0)
+ {
+ int i, j=0;
+ const struct pollfd *pfdp = pos->events;
+ for (i = 0; i < pos->n_events && j < outlen; ++i)
+ {
+ if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP|POLLOUT))
+ {
+ out->rwflags = 0;
+ if (pfdp->revents & (POLLIN|POLLPRI|POLLERR|POLLHUP))
+ out->rwflags |= EVENT_READ;
+ if (pfdp->revents & POLLOUT)
+ out->rwflags |= EVENT_WRITE;
+ out->arg = pos->args[i];
+ dmsg (D_EVENT_WAIT, "PO_WAIT[%d,%d] fd=%d rev=0x%08x rwflags=0x%04x arg=" ptr_format " %s",
+ i, j, pfdp->fd, pfdp->revents, out->rwflags, (ptr_type)out->arg, pos->fast ? "" : "[scalable]");
+ ++out;
+ ++j;
+ }
+ else if (pfdp->revents)
+ {
+ msg (D_EVENT_ERRORS, "Error: poll: unknown revents=0x%04x", (unsigned int)pfdp->revents);
+ }
+ ++pfdp;
+ }
+ return j;
+ }
+ return stat;
+}
+
+static struct event_set *
+po_init (int *maxevents, unsigned int flags)
+{
+ struct po_set *pos;
+
+ dmsg (D_EVENT_WAIT, "PO_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
+
+ ALLOC_OBJ_CLEAR (pos, struct po_set);
+
+ /* set dispatch functions */
+ pos->func.free = po_free;
+ pos->func.reset = po_reset;
+ pos->func.del = po_del;
+ pos->func.ctl = po_ctl;
+ pos->func.wait = po_wait;
+
+ if (flags & EVENT_METHOD_FAST)
+ pos->fast = true;
+
+ pos->n_events = 0;
+
+ /* Figure our event capacity */
+ ASSERT (*maxevents > 0);
+ pos->capacity = *maxevents;
+
+ /* Allocate space for pollfd structures to be passed to poll() */
+ ALLOC_ARRAY_CLEAR (pos->events, struct pollfd, pos->capacity);
+
+ /* Allocate space for event_set_return objects */
+ ALLOC_ARRAY_CLEAR (pos->args, void *, pos->capacity);
+
+ return (struct event_set *) pos;
+}
+#endif /* POLL */
+
+#if SELECT
+
+struct se_set
+{
+ struct event_set_functions func;
+ bool fast;
+ fd_set readfds;
+ fd_set writefds;
+ void **args; /* allocated to capacity size */
+ int maxfd; /* largest fd seen so far, always < capacity */
+ int capacity; /* fixed largest fd + 1 */
+};
+
+static void
+se_free (struct event_set *es)
+{
+ struct se_set *ses = (struct se_set *) es;
+ free (ses->args);
+ free (ses);
+}
+
+static void
+se_reset (struct event_set *es)
+{
+ struct se_set *ses = (struct se_set *) es;
+ int i;
+ ASSERT (ses->fast);
+
+ dmsg (D_EVENT_WAIT, "SE_RESET");
+
+ FD_ZERO (&ses->readfds);
+ FD_ZERO (&ses->writefds);
+ for (i = 0; i <= ses->maxfd; ++i)
+ ses->args[i] = NULL;
+ ses->maxfd = -1;
+}
+
+static void
+se_del (struct event_set *es, event_t event)
+{
+ struct se_set *ses = (struct se_set *) es;
+ ASSERT (!ses->fast);
+
+ dmsg (D_EVENT_WAIT, "SE_DEL ev=%d", (int)event);
+
+ if (event >= 0 && event < ses->capacity)
+ {
+ FD_CLR (event, &ses->readfds);
+ FD_CLR (event, &ses->writefds);
+ ses->args[event] = NULL;
+ }
+ else
+ msg (D_EVENT_ERRORS, "Error: select/se_del: too many I/O wait events");
+ return;
+}
+
+static void
+se_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
+{
+ struct se_set *ses = (struct se_set *) es;
+
+ dmsg (D_EVENT_WAIT, "SE_CTL rwflags=0x%04x ev=%d fast=%d cap=%d maxfd=%d arg=" ptr_format,
+ rwflags, (int)event, (int)ses->fast, ses->capacity, ses->maxfd, (ptr_type)arg);
+
+ if (event >= 0 && event < ses->capacity)
+ {
+ ses->maxfd = max_int (event, ses->maxfd);
+ ses->args[event] = arg;
+ if (ses->fast)
+ {
+ if (rwflags & EVENT_READ)
+ FD_SET (event, &ses->readfds);
+ if (rwflags & EVENT_WRITE)
+ FD_SET (event, &ses->writefds);
+ }
+ else
+ {
+ if (rwflags & EVENT_READ)
+ FD_SET (event, &ses->readfds);
+ else
+ FD_CLR (event, &ses->readfds);
+ if (rwflags & EVENT_WRITE)
+ FD_SET (event, &ses->writefds);
+ else
+ FD_CLR (event, &ses->writefds);
+ }
+ }
+ else
+ {
+ msg (D_EVENT_ERRORS, "Error: select: too many I/O wait events, fd=%d cap=%d",
+ (int) event,
+ ses->capacity);
+ }
+}
+
+static int
+se_wait_return (struct se_set *ses,
+ fd_set *read,
+ fd_set *write,
+ struct event_set_return *out,
+ int outlen)
+{
+ int i, j = 0;
+ for (i = 0; i <= ses->maxfd && j < outlen; ++i)
+ {
+ const bool r = FD_ISSET (i, read);
+ const bool w = FD_ISSET (i, write);
+ if (r || w)
+ {
+ out->rwflags = 0;
+ if (r)
+ out->rwflags |= EVENT_READ;
+ if (w)
+ out->rwflags |= EVENT_WRITE;
+ out->arg = ses->args[i];
+ dmsg (D_EVENT_WAIT, "SE_WAIT[%d,%d] rwflags=0x%04x arg=" ptr_format,
+ i, j, out->rwflags, (ptr_type)out->arg);
+ ++out;
+ ++j;
+ }
+ }
+ return j;
+}
+
+static int
+se_wait_fast (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ struct se_set *ses = (struct se_set *) es;
+ struct timeval tv_tmp = *tv;
+ int stat;
+
+ dmsg (D_EVENT_WAIT, "SE_WAIT_FAST maxfd=%d tv=%d/%d",
+ ses->maxfd,
+ (int)tv_tmp.tv_sec,
+ (int)tv_tmp.tv_usec);
+
+ stat = select (ses->maxfd + 1, &ses->readfds, &ses->writefds, NULL, &tv_tmp);
+
+ if (stat > 0)
+ stat = se_wait_return (ses, &ses->readfds, &ses->writefds, out, outlen);
+
+ return stat;
+}
+
+static int
+se_wait_scalable (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ struct se_set *ses = (struct se_set *) es;
+ struct timeval tv_tmp = *tv;
+ fd_set read = ses->readfds;
+ fd_set write = ses->writefds;
+ int stat;
+
+ dmsg (D_EVENT_WAIT, "SE_WAIT_SCALEABLE maxfd=%d tv=%d/%d",
+ ses->maxfd, (int)tv_tmp.tv_sec, (int)tv_tmp.tv_usec);
+
+ stat = select (ses->maxfd + 1, &read, &write, NULL, &tv_tmp);
+
+ if (stat > 0)
+ stat = se_wait_return (ses, &read, &write, out, outlen);
+
+ return stat;
+}
+
+static struct event_set *
+se_init (int *maxevents, unsigned int flags)
+{
+ struct se_set *ses;
+
+ dmsg (D_EVENT_WAIT, "SE_INIT maxevents=%d flags=0x%08x", *maxevents, flags);
+
+ ALLOC_OBJ_CLEAR (ses, struct se_set);
+
+ /* set dispatch functions */
+ ses->func.free = se_free;
+ ses->func.reset = se_reset;
+ ses->func.del = se_del;
+ ses->func.ctl = se_ctl;
+ ses->func.wait = se_wait_scalable;
+
+ if (flags & EVENT_METHOD_FAST)
+ {
+ ses->fast = true;
+ ses->func.wait = se_wait_fast;
+ }
+
+ /* Select needs to be passed this value + 1 */
+ ses->maxfd = -1;
+
+ /* Set our event capacity */
+ ASSERT (*maxevents > 0);
+ *maxevents = min_int (*maxevents, SELECT_MAX_FDS);
+ ses->capacity = SELECT_MAX_FDS;
+
+ /* Allocate space for event_set_return void * args */
+ ALLOC_ARRAY_CLEAR (ses->args, void *, ses->capacity);
+
+ return (struct event_set *) ses;
+}
+#endif /* SELECT */
+
+static struct event_set *
+event_set_init_simple (int *maxevents, unsigned int flags)
+{
+ struct event_set *ret = NULL;
+#ifdef WIN32
+ ret = we_init (maxevents, flags);
+#elif POLL && SELECT
+#if 0 /* Define to 1 if EVENT_METHOD_US_TIMEOUT should cause select to be favored over poll */
+ if (flags & EVENT_METHOD_US_TIMEOUT)
+ ret = se_init (maxevents, flags);
+#endif
+# ifdef SELECT_PREFERRED_OVER_POLL
+ if (!ret)
+ ret = se_init (maxevents, flags);
+ if (!ret)
+ ret = po_init (maxevents, flags);
+# else
+ if (!ret)
+ ret = po_init (maxevents, flags);
+ if (!ret)
+ ret = se_init (maxevents, flags);
+# endif
+#elif POLL
+ ret = po_init (maxevents, flags);
+#elif SELECT
+ ret = se_init (maxevents, flags);
+#else
+#error At least one of poll, select, or WSAWaitForMultipleEvents must be supported by the kernel
+#endif
+ ASSERT (ret);
+ return ret;
+}
+
+static struct event_set *
+event_set_init_scalable (int *maxevents, unsigned int flags)
+{
+ struct event_set *ret = NULL;
+#if EPOLL
+ ret = ep_init (maxevents, flags);
+ if (!ret)
+ {
+ msg (M_WARN, "Note: sys_epoll API is unavailable, falling back to poll/select API");
+ ret = event_set_init_simple (maxevents, flags);
+ }
+#else
+ ret = event_set_init_simple (maxevents, flags);
+#endif
+ ASSERT (ret);
+ return ret;
+}
+
+struct event_set *
+event_set_init (int *maxevents, unsigned int flags)
+{
+ if (flags & EVENT_METHOD_FAST)
+ return event_set_init_simple (maxevents, flags);
+ else
+ return event_set_init_scalable (maxevents, flags);
+}
diff --git a/event.h b/event.h
new file mode 100644
index 0000000..bd29fdc
--- /dev/null
+++ b/event.h
@@ -0,0 +1,158 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef EVENT_H
+#define EVENT_H
+
+#include "win32.h"
+#include "sig.h"
+#include "perf.h"
+
+/*
+ * rwflags passed to event_ctl and returned by
+ * struct event_set_return.
+ */
+#define EVENT_UNDEF 4
+#define EVENT_READ (1<<0)
+#define EVENT_WRITE (1<<1)
+/*
+ * Initialization flags passed to event_set_init
+ */
+#define EVENT_METHOD_US_TIMEOUT (1<<0)
+#define EVENT_METHOD_FAST (1<<1)
+
+#ifdef WIN32
+
+typedef const struct rw_handle *event_t;
+
+#define UNDEFINED_EVENT (NULL)
+
+#else
+
+typedef int event_t;
+
+#define UNDEFINED_EVENT (-1)
+
+#endif
+
+struct event_set;
+struct event_set_return;
+
+struct event_set_functions
+{
+ void (*free)(struct event_set *es);
+ void (*reset)(struct event_set *es);
+ void (*del)(struct event_set *es, event_t event);
+ void (*ctl)(struct event_set *es, event_t event, unsigned int rwflags, void *arg);
+
+ /*
+ * Return status for wait:
+ * -1 on signal or error
+ * 0 on timeout
+ * length of event_set_return if at least 1 event is returned
+ */
+ int (*wait)(struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen);
+};
+
+struct event_set_return
+{
+ unsigned int rwflags;
+ void *arg;
+};
+
+struct event_set
+{
+ struct event_set_functions func;
+};
+
+/*
+ * maxevents on input: desired max number of event_t descriptors
+ * simultaneously set with event_ctl
+ * maxevents on output: may be modified down, depending on limitations
+ * of underlying API
+ * flags: EVENT_METHOD_x flags
+ */
+struct event_set *event_set_init (int *maxevents, unsigned int flags);
+
+static inline void
+event_free (struct event_set *es)
+{
+ if (es)
+ (*es->func.free)(es);
+}
+
+static inline void
+event_reset (struct event_set *es)
+{
+ (*es->func.reset)(es);
+}
+
+static inline void
+event_del (struct event_set *es, event_t event)
+{
+ (*es->func.del)(es, event);
+}
+
+static inline void
+event_ctl (struct event_set *es, event_t event, unsigned int rwflags, void *arg)
+{
+ (*es->func.ctl)(es, event, rwflags, arg);
+}
+
+static inline int
+event_wait (struct event_set *es, const struct timeval *tv, struct event_set_return *out, int outlen)
+{
+ int ret;
+ perf_push (PERF_IO_WAIT);
+ ret = (*es->func.wait)(es, tv, out, outlen);
+ perf_pop ();
+ return ret;
+}
+
+static inline void
+event_set_return_init (struct event_set_return *esr)
+{
+ esr->rwflags = 0;
+ esr->arg = NULL;
+}
+
+#ifdef WIN32
+
+static inline void
+wait_signal (struct event_set *es, void *arg)
+{
+ if (HANDLE_DEFINED (win32_signal.in.read))
+ event_ctl (es, &win32_signal.in, EVENT_READ, arg);
+}
+
+#else
+
+static inline void
+wait_signal (struct event_set *es, void *arg)
+{
+}
+
+#endif
+
+#endif
diff --git a/fdmisc.c b/fdmisc.c
new file mode 100644
index 0000000..5be3c8b
--- /dev/null
+++ b/fdmisc.c
@@ -0,0 +1,72 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "fdmisc.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+/* Set a file descriptor to non-blocking */
+bool
+set_nonblock_action (int fd)
+{
+#ifdef WIN32
+ u_long arg = 1;
+ if (ioctlsocket (fd, FIONBIO, &arg))
+ return false;
+#else
+ if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
+ return false;
+#endif
+ return true;
+}
+
+/* Set a file descriptor to not be passed across execs */
+bool
+set_cloexec_action (int fd)
+{
+#ifndef WIN32
+ if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
+ return false;
+#endif
+ return true;
+}
+
+/* Set a file descriptor to non-blocking */
+void
+set_nonblock (int fd)
+{
+ if (!set_nonblock_action (fd))
+ msg (M_SOCKERR, "Set socket to non-blocking mode failed");
+}
+
+/* Set a file descriptor to not be passed across execs */
+void
+set_cloexec (int fd)
+{
+ if (!set_cloexec_action (fd))
+ msg (M_ERR, "Set FD_CLOEXEC flag on file descriptor failed");
+}
diff --git a/fdmisc.h b/fdmisc.h
new file mode 100644
index 0000000..4b6b6d0
--- /dev/null
+++ b/fdmisc.h
@@ -0,0 +1,31 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "basic.h"
+
+bool set_nonblock_action (int fd);
+bool set_cloexec_action (int fd);
+
+void set_nonblock (int fd);
+void set_cloexec (int fd);
diff --git a/forward-inline.h b/forward-inline.h
new file mode 100644
index 0000000..64ca941
--- /dev/null
+++ b/forward-inline.h
@@ -0,0 +1,294 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FORWARD_INLINE_H
+#define FORWARD_INLINE_H
+
+/*
+ * Inline functions
+ */
+
+/*
+ * Does TLS session need service?
+ */
+static inline void
+check_tls (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ void check_tls_dowork (struct context *c);
+ if (c->c2.tls_multi)
+ check_tls_dowork (c);
+#endif
+}
+
+/*
+ * TLS errors are fatal in TCP mode.
+ * Also check for --tls-exit trigger.
+ */
+static inline void
+check_tls_errors (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ void check_tls_errors_co (struct context *c);
+ void check_tls_errors_nco (struct context *c);
+ if (c->c2.tls_multi && c->c2.tls_exit_signal)
+ {
+ if (link_socket_connection_oriented (c->c2.link_socket))
+ {
+ if (c->c2.tls_multi->n_soft_errors)
+ check_tls_errors_co (c);
+ }
+ else
+ {
+ if (c->c2.tls_multi->n_hard_errors)
+ check_tls_errors_nco (c);
+ }
+ }
+#endif
+}
+
+/*
+ * Check for possible incoming configuration
+ * messages on the control channel.
+ */
+static inline void
+check_incoming_control_channel (struct context *c)
+{
+#if P2MP
+ void check_incoming_control_channel_dowork (struct context *c);
+ if (tls_test_payload_len (c->c2.tls_multi) > 0)
+ check_incoming_control_channel_dowork (c);
+#endif
+}
+
+/*
+ * Options like --up-delay need to be triggered by this function which
+ * checks for connection establishment.
+ */
+static inline void
+check_connection_established (struct context *c)
+{
+ void check_connection_established_dowork (struct context *c);
+ if (event_timeout_defined (&c->c2.wait_for_connect))
+ check_connection_established_dowork (c);
+}
+
+/*
+ * Should we add routes?
+ */
+static inline void
+check_add_routes (struct context *c)
+{
+ void check_add_routes_dowork (struct context *c);
+ if (event_timeout_trigger (&c->c2.route_wakeup, &c->c2.timeval, ETT_DEFAULT))
+ check_add_routes_dowork (c);
+}
+
+/*
+ * Should we exit due to inactivity timeout?
+ */
+static inline void
+check_inactivity_timeout (struct context *c)
+{
+ void check_inactivity_timeout_dowork (struct context *c);
+
+ if (c->options.inactivity_timeout
+ && event_timeout_trigger (&c->c2.inactivity_interval, &c->c2.timeval, ETT_DEFAULT))
+ check_inactivity_timeout_dowork (c);
+}
+
+#if P2MP
+
+static inline void
+check_server_poll_timeout (struct context *c)
+{
+ void check_server_poll_timeout_dowork (struct context *c);
+
+ if (c->options.server_poll_timeout
+ && event_timeout_trigger (&c->c2.server_poll_interval, &c->c2.timeval, ETT_DEFAULT))
+ check_server_poll_timeout_dowork (c);
+}
+
+/*
+ * Scheduled exit?
+ */
+static inline void
+check_scheduled_exit (struct context *c)
+{
+ void check_scheduled_exit_dowork (struct context *c);
+
+ if (event_timeout_defined (&c->c2.scheduled_exit))
+ {
+ if (event_timeout_trigger (&c->c2.scheduled_exit, &c->c2.timeval, ETT_DEFAULT))
+ check_scheduled_exit_dowork (c);
+ }
+}
+#endif
+
+/*
+ * Should we write timer-triggered status file.
+ */
+static inline void
+check_status_file (struct context *c)
+{
+ void check_status_file_dowork (struct context *c);
+
+ if (c->c1.status_output)
+ {
+ if (status_trigger_tv (c->c1.status_output, &c->c2.timeval))
+ check_status_file_dowork (c);
+ }
+}
+
+#ifdef ENABLE_FRAGMENT
+/*
+ * Should we deliver a datagram fragment to remote?
+ */
+static inline void
+check_fragment (struct context *c)
+{
+ void check_fragment_dowork (struct context *c);
+ if (c->c2.fragment)
+ check_fragment_dowork (c);
+}
+#endif
+
+#if P2MP
+
+/*
+ * see if we should send a push_request in response to --pull
+ */
+static inline void
+check_push_request (struct context *c)
+{
+ void check_push_request_dowork (struct context *c);
+ if (event_timeout_trigger (&c->c2.push_request_interval, &c->c2.timeval, ETT_DEFAULT))
+ check_push_request_dowork (c);
+}
+
+#endif
+
+#ifdef USE_CRYPTO
+/*
+ * Should we persist our anti-replay packet ID state to disk?
+ */
+static inline void
+check_packet_id_persist_flush (struct context *c)
+{
+ if (packet_id_persist_enabled (&c->c1.pid_persist)
+ && event_timeout_trigger (&c->c2.packet_id_persist_interval, &c->c2.timeval, ETT_DEFAULT))
+ packet_id_persist_save (&c->c1.pid_persist);
+}
+#endif
+
+/*
+ * Set our wakeup to 0 seconds, so we will be rescheduled
+ * immediately.
+ */
+static inline void
+context_immediate_reschedule (struct context *c)
+{
+ c->c2.timeval.tv_sec = 0; /* ZERO-TIMEOUT */
+ c->c2.timeval.tv_usec = 0;
+}
+
+static inline void
+context_reschedule_sec (struct context *c, int sec)
+{
+ if (sec < 0)
+ sec = 0;
+ if (sec < c->c2.timeval.tv_sec)
+ {
+ c->c2.timeval.tv_sec = sec;
+ c->c2.timeval.tv_usec = 0;
+ }
+}
+
+static inline struct link_socket_info *
+get_link_socket_info (struct context *c)
+{
+ if (c->c2.link_socket_info)
+ return c->c2.link_socket_info;
+ else
+ return &c->c2.link_socket->info;
+}
+
+static inline void
+register_activity (struct context *c, const int size)
+{
+ if (c->options.inactivity_timeout)
+ {
+ c->c2.inactivity_bytes += size;
+ if (c->c2.inactivity_bytes >= c->options.inactivity_minimum_bytes)
+ {
+ c->c2.inactivity_bytes = 0;
+ event_timeout_reset (&c->c2.inactivity_interval);
+ }
+ }
+}
+
+/*
+ * Return the io_wait() flags appropriate for
+ * a point-to-point tunnel.
+ */
+static inline unsigned int
+p2p_iow_flags (const struct context *c)
+{
+ unsigned int flags = (IOW_SHAPER|IOW_CHECK_RESIDUAL|IOW_FRAG|IOW_READ|IOW_WAIT_SIGNAL);
+ if (c->c2.to_link.len > 0)
+ flags |= IOW_TO_LINK;
+ if (c->c2.to_tun.len > 0)
+ flags |= IOW_TO_TUN;
+ return flags;
+}
+
+/*
+ * This is the core I/O wait function, used for all I/O waits except
+ * for TCP in server mode.
+ */
+static inline void
+io_wait (struct context *c, const unsigned int flags)
+{
+ void io_wait_dowork (struct context *c, const unsigned int flags);
+
+ if (c->c2.fast_io && (flags & (IOW_TO_TUN|IOW_TO_LINK|IOW_MBUF)))
+ {
+ /* fast path -- only for TUN/TAP/UDP writes */
+ unsigned int ret = 0;
+ if (flags & IOW_TO_TUN)
+ ret |= TUN_WRITE;
+ if (flags & (IOW_TO_LINK|IOW_MBUF))
+ ret |= SOCKET_WRITE;
+ c->c2.event_set_status = ret;
+ }
+ else
+ {
+ /* slow path */
+ io_wait_dowork (c, flags);
+ }
+}
+
+#define CONNECTION_ESTABLISHED(c) (get_link_socket_info(c)->connection_established)
+
+#endif /* EVENT_INLINE_H */
diff --git a/forward.c b/forward.c
new file mode 100644
index 0000000..87d05cc
--- /dev/null
+++ b/forward.c
@@ -0,0 +1,1531 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "forward.h"
+#include "init.h"
+#include "push.h"
+#include "gremlin.h"
+#include "mss.h"
+#include "event.h"
+#include "ps.h"
+#include "dhcp.h"
+#include "common.h"
+
+#include "memdbg.h"
+
+#include "forward-inline.h"
+#include "occ-inline.h"
+#include "ping-inline.h"
+
+counter_type link_read_bytes_global; /* GLOBAL */
+counter_type link_write_bytes_global; /* GLOBAL */
+
+/* show event wait debugging info */
+
+#ifdef ENABLE_DEBUG
+
+const char *
+wait_status_string (struct context *c, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ buf_printf (&out, "I/O WAIT %s|%s|%s|%s %s",
+ tun_stat (c->c1.tuntap, EVENT_READ, gc),
+ tun_stat (c->c1.tuntap, EVENT_WRITE, gc),
+ socket_stat (c->c2.link_socket, EVENT_READ, gc),
+ socket_stat (c->c2.link_socket, EVENT_WRITE, gc),
+ tv_string (&c->c2.timeval, gc));
+ return BSTR (&out);
+}
+
+void
+show_wait_status (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+ dmsg (D_EVENT_WAIT, "%s", wait_status_string (c, &gc));
+ gc_free (&gc);
+}
+
+#endif
+
+/*
+ * In TLS mode, let TLS level respond to any control-channel
+ * packets which were received, or prepare any packets for
+ * transmission.
+ *
+ * tmp_int is purely an optimization that allows us to call
+ * tls_multi_process less frequently when there's not much
+ * traffic on the control-channel.
+ *
+ */
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+void
+check_tls_dowork (struct context *c)
+{
+ interval_t wakeup = BIG_TIMEOUT;
+
+ if (interval_test (&c->c2.tmp_int))
+ {
+ const int tmp_status = tls_multi_process
+ (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr,
+ get_link_socket_info (c), &wakeup);
+ if (tmp_status == TLSMP_ACTIVE)
+ {
+ update_time ();
+ interval_action (&c->c2.tmp_int);
+ }
+ else if (tmp_status == TLSMP_KILL)
+ {
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "auth-control-exit";
+ }
+
+ interval_future_trigger (&c->c2.tmp_int, wakeup);
+ }
+
+ interval_schedule_wakeup (&c->c2.tmp_int, &wakeup);
+
+ if (wakeup)
+ context_reschedule_sec (c, wakeup);
+}
+#endif
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+void
+check_tls_errors_co (struct context *c)
+{
+ msg (D_STREAM_ERRORS, "Fatal TLS error (check_tls_errors_co), restarting");
+ c->sig->signal_received = c->c2.tls_exit_signal; /* SOFT-SIGUSR1 -- TLS error */
+ c->sig->signal_text = "tls-error";
+}
+
+void
+check_tls_errors_nco (struct context *c)
+{
+ c->sig->signal_received = c->c2.tls_exit_signal; /* SOFT-SIGUSR1 -- TLS error */
+ c->sig->signal_text = "tls-error";
+}
+
+#endif
+
+#if P2MP
+
+/*
+ * Handle incoming configuration
+ * messages on the control channel.
+ */
+void
+check_incoming_control_channel_dowork (struct context *c)
+{
+ const int len = tls_test_payload_len (c->c2.tls_multi);
+ if (len)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer buf = alloc_buf_gc (len, &gc);
+ if (tls_rec_payload (c->c2.tls_multi, &buf))
+ {
+ /* force null termination of message */
+ buf_null_terminate (&buf);
+
+ /* enforce character class restrictions */
+ string_mod (BSTR (&buf), CC_PRINT, CC_CRLF, 0);
+
+ if (buf_string_match_head_str (&buf, "AUTH_FAILED"))
+ receive_auth_failed (c, &buf);
+ else if (buf_string_match_head_str (&buf, "PUSH_"))
+ incoming_push_message (c, &buf);
+ else if (buf_string_match_head_str (&buf, "RESTART"))
+ server_pushed_restart (c, &buf);
+ else
+ msg (D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR (&buf));
+ }
+ else
+ {
+ msg (D_PUSH_ERRORS, "WARNING: Receive control message failed");
+ }
+
+ gc_free (&gc);
+ }
+}
+
+/*
+ * Periodically resend PUSH_REQUEST until PUSH message received
+ */
+void
+check_push_request_dowork (struct context *c)
+{
+ send_push_request (c);
+
+ /* if no response to first push_request, retry at 5 second intervals */
+ event_timeout_modify_wakeup (&c->c2.push_request_interval, 5);
+}
+
+#endif /* P2MP */
+
+/*
+ * Things that need to happen immediately after connection initiation should go here.
+ */
+void
+check_connection_established_dowork (struct context *c)
+{
+ if (event_timeout_trigger (&c->c2.wait_for_connect, &c->c2.timeval, ETT_DEFAULT))
+ {
+ if (CONNECTION_ESTABLISHED (c))
+ {
+#if P2MP
+ /* if --pull was specified, send a push request to server */
+ if (c->c2.tls_multi && c->options.pull)
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_set_state (management,
+ OPENVPN_STATE_GET_CONFIG,
+ NULL,
+ 0,
+ 0);
+ }
+#endif
+ /* send push request in 1 sec */
+ event_timeout_init (&c->c2.push_request_interval, 1, now);
+ reset_coarse_timers (c);
+ }
+ else
+#endif
+ {
+ do_up (c, false, 0);
+ }
+
+ event_timeout_clear (&c->c2.wait_for_connect);
+ }
+ }
+}
+
+/*
+ * Send a string to remote over the TLS control channel.
+ * Used for push/pull messages, passing username/password,
+ * etc.
+ */
+bool
+send_control_channel_string (struct context *c, const char *str, int msglevel)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+ if (c->c2.tls_multi) {
+ bool stat;
+
+ /* buffered cleartext write onto TLS control channel */
+ stat = tls_send_payload (c->c2.tls_multi, (uint8_t*) str, strlen (str) + 1);
+
+ /* reschedule tls_multi_process */
+ interval_action (&c->c2.tmp_int);
+ context_immediate_reschedule (c); /* ZERO-TIMEOUT */
+
+ msg (msglevel, "SENT CONTROL [%s]: '%s' (status=%d)",
+ tls_common_name (c->c2.tls_multi, false),
+ str,
+ (int) stat);
+
+ return stat;
+ }
+#endif
+ return true;
+}
+
+/*
+ * Add routes.
+ */
+
+static void
+check_add_routes_action (struct context *c, const bool errors)
+{
+ do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
+ update_time ();
+ event_timeout_clear (&c->c2.route_wakeup);
+ event_timeout_clear (&c->c2.route_wakeup_expire);
+ initialization_sequence_completed (c, errors ? ISC_ERRORS : 0); /* client/p2p --route-delay was defined */
+}
+
+void
+check_add_routes_dowork (struct context *c)
+{
+ if (test_routes (c->c1.route_list, c->c1.tuntap))
+ {
+ check_add_routes_action (c, false);
+ }
+ else if (event_timeout_trigger (&c->c2.route_wakeup_expire, &c->c2.timeval, ETT_DEFAULT))
+ {
+ check_add_routes_action (c, true);
+ }
+ else
+ {
+ msg (D_ROUTE, "Route: Waiting for TUN/TAP interface to come up...");
+ if (c->c1.tuntap)
+ {
+ if (!tun_standby (c->c1.tuntap))
+ {
+ c->sig->signal_received = SIGHUP;
+ c->sig->signal_text = "ip-fail";
+ c->persist.restart_sleep_seconds = 10;
+#ifdef WIN32
+ show_routes (M_INFO|M_NOPREFIX);
+ show_adapters (M_INFO|M_NOPREFIX);
+#endif
+ }
+ }
+ update_time ();
+ if (c->c2.route_wakeup.n != 1)
+ event_timeout_init (&c->c2.route_wakeup, 1, now);
+ event_timeout_reset (&c->c2.ping_rec_interval);
+ }
+}
+
+/*
+ * Should we exit due to inactivity timeout?
+ */
+void
+check_inactivity_timeout_dowork (struct context *c)
+{
+ msg (M_INFO, "Inactivity timeout (--inactive), exiting");
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "inactive";
+}
+
+#if P2MP
+
+void
+check_server_poll_timeout_dowork (struct context *c)
+{
+ event_timeout_reset (&c->c2.server_poll_interval);
+ if (!tls_initial_packet_received (c->c2.tls_multi))
+ {
+ msg (M_INFO, "Server poll timeout, restarting");
+ c->sig->signal_received = SIGUSR1;
+ c->sig->signal_text = "server_poll";
+ c->persist.restart_sleep_seconds = -1;
+ }
+}
+
+/*
+ * Schedule a signal n_seconds from now.
+ */
+void
+schedule_exit (struct context *c, const int n_seconds, const int signal)
+{
+ tls_set_single_session (c->c2.tls_multi);
+ update_time ();
+ reset_coarse_timers (c);
+ event_timeout_init (&c->c2.scheduled_exit, n_seconds, now);
+ c->c2.scheduled_exit_signal = signal;
+ msg (D_SCHED_EXIT, "Delayed exit in %d seconds", n_seconds);
+}
+
+/*
+ * Scheduled exit?
+ */
+void
+check_scheduled_exit_dowork (struct context *c)
+{
+ c->sig->signal_received = c->c2.scheduled_exit_signal;
+ c->sig->signal_text = "delayed-exit";
+}
+
+#endif
+
+/*
+ * Should we write timer-triggered status file.
+ */
+void
+check_status_file_dowork (struct context *c)
+{
+ if (c->c1.status_output)
+ print_status (c, c->c1.status_output);
+}
+
+#ifdef ENABLE_FRAGMENT
+/*
+ * Should we deliver a datagram fragment to remote?
+ */
+void
+check_fragment_dowork (struct context *c)
+{
+ struct link_socket_info *lsi = get_link_socket_info (c);
+
+ /* OS MTU Hint? */
+ if (lsi->mtu_changed && c->c2.ipv4_tun)
+ {
+ frame_adjust_path_mtu (&c->c2.frame_fragment, c->c2.link_socket->mtu,
+ c->options.ce.proto);
+ lsi->mtu_changed = false;
+ }
+
+ if (fragment_outgoing_defined (c->c2.fragment))
+ {
+ if (!c->c2.to_link.len)
+ {
+ /* encrypt a fragment for output to TCP/UDP port */
+ ASSERT (fragment_ready_to_send (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment));
+ encrypt_sign (c, false);
+ }
+ }
+
+ fragment_housekeeping (c->c2.fragment, &c->c2.frame_fragment, &c->c2.timeval);
+}
+#endif
+
+/*
+ * Buffer reallocation, for use with null encryption.
+ */
+static inline void
+buffer_turnover (const uint8_t *orig_buf, struct buffer *dest_stub, struct buffer *src_stub, struct buffer *storage)
+{
+ if (orig_buf == src_stub->data && src_stub->data != storage->data)
+ {
+ buf_assign (storage, src_stub);
+ *dest_stub = *storage;
+ }
+ else
+ {
+ *dest_stub = *src_stub;
+ }
+}
+
+/*
+ * Compress, fragment, encrypt and HMAC-sign an outgoing packet.
+ * Input: c->c2.buf
+ * Output: c->c2.to_link
+ */
+void
+encrypt_sign (struct context *c, bool comp_frag)
+{
+ struct context_buffers *b = c->c2.buffers;
+ const uint8_t *orig_buf = c->c2.buf.data;
+
+#if P2MP_SERVER
+ /*
+ * Drop non-TLS outgoing packet if client-connect script/plugin
+ * has not yet succeeded.
+ */
+ if (c->c2.context_auth != CAS_SUCCEEDED)
+ c->c2.buf.len = 0;
+#endif
+
+ if (comp_frag)
+ {
+#ifdef USE_LZO
+ /* Compress the packet. */
+ if (lzo_defined (&c->c2.lzo_compwork))
+ lzo_compress (&c->c2.buf, b->lzo_compress_buf, &c->c2.lzo_compwork, &c->c2.frame);
+#endif
+#ifdef ENABLE_FRAGMENT
+ if (c->c2.fragment)
+ fragment_outgoing (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment);
+#endif
+ }
+
+#ifdef USE_CRYPTO
+#ifdef USE_SSL
+ /*
+ * If TLS mode, get the key we will use to encrypt
+ * the packet.
+ */
+ if (c->c2.tls_multi)
+ {
+ tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &c->c2.crypto_options);
+ }
+#endif
+
+ /*
+ * Encrypt the packet and write an optional
+ * HMAC signature.
+ */
+ openvpn_encrypt (&c->c2.buf, b->encrypt_buf, &c->c2.crypto_options, &c->c2.frame);
+#endif
+ /*
+ * Get the address we will be sending the packet to.
+ */
+ link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c),
+ &c->c2.to_link_addr);
+#ifdef USE_CRYPTO
+#ifdef USE_SSL
+ /*
+ * In TLS mode, prepend the appropriate one-byte opcode
+ * to the packet which identifies it as a data channel
+ * packet and gives the low-permutation version of
+ * the key-id to the recipient so it knows which
+ * decrypt key to use.
+ */
+ if (c->c2.tls_multi)
+ {
+ tls_post_encrypt (c->c2.tls_multi, &c->c2.buf);
+ }
+#endif
+#endif
+
+ /* if null encryption, copy result to read_tun_buf */
+ buffer_turnover (orig_buf, &c->c2.to_link, &c->c2.buf, &b->read_tun_buf);
+}
+
+/*
+ * Coarse timers work to 1 second resolution.
+ */
+static void
+process_coarse_timers (struct context *c)
+{
+#ifdef USE_CRYPTO
+ /* flush current packet-id to file once per 60
+ seconds if --replay-persist was specified */
+ check_packet_id_persist_flush (c);
+#endif
+
+ /* should we update status file? */
+ check_status_file (c);
+
+ /* process connection establishment items */
+ check_connection_established (c);
+
+#if P2MP
+ /* see if we should send a push_request in response to --pull */
+ check_push_request (c);
+#endif
+
+#ifdef PLUGIN_PF
+ pf_check_reload (c);
+#endif
+
+ /* process --route options */
+ check_add_routes (c);
+
+ /* possibly exit due to --inactive */
+ check_inactivity_timeout (c);
+ if (c->sig->signal_received)
+ return;
+
+ /* restart if ping not received */
+ check_ping_restart (c);
+ if (c->sig->signal_received)
+ return;
+
+#if P2MP
+ check_server_poll_timeout (c);
+ if (c->sig->signal_received)
+ return;
+
+ check_scheduled_exit (c);
+ if (c->sig->signal_received)
+ return;
+#endif
+
+#ifdef ENABLE_OCC
+ /* Should we send an OCC_REQUEST message? */
+ check_send_occ_req (c);
+
+ /* Should we send an MTU load test? */
+ check_send_occ_load_test (c);
+
+ /* Should we send an OCC_EXIT message to remote? */
+ if (c->c2.explicit_exit_notification_time_wait)
+ process_explicit_exit_notification_timer_wakeup (c);
+#endif
+
+ /* Should we ping the remote? */
+ check_ping_send (c);
+}
+
+static void
+check_coarse_timers_dowork (struct context *c)
+{
+ const struct timeval save = c->c2.timeval;
+ c->c2.timeval.tv_sec = BIG_TIMEOUT;
+ c->c2.timeval.tv_usec = 0;
+ process_coarse_timers (c);
+ c->c2.coarse_timer_wakeup = now + c->c2.timeval.tv_sec;
+
+ dmsg (D_INTERVAL, "TIMER: coarse timer wakeup %d seconds", (int) c->c2.timeval.tv_sec);
+
+ /* Is the coarse timeout NOT the earliest one? */
+ if (c->c2.timeval.tv_sec > save.tv_sec)
+ c->c2.timeval = save;
+}
+
+static inline void
+check_coarse_timers (struct context *c)
+{
+ const time_t local_now = now;
+ if (local_now >= c->c2.coarse_timer_wakeup)
+ check_coarse_timers_dowork (c);
+ else
+ context_reschedule_sec (c, c->c2.coarse_timer_wakeup - local_now);
+}
+
+static void
+check_timeout_random_component_dowork (struct context *c)
+{
+ const int update_interval = 10; /* seconds */
+ c->c2.update_timeout_random_component = now + update_interval;
+ c->c2.timeout_random_component.tv_usec = (time_t) get_random () & 0x0003FFFF;
+ c->c2.timeout_random_component.tv_sec = 0;
+
+ dmsg (D_INTERVAL, "RANDOM USEC=%d", (int) c->c2.timeout_random_component.tv_usec);
+}
+
+static inline void
+check_timeout_random_component (struct context *c)
+{
+ if (now >= c->c2.update_timeout_random_component)
+ check_timeout_random_component_dowork (c);
+ if (c->c2.timeval.tv_sec >= 1)
+ tv_add (&c->c2.timeval, &c->c2.timeout_random_component);
+}
+
+#ifdef ENABLE_SOCKS
+
+/*
+ * Handle addition and removal of the 10-byte Socks5 header
+ * in UDP packets.
+ */
+
+static inline void
+socks_postprocess_incoming_link (struct context *c)
+{
+ if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4)
+ socks_process_incoming_udp (&c->c2.buf, &c->c2.from);
+}
+
+static inline void
+socks_preprocess_outgoing_link (struct context *c,
+ struct link_socket_actual **to_addr,
+ int *size_delta)
+{
+ if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4)
+ {
+ *size_delta += socks_process_outgoing_udp (&c->c2.to_link, c->c2.to_link_addr);
+ *to_addr = &c->c2.link_socket->socks_relay;
+ }
+}
+
+/* undo effect of socks_preprocess_outgoing_link */
+static inline void
+link_socket_write_post_size_adjust (int *size,
+ int size_delta,
+ struct buffer *buf)
+{
+ if (size_delta > 0 && *size > size_delta)
+ {
+ *size -= size_delta;
+ if (!buf_advance (buf, size_delta))
+ *size = 0;
+ }
+}
+#endif
+
+/*
+ * Output: c->c2.buf
+ */
+
+void
+read_incoming_link (struct context *c)
+{
+ /*
+ * Set up for recvfrom call to read datagram
+ * sent to our TCP/UDP port.
+ */
+ int status;
+
+ /*ASSERT (!c->c2.to_tun.len);*/
+
+ perf_push (PERF_READ_IN_LINK);
+
+ c->c2.buf = c->c2.buffers->read_link_buf;
+ ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM_ADJ (&c->c2.frame, FRAME_HEADROOM_MARKER_READ_LINK)));
+
+ status = link_socket_read (c->c2.link_socket,
+ &c->c2.buf,
+ MAX_RW_SIZE_LINK (&c->c2.frame),
+ &c->c2.from);
+
+ if (socket_connection_reset (c->c2.link_socket, status))
+ {
+#if PORT_SHARE
+ if (port_share && socket_foreign_protocol_detected (c->c2.link_socket))
+ {
+ const struct buffer *fbuf = socket_foreign_protocol_head (c->c2.link_socket);
+ const int sd = socket_foreign_protocol_sd (c->c2.link_socket);
+ port_share_redirect (port_share, fbuf, sd);
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "port-share-redirect";
+ }
+ else
+#endif
+ {
+ /* received a disconnect from a connection-oriented protocol */
+ if (c->options.inetd)
+ {
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "connection-reset-inetd";
+ msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status);
+ }
+ else
+ {
+#ifdef ENABLE_OCC
+ if (event_timeout_defined(&c->c2.explicit_exit_notification_interval))
+ {
+ msg (D_STREAM_ERRORS, "Connection reset during exit notification period, ignoring [%d]", status);
+ openvpn_sleep(1);
+ }
+ else
+#endif
+ {
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */
+ c->sig->signal_text = "connection-reset";
+ msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status);
+ }
+ }
+ }
+ perf_pop ();
+ return;
+ }
+
+ /* check recvfrom status */
+ check_status (status, "read", c->c2.link_socket, NULL);
+
+#ifdef ENABLE_SOCKS
+ /* Remove socks header if applicable */
+ socks_postprocess_incoming_link (c);
+#endif
+
+ perf_pop ();
+}
+
+/*
+ * Input: c->c2.buf
+ * Output: c->c2.to_tun
+ */
+
+void
+process_incoming_link (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+ bool decrypt_status;
+ struct link_socket_info *lsi = get_link_socket_info (c);
+ const uint8_t *orig_buf = c->c2.buf.data;
+
+ perf_push (PERF_PROC_IN_LINK);
+
+ if (c->c2.buf.len > 0)
+ {
+ c->c2.link_read_bytes += c->c2.buf.len;
+ link_read_bytes_global += c->c2.buf.len;
+ c->c2.original_recv_size = c->c2.buf.len;
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_bytes_in (management, c->c2.buf.len);
+#ifdef MANAGEMENT_DEF_AUTH
+ management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context);
+#endif
+ }
+#endif
+ }
+ else
+ c->c2.original_recv_size = 0;
+
+#ifdef ENABLE_DEBUG
+ /* take action to corrupt packet if we are in gremlin test mode */
+ if (c->options.gremlin) {
+ if (!ask_gremlin (c->options.gremlin))
+ c->c2.buf.len = 0;
+ corrupt_gremlin (&c->c2.buf, c->options.gremlin);
+ }
+#endif
+
+ /* log incoming packet */
+#ifdef LOG_RW
+ if (c->c2.log_rw && c->c2.buf.len > 0)
+ fprintf (stderr, "R");
+#endif
+ msg (D_LINK_RW, "%s READ [%d] from %s: %s",
+ proto2ascii (lsi->proto, true),
+ BLEN (&c->c2.buf),
+ print_link_socket_actual (&c->c2.from, &gc),
+ PROTO_DUMP (&c->c2.buf, &gc));
+
+ /*
+ * Good, non-zero length packet received.
+ * Commence multi-stage processing of packet,
+ * such as authenticate, decrypt, decompress.
+ * If any stage fails, it sets buf.len to 0 or -1,
+ * telling downstream stages to ignore the packet.
+ */
+ if (c->c2.buf.len > 0)
+ {
+ if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from))
+ link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from);
+
+#ifdef USE_CRYPTO
+#ifdef USE_SSL
+ if (c->c2.tls_multi)
+ {
+ /*
+ * If tls_pre_decrypt returns true, it means the incoming
+ * packet was a good TLS control channel packet. If so, TLS code
+ * will deal with the packet and set buf.len to 0 so downstream
+ * stages ignore it.
+ *
+ * If the packet is a data channel packet, tls_pre_decrypt
+ * will load crypto_options with the correct encryption key
+ * and return false.
+ */
+ if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options))
+ {
+ interval_action (&c->c2.tmp_int);
+
+ /* reset packet received timer if TLS packet */
+ if (c->options.ping_rec_timeout)
+ event_timeout_reset (&c->c2.ping_rec_interval);
+ }
+ }
+#if P2MP_SERVER
+ /*
+ * Drop non-TLS packet if client-connect script/plugin has not
+ * yet succeeded.
+ */
+ if (c->c2.context_auth != CAS_SUCCEEDED)
+ c->c2.buf.len = 0;
+#endif
+#endif /* USE_SSL */
+
+ /* authenticate and decrypt the incoming packet */
+ decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame);
+
+ if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket))
+ {
+ /* decryption errors are fatal in TCP mode */
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- decryption error in TCP mode */
+ c->sig->signal_text = "decryption-error";
+ msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting");
+ goto done;
+ }
+
+#endif /* USE_CRYPTO */
+
+#ifdef ENABLE_FRAGMENT
+ if (c->c2.fragment)
+ fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment);
+#endif
+
+#ifdef USE_LZO
+ /* decompress the incoming packet */
+ if (lzo_defined (&c->c2.lzo_compwork))
+ lzo_decompress (&c->c2.buf, c->c2.buffers->lzo_decompress_buf, &c->c2.lzo_compwork, &c->c2.frame);
+#endif
+
+#ifdef PACKET_TRUNCATION_CHECK
+ /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
+ ipv4_packet_size_verify (BPTR (&c->c2.buf),
+ BLEN (&c->c2.buf),
+ TUNNEL_TYPE (c->c1.tuntap),
+ "POST_DECRYPT",
+ &c->c2.n_trunc_post_decrypt);
+#endif
+
+ /*
+ * Set our "official" outgoing address, since
+ * if buf.len is non-zero, we know the packet
+ * authenticated. In TLS mode we do nothing
+ * because TLS mode takes care of source address
+ * authentication.
+ *
+ * Also, update the persisted version of our packet-id.
+ */
+ if (!TLS_MODE (c))
+ link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL, c->c2.es);
+
+ /* reset packet received timer */
+ if (c->options.ping_rec_timeout && c->c2.buf.len > 0)
+ event_timeout_reset (&c->c2.ping_rec_interval);
+
+ /* increment authenticated receive byte count */
+ if (c->c2.buf.len > 0)
+ {
+ c->c2.link_read_bytes_auth += c->c2.buf.len;
+ c->c2.max_recv_size_local = max_int (c->c2.original_recv_size, c->c2.max_recv_size_local);
+ }
+
+ /* Did we just receive an openvpn ping packet? */
+ if (is_ping_msg (&c->c2.buf))
+ {
+ dmsg (D_PING, "RECEIVED PING PACKET");
+ c->c2.buf.len = 0; /* drop packet */
+ }
+
+#ifdef ENABLE_OCC
+ /* Did we just receive an OCC packet? */
+ if (is_occ_msg (&c->c2.buf))
+ process_received_occ_msg (c);
+#endif
+
+ buffer_turnover (orig_buf, &c->c2.to_tun, &c->c2.buf, &c->c2.buffers->read_link_buf);
+
+ /* to_tun defined + unopened tuntap can cause deadlock */
+ if (!tuntap_defined (c->c1.tuntap))
+ c->c2.to_tun.len = 0;
+ }
+ else
+ {
+ buf_reset (&c->c2.to_tun);
+ }
+ done:
+ perf_pop ();
+ gc_free (&gc);
+}
+
+/*
+ * Output: c->c2.buf
+ */
+
+void
+read_incoming_tun (struct context *c)
+{
+ /*
+ * Setup for read() call on TUN/TAP device.
+ */
+ /*ASSERT (!c->c2.to_link.len);*/
+
+ perf_push (PERF_READ_IN_TUN);
+
+ c->c2.buf = c->c2.buffers->read_tun_buf;
+#ifdef TUN_PASS_BUFFER
+ read_tun_buffered (c->c1.tuntap, &c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame));
+#else
+ ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame)));
+ ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)));
+ c->c2.buf.len = read_tun (c->c1.tuntap, BPTR (&c->c2.buf), MAX_RW_SIZE_TUN (&c->c2.frame));
+#endif
+
+#ifdef PACKET_TRUNCATION_CHECK
+ ipv4_packet_size_verify (BPTR (&c->c2.buf),
+ BLEN (&c->c2.buf),
+ TUNNEL_TYPE (c->c1.tuntap),
+ "READ_TUN",
+ &c->c2.n_trunc_tun_read);
+#endif
+
+ /* Was TUN/TAP interface stopped? */
+ if (tuntap_stop (c->c2.buf.len))
+ {
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "tun-stop";
+ msg (M_INFO, "TUN/TAP interface has been stopped, exiting");
+ perf_pop ();
+ return;
+ }
+
+ /* Check the status return from read() */
+ check_status (c->c2.buf.len, "read from TUN/TAP", NULL, c->c1.tuntap);
+
+ perf_pop ();
+}
+
+/*
+ * Input: c->c2.buf
+ * Output: c->c2.to_link
+ */
+
+void
+process_incoming_tun (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+
+ perf_push (PERF_PROC_IN_TUN);
+
+ if (c->c2.buf.len > 0)
+ c->c2.tun_read_bytes += c->c2.buf.len;
+
+#ifdef LOG_RW
+ if (c->c2.log_rw && c->c2.buf.len > 0)
+ fprintf (stderr, "r");
+#endif
+
+ /* Show packet content */
+ dmsg (D_TUN_RW, "TUN READ [%d]", BLEN (&c->c2.buf));
+
+ if (c->c2.buf.len > 0)
+ {
+ /*
+ * The --passtos and --mssfix options require
+ * us to examine the IPv4 header.
+ */
+ process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX, &c->c2.buf);
+
+#ifdef PACKET_TRUNCATION_CHECK
+ /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
+ ipv4_packet_size_verify (BPTR (&c->c2.buf),
+ BLEN (&c->c2.buf),
+ TUNNEL_TYPE (c->c1.tuntap),
+ "PRE_ENCRYPT",
+ &c->c2.n_trunc_pre_encrypt);
+#endif
+
+ encrypt_sign (c, true);
+ }
+ else
+ {
+ buf_reset (&c->c2.to_link);
+ }
+ perf_pop ();
+ gc_free (&gc);
+}
+
+void
+process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
+{
+ if (!c->options.mssfix)
+ flags &= ~PIPV4_MSSFIX;
+#if PASSTOS_CAPABILITY
+ if (!c->options.passtos)
+ flags &= ~PIPV4_PASSTOS;
+#endif
+ if (!c->options.route_gateway_via_dhcp || !route_list_default_gateway_needed (c->c1.route_list))
+ flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
+
+ if (buf->len > 0)
+ {
+ /*
+ * The --passtos and --mssfix options require
+ * us to examine the IPv4 header.
+ */
+#if PASSTOS_CAPABILITY
+ if (flags & (PIPV4_PASSTOS|PIPV4_MSSFIX))
+#else
+ if (flags & PIPV4_MSSFIX)
+#endif
+ {
+ struct buffer ipbuf = *buf;
+ if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf))
+ {
+#if PASSTOS_CAPABILITY
+ /* extract TOS from IP header */
+ if (flags & PIPV4_PASSTOS)
+ link_socket_extract_tos (c->c2.link_socket, &ipbuf);
+#endif
+
+ /* possibly alter the TCP MSS */
+ if (flags & PIPV4_MSSFIX)
+ mss_fixup (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame)));
+
+ /* possibly extract a DHCP router message */
+ if (flags & PIPV4_EXTRACT_DHCP_ROUTER)
+ {
+ const in_addr_t dhcp_router = dhcp_extract_router_msg (&ipbuf);
+ if (dhcp_router)
+ route_list_add_default_gateway (c->c1.route_list, c->c2.es, dhcp_router);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Input: c->c2.to_link
+ */
+
+void
+process_outgoing_link (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+
+ perf_push (PERF_PROC_OUT_LINK);
+
+ if (c->c2.to_link.len > 0 && c->c2.to_link.len <= EXPANDED_SIZE (&c->c2.frame))
+ {
+ /*
+ * Setup for call to send/sendto which will send
+ * packet to remote over the TCP/UDP port.
+ */
+ int size = 0;
+ ASSERT (link_socket_actual_defined (c->c2.to_link_addr));
+
+#ifdef ENABLE_DEBUG
+ /* In gremlin-test mode, we may choose to drop this packet */
+ if (!c->options.gremlin || ask_gremlin (c->options.gremlin))
+#endif
+ {
+ /*
+ * Let the traffic shaper know how many bytes
+ * we wrote.
+ */
+#ifdef HAVE_GETTIMEOFDAY
+ if (c->options.shaper)
+ shaper_wrote_bytes (&c->c2.shaper, BLEN (&c->c2.to_link)
+ + datagram_overhead (c->options.ce.proto));
+#endif
+ /*
+ * Let the pinger know that we sent a packet.
+ */
+ if (c->options.ping_send_timeout)
+ event_timeout_reset (&c->c2.ping_send_interval);
+
+#if PASSTOS_CAPABILITY
+ /* Set TOS */
+ link_socket_set_tos (c->c2.link_socket);
+#endif
+
+ /* Log packet send */
+#ifdef LOG_RW
+ if (c->c2.log_rw)
+ fprintf (stderr, "W");
+#endif
+ msg (D_LINK_RW, "%s WRITE [%d] to %s: %s",
+ proto2ascii (c->c2.link_socket->info.proto, true),
+ BLEN (&c->c2.to_link),
+ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ PROTO_DUMP (&c->c2.to_link, &gc));
+
+ /* Packet send complexified by possible Socks5 usage */
+ {
+ struct link_socket_actual *to_addr = c->c2.to_link_addr;
+#ifdef ENABLE_SOCKS
+ int size_delta = 0;
+#endif
+
+#ifdef ENABLE_SOCKS
+ /* If Socks5 over UDP, prepend header */
+ socks_preprocess_outgoing_link (c, &to_addr, &size_delta);
+#endif
+ /* Send packet */
+ size = link_socket_write (c->c2.link_socket,
+ &c->c2.to_link,
+ to_addr);
+
+#ifdef ENABLE_SOCKS
+ /* Undo effect of prepend */
+ link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link);
+#endif
+ }
+
+ if (size > 0)
+ {
+ c->c2.max_send_size_local = max_int (size, c->c2.max_send_size_local);
+ c->c2.link_write_bytes += size;
+ link_write_bytes_global += size;
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_bytes_out (management, size);
+#ifdef MANAGEMENT_DEF_AUTH
+ management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context);
+#endif
+ }
+#endif
+ }
+ }
+
+ /* Check return status */
+ check_status (size, "write", c->c2.link_socket, NULL);
+
+ if (size > 0)
+ {
+ /* Did we write a different size packet than we intended? */
+ if (size != BLEN (&c->c2.to_link))
+ msg (D_LINK_ERRORS,
+ "TCP/UDP packet was truncated/expanded on write to %s (tried=%d,actual=%d)",
+ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ BLEN (&c->c2.to_link),
+ size);
+ }
+
+ /* if not a ping/control message, indicate activity regarding --inactive parameter */
+ if (c->c2.buf.len > 0 )
+ register_activity (c, size);
+ }
+ else
+ {
+ if (c->c2.to_link.len > 0)
+ msg (D_LINK_ERRORS, "TCP/UDP packet too large on write to %s (tried=%d,max=%d)",
+ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ c->c2.to_link.len,
+ EXPANDED_SIZE (&c->c2.frame));
+ }
+
+ buf_reset (&c->c2.to_link);
+
+ perf_pop ();
+ gc_free (&gc);
+}
+
+/*
+ * Input: c->c2.to_tun
+ */
+
+void
+process_outgoing_tun (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+
+ /*
+ * Set up for write() call to TUN/TAP
+ * device.
+ */
+ if (c->c2.to_tun.len <= 0)
+ return;
+
+ perf_push (PERF_PROC_OUT_TUN);
+
+ /*
+ * The --mssfix option requires
+ * us to examine the IPv4 header.
+ */
+ process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_OUTGOING, &c->c2.to_tun);
+
+ if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame))
+ {
+ /*
+ * Write to TUN/TAP device.
+ */
+ int size;
+
+#ifdef LOG_RW
+ if (c->c2.log_rw)
+ fprintf (stderr, "w");
+#endif
+ dmsg (D_TUN_RW, "TUN WRITE [%d]", BLEN (&c->c2.to_tun));
+
+#ifdef PACKET_TRUNCATION_CHECK
+ ipv4_packet_size_verify (BPTR (&c->c2.to_tun),
+ BLEN (&c->c2.to_tun),
+ TUNNEL_TYPE (c->c1.tuntap),
+ "WRITE_TUN",
+ &c->c2.n_trunc_tun_write);
+#endif
+
+#ifdef TUN_PASS_BUFFER
+ size = write_tun_buffered (c->c1.tuntap, &c->c2.to_tun);
+#else
+ size = write_tun (c->c1.tuntap, BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun));
+#endif
+
+ if (size > 0)
+ c->c2.tun_write_bytes += size;
+ check_status (size, "write to TUN/TAP", NULL, c->c1.tuntap);
+
+ /* check written packet size */
+ if (size > 0)
+ {
+ /* Did we write a different size packet than we intended? */
+ if (size != BLEN (&c->c2.to_tun))
+ msg (D_LINK_ERRORS,
+ "TUN/TAP packet was destructively fragmented on write to %s (tried=%d,actual=%d)",
+ c->c1.tuntap->actual_name,
+ BLEN (&c->c2.to_tun),
+ size);
+
+ /* indicate activity regarding --inactive parameter */
+ register_activity (c, size);
+ }
+ }
+ else
+ {
+ /*
+ * This should never happen, probably indicates some kind
+ * of MTU mismatch.
+ */
+ msg (D_LINK_ERRORS, "tun packet too large on write (tried=%d,max=%d)",
+ c->c2.to_tun.len,
+ MAX_RW_SIZE_TUN (&c->c2.frame));
+ }
+
+ buf_reset (&c->c2.to_tun);
+
+ perf_pop ();
+ gc_free (&gc);
+}
+
+void
+pre_select (struct context *c)
+{
+ /* make sure current time (now) is updated on function entry */
+
+ /*
+ * Start with an effectively infinite timeout, then let it
+ * reduce to a timeout that reflects the component which
+ * needs the earliest service.
+ */
+ c->c2.timeval.tv_sec = BIG_TIMEOUT;
+ c->c2.timeval.tv_usec = 0;
+
+#if defined(WIN32)
+ if (check_debug_level (D_TAP_WIN32_DEBUG))
+ {
+ c->c2.timeval.tv_sec = 1;
+ if (tuntap_defined (c->c1.tuntap))
+ tun_show_debug (c->c1.tuntap);
+ }
+#endif
+
+ /* check coarse timers? */
+ check_coarse_timers (c);
+ if (c->sig->signal_received)
+ return;
+
+ /* Does TLS need service? */
+ check_tls (c);
+
+ /* In certain cases, TLS errors will require a restart */
+ check_tls_errors (c);
+ if (c->sig->signal_received)
+ return;
+
+ /* check for incoming configuration info on the control channel */
+ check_incoming_control_channel (c);
+
+#ifdef ENABLE_OCC
+ /* Should we send an OCC message? */
+ check_send_occ_msg (c);
+#endif
+
+#ifdef ENABLE_FRAGMENT
+ /* Should we deliver a datagram fragment to remote? */
+ check_fragment (c);
+#endif
+
+ /* Update random component of timeout */
+ check_timeout_random_component (c);
+}
+
+/*
+ * Wait for I/O events. Used for both TCP & UDP sockets
+ * in point-to-point mode and for UDP sockets in
+ * point-to-multipoint mode.
+ */
+
+void
+io_wait_dowork (struct context *c, const unsigned int flags)
+{
+ unsigned int socket = 0;
+ unsigned int tuntap = 0;
+ struct event_set_return esr[4];
+
+ /* These shifts all depend on EVENT_READ and EVENT_WRITE */
+ static int socket_shift = 0; /* depends on SOCKET_READ and SOCKET_WRITE */
+ static int tun_shift = 2; /* depends on TUN_READ and TUN_WRITE */
+ static int err_shift = 4; /* depends on ES_ERROR */
+#ifdef ENABLE_MANAGEMENT
+ static int management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */
+#endif
+
+ /*
+ * Decide what kind of events we want to wait for.
+ */
+ event_reset (c->c2.event_set);
+
+ /*
+ * On win32 we use the keyboard or an event object as a source
+ * of asynchronous signals.
+ */
+ if (flags & IOW_WAIT_SIGNAL)
+ wait_signal (c->c2.event_set, (void*)&err_shift);
+
+ /*
+ * If outgoing data (for TCP/UDP port) pending, wait for ready-to-send
+ * status from TCP/UDP port. Otherwise, wait for incoming data on
+ * TUN/TAP device.
+ */
+ if (flags & IOW_TO_LINK)
+ {
+ if (flags & IOW_SHAPER)
+ {
+ /*
+ * If sending this packet would put us over our traffic shaping
+ * quota, don't send -- instead compute the delay we must wait
+ * until it will be OK to send the packet.
+ */
+#ifdef HAVE_GETTIMEOFDAY
+ int delay = 0;
+
+ /* set traffic shaping delay in microseconds */
+ if (c->options.shaper)
+ delay = max_int (delay, shaper_delay (&c->c2.shaper));
+
+ if (delay < 1000)
+ {
+ socket |= EVENT_WRITE;
+ }
+ else
+ {
+ shaper_soonest_event (&c->c2.timeval, delay);
+ }
+#else /* HAVE_GETTIMEOFDAY */
+ socket |= EVENT_WRITE;
+#endif /* HAVE_GETTIMEOFDAY */
+ }
+ else
+ {
+ socket |= EVENT_WRITE;
+ }
+ }
+ else if (!((flags & IOW_FRAG) && TO_LINK_FRAG (c)))
+ {
+ if (flags & IOW_READ_TUN)
+ tuntap |= EVENT_READ;
+ }
+
+ /*
+ * If outgoing data (for TUN/TAP device) pending, wait for ready-to-send status
+ * from device. Otherwise, wait for incoming data on TCP/UDP port.
+ */
+ if (flags & IOW_TO_TUN)
+ {
+ tuntap |= EVENT_WRITE;
+ }
+ else
+ {
+ if (flags & IOW_READ_LINK)
+ socket |= EVENT_READ;
+ }
+
+ /*
+ * outgoing bcast buffer waiting to be sent?
+ */
+ if (flags & IOW_MBUF)
+ socket |= EVENT_WRITE;
+
+ /*
+ * Force wait on TUN input, even if also waiting on TCP/UDP output
+ */
+ if (flags & IOW_READ_TUN_FORCE)
+ tuntap |= EVENT_READ;
+
+ /*
+ * Configure event wait based on socket, tuntap flags.
+ */
+ socket_set (c->c2.link_socket, c->c2.event_set, socket, (void*)&socket_shift, NULL);
+ tun_set (c->c1.tuntap, c->c2.event_set, tuntap, (void*)&tun_shift, NULL);
+
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_socket_set (management, c->c2.event_set, (void*)&management_shift, NULL);
+#endif
+
+ /*
+ * Possible scenarios:
+ * (1) tcp/udp port has data available to read
+ * (2) tcp/udp port is ready to accept more data to write
+ * (3) tun dev has data available to read
+ * (4) tun dev is ready to accept more data to write
+ * (5) we received a signal (handler sets signal_received)
+ * (6) timeout (tv) expired
+ */
+
+ c->c2.event_set_status = ES_ERROR;
+
+ if (!c->sig->signal_received)
+ {
+ if (!(flags & IOW_CHECK_RESIDUAL) || !socket_read_residual (c->c2.link_socket))
+ {
+ int status;
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_EVENT_WAIT))
+ show_wait_status (c);
+#endif
+
+ /*
+ * Wait for something to happen.
+ */
+ status = event_wait (c->c2.event_set, &c->c2.timeval, esr, SIZE(esr));
+
+ check_status (status, "event_wait", NULL, NULL);
+
+ if (status > 0)
+ {
+ int i;
+ c->c2.event_set_status = 0;
+ for (i = 0; i < status; ++i)
+ {
+ const struct event_set_return *e = &esr[i];
+ c->c2.event_set_status |= ((e->rwflags & 3) << *((int*)e->arg));
+ }
+ }
+ else if (status == 0)
+ {
+ c->c2.event_set_status = ES_TIMEOUT;
+ }
+ }
+ else
+ {
+ c->c2.event_set_status = SOCKET_READ;
+ }
+ }
+
+ /* 'now' should always be a reasonably up-to-date timestamp */
+ update_time ();
+
+ /* set signal_received if a signal was received */
+ if (c->c2.event_set_status & ES_ERROR)
+ get_signal (&c->sig->signal_received);
+
+ dmsg (D_EVENT_WAIT, "I/O WAIT status=0x%04x", c->c2.event_set_status);
+}
+
+void
+process_io (struct context *c)
+{
+ const unsigned int status = c->c2.event_set_status;
+
+#ifdef ENABLE_MANAGEMENT
+ if (status & (MANAGEMENT_READ|MANAGEMENT_WRITE))
+ {
+ ASSERT (management);
+ management_io (management);
+ }
+#endif
+
+ /* TCP/UDP port ready to accept write */
+ if (status & SOCKET_WRITE)
+ {
+ process_outgoing_link (c);
+ }
+ /* TUN device ready to accept write */
+ else if (status & TUN_WRITE)
+ {
+ process_outgoing_tun (c);
+ }
+ /* Incoming data on TCP/UDP port */
+ else if (status & SOCKET_READ)
+ {
+ read_incoming_link (c);
+ if (!IS_SIG (c))
+ process_incoming_link (c);
+ }
+ /* Incoming data on TUN device */
+ else if (status & TUN_READ)
+ {
+ read_incoming_tun (c);
+ if (!IS_SIG (c))
+ process_incoming_tun (c);
+ }
+}
diff --git a/forward.h b/forward.h
new file mode 100644
index 0000000..17cc928
--- /dev/null
+++ b/forward.h
@@ -0,0 +1,86 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FORWARD_H
+#define FORWARD_H
+
+#include "openvpn.h"
+#include "occ.h"
+#include "ping.h"
+
+#define TUN_OUT(c) (BLEN(&(c)->c2.to_tun) > 0)
+#define LINK_OUT(c) (BLEN(&(c)->c2.to_link) > 0)
+#define ANY_OUT(c) (TUN_OUT(c) || LINK_OUT(c))
+
+#ifdef ENABLE_FRAGMENT
+#define TO_LINK_FRAG(c) ((c)->c2.fragment && fragment_outgoing_defined ((c)->c2.fragment))
+#else
+#define TO_LINK_FRAG(c) (false)
+#endif
+
+#define TO_LINK_DEF(c) (LINK_OUT(c) || TO_LINK_FRAG(c))
+
+#define IOW_TO_TUN (1<<0)
+#define IOW_TO_LINK (1<<1)
+#define IOW_READ_TUN (1<<2)
+#define IOW_READ_LINK (1<<3)
+#define IOW_SHAPER (1<<4)
+#define IOW_CHECK_RESIDUAL (1<<5)
+#define IOW_FRAG (1<<6)
+#define IOW_MBUF (1<<7)
+#define IOW_READ_TUN_FORCE (1<<8)
+#define IOW_WAIT_SIGNAL (1<<9)
+
+#define IOW_READ (IOW_READ_TUN|IOW_READ_LINK)
+
+void pre_select (struct context *c);
+
+void process_io (struct context *c);
+
+void encrypt_sign (struct context *c, bool comp_frag);
+
+const char *wait_status_string (struct context *c, struct gc_arena *gc);
+void show_wait_status (struct context *c);
+
+void read_incoming_link (struct context *c);
+void process_incoming_link (struct context *c);
+void read_incoming_tun (struct context *c);
+void process_incoming_tun (struct context *c);
+void process_outgoing_link (struct context *c);
+void process_outgoing_tun (struct context *c);
+
+bool send_control_channel_string (struct context *c, const char *str, int msglevel);
+
+#define PIPV4_PASSTOS (1<<0)
+#define PIPV4_MSSFIX (1<<1)
+#define PIPV4_OUTGOING (1<<2)
+#define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
+
+void process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf);
+
+#if P2MP
+void schedule_exit (struct context *c, const int n_seconds, const int signal);
+#endif
+
+#endif /* FORWARD_H */
diff --git a/fragment.c b/fragment.c
new file mode 100644
index 0000000..5ef3a47
--- /dev/null
+++ b/fragment.c
@@ -0,0 +1,408 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_FRAGMENT
+
+#include "misc.h"
+#include "fragment.h"
+#include "integer.h"
+#include "memdbg.h"
+
+#define FRAG_ERR(s) { errmsg = s; goto error; }
+
+static void
+fragment_list_buf_init (struct fragment_list *list, const struct frame *frame)
+{
+ int i;
+ for (i = 0; i < N_FRAG_BUF; ++i)
+ list->fragments[i].buf = alloc_buf (BUF_SIZE (frame));
+}
+
+static void
+fragment_list_buf_free (struct fragment_list *list)
+{
+ int i;
+ for (i = 0; i < N_FRAG_BUF; ++i)
+ free_buf (&list->fragments[i].buf);
+}
+
+/*
+ * Given a sequence ID number, get a fragment buffer. Use a sliding window,
+ * similar to packet_id code.
+ */
+static struct fragment *
+fragment_list_get_buf (struct fragment_list *list, int seq_id)
+{
+ int diff;
+ if (abs (diff = modulo_subtract (seq_id, list->seq_id, N_SEQ_ID)) >= N_FRAG_BUF)
+ {
+ int i;
+ for (i = 0; i < N_FRAG_BUF; ++i)
+ list->fragments[i].defined = false;
+ list->index = 0;
+ list->seq_id = seq_id;
+ diff = 0;
+ }
+ while (diff > 0)
+ {
+ list->fragments[list->index = modulo_add (list->index, 1, N_FRAG_BUF)].defined = false;
+ list->seq_id = modulo_add (list->seq_id, 1, N_SEQ_ID);
+ --diff;
+ }
+ return &list->fragments[modulo_add (list->index, diff, N_FRAG_BUF)];
+}
+
+struct fragment_master *
+fragment_init (struct frame *frame)
+{
+ struct fragment_master *ret;
+
+ /* code that initializes other parts of
+ fragment_master assume an initial CLEAR */
+ ALLOC_OBJ_CLEAR (ret, struct fragment_master);
+
+ /* add in the size of our contribution to the expanded frame size */
+ frame_add_to_extra_frame (frame, sizeof(fragment_header_type));
+
+ /*
+ * Outgoing sequence ID is randomized to reduce
+ * the probability of sequence number collisions
+ * when openvpn sessions are restarted. This is
+ * not done out of any need for security, as all
+ * fragmentation control information resides
+ * inside of the encrypted/authenticated envelope.
+ */
+ ret->outgoing_seq_id = (int)get_random() & (N_SEQ_ID - 1);
+
+ event_timeout_init (&ret->wakeup, FRAG_WAKEUP_INTERVAL, now);
+
+ return ret;
+}
+
+void
+fragment_free (struct fragment_master *f)
+{
+ fragment_list_buf_free (&f->incoming);
+ free_buf (&f->outgoing);
+ free_buf (&f->outgoing_return);
+ free (f);
+}
+
+void
+fragment_frame_init (struct fragment_master *f, const struct frame *frame)
+{
+ fragment_list_buf_init (&f->incoming, frame);
+ f->outgoing = alloc_buf (BUF_SIZE (frame));
+ f->outgoing_return = alloc_buf (BUF_SIZE (frame));
+}
+
+/*
+ * Accept an incoming datagram (which may be a fragment) from remote.
+ * If the datagram is whole (i.e not a fragment), pass through.
+ * If the datagram is a fragment, join with other fragments received so far.
+ * If a fragment fully completes the datagram, return the datagram.
+ */
+void
+fragment_incoming (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame)
+{
+ const char *errmsg = NULL;
+ fragment_header_type flags = 0;
+ int frag_type = 0;
+
+ if (buf->len > 0)
+ {
+ /* get flags from packet head */
+ if (!buf_read (buf, &flags, sizeof (flags)))
+ FRAG_ERR ("flags not found in packet");
+ flags = ntoh_fragment_header_type (flags);
+
+ /* get fragment type from flags */
+ frag_type = ((flags >> FRAG_TYPE_SHIFT) & FRAG_TYPE_MASK);
+
+#if 0
+ /*
+ * If you want to extract FRAG_EXTRA_MASK/FRAG_EXTRA_SHIFT bits,
+ * do it here.
+ */
+ if (frag_type == FRAG_WHOLE || frag_type == FRAG_YES_NOTLAST)
+ {
+ }
+#endif
+
+ /* handle the fragment type */
+ if (frag_type == FRAG_WHOLE)
+ {
+ dmsg (D_FRAG_DEBUG,
+ "FRAG_IN buf->len=%d type=FRAG_WHOLE flags="
+ fragment_header_format,
+ buf->len,
+ flags);
+
+ if (flags & (FRAG_SEQ_ID_MASK | FRAG_ID_MASK))
+ FRAG_ERR ("spurrious FRAG_WHOLE flags");
+ }
+ else if (frag_type == FRAG_YES_NOTLAST || frag_type == FRAG_YES_LAST)
+ {
+ const int seq_id = ((flags >> FRAG_SEQ_ID_SHIFT) & FRAG_SEQ_ID_MASK);
+ const int n = ((flags >> FRAG_ID_SHIFT) & FRAG_ID_MASK);
+ const int size = ((frag_type == FRAG_YES_LAST)
+ ? (int)(((flags >> FRAG_SIZE_SHIFT) & FRAG_SIZE_MASK) << FRAG_SIZE_ROUND_SHIFT)
+ : buf->len);
+
+ /* get the appropriate fragment buffer based on received seq_id */
+ struct fragment *frag = fragment_list_get_buf (&f->incoming, seq_id);
+
+ dmsg (D_FRAG_DEBUG,
+ "FRAG_IN len=%d type=%d seq_id=%d frag_id=%d size=%d flags="
+ fragment_header_format,
+ buf->len,
+ frag_type,
+ seq_id,
+ n,
+ size,
+ flags);
+
+ /* make sure that size is an even multiple of 1<<FRAG_SIZE_ROUND_SHIFT */
+ if (size & FRAG_SIZE_ROUND_MASK)
+ FRAG_ERR ("bad fragment size");
+
+ /* is this the first fragment for our sequence number? */
+ if (!frag->defined || (frag->defined && frag->max_frag_size != size))
+ {
+ frag->defined = true;
+ frag->max_frag_size = size;
+ frag->map = 0;
+ ASSERT (buf_init (&frag->buf, FRAME_HEADROOM_ADJ (frame, FRAME_HEADROOM_MARKER_FRAGMENT)));
+ }
+
+ /* copy the data to fragment buffer */
+ if (!buf_copy_range (&frag->buf, n * size, buf, 0, buf->len))
+ FRAG_ERR ("fragment buffer overflow");
+
+ /* set elements in bit array to reflect which fragments have been received */
+ frag->map |= (((frag_type == FRAG_YES_LAST) ? FRAG_MAP_MASK : 1) << n);
+
+ /* update timestamp on partially built datagram */
+ frag->timestamp = now;
+
+ /* received full datagram? */
+ if ((frag->map & FRAG_MAP_MASK) == FRAG_MAP_MASK)
+ {
+ frag->defined = false;
+ *buf = frag->buf;
+ }
+ else
+ {
+ buf->len = 0;
+ }
+ }
+ else if (frag_type == FRAG_TEST)
+ {
+ FRAG_ERR ("FRAG_TEST not implemented");
+ }
+ else
+ {
+ FRAG_ERR ("unknown fragment type");
+ }
+ }
+
+ return;
+
+ error:
+ if (errmsg)
+ msg (D_FRAG_ERRORS, "FRAG_IN error flags=" fragment_header_format ": %s", flags, errmsg);
+ buf->len = 0;
+ return;
+}
+
+/* pack fragment parms into a uint32_t and prepend to buffer */
+static void
+fragment_prepend_flags (struct buffer *buf,
+ int type,
+ int seq_id,
+ int frag_id,
+ int frag_size)
+{
+ fragment_header_type flags = ((type & FRAG_TYPE_MASK) << FRAG_TYPE_SHIFT)
+ | ((seq_id & FRAG_SEQ_ID_MASK) << FRAG_SEQ_ID_SHIFT)
+ | ((frag_id & FRAG_ID_MASK) << FRAG_ID_SHIFT);
+
+ if (type == FRAG_WHOLE || type == FRAG_YES_NOTLAST)
+ {
+ /*
+ * If you want to set FRAG_EXTRA_MASK/FRAG_EXTRA_SHIFT bits,
+ * do it here.
+ */
+ dmsg (D_FRAG_DEBUG,
+ "FRAG_OUT len=%d type=%d seq_id=%d frag_id=%d frag_size=%d flags="
+ fragment_header_format,
+ buf->len, type, seq_id, frag_id, frag_size, flags);
+ }
+ else
+ {
+ flags |= (((frag_size >> FRAG_SIZE_ROUND_SHIFT) & FRAG_SIZE_MASK) << FRAG_SIZE_SHIFT);
+
+ dmsg (D_FRAG_DEBUG,
+ "FRAG_OUT len=%d type=%d seq_id=%d frag_id=%d frag_size=%d flags="
+ fragment_header_format,
+ buf->len, type, seq_id, frag_id, frag_size, flags);
+ }
+
+ flags = hton_fragment_header_type (flags);
+ ASSERT (buf_write_prepend (buf, &flags, sizeof (flags)));
+}
+
+/*
+ * Without changing the number of fragments, return a possibly smaller
+ * max fragment size that will allow for the last fragment to be of
+ * similar size as previous fragments.
+ */
+static inline int
+optimal_fragment_size (int len, int max_frag_size)
+{
+ const int mfs_aligned = (max_frag_size & ~FRAG_SIZE_ROUND_MASK);
+ const int div = len / mfs_aligned;
+ const int mod = len % mfs_aligned;
+
+ if (div > 0 && mod > 0 && mod < mfs_aligned * 3 / 4)
+ return min_int (mfs_aligned, (max_frag_size - ((max_frag_size - mod) / (div + 1))
+ + FRAG_SIZE_ROUND_MASK) & ~FRAG_SIZE_ROUND_MASK);
+ else
+ return mfs_aligned;
+}
+
+/* process an outgoing datagram, possibly breaking it up into fragments */
+void
+fragment_outgoing (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame)
+{
+ const char *errmsg = NULL;
+ if (buf->len > 0)
+ {
+ /* The outgoing buffer should be empty so we can put new data in it */
+ if (f->outgoing.len)
+ msg (D_FRAG_ERRORS, "FRAG: outgoing buffer is not empty, len=[%d,%d]",
+ buf->len, f->outgoing.len);
+ if (buf->len > PAYLOAD_SIZE_DYNAMIC(frame)) /* should we fragment? */
+ {
+ /*
+ * Send the datagram as a series of 2 or more fragments.
+ */
+ f->outgoing_frag_size = optimal_fragment_size (buf->len, PAYLOAD_SIZE_DYNAMIC(frame));
+ if (buf->len > f->outgoing_frag_size * MAX_FRAGS)
+ FRAG_ERR ("too many fragments would be required to send datagram");
+ ASSERT (buf_init (&f->outgoing, FRAME_HEADROOM (frame)));
+ ASSERT (buf_copy (&f->outgoing, buf));
+ f->outgoing_seq_id = modulo_add (f->outgoing_seq_id, 1, N_SEQ_ID);
+ f->outgoing_frag_id = 0;
+ buf->len = 0;
+ ASSERT (fragment_ready_to_send (f, buf, frame));
+ }
+ else
+ {
+ /*
+ * Send the datagram whole.
+ */
+ fragment_prepend_flags (buf,
+ FRAG_WHOLE,
+ 0,
+ 0,
+ 0);
+ }
+ }
+ return;
+
+ error:
+ if (errmsg)
+ msg (D_FRAG_ERRORS, "FRAG_OUT error, len=%d frag_size=%d MAX_FRAGS=%d: %s",
+ buf->len, f->outgoing_frag_size, MAX_FRAGS, errmsg);
+ buf->len = 0;
+ return;
+}
+
+/* return true (and set buf) if we have an outgoing fragment which is ready to send */
+bool
+fragment_ready_to_send (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame)
+{
+ if (fragment_outgoing_defined (f))
+ {
+ /* get fragment size, and determine if it is the last fragment */
+ int size = f->outgoing_frag_size;
+ int last = false;
+ if (f->outgoing.len <= size)
+ {
+ size = f->outgoing.len;
+ last = true;
+ }
+
+ /* initialize return buffer */
+ *buf = f->outgoing_return;
+ ASSERT (buf_init (buf, FRAME_HEADROOM (frame)));
+ ASSERT (buf_copy_n (buf, &f->outgoing, size));
+
+ /* fragment flags differ based on whether or not we are sending the last fragment */
+ fragment_prepend_flags (buf,
+ last ? FRAG_YES_LAST : FRAG_YES_NOTLAST,
+ f->outgoing_seq_id,
+ f->outgoing_frag_id++,
+ f->outgoing_frag_size);
+
+ ASSERT (!last || !f->outgoing.len); /* outgoing buffer length should be zero after last fragment sent */
+
+ return true;
+ }
+ else
+ return false;
+}
+
+static void
+fragment_ttl_reap (struct fragment_master *f)
+{
+ int i;
+ for (i = 0; i < N_FRAG_BUF; ++i)
+ {
+ struct fragment *frag = &f->incoming.fragments[i];
+ if (frag->defined && frag->timestamp + FRAG_TTL_SEC <= now)
+ {
+ msg (D_FRAG_ERRORS, "FRAG TTL expired i=%d", i);
+ frag->defined = false;
+ }
+ }
+}
+
+/* called every FRAG_WAKEUP_INTERVAL seconds */
+void
+fragment_wakeup (struct fragment_master *f, struct frame *frame)
+{
+ /* delete fragments with expired TTLs */
+ fragment_ttl_reap (f);
+}
+
+#else
+static void dummy(void) {}
+#endif
diff --git a/fragment.h b/fragment.h
new file mode 100644
index 0000000..fc2b1b8
--- /dev/null
+++ b/fragment.h
@@ -0,0 +1,188 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef FRAGMENT_H
+#define FRAGMENT_H
+
+#ifdef ENABLE_FRAGMENT
+
+#include "common.h"
+#include "buffer.h"
+#include "interval.h"
+#include "mtu.h"
+#include "shaper.h"
+#include "error.h"
+
+#define N_FRAG_BUF 25 /* number of packet buffers */
+#define FRAG_TTL_SEC 10 /* number of seconds time-to-live for a fragment */
+#define FRAG_WAKEUP_INTERVAL 5 /* wakeup code called once per n seconds */
+
+struct fragment {
+ bool defined;
+
+ int max_frag_size; /* maximum size of each fragment */
+
+ /*
+ * 32 bit array corresponding to each fragment. A 1 bit in element n means that
+ * the fragment n has been received. Needs to have at least MAX_FRAGS bits.
+ */
+# define FRAG_MAP_MASK 0xFFFFFFFF
+# define MAX_FRAGS 32 /* maximum number of fragments per packet */
+ unsigned int map;
+
+ time_t timestamp; /* timestamp for time-to-live purposes */
+
+ struct buffer buf; /* fragment assembly buffer for received datagrams */
+};
+
+struct fragment_list {
+ int seq_id;
+ int index;
+ struct fragment fragments[N_FRAG_BUF];
+};
+
+struct fragment_master {
+ struct event_timeout wakeup; /* when should main openvpn event loop wake us up */
+
+ /* true if the OS has explicitly recommended an MTU value */
+ bool received_os_mtu_hint;
+
+ /* a sequence ID describes a set of fragments that make up one datagram */
+# define N_SEQ_ID 256 /* sequence number wraps to 0 at this value (should be a power of 2) */
+ int outgoing_seq_id; /* sent as FRAG_SEQ_ID below */
+
+ /* outgoing packet is possibly sent as a series of fragments */
+
+# define MAX_FRAG_PKT_SIZE 65536 /* maximum packet size */
+ int outgoing_frag_size; /* sent to peer via FRAG_SIZE when FRAG_YES_LAST set */
+
+ int outgoing_frag_id; /* each fragment in a datagram is numbered 0 to MAX_FRAGS-1 */
+
+ struct buffer outgoing; /* outgoing datagram, free if current_frag_id == 0 */
+ struct buffer outgoing_return; /* buffer to return outgoing fragment */
+
+ /* incoming fragments from remote */
+ struct fragment_list incoming;
+};
+
+/*
+ * Fragment header sent over the wire.
+ */
+
+typedef uint32_t fragment_header_type;
+
+/* convert a fragment_header_type from host to network order */
+#define hton_fragment_header_type(x) htonl(x)
+
+/* convert a fragment_header_type from network to host order */
+#define ntoh_fragment_header_type(x) ntohl(x)
+
+/* FRAG_TYPE 2 bits */
+#define FRAG_TYPE_MASK 0x00000003
+#define FRAG_TYPE_SHIFT 0
+
+#define FRAG_WHOLE 0 /* packet is whole, FRAG_N_PACKETS_RECEIVED echoed back to peer */
+#define FRAG_YES_NOTLAST 1 /* packet is a fragment, but is not the last fragment,
+ FRAG_N_PACKETS_RECEIVED set as above */
+#define FRAG_YES_LAST 2 /* packet is the last fragment, FRAG_SIZE = size of non-last frags */
+#define FRAG_TEST 3 /* control packet for establishing MTU size (not implemented yet) */
+
+/* FRAG_SEQ_ID 8 bits */
+#define FRAG_SEQ_ID_MASK 0x000000ff
+#define FRAG_SEQ_ID_SHIFT 2
+
+/* FRAG_ID 5 bits */
+#define FRAG_ID_MASK 0x0000001f
+#define FRAG_ID_SHIFT 10
+
+/*
+ * FRAG_SIZE 14 bits
+ *
+ * IF FRAG_YES_LAST (FRAG_SIZE):
+ * The max size of a fragment. If a fragment is not the last fragment in the packet,
+ * then the fragment size is guaranteed to be equal to the max fragment size. Therefore,
+ * max_frag_size is only sent over the wire if FRAG_LAST is set. Otherwise it is assumed
+ * to be the actual fragment size received.
+ */
+
+/* FRAG_SIZE 14 bits */
+#define FRAG_SIZE_MASK 0x00003fff
+#define FRAG_SIZE_SHIFT 15
+#define FRAG_SIZE_ROUND_SHIFT 2 /* fragment/datagram sizes represented as multiple of 4 */
+
+#define FRAG_SIZE_ROUND_MASK ((1 << FRAG_SIZE_ROUND_SHIFT) - 1)
+
+/*
+ * FRAG_EXTRA 16 bits
+ *
+ * IF FRAG_WHOLE or FRAG_YES_NOTLAST, these 16 bits are available (not currently used)
+ */
+
+/* FRAG_EXTRA 16 bits */
+#define FRAG_EXTRA_MASK 0x0000ffff
+#define FRAG_EXTRA_SHIFT 15
+
+/*
+ * Public functions
+ */
+
+struct fragment_master *fragment_init (struct frame *frame);
+
+void fragment_frame_init (struct fragment_master *f, const struct frame *frame);
+
+void fragment_free (struct fragment_master *f);
+
+void fragment_incoming (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame);
+
+void fragment_outgoing (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame);
+
+bool fragment_ready_to_send (struct fragment_master *f, struct buffer *buf,
+ const struct frame* frame);
+
+/*
+ * Private functions.
+ */
+void fragment_wakeup (struct fragment_master *f, struct frame *frame);
+
+/*
+ * Inline functions
+ */
+
+static inline void
+fragment_housekeeping (struct fragment_master *f, struct frame *frame, struct timeval *tv)
+{
+ if (event_timeout_trigger (&f->wakeup, tv, ETT_DEFAULT))
+ fragment_wakeup (f, frame);
+}
+
+static inline bool
+fragment_outgoing_defined (struct fragment_master *f)
+{
+ return f->outgoing.len > 0;
+}
+
+#endif
+#endif
diff --git a/gremlin.c b/gremlin.c
new file mode 100644
index 0000000..c7c0206
--- /dev/null
+++ b/gremlin.c
@@ -0,0 +1,215 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Test protocol robustness by simulating dropped packets and
+ * network outages when the --gremlin option is used.
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_DEBUG
+
+#include "error.h"
+#include "common.h"
+#include "misc.h"
+#include "otime.h"
+#include "gremlin.h"
+
+#include "memdbg.h"
+
+/*
+ * Parameters for packet corruption and droppage.
+ * Each parameter has 4 possible levels, 0 = disabled,
+ * while 1, 2, and 3 are enumerated in the below arrays.
+ * The parameter is a 2-bit field within the --gremlin
+ * parameter.
+ */
+
+/*
+ * Probability that we will drop a packet is 1 / n
+ */
+static const int drop_freq[] = { 500, 100, 50 };
+
+/*
+ * Probability that we will corrupt a packet is 1 / n
+ */
+static const int corrupt_freq[] = { 500, 100, 50 };
+
+/*
+ * When network goes up, it will be up for between
+ * UP_LOW and UP_HIGH seconds.
+ */
+static const int up_low[] = { 60, 10, 5 };
+static const int up_high[] = { 600, 60, 10 };
+
+/*
+ * When network goes down, it will be down for between
+ * DOWN_LOW and DOWN_HIGH seconds.
+ */
+static const int down_low[] = { 5, 10, 10 };
+static const int down_high[] = { 10, 60, 120 };
+
+/*
+ * Packet flood levels:
+ * { number of packets, packet size }
+ */
+static const struct packet_flood_parms packet_flood_data[] =
+ {{10, 100}, {10, 1500}, {100, 1500}};
+
+struct packet_flood_parms
+get_packet_flood_parms (int level)
+{
+ ASSERT (level > 0 && level < 4);
+ return packet_flood_data [level - 1];
+}
+
+/*
+ * Return true with probability 1/n
+ */
+static bool flip(int n) {
+ return (get_random() % n) == 0;
+}
+
+/*
+ * Return uniformly distributed random number between
+ * low and high.
+ */
+static int roll(int low, int high) {
+ int ret;
+ ASSERT (low <= high);
+ ret = low + (get_random() % (high - low + 1));
+ ASSERT (ret >= low && ret <= high);
+ return ret;
+}
+
+static bool initialized; /* GLOBAL */
+static bool up; /* GLOBAL */
+static time_t next; /* GLOBAL */
+
+/*
+ * Return false if we should drop a packet.
+ */
+bool
+ask_gremlin (int flags)
+{
+ const int up_down_level = GREMLIN_UP_DOWN_LEVEL (flags);
+ const int drop_level = GREMLIN_DROP_LEVEL (flags);
+
+ if (!initialized)
+ {
+ initialized = true;
+
+ if (up_down_level)
+ up = false;
+ else
+ up = true;
+
+ next = now;
+ }
+
+ if (up_down_level) /* change up/down state? */
+ {
+ if (now >= next)
+ {
+ int delta;
+ if (up)
+ {
+ delta = roll (down_low[up_down_level-1], down_high[up_down_level-1]);
+ up = false;
+ }
+ else
+ {
+ delta = roll (up_low[up_down_level-1], up_high[up_down_level-1]);
+ up = true;
+ }
+
+ msg (D_GREMLIN,
+ "GREMLIN: CONNECTION GOING %s FOR %d SECONDS",
+ (up ? "UP" : "DOWN"),
+ delta);
+ next = now + delta;
+ }
+ }
+
+ if (drop_level)
+ {
+ if (up && flip (drop_freq[drop_level-1]))
+ {
+ dmsg (D_GREMLIN_VERBOSE, "GREMLIN: Random packet drop");
+ return false;
+ }
+ }
+
+ return up;
+}
+
+/*
+ * Possibly corrupt a packet.
+ */
+void corrupt_gremlin (struct buffer *buf, int flags) {
+ const int corrupt_level = GREMLIN_CORRUPT_LEVEL (flags);
+ if (corrupt_level)
+ {
+ if (flip (corrupt_freq[corrupt_level-1]))
+ {
+ do
+ {
+ if (buf->len > 0)
+ {
+ uint8_t r = roll (0, 255);
+ int method = roll (0, 5);
+
+ switch (method) {
+ case 0: /* corrupt the first byte */
+ *BPTR (buf) = r;
+ break;
+ case 1: /* corrupt the last byte */
+ *(BPTR (buf) + buf->len - 1) = r;
+ break;
+ case 2: /* corrupt a random byte */
+ *(BPTR(buf) + roll (0, buf->len - 1)) = r;
+ break;
+ case 3: /* append a random byte */
+ buf_write (buf, &r, 1);
+ break;
+ case 4: /* reduce length by 1 */
+ --buf->len;
+ break;
+ case 5: /* reduce length by a random amount */
+ buf->len -= roll (0, buf->len - 1);
+ break;
+ }
+ dmsg (D_GREMLIN_VERBOSE, "GREMLIN: Packet Corruption, method=%d", method);
+ }
+ else
+ break;
+ } while (flip (2)); /* a 50% chance we will corrupt again */
+ }
+ }
+}
+
+#else
+static void dummy(void) {}
+#endif
diff --git a/gremlin.h b/gremlin.h
new file mode 100644
index 0000000..c0aeab1
--- /dev/null
+++ b/gremlin.h
@@ -0,0 +1,72 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef GREMLIN_H
+#define GREMLIN_H
+
+#ifdef ENABLE_DEBUG
+
+/*
+ * Gremlin options, presented as bitmask argument to --gremlin directive
+ */
+
+#define GREMLIN_CONNECTION_FLOOD_SHIFT (0)
+#define GREMLIN_CONNECTION_FLOOD_MASK (0x07)
+
+#define GREMLIN_PACKET_FLOOD_SHIFT (3)
+#define GREMLIN_PACKET_FLOOD_MASK (0x03)
+
+#define GREMLIN_CORRUPT_SHIFT (5)
+#define GREMLIN_CORRUPT_MASK (0x03)
+
+#define GREMLIN_UP_DOWN_SHIFT (7)
+#define GREMLIN_UP_DOWN_MASK (0x03)
+
+/* 512:1/500 1024:1/100 1536:1/50 */
+
+#define GREMLIN_DROP_SHIFT (9)
+#define GREMLIN_DROP_MASK (0x03)
+
+/* extract gremlin parms */
+
+#define GREMLIN_CONNECTION_FLOOD_LEVEL(x) (((x)>>GREMLIN_CONNECTION_FLOOD_SHIFT) & GREMLIN_CONNECTION_FLOOD_MASK)
+#define GREMLIN_PACKET_FLOOD_LEVEL(x) (((x)>>GREMLIN_PACKET_FLOOD_SHIFT) & GREMLIN_PACKET_FLOOD_MASK)
+#define GREMLIN_CORRUPT_LEVEL(x) (((x)>>GREMLIN_CORRUPT_SHIFT) & GREMLIN_CORRUPT_MASK)
+#define GREMLIN_UP_DOWN_LEVEL(x) (((x)>>GREMLIN_UP_DOWN_SHIFT) & GREMLIN_UP_DOWN_MASK)
+#define GREMLIN_DROP_LEVEL(x) (((x)>>GREMLIN_DROP_SHIFT) & GREMLIN_DROP_MASK)
+
+#include "buffer.h"
+
+struct packet_flood_parms
+{
+ int n_packets;
+ int packet_size;
+};
+
+bool ask_gremlin (int flags);
+void corrupt_gremlin (struct buffer* buf, int flags);
+struct packet_flood_parms get_packet_flood_parms (int level);
+
+#endif
+#endif
diff --git a/helper.c b/helper.c
new file mode 100644
index 0000000..a9d7fd9
--- /dev/null
+++ b/helper.c
@@ -0,0 +1,480 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "forward.h"
+#include "helper.h"
+#include "pool.h"
+#include "push.h"
+
+#include "memdbg.h"
+
+#if P2MP_SERVER
+
+static const char *
+print_netmask (int netbits, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+ const in_addr_t netmask = netbits_to_netmask (netbits);
+
+ buf_printf (&out, "%s (/%d)", print_in_addr_t (netmask, 0, gc), netbits);
+
+ return BSTR (&out);
+}
+
+static const char *
+print_opt_route_gateway (const in_addr_t route_gateway, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+ ASSERT (route_gateway);
+ buf_printf (&out, "route-gateway %s", print_in_addr_t (route_gateway, 0, gc));
+ return BSTR (&out);
+}
+
+static const char *
+print_opt_route_gateway_dhcp (struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (32, gc);
+ buf_printf (&out, "route-gateway dhcp");
+ return BSTR (&out);
+}
+
+static const char *
+print_opt_route (const in_addr_t network, const in_addr_t netmask, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+ ASSERT (network);
+
+ if (netmask)
+ buf_printf (&out, "route %s %s",
+ print_in_addr_t (network, 0, gc),
+ print_in_addr_t (netmask, 0, gc));
+ else
+ buf_printf (&out, "route %s",
+ print_in_addr_t (network, 0, gc));
+
+ return BSTR (&out);
+}
+
+static const char *
+print_opt_topology (const int topology, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+
+ buf_printf (&out, "topology %s", print_topology (topology));
+
+ return BSTR (&out);
+}
+
+static const char *
+print_str_int (const char *str, const int i, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+ buf_printf (&out, "%s %d", str, i);
+ return BSTR (&out);
+}
+
+static const char *
+print_str (const char *str, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (128, gc);
+ buf_printf (&out, "%s", str);
+ return BSTR (&out);
+}
+
+static void
+helper_add_route (const in_addr_t network, const in_addr_t netmask, struct options *o)
+{
+ rol_check_alloc (o);
+ add_route_to_option_list (o->routes,
+ print_in_addr_t (network, 0, &o->gc),
+ print_in_addr_t (netmask, 0, &o->gc),
+ NULL,
+ NULL);
+}
+
+static void
+verify_common_subnet (const char *opt, const in_addr_t a, const in_addr_t b, const in_addr_t subnet)
+{
+ struct gc_arena gc = gc_new ();
+ if ((a & subnet) != (b & subnet))
+ msg (M_USAGE, "%s IP addresses %s and %s are not in the same %s subnet",
+ opt,
+ print_in_addr_t (a, 0, &gc),
+ print_in_addr_t (b, 0, &gc),
+ print_in_addr_t (subnet, 0, &gc));
+ gc_free (&gc);
+}
+
+#endif
+
+/*
+ * Process server, server-bridge, and client helper
+ * directives after the parameters themselves have been
+ * parsed and placed in struct options.
+ */
+void
+helper_client_server (struct options *o)
+{
+ struct gc_arena gc = gc_new ();
+
+#if P2MP
+#if P2MP_SERVER
+ /*
+ *
+ * HELPER DIRECTIVE:
+ *
+ * server 10.8.0.0 255.255.255.0
+ *
+ * EXPANDS TO:
+ *
+ * mode server
+ * tls-server
+ * push "topology [topology]"
+ *
+ * if tun AND (topology == net30 OR topology == p2p):
+ * ifconfig 10.8.0.1 10.8.0.2
+ * if !nopool:
+ * ifconfig-pool 10.8.0.4 10.8.0.251
+ * route 10.8.0.0 255.255.255.0
+ * if client-to-client:
+ * push "route 10.8.0.0 255.255.255.0"
+ * else if topology == net30:
+ * push "route 10.8.0.1"
+ *
+ * if tap OR (tun AND topology == subnet):
+ * ifconfig 10.8.0.1 255.255.255.0
+ * if !nopool:
+ * ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0
+ * push "route-gateway 10.8.0.1"
+ */
+
+ /*
+ * Get tun/tap/null device type
+ */
+ const int dev = dev_type_enum (o->dev, o->dev_type);
+ const int topology = o->topology;
+
+ if (o->server_defined)
+ {
+ int netbits = -2;
+ bool status = false;
+
+ if (o->client)
+ msg (M_USAGE, "--server and --client cannot be used together");
+
+ if (o->server_bridge_defined || o->server_bridge_proxy_dhcp)
+ msg (M_USAGE, "--server and --server-bridge cannot be used together");
+
+ if (o->shared_secret_file)
+ msg (M_USAGE, "--server and --secret cannot be used together (you must use SSL/TLS keys)");
+
+ if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined)
+ msg (M_USAGE, "--server already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly");
+
+ if (!(dev == DEV_TYPE_TAP || dev == DEV_TYPE_TUN))
+ msg (M_USAGE, "--server directive only makes sense with --dev tun or --dev tap");
+
+ status = netmask_to_netbits (o->server_network, o->server_netmask, &netbits);
+ if (!status)
+ msg (M_USAGE, "--server directive network/netmask combination is invalid");
+
+ if (netbits < 0)
+ msg (M_USAGE, "--server directive netmask is invalid");
+
+ if (netbits < IFCONFIG_POOL_MIN_NETBITS)
+ msg (M_USAGE, "--server directive netmask allows for too many host addresses (subnet must be %s or higher)",
+ print_netmask (IFCONFIG_POOL_MIN_NETBITS, &gc));
+
+ if (dev == DEV_TYPE_TUN)
+ {
+ int pool_end_reserve = 4;
+
+ if (netbits > 29)
+ msg (M_USAGE, "--server directive when used with --dev tun must define a subnet of %s or lower",
+ print_netmask (29, &gc));
+
+ if (netbits == 29)
+ pool_end_reserve = 0;
+
+ o->mode = MODE_SERVER;
+ o->tls_server = true;
+
+ if (topology == TOP_NET30 || topology == TOP_P2P)
+ {
+ o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc);
+ o->ifconfig_remote_netmask = print_in_addr_t (o->server_network + 2, 0, &o->gc);
+
+ if (!(o->server_flags & SF_NOPOOL))
+ {
+ o->ifconfig_pool_defined = true;
+ o->ifconfig_pool_start = o->server_network + 4;
+ o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - pool_end_reserve;
+ ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
+ }
+
+ helper_add_route (o->server_network, o->server_netmask, o);
+ if (o->enable_c2c)
+ push_option (o, print_opt_route (o->server_network, o->server_netmask, &o->gc), M_USAGE);
+ else if (topology == TOP_NET30)
+ push_option (o, print_opt_route (o->server_network + 1, 0, &o->gc), M_USAGE);
+ }
+ else if (topology == TOP_SUBNET)
+ {
+ o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc);
+ o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc);
+
+ if (!(o->server_flags & SF_NOPOOL))
+ {
+ o->ifconfig_pool_defined = true;
+ o->ifconfig_pool_start = o->server_network + 2;
+ o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 2;
+ ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
+ }
+ o->ifconfig_pool_netmask = o->server_netmask;
+
+ push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE);
+ }
+ else
+ ASSERT (0);
+
+ push_option (o, print_opt_topology (topology, &o->gc), M_USAGE);
+ }
+ else if (dev == DEV_TYPE_TAP)
+ {
+ if (netbits > 30)
+ msg (M_USAGE, "--server directive when used with --dev tap must define a subnet of %s or lower",
+ print_netmask (30, &gc));
+
+ o->mode = MODE_SERVER;
+ o->tls_server = true;
+ o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc);
+ o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc);
+
+ if (!(o->server_flags & SF_NOPOOL))
+ {
+ o->ifconfig_pool_defined = true;
+ o->ifconfig_pool_start = o->server_network + 2;
+ o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 1;
+ ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
+ }
+ o->ifconfig_pool_netmask = o->server_netmask;
+
+ push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ /* set push-ifconfig-constraint directive */
+ if ((dev == DEV_TYPE_TAP || topology == TOP_SUBNET))
+ {
+ o->push_ifconfig_constraint_defined = true;
+ o->push_ifconfig_constraint_network = o->server_network;
+ o->push_ifconfig_constraint_netmask = o->server_netmask;
+ }
+ }
+
+ /*
+ * HELPER DIRECTIVE:
+ *
+ * server-bridge 10.8.0.4 255.255.255.0 10.8.0.128 10.8.0.254
+ *
+ * EXPANDS TO:
+ *
+ * mode server
+ * tls-server
+ *
+ * ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
+ * push "route-gateway 10.8.0.4"
+ *
+ * OR
+ *
+ * server-bridge
+ *
+ * EXPANDS TO:
+ *
+ * mode server
+ * tls-server
+ *
+ * if !nogw:
+ * push "route-gateway dhcp"
+ */
+ else if (o->server_bridge_defined | o->server_bridge_proxy_dhcp)
+ {
+ if (o->client)
+ msg (M_USAGE, "--server-bridge and --client cannot be used together");
+
+ if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined)
+ msg (M_USAGE, "--server-bridge already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly");
+
+ if (o->shared_secret_file)
+ msg (M_USAGE, "--server-bridge and --secret cannot be used together (you must use SSL/TLS keys)");
+
+ if (dev != DEV_TYPE_TAP)
+ msg (M_USAGE, "--server-bridge directive only makes sense with --dev tap");
+
+ if (o->server_bridge_defined)
+ {
+ verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_start, o->server_bridge_netmask);
+ verify_common_subnet ("--server-bridge", o->server_bridge_pool_start, o->server_bridge_pool_end, o->server_bridge_netmask);
+ verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_end, o->server_bridge_netmask);
+ }
+
+ o->mode = MODE_SERVER;
+ o->tls_server = true;
+
+ if (o->server_bridge_defined)
+ {
+ o->ifconfig_pool_defined = true;
+ o->ifconfig_pool_start = o->server_bridge_pool_start;
+ o->ifconfig_pool_end = o->server_bridge_pool_end;
+ ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
+ o->ifconfig_pool_netmask = o->server_bridge_netmask;
+ push_option (o, print_opt_route_gateway (o->server_bridge_ip, &o->gc), M_USAGE);
+ }
+ else if (o->server_bridge_proxy_dhcp && !(o->server_flags & SF_NO_PUSH_ROUTE_GATEWAY))
+ {
+ push_option (o, print_opt_route_gateway_dhcp (&o->gc), M_USAGE);
+ }
+ }
+ else
+#endif /* P2MP_SERVER */
+
+ /*
+ * HELPER DIRECTIVE:
+ *
+ * client
+ *
+ * EXPANDS TO:
+ *
+ * pull
+ * tls-client
+ */
+ if (o->client)
+ {
+ if (o->key_method != 2)
+ msg (M_USAGE, "--client requires --key-method 2");
+
+ o->pull = true;
+ o->tls_client = true;
+ }
+
+#endif /* P2MP */
+
+ gc_free (&gc);
+}
+
+/*
+ *
+ * HELPER DIRECTIVE:
+ *
+ * keepalive 10 60
+ *
+ * EXPANDS TO:
+ *
+ * if mode server:
+ * ping 10
+ * ping-restart 120
+ * push "ping 10"
+ * push "ping-restart 60"
+ * else
+ * ping 10
+ * ping-restart 60
+ */
+void
+helper_keepalive (struct options *o)
+{
+ if (o->keepalive_ping || o->keepalive_timeout)
+ {
+ /*
+ * Sanity checks.
+ */
+ if (o->keepalive_ping <= 0 || o->keepalive_timeout <= 0)
+ msg (M_USAGE, "--keepalive parameters must be > 0");
+ if (o->keepalive_ping * 2 > o->keepalive_timeout)
+ msg (M_USAGE, "the second parameter to --keepalive (restart timeout=%d) must be at least twice the value of the first parameter (ping interval=%d). A ratio of 1:5 or 1:6 would be even better. Recommended setting is --keepalive 10 60.",
+ o->keepalive_timeout,
+ o->keepalive_ping);
+ if (o->ping_send_timeout || o->ping_rec_timeout)
+ msg (M_USAGE, "--keepalive conflicts with --ping, --ping-exit, or --ping-restart. If you use --keepalive, you don't need any of the other --ping directives.");
+
+ /*
+ * Expand.
+ */
+ if (o->mode == MODE_POINT_TO_POINT)
+ {
+ o->ping_rec_timeout_action = PING_RESTART;
+ o->ping_send_timeout = o->keepalive_ping;
+ o->ping_rec_timeout = o->keepalive_timeout;
+ }
+#if P2MP_SERVER
+ else if (o->mode == MODE_SERVER)
+ {
+ o->ping_rec_timeout_action = PING_RESTART;
+ o->ping_send_timeout = o->keepalive_ping;
+ o->ping_rec_timeout = o->keepalive_timeout * 2;
+ push_option (o, print_str_int ("ping", o->keepalive_ping, &o->gc), M_USAGE);
+ push_option (o, print_str_int ("ping-restart", o->keepalive_timeout, &o->gc), M_USAGE);
+ }
+#endif
+ else
+ {
+ ASSERT (0);
+ }
+ }
+}
+
+/*
+ *
+ * HELPER DIRECTIVE:
+ *
+ * tcp-nodelay
+ *
+ * EXPANDS TO:
+ *
+ * if mode server:
+ * socket-flags TCP_NODELAY
+ * push "socket-flags TCP_NODELAY"
+ */
+void
+helper_tcp_nodelay (struct options *o)
+{
+#if P2MP_SERVER
+ if (o->server_flags & SF_TCP_NODELAY_HELPER)
+ {
+ if (o->mode == MODE_SERVER)
+ {
+ o->sockflags |= SF_TCP_NODELAY;
+ push_option (o, print_str ("socket-flags TCP_NODELAY", &o->gc), M_USAGE);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
+#endif
+}
diff --git a/helper.h b/helper.h
new file mode 100644
index 0000000..444969c
--- /dev/null
+++ b/helper.h
@@ -0,0 +1,38 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Process helper directives such as server, client, and keepalive.
+ */
+
+#ifndef HELPER_H
+#define HELPER_H
+
+#include "options.h"
+
+void helper_keepalive (struct options *o);
+void helper_client_server (struct options *o);
+void helper_tcp_nodelay (struct options *o);
+
+#endif
diff --git a/httpdigest.c b/httpdigest.c
new file mode 100644
index 0000000..90abc6a
--- /dev/null
+++ b/httpdigest.c
@@ -0,0 +1,143 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if PROXY_DIGEST_AUTH
+
+#include "crypto.h"
+#include "httpdigest.h"
+
+static void
+CvtHex(
+ IN HASH Bin,
+ OUT HASHHEX Hex
+ )
+{
+ unsigned short i;
+ unsigned char j;
+
+ for (i = 0; i < HASHLEN; i++) {
+ j = (Bin[i] >> 4) & 0xf;
+ if (j <= 9)
+ Hex[i*2] = (j + '0');
+ else
+ Hex[i*2] = (j + 'a' - 10);
+ j = Bin[i] & 0xf;
+ if (j <= 9)
+ Hex[i*2+1] = (j + '0');
+ else
+ Hex[i*2+1] = (j + 'a' - 10);
+ };
+ Hex[HASHHEXLEN] = '\0';
+};
+
+/* calculate H(A1) as per spec */
+void
+DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA1;
+
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, pszUserName, strlen(pszUserName));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszRealm, strlen(pszRealm));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszPassword, strlen(pszPassword));
+ MD5_Final(HA1, &Md5Ctx);
+ if (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)
+ {
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, HA1, HASHLEN);
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5_Final(HA1, &Md5Ctx);
+ };
+ CvtHex(HA1, SessionKey);
+}
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void
+DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ )
+{
+ MD5_CTX Md5Ctx;
+ HASH HA2;
+ HASH RespHash;
+ HASHHEX HA2Hex;
+
+ // calculate H(A2)
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, pszMethod, strlen(pszMethod));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
+ if (strcasecmp(pszQop, "auth-int") == 0)
+ {
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, HEntity, HASHHEXLEN);
+ };
+ MD5_Final(HA2, &Md5Ctx);
+ CvtHex(HA2, HA2Hex);
+
+ // calculate response
+ MD5_Init(&Md5Ctx);
+ MD5_Update(&Md5Ctx, HA1, HASHHEXLEN);
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszNonce, strlen(pszNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ if (*pszQop)
+ {
+ MD5_Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
+ MD5_Update(&Md5Ctx, ":", 1);
+ MD5_Update(&Md5Ctx, pszQop, strlen(pszQop));
+ MD5_Update(&Md5Ctx, ":", 1);
+ };
+ MD5_Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
+ MD5_Final(RespHash, &Md5Ctx);
+ CvtHex(RespHash, Response);
+}
+
+#endif
diff --git a/httpdigest.h b/httpdigest.h
new file mode 100644
index 0000000..8423841
--- /dev/null
+++ b/httpdigest.h
@@ -0,0 +1,60 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if PROXY_DIGEST_AUTH
+
+#define HASHLEN 16
+typedef unsigned char HASH[HASHLEN];
+#define HASHHEXLEN 32
+typedef unsigned char HASHHEX[HASHHEXLEN+1];
+#undef IN
+#undef OUT
+#define IN const
+#define OUT
+
+/* calculate H(A1) as per HTTP Digest spec */
+void DigestCalcHA1(
+ IN char * pszAlg,
+ IN char * pszUserName,
+ IN char * pszRealm,
+ IN char * pszPassword,
+ IN char * pszNonce,
+ IN char * pszCNonce,
+ OUT HASHHEX SessionKey
+ );
+
+/* calculate request-digest/response-digest as per HTTP Digest spec */
+void DigestCalcResponse(
+ IN HASHHEX HA1, /* H(A1) */
+ IN char * pszNonce, /* nonce from server */
+ IN char * pszNonceCount, /* 8 hex digits */
+ IN char * pszCNonce, /* client nonce */
+ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
+ IN char * pszMethod, /* method from the request */
+ IN char * pszDigestUri, /* requested URL */
+ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
+ OUT HASHHEX Response /* request-digest or response-digest */
+ );
+
+#endif
diff --git a/ieproxy.c b/ieproxy.c
new file mode 100644
index 0000000..3099870
--- /dev/null
+++ b/ieproxy.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2004 Ewan Bhamrah Harley <code@ewan.info>
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef WIN32
+
+#include <wininet.h>
+#include <malloc.h>
+
+LPCTSTR getIeHttpProxyError=NULL;
+
+/* getIeHttpProxy fetches the current IE proxy settings for http */
+
+LPCTSTR getIeHttpProxy()
+{
+ DWORD psize=0;
+ INTERNET_PROXY_INFO *pinfo;
+ LPTSTR proxyString;
+ LPTSTR p;
+ LPTSTR q;
+ unsigned int len;
+
+ /* first see how big a buffer we need for the IPO structure */
+ InternetQueryOption(NULL, INTERNET_OPTION_PROXY, NULL, &psize);
+ if(!psize)
+ {
+ getIeHttpProxyError="InternetQueryOption failed to return buffer size";
+ return(NULL);
+ }
+
+ /* allocate memory for IPO */
+ pinfo = malloc (psize*sizeof(TCHAR));
+ if (pinfo == NULL)
+ {
+ getIeHttpProxyError="malloc failed (1)";
+ return(NULL);
+ }
+
+ /* now run the real query */
+ if(!InternetQueryOption(NULL, INTERNET_OPTION_PROXY, (LPVOID) pinfo, &psize))
+ {
+ getIeHttpProxyError="InternetQueryOption() failed to find proxy info";
+ free(pinfo);
+ return(NULL);
+ }
+
+
+ /* see what sort of result we got */
+
+ if(pinfo->dwAccessType == INTERNET_OPEN_TYPE_DIRECT)
+ {
+ /* No proxy configured */
+ free(pinfo);
+ return("");
+ }
+ else if(pinfo->dwAccessType == INTERNET_OPEN_TYPE_PROXY)
+ {
+ /* we have a proxy - now parse result string */
+ /* if result string does NOT contain an '=' sign then */
+ /* there is a single proxy for all protocols */
+ for (p=(LPTSTR)pinfo->lpszProxy; *p && *p != '='; p++);
+ if(!*p)
+ {
+ /* single proxy */
+ /* allocate a new string to return */
+ len = 1+strlen(pinfo->lpszProxy);
+ proxyString = malloc (len*sizeof(TCHAR));
+ if (proxyString == NULL)
+ {
+ getIeHttpProxyError="malloc failed (2)";
+ free(pinfo);
+ return(NULL);
+ }
+ strncpy(proxyString, pinfo->lpszProxy,len);
+ proxyString[len]=0;
+ free(pinfo);
+ return(proxyString);
+ }
+ else
+ {
+ /* multiple space seperated proxies defined in the form */
+ /* protocol=proxyhost[:port] */
+ /* we want the one marked "http=", if any. */
+ p=(LPTSTR)pinfo->lpszProxy;
+ while(*p && strncmp(p, "http=", 5))
+ {
+ for(; *p && *p != ' '; p++);
+ if(*p) p++;
+ }
+ if(*p)
+ {
+ /* found the proxy */
+ p+=5;
+ for(q=p; *q && *q != ' '; q++);
+ /* allocate a buffer for the proxy information */
+ len=1+(q-p);
+ proxyString=malloc(len*sizeof(TCHAR));
+ if(proxyString==NULL)
+ {
+ getIeHttpProxyError="malloc failed (3)";
+ free(pinfo);
+ return(NULL);
+ }
+ strncpy(proxyString, p, len);
+ proxyString[len]=0;
+ free(pinfo);
+ return(proxyString);
+ }
+ else
+ {
+ /* No http proxy in list */
+ free(pinfo);
+ return("");
+ }
+ }
+ }
+ else
+ {
+ /* InternetQueryOption returned a proxy type we don't know about*/
+ getIeHttpProxyError="Unknown Proxy Type";
+ free(pinfo);
+ return(NULL);
+ }
+}
+#else
+#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
+static void dummy (void) {}
+#endif
+#endif /* WIN32 */
diff --git a/ieproxy.h b/ieproxy.h
new file mode 100644
index 0000000..0786c05
--- /dev/null
+++ b/ieproxy.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2004 Ewan Bhamrah Harley <code@ewan.info>
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __GETIEHTTPPROXY__
+#define __GETIEHTTPPROXY__
+extern LPTSTR getIeHttpProxyError;
+LPCTSTR getIeHttpProxy();
+#endif
diff --git a/images/Makefile.am b/images/Makefile.am
new file mode 100644
index 0000000..334554f
--- /dev/null
+++ b/images/Makefile.am
@@ -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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+images = \
+ install-whirl.bmp \
+ icon.ico
+
+if WIN32
+
+imagedir = $(win32datadir)/images
+dist_image_DATA = $(images)
+
+else
+
+dist_noinst_DATA = $(images)
+
+endif
+
diff --git a/images/Makefile.in b/images/Makefile.in
new file mode 100644
index 0000000..afda84e
--- /dev/null
+++ b/images/Makefile.in
@@ -0,0 +1,418 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = images
+DIST_COMMON = $(am__dist_image_DATA_DIST) $(am__dist_noinst_DATA_DIST) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__dist_image_DATA_DIST = install-whirl.bmp icon.ico
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(imagedir)"
+am__dist_noinst_DATA_DIST = install-whirl.bmp icon.ico
+DATA = $(dist_image_DATA) $(dist_noinst_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+IFCONFIG = @IFCONFIG@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPROUTE = @IPROUTE@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAN2HTML = @MAN2HTML@
+MKDIR_P = @MKDIR_P@
+NETSTAT = @NETSTAT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+ROUTE = @ROUTE@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAP_ID = @TAP_ID@
+TAP_WIN32_MIN_MAJOR = @TAP_WIN32_MIN_MAJOR@
+TAP_WIN32_MIN_MINOR = @TAP_WIN32_MIN_MINOR@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+win32datadir = @win32datadir@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+images = \
+ install-whirl.bmp \
+ icon.ico
+
+@WIN32_TRUE@imagedir = $(win32datadir)/images
+@WIN32_TRUE@dist_image_DATA = $(images)
+@WIN32_FALSE@dist_noinst_DATA = $(images)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu images/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu images/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-dist_imageDATA: $(dist_image_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(imagedir)" || $(MKDIR_P) "$(DESTDIR)$(imagedir)"
+ @list='$(dist_image_DATA)'; test -n "$(imagedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(imagedir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(imagedir)" || exit $$?; \
+ done
+
+uninstall-dist_imageDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_image_DATA)'; test -n "$(imagedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(imagedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(imagedir)" && rm -f $$files
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(imagedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_imageDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_imageDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+ distclean-generic distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-dist_imageDATA install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am uninstall uninstall-am \
+ uninstall-dist_imageDATA
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/images/icon.ico b/images/icon.ico
new file mode 100755
index 0000000..03ea0b1
--- /dev/null
+++ b/images/icon.ico
Binary files differ
diff --git a/images/install-whirl.bmp b/images/install-whirl.bmp
new file mode 100755
index 0000000..03f33fc
--- /dev/null
+++ b/images/install-whirl.bmp
Binary files differ
diff --git a/init.c b/init.c
new file mode 100644
index 0000000..d47a4ef
--- /dev/null
+++ b/init.c
@@ -0,0 +1,3554 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "win32.h"
+#include "init.h"
+#include "sig.h"
+#include "occ.h"
+#include "list.h"
+#include "otime.h"
+#include "pool.h"
+#include "gremlin.h"
+#include "pkcs11.h"
+#include "ps.h"
+#include "lladdr.h"
+#include "ping.h"
+
+#include "memdbg.h"
+
+#include "occ-inline.h"
+
+static struct context *static_context; /* GLOBAL */
+
+/*
+ * Crypto initialization flags
+ */
+#define CF_LOAD_PERSISTED_PACKET_ID (1<<0)
+#define CF_INIT_TLS_MULTI (1<<1)
+#define CF_INIT_TLS_AUTH_STANDALONE (1<<2)
+
+static void do_init_first_time (struct context *c);
+
+void
+context_clear (struct context *c)
+{
+ CLEAR (*c);
+}
+
+void
+context_clear_1 (struct context *c)
+{
+ CLEAR (c->c1);
+}
+
+void
+context_clear_2 (struct context *c)
+{
+ CLEAR (c->c2);
+}
+
+void
+context_clear_all_except_first_time (struct context *c)
+{
+ const bool first_time_save = c->first_time;
+ const struct context_persist cpsave = c->persist;
+ context_clear (c);
+ c->first_time = first_time_save;
+ c->persist = cpsave;
+}
+
+/*
+ * Should be called after options->ce is modified at the top
+ * of a SIGUSR1 restart.
+ */
+static void
+update_options_ce_post (struct options *options)
+{
+#if P2MP
+ /*
+ * In pull mode, we usually import --ping/--ping-restart parameters from
+ * the server. However we should also set an initial default --ping-restart
+ * for the period of time before we pull the --ping-restart parameter
+ * from the server.
+ */
+ if (options->pull
+ && options->ping_rec_timeout_action == PING_UNDEF
+ && options->ce.proto == PROTO_UDPv4)
+ {
+ options->ping_rec_timeout = PRE_PULL_INITIAL_PING_RESTART;
+ options->ping_rec_timeout_action = PING_RESTART;
+ }
+#endif
+#ifdef USE_CRYPTO
+ /*
+ * Don't use replay window for TCP mode (i.e. require that packets be strictly in sequence).
+ */
+ if (link_socket_proto_connection_oriented (options->ce.proto))
+ options->replay_window = options->replay_time = 0;
+#endif
+}
+
+#if HTTP_PROXY_FALLBACK
+
+static bool
+ce_http_proxy_fallback_defined(const struct context *c)
+{
+ const struct connection_list *l = c->options.connection_list;
+ if (l && l->current == 0)
+ {
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ const struct connection_entry *ce = l->array[i];
+ if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+ce_http_proxy_fallback_start(struct context *c, const char *remote_ip_hint)
+{
+ const struct connection_list *l = c->options.connection_list;
+ if (l)
+ {
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ struct connection_entry *ce = l->array[i];
+ if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+ {
+ ce->http_proxy_options = NULL;
+ ce->ce_http_proxy_fallback_timestamp = 0;
+ if (!remote_ip_hint)
+ remote_ip_hint = ce->remote;
+ }
+ }
+ }
+
+ if (management)
+ management_http_proxy_fallback_notify(management, "NEED_LATER", remote_ip_hint);
+}
+
+static bool
+ce_http_proxy_fallback (struct context *c, volatile const struct connection_entry *ce)
+{
+ const int proxy_info_expire = 120; /* seconds before proxy info expires */
+
+ update_time();
+ if (management)
+ {
+ if (!ce->ce_http_proxy_fallback_timestamp)
+ {
+ management_http_proxy_fallback_notify(management, "NEED_NOW", NULL);
+ while (!ce->ce_http_proxy_fallback_timestamp)
+ {
+ management_event_loop_n_seconds (management, 1);
+ if (IS_SIG (c))
+ return false;
+ }
+ }
+ return (now < ce->ce_http_proxy_fallback_timestamp + proxy_info_expire && ce->http_proxy_options);
+ }
+ return false;
+}
+
+static bool
+management_callback_http_proxy_fallback_cmd (void *arg, const char *server, const char *port, const char *flags)
+{
+ struct context *c = (struct context *) arg;
+ const struct connection_list *l = c->options.connection_list;
+ int ret = false;
+ struct http_proxy_options *ho = parse_http_proxy_fallback (c, server, port, flags, M_WARN);
+
+ update_time();
+ if (l)
+ {
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ struct connection_entry *ce = l->array[i];
+ if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+ {
+ ce->http_proxy_options = ho;
+ ce->ce_http_proxy_fallback_timestamp = now;
+ ret = true;
+ }
+ }
+ }
+
+ return ret;
+}
+
+#endif
+
+/*
+ * Initialize and possibly randomize connection list.
+ */
+static void
+init_connection_list (struct context *c)
+{
+#ifdef ENABLE_CONNECTION
+ struct connection_list *l = c->options.connection_list;
+ if (l)
+ {
+ l->current = -1;
+ if (c->options.remote_random)
+ {
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ const int j = get_random () % l->len;
+ if (i != j)
+ {
+ struct connection_entry *tmp;
+ tmp = l->array[i];
+ l->array[i] = l->array[j];
+ l->array[j] = tmp;
+ }
+ }
+ }
+ }
+#endif
+}
+
+#if 0 /* fixme -- disable for production */
+static void
+show_connection_list (const struct connection_list *l)
+{
+ int i;
+ dmsg (M_INFO, "CONNECTION_LIST len=%d current=%d",
+ l->len, l->current);
+ for (i = 0; i < l->len; ++i)
+ {
+ dmsg (M_INFO, "[%d] %s:%d proto=%s http_proxy=%d",
+ i,
+ l->array[i]->remote,
+ l->array[i]->remote_port,
+ proto2ascii(l->array[i]->proto, true),
+ BOOL_CAST(l->array[i]->http_proxy_options));
+ }
+}
+#else
+static inline void
+show_connection_list (const struct connection_list *l)
+{
+}
+#endif
+
+/*
+ * Increment to next connection entry
+ */
+static void
+next_connection_entry (struct context *c)
+{
+#ifdef ENABLE_CONNECTION
+ struct connection_list *l = c->options.connection_list;
+ if (l)
+ {
+ bool ce_defined;
+ struct connection_entry *ce;
+ int n_cycles = 0;
+
+ do {
+ const char *remote_ip_hint = NULL;
+ bool newcycle = false;
+
+ ce_defined = true;
+ if (l->no_advance && l->current >= 0)
+ {
+ l->no_advance = false;
+ }
+ else
+ {
+ if (++l->current >= l->len)
+ {
+ l->current = 0;
+ ++l->n_cycles;
+ if (++n_cycles >= 2)
+ msg (M_FATAL, "No usable connection profiles are present");
+ }
+
+ if (l->current == 0)
+ newcycle = true;
+ show_connection_list(l);
+ }
+
+ ce = l->array[l->current];
+
+ if (c->options.remote_ip_hint && !l->n_cycles)
+ remote_ip_hint = c->options.remote_ip_hint;
+
+#if HTTP_PROXY_FALLBACK
+ if (newcycle && ce_http_proxy_fallback_defined(c))
+ ce_http_proxy_fallback_start(c, remote_ip_hint);
+
+ if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+ {
+ ce_defined = ce_http_proxy_fallback(c, ce);
+ if (IS_SIG (c))
+ break;
+ }
+#endif
+
+ if (ce->flags & CE_DISABLED)
+ ce_defined = false;
+
+ c->options.ce = *ce;
+
+ if (remote_ip_hint)
+ c->options.ce.remote = remote_ip_hint;
+
+#if 0 /* fixme -- disable for production, this code simulates a network where proxy fallback is the only method to reach the OpenVPN server */
+ if (!(c->options.ce.flags & CE_HTTP_PROXY_FALLBACK))
+ {
+ c->options.ce.remote = "10.10.0.1"; /* use an unreachable address here */
+ }
+#endif
+ } while (!ce_defined);
+ }
+#endif
+ update_options_ce_post (&c->options);
+}
+
+/*
+ * Query for private key and auth-user-pass username/passwords
+ */
+static void
+init_query_passwords (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ /* Certificate password input */
+ if (c->options.key_pass_file)
+ pem_password_setup (c->options.key_pass_file);
+#endif
+
+#if P2MP
+ /* Auth user/pass input */
+ if (c->options.auth_user_pass_file)
+ auth_user_pass_setup (c->options.auth_user_pass_file);
+#endif
+}
+
+/*
+ * Initialize/Uninitialize HTTP or SOCKS proxy
+ */
+
+#ifdef GENERAL_PROXY_SUPPORT
+
+static int
+proxy_scope (struct context *c)
+{
+ return connection_list_defined (&c->options) ? 2 : 1;
+}
+
+static void
+uninit_proxy_dowork (struct context *c)
+{
+#ifdef ENABLE_HTTP_PROXY
+ if (c->c1.http_proxy_owned && c->c1.http_proxy)
+ {
+ http_proxy_close (c->c1.http_proxy);
+ c->c1.http_proxy = NULL;
+ c->c1.http_proxy_owned = false;
+ }
+#endif
+#ifdef ENABLE_SOCKS
+ if (c->c1.socks_proxy_owned && c->c1.socks_proxy)
+ {
+ socks_proxy_close (c->c1.socks_proxy);
+ c->c1.socks_proxy = NULL;
+ c->c1.socks_proxy_owned = false;
+ }
+#endif
+}
+
+static void
+init_proxy_dowork (struct context *c)
+{
+#ifdef ENABLE_HTTP_PROXY
+ bool did_http = false;
+#else
+ const bool did_http = false;
+#endif
+
+ uninit_proxy_dowork (c);
+
+#ifdef ENABLE_HTTP_PROXY
+ if (c->options.ce.http_proxy_options || c->options.auto_proxy_info)
+ {
+ /* Possible HTTP proxy user/pass input */
+ c->c1.http_proxy = http_proxy_new (c->options.ce.http_proxy_options,
+ c->options.auto_proxy_info);
+ if (c->c1.http_proxy)
+ {
+ did_http = true;
+ c->c1.http_proxy_owned = true;
+ }
+ }
+#endif
+
+#ifdef ENABLE_SOCKS
+ if (!did_http && (c->options.ce.socks_proxy_server || c->options.auto_proxy_info))
+ {
+ c->c1.socks_proxy = socks_proxy_new (c->options.ce.socks_proxy_server,
+ c->options.ce.socks_proxy_port,
+ c->options.ce.socks_proxy_authfile,
+ c->options.ce.socks_proxy_retry,
+ c->options.auto_proxy_info);
+ if (c->c1.socks_proxy)
+ {
+ c->c1.socks_proxy_owned = true;
+ }
+ }
+#endif
+}
+
+static void
+init_proxy (struct context *c, const int scope)
+{
+ if (scope == proxy_scope (c))
+ init_proxy_dowork (c);
+}
+
+static void
+uninit_proxy (struct context *c)
+{
+ if (c->sig->signal_received != SIGUSR1 || proxy_scope (c) == 2)
+ uninit_proxy_dowork (c);
+}
+
+#else
+
+static inline void
+init_proxy (struct context *c, const int scope)
+{
+}
+
+static inline void
+uninit_proxy (struct context *c)
+{
+}
+
+#endif
+
+void
+context_init_1 (struct context *c)
+{
+ context_clear_1 (c);
+
+ packet_id_persist_init (&c->c1.pid_persist);
+
+ init_connection_list (c);
+
+ init_query_passwords (c);
+
+#if defined(ENABLE_PKCS11)
+ if (c->first_time) {
+ int i;
+ pkcs11_initialize (true, c->options.pkcs11_pin_cache_period);
+ for (i=0;i<MAX_PARMS && c->options.pkcs11_providers[i] != NULL;i++)
+ pkcs11_addProvider (c->options.pkcs11_providers[i], c->options.pkcs11_protected_authentication[i],
+ c->options.pkcs11_private_mode[i], c->options.pkcs11_cert_private[i]);
+ }
+#endif
+
+#if 0 /* test get_user_pass with GET_USER_PASS_NEED_OK flag */
+ {
+ /*
+ * In the management interface, you can okay the request by entering "needok token-insertion-request ok"
+ */
+ struct user_pass up;
+ CLEAR (up);
+ strcpy (up.username, "Please insert your cryptographic token"); /* put the high-level message in up.username */
+ get_user_pass (&up, NULL, "token-insertion-request", GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_OK);
+ msg (M_INFO, "RET:%s", up.password); /* will return the third argument to management interface
+ 'needok' command, usually 'ok' or 'cancel'. */
+ }
+#endif
+
+ /* initialize HTTP or SOCKS proxy object at scope level 1 */
+ init_proxy (c, 1);
+}
+
+void
+context_gc_free (struct context *c)
+{
+ gc_free (&c->c2.gc);
+ gc_free (&c->options.gc);
+ gc_free (&c->gc);
+}
+
+#if PORT_SHARE
+
+static void
+close_port_share (void)
+{
+ if (port_share)
+ {
+ port_share_close (port_share);
+ port_share = NULL;
+ }
+}
+
+static void
+init_port_share (struct context *c)
+{
+ if (!port_share && (c->options.port_share_host && c->options.port_share_port))
+ {
+ port_share = port_share_open (c->options.port_share_host,
+ c->options.port_share_port);
+ if (port_share == NULL)
+ msg (M_FATAL, "Fatal error: Port sharing failed");
+ }
+}
+
+#endif
+
+bool
+init_static (void)
+{
+ /* configure_path (); */
+
+#if defined(USE_CRYPTO) && defined(DMALLOC)
+ openssl_dmalloc_init ();
+#endif
+
+ init_random_seed (); /* init random() function, only used as
+ source for weak random numbers */
+ error_reset (); /* initialize error.c */
+ reset_check_status (); /* initialize status check code in socket.c */
+
+#ifdef WIN32
+ init_win32 ();
+#endif
+
+#ifdef OPENVPN_DEBUG_COMMAND_LINE
+ {
+ int i;
+ for (i = 0; i < argc; ++i)
+ msg (M_INFO, "argv[%d] = '%s'", i, argv[i]);
+ }
+#endif
+
+ update_time ();
+
+#ifdef USE_CRYPTO
+ init_ssl_lib ();
+
+ /* init PRNG used for IV generation */
+ /* When forking, copy this to more places in the code to avoid fork
+ random-state predictability */
+ prng_init (NULL, 0);
+#endif
+
+#ifdef PID_TEST
+ packet_id_interactive_test (); /* test the sequence number code */
+ return false;
+#endif
+
+#ifdef SCHEDULE_TEST
+ schedule_test ();
+ return false;
+#endif
+
+#ifdef LIST_TEST
+ list_test ();
+ return false;
+#endif
+
+#ifdef IFCONFIG_POOL_TEST
+ ifconfig_pool_test (0x0A010004, 0x0A0100FF);
+ return false;
+#endif
+
+#ifdef CHARACTER_CLASS_DEBUG
+ character_class_debug ();
+ return false;
+#endif
+
+#ifdef EXTRACT_X509_FIELD_TEST
+ extract_x509_field_test ();
+ return false;
+#endif
+
+#ifdef TIME_TEST
+ time_test ();
+ return false;
+#endif
+
+#ifdef GEN_PATH_TEST
+ {
+ struct gc_arena gc = gc_new ();
+ const char *fn = gen_path ("foo",
+ "bar",
+ &gc);
+ printf ("%s\n", fn);
+ gc_free (&gc);
+ }
+ return false;
+#endif
+
+#ifdef STATUS_PRINTF_TEST
+ {
+ struct gc_arena gc = gc_new ();
+ const char *tmp_file = create_temp_file ("/tmp", "foo", &gc);
+ struct status_output *so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
+ status_printf (so, "%s", "foo");
+ status_printf (so, "%s", "bar");
+ if (!status_close (so))
+ msg (M_WARN, "STATUS_PRINTF_TEST: %s: write error", tmp_file);
+ gc_free (&gc);
+ }
+ return false;
+#endif
+
+#ifdef ARGV_TEST
+ {
+ void argv_test (void);
+ argv_test ();
+ return false;
+ }
+#endif
+
+#ifdef PRNG_TEST
+ {
+ struct gc_arena gc = gc_new ();
+ uint8_t rndbuf[8];
+ int i;
+ prng_init ("sha1", 16);
+ //prng_init (NULL, 0);
+ const int factor = 1;
+ for (i = 0; i < factor * 8; ++i)
+ {
+#if 1
+ prng_bytes (rndbuf, sizeof (rndbuf));
+#else
+ ASSERT(RAND_bytes (rndbuf, sizeof (rndbuf)));
+#endif
+ printf ("[%d] %s\n", i, format_hex (rndbuf, sizeof (rndbuf), 0, &gc));
+ }
+ gc_free (&gc);
+ prng_uninit ();
+ return false;
+ }
+#endif
+
+#ifdef BUFFER_LIST_AGGREGATE_TEST
+ /* test buffer_list_aggregate function */
+ {
+ static const char *text[] = {
+ "It was a bright cold day in April, ",
+ "and the clocks were striking ",
+ "thirteen. ",
+ "Winston Smith, ",
+ "his chin nuzzled into his breast in an ",
+ "effort to escape the vile wind, ",
+ "slipped quickly through the glass doors ",
+ "of Victory Mansions, though not quickly ",
+ "enough to prevent a swirl of gritty dust from ",
+ "entering along with him."
+ };
+
+ int iter, listcap;
+ for (listcap = 0; listcap < 12; ++listcap)
+ {
+ for (iter = 0; iter < 512; ++iter)
+ {
+ struct buffer_list *bl = buffer_list_new(listcap);
+ {
+ int i;
+ for (i = 0; i < SIZE(text); ++i)
+ buffer_list_push(bl, (unsigned char *)text[i]);
+ }
+ printf("[cap=%d i=%d] *************************\n", listcap, iter);
+ if (!(iter & 8))
+ buffer_list_aggregate(bl, iter/2);
+ if (!(iter & 16))
+ buffer_list_push(bl, (unsigned char *)"Even more text...");
+ buffer_list_aggregate(bl, iter);
+ if (!(iter & 1))
+ buffer_list_push(bl, (unsigned char *)"More text...");
+ {
+ struct buffer *buf;
+ while ((buf = buffer_list_peek(bl)))
+ {
+ int c;
+ printf ("'");
+ while ((c = buf_read_u8(buf)) >= 0)
+ putchar(c);
+ printf ("'\n");
+ buffer_list_advance(bl, 0);
+ }
+ }
+ buffer_list_free(bl);
+ }
+ }
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void
+uninit_static (void)
+{
+#ifdef USE_CRYPTO
+ free_ssl_lib ();
+#endif
+
+#ifdef ENABLE_PKCS11
+ pkcs11_terminate ();
+#endif
+
+#if PORT_SHARE
+ close_port_share ();
+#endif
+
+#if defined(MEASURE_TLS_HANDSHAKE_STATS) && defined(USE_CRYPTO) && defined(USE_SSL)
+ show_tls_performance_stats ();
+#endif
+}
+
+void
+init_verb_mute (struct context *c, unsigned int flags)
+{
+ if (flags & IVM_LEVEL_1)
+ {
+ /* set verbosity and mute levels */
+ set_check_status (D_LINK_ERRORS, D_READ_WRITE);
+ set_debug_level (c->options.verbosity, SDL_CONSTRAIN);
+ set_mute_cutoff (c->options.mute);
+ }
+
+ /* special D_LOG_RW mode */
+ if (flags & IVM_LEVEL_2)
+ c->c2.log_rw = (check_debug_level (D_LOG_RW) && !check_debug_level (D_LOG_RW + 1));
+}
+
+/*
+ * Possibly set --dev based on --dev-node.
+ * For example, if --dev-node /tmp/foo/tun, and --dev undefined,
+ * set --dev to tun.
+ */
+void
+init_options_dev (struct options *options)
+{
+ if (!options->dev)
+ options->dev = openvpn_basename (options->dev_node);
+}
+
+bool
+print_openssl_info (const struct options *options)
+{
+ /*
+ * OpenSSL info print mode?
+ */
+#ifdef USE_CRYPTO
+ if (options->show_ciphers || options->show_digests || options->show_engines
+#ifdef USE_SSL
+ || options->show_tls_ciphers
+#endif
+ )
+ {
+ if (options->show_ciphers)
+ show_available_ciphers ();
+ if (options->show_digests)
+ show_available_digests ();
+ if (options->show_engines)
+ show_available_engines ();
+#ifdef USE_SSL
+ if (options->show_tls_ciphers)
+ show_available_tls_ciphers ();
+#endif
+ return true;
+ }
+#endif
+ return false;
+}
+
+/*
+ * Static pre-shared key generation mode?
+ */
+bool
+do_genkey (const struct options * options)
+{
+#ifdef USE_CRYPTO
+ if (options->genkey)
+ {
+ int nbits_written;
+
+ notnull (options->shared_secret_file,
+ "shared secret output file (--secret)");
+
+ if (options->mlock) /* should we disable paging? */
+ do_mlockall (true);
+
+ nbits_written = write_key_file (2, options->shared_secret_file);
+
+ msg (D_GENKEY | M_NOPREFIX,
+ "Randomly generated %d bit key written to %s", nbits_written,
+ options->shared_secret_file);
+ return true;
+ }
+#endif
+ return false;
+}
+
+/*
+ * Persistent TUN/TAP device management mode?
+ */
+bool
+do_persist_tuntap (const struct options *options)
+{
+#ifdef TUNSETPERSIST
+ if (options->persist_config)
+ {
+ /* sanity check on options for --mktun or --rmtun */
+ notnull (options->dev, "TUN/TAP device (--dev)");
+ if (options->ce.remote || options->ifconfig_local
+ || options->ifconfig_remote_netmask
+#ifdef USE_CRYPTO
+ || options->shared_secret_file
+#ifdef USE_SSL
+ || options->tls_server || options->tls_client
+#endif
+#endif
+ )
+ msg (M_FATAL|M_OPTERR,
+ "options --mktun or --rmtun should only be used together with --dev");
+ tuncfg (options->dev, options->dev_type, options->dev_node,
+ options->tun_ipv6, options->persist_mode,
+ options->username, options->groupname, &options->tuntap_options);
+ if (options->persist_mode && options->lladdr)
+ set_lladdr(options->dev, options->lladdr, NULL);
+ return true;
+ }
+#endif
+ return false;
+}
+
+/*
+ * Should we become a daemon?
+ * Return true if we did it.
+ */
+static bool
+possibly_become_daemon (const struct options *options, const bool first_time)
+{
+ bool ret = false;
+ if (first_time && options->daemon)
+ {
+ ASSERT (!options->inetd);
+ if (daemon (options->cd_dir != NULL, options->log) < 0)
+ msg (M_ERR, "daemon() failed");
+ restore_signal_state ();
+ if (options->log)
+ set_std_files_to_null (true);
+
+#if defined(ENABLE_PKCS11)
+ pkcs11_forkFixup ();
+#endif
+
+ ret = true;
+ }
+ return ret;
+}
+
+/*
+ * Actually do UID/GID downgrade, chroot and SELinux context switching, if requested.
+ */
+static void
+do_uid_gid_chroot (struct context *c, bool no_delay)
+{
+ static const char why_not[] = "will be delayed because of --client, --pull, or --up-delay";
+ struct context_0 *c0 = c->c0;
+
+ if (c->first_time && c0 && !c0->uid_gid_set)
+ {
+ /* chroot if requested */
+ if (c->options.chroot_dir)
+ {
+ if (no_delay)
+ do_chroot (c->options.chroot_dir);
+ else
+ msg (M_INFO, "NOTE: chroot %s", why_not);
+ }
+
+ /* set user and/or group that we want to setuid/setgid to */
+ if (no_delay)
+ {
+ set_group (&c0->group_state);
+ set_user (&c0->user_state);
+ c0->uid_gid_set = true;
+ }
+ else if (c0->uid_gid_specified)
+ {
+ msg (M_INFO, "NOTE: UID/GID downgrade %s", why_not);
+ }
+
+#ifdef HAVE_SETCON
+ /* Apply a SELinux context in order to restrict what OpenVPN can do
+ * to _only_ what it is supposed to do after initialization is complete
+ * (basically just network I/O operations). Doing it after chroot
+ * requires /proc to be mounted in the chroot (which is annoying indeed
+ * but doing it before requires more complex SELinux policies.
+ */
+ if (c->options.selinux_context)
+ {
+ if (no_delay) {
+ if (-1 == setcon (c->options.selinux_context))
+ msg (M_ERR, "setcon to '%s' failed; is /proc accessible?", c->options.selinux_context);
+ else
+ msg (M_INFO, "setcon to '%s' succeeded", c->options.selinux_context);
+ }
+ else
+ msg (M_INFO, "NOTE: setcon %s", why_not);
+ }
+#endif
+ }
+}
+
+/*
+ * Return common name in a way that is formatted for
+ * prepending to msg() output.
+ */
+const char *
+format_common_name (struct context *c, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ if (c->c2.tls_multi)
+ {
+ buf_printf (&out, "[%s] ", tls_common_name (c->c2.tls_multi, false));
+ }
+#endif
+ return BSTR (&out);
+}
+
+void
+pre_setup (const struct options *options)
+{
+#ifdef WIN32
+ if (options->exit_event_name)
+ {
+ win32_signal_open (&win32_signal,
+ WSO_FORCE_SERVICE,
+ options->exit_event_name,
+ options->exit_event_initial_state);
+ }
+ else
+ {
+ win32_signal_open (&win32_signal,
+ WSO_FORCE_CONSOLE,
+ NULL,
+ false);
+
+ /* put a title on the top window bar */
+ if (win32_signal.mode == WSO_MODE_CONSOLE)
+ {
+ window_title_save (&window_title);
+ window_title_generate (options->config);
+ }
+ }
+#endif
+}
+
+void
+reset_coarse_timers (struct context *c)
+{
+ c->c2.coarse_timer_wakeup = 0;
+}
+
+/*
+ * Initialize timers
+ */
+static void
+do_init_timers (struct context *c, bool deferred)
+{
+ update_time ();
+ reset_coarse_timers (c);
+
+ /* initialize inactivity timeout */
+ if (c->options.inactivity_timeout)
+ event_timeout_init (&c->c2.inactivity_interval, c->options.inactivity_timeout, now);
+
+ /* initialize pings */
+
+ if (c->options.ping_send_timeout)
+ event_timeout_init (&c->c2.ping_send_interval, c->options.ping_send_timeout, 0);
+
+ if (c->options.ping_rec_timeout)
+ event_timeout_init (&c->c2.ping_rec_interval, c->options.ping_rec_timeout, now);
+
+#if P2MP
+ if (c->options.server_poll_timeout)
+ event_timeout_init (&c->c2.server_poll_interval, c->options.server_poll_timeout, now);
+#endif
+
+ if (!deferred)
+ {
+ /* initialize connection establishment timer */
+ event_timeout_init (&c->c2.wait_for_connect, 1, now);
+
+#ifdef ENABLE_OCC
+ /* initialize occ timers */
+
+ if (c->options.occ
+ && !TLS_MODE (c)
+ && c->c2.options_string_local && c->c2.options_string_remote)
+ event_timeout_init (&c->c2.occ_interval, OCC_INTERVAL_SECONDS, now);
+
+ if (c->options.mtu_test)
+ event_timeout_init (&c->c2.occ_mtu_load_test_interval, OCC_MTU_LOAD_INTERVAL_SECONDS, now);
+#endif
+
+ /* initialize packet_id persistence timer */
+#ifdef USE_CRYPTO
+ if (c->options.packet_id_file)
+ event_timeout_init (&c->c2.packet_id_persist_interval, 60, now);
+#endif
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ /* initialize tmp_int optimization that limits the number of times we call
+ tls_multi_process in the main event loop */
+ interval_init (&c->c2.tmp_int, TLS_MULTI_HORIZON, TLS_MULTI_REFRESH);
+#endif
+ }
+}
+
+/*
+ * Initialize traffic shaper.
+ */
+static void
+do_init_traffic_shaper (struct context *c)
+{
+#ifdef HAVE_GETTIMEOFDAY
+ /* initialize traffic shaper (i.e. transmit bandwidth limiter) */
+ if (c->options.shaper)
+ {
+ shaper_init (&c->c2.shaper, c->options.shaper);
+ shaper_msg (&c->c2.shaper);
+ }
+#endif
+}
+
+/*
+ * Allocate a route list structure if at least one
+ * --route option was specified.
+ */
+static void
+do_alloc_route_list (struct context *c)
+{
+ if (c->options.routes && !c->c1.route_list)
+ c->c1.route_list = new_route_list (c->options.max_routes, &c->gc);
+}
+
+
+/*
+ * Initialize the route list, resolving any DNS names in route
+ * options and saving routes in the environment.
+ */
+static void
+do_init_route_list (const struct options *options,
+ struct route_list *route_list,
+ const struct link_socket_info *link_socket_info,
+ bool fatal,
+ struct env_set *es)
+{
+ const char *gw = NULL;
+ int dev = dev_type_enum (options->dev, options->dev_type);
+ int metric = 0;
+
+ if (dev == DEV_TYPE_TUN && (options->topology == TOP_NET30 || options->topology == TOP_P2P))
+ gw = options->ifconfig_remote_netmask;
+ if (options->route_default_gateway)
+ gw = options->route_default_gateway;
+ if (options->route_default_metric)
+ metric = options->route_default_metric;
+
+ if (!init_route_list (route_list,
+ options->routes,
+ gw,
+ metric,
+ link_socket_current_remote (link_socket_info),
+ es))
+ {
+ if (fatal)
+ openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
+ }
+ else
+ {
+ /* copy routes to environment */
+ setenv_routes (es, route_list);
+ }
+}
+
+/*
+ * Called after all initialization has been completed.
+ */
+void
+initialization_sequence_completed (struct context *c, const unsigned int flags)
+{
+ static const char message[] = "Initialization Sequence Completed";
+
+ /* If we delayed UID/GID downgrade or chroot, do it now */
+ do_uid_gid_chroot (c, true);
+
+ /* Test if errors */
+ if (flags & ISC_ERRORS)
+ {
+#ifdef WIN32
+ show_routes (M_INFO|M_NOPREFIX);
+ show_adapters (M_INFO|M_NOPREFIX);
+ msg (M_INFO, "%s With Errors ( see http://openvpn.net/faq.html#dhcpclientserv )", message);
+#else
+ msg (M_INFO, "%s With Errors", message);
+#endif
+ }
+ else
+ msg (M_INFO, "%s", message);
+
+ /* Flag connection_list that we initialized */
+ if ((flags & (ISC_ERRORS|ISC_SERVER)) == 0 && connection_list_defined (&c->options))
+ connection_list_set_no_advance (&c->options);
+
+#ifdef WIN32
+ fork_register_dns_action (c->c1.tuntap);
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+ /* Tell management interface that we initialized */
+ if (management)
+ {
+ in_addr_t tun_local = 0;
+ in_addr_t tun_remote = 0; /* FKS */
+ const char *detail = "SUCCESS";
+ if (c->c1.tuntap)
+ tun_local = c->c1.tuntap->local;
+ tun_remote = htonl (c->c1.link_socket_addr.actual.dest.sa.sin_addr.s_addr);
+ if (flags & ISC_ERRORS)
+ detail = "ERROR";
+ management_set_state (management,
+ OPENVPN_STATE_CONNECTED,
+ detail,
+ tun_local,
+ tun_remote);
+ if (tun_local)
+ management_post_tunnel_open (management, tun_local);
+ }
+#endif
+
+}
+
+/*
+ * Possibly add routes and/or call route-up script
+ * based on options.
+ */
+void
+do_route (const struct options *options,
+ struct route_list *route_list,
+ const struct tuntap *tt,
+ const struct plugin_list *plugins,
+ struct env_set *es)
+{
+ if (!options->route_noexec && route_list)
+ add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
+
+ if (plugin_defined (plugins, OPENVPN_PLUGIN_ROUTE_UP))
+ {
+ if (plugin_call (plugins, OPENVPN_PLUGIN_ROUTE_UP, NULL, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ msg (M_WARN, "WARNING: route-up plugin call failed");
+ }
+
+ if (options->route_script)
+ {
+ struct argv argv = argv_new ();
+ setenv_str (es, "script_type", "route-up");
+ argv_printf (&argv, "%sc", options->route_script);
+ openvpn_run_script (&argv, es, 0, "--route-up");
+ argv_reset (&argv);
+ }
+
+#ifdef WIN32
+ if (options->show_net_up)
+ {
+ show_routes (M_INFO|M_NOPREFIX);
+ show_adapters (M_INFO|M_NOPREFIX);
+ }
+ else if (check_debug_level (D_SHOW_NET))
+ {
+ show_routes (D_SHOW_NET|M_NOPREFIX);
+ show_adapters (D_SHOW_NET|M_NOPREFIX);
+ }
+#endif
+}
+
+/*
+ * Save current pulled options string in the c1 context store, so we can
+ * compare against it after possible future restarts.
+ */
+#if P2MP
+static void
+save_pulled_options_digest (struct context *c, const struct md5_digest *newdigest)
+{
+ if (newdigest)
+ c->c1.pulled_options_digest_save = *newdigest;
+ else
+ md5_digest_clear (&c->c1.pulled_options_digest_save);
+}
+#endif
+
+/*
+ * initialize tun/tap device object
+ */
+static void
+do_init_tun (struct context *c)
+{
+ c->c1.tuntap = init_tun (c->options.dev,
+ c->options.dev_type,
+ c->options.topology,
+ c->options.ifconfig_local,
+ c->options.ifconfig_remote_netmask,
+ addr_host (&c->c1.link_socket_addr.local),
+ addr_host (&c->c1.link_socket_addr.remote),
+ !c->options.ifconfig_nowarn,
+ c->c2.es);
+
+ init_tun_post (c->c1.tuntap,
+ &c->c2.frame,
+ &c->options.tuntap_options);
+
+ c->c1.tuntap_owned = true;
+}
+
+/*
+ * Open tun/tap device, ifconfig, call up script, etc.
+ */
+
+static bool
+do_open_tun (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+ c->c2.ipv4_tun = (!c->options.tun_ipv6
+ && is_dev_type (c->options.dev, c->options.dev_type, "tun"));
+
+ if (!c->c1.tuntap)
+ {
+ /* initialize (but do not open) tun/tap object */
+ do_init_tun (c);
+
+ /* allocate route list structure */
+ do_alloc_route_list (c);
+
+ /* parse and resolve the route option list */
+ if (c->options.routes && c->c1.route_list && c->c2.link_socket)
+ do_init_route_list (&c->options, c->c1.route_list, &c->c2.link_socket->info, false, c->c2.es);
+
+ /* do ifconfig */
+ if (!c->options.ifconfig_noexec
+ && ifconfig_order () == IFCONFIG_BEFORE_TUN_OPEN)
+ {
+ /* guess actual tun/tap unit number that will be returned
+ by open_tun */
+ const char *guess = guess_tuntap_dev (c->options.dev,
+ c->options.dev_type,
+ c->options.dev_node,
+ &gc);
+ do_ifconfig (c->c1.tuntap, guess, TUN_MTU_SIZE (&c->c2.frame), c->c2.es);
+ }
+
+ /* open the tun device */
+ open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
+ c->options.tun_ipv6, c->c1.tuntap);
+
+ /* set the hardware address */
+ if (c->options.lladdr)
+ set_lladdr(c->c1.tuntap->actual_name, c->options.lladdr, c->c2.es);
+
+ /* do ifconfig */
+ if (!c->options.ifconfig_noexec
+ && ifconfig_order () == IFCONFIG_AFTER_TUN_OPEN)
+ {
+ do_ifconfig (c->c1.tuntap, c->c1.tuntap->actual_name, TUN_MTU_SIZE (&c->c2.frame), c->c2.es);
+ }
+
+ /* run the up script */
+ run_up_down (c->options.up_script,
+ c->plugins,
+ OPENVPN_PLUGIN_UP,
+ c->c1.tuntap->actual_name,
+ TUN_MTU_SIZE (&c->c2.frame),
+ EXPANDED_SIZE (&c->c2.frame),
+ print_in_addr_t (c->c1.tuntap->local, IA_EMPTY_IF_UNDEF, &gc),
+ print_in_addr_t (c->c1.tuntap->remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
+ "init",
+ NULL,
+ "up",
+ c->c2.es);
+
+ /* possibly add routes */
+ if (!c->options.route_delay_defined)
+ do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->plugins, c->c2.es);
+
+ /*
+ * Did tun/tap driver give us an MTU?
+ */
+ if (c->c1.tuntap->post_open_mtu)
+ frame_set_mtu_dynamic (&c->c2.frame,
+ c->c1.tuntap->post_open_mtu,
+ SET_MTU_TUN | SET_MTU_UPPER_BOUND);
+
+ ret = true;
+ static_context = c;
+ }
+ else
+ {
+ msg (M_INFO, "Preserving previous TUN/TAP instance: %s",
+ c->c1.tuntap->actual_name);
+
+ /* run the up script if user specified --up-restart */
+ if (c->options.up_restart)
+ run_up_down (c->options.up_script,
+ c->plugins,
+ OPENVPN_PLUGIN_UP,
+ c->c1.tuntap->actual_name,
+ TUN_MTU_SIZE (&c->c2.frame),
+ EXPANDED_SIZE (&c->c2.frame),
+ print_in_addr_t (c->c1.tuntap->local, IA_EMPTY_IF_UNDEF, &gc),
+ print_in_addr_t (c->c1.tuntap->remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
+ "restart",
+ NULL,
+ "up",
+ c->c2.es);
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Close TUN/TAP device
+ */
+
+static void
+do_close_tun_simple (struct context *c)
+{
+ msg (D_CLOSE, "Closing TUN/TAP interface");
+ close_tun (c->c1.tuntap);
+ c->c1.tuntap = NULL;
+ c->c1.tuntap_owned = false;
+#if P2MP
+ save_pulled_options_digest (c, NULL); /* delete C1-saved pulled_options_digest */
+#endif
+}
+
+static void
+do_close_tun (struct context *c, bool force)
+{
+ struct gc_arena gc = gc_new ();
+ if (c->c1.tuntap && c->c1.tuntap_owned)
+ {
+ const char *tuntap_actual = string_alloc (c->c1.tuntap->actual_name, &gc);
+ const in_addr_t local = c->c1.tuntap->local;
+ const in_addr_t remote_netmask = c->c1.tuntap->remote_netmask;
+
+ if (force || !(c->sig->signal_received == SIGUSR1 && c->options.persist_tun))
+ {
+ static_context = NULL;
+
+#ifdef ENABLE_MANAGEMENT
+ /* tell management layer we are about to close the TUN/TAP device */
+ if (management)
+ management_pre_tunnel_close (management);
+#endif
+
+ /* delete any routes we added */
+ if (c->c1.route_list)
+ delete_routes (c->c1.route_list, c->c1.tuntap, ROUTE_OPTION_FLAGS (&c->options), c->c2.es);
+
+ /* actually close tun/tap device based on --down-pre flag */
+ if (!c->options.down_pre)
+ do_close_tun_simple (c);
+
+ /* Run the down script -- note that it will run at reduced
+ privilege if, for example, "--user nobody" was used. */
+ run_up_down (c->options.down_script,
+ c->plugins,
+ OPENVPN_PLUGIN_DOWN,
+ tuntap_actual,
+ TUN_MTU_SIZE (&c->c2.frame),
+ EXPANDED_SIZE (&c->c2.frame),
+ print_in_addr_t (local, IA_EMPTY_IF_UNDEF, &gc),
+ print_in_addr_t (remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
+ "init",
+ signal_description (c->sig->signal_received,
+ c->sig->signal_text),
+ "down",
+ c->c2.es);
+
+ /* actually close tun/tap device based on --down-pre flag */
+ if (c->options.down_pre)
+ do_close_tun_simple (c);
+ }
+ else
+ {
+ /* run the down script on this restart if --up-restart was specified */
+ if (c->options.up_restart)
+ run_up_down (c->options.down_script,
+ c->plugins,
+ OPENVPN_PLUGIN_DOWN,
+ tuntap_actual,
+ TUN_MTU_SIZE (&c->c2.frame),
+ EXPANDED_SIZE (&c->c2.frame),
+ print_in_addr_t (local, IA_EMPTY_IF_UNDEF, &gc),
+ print_in_addr_t (remote_netmask, IA_EMPTY_IF_UNDEF, &gc),
+ "restart",
+ signal_description (c->sig->signal_received,
+ c->sig->signal_text),
+ "down",
+ c->c2.es);
+ }
+ }
+ gc_free (&gc);
+}
+
+void
+tun_abort()
+{
+ struct context *c = static_context;
+ if (c)
+ {
+ static_context = NULL;
+ do_close_tun (c, true);
+ }
+}
+
+/*
+ * Handle delayed tun/tap interface bringup due to --up-delay or --pull
+ */
+
+void
+do_up (struct context *c, bool pulled_options, unsigned int option_types_found)
+{
+ if (!c->c2.do_up_ran)
+ {
+ reset_coarse_timers (c);
+
+ if (pulled_options && option_types_found)
+ do_deferred_options (c, option_types_found);
+
+ /* if --up-delay specified, open tun, do ifconfig, and run up script now */
+ if (c->options.up_delay || PULL_DEFINED (&c->options))
+ {
+ c->c2.did_open_tun = do_open_tun (c);
+ update_time ();
+
+#if P2MP
+ /*
+ * Was tun interface object persisted from previous restart iteration,
+ * and if so did pulled options string change from previous iteration?
+ */
+ if (!c->c2.did_open_tun
+ && PULL_DEFINED (&c->options)
+ && c->c1.tuntap
+ && (!md5_digest_defined (&c->c1.pulled_options_digest_save) || !md5_digest_defined (&c->c2.pulled_options_digest)
+ || !md5_digest_equal (&c->c1.pulled_options_digest_save, &c->c2.pulled_options_digest)))
+ {
+ /* if so, close tun, delete routes, then reinitialize tun and add routes */
+ msg (M_INFO, "NOTE: Pulled options changed on restart, will need to close and reopen TUN/TAP device.");
+ do_close_tun (c, true);
+ openvpn_sleep (1);
+ c->c2.did_open_tun = do_open_tun (c);
+ update_time ();
+ }
+#endif
+ }
+
+ if (c->c2.did_open_tun)
+ {
+#if P2MP
+ save_pulled_options_digest (c, &c->c2.pulled_options_digest);
+#endif
+
+ /* if --route-delay was specified, start timer */
+ if (c->options.route_delay_defined)
+ {
+ event_timeout_init (&c->c2.route_wakeup, c->options.route_delay, now);
+ event_timeout_init (&c->c2.route_wakeup_expire, c->options.route_delay + c->options.route_delay_window, now);
+ if (c->c1.tuntap)
+ tun_standby_init (c->c1.tuntap);
+ }
+ else
+ {
+ initialization_sequence_completed (c, 0); /* client/p2p --route-delay undefined */
+ }
+ }
+ else if (c->options.mode == MODE_POINT_TO_POINT)
+ {
+ initialization_sequence_completed (c, 0); /* client/p2p restart with --persist-tun */
+ }
+
+ c->c2.do_up_ran = true;
+ }
+}
+
+/*
+ * These are the option categories which will be accepted by pull.
+ */
+unsigned int
+pull_permission_mask (const struct context *c)
+{
+ unsigned int flags =
+ OPT_P_UP
+ | OPT_P_ROUTE_EXTRAS
+ | OPT_P_IPWIN32
+ | OPT_P_SOCKBUF
+ | OPT_P_SOCKFLAGS
+ | OPT_P_SETENV
+ | OPT_P_SHAPER
+ | OPT_P_TIMER
+ | OPT_P_COMP
+ | OPT_P_PERSIST
+ | OPT_P_MESSAGES
+ | OPT_P_EXPLICIT_NOTIFY
+ | OPT_P_ECHO
+ | OPT_P_PULL_MODE;
+
+ if (!c->options.route_nopull)
+ flags |= OPT_P_ROUTE;
+
+ return flags;
+}
+
+/*
+ * Handle non-tun-related pulled options.
+ */
+void
+do_deferred_options (struct context *c, const unsigned int found)
+{
+ if (found & OPT_P_MESSAGES)
+ {
+ init_verb_mute (c, IVM_LEVEL_1|IVM_LEVEL_2);
+ msg (D_PUSH, "OPTIONS IMPORT: --verb and/or --mute level changed");
+ }
+ if (found & OPT_P_TIMER)
+ {
+ do_init_timers (c, true);
+ msg (D_PUSH, "OPTIONS IMPORT: timers and/or timeouts modified");
+ }
+
+#ifdef ENABLE_OCC
+ if (found & OPT_P_EXPLICIT_NOTIFY)
+ {
+ if (c->options.ce.proto != PROTO_UDPv4 && c->options.explicit_exit_notification)
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: --explicit-exit-notify can only be used with --proto udp");
+ c->options.explicit_exit_notification = 0;
+ }
+ else
+ msg (D_PUSH, "OPTIONS IMPORT: explicit notify parm(s) modified");
+ }
+#endif
+
+#ifdef USE_LZO
+ if (found & OPT_P_COMP)
+ {
+ if (lzo_defined (&c->c2.lzo_compwork))
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: LZO parms modified");
+ lzo_modify_flags (&c->c2.lzo_compwork, c->options.lzo);
+ }
+ }
+#endif
+
+ if (found & OPT_P_SHAPER)
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: traffic shaper enabled");
+ do_init_traffic_shaper (c);
+ }
+
+ if (found & OPT_P_SOCKBUF)
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: --sndbuf/--rcvbuf options modified");
+ link_socket_update_buffer_sizes (c->c2.link_socket, c->options.rcvbuf, c->options.sndbuf);
+ }
+
+ if (found & OPT_P_SOCKFLAGS)
+ {
+ msg (D_PUSH, "OPTIONS IMPORT: --socket-flags option modified");
+ link_socket_update_flags (c->c2.link_socket, c->options.sockflags);
+ }
+
+ if (found & OPT_P_PERSIST)
+ msg (D_PUSH, "OPTIONS IMPORT: --persist options modified");
+ if (found & OPT_P_UP)
+ msg (D_PUSH, "OPTIONS IMPORT: --ifconfig/up options modified");
+ if (found & OPT_P_ROUTE)
+ msg (D_PUSH, "OPTIONS IMPORT: route options modified");
+ if (found & OPT_P_ROUTE_EXTRAS)
+ msg (D_PUSH, "OPTIONS IMPORT: route-related options modified");
+ if (found & OPT_P_IPWIN32)
+ msg (D_PUSH, "OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified");
+ if (found & OPT_P_SETENV)
+ msg (D_PUSH, "OPTIONS IMPORT: environment modified");
+}
+
+/*
+ * Possible hold on initialization
+ */
+static bool
+do_hold (struct context *c)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ /* if c is defined, daemonize before hold */
+ if (c && c->options.daemon && management_should_daemonize (management))
+ do_init_first_time (c);
+
+ /* block until management hold is released */
+ if (management_hold (management))
+ return true;
+ }
+#endif
+ return false;
+}
+
+/*
+ * Sleep before restart.
+ */
+static void
+socket_restart_pause (struct context *c)
+{
+ bool proxy = false;
+ int sec = 2;
+
+#ifdef ENABLE_HTTP_PROXY
+ if (c->options.ce.http_proxy_options)
+ proxy = true;
+#endif
+#ifdef ENABLE_SOCKS
+ if (c->options.ce.socks_proxy_server)
+ proxy = true;
+#endif
+
+ switch (c->options.ce.proto)
+ {
+ case PROTO_UDPv4:
+ if (proxy)
+ sec = c->options.ce.connect_retry_seconds;
+ break;
+ case PROTO_TCPv4_SERVER:
+ sec = 1;
+ break;
+ case PROTO_TCPv4_CLIENT:
+ sec = c->options.ce.connect_retry_seconds;
+ break;
+ }
+
+#ifdef ENABLE_DEBUG
+ if (GREMLIN_CONNECTION_FLOOD_LEVEL (c->options.gremlin))
+ sec = 0;
+#endif
+
+#if P2MP
+ if (auth_retry_get () == AR_NOINTERACT)
+ sec = 10;
+
+ if (c->options.server_poll_timeout && sec > 1)
+ sec = 1;
+#endif
+
+ if (c->persist.restart_sleep_seconds > 0 && c->persist.restart_sleep_seconds > sec)
+ sec = c->persist.restart_sleep_seconds;
+ else if (c->persist.restart_sleep_seconds == -1)
+ sec = 0;
+ c->persist.restart_sleep_seconds = 0;
+
+ /* do managment hold on context restart, i.e. second, third, fourth, etc. initialization */
+ if (do_hold (NULL))
+ sec = 0;
+
+ if (sec)
+ {
+ msg (D_RESTART, "Restart pause, %d second(s)", sec);
+ openvpn_sleep (sec);
+ }
+}
+
+/*
+ * Do a possible pause on context_2 initialization.
+ */
+static void
+do_startup_pause (struct context *c)
+{
+ if (!c->first_time)
+ socket_restart_pause (c);
+ else
+ do_hold (NULL); /* do management hold on first context initialization */
+}
+
+/*
+ * Finalize MTU parameters based on command line or config file options.
+ */
+static void
+frame_finalize_options (struct context *c, const struct options *o)
+{
+ if (!o)
+ o = &c->options;
+
+ /*
+ * Set adjustment factor for buffer alignment when no
+ * cipher is used.
+ */
+ if (!CIPHER_ENABLED (c))
+ {
+ frame_align_to_extra_frame (&c->c2.frame);
+ frame_or_align_flags (&c->c2.frame,
+ FRAME_HEADROOM_MARKER_FRAGMENT
+ |FRAME_HEADROOM_MARKER_READ_LINK
+ |FRAME_HEADROOM_MARKER_READ_STREAM);
+ }
+
+ frame_finalize (&c->c2.frame,
+ o->link_mtu_defined,
+ o->link_mtu,
+ o->tun_mtu_defined,
+ o->tun_mtu);
+}
+
+/*
+ * Free a key schedule, including OpenSSL components.
+ */
+static void
+key_schedule_free (struct key_schedule *ks, bool free_ssl_ctx)
+{
+#ifdef USE_CRYPTO
+ free_key_ctx_bi (&ks->static_key);
+#ifdef USE_SSL
+ if (ks->ssl_ctx && free_ssl_ctx)
+ {
+ SSL_CTX_free (ks->ssl_ctx);
+ free_key_ctx_bi (&ks->tls_auth_key);
+ }
+#endif /* USE_SSL */
+#endif /* USE_CRYPTO */
+ CLEAR (*ks);
+}
+
+#ifdef USE_CRYPTO
+
+static void
+init_crypto_pre (struct context *c, const unsigned int flags)
+{
+ if (c->options.engine)
+ init_crypto_lib_engine (c->options.engine);
+
+ if (flags & CF_LOAD_PERSISTED_PACKET_ID)
+ {
+ /* load a persisted packet-id for cross-session replay-protection */
+ if (c->options.packet_id_file)
+ packet_id_persist_load (&c->c1.pid_persist, c->options.packet_id_file);
+ }
+
+ /* Initialize crypto options */
+
+ if (c->options.use_iv)
+ c->c2.crypto_options.flags |= CO_USE_IV;
+
+ if (c->options.mute_replay_warnings)
+ c->c2.crypto_options.flags |= CO_MUTE_REPLAY_WARNINGS;
+}
+
+/*
+ * Static Key Mode (using a pre-shared key)
+ */
+static void
+do_init_crypto_static (struct context *c, const unsigned int flags)
+{
+ const struct options *options = &c->options;
+ ASSERT (options->shared_secret_file);
+
+ init_crypto_pre (c, flags);
+
+ /* Initialize packet ID tracking */
+ if (options->replay)
+ {
+ packet_id_init (&c->c2.packet_id, options->replay_window,
+ options->replay_time);
+ c->c2.crypto_options.packet_id = &c->c2.packet_id;
+ c->c2.crypto_options.pid_persist = &c->c1.pid_persist;
+ c->c2.crypto_options.flags |= CO_PACKET_ID_LONG_FORM;
+ packet_id_persist_load_obj (&c->c1.pid_persist,
+ c->c2.crypto_options.packet_id);
+ }
+
+ if (!key_ctx_bi_defined (&c->c1.ks.static_key))
+ {
+ struct key2 key2;
+ struct key_direction_state kds;
+
+ /* Get cipher & hash algorithms */
+ init_key_type (&c->c1.ks.key_type, options->ciphername,
+ options->ciphername_defined, options->authname,
+ options->authname_defined, options->keysize,
+ options->test_crypto, true);
+
+ /* Read cipher and hmac keys from shared secret file */
+ {
+ unsigned int rkf_flags = RKF_MUST_SUCCEED;
+ const char *rkf_file = options->shared_secret_file;
+
+#if ENABLE_INLINE_FILES
+ if (options->shared_secret_file_inline)
+ {
+ rkf_file = options->shared_secret_file_inline;
+ rkf_flags |= RKF_INLINE;
+ }
+#endif
+ read_key_file (&key2, rkf_file, rkf_flags);
+ }
+
+ /* Check for and fix highly unlikely key problems */
+ verify_fix_key2 (&key2, &c->c1.ks.key_type,
+ options->shared_secret_file);
+
+ /* Initialize OpenSSL key objects */
+ key_direction_state_init (&kds, options->key_direction);
+ must_have_n_keys (options->shared_secret_file, "secret", &key2,
+ kds.need_keys);
+ init_key_ctx (&c->c1.ks.static_key.encrypt, &key2.keys[kds.out_key],
+ &c->c1.ks.key_type, DO_ENCRYPT, "Static Encrypt");
+ init_key_ctx (&c->c1.ks.static_key.decrypt, &key2.keys[kds.in_key],
+ &c->c1.ks.key_type, DO_DECRYPT, "Static Decrypt");
+
+ /* Erase the temporary copy of key */
+ CLEAR (key2);
+ }
+ else
+ {
+ msg (M_INFO, "Re-using pre-shared static key");
+ }
+
+ /* Get key schedule */
+ c->c2.crypto_options.key_ctx_bi = &c->c1.ks.static_key;
+
+ /* Compute MTU parameters */
+ crypto_adjust_frame_parameters (&c->c2.frame,
+ &c->c1.ks.key_type,
+ options->ciphername_defined,
+ options->use_iv, options->replay, true);
+
+ /* Sanity check on IV, sequence number, and cipher mode options */
+ check_replay_iv_consistency (&c->c1.ks.key_type, options->replay,
+ options->use_iv);
+}
+
+#ifdef USE_SSL
+
+/*
+ * Initialize the persistent component of OpenVPN's TLS mode,
+ * which is preserved across SIGUSR1 resets.
+ */
+static void
+do_init_crypto_tls_c1 (struct context *c)
+{
+ const struct options *options = &c->options;
+
+ if (!c->c1.ks.ssl_ctx)
+ {
+ /*
+ * Initialize the OpenSSL library's global
+ * SSL context.
+ */
+ c->c1.ks.ssl_ctx = init_ssl (options);
+ if (!c->c1.ks.ssl_ctx)
+ {
+#if P2MP
+ switch (auth_retry_get ())
+ {
+ case AR_NONE:
+ msg (M_FATAL, "Error: private key password verification failed");
+ break;
+ case AR_INTERACT:
+ ssl_purge_auth ();
+ case AR_NOINTERACT:
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Password failure error */
+ break;
+ default:
+ ASSERT (0);
+ }
+ c->sig->signal_text = "private-key-password-failure";
+ return;
+#else
+ msg (M_FATAL, "Error: private key password verification failed");
+#endif
+ }
+
+ /* Get cipher & hash algorithms */
+ init_key_type (&c->c1.ks.key_type, options->ciphername,
+ options->ciphername_defined, options->authname,
+ options->authname_defined, options->keysize, true, true);
+
+ /* Initialize PRNG with config-specified digest */
+ prng_init (options->prng_hash, options->prng_nonce_secret_len);
+
+ /* TLS handshake authentication (--tls-auth) */
+ if (options->tls_auth_file)
+ {
+ unsigned int flags = 0;
+ const char *file = options->tls_auth_file;
+
+#if ENABLE_INLINE_FILES
+ if (options->tls_auth_file_inline)
+ {
+ flags |= GHK_INLINE;
+ file = options->tls_auth_file_inline;
+ }
+#endif
+ get_tls_handshake_key (&c->c1.ks.key_type,
+ &c->c1.ks.tls_auth_key,
+ file,
+ options->key_direction,
+ flags);
+ }
+
+#if 0 /* was: #if ENABLE_INLINE_FILES -- Note that enabling this code will break restarts */
+ if (options->priv_key_file_inline)
+ {
+ string_clear (c->options.priv_key_file_inline);
+ c->options.priv_key_file_inline = NULL;
+ }
+#endif
+ }
+ else
+ {
+ msg (M_INFO, "Re-using SSL/TLS context");
+ }
+}
+
+static void
+do_init_crypto_tls (struct context *c, const unsigned int flags)
+{
+ const struct options *options = &c->options;
+ struct tls_options to;
+ bool packet_id_long_form;
+
+ ASSERT (options->tls_server || options->tls_client);
+ ASSERT (!options->test_crypto);
+
+ init_crypto_pre (c, flags);
+
+ /* Make sure we are either a TLS client or server but not both */
+ ASSERT (options->tls_server == !options->tls_client);
+
+ /* initialize persistent component */
+ do_init_crypto_tls_c1 (c);
+ if (IS_SIG (c))
+ return;
+
+ /* Sanity check on IV, sequence number, and cipher mode options */
+ check_replay_iv_consistency (&c->c1.ks.key_type, options->replay,
+ options->use_iv);
+
+ /* In short form, unique datagram identifier is 32 bits, in long form 64 bits */
+ packet_id_long_form = cfb_ofb_mode (&c->c1.ks.key_type);
+
+ /* Compute MTU parameters */
+ crypto_adjust_frame_parameters (&c->c2.frame,
+ &c->c1.ks.key_type,
+ options->ciphername_defined,
+ options->use_iv,
+ options->replay, packet_id_long_form);
+ tls_adjust_frame_parameters (&c->c2.frame);
+
+ /* Set all command-line TLS-related options */
+ CLEAR (to);
+
+ to.crypto_flags_and = ~(CO_PACKET_ID_LONG_FORM);
+ if (packet_id_long_form)
+ to.crypto_flags_or = CO_PACKET_ID_LONG_FORM;
+
+ to.ssl_ctx = c->c1.ks.ssl_ctx;
+ to.key_type = c->c1.ks.key_type;
+ to.server = options->tls_server;
+ to.key_method = options->key_method;
+ to.replay = options->replay;
+ to.replay_window = options->replay_window;
+ to.replay_time = options->replay_time;
+ to.transition_window = options->transition_window;
+ to.handshake_window = options->handshake_window;
+ to.packet_timeout = options->tls_timeout;
+ to.renegotiate_bytes = options->renegotiate_bytes;
+ to.renegotiate_packets = options->renegotiate_packets;
+ to.renegotiate_seconds = options->renegotiate_seconds;
+ to.single_session = options->single_session;
+#ifdef ENABLE_PUSH_PEER_INFO
+ to.push_peer_info = options->push_peer_info;
+#endif
+
+ /* should we not xmit any packets until we get an initial
+ response from client? */
+ if (to.server && options->ce.proto == PROTO_TCPv4_SERVER)
+ to.xmit_hold = true;
+
+#ifdef ENABLE_OCC
+ to.disable_occ = !options->occ;
+#endif
+
+ to.verify_command = options->tls_verify;
+ to.verify_export_cert = options->tls_export_cert;
+ to.verify_x509name = options->tls_remote;
+ to.crl_file = options->crl_file;
+ to.ns_cert_type = options->ns_cert_type;
+ memmove (to.remote_cert_ku, options->remote_cert_ku, sizeof (to.remote_cert_ku));
+ to.remote_cert_eku = options->remote_cert_eku;
+ to.es = c->c2.es;
+
+#ifdef ENABLE_DEBUG
+ to.gremlin = c->options.gremlin;
+#endif
+
+ to.plugins = c->plugins;
+
+#ifdef MANAGEMENT_DEF_AUTH
+ to.mda_context = &c->c2.mda_context;
+#endif
+
+#if P2MP_SERVER
+ to.auth_user_pass_verify_script = options->auth_user_pass_verify_script;
+ to.auth_user_pass_verify_script_via_file = options->auth_user_pass_verify_script_via_file;
+ to.tmp_dir = options->tmp_dir;
+ to.ssl_flags = options->ssl_flags;
+ if (options->ccd_exclusive)
+ to.client_config_dir_exclusive = options->client_config_dir;
+#endif
+
+ /* TLS handshake authentication (--tls-auth) */
+ if (options->tls_auth_file)
+ {
+ to.tls_auth_key = c->c1.ks.tls_auth_key;
+ to.tls_auth.pid_persist = &c->c1.pid_persist;
+ to.tls_auth.flags |= CO_PACKET_ID_LONG_FORM;
+ crypto_adjust_frame_parameters (&to.frame,
+ &c->c1.ks.key_type,
+ false, false, true, true);
+ }
+
+ /* If we are running over TCP, allow for
+ length prefix */
+ socket_adjust_frame_parameters (&to.frame, options->ce.proto);
+
+ /*
+ * Initialize OpenVPN's master TLS-mode object.
+ */
+ if (flags & CF_INIT_TLS_MULTI)
+ c->c2.tls_multi = tls_multi_init (&to);
+
+ if (flags & CF_INIT_TLS_AUTH_STANDALONE)
+ c->c2.tls_auth_standalone = tls_auth_standalone_init (&to, &c->c2.gc);
+}
+
+static void
+do_init_finalize_tls_frame (struct context *c)
+{
+ if (c->c2.tls_multi)
+ {
+ tls_multi_init_finalize (c->c2.tls_multi, &c->c2.frame);
+ ASSERT (EXPANDED_SIZE (&c->c2.tls_multi->opt.frame) <=
+ EXPANDED_SIZE (&c->c2.frame));
+ frame_print (&c->c2.tls_multi->opt.frame, D_MTU_INFO,
+ "Control Channel MTU parms");
+ }
+ if (c->c2.tls_auth_standalone)
+ {
+ tls_auth_standalone_finalize (c->c2.tls_auth_standalone, &c->c2.frame);
+ frame_print (&c->c2.tls_auth_standalone->frame, D_MTU_INFO,
+ "TLS-Auth MTU parms");
+ }
+}
+
+#endif /* USE_SSL */
+#endif /* USE_CRYPTO */
+
+#ifdef USE_CRYPTO
+/*
+ * No encryption or authentication.
+ */
+static void
+do_init_crypto_none (const struct context *c)
+{
+ ASSERT (!c->options.test_crypto);
+ msg (M_WARN,
+ "******* WARNING *******: all encryption and authentication features disabled -- all data will be tunnelled as cleartext");
+}
+#endif
+
+static void
+do_init_crypto (struct context *c, const unsigned int flags)
+{
+#ifdef USE_CRYPTO
+ if (c->options.shared_secret_file)
+ do_init_crypto_static (c, flags);
+#ifdef USE_SSL
+ else if (c->options.tls_server || c->options.tls_client)
+ do_init_crypto_tls (c, flags);
+#endif
+ else /* no encryption or authentication. */
+ do_init_crypto_none (c);
+#else /* USE_CRYPTO */
+ msg (M_WARN,
+ "******* WARNING *******: " PACKAGE_NAME
+ " built without OpenSSL -- encryption and authentication features disabled -- all data will be tunnelled as cleartext");
+#endif /* USE_CRYPTO */
+}
+
+static void
+do_init_frame (struct context *c)
+{
+#ifdef USE_LZO
+ /*
+ * Initialize LZO compression library.
+ */
+ if (c->options.lzo & LZO_SELECTED)
+ {
+ lzo_adjust_frame_parameters (&c->c2.frame);
+
+ /*
+ * LZO usage affects buffer alignment.
+ */
+ if (CIPHER_ENABLED (c))
+ {
+ frame_add_to_align_adjust (&c->c2.frame, LZO_PREFIX_LEN);
+ frame_or_align_flags (&c->c2.frame,
+ FRAME_HEADROOM_MARKER_FRAGMENT
+ |FRAME_HEADROOM_MARKER_DECRYPT);
+ }
+
+#ifdef ENABLE_FRAGMENT
+ lzo_adjust_frame_parameters (&c->c2.frame_fragment_omit); /* omit LZO frame delta from final frame_fragment */
+#endif
+ }
+#endif /* USE_LZO */
+
+#ifdef ENABLE_SOCKS
+ /*
+ * Adjust frame size for UDP Socks support.
+ */
+ if (c->options.ce.socks_proxy_server)
+ socks_adjust_frame_parameters (&c->c2.frame, c->options.ce.proto);
+#endif
+
+ /*
+ * Adjust frame size based on the --tun-mtu-extra parameter.
+ */
+ if (c->options.tun_mtu_extra_defined)
+ tun_adjust_frame_parameters (&c->c2.frame, c->options.tun_mtu_extra);
+
+ /*
+ * Adjust frame size based on link socket parameters.
+ * (Since TCP is a stream protocol, we need to insert
+ * a packet length uint16_t in the buffer.)
+ */
+ socket_adjust_frame_parameters (&c->c2.frame, c->options.ce.proto);
+
+ /*
+ * Fill in the blanks in the frame parameters structure,
+ * make sure values are rational, etc.
+ */
+ frame_finalize_options (c, NULL);
+
+#ifdef ENABLE_FRAGMENT
+ /*
+ * Set frame parameter for fragment code. This is necessary because
+ * the fragmentation code deals with payloads which have already been
+ * passed through the compression code.
+ */
+ c->c2.frame_fragment = c->c2.frame;
+ frame_subtract_extra (&c->c2.frame_fragment, &c->c2.frame_fragment_omit);
+#endif
+
+#if defined(ENABLE_FRAGMENT) && defined(ENABLE_OCC)
+ /*
+ * MTU advisories
+ */
+ if (c->options.fragment && c->options.mtu_test)
+ msg (M_WARN,
+ "WARNING: using --fragment and --mtu-test together may produce an inaccurate MTU test result");
+#endif
+
+#ifdef ENABLE_FRAGMENT
+ if ((c->options.mssfix || c->options.fragment)
+ && TUN_MTU_SIZE (&c->c2.frame_fragment) != ETHERNET_MTU)
+ msg (M_WARN,
+ "WARNING: normally if you use --mssfix and/or --fragment, you should also set --tun-mtu %d (currently it is %d)",
+ ETHERNET_MTU, TUN_MTU_SIZE (&c->c2.frame_fragment));
+#endif
+}
+
+static void
+do_option_warnings (struct context *c)
+{
+ const struct options *o = &c->options;
+
+#if 1 /* JYFIXME -- port warning */
+ if (!o->ce.port_option_used && (o->ce.local_port == OPENVPN_PORT && o->ce.remote_port == OPENVPN_PORT))
+ msg (M_WARN, "IMPORTANT: OpenVPN's default port number is now %d, based on an official port number assignment by IANA. OpenVPN 2.0-beta16 and earlier used 5000 as the default port.",
+ OPENVPN_PORT);
+#endif
+
+ if (o->ping_send_timeout && !o->ping_rec_timeout)
+ msg (M_WARN, "WARNING: --ping should normally be used with --ping-restart or --ping-exit");
+
+ if (o->username || o->groupname || o->chroot_dir
+#ifdef HAVE_SETCON
+ || o->selinux_context
+#endif
+ )
+ {
+ if (!o->persist_tun)
+ msg (M_WARN, "WARNING: you are using user/group/chroot/setcon without persist-tun -- this may cause restarts to fail");
+ if (!o->persist_key
+#ifdef ENABLE_PKCS11
+ && !o->pkcs11_id
+#endif
+ )
+ msg (M_WARN, "WARNING: you are using user/group/chroot/setcon without persist-key -- this may cause restarts to fail");
+ }
+
+ if (o->chroot_dir && !(o->username && o->groupname))
+ msg (M_WARN, "WARNING: you are using chroot without specifying user and group -- this may cause the chroot jail to be insecure");
+
+#if P2MP
+ if (o->pull && o->ifconfig_local && c->first_time)
+ msg (M_WARN, "WARNING: using --pull/--client and --ifconfig together is probably not what you want");
+
+#if P2MP_SERVER
+ if (o->server_bridge_defined | o->server_bridge_proxy_dhcp)
+ msg (M_WARN, "NOTE: when bridging your LAN adapter with the TAP adapter, note that the new bridge adapter will often take on its own IP address that is different from what the LAN adapter was previously set to");
+
+ if (o->mode == MODE_SERVER)
+ {
+ if (o->duplicate_cn && o->client_config_dir)
+ msg (M_WARN, "WARNING: using --duplicate-cn and --client-config-dir together is probably not what you want");
+ if (o->duplicate_cn && o->ifconfig_pool_persist_filename)
+ msg (M_WARN, "WARNING: --ifconfig-pool-persist will not work with --duplicate-cn");
+ if (!o->keepalive_ping || !o->keepalive_timeout)
+ msg (M_WARN, "WARNING: --keepalive option is missing from server config");
+ }
+#endif
+#endif
+
+#ifdef USE_CRYPTO
+ if (!o->replay)
+ msg (M_WARN, "WARNING: You have disabled Replay Protection (--no-replay) which may make " PACKAGE_NAME " less secure");
+ if (!o->use_iv)
+ msg (M_WARN, "WARNING: You have disabled Crypto IVs (--no-iv) which may make " PACKAGE_NAME " less secure");
+
+#ifdef USE_SSL
+ if (o->tls_server)
+ warn_on_use_of_common_subnets ();
+ if (o->tls_client
+ && !o->tls_verify
+ && !o->tls_remote
+ && !(o->ns_cert_type & NS_SSL_SERVER)
+ && !o->remote_cert_eku)
+ msg (M_WARN, "WARNING: No server certificate verification method has been enabled. See http://openvpn.net/howto.html#mitm for more info.");
+ if (o->tls_remote)
+ msg (M_WARN, "WARNING: Make sure you understand the semantics of --tls-remote before using it (see the man page).");
+#endif
+#endif
+
+#ifndef CONNECT_NONBLOCK
+ if (o->ce.connect_timeout_defined)
+ msg (M_WARN, "NOTE: --connect-timeout option is not supported on this OS");
+#endif
+
+ if (script_security >= SSEC_SCRIPTS)
+ msg (M_WARN, "NOTE: the current --script-security setting may allow this configuration to call user-defined scripts");
+ else if (script_security >= SSEC_PW_ENV)
+ msg (M_WARN, "WARNING: the current --script-security setting may allow passwords to be passed to scripts via environmental variables");
+ else
+ msg (M_WARN, "NOTE: " PACKAGE_NAME " 2.1 requires '--script-security 2' or higher to call user-defined scripts or executables");
+
+ if (script_method == SM_SYSTEM)
+ msg (M_WARN, "NOTE: --script-security method='system' is deprecated due to the fact that passed parameters will be subject to shell expansion");
+}
+
+static void
+do_init_frame_tls (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ do_init_finalize_tls_frame (c);
+#endif
+}
+
+struct context_buffers *
+init_context_buffers (const struct frame *frame)
+{
+ struct context_buffers *b;
+
+ ALLOC_OBJ_CLEAR (b, struct context_buffers);
+
+ b->read_link_buf = alloc_buf (BUF_SIZE (frame));
+ b->read_tun_buf = alloc_buf (BUF_SIZE (frame));
+
+ b->aux_buf = alloc_buf (BUF_SIZE (frame));
+
+#ifdef USE_CRYPTO
+ b->encrypt_buf = alloc_buf (BUF_SIZE (frame));
+ b->decrypt_buf = alloc_buf (BUF_SIZE (frame));
+#endif
+
+#ifdef USE_LZO
+ b->lzo_compress_buf = alloc_buf (BUF_SIZE (frame));
+ b->lzo_decompress_buf = alloc_buf (BUF_SIZE (frame));
+#endif
+
+ return b;
+}
+
+void
+free_context_buffers (struct context_buffers *b)
+{
+ if (b)
+ {
+ free_buf (&b->read_link_buf);
+ free_buf (&b->read_tun_buf);
+ free_buf (&b->aux_buf);
+
+#ifdef USE_LZO
+ free_buf (&b->lzo_compress_buf);
+ free_buf (&b->lzo_decompress_buf);
+#endif
+
+#ifdef USE_CRYPTO
+ free_buf (&b->encrypt_buf);
+ free_buf (&b->decrypt_buf);
+#endif
+
+ free (b);
+ }
+}
+
+/*
+ * Now that we know all frame parameters, initialize
+ * our buffers.
+ */
+static void
+do_init_buffers (struct context *c)
+{
+ c->c2.buffers = init_context_buffers (&c->c2.frame);
+ c->c2.buffers_owned = true;
+}
+
+#ifdef ENABLE_FRAGMENT
+/*
+ * Fragmenting code has buffers to initialize
+ * once frame parameters are known.
+ */
+static void
+do_init_fragment (struct context *c)
+{
+ ASSERT (c->options.fragment);
+ frame_set_mtu_dynamic (&c->c2.frame_fragment,
+ c->options.fragment, SET_MTU_UPPER_BOUND);
+ fragment_frame_init (c->c2.fragment, &c->c2.frame_fragment);
+}
+#endif
+
+/*
+ * Set the --mssfix option.
+ */
+static void
+do_init_mssfix (struct context *c)
+{
+ if (c->options.mssfix)
+ {
+ frame_set_mtu_dynamic (&c->c2.frame,
+ c->options.mssfix, SET_MTU_UPPER_BOUND);
+ }
+}
+
+/*
+ * Allocate our socket object.
+ */
+static void
+do_link_socket_new (struct context *c)
+{
+ ASSERT (!c->c2.link_socket);
+ c->c2.link_socket = link_socket_new ();
+ c->c2.link_socket_owned = true;
+}
+
+/*
+ * bind the TCP/UDP socket
+ */
+static void
+do_init_socket_1 (struct context *c, const int mode)
+{
+ unsigned int sockflags = c->options.sockflags;
+
+#if PORT_SHARE
+ if (c->options.port_share_host && c->options.port_share_port)
+ sockflags |= SF_PORT_SHARE;
+#endif
+
+ link_socket_init_phase1 (c->c2.link_socket,
+ connection_list_defined (&c->options),
+ c->options.ce.local,
+ c->options.ce.local_port,
+ c->options.ce.remote,
+ c->options.ce.remote_port,
+ c->options.ce.proto,
+ mode,
+ c->c2.accept_from,
+#ifdef ENABLE_HTTP_PROXY
+ c->c1.http_proxy,
+#endif
+#ifdef ENABLE_SOCKS
+ c->c1.socks_proxy,
+#endif
+#ifdef ENABLE_DEBUG
+ c->options.gremlin,
+#endif
+ c->options.ce.bind_local,
+ c->options.ce.remote_float,
+ c->options.inetd,
+ &c->c1.link_socket_addr,
+ c->options.ipchange,
+ c->plugins,
+ c->options.resolve_retry_seconds,
+ c->options.ce.connect_retry_seconds,
+ c->options.ce.connect_timeout,
+ c->options.ce.connect_retry_max,
+ c->options.mtu_discover_type,
+ c->options.rcvbuf,
+ c->options.sndbuf,
+ sockflags);
+}
+
+/*
+ * finalize the TCP/UDP socket
+ */
+static void
+do_init_socket_2 (struct context *c)
+{
+ link_socket_init_phase2 (c->c2.link_socket, &c->c2.frame,
+ &c->sig->signal_received);
+}
+
+/*
+ * Print MTU INFO
+ */
+static void
+do_print_data_channel_mtu_parms (struct context *c)
+{
+ frame_print (&c->c2.frame, D_MTU_INFO, "Data Channel MTU parms");
+#ifdef ENABLE_FRAGMENT
+ if (c->c2.fragment)
+ frame_print (&c->c2.frame_fragment, D_MTU_INFO,
+ "Fragmentation MTU parms");
+#endif
+}
+
+#ifdef ENABLE_OCC
+/*
+ * Get local and remote options compatibility strings.
+ */
+static void
+do_compute_occ_strings (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+
+ c->c2.options_string_local =
+ options_string (&c->options, &c->c2.frame, c->c1.tuntap, false, &gc);
+ c->c2.options_string_remote =
+ options_string (&c->options, &c->c2.frame, c->c1.tuntap, true, &gc);
+
+ msg (D_SHOW_OCC, "Local Options String: '%s'", c->c2.options_string_local);
+ msg (D_SHOW_OCC, "Expected Remote Options String: '%s'",
+ c->c2.options_string_remote);
+
+#ifdef USE_CRYPTO
+ msg (D_SHOW_OCC_HASH, "Local Options hash (VER=%s): '%s'",
+ options_string_version (c->c2.options_string_local, &gc),
+ md5sum ((uint8_t*)c->c2.options_string_local,
+ strlen (c->c2.options_string_local), 9, &gc));
+ msg (D_SHOW_OCC_HASH, "Expected Remote Options hash (VER=%s): '%s'",
+ options_string_version (c->c2.options_string_remote, &gc),
+ md5sum ((uint8_t*)c->c2.options_string_remote,
+ strlen (c->c2.options_string_remote), 9, &gc));
+#endif
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ if (c->c2.tls_multi)
+ tls_multi_init_set_options (c->c2.tls_multi,
+ c->c2.options_string_local,
+ c->c2.options_string_remote);
+#endif
+
+ gc_free (&gc);
+}
+#endif
+
+/*
+ * These things can only be executed once per program instantiation.
+ * Set up for possible UID/GID downgrade, but don't do it yet.
+ * Daemonize if requested.
+ */
+static void
+do_init_first_time (struct context *c)
+{
+ if (c->first_time && !c->did_we_daemonize && !c->c0)
+ {
+ struct context_0 *c0;
+
+ ALLOC_OBJ_CLEAR_GC (c->c0, struct context_0, &c->gc);
+ c0 = c->c0;
+
+ /* get user and/or group that we want to setuid/setgid to */
+ c0->uid_gid_specified =
+ get_group (c->options.groupname, &c0->group_state) |
+ get_user (c->options.username, &c0->user_state);
+
+ /* get --writepid file descriptor */
+ get_pid_file (c->options.writepid, &c0->pid_state);
+
+ /* become a daemon if --daemon */
+ c->did_we_daemonize = possibly_become_daemon (&c->options, c->first_time);
+
+ /* should we disable paging? */
+ if (c->options.mlock && c->did_we_daemonize)
+ do_mlockall (true); /* call again in case we daemonized */
+
+ /* save process ID in a file */
+ write_pid (&c0->pid_state);
+
+ /* should we change scheduling priority? */
+ set_nice (c->options.nice);
+ }
+}
+
+/*
+ * If xinetd/inetd mode, don't allow restart.
+ */
+static void
+do_close_check_if_restart_permitted (struct context *c)
+{
+ if (c->options.inetd
+ && (c->sig->signal_received == SIGHUP
+ || c->sig->signal_received == SIGUSR1))
+ {
+ c->sig->signal_received = SIGTERM;
+ msg (M_INFO,
+ PACKAGE_NAME
+ " started by inetd/xinetd cannot restart... Exiting.");
+ }
+}
+
+/*
+ * free buffers
+ */
+static void
+do_close_free_buf (struct context *c)
+{
+ if (c->c2.buffers_owned)
+ {
+ free_context_buffers (c->c2.buffers);
+ c->c2.buffers = NULL;
+ c->c2.buffers_owned = false;
+ }
+}
+
+/*
+ * close TLS
+ */
+static void
+do_close_tls (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ if (c->c2.tls_multi)
+ {
+ tls_multi_free (c->c2.tls_multi, true);
+ c->c2.tls_multi = NULL;
+ }
+
+#ifdef ENABLE_OCC
+ /* free options compatibility strings */
+ if (c->c2.options_string_local)
+ free (c->c2.options_string_local);
+ if (c->c2.options_string_remote)
+ free (c->c2.options_string_remote);
+ c->c2.options_string_local = c->c2.options_string_remote = NULL;
+#endif
+#endif
+}
+
+/*
+ * Free key schedules
+ */
+static void
+do_close_free_key_schedule (struct context *c, bool free_ssl_ctx)
+{
+ if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_key))
+ key_schedule_free (&c->c1.ks, free_ssl_ctx);
+}
+
+/*
+ * Close TCP/UDP connection
+ */
+static void
+do_close_link_socket (struct context *c)
+{
+ if (c->c2.link_socket && c->c2.link_socket_owned)
+ {
+ link_socket_close (c->c2.link_socket);
+ c->c2.link_socket = NULL;
+ }
+
+ if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_remote_ip))
+ {
+ CLEAR (c->c1.link_socket_addr.remote);
+ CLEAR (c->c1.link_socket_addr.actual);
+ }
+
+ if (!(c->sig->signal_received == SIGUSR1 && c->options.persist_local_ip))
+ CLEAR (c->c1.link_socket_addr.local);
+}
+
+/*
+ * Close packet-id persistance file
+ */
+static void
+do_close_packet_id (struct context *c)
+{
+#ifdef USE_CRYPTO
+ packet_id_free (&c->c2.packet_id);
+ packet_id_persist_save (&c->c1.pid_persist);
+ if (!(c->sig->signal_received == SIGUSR1))
+ packet_id_persist_close (&c->c1.pid_persist);
+#endif
+}
+
+#ifdef ENABLE_FRAGMENT
+/*
+ * Close fragmentation handler.
+ */
+static void
+do_close_fragment (struct context *c)
+{
+ if (c->c2.fragment)
+ {
+ fragment_free (c->c2.fragment);
+ c->c2.fragment = NULL;
+ }
+}
+#endif
+
+/*
+ * Open and close our event objects.
+ */
+
+static void
+do_event_set_init (struct context *c,
+ bool need_us_timeout)
+{
+ unsigned int flags = 0;
+
+ c->c2.event_set_max = BASE_N_EVENTS;
+
+ flags |= EVENT_METHOD_FAST;
+
+ if (need_us_timeout)
+ flags |= EVENT_METHOD_US_TIMEOUT;
+
+ c->c2.event_set = event_set_init (&c->c2.event_set_max, flags);
+ c->c2.event_set_owned = true;
+}
+
+static void
+do_close_event_set (struct context *c)
+{
+ if (c->c2.event_set && c->c2.event_set_owned)
+ {
+ event_free (c->c2.event_set);
+ c->c2.event_set = NULL;
+ c->c2.event_set_owned = false;
+ }
+}
+
+/*
+ * Open and close --status file
+ */
+
+static void
+do_open_status_output (struct context *c)
+{
+ if (!c->c1.status_output)
+ {
+ c->c1.status_output = status_open (c->options.status_file,
+ c->options.status_file_update_freq,
+ -1,
+ NULL,
+ STATUS_OUTPUT_WRITE);
+ c->c1.status_output_owned = true;
+ }
+}
+
+static void
+do_close_status_output (struct context *c)
+{
+ if (!(c->sig->signal_received == SIGUSR1))
+ {
+ if (c->c1.status_output_owned && c->c1.status_output)
+ {
+ status_close (c->c1.status_output);
+ c->c1.status_output = NULL;
+ c->c1.status_output_owned = false;
+ }
+ }
+}
+
+/*
+ * Handle ifconfig-pool persistance object.
+ */
+static void
+do_open_ifconfig_pool_persist (struct context *c)
+{
+#if P2MP_SERVER
+ if (!c->c1.ifconfig_pool_persist && c->options.ifconfig_pool_persist_filename)
+ {
+ c->c1.ifconfig_pool_persist = ifconfig_pool_persist_init (c->options.ifconfig_pool_persist_filename,
+ c->options.ifconfig_pool_persist_refresh_freq);
+ c->c1.ifconfig_pool_persist_owned = true;
+ }
+#endif
+}
+
+static void
+do_close_ifconfig_pool_persist (struct context *c)
+{
+#if P2MP_SERVER
+ if (!(c->sig->signal_received == SIGUSR1))
+ {
+ if (c->c1.ifconfig_pool_persist && c->c1.ifconfig_pool_persist_owned)
+ {
+ ifconfig_pool_persist_close (c->c1.ifconfig_pool_persist);
+ c->c1.ifconfig_pool_persist = NULL;
+ c->c1.ifconfig_pool_persist_owned = false;
+ }
+ }
+#endif
+}
+
+/*
+ * Inherit environmental variables
+ */
+
+static void
+do_inherit_env (struct context *c, const struct env_set *src)
+{
+ c->c2.es = env_set_create (NULL);
+ c->c2.es_owned = true;
+ env_set_inherit (c->c2.es, src);
+}
+
+static void
+do_env_set_destroy (struct context *c)
+{
+ if (c->c2.es && c->c2.es_owned)
+ {
+ env_set_destroy (c->c2.es);
+ c->c2.es = NULL;
+ c->c2.es_owned = false;
+ }
+}
+
+/*
+ * Fast I/O setup. Fast I/O is an optimization which only works
+ * if all of the following are true:
+ *
+ * (1) The platform is not Windows
+ * (2) --proto udp is enabled
+ * (3) --shaper is disabled
+ */
+static void
+do_setup_fast_io (struct context *c)
+{
+ if (c->options.fast_io)
+ {
+#ifdef WIN32
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are running on Windows");
+#else
+ if (c->options.ce.proto != PROTO_UDPv4)
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are not using UDP");
+ else
+ {
+#ifdef HAVE_GETTIMEOFDAY
+ if (c->options.shaper)
+ msg (M_INFO, "NOTE: --fast-io is disabled since we are using --shaper");
+ else
+#endif
+ {
+ c->c2.fast_io = true;
+ }
+ }
+#endif
+ }
+}
+
+static void
+do_signal_on_tls_errors (struct context *c)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ if (c->options.tls_exit)
+ c->c2.tls_exit_signal = SIGTERM;
+ else
+ c->c2.tls_exit_signal = SIGUSR1;
+#endif
+}
+
+#ifdef ENABLE_PLUGIN
+
+void
+init_plugins (struct context *c)
+{
+ if (c->options.plugin_list && !c->plugins)
+ {
+ c->plugins = plugin_list_init (c->options.plugin_list);
+ c->plugins_owned = true;
+ }
+}
+
+void
+open_plugins (struct context *c, const bool import_options, int init_point)
+{
+ if (c->plugins && c->plugins_owned)
+ {
+ if (import_options)
+ {
+ struct plugin_return pr, config;
+ plugin_return_init (&pr);
+ plugin_list_open (c->plugins, c->options.plugin_list, &pr, c->c2.es, init_point);
+ plugin_return_get_column (&pr, &config, "config");
+ if (plugin_return_defined (&config))
+ {
+ int i;
+ for (i = 0; i < config.n; ++i)
+ {
+ unsigned int option_types_found = 0;
+ if (config.list[i] && config.list[i]->value)
+ options_string_import (&c->options,
+ config.list[i]->value,
+ D_IMPORT_ERRORS|M_OPTERR,
+ OPT_P_DEFAULT & ~OPT_P_PLUGIN,
+ &option_types_found,
+ c->es);
+ }
+ }
+ plugin_return_free (&pr);
+ }
+ else
+ {
+ plugin_list_open (c->plugins, c->options.plugin_list, NULL, c->c2.es, init_point);
+ }
+ }
+}
+
+static void
+do_close_plugins (struct context *c)
+{
+ if (c->plugins && c->plugins_owned && !(c->sig->signal_received == SIGUSR1))
+ {
+ plugin_list_close (c->plugins);
+ c->plugins = NULL;
+ c->plugins_owned = false;
+ }
+}
+
+static void
+do_inherit_plugins (struct context *c, const struct context *src)
+{
+ if (!c->plugins && src->plugins)
+ {
+ c->plugins = plugin_list_inherit (src->plugins);
+ c->plugins_owned = true;
+ }
+}
+
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+
+static void
+management_callback_status_p2p (void *arg, const int version, struct status_output *so)
+{
+ struct context *c = (struct context *) arg;
+ print_status (c, so);
+}
+
+void
+management_show_net_callback (void *arg, const int msglevel)
+{
+#ifdef WIN32
+ show_routes (msglevel);
+ show_adapters (msglevel);
+ msg (msglevel, "END");
+#else
+ msg (msglevel, "ERROR: Sorry, this command is currently only implemented on Windows");
+#endif
+}
+
+#endif
+
+void
+init_management_callback_p2p (struct context *c)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ struct management_callback cb;
+ CLEAR (cb);
+ cb.arg = c;
+ cb.status = management_callback_status_p2p;
+ cb.show_net = management_show_net_callback;
+#if HTTP_PROXY_FALLBACK
+ cb.http_proxy_fallback_cmd = management_callback_http_proxy_fallback_cmd;
+#endif
+ management_set_callback (management, &cb);
+ }
+#endif
+}
+
+#ifdef ENABLE_MANAGEMENT
+
+void
+init_management (struct context *c)
+{
+ if (!management)
+ management = management_init ();
+}
+
+bool
+open_management (struct context *c)
+{
+ /* initialize management layer */
+ if (management)
+ {
+ if (c->options.management_addr)
+ {
+ unsigned int flags = c->options.management_flags;
+ if (c->options.mode == MODE_SERVER)
+ flags |= MF_SERVER;
+ if (management_open (management,
+ c->options.management_addr,
+ c->options.management_port,
+ c->options.management_user_pass,
+ c->options.management_client_user,
+ c->options.management_client_group,
+ c->options.management_log_history_cache,
+ c->options.management_echo_buffer_size,
+ c->options.management_state_buffer_size,
+ c->options.management_write_peer_info_file,
+ c->options.remap_sigusr1,
+ flags))
+ {
+ management_set_state (management,
+ OPENVPN_STATE_CONNECTING,
+ NULL,
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+
+ /* initial management hold, called early, before first context initialization */
+ do_hold (c);
+ if (IS_SIG (c))
+ {
+ msg (M_WARN, "Signal received from management interface, exiting");
+ return false;
+ }
+ }
+ else
+ close_management ();
+ }
+ return true;
+}
+
+void
+close_management (void)
+{
+ if (management)
+ {
+ management_close (management);
+ management = NULL;
+ }
+}
+
+#endif
+
+
+void
+uninit_management_callback (void)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_clear_callback (management);
+ }
+#endif
+}
+
+/*
+ * Initialize a tunnel instance, handle pre and post-init
+ * signal settings.
+ */
+void
+init_instance_handle_signals (struct context *c, const struct env_set *env, const unsigned int flags)
+{
+ pre_init_signal_catch ();
+ init_instance (c, env, flags);
+ post_init_signal_catch ();
+
+ /*
+ * This is done so that signals thrown during
+ * initialization can bring us back to
+ * a management hold.
+ */
+ if (IS_SIG (c))
+ {
+ remap_signal (c);
+ uninit_management_callback ();
+ }
+}
+
+/*
+ * Initialize a tunnel instance.
+ */
+void
+init_instance (struct context *c, const struct env_set *env, const unsigned int flags)
+{
+ const struct options *options = &c->options;
+ const bool child = (c->mode == CM_CHILD_TCP || c->mode == CM_CHILD_UDP);
+ int link_socket_mode = LS_MODE_DEFAULT;
+
+ /* init garbage collection level */
+ gc_init (&c->c2.gc);
+
+ /* signals caught here will abort */
+ c->sig->signal_received = 0;
+ c->sig->signal_text = NULL;
+ c->sig->hard = false;
+
+ if (c->mode == CM_P2P)
+ init_management_callback_p2p (c);
+
+ /* possible sleep or management hold if restart */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ {
+ do_startup_pause (c);
+ if (IS_SIG (c))
+ goto sig;
+ }
+
+ /* map in current connection entry */
+ next_connection_entry (c);
+
+ /* link_socket_mode allows CM_CHILD_TCP
+ instances to inherit acceptable fds
+ from a top-level parent */
+ if (c->options.ce.proto == PROTO_TCPv4_SERVER)
+ {
+ if (c->mode == CM_TOP)
+ link_socket_mode = LS_MODE_TCP_LISTEN;
+ else if (c->mode == CM_CHILD_TCP)
+ link_socket_mode = LS_MODE_TCP_ACCEPT_FROM;
+ }
+
+ /* should we disable paging? */
+ if (c->first_time && options->mlock)
+ do_mlockall (true);
+
+#if P2MP
+ /* get passwords if undefined */
+ if (auth_retry_get () == AR_INTERACT)
+ init_query_passwords (c);
+#endif
+
+ /* initialize context level 2 --verb/--mute parms */
+ init_verb_mute (c, IVM_LEVEL_2);
+
+ /* set error message delay for non-server modes */
+ if (c->mode == CM_P2P)
+ set_check_status_error_delay (P2P_ERROR_DELAY_MS);
+
+ /* warn about inconsistent options */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ do_option_warnings (c);
+
+ /* inherit environmental variables */
+ if (env)
+ do_inherit_env (c, env);
+
+#ifdef ENABLE_PLUGIN
+ /* initialize plugins */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ open_plugins (c, false, OPENVPN_PLUGIN_INIT_PRE_DAEMON);
+#endif
+
+ /* should we enable fast I/O? */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ do_setup_fast_io (c);
+
+ /* should we throw a signal on TLS errors? */
+ do_signal_on_tls_errors (c);
+
+ /* open --status file */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ do_open_status_output (c);
+
+ /* open --ifconfig-pool-persist file */
+ if (c->mode == CM_TOP)
+ do_open_ifconfig_pool_persist (c);
+
+#ifdef ENABLE_OCC
+ /* reset OCC state */
+ if (c->mode == CM_P2P || child)
+ c->c2.occ_op = occ_reset_op ();
+#endif
+
+ /* our wait-for-i/o objects, different for posix vs. win32 */
+ if (c->mode == CM_P2P)
+ do_event_set_init (c, SHAPER_DEFINED (&c->options));
+ else if (c->mode == CM_CHILD_TCP)
+ do_event_set_init (c, false);
+
+ /* initialize HTTP or SOCKS proxy object at scope level 2 */
+ init_proxy (c, 2);
+
+ /* allocate our socket object */
+ if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP)
+ do_link_socket_new (c);
+
+#ifdef ENABLE_FRAGMENT
+ /* initialize internal fragmentation object */
+ if (options->fragment && (c->mode == CM_P2P || child))
+ c->c2.fragment = fragment_init (&c->c2.frame);
+#endif
+
+ /* init crypto layer */
+ {
+ unsigned int crypto_flags = 0;
+ if (c->mode == CM_TOP)
+ crypto_flags = CF_INIT_TLS_AUTH_STANDALONE;
+ else if (c->mode == CM_P2P)
+ crypto_flags = CF_LOAD_PERSISTED_PACKET_ID | CF_INIT_TLS_MULTI;
+ else if (child)
+ crypto_flags = CF_INIT_TLS_MULTI;
+ do_init_crypto (c, crypto_flags);
+ if (IS_SIG (c) && !child)
+ goto sig;
+ }
+
+#ifdef USE_LZO
+ /* initialize LZO compression library. */
+ if ((options->lzo & LZO_SELECTED) && (c->mode == CM_P2P || child))
+ lzo_compress_init (&c->c2.lzo_compwork, options->lzo);
+#endif
+
+ /* initialize MTU variables */
+ do_init_frame (c);
+
+ /* initialize TLS MTU variables */
+ do_init_frame_tls (c);
+
+ /* init workspace buffers whose size is derived from frame size */
+ if (c->mode == CM_P2P || c->mode == CM_CHILD_TCP)
+ do_init_buffers (c);
+
+#ifdef ENABLE_FRAGMENT
+ /* initialize internal fragmentation capability with known frame size */
+ if (options->fragment && (c->mode == CM_P2P || child))
+ do_init_fragment (c);
+#endif
+
+ /* initialize dynamic MTU variable */
+ do_init_mssfix (c);
+
+ /* bind the TCP/UDP socket */
+ if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP)
+ do_init_socket_1 (c, link_socket_mode);
+
+ /* initialize tun/tap device object,
+ open tun/tap device, ifconfig, run up script, etc. */
+ if (!(options->up_delay || PULL_DEFINED (options)) && (c->mode == CM_P2P || c->mode == CM_TOP))
+ c->c2.did_open_tun = do_open_tun (c);
+
+ /* print MTU info */
+ do_print_data_channel_mtu_parms (c);
+
+#ifdef ENABLE_OCC
+ /* get local and remote options compatibility strings */
+ if (c->mode == CM_P2P || child)
+ do_compute_occ_strings (c);
+#endif
+
+ /* initialize output speed limiter */
+ if (c->mode == CM_P2P)
+ do_init_traffic_shaper (c);
+
+ /* do one-time inits, and possibily become a daemon here */
+ do_init_first_time (c);
+
+#ifdef ENABLE_PLUGIN
+ /* initialize plugins */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ open_plugins (c, false, OPENVPN_PLUGIN_INIT_POST_DAEMON);
+#endif
+
+ /*
+ * Actually do UID/GID downgrade, and chroot, if requested.
+ * May be delayed by --client, --pull, or --up-delay.
+ */
+ do_uid_gid_chroot (c, c->c2.did_open_tun);
+
+ /* finalize the TCP/UDP socket */
+ if (c->mode == CM_P2P || c->mode == CM_TOP || c->mode == CM_CHILD_TCP)
+ do_init_socket_2 (c);
+
+ /* initialize timers */
+ if (c->mode == CM_P2P || child)
+ do_init_timers (c, false);
+
+#ifdef ENABLE_PLUGIN
+ /* initialize plugins */
+ if (c->mode == CM_P2P || c->mode == CM_TOP)
+ open_plugins (c, false, OPENVPN_PLUGIN_INIT_POST_UID_CHANGE);
+#endif
+
+#if PORT_SHARE
+ /* share OpenVPN port with foreign (such as HTTPS) server */
+ if (c->first_time && (c->mode == CM_P2P || c->mode == CM_TOP))
+ init_port_share (c);
+#endif
+
+#ifdef ENABLE_PF
+ if (child)
+ pf_init_context (c);
+#endif
+
+ /* Check for signals */
+ if (IS_SIG (c))
+ goto sig;
+
+ return;
+
+ sig:
+ if (!c->sig->signal_text)
+ c->sig->signal_text = "init_instance";
+ close_context (c, -1, flags);
+ return;
+}
+
+/*
+ * Close a tunnel instance.
+ */
+void
+close_instance (struct context *c)
+{
+ /* close event objects */
+ do_close_event_set (c);
+
+ if (c->mode == CM_P2P
+ || c->mode == CM_CHILD_TCP
+ || c->mode == CM_CHILD_UDP
+ || c->mode == CM_TOP)
+ {
+ /* if xinetd/inetd mode, don't allow restart */
+ do_close_check_if_restart_permitted (c);
+
+#ifdef USE_LZO
+ if (lzo_defined (&c->c2.lzo_compwork))
+ lzo_compress_uninit (&c->c2.lzo_compwork);
+#endif
+
+ /* free buffers */
+ do_close_free_buf (c);
+
+ /* close TLS */
+ do_close_tls (c);
+
+ /* free key schedules */
+ do_close_free_key_schedule (c, (c->mode == CM_P2P || c->mode == CM_TOP));
+
+ /* close TCP/UDP connection */
+ do_close_link_socket (c);
+
+ /* close TUN/TAP device */
+ do_close_tun (c, false);
+
+#ifdef MANAGEMENT_DEF_AUTH
+ if (management)
+ management_notify_client_close (management, &c->c2.mda_context, NULL);
+#endif
+
+#ifdef ENABLE_PF
+ pf_destroy_context (&c->c2.pf);
+#endif
+
+#ifdef ENABLE_PLUGIN
+ /* call plugin close functions and unload */
+ do_close_plugins (c);
+#endif
+
+ /* close packet-id persistance file */
+ do_close_packet_id (c);
+
+ /* close --status file */
+ do_close_status_output (c);
+
+#ifdef ENABLE_FRAGMENT
+ /* close fragmentation handler */
+ do_close_fragment (c);
+#endif
+
+ /* close --ifconfig-pool-persist obj */
+ do_close_ifconfig_pool_persist (c);
+
+ /* free up environmental variable store */
+ do_env_set_destroy (c);
+
+ /* close HTTP or SOCKS proxy */
+ uninit_proxy (c);
+
+ /* garbage collect */
+ gc_free (&c->c2.gc);
+ }
+}
+
+void
+inherit_context_child (struct context *dest,
+ const struct context *src)
+{
+ CLEAR (*dest);
+
+ switch (src->options.ce.proto)
+ {
+ case PROTO_UDPv4:
+ dest->mode = CM_CHILD_UDP;
+ break;
+ case PROTO_TCPv4_SERVER:
+ dest->mode = CM_CHILD_TCP;
+ break;
+ default:
+ ASSERT (0);
+ }
+
+ dest->gc = gc_new ();
+
+ ALLOC_OBJ_CLEAR_GC (dest->sig, struct signal_info, &dest->gc);
+
+ /* c1 init */
+ packet_id_persist_init (&dest->c1.pid_persist);
+
+#ifdef USE_CRYPTO
+ dest->c1.ks.key_type = src->c1.ks.key_type;
+#ifdef USE_SSL
+ /* inherit SSL context */
+ dest->c1.ks.ssl_ctx = src->c1.ks.ssl_ctx;
+ dest->c1.ks.tls_auth_key = src->c1.ks.tls_auth_key;
+#endif
+#endif
+
+ /* options */
+ dest->options = src->options;
+ options_detach (&dest->options);
+
+ if (dest->mode == CM_CHILD_TCP)
+ {
+ /*
+ * The CM_TOP context does the socket listen(),
+ * and the CM_CHILD_TCP context does the accept().
+ */
+ dest->c2.accept_from = src->c2.link_socket;
+ }
+
+#ifdef ENABLE_PLUGIN
+ /* inherit plugins */
+ do_inherit_plugins (dest, src);
+#endif
+
+ /* context init */
+ init_instance (dest, src->c2.es, CC_NO_CLOSE | CC_USR1_TO_HUP);
+ if (IS_SIG (dest))
+ return;
+
+ /* inherit tun/tap interface object */
+ dest->c1.tuntap = src->c1.tuntap;
+
+ /* UDP inherits some extra things which TCP does not */
+ if (dest->mode == CM_CHILD_UDP)
+ {
+ /* inherit buffers */
+ dest->c2.buffers = src->c2.buffers;
+
+ /* inherit parent link_socket and tuntap */
+ dest->c2.link_socket = src->c2.link_socket;
+
+ ALLOC_OBJ_GC (dest->c2.link_socket_info, struct link_socket_info, &dest->gc);
+ *dest->c2.link_socket_info = src->c2.link_socket->info;
+
+ /* locally override some link_socket_info fields */
+ dest->c2.link_socket_info->lsa = &dest->c1.link_socket_addr;
+ dest->c2.link_socket_info->connection_established = false;
+ }
+}
+
+void
+inherit_context_top (struct context *dest,
+ const struct context *src)
+{
+ /* copy parent */
+ *dest = *src;
+
+ /*
+ * CM_TOP_CLONE will prevent close_instance from freeing or closing
+ * resources owned by the parent.
+ *
+ * Also note that CM_TOP_CLONE context objects are
+ * closed by multi_top_free in multi.c.
+ */
+ dest->mode = CM_TOP_CLONE;
+
+ dest->first_time = false;
+ dest->c0 = NULL;
+
+ options_detach (&dest->options);
+ gc_detach (&dest->gc);
+ gc_detach (&dest->c2.gc);
+
+ /* detach plugins */
+ dest->plugins_owned = false;
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ dest->c2.tls_multi = NULL;
+#endif
+
+ /* detach c1 ownership */
+ dest->c1.tuntap_owned = false;
+ dest->c1.status_output_owned = false;
+#if P2MP_SERVER
+ dest->c1.ifconfig_pool_persist_owned = false;
+#endif
+
+ /* detach c2 ownership */
+ dest->c2.event_set_owned = false;
+ dest->c2.link_socket_owned = false;
+ dest->c2.buffers_owned = false;
+ dest->c2.es_owned = false;
+
+ dest->c2.event_set = NULL;
+ if (src->options.ce.proto == PROTO_UDPv4)
+ do_event_set_init (dest, false);
+}
+
+void
+close_context (struct context *c, int sig, unsigned int flags)
+{
+ ASSERT (c);
+ ASSERT (c->sig);
+
+ if (sig >= 0)
+ c->sig->signal_received = sig;
+
+ if (c->sig->signal_received == SIGUSR1)
+ {
+ if ((flags & CC_USR1_TO_HUP)
+ || (c->sig->hard && (flags & CC_HARD_USR1_TO_HUP)))
+ c->sig->signal_received = SIGHUP;
+ }
+
+ if (!(flags & CC_NO_CLOSE))
+ close_instance (c);
+
+ if (flags & CC_GC_FREE)
+ context_gc_free (c);
+}
+
+#ifdef USE_CRYPTO
+
+/*
+ * Do a loopback test
+ * on the crypto subsystem.
+ */
+static void *
+test_crypto_thread (void *arg)
+{
+ struct context *c = (struct context *) arg;
+ const struct options *options = &c->options;
+
+ ASSERT (options->test_crypto);
+ init_verb_mute (c, IVM_LEVEL_1);
+ context_init_1 (c);
+ do_init_crypto_static (c, 0);
+
+ frame_finalize_options (c, options);
+
+ test_crypto (&c->c2.crypto_options, &c->c2.frame);
+
+ key_schedule_free (&c->c1.ks, true);
+ packet_id_free (&c->c2.packet_id);
+
+ context_gc_free (c);
+ return NULL;
+}
+
+#endif
+
+bool
+do_test_crypto (const struct options *o)
+{
+#ifdef USE_CRYPTO
+ if (o->test_crypto)
+ {
+ struct context c;
+
+ /* print version number */
+ msg (M_INFO, "%s", title_string);
+
+ context_clear (&c);
+ c.options = *o;
+ options_detach (&c.options);
+ c.first_time = true;
+ test_crypto_thread ((void *) &c);
+ return true;
+ }
+#endif
+ return false;
+}
diff --git a/init.h b/init.h
new file mode 100644
index 0000000..cf5ca8a
--- /dev/null
+++ b/init.h
@@ -0,0 +1,127 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INIT_H
+#define INIT_H
+
+#include "openvpn.h"
+
+/*
+ * Baseline maximum number of events
+ * to wait for.
+ */
+#define BASE_N_EVENTS 4
+
+void context_clear (struct context *c);
+void context_clear_1 (struct context *c);
+void context_clear_2 (struct context *c);
+void context_init_1 (struct context *c);
+void context_clear_all_except_first_time (struct context *c);
+
+bool init_static (void);
+
+void uninit_static (void);
+
+#define IVM_LEVEL_1 (1<<0)
+#define IVM_LEVEL_2 (1<<1)
+void init_verb_mute (struct context *c, unsigned int flags);
+
+void init_options_dev (struct options *options);
+
+bool print_openssl_info (const struct options *options);
+
+bool do_genkey (const struct options *options);
+
+bool do_persist_tuntap (const struct options *options);
+
+void pre_setup (const struct options *options);
+
+void init_instance_handle_signals (struct context *c, const struct env_set *env, const unsigned int flags);
+
+void init_instance (struct context *c, const struct env_set *env, const unsigned int flags);
+
+void do_route (const struct options *options,
+ struct route_list *route_list,
+ const struct tuntap *tt,
+ const struct plugin_list *plugins,
+ struct env_set *es);
+
+void close_instance (struct context *c);
+
+bool do_test_crypto (const struct options *o);
+
+void context_gc_free (struct context *c);
+
+void do_up (struct context *c,
+ bool pulled_options,
+ unsigned int option_types_found);
+
+unsigned int pull_permission_mask (const struct context *c);
+
+const char *format_common_name (struct context *c, struct gc_arena *gc);
+
+void reset_coarse_timers (struct context *c);
+
+void do_deferred_options (struct context *c, const unsigned int found);
+
+void inherit_context_child (struct context *dest,
+ const struct context *src);
+
+void inherit_context_top (struct context *dest,
+ const struct context *src);
+
+#define CC_GC_FREE (1<<0)
+#define CC_USR1_TO_HUP (1<<1)
+#define CC_HARD_USR1_TO_HUP (1<<2)
+#define CC_NO_CLOSE (1<<3)
+
+void close_context (struct context *c, int sig, unsigned int flags);
+
+struct context_buffers *init_context_buffers (const struct frame *frame);
+
+void free_context_buffers (struct context_buffers *b);
+
+#define ISC_ERRORS (1<<0)
+#define ISC_SERVER (1<<1)
+void initialization_sequence_completed (struct context *c, const unsigned int flags);
+
+#ifdef ENABLE_MANAGEMENT
+
+void init_management (struct context *c);
+bool open_management (struct context *c);
+void close_management (void);
+
+void management_show_net_callback (void *arg, const int msglevel);
+
+#endif
+
+void init_management_callback_p2p (struct context *c);
+void uninit_management_callback (void);
+
+#ifdef ENABLE_PLUGIN
+void init_plugins (struct context *c);
+void open_plugins (struct context *c, const bool import_options, int init_point);
+#endif
+
+#endif
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..6781b98
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,520 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2009-04-28.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -t) dst_arg=$2
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dst_arg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ -*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/install-win32/GetWindowsVersion.nsi b/install-win32/GetWindowsVersion.nsi
new file mode 100644
index 0000000..103caff
--- /dev/null
+++ b/install-win32/GetWindowsVersion.nsi
@@ -0,0 +1,109 @@
+; Turn off old selected section
+; GetWindowsVersion
+;
+; Based on Yazno's function
+; Updated by Joost Verburg
+; Updated for Windows 98 SE by Matthew Win Tibbals 5-21-03
+; Updated for Vista by Joe Cincotta 12-2-07
+;
+; Returns on top of stack
+;
+; Windows Version (95, 98, ME, NT x.x, 2000, XP, 2003, VISTA)
+; or
+; '' (Unknown Windows Version)
+;
+; Usage:
+; Call GetWindowsVersion
+; Pop $R0
+; ; at this point $R0 is "NT 4.0" or whatnot
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+Function GetWindowsVersion
+
+ Push $R0
+ Push $R1
+
+ ClearErrors
+
+ ReadRegStr $R0 HKLM \
+ "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+
+ IfErrors 0 lbl_winnt
+
+ ; we are not NT
+ ReadRegStr $R0 HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion" VersionNumber
+
+ StrCpy $R1 $R0 1
+ StrCmp $R1 '4' 0 lbl_error
+
+ StrCpy $R1 $R0 3
+
+ StrCmp $R1 '4.0' lbl_win32_95
+ StrCmp $R1 '4.9' lbl_win32_ME lbl_win32_98
+
+ lbl_win32_95:
+ StrCpy $R0 '95'
+ Goto lbl_done
+
+ lbl_win32_98:
+;;beginning of additions to support win 98 SE
+ push $R0
+ push "."
+ call strstr
+ pop $R0
+ StrCpy $R0 $R0 "" 1
+ StrCmp $R0 "10.2222" lbl_win32_98SE
+ StrCpy $R0 '98' ;;this line was not added
+ Goto lbl_done ;;this line was not added either
+
+ lbl_win32_98SE:
+ StrCpy $R0 '98 SE'
+ Goto lbl_done
+;;end of additions to support win 98 SE
+ lbl_win32_ME:
+ StrCpy $R0 'ME'
+ Goto lbl_done
+
+ lbl_winnt:
+
+ StrCpy $R1 $R0 1
+
+ StrCmp $R1 '3' lbl_winnt_x
+ StrCmp $R1 '4' lbl_winnt_x
+
+ StrCpy $R1 $R0 3
+
+ StrCmp $R1 '5.0' lbl_winnt_2000
+ StrCmp $R1 '5.1' lbl_winnt_XP
+ StrCmp $R1 '5.2' lbl_winnt_2003
+ StrCmp $R1 '6.0' lbl_winnt_VISTA lbl_error
+
+ lbl_winnt_x:
+ StrCpy $R0 "NT $R0" 6
+ Goto lbl_done
+
+ lbl_winnt_2000:
+ Strcpy $R0 '2000'
+ Goto lbl_done
+
+ lbl_winnt_XP:
+ Strcpy $R0 'XP'
+ Goto lbl_done
+
+ lbl_winnt_2003:
+ Strcpy $R0 '2003'
+ Goto lbl_done
+
+ lbl_winnt_VISTA:
+ Strcpy $R0 'VISTA'
+ Goto lbl_done
+
+ lbl_error:
+ Strcpy $R0 ''
+ lbl_done:
+
+ Pop $R1
+ Exch $R0
+
+FunctionEnd
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/install-win32/Makefile.am b/install-win32/Makefile.am
new file mode 100644
index 0000000..75932fe
--- /dev/null
+++ b/install-win32/Makefile.am
@@ -0,0 +1,97 @@
+#
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+dist_noinst_DATA = \
+ openssl \
+ GetWindowsVersion.nsi \
+ build-pkcs11-helper.sh \
+ buildinstaller \
+ ddk-common \
+ doclean \
+ dosname.pl \
+ getgui \
+ getopenssl \
+ getpkcs11helper \
+ getprebuilt \
+ getxgui \
+ ifdef.pl \
+ m4todef.pl \
+ macro.pl \
+ makeopenvpn \
+ maketap \
+ maketapinstall \
+ maketext \
+ openvpn.nsi \
+ setpath.nsi \
+ settings.in \
+ trans.pl \
+ u2d.c \
+ winconfig
+
+if WIN32
+
+nodist_doc_DATA = tmp/license.txt
+
+confdir = $(win32datadir)/config
+nodist_conf_DATA = \
+ tmp/openssl-1.0.0.cnf \
+ tmp/client.ovpn \
+ tmp/server.ovpn
+dist_conf_DATA = \
+ sample.ovpn
+
+easyrsadir = $(win32datadir)/easy-rsa/Windows
+nodist_easyrsa_DATA = \
+ $(top_srcdir)/easy-rsa/Windows/*
+
+keysdir = $(win32datadir)/sample-keys
+nodist_keys_DATA = \
+ $(top_srcdir)/sample-keys/*
+
+tmp:
+ mkdir tmp
+
+tmp/client.ovpn: tmp $(top_srcdir)/sample-config-files/client.conf
+ cp $(top_srcdir)/sample-config-files/client.conf tmp/client.ovpn
+
+tmp/server.ovpn: tmp $(top_srcdir)/sample-config-files/server.conf
+ cp $(top_srcdir)/sample-config-files/server.conf tmp/server.ovpn
+
+tmp/license.txt: tmp $(top_srcdir)/COPYING $(top_srcdir)/COPYRIGHT.GPL
+ cat $(top_srcdir)/COPYING $(top_srcdir)/COPYRIGHT.GPL > tmp/license.txt
+
+tmp/openssl-1.0.0.cnf: tmp $(top_srcdir)/easy-rsa/2.0/openssl-1.0.0.cnf
+ cp $(top_srcdir)/easy-rsa/2.0/openssl-1.0.0.cnf tmp/openssl-1.0.0.cnf
+
+clean-local:
+ -rm -fr tmp
+
+else
+
+dist_noinst_DATA += sample.ovpn
+
+endif
+
diff --git a/install-win32/Makefile.in b/install-win32/Makefile.in
new file mode 100644
index 0000000..5f0387f
--- /dev/null
+++ b/install-win32/Makefile.in
@@ -0,0 +1,553 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@WIN32_FALSE@am__append_1 = sample.ovpn
+subdir = install-win32
+DIST_COMMON = $(am__dist_conf_DATA_DIST) $(am__dist_noinst_DATA_DIST) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/settings.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = settings
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+am__dist_conf_DATA_DIST = sample.ovpn
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(confdir)" "$(DESTDIR)$(confdir)" \
+ "$(DESTDIR)$(docdir)" "$(DESTDIR)$(easyrsadir)" \
+ "$(DESTDIR)$(keysdir)"
+am__dist_noinst_DATA_DIST = openssl GetWindowsVersion.nsi \
+ build-pkcs11-helper.sh buildinstaller ddk-common doclean \
+ dosname.pl getgui getopenssl getpkcs11helper getprebuilt \
+ getxgui ifdef.pl m4todef.pl macro.pl makeopenvpn maketap \
+ maketapinstall maketext openvpn.nsi setpath.nsi settings.in \
+ trans.pl u2d.c winconfig sample.ovpn
+DATA = $(dist_conf_DATA) $(dist_noinst_DATA) $(nodist_conf_DATA) \
+ $(nodist_doc_DATA) $(nodist_easyrsa_DATA) $(nodist_keys_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+IFCONFIG = @IFCONFIG@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPROUTE = @IPROUTE@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAN2HTML = @MAN2HTML@
+MKDIR_P = @MKDIR_P@
+NETSTAT = @NETSTAT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+ROUTE = @ROUTE@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAP_ID = @TAP_ID@
+TAP_WIN32_MIN_MAJOR = @TAP_WIN32_MIN_MAJOR@
+TAP_WIN32_MIN_MINOR = @TAP_WIN32_MIN_MINOR@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+win32datadir = @win32datadir@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+dist_noinst_DATA = openssl GetWindowsVersion.nsi \
+ build-pkcs11-helper.sh buildinstaller ddk-common doclean \
+ dosname.pl getgui getopenssl getpkcs11helper getprebuilt \
+ getxgui ifdef.pl m4todef.pl macro.pl makeopenvpn maketap \
+ maketapinstall maketext openvpn.nsi setpath.nsi settings.in \
+ trans.pl u2d.c winconfig $(am__append_1)
+@WIN32_TRUE@nodist_doc_DATA = tmp/license.txt
+@WIN32_TRUE@confdir = $(win32datadir)/config
+@WIN32_TRUE@nodist_conf_DATA = \
+@WIN32_TRUE@ tmp/openssl-1.0.0.cnf \
+@WIN32_TRUE@ tmp/client.ovpn \
+@WIN32_TRUE@ tmp/server.ovpn
+
+@WIN32_TRUE@dist_conf_DATA = \
+@WIN32_TRUE@ sample.ovpn
+
+@WIN32_TRUE@easyrsadir = $(win32datadir)/easy-rsa/Windows
+@WIN32_TRUE@nodist_easyrsa_DATA = \
+@WIN32_TRUE@ $(top_srcdir)/easy-rsa/Windows/*
+
+@WIN32_TRUE@keysdir = $(win32datadir)/sample-keys
+@WIN32_TRUE@nodist_keys_DATA = \
+@WIN32_TRUE@ $(top_srcdir)/sample-keys/*
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu install-win32/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu install-win32/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+settings: $(top_builddir)/config.status $(srcdir)/settings.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-dist_confDATA: $(dist_conf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(confdir)" || $(MKDIR_P) "$(DESTDIR)$(confdir)"
+ @list='$(dist_conf_DATA)'; test -n "$(confdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+ done
+
+uninstall-dist_confDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_conf_DATA)'; test -n "$(confdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(confdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(confdir)" && rm -f $$files
+install-nodist_confDATA: $(nodist_conf_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(confdir)" || $(MKDIR_P) "$(DESTDIR)$(confdir)"
+ @list='$(nodist_conf_DATA)'; test -n "$(confdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+ done
+
+uninstall-nodist_confDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_conf_DATA)'; test -n "$(confdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(confdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(confdir)" && rm -f $$files
+install-nodist_docDATA: $(nodist_doc_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(docdir)" || $(MKDIR_P) "$(DESTDIR)$(docdir)"
+ @list='$(nodist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \
+ done
+
+uninstall-nodist_docDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_doc_DATA)'; test -n "$(docdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(docdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(docdir)" && rm -f $$files
+install-nodist_easyrsaDATA: $(nodist_easyrsa_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(easyrsadir)" || $(MKDIR_P) "$(DESTDIR)$(easyrsadir)"
+ @list='$(nodist_easyrsa_DATA)'; test -n "$(easyrsadir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(easyrsadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(easyrsadir)" || exit $$?; \
+ done
+
+uninstall-nodist_easyrsaDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_easyrsa_DATA)'; test -n "$(easyrsadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(easyrsadir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(easyrsadir)" && rm -f $$files
+install-nodist_keysDATA: $(nodist_keys_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(keysdir)" || $(MKDIR_P) "$(DESTDIR)$(keysdir)"
+ @list='$(nodist_keys_DATA)'; test -n "$(keysdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(keysdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(keysdir)" || exit $$?; \
+ done
+
+uninstall-nodist_keysDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_keys_DATA)'; test -n "$(keysdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(keysdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(keysdir)" && rm -f $$files
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(confdir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(easyrsadir)" "$(DESTDIR)$(keysdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+@WIN32_FALSE@clean-local:
+clean: clean-am
+
+clean-am: clean-generic clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_confDATA install-nodist_confDATA \
+ install-nodist_docDATA install-nodist_easyrsaDATA \
+ install-nodist_keysDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_confDATA uninstall-nodist_confDATA \
+ uninstall-nodist_docDATA uninstall-nodist_easyrsaDATA \
+ uninstall-nodist_keysDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-local \
+ distclean distclean-generic distdir dvi dvi-am html html-am \
+ info info-am install install-am install-data install-data-am \
+ install-dist_confDATA install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-nodist_confDATA \
+ install-nodist_docDATA install-nodist_easyrsaDATA \
+ install-nodist_keysDATA install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \
+ uninstall-am uninstall-dist_confDATA uninstall-nodist_confDATA \
+ uninstall-nodist_docDATA uninstall-nodist_easyrsaDATA \
+ uninstall-nodist_keysDATA
+
+
+@WIN32_TRUE@tmp:
+@WIN32_TRUE@ mkdir tmp
+
+@WIN32_TRUE@tmp/client.ovpn: tmp $(top_srcdir)/sample-config-files/client.conf
+@WIN32_TRUE@ cp $(top_srcdir)/sample-config-files/client.conf tmp/client.ovpn
+
+@WIN32_TRUE@tmp/server.ovpn: tmp $(top_srcdir)/sample-config-files/server.conf
+@WIN32_TRUE@ cp $(top_srcdir)/sample-config-files/server.conf tmp/server.ovpn
+
+@WIN32_TRUE@tmp/license.txt: tmp $(top_srcdir)/COPYING $(top_srcdir)/COPYRIGHT.GPL
+@WIN32_TRUE@ cat $(top_srcdir)/COPYING $(top_srcdir)/COPYRIGHT.GPL > tmp/license.txt
+
+@WIN32_TRUE@tmp/openssl-1.0.0.cnf: tmp $(top_srcdir)/easy-rsa/2.0/openssl-1.0.0.cnf
+@WIN32_TRUE@ cp $(top_srcdir)/easy-rsa/2.0/openssl-1.0.0.cnf tmp/openssl-1.0.0.cnf
+
+@WIN32_TRUE@clean-local:
+@WIN32_TRUE@ -rm -fr tmp
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/install-win32/build-pkcs11-helper.sh b/install-win32/build-pkcs11-helper.sh
new file mode 100644
index 0000000..fd336df
--- /dev/null
+++ b/install-win32/build-pkcs11-helper.sh
@@ -0,0 +1,24 @@
+F=pkcs11-helper-1.06-beta1
+OPENSSL_DIR=`pwd`/openssl-0.9.8h
+
+PKCS11_HELPER_DIR=`pwd`/pkcs11-helper
+rm -rf $PKCS11_HELPER_DIR
+mkdir $PKCS11_HELPER_DIR
+tbz=$F.tar.bz2
+
+rm -rf $F
+tar xfj $tbz
+
+cd $F
+./configure \
+ MAN2HTML=true \
+ --disable-crypto-engine-gnutls \
+ --disable-crypto-engine-nss \
+ PKG_CONFIG=true \
+ OPENSSL_CFLAGS="-I${OPENSSL_DIR}/include" \
+ OPENSSL_LIBS="-L${OPENSSL_DIR}/out -leay32"
+
+make
+make install DESTDIR="${PKCS11_HELPER_DIR}"
+
+# ./configure doesn't need this any more: ac_cv_type_size_t=no
diff --git a/install-win32/buildinstaller b/install-win32/buildinstaller
new file mode 100644
index 0000000..a17a027
--- /dev/null
+++ b/install-win32/buildinstaller
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# load version.nsi definitions
+. autodefs/defs.sh
+
+# build the installer
+rm -f $GENOUT/*.exe
+'/c/Program Files/NSIS/makensis' $GENOUT/nsi/openvpn.nsi &>makensis.log
+tail -20 makensis.log
+
+# sign the installer
+if [ -d "$SIGNTOOL" ]; then
+ python $SIGNTOOL/signapp.py "$(echo $(pwd)/$GENOUT/*.exe)"
+fi
diff --git a/install-win32/ddk-common b/install-win32/ddk-common
new file mode 100644
index 0000000..b45c9e5
--- /dev/null
+++ b/install-win32/ddk-common
@@ -0,0 +1,2 @@
+# DDKs <= 5600 use "AMD64", later use "x64"
+x64_tag=x64
diff --git a/install-win32/doclean b/install-win32/doclean
new file mode 100644
index 0000000..3f39543
--- /dev/null
+++ b/install-win32/doclean
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+[ "$CLEAN" = "yes" ] && rm -rf $GENOUT && KEEPAUTODEFS="yes" ./doclean
diff --git a/install-win32/dosname.pl b/install-win32/dosname.pl
new file mode 100644
index 0000000..a678e66
--- /dev/null
+++ b/install-win32/dosname.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+
+# convert a unix filename to a DOS filename
+
+while ($unixname = shift(@ARGV)) {
+ $unixname =~ s#^/([a-zA-Z])(/|$)#$1:\\#g;
+ $unixname =~ s#/#\\#g;
+ print "$unixname\n";
+}
diff --git a/install-win32/getgui b/install-win32/getgui
new file mode 100644
index 0000000..aa83e85
--- /dev/null
+++ b/install-win32/getgui
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Get and sign the OpenVPN GUI
+
+# load version.nsi definitions
+. autodefs/defs.sh
+
+GUI="$OPENVPN_GUI_DIR/$OPENVPN_GUI"
+
+if [ -f "$GUI" ]; then
+ mkdir -p $GENOUT/bin &>/dev/null
+ cp $GUI $GENOUT/bin
+fi
+
+if [ -f "$GENOUT/bin/$OPENVPN_GUI" ]; then
+ echo '!define OPENVPN_GUI_DEFINED' >autodefs/guidefs.nsi
+else
+ cat /dev/null >autodefs/guidefs.nsi
+fi
diff --git a/install-win32/getopenssl b/install-win32/getopenssl
new file mode 100644
index 0000000..b772741
--- /dev/null
+++ b/install-win32/getopenssl
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+# Get OpenSSL binaries
+if [ -d "$OPENSSL_DIR" ] ; then
+ mkdir -p $GENOUT/lib &>/dev/null
+ mkdir -p $GENOUT/bin &>/dev/null
+ for f in libeay32.dll libssl32.dll out/openssl.exe ; do
+ cp $OPENSSL_DIR/$f $GENOUT/lib
+ if [ -z "$NO_STRIP" ]; then
+ strip $GENOUT/lib/`basename $f`
+ fi
+ done
+ mv $GENOUT/lib/openssl.exe $GENOUT/bin
+else
+ echo OpenSSL DIR $OPENSSL_DIR NOT FOUND
+fi
diff --git a/install-win32/getpkcs11helper b/install-win32/getpkcs11helper
new file mode 100644
index 0000000..8fcfdd4
--- /dev/null
+++ b/install-win32/getpkcs11helper
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+# Get PKCS11-helper libraries
+if [ -d "$PKCS11_HELPER_DIR" ] ; then
+ mkdir -p $GENOUT/lib &>/dev/null
+ for f in libpkcs11-helper-1.dll ; do
+ cp $PKCS11_HELPER_DIR/usr/local/bin/$f $GENOUT/lib
+ if [ -z "$NO_STRIP" ]; then
+ strip $GENOUT/lib/$f
+ fi
+ done
+else
+ echo PKCS11-helper DIR $PKCS11_HELPER_DIR NOT FOUND
+fi
diff --git a/install-win32/getprebuilt b/install-win32/getprebuilt
new file mode 100644
index 0000000..36c4827
--- /dev/null
+++ b/install-win32/getprebuilt
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+# Get PKCS11-helper libraries
+if [ -d "$GENOUT_PREBUILT" ] && ! [ -d "$GENOUT" ]; then
+ echo LOADING prebuilt binaries from $GENOUT_PREBUILT
+ cp -a $GENOUT_PREBUILT $GENOUT
+fi
diff --git a/install-win32/getxgui b/install-win32/getxgui
new file mode 100644
index 0000000..3a1e626
--- /dev/null
+++ b/install-win32/getxgui
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# Get and sign the OpenVPN XML-based GUI
+
+# load version.nsi definitions
+. autodefs/defs.sh
+
+if [ -d "$OPENVPN_XGUI_DIR" ]; then
+ SIGNED_EXES="gui/ovpn-xgui-en.exe sta/ovpn-tray.exe"
+ UNSIGNED_EXES="xmlserv/ovpn-xmlserv.exe"
+ EXES="$SIGNED_EXES $UNSIGNED_EXES"
+
+ mkdir -p $GENOUT/bin &>/dev/null
+
+ if [ -z "$NO_STRIP" ]; then
+ for f in $EXES; do
+ cp $OPENVPN_XGUI_DIR/$f $GENOUT/bin
+ strip $GENOUT/bin/`basename $f`
+ done
+ fi
+
+ rm -rf $GENOUT/htdocs
+ cp -a $OPENVPN_XGUI_DIR/ajax/htdocs $GENOUT/htdocs
+
+ echo '!define OPENVPN_XGUI_DEFINED' >autodefs/xguidefs.nsi
+else
+ cat /dev/null >autodefs/xguidefs.nsi
+fi
diff --git a/install-win32/ifdef.pl b/install-win32/ifdef.pl
new file mode 100644
index 0000000..d240ebb
--- /dev/null
+++ b/install-win32/ifdef.pl
@@ -0,0 +1,53 @@
+#!/usr/bin/perl
+
+# Simple ifdef/else/endif processor.
+
+die "usage: ifdef [-C<command-prefix>] [-Dname ...] [control-file ...] " if (@ARGV[0] =~ /^(-h|--help)$/);
+
+%Parms = ();
+
+$pre = "!";
+while ($arg=shift(@ARGV)) {
+ if ($arg =~ /^-/) {
+ if ($arg =~ /^-D(\w+)$/) {
+ $Parms{$1} = 1;
+ } elsif ($arg =~ /-C(.*)$/) {
+ $pre = $1;
+ } else {
+ die "unrecognized option: $arg";
+ }
+ } else {
+ open(CONTROL, "< $arg") or die "cannot open $arg";
+ while (<CONTROL>) {
+ if (/^!define\s+(\w+)/) {
+ $Parms{$1} = 1;
+ }
+ }
+ }
+}
+
+sub ifdef {
+ my ($var, $enabled) = @_;
+ my $def = 0;
+ $def = 1 if (defined $Parms{$var}) || ($var eq "true");
+ $def = 0 if $var eq "false";
+ while (<STDIN>) {
+ if (/^\s*\Q$pre\Eifdef\s+(\w+)\s*$/) {
+ return 1 if ifdef ($1, $def & $enabled);
+ } elsif (/^\s*\Q$pre\Eelseif\s+(\w+)\s*$/) {
+ $def = $def ^ 1;
+ return ifdef ($1, $def & $enabled);
+ } elsif (/^\s*\Q$pre\Eelse\s*$/) {
+ $def = $def ^ 1;
+ } elsif (/^\s*\Q$pre\Eendif\s*$/) {
+ return 0;
+ } elsif (/^\s*\Q$pre\E/) {
+ die "unrecognized command: $_";
+ } else {
+ print if $def && $enabled;
+ }
+ }
+ return 1;
+}
+
+ifdef("true", 1);
diff --git a/install-win32/m4todef.pl b/install-win32/m4todef.pl
new file mode 100644
index 0000000..d2705b0
--- /dev/null
+++ b/install-win32/m4todef.pl
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+# used to convert version.m4 to simple
+# definition format
+
+while (<STDIN>) {
+ chomp;
+ if (/^\s*$/) {
+ print "\n";
+ } elsif (/^define\((\w+),\[(.*?)\]\)/) {
+ print "!define $1 \"$2\"\n";
+ } elsif (/^dnl(.*)$/) {
+ print "#$1\n";
+ }
+}
diff --git a/install-win32/macro.pl b/install-win32/macro.pl
new file mode 100644
index 0000000..08ba58a
--- /dev/null
+++ b/install-win32/macro.pl
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+# Simple macro processor.
+
+# Macros are defined in a control file that follows
+# a simple definition-based grammar as documented in the
+# trans script. Stdin is then copied to stdout, and any
+# occurrence of @@MACRO@@ is substituted. Macros can also
+# be specified on the command line.
+
+die "usage: macro [-O<openquote>] [-C<closequote>] [-Dname=var ...] [control-file ...] " if (@ARGV < 1);
+
+%Parms = ();
+$open_quote = "@@";
+$close_quote = "@@";
+
+while ($arg=shift(@ARGV)) {
+ if ($arg =~ /^-/) {
+ if ($arg =~ /^-D(\w+)(?:=(.*))?$/) {
+ $Parms{$1} = $2
+ } elsif ($arg =~ /-O(.*)$/) {
+ $open_quote = $1;
+ } elsif ($arg =~ /-C(.*)$/) {
+ $close_quote = $1;
+ } else {
+ die "unrecognized option: $arg";
+ }
+ } else {
+ open(CONTROL, "< $arg") or die "cannot open $arg";
+ while (<CONTROL>) {
+ if (/^!define\s+(\w+)(?:\s+['"]?(.*?)['"]?)?\s*$/) {
+ $Parms{$1} = $2;
+ }
+ }
+ }
+}
+
+sub print_symbol_table {
+ foreach my $k (sort (keys(%Parms))) {
+ my $v = $Parms{$k};
+ print "[$k] -> \"$v\"\n";
+ }
+}
+
+#print_symbol_table ();
+#exit 0;
+
+while (<STDIN>) {
+ s{
+ \Q$open_quote\E
+ \s*
+ (
+ \w+
+ )
+ \s*
+ \Q$close_quote\E
+ }{
+ $Parms{$1}
+ }xge;
+ print;
+}
diff --git a/install-win32/makeopenvpn b/install-win32/makeopenvpn
new file mode 100644
index 0000000..c1a805d
--- /dev/null
+++ b/install-win32/makeopenvpn
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+H=`pwd`
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+if gcc --version &>/dev/null && [ -d "$OPENSSL_DIR" ] && [ -d "$LZO_DIR" ] && [ -d "$PKCS11_HELPER_DIR" ]; then
+ # build OpenVPN binary
+
+ if ! [ -f Makefile ]; then
+ autoreconf -i -v \
+ && ./configure \
+ --enable-strict \
+ --prefix=$H/windest \
+ MAN2HTML=true \
+ --with-ssl-headers=$H/$OPENSSL_DIR/include \
+ --with-ssl-lib=$H/$OPENSSL_DIR/out \
+ --with-lzo-headers=$H/$LZO_DIR/include \
+ --with-lzo-lib=$H/$LZO_DIR \
+ --with-pkcs11-helper-headers=$H/$PKCS11_HELPER_DIR/usr/local/include \
+ --with-pkcs11-helper-lib=$H/$PKCS11_HELPER_DIR/usr/local/lib
+ fi
+
+ make -j $MAKE_JOBS && make install
+
+ # copy OpenVPN and service executables to GENOUT/bin
+ mkdir -p $GENOUT/bin &>/dev/null
+ cp windest/sbin/openvpn.exe $GENOUT/bin
+ cp windest/sbin/openvpnserv.exe $GENOUT/bin
+ if [ -z "$NO_STRIP" ]; then
+ strip $GENOUT/bin/openvpn.exe
+ strip $GENOUT/bin/openvpnserv.exe
+ fi
+else
+ echo DID NOT BUILD openvpn.exe and openvpnserv.exe because one or more of gcc, OPENSSL_DIR, LZO_DIR, or PKCS11_HELPER_DIR directories were missing
+fi
diff --git a/install-win32/maketap b/install-win32/maketap
new file mode 100644
index 0000000..b9c4070
--- /dev/null
+++ b/install-win32/maketap
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Get the x86 and x64 versions of the TAP driver
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+if [ -d "$TAPBINSRC" ]; then
+ mkdir -p $GENOUT/driver/i386 &>/dev/null
+ mkdir -p $GENOUT/driver/amd64 &>/dev/null
+ for arch in i386 amd64; do
+ s=$TAPBINSRC/$arch
+ cp $s/*.sys $s/*.cat $s/*.inf $GENOUT/driver/$arch
+ done
+else
+ echo Cannot find pre-built tap drivers
+fi
diff --git a/install-win32/maketapinstall b/install-win32/maketapinstall
new file mode 100644
index 0000000..9fe0470
--- /dev/null
+++ b/install-win32/maketapinstall
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Get the x86 and x64 versions of the tapinstall tool
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+if [ -d "$TAPBINSRC" ]; then
+ mkdir -p $GENOUT/tapinstall/i386 &>/dev/null
+ mkdir -p $GENOUT/tapinstall/amd64 &>/dev/null
+ cp $TAPBINSRC/i386/tapinstall.exe $GENOUT/tapinstall/i386
+ cp $TAPBINSRC/amd64/tapinstall.exe $GENOUT/tapinstall/amd64
+else
+ echo Cannot find pre-built tapinstall
+fi
diff --git a/install-win32/maketext b/install-win32/maketext
new file mode 100644
index 0000000..9a94a81
--- /dev/null
+++ b/install-win32/maketext
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# get version.nsi definitions
+. autodefs/defs.sh
+
+mkdir -p $GENOUT/text &>/dev/null
+
+# build license file
+cat COPYING COPYRIGHT.GPL >$GENOUT/text/license.txt
+
+# copy install file
+cp INSTALL-win32.txt $GENOUT/text/INSTALL-win32.txt
+
+# copy sample configuration files and docs
+s=$GENOUT/samples
+mkdir -p $s &>/dev/null
+cp sample-config-files/client.conf $s/client.$PRODUCT_FILE_EXT
+cp sample-config-files/server.conf $s/server.$PRODUCT_FILE_EXT
+cp install-win32/sample.ovpn $s/sample.$PRODUCT_FILE_EXT
+
+# get easy-rsa (Windows)
+e=$GENOUT/easy-rsa
+mkdir -p $e &>/dev/null
+cp easy-rsa/1.0/openssl.cnf $e/openssl.cnf.sample
+cp easy-rsa/Windows/* $e
+
+# get images
+i=$GENOUT/images
+mkdir -p $i &>/dev/null
+cp images/*.ico $i
+cp images/*.bmp $i
+
+# get NSI files
+n=$GENOUT/nsi
+mkdir -p $n &>/dev/null
+cp autodefs/defs.nsi $n
+cp autodefs/guidefs.nsi $n
+cp autodefs/xguidefs.nsi $n
+cp install-win32/openvpn.nsi $n
+cp install-win32/setpath.nsi $n
+cp install-win32/GetWindowsVersion.nsi $n
+
+if [ -n "$EXTRACT_FILES" ]; then
+ cp "$EXTRACT_FILES/MultiFileExtract.nsi" $n
+fi
+
+# get OpenVPN client config files
+if [ -n "$SAMPCONF_DIR" ]; then
+ c=$GENOUT/conf
+ mkdir -p $c &>/dev/null
+ test -n "$SAMPCONF_CONF" && cp "../$SAMPCONF_DIR/$SAMPCONF_CONF" $c
+ test -n "$SAMPCONF_CONF2" && cp "../$SAMPCONF_DIR/$SAMPCONF_CONF2" $c
+ test -n "$SAMPCONF_P12" && cp "../$SAMPCONF_DIR/$SAMPCONF_P12" $c
+ test -n "$SAMPCONF_TA" && cp "../$SAMPCONF_DIR/$SAMPCONF_TA" $c
+ test -n "$SAMPCONF_CA" && cp "../$SAMPCONF_DIR/$SAMPCONF_CA" $c
+ test -n "$SAMPCONF_CRT" && cp "../$SAMPCONF_DIR/$SAMPCONF_CRT" $c
+ test -n "$SAMPCONF_KEY" && cp "../$SAMPCONF_DIR/$SAMPCONF_KEY" $c
+ test -n "$SAMPCONF_DH" && cp "../$SAMPCONF_DIR/$SAMPCONF_DH" $c
+fi
diff --git a/install-win32/openssl/README.txt b/install-win32/openssl/README.txt
new file mode 100644
index 0000000..6a042f4
--- /dev/null
+++ b/install-win32/openssl/README.txt
@@ -0,0 +1,21 @@
+Rebuild OpenSSL tarball without symbolic links, so
+it can be extracted on Windows (run on Unix):
+
+ [download tarball and .asc sig]
+ gpg --verify openssl-0.9.8k.tar.gz.asc
+ tar xfz openssl-0.9.8k.tar.gz
+ tar cfzh openssl-0.9.8k-nolinks.tar.gz openssl-0.9.8k
+
+To apply patch (in MSYS shell):
+
+ cd /c/src/openssl-0.9.8k
+ patch -p1 <../21/install-win32/openssl/openssl098.patch
+
+To build OpenSSL, open a command prompt window, then:
+
+ cd \src\openssl-0.9.8k
+ ms\mw
+
+To build a new patch (optional):
+
+ diff -urw openssl-0.9.8k.orig openssl-0.9.8k | grep -v '^Only in' >openssl098.patch
diff --git a/install-win32/openssl/openssl097.patch b/install-win32/openssl/openssl097.patch
new file mode 100644
index 0000000..ccef40a
--- /dev/null
+++ b/install-win32/openssl/openssl097.patch
@@ -0,0 +1,68 @@
+[in msys bash window]
+cd /c/src/openssl-0.9.7m
+patch -p1 <../21/install-win32/openssl.patch
+
+[open command prompt window]
+cd \src\openssl-0.9.7m
+ms\mw
+
+diff -wur openssl-0.9.7m.orig/ms/mw.bat openssl-0.9.7m/ms/mw.bat
+--- openssl-0.9.7m.orig/ms/mw.bat Sat Feb 22 11:02:46 2003
++++ openssl-0.9.7m/ms/mw.bat Mon Jan 21 23:12:34 2008
+@@ -1,17 +1,23 @@
+ @rem OpenSSL with Mingw32
+ @rem --------------------
+
++@rem Include MinGW, MSYS, and ActiveState Perl in path
++set PATH=c:\perl\bin;c:\MinGW\bin;c:\msys\1.0\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
++
+ @rem Makefile
+ perl util\mkfiles.pl >MINFO
+-perl util\mk1mf.pl Mingw32 >ms\mingw32.mak
++perl util\mk1mf.pl no-idea no-mdc2 no-rc5 Mingw32 >ms\mingw32.mak
++
+ @rem DLL definition files
+-perl util\mkdef.pl 32 libeay >ms\libeay32.def
++perl util\mkdef.pl no-idea no-mdc2 no-rc5 32 libeay >ms\libeay32.def
+ if errorlevel 1 goto end
+-perl util\mkdef.pl 32 ssleay >ms\ssleay32.def
++perl util\mkdef.pl no-idea no-mdc2 no-rc5 32 ssleay >ms\ssleay32.def
+ if errorlevel 1 goto end
+
+ @rem Build the libraries
+-make -f ms/mingw32.mak
++
++@rem JY added --win32 flag
++make --win32 -f ms/mingw32.mak
+ if errorlevel 1 goto end
+
+ @rem Generate the DLLs and input libraries
+@@ -20,7 +26,9 @@
+ dllwrap --dllname libssl32.dll --output-lib out/libssl32.a --def ms/ssleay32.def out/libssl.a out/libeay32.a
+ if errorlevel 1 goto end
+
++@rem JY added openssl.exe linked to DLL
++gcc -o openssl tmp\verify.o tmp\asn1pars.o tmp\req.o tmp\dgst.o tmp\dh.o tmp\dhparam.o tmp\enc.o tmp\passwd.o tmp\gendh.o tmp\errstr.o tmp\ca.o tmp\pkcs7.o tmp\crl2p7.o tmp\crl.o tmp\rsa.o tmp\rsautl.o tmp\dsa.o tmp\dsaparam.o tmp\x509.o tmp\genrsa.o tmp\gendsa.o tmp\s_server.o tmp\s_client.o tmp\speed.o tmp\s_time.o tmp\apps.o tmp\s_cb.o tmp\s_socket.o tmp\app_rand.o tmp\version.o tmp\sess_id.o tmp\ciphers.o tmp\nseq.o tmp\pkcs12.o tmp\pkcs8.o tmp\spkac.o tmp\smime.o tmp\rand.o tmp\engine.o tmp\ocsp.o tmp\prime.o tmp\openssl.o -leay32 -lssl32 -L. -lwsock32 -lgdi32
++
+ echo Done compiling OpenSSL
+
+ :end
+-
+diff -wur openssl-0.9.7m.orig/util/pl/Mingw32.pl openssl-0.9.7m/util/pl/Mingw32.pl
+--- openssl-0.9.7m.orig/util/pl/Mingw32.pl Sun May 16 23:28:32 2004
++++ openssl-0.9.7m/util/pl/Mingw32.pl Mon Jan 21 17:52:36 2008
+@@ -99,10 +99,10 @@
+ $n=&bname($target);
+ $ret.="$target: $files $dep_libs\n";
+ $ret.="\t\$(LINK) ${efile}$target \$(LFLAGS) $files $libs\n";
+- if (defined $sha1file)
+- {
+- $ret.="\t$openssl sha1 -hmac etaonrishdlcupfm -binary $target > $sha1file";
+- }
++# if (defined $sha1file)
++# {
++# $ret.="\t$openssl sha1 -hmac etaonrishdlcupfm -binary $target > $sha1file";
++# }
+ $ret.="\n";
+ return($ret);
+ }
diff --git a/install-win32/openssl/openssl098.patch b/install-win32/openssl/openssl098.patch
new file mode 100644
index 0000000..653d2fe
--- /dev/null
+++ b/install-win32/openssl/openssl098.patch
@@ -0,0 +1,56 @@
+diff -urw tmp/openssl-0.9.8h/crypto/pqueue/pqueue.c openssl-0.9.8h/crypto/pqueue/pqueue.c
+--- tmp/openssl-0.9.8h/crypto/pqueue/pqueue.c Tue Jun 28 06:53:34 2005
++++ openssl-0.9.8h/crypto/pqueue/pqueue.c Wed Jun 4 02:52:42 2008
+@@ -199,10 +199,10 @@
+ return found;
+ }
+
+-#if PQ_64BIT_IS_INTEGER
+ void
+ pqueue_print(pqueue_s *pq)
+ {
++#if PQ_64BIT_IS_INTEGER
+ pitem *item = pq->items;
+
+ while(item != NULL)
+@@ -210,8 +210,8 @@
+ printf("item\t" PQ_64BIT_PRINT "\n", item->priority);
+ item = item->next;
+ }
+- }
+ #endif
++ }
+
+ pitem *
+ pqueue_iterator(pqueue_s *pq)
+diff -urw tmp/openssl-0.9.8h/ms/mw.bat openssl-0.9.8h/ms/mw.bat
+--- tmp/openssl-0.9.8h/ms/mw.bat Sat Feb 22 11:00:10 2003
++++ openssl-0.9.8h/ms/mw.bat Wed Jun 4 02:56:54 2008
+@@ -1,17 +1,23 @@
+ @rem OpenSSL with Mingw32
+ @rem --------------------
+
++@rem Include MinGW, MSYS, and ActiveState Perl in path
++set PATH=c:\bin;C:\Perl\bin\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\MinGW\bin;c:\msys\1.0\bin
++
+ @rem Makefile
+ perl util\mkfiles.pl >MINFO
+-perl util\mk1mf.pl Mingw32 >ms\mingw32.mak
++perl util\mk1mf.pl no-idea no-mdc2 no-rc5 Mingw32 >ms\mingw32.mak
++
+ @rem DLL definition files
+-perl util\mkdef.pl 32 libeay >ms\libeay32.def
++perl util\mkdef.pl no-idea no-mdc2 no-rc5 32 libeay >ms\libeay32.def
+ if errorlevel 1 goto end
+-perl util\mkdef.pl 32 ssleay >ms\ssleay32.def
++perl util\mkdef.pl no-idea no-mdc2 no-rc5 32 ssleay >ms\ssleay32.def
+ if errorlevel 1 goto end
+
+ @rem Build the libraries
+-make -f ms/mingw32.mak
++
++@rem JY added --win32
++make --win32 -f ms/mingw32.mak
+ if errorlevel 1 goto end
+
+ @rem Generate the DLLs and input libraries
diff --git a/install-win32/openvpn.nsi b/install-win32/openvpn.nsi
new file mode 100755
index 0000000..f06c5aa
--- /dev/null
+++ b/install-win32/openvpn.nsi
@@ -0,0 +1,886 @@
+; ****************************************************************************
+; * Copyright (C) 2002-2010 OpenVPN Technologies, 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. *
+; ****************************************************************************
+
+; OpenVPN install script for Windows, using NSIS
+
+SetCompressor lzma
+
+!include "MUI.nsh"
+
+!include "defs.nsi"
+!include "guidefs.nsi"
+!include "xguidefs.nsi"
+!include "setpath.nsi"
+!include "GetWindowsVersion.nsi"
+
+!ifdef EXTRACT_FILES
+!include "MultiFileExtract.nsi"
+!endif
+
+!define GEN ".."
+!define BIN "${GEN}\bin"
+!define LIB "${GEN}\lib"
+
+; Which GUI to use (XGUI has priority).
+; We will define either USE_XGUI (XML-based version) or
+; USE_GUI (Mathias Sundman version) but not both.
+!ifdef OPENVPN_XGUI_DEFINED
+!define USE_XGUI
+!else
+!ifdef OPENVPN_GUI_DEFINED
+!define USE_GUI
+!endif
+!endif
+
+!define PRODUCT_ICON "icon.ico"
+
+!ifdef USE_XGUI
+!define XGUI_POSTFIX "X"
+!else
+!define XGUI_POSTFIX ""
+!endif
+
+!ifdef PRODUCT_TAP_DEBUG
+!define DBG_POSTFIX "-DBG"
+!else
+!define DBG_POSTFIX ""
+!endif
+
+!define VERSION "${PRODUCT_VERSION}${XGUI_POSTFIX}${DBG_POSTFIX}"
+
+!define TAP "${PRODUCT_TAP_ID}"
+!define TAPDRV "${TAP}.sys"
+
+; Default service settings
+!define SERV_CONFIG_DIR "$INSTDIR\config"
+!define SERV_CONFIG_EXT "${PRODUCT_FILE_EXT}"
+!define SERV_EXE_PATH "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+!define SERV_LOG_DIR "$INSTDIR\log"
+!define SERV_PRIORITY "NORMAL_PRIORITY_CLASS"
+!define SERV_LOG_APPEND "0"
+
+; XGUI variables
+!define XGUI_EXE ovpn-xgui-en.exe
+!define XGUI_TRAY ovpn-tray.exe
+!define XGUI_XMLSERV ovpn-xmlserv.exe
+!define XGUI_HTDOCS htdocs
+
+!define XGUI_AJAX_GUI_NAME "${PRODUCT_NAME} Ajax GUI"
+!define XGUI_TRANSITION_GUI_NAME "${PRODUCT_NAME} Transitional GUI"
+
+;--------------------------------
+;Configuration
+
+ ;General
+
+ OutFile "${GEN}\${PRODUCT_UNIX_NAME}-${VERSION}${OUTFILE_LABEL}-install.exe"
+
+ ShowInstDetails show
+ ShowUninstDetails show
+
+ ;Folder selection page
+ InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
+
+ ;Remember install folder
+ InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" ""
+
+;--------------------------------
+;Modern UI Configuration
+
+ Name "${PRODUCT_NAME} ${VERSION} ${TITLE_LABEL}"
+
+ !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of ${PRODUCT_NAME}, an Open Source VPN package by James Yonan.\r\n\r\nNote that the Windows version of ${PRODUCT_NAME} will only run on Win 2000, XP, or higher.\r\n\r\n\r\n"
+
+ !define MUI_COMPONENTSPAGE_TEXT_TOP "Select the components to install/upgrade. Stop any ${PRODUCT_NAME} processes or the ${PRODUCT_NAME} service if it is running. All DLLs are installed locally."
+
+ !define MUI_COMPONENTSPAGE_SMALLDESC
+ !ifdef USE_XGUI
+ !define MUI_FINISHPAGE_SHOWREADME "http://openvpn.net/"
+ !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
+ !else
+ !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\INSTALL-win32.txt"
+ !endif
+ !define MUI_FINISHPAGE_NOAUTOCLOSE
+ !define MUI_ABORTWARNING
+ !define MUI_ICON "${GEN}\images\${PRODUCT_ICON}"
+ !define MUI_UNICON "${GEN}\images\${PRODUCT_ICON}"
+ !define MUI_HEADERIMAGE
+ !define MUI_HEADERIMAGE_BITMAP "${GEN}\images\install-whirl.bmp"
+ !define MUI_UNFINISHPAGE_NOAUTOCLOSE
+
+ !insertmacro MUI_PAGE_WELCOME
+ !insertmacro MUI_PAGE_LICENSE "${GEN}\text\license.txt"
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+ !insertmacro MUI_PAGE_INSTFILES
+ !insertmacro MUI_PAGE_FINISH
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro MUI_UNPAGE_FINISH
+
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+ LangString DESC_SecOpenVPNUserSpace ${LANG_ENGLISH} "Install ${PRODUCT_NAME} user-space components, including ${PRODUCT_UNIX_NAME}.exe."
+
+!ifdef USE_GUI
+ LangString DESC_SecOpenVPNGUI ${LANG_ENGLISH} "Install ${PRODUCT_NAME} GUI by Mathias Sundman"
+!endif
+
+!ifdef USE_XGUI
+ LangString DESC_SecOpenVPNXGUI ${LANG_ENGLISH} "Install ${PRODUCT_NAME} XML-based GUI"
+!endif
+
+ LangString DESC_SecOpenVPNEasyRSA ${LANG_ENGLISH} "Install ${PRODUCT_NAME} RSA scripts for X509 certificate management."
+
+ LangString DESC_SecOpenSSLDLLs ${LANG_ENGLISH} "Install OpenSSL DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecPKCS11DLLs ${LANG_ENGLISH} "Install PKCS#11 helper DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecTAP ${LANG_ENGLISH} "Install/Upgrade the TAP virtual device driver. Will not interfere with CIPE."
+
+ LangString DESC_SecService ${LANG_ENGLISH} "Install the ${PRODUCT_NAME} service wrapper (${PRODUCT_UNIX_NAME}serv.exe)"
+
+ LangString DESC_SecOpenSSLUtilities ${LANG_ENGLISH} "Install the OpenSSL Utilities (used for generating public/private key pairs)."
+
+ LangString DESC_SecAddPath ${LANG_ENGLISH} "Add ${PRODUCT_NAME} executable directory to the current user's PATH."
+
+ LangString DESC_SecAddShortcuts ${LANG_ENGLISH} "Add ${PRODUCT_NAME} shortcuts to the current user's Start Menu."
+
+ LangString DESC_SecFileAssociation ${LANG_ENGLISH} "Register ${PRODUCT_NAME} config file association (*.${SERV_CONFIG_EXT})"
+
+;--------------------------------
+;Reserve Files
+
+ ;Things that need to be extracted on first (keep these lines before any File command!)
+ ;Only useful for BZIP2 compression
+
+ ReserveFile "${GEN}\images\install-whirl.bmp"
+
+;--------------------------------
+;Macros
+
+!macro WriteRegStringIfUndef ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 "" +1 +2
+WriteRegStr "${ROOT}" "${SUBKEY}" "${KEY}" '${VALUE}'
+Pop $R0
+!macroend
+
+!macro DelRegStringIfUnchanged ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegValue "${ROOT}" "${SUBKEY}" "${KEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfUnchanged ROOT SUBKEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" ""
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegKey "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfEmpty ROOT SUBKEY
+Push $R0
+EnumRegValue $R0 "${ROOT}" "${SUBKEY}" 1
+StrCmp $R0 "" +1 +2
+DeleteRegKey /ifempty "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+;------------------------------------------
+;Set reboot flag based on tapinstall return
+
+Function CheckReboot
+ IntCmp $R0 1 "" noreboot noreboot
+ IntOp $R0 0 & 0
+ SetRebootFlag true
+ DetailPrint "REBOOT flag set"
+ noreboot:
+FunctionEnd
+
+;--------------------------------
+;Installer Sections
+
+Function .onInit
+ ClearErrors
+
+# Verify that user has admin privs
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to install ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+
+# Delete previous start menu
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+!ifdef CHECK_WINDOWS_VERSION
+# Check windows version
+ Call GetWindowsVersion
+ Pop $1
+ StrCmp $1 "2000" goodwinver
+ StrCmp $1 "XP" goodwinver
+ StrCmp $1 "2003" goodwinver
+ StrCmp $1 "VISTA" goodwinver
+
+ Messagebox MB_OK "Sorry, ${PRODUCT_NAME} does not currently support Windows $1"
+ Abort
+
+goodwinver:
+ System::Call "kernel32::GetCurrentProcess() i .s"
+ System::Call "kernel32::IsWow64Process(i s, *i .r0)"
+ IntCmp $0 0 init32bits
+
+ ; we are running on 64-bit windows
+ StrCmp $1 "VISTA" vista64bummer
+
+# Messagebox MB_OK "Sorry, ${PRODUCT_NAME} doesn't currently support 64-bit Windows."
+# Abort
+
+vista64bummer:
+
+# Messagebox MB_OK "Sorry, ${PRODUCT_NAME} doesn't currently support 64-bit Vista because Microsoft doesn't allow the installation of 64 bit unsigned drivers."
+# Abort
+
+init32bits:
+
+!endif
+
+FunctionEnd
+
+!ifndef SF_SELECTED
+!define SF_SELECTED 1
+!endif
+
+;--------------------
+;Pre-install section
+
+Section -pre
+
+ ; Stop OpenVPN if currently running
+ DetailPrint "Previous Service REMOVE (if exists)"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+!ifdef USE_XGUI
+ DetailPrint "Previous XML Service REMOVE (if exists)"
+ nsExec::ExecToLog '"$INSTDIR\bin\${XGUI_XMLSERV}" -remove'
+ Pop $R0 # return value/error/timeout
+!endif
+
+ Sleep 3000
+
+SectionEnd
+
+Section "${PRODUCT_NAME} User-Space Components" SecOpenVPNUserSpace
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${PRODUCT_UNIX_NAME}.exe"
+
+SectionEnd
+
+!ifdef USE_GUI
+Section "${PRODUCT_NAME} GUI" SecOpenVPNGUI
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${OPENVPN_GUI}"
+
+SectionEnd
+!endif
+
+!ifdef USE_XGUI
+Section "${PRODUCT_NAME} XML-based GUI" SecOpenVPNXGUI
+
+ SetOverwrite on
+
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\${XGUI_EXE}"
+ File "${BIN}\${XGUI_TRAY}"
+ File "${BIN}\${XGUI_XMLSERV}"
+
+ SetOutPath "$INSTDIR\${XGUI_HTDOCS}"
+ File "${GEN}\${XGUI_HTDOCS}\*.*"
+
+SectionEnd
+!endif
+
+Section "${PRODUCT_NAME} RSA Certificate Management Scripts" SecOpenVPNEasyRSA
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\easy-rsa"
+
+ File "${GEN}\easy-rsa\openssl-1.0.0.cnf"
+ File "${GEN}\easy-rsa\vars.bat.sample"
+
+ File "${GEN}\easy-rsa\init-config.bat"
+
+ File "${GEN}\easy-rsa\README.txt"
+ File "${GEN}\easy-rsa\build-ca.bat"
+ File "${GEN}\easy-rsa\build-dh.bat"
+ File "${GEN}\easy-rsa\build-key-server.bat"
+ File "${GEN}\easy-rsa\build-key.bat"
+ File "${GEN}\easy-rsa\build-key-pkcs12.bat"
+ File "${GEN}\easy-rsa\clean-all.bat"
+ File "${GEN}\easy-rsa\index.txt.start"
+ File "${GEN}\easy-rsa\revoke-full.bat"
+ File "${GEN}\easy-rsa\serial.start"
+
+SectionEnd
+
+Section "${PRODUCT_NAME} Service" SecService
+
+ SetOverwrite on
+
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\${PRODUCT_UNIX_NAME}serv.exe"
+
+ SetOutPath "$INSTDIR\config"
+
+ FileOpen $R0 "$INSTDIR\config\README.txt" w
+ FileWrite $R0 "This directory should contain ${PRODUCT_NAME} configuration files$\r$\n"
+ FileWrite $R0 "each having an extension of .${SERV_CONFIG_EXT}$\r$\n"
+ FileWrite $R0 "$\r$\n"
+ FileWrite $R0 "When ${PRODUCT_NAME} is started as a service, a separate ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "process will be instantiated for each configuration file.$\r$\n"
+ FileClose $R0
+
+ SetOutPath "$INSTDIR\sample-config"
+ File "${GEN}\samples\sample.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\client.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\server.${SERV_CONFIG_EXT}"
+
+ CreateDirectory "$INSTDIR\log"
+ FileOpen $R0 "$INSTDIR\log\README.txt" w
+ FileWrite $R0 "This directory will contain the log files for ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "sessions which are being run as a service.$\r$\n"
+ FileClose $R0
+
+SectionEnd
+
+Section "${PRODUCT_NAME} File Associations" SecFileAssociation
+SectionEnd
+
+Section "OpenSSL DLLs" SecOpenSSLDLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${LIB}\libeay32.dll"
+ File "${LIB}\libssl32.dll"
+
+SectionEnd
+
+Section "OpenSSL Utilities" SecOpenSSLUtilities
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\openssl.exe"
+
+SectionEnd
+
+Section "PKCS#11 DLLs" SecPKCS11DLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${LIB}\libpkcs11-helper-1.dll"
+
+SectionEnd
+
+Section "TAP Virtual Ethernet Adapter" SecTAP
+
+ SetOverwrite on
+
+ FileOpen $R0 "$INSTDIR\bin\addtap.bat" w
+ FileWrite $R0 "rem Add a new TAP virtual ethernet adapter$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ FileOpen $R0 "$INSTDIR\bin\deltapall.bat" w
+ FileWrite $R0 "echo WARNING: this script will delete ALL TAP virtual adapters (use the device manager to delete adapters one at a time)$\r$\n"
+ FileWrite $R0 "pause$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ ; Check if we are running on a 64 bit system.
+ System::Call "kernel32::GetCurrentProcess() i .s"
+ System::Call "kernel32::IsWow64Process(i s, *i .r0)"
+ IntCmp $0 0 tap-32bit
+
+; tap-64bit:
+
+ DetailPrint "We are running on a 64-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+
+ File "${GEN}\tapinstall\amd64\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+
+ File "${GEN}\driver\amd64\OemWin2k.inf"
+ File "${GEN}\driver\amd64\${PRODUCT_TAP_ID}.cat"
+ File "${GEN}\driver\amd64\${TAPDRV}"
+
+goto tapend
+
+tap-32bit:
+
+ DetailPrint "We are running on a 32-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+ File "${GEN}\tapinstall\i386\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+ File "${GEN}\driver\i386\OemWin2k.inf"
+ File "${GEN}\driver\i386\${PRODUCT_TAP_ID}.cat"
+ File "${GEN}\driver\i386\${TAPDRV}"
+
+ tapend:
+
+SectionEnd
+
+Section "Add ${PRODUCT_NAME} to PATH" SecAddPath
+
+ ; remove previously set path (if any)
+ Push "$INSTDIR\bin"
+ Call RemoveFromPath
+
+ ; append our bin directory to end of current user path
+ Push "$INSTDIR\bin"
+ Call AddToPath
+
+SectionEnd
+
+Section "Add Shortcuts to Start Menu" SecAddShortcuts
+
+ SetOverwrite on
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Documentation"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Windows Notes.url" "InternetShortcut" "URL" "http://openvpn.net/INSTALL-win32.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Manual Page.url" "InternetShortcut" "URL" "http://openvpn.net/man.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} HOWTO.url" "InternetShortcut" "URL" "http://openvpn.net/howto.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Web Site.url" "InternetShortcut" "URL" "http://openvpn.net/"
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\Uninstall.exe"
+
+SectionEnd
+
+;--------------------
+;Post-install section
+
+Section -post
+
+ SetOverwrite on
+
+ ; delete old devcon.exe
+ Delete "$INSTDIR\bin\devcon.exe"
+
+ ; Store README, license, icon
+ SetOverwrite on
+ SetOutPath $INSTDIR
+ !ifndef USE_XGUI
+ File "${GEN}\text\INSTALL-win32.txt"
+ !endif
+ File "${GEN}\text\license.txt"
+ File "${GEN}\images\${PRODUCT_ICON}"
+
+ ; store sample config files
+ !ifdef SAMPCONF_DIR
+ SetOverwrite on
+ SetOutPath "$INSTDIR\config"
+ !ifdef SAMPCONF_CONF
+ File "${GEN}\conf\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ File "${GEN}\conf\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ File "${GEN}\conf\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ File "${GEN}\conf\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ File "${GEN}\conf\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ File "${GEN}\conf\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ File "${GEN}\conf\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ File "${GEN}\conf\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ ; Try to extract files if present
+ !ifdef EXTRACT_FILES
+ Push "$INSTDIR"
+ Call MultiFileExtract
+ Pop $R0
+ IntCmp $R0 0 +3 +1 +1
+ DetailPrint "MultiFileExtract Failed status=$R0"
+ goto +2
+ DetailPrint "MultiFileExtract Succeeded"
+ !endif
+
+ ;
+ ; install/upgrade TAP driver if selected, using tapinstall.exe
+ ;
+ SectionGetFlags ${SecTAP} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" notap notap
+ ; TAP install/update was selected.
+ ; Should we install or update?
+ ; If tapinstall error occurred, $5 will
+ ; be nonzero.
+ IntOp $5 0 & 0
+ nsExec::ExecToStack '"$INSTDIR\bin\tapinstall.exe" hwids ${TAP}'
+ Pop $R0 # return value/error/timeout
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall hwids returned: $R0"
+
+ ; If tapinstall output string contains "${TAP}" we assume
+ ; that TAP device has been previously installed,
+ ; therefore we will update, not install.
+ Push "${TAP}"
+ Call StrStr
+ Pop $R0
+
+ IntCmp $5 0 "" tapinstall_check_error tapinstall_check_error
+ IntCmp $R0 -1 tapinstall
+
+ ;tapupdate:
+ DetailPrint "TAP UPDATE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" update "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall update returned: $R0"
+ Goto tapinstall_check_error
+
+ tapinstall:
+ DetailPrint "TAP REMOVE OLD TAP"
+
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove TAP0801'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove TAP0801 returned: $R0"
+
+ DetailPrint "TAP INSTALL (${TAP})"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall install returned: $R0"
+
+ tapinstall_check_error:
+ DetailPrint "tapinstall cumulative status: $5"
+ IntCmp $5 0 notap
+ MessageBox MB_OK "An error occurred installing the TAP device driver."
+
+ notap:
+
+ ; Store install folder in registry
+ WriteRegStr HKLM SOFTWARE\${PRODUCT_NAME} "" $INSTDIR
+
+ ; install as a service if requested
+ SectionGetFlags ${SecService} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noserv noserv
+
+ ; set registry parameters for openvpnserv
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_dir" "${SERV_CONFIG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_ext" "${SERV_CONFIG_EXT}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "exe_path" "${SERV_EXE_PATH}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_dir" "${SERV_LOG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "priority" "${SERV_PRIORITY}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_append" "${SERV_LOG_APPEND}"
+
+ ; install openvpnserv as a service (to be started manually from service control manager)
+ DetailPrint "Service INSTALL"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -install'
+ Pop $R0 # return value/error/timeout
+
+ noserv:
+ !ifdef USE_XGUI
+ IfFileExists "$INSTDIR\bin\${XGUI_XMLSERV}" "" fileass
+ ; install and automatically start XML service
+ DetailPrint "XML Service INSTALL"
+ nsExec::ExecToLog '"$INSTDIR\bin\${XGUI_XMLSERV}" -install'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 2000
+
+ DetailPrint "XML Service START"
+ nsExec::ExecToLog '"$INSTDIR\bin\${XGUI_XMLSERV}" -start'
+ Pop $R0 # return value/error/timeout
+
+ !endif
+
+ ; Create file association if requested
+ fileass:
+ SectionGetFlags ${SecFileAssociation} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noass noass
+ WriteRegStr HKCR ".${SERV_CONFIG_EXT}" "" "${PRODUCT_NAME}File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File" "" "${PRODUCT_NAME} Config File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell" "" "open"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\DefaultIcon" "" "$INSTDIR\${PRODUCT_ICON},0"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\open\command" "" 'notepad.exe "%1"'
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run" "" "Start ${PRODUCT_NAME} on this config file"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run\command" "" '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" --pause-exit --config "%1"'
+
+ ; Create start menu folders
+ noass:
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Utilities"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts"
+
+ ; Create start menu and desktop shortcuts to OpenVPN GUI
+ !ifdef USE_GUI
+ IfFileExists "$INSTDIR\bin\${OPENVPN_GUI}" "" tryaddxgui
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}" ""
+ CreateShortcut "$DESKTOP\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}"
+ !endif
+
+ ; Create start menu and desktop shortcuts to OpenVPN XGUI
+ tryaddxgui:
+ !ifdef USE_XGUI
+ IfFileExists "$INSTDIR\bin\${XGUI_EXE}" "" tryaddtray
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${XGUI_TRANSITION_GUI_NAME}.lnk" "$INSTDIR\bin\${XGUI_EXE}" ""
+# CreateShortcut "$DESKTOP\${XGUI_TRANSITION_GUI_NAME}.lnk" "$INSTDIR\bin\${XGUI_EXE}"
+ tryaddtray:
+ IfFileExists "$INSTDIR\bin\${XGUI_TRAY}" "" tryaddtap
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${XGUI_AJAX_GUI_NAME}.lnk" "$INSTDIR\bin\${XGUI_EXE}" ""
+ CreateShortcut "$DESKTOP\${XGUI_AJAX_GUI_NAME}.lnk" "$INSTDIR\bin\${XGUI_TRAY}"
+ !endif
+
+ ; Create start menu shortcuts to addtap.bat and deltapall.bat
+ tryaddtap:
+ IfFileExists "$INSTDIR\bin\addtap.bat" "" trydeltap
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Add a new TAP virtual ethernet adapter.lnk" "$INSTDIR\bin\addtap.bat" ""
+
+ trydeltap:
+ IfFileExists "$INSTDIR\bin\deltapall.bat" "" config_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Delete ALL TAP virtual ethernet adapters.lnk" "$INSTDIR\bin\deltapall.bat" ""
+
+ ; Create start menu shortcuts for config and log directories
+ config_shortcut:
+ IfFileExists "$INSTDIR\config" "" log_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} configuration file directory.lnk" "$INSTDIR\config" ""
+
+ log_shortcut:
+ IfFileExists "$INSTDIR\log" "" samp_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} log file directory.lnk" "$INSTDIR\log" ""
+
+ samp_shortcut:
+ IfFileExists "$INSTDIR\sample-config" "" genkey_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} Sample Configuration Files.lnk" "$INSTDIR\sample-config" ""
+
+ genkey_shortcut:
+ IfFileExists "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" "" noshortcuts
+ IfFileExists "$INSTDIR\config" "" noshortcuts
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Generate a static ${PRODUCT_NAME} key.lnk" "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" '--pause-exit --verb 3 --genkey --secret "$INSTDIR\config\key.txt"' "$INSTDIR\${PRODUCT_ICON}" 0
+
+ noshortcuts:
+ ; Create uninstaller
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ ; Show up in Add/Remove programs
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME} ${VERSION}"
+ WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$INSTDIR\${PRODUCT_ICON}"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${VERSION}"
+
+ ; Advise a reboot
+ ;Messagebox MB_OK "IMPORTANT: Rebooting the system is advised in order to finalize TAP driver installation/upgrade (this is an informational message only, pressing OK will not reboot)."
+
+SectionEnd
+
+;--------------------------------
+;Descriptions
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNUserSpace} $(DESC_SecOpenVPNUserSpace)
+ !ifdef USE_GUI
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNGUI} $(DESC_SecOpenVPNGUI)
+ !endif
+ !ifdef USE_XGUI
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNXGUI} $(DESC_SecOpenVPNXGUI)
+ !endif
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNEasyRSA} $(DESC_SecOpenVPNEasyRSA)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecTAP} $(DESC_SecTAP)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLUtilities} $(DESC_SecOpenSSLUtilities)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLDLLs} $(DESC_SecOpenSSLDLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecPKCS11DLLs} $(DESC_SecPKCS11DLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddPath} $(DESC_SecAddPath)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddShortcuts} $(DESC_SecAddShortcuts)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecFileAssociation} $(DESC_SecFileAssociation)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Function un.onInit
+ ClearErrors
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to uninstall ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+FunctionEnd
+
+Section "Uninstall"
+
+!ifdef USE_XGUI
+ DetailPrint "XML Service REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\${XGUI_XMLSERV}" -remove'
+ Pop $R0 # return value/error/timeout
+!endif
+
+ ; Stop OpenVPN if currently running
+ DetailPrint "Service REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 3000
+
+ DetailPrint "TAP REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove returned: $R0"
+
+ Push "$INSTDIR\bin"
+ Call un.RemoveFromPath
+
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+ ; delete sample config files
+ !ifdef SAMPCONF_DIR
+ !ifdef SAMPCONF_CONF
+ Delete "$INSTDIR\config\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ Delete "$INSTDIR\config\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ Delete "$INSTDIR\config\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ Delete "$INSTDIR\config\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ Delete "$INSTDIR\config\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ Delete "$INSTDIR\config\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ Delete "$INSTDIR\config\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ Delete "$INSTDIR\config\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ !ifdef USE_GUI
+ Delete "$INSTDIR\bin\${OPENVPN_GUI}"
+ Delete "$DESKTOP\${PRODUCT_NAME} GUI.lnk"
+ !endif
+
+ !ifdef USE_XGUI
+ Delete "$INSTDIR\bin\${XGUI_EXE}"
+ Delete "$INSTDIR\bin\${XGUI_TRAY}"
+ Delete "$INSTDIR\bin\${XGUI_XMLSERV}"
+ RMDir /r "$INSTDIR\${XGUI_HTDOCS}"
+ Delete "$DESKTOP\${XGUI_AJAX_GUI_NAME}.lnk"
+ Delete "$DESKTOP\${XGUI_TRANSITION_GUI_NAME}.lnk"
+ !endif
+
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe"
+ Delete "$INSTDIR\bin\libeay32.dll"
+ Delete "$INSTDIR\bin\libssl32.dll"
+ Delete "$INSTDIR\bin\libpkcs11-helper-1.dll"
+ Delete "$INSTDIR\bin\tapinstall.exe"
+ Delete "$INSTDIR\bin\addtap.bat"
+ Delete "$INSTDIR\bin\deltapall.bat"
+
+ Delete "$INSTDIR\config\README.txt"
+ Delete "$INSTDIR\config\sample.${SERV_CONFIG_EXT}.txt"
+
+ Delete "$INSTDIR\log\README.txt"
+
+ Delete "$INSTDIR\driver\OemWin2k.inf"
+ Delete "$INSTDIR\driver\${PRODUCT_TAP_ID}.cat"
+ Delete "$INSTDIR\driver\${TAPDRV}"
+
+ Delete "$INSTDIR\bin\openssl.exe"
+
+ Delete "$INSTDIR\INSTALL-win32.txt"
+ Delete "$INSTDIR\${PRODUCT_ICON}"
+ Delete "$INSTDIR\license.txt"
+ Delete "$INSTDIR\Uninstall.exe"
+
+ Delete "$INSTDIR\easy-rsa\openssl-1.0.0.cnf"
+ Delete "$INSTDIR\easy-rsa\vars.bat.sample"
+ Delete "$INSTDIR\easy-rsa\init-config.bat"
+ Delete "$INSTDIR\easy-rsa\README.txt"
+ Delete "$INSTDIR\easy-rsa\build-ca.bat"
+ Delete "$INSTDIR\easy-rsa\build-dh.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-server.bat"
+ Delete "$INSTDIR\easy-rsa\build-key.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-pkcs12.bat"
+ Delete "$INSTDIR\easy-rsa\clean-all.bat"
+ Delete "$INSTDIR\easy-rsa\index.txt.start"
+ Delete "$INSTDIR\easy-rsa\revoke-key.bat"
+ Delete "$INSTDIR\easy-rsa\revoke-full.bat"
+ Delete "$INSTDIR\easy-rsa\serial.start"
+
+ Delete "$INSTDIR\sample-config\*.${PRODUCT_FILE_EXT}"
+
+ RMDir "$INSTDIR\bin"
+ RMDir "$INSTDIR\config"
+ RMDir "$INSTDIR\driver"
+ RMDir "$INSTDIR\easy-rsa"
+ RMDir "$INSTDIR\sample-config"
+ RMDir /r "$INSTDIR\log"
+ RMDir "$INSTDIR"
+
+ !insertmacro DelRegKeyIfUnchanged HKCR ".${SERV_CONFIG_EXT}" "${PRODUCT_NAME}File"
+ DeleteRegKey HKCR "${PRODUCT_NAME}File"
+ DeleteRegKey HKLM SOFTWARE\${PRODUCT_NAME}
+ DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+
+ ;Messagebox MB_OK "IMPORTANT: If you intend on reinstalling ${PRODUCT_NAME} after this uninstall, and you are running Win2K, you are strongly urged to reboot before reinstalling (this is an informational message only, pressing OK will not reboot)."
+
+SectionEnd
diff --git a/install-win32/sample.ovpn b/install-win32/sample.ovpn
new file mode 100755
index 0000000..5accd57
--- /dev/null
+++ b/install-win32/sample.ovpn
@@ -0,0 +1,103 @@
+# Edit this file, and save to a .ovpn extension
+# so that OpenVPN will activate it when run
+# as a service.
+
+# Change 'myremote' to be your remote host,
+# or comment out to enter a listening
+# server mode.
+remote myremote
+
+# Uncomment this line to use a different
+# port number than the default of 1194.
+; port 1194
+
+# Choose one of three protocols supported by
+# OpenVPN. If left commented out, defaults
+# to udp.
+; proto [tcp-server | tcp-client | udp]
+
+# You must specify one of two possible network
+# protocols, 'dev tap' or 'dev tun' to be used
+# on both sides of the connection. 'tap' creates
+# a VPN using the ethernet protocol while 'tun'
+# uses the IP protocol. You must use 'tap'
+# if you are ethernet bridging or want to route
+# broadcasts. 'tun' is somewhat more efficient
+# but requires configuration of client software
+# to not depend on broadcasts. Some platforms
+# such as Solaris, OpenBSD, and Mac OS X only
+# support 'tun' interfaces, so if you are
+# connecting to such a platform, you must also
+# use a 'tun' interface on the Windows side.
+
+# Enable 'dev tap' or 'dev tun' but not both!
+dev tap
+
+# This is a 'dev tap' ifconfig that creates
+# a virtual ethernet subnet.
+# 10.3.0.1 is the local VPN IP address
+# and 255.255.255.0 is the VPN subnet.
+# Only define this option for 'dev tap'.
+ifconfig 10.3.0.1 255.255.255.0
+
+# This is a 'dev tun' ifconfig that creates
+# a point-to-point IP link.
+# 10.3.0.1 is the local VPN IP address and
+# 10.3.0.2 is the remote VPN IP address.
+# Only define this option for 'dev tun'.
+# Make sure to include the "tun-mtu" option
+# on the remote machine, but swap the order
+# of the ifconfig addresses.
+;tun-mtu 1500
+;ifconfig 10.3.0.1 10.3.0.2
+
+# If you have fragmentation issues or misconfigured
+# routers in the path which block Path MTU discovery,
+# lower the TCP MSS and internally fragment non-TCP
+# protocols.
+;fragment 1300
+;mssfix
+
+# If you have set up more than one TAP-Win32 adapter
+# on your system, you must refer to it by name.
+;dev-node my-tap
+
+# You can generate a static OpenVPN key
+# by selecting the Generate Key option
+# in the start menu.
+#
+# You can also generate key.txt manually
+# with the following command:
+# openvpn --genkey --secret key.txt
+#
+# key must match on both ends of the connection,
+# so you should generate it on one machine and
+# copy it to the other over a secure medium.
+# Place key.txt in the same directory as this
+# config file.
+secret key.txt
+
+# Uncomment this section for a more reliable
+# detection when a system loses its connection.
+# For example, dial-ups or laptops that travel
+# to other locations.
+#
+# If this section is enabled and "myremote"
+# above is a dynamic DNS name (i.e. dyndns.org),
+# OpenVPN will dynamically "follow" the IP
+# address of "myremote" if it changes.
+; ping-restart 60
+; ping-timer-rem
+; persist-tun
+; persist-key
+; resolv-retry 86400
+
+# keep-alive ping
+ping 10
+
+# enable LZO compression
+comp-lzo
+
+# moderate verbosity
+verb 4
+mute 10
diff --git a/install-win32/setpath.nsi b/install-win32/setpath.nsi
new file mode 100755
index 0000000..a9626c3
--- /dev/null
+++ b/install-win32/setpath.nsi
@@ -0,0 +1,231 @@
+; Modify the user's PATH variable.
+;
+; Modified by JY to have both a RemoveFromPath
+; and an un.RemoveFromPath which are basically
+; copies of each other. Why does NSIS demand
+; this nonsense?
+;
+; Modified Feb 14, 2005 by Mathias Sundman:
+; Added code to remove the semicolon at the end of the path
+; when uninstalling.
+;
+; Added code to make sure we don't insert an extra semicolon
+; before our path if there already exist one at the end of
+; the original path.
+;
+; Removed duplicated "un. and install" functions and made
+; macros to duplicate the code instead.
+
+; example usage
+;
+;Section "Add to path"
+; Push $INSTDIR
+; Call AddToPath
+;SectionEnd
+;
+;# ...
+;
+;Section "uninstall"
+; # ...
+; Push $INSTDIR
+; Call un.RemoveFromPath
+; # ...
+;SectionEnd
+
+!verbose 3
+!include "WinMessages.NSH"
+!verbose 4
+
+;====================================================
+; AddToPath - Adds the given dir to the search path.
+; Input - head of the stack
+; Note - Win9x systems requires reboot
+;====================================================
+Function AddToPath
+ Exch $0
+ Push $1
+ Push $2
+
+ Call IsNT
+ Pop $1
+ StrCmp $1 1 AddToPath_NT
+ ; Not on NT
+ StrCpy $1 $WINDIR 2
+ FileOpen $1 "$1\autoexec.bat" a
+ FileSeek $1 0 END
+ GetFullPathName /SHORT $0 $0
+ FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n"
+ FileClose $1
+ Goto AddToPath_done
+
+ AddToPath_NT:
+ ReadRegStr $1 HKCU "Environment" "PATH"
+ StrCpy $2 $1 1 -1 # copy last char
+ StrCmp $2 ";" 0 +2 # if last char == ;
+ StrCpy $1 $1 -1 # remove last char
+
+ StrCmp $1 "" AddToPath_NTdoIt
+ StrCpy $0 "$1;$0"
+ Goto AddToPath_NTdoIt
+ AddToPath_NTdoIt:
+ WriteRegExpandStr HKCU "Environment" "PATH" $0
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+ AddToPath_done:
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+;====================================================
+; RemoveFromPath - Remove a given dir from the path
+; Input: head of the stack
+;====================================================
+!macro RemoveFromPath un
+Function ${un}RemoveFromPath
+ Exch $0
+ Push $1
+ Push $2
+ Push $3
+ Push $4
+ Push $5
+
+ Call ${un}IsNT
+ Pop $1
+ StrCmp $1 1 RemoveFromPath_NT
+ ; Not on NT
+ StrCpy $1 $WINDIR 2
+ FileOpen $1 "$1\autoexec.bat" r
+ GetTempFileName $4
+ FileOpen $2 $4 w
+ GetFullPathName /SHORT $0 $0
+ StrCpy $0 "SET PATH=%PATH%;$0"
+ SetRebootFlag true
+ Goto RemoveFromPath_dosLoop
+
+ RemoveFromPath_dosLoop:
+ FileRead $1 $3
+ StrCmp $3 "$0$\r$\n" RemoveFromPath_dosLoop
+ StrCmp $3 "$0$\n" RemoveFromPath_dosLoop
+ StrCmp $3 "$0" RemoveFromPath_dosLoop
+ StrCmp $3 "" RemoveFromPath_dosLoopEnd
+ FileWrite $2 $3
+ Goto RemoveFromPath_dosLoop
+
+ RemoveFromPath_dosLoopEnd:
+ FileClose $2
+ FileClose $1
+ StrCpy $1 $WINDIR 2
+ Delete "$1\autoexec.bat"
+ CopyFiles /SILENT $4 "$1\autoexec.bat"
+ Delete $4
+ Goto RemoveFromPath_done
+
+ RemoveFromPath_NT:
+ StrLen $2 $0
+ ReadRegStr $1 HKCU "Environment" "PATH"
+ Push $1
+ Push $0
+ Call ${un}StrStr ; Find $0 in $1
+ Pop $0 ; pos of our dir
+ IntCmp $0 -1 RemoveFromPath_done
+ ; else, it is in path
+ StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+ IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+ IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+ StrLen $0 $1
+ StrCpy $1 $1 $0 $2
+ StrCpy $3 "$3$1"
+
+ StrCpy $5 $3 1 -1 # copy last char
+ StrCmp $5 ";" 0 +2 # if last char == ;
+ StrCpy $3 $3 -1 # remove last char
+
+ WriteRegExpandStr HKCU "Environment" "PATH" $3
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+ RemoveFromPath_done:
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+!macroend
+!insertmacro RemoveFromPath ""
+!insertmacro RemoveFromPath "un."
+
+
+;====================================================
+; StrStr - Finds a given string in another given string.
+; Returns -1 if not found and the pos if found.
+; Input: head of the stack - string to find
+; second in the stack - string to find in
+; Output: head of the stack
+;====================================================
+!macro StrStr un
+Function ${un}StrStr
+ Push $0
+ Exch
+ Pop $0 ; $0 now have the string to find
+ Push $1
+ Exch 2
+ Pop $1 ; $1 now have the string to find in
+ Exch
+ Push $2
+ Push $3
+ Push $4
+ Push $5
+
+ StrCpy $2 -1
+ StrLen $3 $0
+ StrLen $4 $1
+ IntOp $4 $4 - $3
+
+ StrStr_loop:
+ IntOp $2 $2 + 1
+ IntCmp $2 $4 0 0 StrStrReturn_notFound
+ StrCpy $5 $1 $3 $2
+ StrCmp $5 $0 StrStr_done StrStr_loop
+
+ StrStrReturn_notFound:
+ StrCpy $2 -1
+
+ StrStr_done:
+ Pop $5
+ Pop $4
+ Pop $3
+ Exch $2
+ Exch 2
+ Pop $0
+ Pop $1
+FunctionEnd
+!macroend
+!insertmacro StrStr ""
+!insertmacro StrStr "un."
+
+;====================================================
+; IsNT - Returns 1 if the current system is NT, 0
+; otherwise.
+; Output: head of the stack
+;====================================================
+!macro IsNT un
+Function ${un}IsNT
+ Push $0
+ ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+ StrCmp $0 "" 0 IsNT_yes
+ ; we are not NT.
+ Pop $0
+ Push 0
+ Return
+
+ IsNT_yes:
+ ; NT!!!
+ Pop $0
+ Push 1
+FunctionEnd
+!macroend
+!insertmacro IsNT ""
+!insertmacro IsNT "un."
+
diff --git a/install-win32/settings.in b/install-win32/settings.in
new file mode 100644
index 0000000..4a0a564
--- /dev/null
+++ b/install-win32/settings.in
@@ -0,0 +1,71 @@
+# Version numbers, settings, and dependencies
+# for Windows OpenVPN installer.
+
+# Get the OpenVPN version number
+!include "autodefs/version.in"
+
+# Branding
+!define PRODUCT_NAME "OpenVPN"
+!define PRODUCT_UNIX_NAME "openvpn"
+!define PRODUCT_FILE_EXT "ovpn"
+
+# Allow --askpass and --auth-user-pass passwords to be read from a file
+;!define ENABLE_PASSWORD_SAVE
+
+# Include the OpenVPN GUI exe in the installer.
+# May be undefined.
+!define OPENVPN_GUI_DIR "../openvpn-gui"
+!define OPENVPN_GUI "openvpn-gui-1.0.3.exe"
+
+# Include the OpenVPN XML-based GUI exe in the installer.
+# May be undefined.
+;!define OPENVPN_XGUI_DIR "../ovpnxml"
+
+# Prebuilt libraries. DMALLOC is optional.
+!define OPENSSL_DIR "../openssl.mingw/openssl-0.9.8o"
+!define LZO_DIR "../lzo-2.02"
+!define PKCS11_HELPER_DIR "../pkcs11-helper"
+;!define DMALLOC_DIR "../dmalloc-5.4.2"
+
+# Prebuilt TAP drivers and tapinstall
+!define TAPBINSRC "../tap_dist"
+
+# Directory containing python script for signing .exe files
+!define SIGNTOOL "../signtool"
+
+# Optional directory of prebuilt OpenVPN binary components,
+# to be used as a source when build-from-scratch prerequisites
+# are not met.
+;!define GENOUT_PREBUILT "../gen-prebuilt"
+
+# -j parameter passed to make
+!define MAKE_JOBS 1
+
+# output directory for built binaries
+# and other generated files
+!define GENOUT "gen"
+
+# delete GENOUT directory before starting
+# set to "yes" or "no"
+!define CLEAN "yes"
+
+# Don't strip executables and DLLs
+;!define NO_STRIP
+
+; DEBUGGING -- set to something like "-DBG2"
+!define OUTFILE_LABEL ""
+
+; DEBUGGING -- set to something like "DEBUG2"
+!define TITLE_LABEL ""
+
+# include a sample configuration file and key
+;!define SAMPCONF_DIR "test-key"
+;!define SAMPCONF_CONF "test.ovpn"
+;!define SAMPCONF_P12 "test.p12"
+;!define SAMPCONF_TA "ta.key"
+;!define SAMPCONF_CA "ca.crt"
+;!define SAMPCONF_CRT "test.crt"
+;!define SAMPCONF_KEY "test.key"
+
+# Extract files embedded in installer
+;!define EXTRACT_FILES
diff --git a/install-win32/trans.pl b/install-win32/trans.pl
new file mode 100644
index 0000000..34fd207
--- /dev/null
+++ b/install-win32/trans.pl
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+# This script translates a simple definition-based grammar
+# to either C, sh, Javascript, or in (in = identity grammar, i.e.
+# same grammar as input).
+#
+# Input grammar:
+# (1) comments having ';' or '#' as the first char in the line
+# (2) a blank line
+# (3) !include "file"
+# (4) !define foo bar
+# (5) !define foo "bar"
+#
+# Environmental variables can be used to override a setting.
+# The special value "null" causes the variable to be undefined.
+# If an environmental value is bracketed, i.e [abc], the brackets
+# will be converted to double quotes prior to output.
+
+sub comment {
+ my ($cmt) = @_;
+ print "//$cmt\n" if ($mode =~ /^(c|js|h)$/);
+ print "#$cmt\n" if ($mode =~ /^(sh|nsi|in)$/);
+}
+
+sub define {
+ my ($name, $value) = @_;
+ if ($mode eq "sh") {
+ $value="true" if !$value;
+ print "[ -z \"\$$name\" ] && export $name=$value\n";
+ print "[ \"\$$name\" = \"$nulltag\" ] && unset $name\n";
+ } else {
+ if ($ENV{$name}) {
+ $value = $ENV{$name};
+ $value = "\"$1\"" if ($value =~ /\[(.*)\]$/);
+ }
+ if ($value ne $nulltag) {
+ print "#define $name $value\n" if ($mode =~ /^(c|h)$/);
+ print "!define $name $value\n" if ($mode =~ /^(nsi|in)$/);
+ print "var $name=$value;\n" if ($mode eq "js");
+ } else {
+ print "//#undef $name\n" if ($mode =~ /^(c|h)$/);
+ print "#!undef $name\n" if ($mode eq "nsi");
+ print ";!undef $name\n" if ($mode eq "in");
+ print "//undef $name\n" if ($mode eq "js");
+ }
+ }
+}
+
+sub include_file {
+ local $_;
+ $include_file_level++;
+ die "!include file nesting too deep" if ($include_file_level > $max_inc_depth);
+ my ($parm) = @_;
+ my $fn = "$incdir/$parm";
+ local *IN;
+ open(IN, "< $fn") or die "cannot open $fn";
+ while (<IN>) {
+ chomp;
+ if (/^\s*$/) {
+ print "\n";
+ } elsif (/^[#;](.*)$/) {
+ comment ($1);
+ } elsif (/^!define\s+(\w+)(?:\s+(.*?))?\s*$/) {
+ define ($1, $2);
+ } elsif (/^!include\s+"(.+)"$/) {
+ include_file ($1);
+ } else {
+ die "can't parse this line: $_\n";
+ }
+ }
+ $include_file_level--;
+}
+
+die "usage: trans <c|h|sh|js|nsi|in> [-I<dir>] [files ...]" if (@ARGV < 1);
+
+($mode) = shift(@ARGV);
+die "mode must be one of c, h, sh, js, nsi, or in" if !($mode =~ /^(c|h|sh|js|nsi|in)$/);
+
+$nulltag = "null";
+$max_inc_depth = 10;
+$include_file_level = 0;
+$incdir = ".";
+
+comment(" This file was automatically generated by trans.pl");
+
+while ($arg=shift(@ARGV)) {
+ if ($arg =~ /^-/) {
+ if ($arg =~ /^-I(.*)$/) {
+ $incdir = $1;
+ } else {
+ die "unrecognized option: $arg";
+ }
+ } else {
+ print "\n";
+ include_file ($arg);
+ }
+}
diff --git a/install-win32/u2d.c b/install-win32/u2d.c
new file mode 100755
index 0000000..bf1f5e8
--- /dev/null
+++ b/install-win32/u2d.c
@@ -0,0 +1,20 @@
+#include <stdio.h>
+
+int
+main (int argc, char *argv[])
+{
+ int c;
+ int enable = 1;
+
+ while ((c = getchar()) != EOF)
+ {
+#if 0
+ if (c == '\r')
+ enable = 0;
+ if (enable && c == '\n')
+ putchar ('\r');
+#endif
+ putchar (c);
+ }
+ return 0;
+}
diff --git a/install-win32/winconfig b/install-win32/winconfig
new file mode 100644
index 0000000..9d686c9
--- /dev/null
+++ b/install-win32/winconfig
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# prepare files for building on Windows
+# run from top directory: install-win32/winconfig
+
+rm -rf autodefs
+mkdir autodefs
+
+# build multi-grammar definition files
+perl install-win32/m4todef.pl <version.m4 >autodefs/version.in
+for g in "h" "sh" "nsi" "in" ; do
+ perl install-win32/trans.pl $g install-win32/settings.in >autodefs/defs.$g
+done
+
+cat /dev/null >autodefs/guidefs.nsi
+
+echo '#include "autodefs/defs.h"' >autodefs.h
+echo '#include "config.h"' >>autodefs.h
diff --git a/integer.h b/integer.h
new file mode 100644
index 0000000..f0fc196
--- /dev/null
+++ b/integer.h
@@ -0,0 +1,114 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef INTEGER_H
+#define INTEGER_H
+
+#include "error.h"
+
+/*
+ * min/max functions
+ */
+
+static inline int
+max_int (int x, int y)
+{
+ if (x > y)
+ return x;
+ else
+ return y;
+}
+
+static inline int
+min_int (int x, int y)
+{
+ if (x < y)
+ return x;
+ else
+ return y;
+}
+
+static inline int
+constrain_int (int x, int min, int max)
+{
+ if (min > max)
+ return min;
+ if (x < min)
+ return min;
+ else if (x > max)
+ return max;
+ else
+ return x;
+}
+
+/*
+ * Functions used for circular buffer index arithmetic.
+ */
+
+/*
+ * Return x - y on a circle of circumference mod by shortest path.
+ *
+ * 0 <= x < mod
+ * 0 <= y < mod
+ */
+static inline int
+modulo_subtract(int x, int y, int mod)
+{
+ const int d1 = x - y;
+ const int d2 = (x > y ? -mod : mod) + d1;
+ ASSERT (0 <= x && x < mod && 0 <= y && y < mod);
+ return abs(d1) > abs(d2) ? d2 : d1;
+}
+
+/*
+ * Return x + y on a circle of circumference mod.
+ *
+ * 0 <= x < mod
+ * -mod <= y <= mod
+ */
+static inline int
+modulo_add(int x, int y, int mod)
+{
+ int sum = x + y;
+ ASSERT (0 <= x && x < mod && -mod <= y && y <= mod);
+ if (sum >= mod)
+ sum -= mod;
+ if (sum < 0)
+ sum += mod;
+ return sum;
+}
+
+static inline int
+index_verify (int index, int size, const char *file, int line)
+{
+ if (index < 0 || index >= size)
+ msg (M_FATAL, "Assertion Failed: Array index=%d out of bounds for array size=%d in %s:%d",
+ index,
+ size,
+ file,
+ line);
+ return index;
+}
+
+#endif
diff --git a/interval.c b/interval.c
new file mode 100644
index 0000000..44d59d0
--- /dev/null
+++ b/interval.c
@@ -0,0 +1,77 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "interval.h"
+
+#include "memdbg.h"
+
+void
+interval_init (struct interval *top, int horizon, int refresh)
+{
+ CLEAR (*top);
+ top->refresh = refresh;
+ top->horizon = horizon;
+}
+
+bool
+event_timeout_trigger (struct event_timeout *et,
+ struct timeval *tv,
+ const int et_const_retry)
+{
+ bool ret = false;
+ const time_t local_now = now;
+
+ if (et->defined)
+ {
+ int wakeup = (int) et->last + et->n - local_now;
+ if (wakeup <= 0)
+ {
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "EVENT event_timeout_trigger (%d) etcr=%d", et->n, et_const_retry);
+#endif
+ if (et_const_retry < 0)
+ {
+ et->last = local_now;
+ wakeup = et->n;
+ ret = true;
+ }
+ else
+ {
+ wakeup = et_const_retry;
+ }
+ }
+
+ if (tv && wakeup < tv->tv_sec)
+ {
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "EVENT event_timeout_wakeup (%d/%d) etcr=%d", wakeup, et->n, et_const_retry);
+#endif
+ tv->tv_sec = wakeup;
+ tv->tv_usec = 0;
+ }
+ }
+ return ret;
+}
diff --git a/interval.h b/interval.h
new file mode 100644
index 0000000..4814ec9
--- /dev/null
+++ b/interval.h
@@ -0,0 +1,247 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * The interval_ routines are designed to optimize the calling of a routine
+ * (normally tls_multi_process()) which can be called less frequently
+ * between triggers.
+ */
+
+#ifndef INTERVAL_H
+#define INTERVAL_H
+
+#include "otime.h"
+
+#define INTERVAL_DEBUG 0
+
+/*
+ * Designed to limit calls to expensive functions that need to be called
+ * regularly.
+ */
+
+struct interval
+{
+ interval_t refresh;
+ interval_t horizon;
+ time_t future_trigger;
+ time_t last_action;
+ time_t last_test_true;
+};
+
+void interval_init (struct interval *top, int horizon, int refresh);
+
+/*
+ * IF
+ * last_action less than horizon seconds ago
+ * OR last_test_true more than refresh seconds ago
+ * OR hit future_trigger
+ * THEN
+ * return true
+ * ELSE
+ * set wakeup to the number of seconds until a true return
+ * return false
+ */
+
+static inline bool
+interval_test (struct interval* top)
+{
+ bool trigger = false;
+ const time_t local_now = now;
+
+ if (top->future_trigger && local_now >= top->future_trigger)
+ {
+ trigger = true;
+ top->future_trigger = 0;
+ }
+
+ if (top->last_action + top->horizon > local_now ||
+ top->last_test_true + top->refresh <= local_now ||
+ trigger)
+ {
+ top->last_test_true = local_now;
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "INTERVAL interval_test true");
+#endif
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static inline void
+interval_schedule_wakeup (struct interval* top, interval_t *wakeup)
+{
+ const time_t local_now = now;
+ interval_earliest_wakeup (wakeup, top->last_test_true + top->refresh, local_now);
+ interval_earliest_wakeup (wakeup, top->future_trigger, local_now);
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "INTERVAL interval_schedule wakeup=%d", (int)*wakeup);
+#endif
+}
+
+/*
+ * In wakeup seconds, interval_test will return true once.
+ */
+static inline void
+interval_future_trigger (struct interval* top, interval_t wakeup) {
+ if (wakeup)
+ {
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "INTERVAL interval_future_trigger %d", (int)wakeup);
+#endif
+ top->future_trigger = now + wakeup;
+ }
+}
+
+/*
+ * Once an action is triggered, interval_test will remain true for
+ * horizon seconds.
+ */
+static inline void
+interval_action (struct interval* top)
+{
+#if INTERVAL_DEBUG
+ dmsg (D_INTERVAL, "INTERVAL action");
+#endif
+ top->last_action = now;
+}
+
+/*
+ * Measure when n seconds beyond an event have elapsed
+ */
+
+struct event_timeout
+{
+ bool defined;
+ interval_t n;
+ time_t last; /* time of last event */
+};
+
+static inline bool
+event_timeout_defined (const struct event_timeout* et)
+{
+ return et->defined;
+}
+
+static inline void
+event_timeout_clear (struct event_timeout* et)
+{
+ et->defined = false;
+ et->n = 0;
+ et->last = 0;
+}
+
+static inline struct event_timeout
+event_timeout_clear_ret ()
+{
+ struct event_timeout ret;
+ event_timeout_clear (&ret);
+ return ret;
+}
+
+static inline void
+event_timeout_init (struct event_timeout* et, interval_t n, const time_t local_now)
+{
+ et->defined = true;
+ et->n = (n >= 0) ? n : 0;
+ et->last = local_now;
+}
+
+static inline void
+event_timeout_reset (struct event_timeout* et)
+{
+ if (et->defined)
+ et->last = now;
+}
+
+static inline void
+event_timeout_modify_wakeup (struct event_timeout* et, interval_t n)
+{
+ /* note that you might need to call reset_coarse_timers after this */
+ if (et->defined)
+ et->n = (n >= 0) ? n : 0;
+}
+
+/*
+ * This is the principal function for testing and triggering recurring
+ * timers and will return true on a timer signal event.
+ * If et_const_retry == ETT_DEFAULT and a signal occurs,
+ * the function will return true and *et will be armed for the
+ * next event. If et_const_retry >= 0 and a signal occurs,
+ * *et will not be touched, but *tv will be set to
+ * minimum (*tv, et_const_retry) for a future re-test,
+ * and the function will return true.
+ */
+
+#define ETT_DEFAULT (-1)
+
+bool event_timeout_trigger (struct event_timeout *et,
+ struct timeval *tv,
+ const int et_const_retry);
+
+/*
+ * Measure time intervals in microseconds
+ */
+
+#define USEC_TIMER_MAX 60 /* maximum interval size in seconds */
+
+#define USEC_TIMER_MAX_USEC (USEC_TIMER_MAX * 1000000)
+
+struct usec_timer {
+ struct timeval start;
+ struct timeval end;
+};
+
+#ifdef HAVE_GETTIMEOFDAY
+
+static inline void
+usec_timer_start (struct usec_timer *obj)
+{
+ CLEAR (*obj);
+ openvpn_gettimeofday (&obj->start, NULL);
+}
+
+static inline void
+usec_timer_end (struct usec_timer *obj)
+{
+ openvpn_gettimeofday (&obj->end, NULL);
+}
+
+#endif /* HAVE_GETTIMEOFDAY */
+
+static inline bool
+usec_timer_interval_defined (struct usec_timer *obj)
+{
+ return obj->start.tv_sec && obj->end.tv_sec;
+}
+
+static inline int
+usec_timer_interval (struct usec_timer *obj)
+{
+ return tv_subtract (&obj->end, &obj->start, USEC_TIMER_MAX);
+}
+
+#endif /* INTERVAL_H */
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..fb93d0a
--- /dev/null
+++ b/list.c
@@ -0,0 +1,643 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "list.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+struct hash *
+hash_init (const int n_buckets,
+ const uint32_t iv,
+ uint32_t (*hash_function)(const void *key, uint32_t iv),
+ bool (*compare_function)(const void *key1, const void *key2))
+{
+ struct hash *h;
+ int i;
+
+ ASSERT (n_buckets > 0);
+ ALLOC_OBJ_CLEAR (h, struct hash);
+ h->n_buckets = (int) adjust_power_of_2 (n_buckets);
+ h->mask = h->n_buckets - 1;
+ h->hash_function = hash_function;
+ h->compare_function = compare_function;
+ h->iv = iv;
+ ALLOC_ARRAY (h->buckets, struct hash_bucket, h->n_buckets);
+ for (i = 0; i < h->n_buckets; ++i)
+ {
+ struct hash_bucket *b = &h->buckets[i];
+ b->list = NULL;
+ }
+ return h;
+}
+
+void
+hash_free (struct hash *hash)
+{
+ int i;
+ for (i = 0; i < hash->n_buckets; ++i)
+ {
+ struct hash_bucket *b = &hash->buckets[i];
+ struct hash_element *he = b->list;
+
+ while (he)
+ {
+ struct hash_element *next = he->next;
+ free (he);
+ he = next;
+ }
+ }
+ free (hash->buckets);
+ free (hash);
+}
+
+struct hash_element *
+hash_lookup_fast (struct hash *hash,
+ struct hash_bucket *bucket,
+ const void *key,
+ uint32_t hv)
+{
+ struct hash_element *he;
+ struct hash_element *prev = NULL;
+
+ he = bucket->list;
+
+ while (he)
+ {
+ if (hv == he->hash_value && (*hash->compare_function)(key, he->key))
+ {
+ /* move to head of list */
+ if (prev)
+ {
+ prev->next = he->next;
+ he->next = bucket->list;
+ bucket->list = he;
+ }
+ return he;
+ }
+ prev = he;
+ he = he->next;
+ }
+
+ return NULL;
+}
+
+bool
+hash_remove_fast (struct hash *hash,
+ struct hash_bucket *bucket,
+ const void *key,
+ uint32_t hv)
+{
+ struct hash_element *he;
+ struct hash_element *prev = NULL;
+
+ he = bucket->list;
+
+ while (he)
+ {
+ if (hv == he->hash_value && (*hash->compare_function)(key, he->key))
+ {
+ if (prev)
+ prev->next = he->next;
+ else
+ bucket->list = he->next;
+ free (he);
+ --hash->n_elements;
+ return true;
+ }
+ prev = he;
+ he = he->next;
+ }
+ return false;
+}
+
+bool
+hash_add (struct hash *hash, const void *key, void *value, bool replace)
+{
+ uint32_t hv;
+ struct hash_bucket *bucket;
+ struct hash_element *he;
+ bool ret = false;
+
+ hv = hash_value (hash, key);
+ bucket = &hash->buckets[hv & hash->mask];
+
+ if ((he = hash_lookup_fast (hash, bucket, key, hv))) /* already exists? */
+ {
+ if (replace)
+ {
+ he->value = value;
+ ret = true;
+ }
+ }
+ else
+ {
+ hash_add_fast (hash, bucket, key, hv, value);
+ ret = true;
+ }
+
+ return ret;
+}
+
+void
+hash_remove_by_value (struct hash *hash, void *value)
+{
+ struct hash_iterator hi;
+ struct hash_element *he;
+
+ hash_iterator_init (hash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ if (he->value == value)
+ hash_iterator_delete_element (&hi);
+ }
+ hash_iterator_free (&hi);
+}
+
+static void
+hash_remove_marked (struct hash *hash, struct hash_bucket *bucket)
+{
+ struct hash_element *prev = NULL;
+ struct hash_element *he = bucket->list;
+
+ while (he)
+ {
+ if (!he->key) /* marked? */
+ {
+ struct hash_element *newhe;
+ if (prev)
+ newhe = prev->next = he->next;
+ else
+ newhe = bucket->list = he->next;
+ free (he);
+ --hash->n_elements;
+ he = newhe;
+ }
+ else
+ {
+ prev = he;
+ he = he->next;
+ }
+ }
+}
+
+uint32_t
+void_ptr_hash_function (const void *key, uint32_t iv)
+{
+ return hash_func ((const void *)&key, sizeof (key), iv);
+}
+
+bool
+void_ptr_compare_function (const void *key1, const void *key2)
+{
+ return key1 == key2;
+}
+
+void
+hash_iterator_init_range (struct hash *hash,
+ struct hash_iterator *hi,
+ int start_bucket,
+ int end_bucket)
+{
+ if (end_bucket > hash->n_buckets)
+ end_bucket = hash->n_buckets;
+
+ ASSERT (start_bucket >= 0 && start_bucket <= end_bucket);
+
+ hi->hash = hash;
+ hi->elem = NULL;
+ hi->bucket = NULL;
+ hi->last = NULL;
+ hi->bucket_marked = false;
+ hi->bucket_index_start = start_bucket;
+ hi->bucket_index_end = end_bucket;
+ hi->bucket_index = hi->bucket_index_start - 1;
+}
+
+void
+hash_iterator_init (struct hash *hash,
+ struct hash_iterator *hi)
+{
+ hash_iterator_init_range (hash, hi, 0, hash->n_buckets);
+}
+
+static inline void
+hash_iterator_lock (struct hash_iterator *hi, struct hash_bucket *b)
+{
+ hi->bucket = b;
+ hi->last = NULL;
+ hi->bucket_marked = false;
+}
+
+static inline void
+hash_iterator_unlock (struct hash_iterator *hi)
+{
+ if (hi->bucket)
+ {
+ if (hi->bucket_marked)
+ {
+ hash_remove_marked (hi->hash, hi->bucket);
+ hi->bucket_marked = false;
+ }
+ hi->bucket = NULL;
+ hi->last = NULL;
+ }
+}
+
+static inline void
+hash_iterator_advance (struct hash_iterator *hi)
+{
+ hi->last = hi->elem;
+ hi->elem = hi->elem->next;
+}
+
+void
+hash_iterator_free (struct hash_iterator *hi)
+{
+ hash_iterator_unlock (hi);
+}
+
+struct hash_element *
+hash_iterator_next (struct hash_iterator *hi)
+{
+ struct hash_element *ret = NULL;
+ if (hi->elem)
+ {
+ ret = hi->elem;
+ hash_iterator_advance (hi);
+ }
+ else
+ {
+ while (++hi->bucket_index < hi->bucket_index_end)
+ {
+ struct hash_bucket *b;
+ hash_iterator_unlock (hi);
+ b = &hi->hash->buckets[hi->bucket_index];
+ if (b->list)
+ {
+ hash_iterator_lock (hi, b);
+ hi->elem = b->list;
+ if (hi->elem)
+ {
+ ret = hi->elem;
+ hash_iterator_advance (hi);
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+void
+hash_iterator_delete_element (struct hash_iterator *hi)
+{
+ ASSERT (hi->last);
+ hi->last->key = NULL;
+ hi->bucket_marked = true;
+}
+
+
+#ifdef LIST_TEST
+
+/*
+ * Test the hash code by implementing a simple
+ * word frequency algorithm.
+ */
+
+struct word
+{
+ const char *word;
+ int n;
+};
+
+static uint32_t
+word_hash_function (const void *key, uint32_t iv)
+{
+ const char *str = (const char *) key;
+ const int len = strlen (str);
+ return hash_func ((const uint8_t *)str, len, iv);
+}
+
+static bool
+word_compare_function (const void *key1, const void *key2)
+{
+ return strcmp ((const char *)key1, (const char *)key2) == 0;
+}
+
+static void
+print_nhash (struct hash *hash)
+{
+ struct hash_iterator hi;
+ struct hash_element *he;
+ int count = 0;
+
+ hash_iterator_init (hash, &hi, true);
+
+ while ((he = hash_iterator_next (&hi)))
+ {
+ printf ("%d ", (int) he->value);
+ ++count;
+ }
+ printf ("\n");
+
+ hash_iterator_free (&hi);
+ ASSERT (count == hash_n_elements (hash));
+}
+
+static void
+rmhash (struct hash *hash, const char *word)
+{
+ hash_remove (hash, word);
+}
+
+void
+list_test (void)
+{
+ openvpn_thread_init ();
+
+ {
+ struct gc_arena gc = gc_new ();
+ struct hash *hash = hash_init (10000, get_random (), word_hash_function, word_compare_function);
+ struct hash *nhash = hash_init (256, get_random (), word_hash_function, word_compare_function);
+
+ printf ("hash_init n_buckets=%d mask=0x%08x\n", hash->n_buckets, hash->mask);
+
+ /* parse words from stdin */
+ while (true)
+ {
+ char buf[256];
+ char wordbuf[256];
+ int wbi;
+ int bi;
+ char c;
+
+ if (!fgets(buf, sizeof(buf), stdin))
+ break;
+
+ bi = wbi = 0;
+ do
+ {
+ c = buf[bi++];
+ if (isalnum (c) || c == '_')
+ {
+ ASSERT (wbi < (int) sizeof (wordbuf));
+ wordbuf[wbi++] = c;
+ }
+ else
+ {
+ if (wbi)
+ {
+ struct word *w;
+ ASSERT (wbi < (int) sizeof (wordbuf));
+ wordbuf[wbi++] = '\0';
+
+ /* word is parsed from stdin */
+
+ /* does it already exist in table? */
+ w = (struct word *) hash_lookup (hash, wordbuf);
+
+ if (w)
+ {
+ /* yes, increment count */
+ ++w->n;
+ }
+ else
+ {
+ /* no, make a new object */
+ ALLOC_OBJ_GC (w, struct word, &gc);
+ w->word = string_alloc (wordbuf, &gc);
+ w->n = 1;
+ ASSERT (hash_add (hash, w->word, w, false));
+ ASSERT (hash_add (nhash, w->word, (void*) ((random() & 0x0F) + 1), false));
+ }
+ }
+ wbi = 0;
+ }
+ } while (c);
+ }
+
+#if 1
+ /* remove some words from the table */
+ {
+ rmhash (hash, "true");
+ rmhash (hash, "false");
+ }
+#endif
+
+ /* output contents of hash table */
+ {
+ int base;
+ int inc = 0;
+ int count = 0;
+
+ for (base = 0; base < hash_n_buckets (hash); base += inc) {
+ struct hash_iterator hi;
+ struct hash_element *he;
+ inc = (get_random () % 3) + 1;
+ hash_iterator_init_range (hash, &hi, true, base, base + inc);
+
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct word *w = (struct word *) he->value;
+ printf ("%6d '%s'\n", w->n, w->word);
+ ++count;
+ }
+
+ hash_iterator_free (&hi);
+ }
+ ASSERT (count == hash_n_elements (hash));
+ }
+
+#if 1
+ /* test hash_remove_by_value function */
+ {
+ int i;
+ for (i = 1; i <= 16; ++i)
+ {
+ printf ("[%d] ***********************************\n", i);
+ print_nhash (nhash);
+ hash_remove_by_value (nhash, (void *) i, true);
+ }
+ printf ("FINAL **************************\n");
+ print_nhash (nhash);
+ }
+#endif
+
+ hash_free (hash);
+ hash_free (nhash);
+ gc_free (&gc);
+ }
+
+ openvpn_thread_cleanup ();
+}
+
+#endif
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ level : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 36+6len instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+See http://burlteburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable. Do NOT use for cryptographic purposes.
+
+--------------------------------------------------------------------
+
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+
+James Yonan Notes:
+
+* This function is faster than it looks, and appears to be
+ appropriate for our usage in OpenVPN which is primarily
+ for hash-table based address lookup (IPv4, IPv6, and Ethernet MAC).
+ NOTE: This function is never used for cryptographic purposes, only
+ to produce evenly-distributed indexes into hash tables.
+
+* Benchmark results: 11.39 machine cycles per byte on a P2 266Mhz,
+ and 12.1 machine cycles per byte on a
+ 2.2 Ghz P4 when hashing a 6 byte string.
+--------------------------------------------------------------------
+*/
+
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+uint32_t
+hash_func (const uint8_t *k, uint32_t length, uint32_t initval)
+{
+ uint32_t a, b, c, len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] + ((uint32_t) k[1] << 8)
+ + ((uint32_t) k[2] << 16)
+ + ((uint32_t) k[3] << 24));
+ b += (k[4] + ((uint32_t) k[5] << 8)
+ + ((uint32_t) k[6] << 16)
+ + ((uint32_t) k[7] << 24));
+ c += (k[8] + ((uint32_t) k[9] << 8)
+ + ((uint32_t) k[10] << 16)
+ + ((uint32_t) k[11] << 24));
+ mix (a, b, c);
+ k += 12;
+ len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch (len) /* all the case statements fall through */
+ {
+ case 11:
+ c += ((uint32_t) k[10] << 24);
+ case 10:
+ c += ((uint32_t) k[9] << 16);
+ case 9:
+ c += ((uint32_t) k[8] << 8);
+ /* the first byte of c is reserved for the length */
+ case 8:
+ b += ((uint32_t) k[7] << 24);
+ case 7:
+ b += ((uint32_t) k[6] << 16);
+ case 6:
+ b += ((uint32_t) k[5] << 8);
+ case 5:
+ b += k[4];
+ case 4:
+ a += ((uint32_t) k[3] << 24);
+ case 3:
+ a += ((uint32_t) k[2] << 16);
+ case 2:
+ a += ((uint32_t) k[1] << 8);
+ case 1:
+ a += k[0];
+ /* case 0: nothing left to add */
+ }
+ mix (a, b, c);
+ /*-------------------------------------- report the result */
+ return c;
+}
+
+#else
+static void dummy(void) {}
+#endif /* P2MP_SERVER */
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..adde36b
--- /dev/null
+++ b/list.h
@@ -0,0 +1,196 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/*
+ * This code is a fairly straightforward hash
+ * table implementation using Bob Jenkins'
+ * hash function.
+ *
+ * Hash tables are used in OpenVPN to keep track of
+ * client instances over various key spaces.
+ */
+
+#if P2MP_SERVER
+
+/* define this to enable special list test mode */
+/*#define LIST_TEST*/
+
+#include "basic.h"
+#include "buffer.h"
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+struct hash_element
+{
+ void *value;
+ const void *key;
+ unsigned int hash_value;
+ struct hash_element *next;
+};
+
+struct hash_bucket
+{
+ struct hash_element *list;
+};
+
+struct hash
+{
+ int n_buckets;
+ int n_elements;
+ int mask;
+ uint32_t iv;
+ uint32_t (*hash_function)(const void *key, uint32_t iv);
+ bool (*compare_function)(const void *key1, const void *key2); /* return true if equal */
+ struct hash_bucket *buckets;
+};
+
+struct hash *hash_init (const int n_buckets,
+ const uint32_t iv,
+ uint32_t (*hash_function)(const void *key, uint32_t iv),
+ bool (*compare_function)(const void *key1, const void *key2));
+
+void hash_free (struct hash *hash);
+
+bool hash_add (struct hash *hash, const void *key, void *value, bool replace);
+
+struct hash_element *hash_lookup_fast (struct hash *hash,
+ struct hash_bucket *bucket,
+ const void *key,
+ uint32_t hv);
+
+bool hash_remove_fast (struct hash *hash,
+ struct hash_bucket *bucket,
+ const void *key,
+ uint32_t hv);
+
+void hash_remove_by_value (struct hash *hash, void *value);
+
+struct hash_iterator
+{
+ struct hash *hash;
+ int bucket_index;
+ struct hash_bucket *bucket;
+ struct hash_element *elem;
+ struct hash_element *last;
+ bool bucket_marked;
+ int bucket_index_start;
+ int bucket_index_end;
+};
+
+void hash_iterator_init_range (struct hash *hash,
+ struct hash_iterator *hi,
+ int start_bucket,
+ int end_bucket);
+
+void hash_iterator_init (struct hash *hash, struct hash_iterator *iter);
+struct hash_element *hash_iterator_next (struct hash_iterator *hi);
+void hash_iterator_delete_element (struct hash_iterator *hi);
+void hash_iterator_free (struct hash_iterator *hi);
+
+uint32_t hash_func (const uint8_t *k, uint32_t length, uint32_t initval);
+
+uint32_t void_ptr_hash_function (const void *key, uint32_t iv);
+bool void_ptr_compare_function (const void *key1, const void *key2);
+
+#ifdef LIST_TEST
+void list_test (void);
+#endif
+
+static inline uint32_t
+hash_value (const struct hash *hash, const void *key)
+{
+ return (*hash->hash_function)(key, hash->iv);
+}
+
+static inline int
+hash_n_elements (const struct hash *hash)
+{
+ return hash->n_elements;
+}
+
+static inline int
+hash_n_buckets (const struct hash *hash)
+{
+ return hash->n_buckets;
+}
+
+static inline struct hash_bucket *
+hash_bucket (struct hash *hash, uint32_t hv)
+{
+ return &hash->buckets[hv & hash->mask];
+}
+
+static inline void *
+hash_lookup (struct hash *hash, const void *key)
+{
+ void *ret = NULL;
+ struct hash_element *he;
+ uint32_t hv = hash_value (hash, key);
+ struct hash_bucket *bucket = &hash->buckets[hv & hash->mask];
+
+ he = hash_lookup_fast (hash, bucket, key, hv);
+ if (he)
+ ret = he->value;
+
+ return ret;
+}
+
+/* NOTE: assumes that key is not a duplicate */
+static inline void
+hash_add_fast (struct hash *hash,
+ struct hash_bucket *bucket,
+ const void *key,
+ uint32_t hv,
+ void *value)
+{
+ struct hash_element *he;
+
+ ALLOC_OBJ (he, struct hash_element);
+ he->value = value;
+ he->key = key;
+ he->hash_value = hv;
+ he->next = bucket->list;
+ bucket->list = he;
+ ++hash->n_elements;
+}
+
+static inline bool
+hash_remove (struct hash *hash, const void *key)
+{
+ uint32_t hv;
+ struct hash_bucket *bucket;
+ bool ret;
+
+ hv = hash_value (hash, key);
+ bucket = &hash->buckets[hv & hash->mask];
+ ret = hash_remove_fast (hash, bucket, key, hv);
+ return ret;
+}
+
+#endif /* P2MP_SERVER */
+#endif /* LIST */
diff --git a/lladdr.c b/lladdr.c
new file mode 100644
index 0000000..7aefdba
--- /dev/null
+++ b/lladdr.c
@@ -0,0 +1,61 @@
+/*
+ * Support routine for configuring link layer address
+ */
+
+#include "syshead.h"
+#include "error.h"
+#include "misc.h"
+
+int set_lladdr(const char *ifname, const char *lladdr,
+ const struct env_set *es)
+{
+ struct argv argv = argv_new ();
+ int r;
+
+ if (!ifname || !lladdr)
+ return -1;
+
+#if defined(TARGET_LINUX)
+#ifdef CONFIG_FEATURE_IPROUTE
+ argv_printf (&argv,
+ "%s link set addr %s dev %s",
+ iproute_path, lladdr, ifname);
+#else
+ argv_printf (&argv,
+ "%s %s hw ether %s",
+ IFCONFIG_PATH,
+ ifname, lladdr);
+#endif
+#elif defined(TARGET_SOLARIS)
+ argv_printf (&argv,
+ "%s %s ether %s",
+ IFCONFIG_PATH,
+ ifname, lladdr);
+#elif defined(TARGET_OPENBSD)
+ argv_printf (&argv,
+ "%s %s lladdr %s",
+ IFCONFIG_PATH,
+ ifname, lladdr);
+#elif defined(TARGET_DARWIN)
+ argv_printf (&argv,
+ "%s %s lladdr %s",
+ IFCONFIG_PATH,
+ ifname, lladdr);
+#elif defined(TARGET_FREEBSD)
+ argv_printf (&argv,
+ "%s %s ether %s",
+ IFCONFIG_PATH,
+ ifname, lladdr);
+#else
+ msg (M_WARN, "Sorry, but I don't know how to configure link layer addresses on this operating system.");
+ return -1;
+#endif
+
+ argv_msg (M_INFO, &argv);
+ r = openvpn_execve_check (&argv, es, M_WARN, "ERROR: Unable to set link layer address.");
+ if (r)
+ msg (M_INFO, "TUN/TAP link layer address set to %s", lladdr);
+
+ argv_reset (&argv);
+ return r;
+}
diff --git a/lladdr.h b/lladdr.h
new file mode 100644
index 0000000..d6c4256
--- /dev/null
+++ b/lladdr.h
@@ -0,0 +1,8 @@
+/*
+ * Support routine for configuring link layer address
+ */
+
+#include "misc.h"
+
+int set_lladdr(const char *ifname, const char *lladdr,
+ const struct env_set *es);
diff --git a/lzo.c b/lzo.c
new file mode 100644
index 0000000..6ea0d03
--- /dev/null
+++ b/lzo.c
@@ -0,0 +1,275 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef USE_LZO
+
+#include "lzo.h"
+#include "error.h"
+#include "otime.h"
+
+#include "memdbg.h"
+
+static bool
+lzo_adaptive_compress_test (struct lzo_adaptive_compress *ac)
+{
+ const bool save = ac->compress_state;
+ const time_t local_now = now;
+
+ if (!ac->compress_state)
+ {
+ if (local_now >= ac->next)
+ {
+ if (ac->n_total > AC_MIN_BYTES
+ && (ac->n_total - ac->n_comp) < (ac->n_total / (100 / AC_SAVE_PCT)))
+ {
+ ac->compress_state = true;
+ ac->next = local_now + AC_OFF_SEC;
+ }
+ else
+ {
+ ac->next = local_now + AC_SAMP_SEC;
+ }
+ dmsg (D_COMP, "lzo_adaptive_compress_test: comp=%d total=%d", ac->n_comp, ac->n_total);
+ ac->n_total = ac->n_comp = 0;
+ }
+ }
+ else
+ {
+ if (local_now >= ac->next)
+ {
+ ac->next = local_now + AC_SAMP_SEC;
+ ac->n_total = ac->n_comp = 0;
+ ac->compress_state = false;
+ }
+ }
+
+ if (ac->compress_state != save)
+ dmsg (D_COMP_LOW, "Adaptive compression state %s", (ac->compress_state ? "OFF" : "ON"));
+
+ return !ac->compress_state;
+}
+
+static inline void
+lzo_adaptive_compress_data (struct lzo_adaptive_compress *ac, int n_total, int n_comp)
+{
+ ac->n_total += n_total;
+ ac->n_comp += n_comp;
+}
+
+void lzo_adjust_frame_parameters (struct frame *frame)
+{
+ /* Leave room for our one-byte compressed/didn't-compress prefix byte. */
+ frame_add_to_extra_frame (frame, LZO_PREFIX_LEN);
+
+ /* Leave room for compression buffer to expand in worst case scenario
+ where data is totally uncompressible */
+ frame_add_to_extra_buffer (frame, LZO_EXTRA_BUFFER (EXPANDED_SIZE(frame)));
+}
+
+void
+lzo_compress_init (struct lzo_compress_workspace *lzowork, unsigned int flags)
+{
+ CLEAR (*lzowork);
+
+ lzowork->wmem_size = LZO_WORKSPACE;
+ lzowork->flags = flags;
+
+ if (lzo_init () != LZO_E_OK)
+ msg (M_FATAL, "Cannot initialize LZO compression library");
+ lzowork->wmem = (lzo_voidp) lzo_malloc (lzowork->wmem_size);
+ check_malloc_return (lzowork->wmem);
+ msg (M_INFO, "LZO compression initialized");
+ lzowork->defined = true;
+}
+
+void
+lzo_compress_uninit (struct lzo_compress_workspace *lzowork)
+{
+ if (lzowork)
+ {
+ ASSERT (lzowork->defined);
+ lzo_free (lzowork->wmem);
+ lzowork->wmem = NULL;
+ lzowork->defined = false;
+ }
+}
+
+static inline bool
+lzo_compression_enabled (struct lzo_compress_workspace *lzowork)
+{
+ if ((lzowork->flags & (LZO_SELECTED|LZO_ON)) == (LZO_SELECTED|LZO_ON))
+ {
+ if (lzowork->flags & LZO_ADAPTIVE)
+ return lzo_adaptive_compress_test (&lzowork->ac);
+ else
+ return true;
+ }
+ return false;
+}
+
+/* Magic numbers to tell our peer if we compressed or not */
+#define YES_COMPRESS 0x66
+#define NO_COMPRESS 0xFA
+
+void
+lzo_compress (struct buffer *buf, struct buffer work,
+ struct lzo_compress_workspace *lzowork,
+ const struct frame* frame)
+{
+ lzo_uint zlen = 0;
+ int err;
+ bool compressed = false;
+
+ ASSERT (lzowork->defined);
+
+ if (buf->len <= 0)
+ return;
+
+ /*
+ * In order to attempt compression, length must be at least COMPRESS_THRESHOLD,
+ * and our adaptive level must give the OK.
+ */
+ if (buf->len >= COMPRESS_THRESHOLD && lzo_compression_enabled (lzowork))
+ {
+ ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+ ASSERT (buf_safe (&work, LZO_EXTRA_BUFFER (PAYLOAD_SIZE (frame))));
+
+ if (!(buf->len <= PAYLOAD_SIZE (frame)))
+ {
+ dmsg (D_COMP_ERRORS, "LZO compression buffer overflow");
+ buf->len = 0;
+ return;
+ }
+
+ err = LZO_COMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen, lzowork->wmem);
+ if (err != LZO_E_OK)
+ {
+ dmsg (D_COMP_ERRORS, "LZO compression error: %d", err);
+ buf->len = 0;
+ return;
+ }
+
+ ASSERT (buf_safe (&work, zlen));
+ work.len = zlen;
+ compressed = true;
+
+ dmsg (D_COMP, "compress %d -> %d", buf->len, work.len);
+ lzowork->pre_compress += buf->len;
+ lzowork->post_compress += work.len;
+
+ /* tell adaptive level about our success or lack thereof in getting any size reduction */
+ if (lzowork->flags & LZO_ADAPTIVE)
+ lzo_adaptive_compress_data (&lzowork->ac, buf->len, work.len);
+ }
+
+ /* did compression save us anything ? */
+ if (compressed && work.len < buf->len)
+ {
+ uint8_t *header = buf_prepend (&work, 1);
+ *header = YES_COMPRESS;
+ *buf = work;
+ }
+ else
+ {
+ uint8_t *header = buf_prepend (buf, 1);
+ *header = NO_COMPRESS;
+ }
+}
+
+void
+lzo_decompress (struct buffer *buf, struct buffer work,
+ struct lzo_compress_workspace *lzowork,
+ const struct frame* frame)
+{
+ lzo_uint zlen = EXPANDED_SIZE (frame);
+ uint8_t c; /* flag indicating whether or not our peer compressed */
+ int err;
+
+ ASSERT (lzowork->defined);
+
+ if (buf->len <= 0)
+ return;
+
+ ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+ c = *BPTR (buf);
+ ASSERT (buf_advance (buf, 1));
+
+ if (c == YES_COMPRESS) /* packet was compressed */
+ {
+ ASSERT (buf_safe (&work, zlen));
+ err = LZO_DECOMPRESS (BPTR (buf), BLEN (buf), BPTR (&work), &zlen,
+ lzowork->wmem);
+ if (err != LZO_E_OK)
+ {
+ dmsg (D_COMP_ERRORS, "LZO decompression error: %d", err);
+ buf->len = 0;
+ return;
+ }
+
+ ASSERT (buf_safe (&work, zlen));
+ work.len = zlen;
+
+ dmsg (D_COMP, "decompress %d -> %d", buf->len, work.len);
+ lzowork->pre_decompress += buf->len;
+ lzowork->post_decompress += work.len;
+
+ *buf = work;
+ }
+ else if (c == NO_COMPRESS) /* packet was not compressed */
+ {
+ ;
+ }
+ else
+ {
+ dmsg (D_COMP_ERRORS, "Bad LZO decompression header byte: %d", c);
+ buf->len = 0;
+ }
+}
+
+void
+lzo_modify_flags (struct lzo_compress_workspace *lzowork, unsigned int flags)
+{
+ ASSERT (lzowork->defined);
+ lzowork->flags = flags;
+}
+
+/*
+ * Print statistics
+ */
+void lzo_print_stats (const struct lzo_compress_workspace *lzo_compwork, struct status_output *so)
+{
+ ASSERT (lzo_compwork->defined);
+
+ status_printf (so, "pre-compress bytes," counter_format, lzo_compwork->pre_compress);
+ status_printf (so, "post-compress bytes," counter_format, lzo_compwork->post_compress);
+ status_printf (so, "pre-decompress bytes," counter_format, lzo_compwork->pre_decompress);
+ status_printf (so, "post-decompress bytes," counter_format, lzo_compwork->post_decompress);
+}
+
+#else
+static void dummy(void) {}
+#endif /* USE_LZO */
diff --git a/lzo.h b/lzo.h
new file mode 100644
index 0000000..bb15753
--- /dev/null
+++ b/lzo.h
@@ -0,0 +1,136 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OPENVPN_LZO_H
+#define OPENVPN_LZO_H
+
+#ifdef USE_LZO
+
+#ifdef LZO_HEADER_DIR
+#include "lzo/lzoutil.h"
+#include "lzo/lzo1x.h"
+#else
+#include "lzoutil.h"
+#include "lzo1x.h"
+#endif
+
+#include "buffer.h"
+#include "mtu.h"
+#include "common.h"
+#include "status.h"
+
+/* LZO flags */
+#define LZO_SELECTED (1<<0)
+#define LZO_ON (1<<1)
+#define LZO_ADAPTIVE (1<<2)
+
+/*
+ * Use LZO compress routine lzo1x_1_15_compress which is described
+ * as faster but needs a bit more memory than the standard routine.
+ * Use safe decompress (i.e. check for buffer overflows).
+ * You may want to use the non-safe version
+ * of decompress if speed is essential and if you know
+ * that you will always be using a MAC to verify the
+ * integrity of incoming packets.
+ */
+#define LZO_COMPRESS lzo1x_1_15_compress
+#define LZO_WORKSPACE LZO1X_1_15_MEM_COMPRESS
+#define LZO_DECOMPRESS lzo1x_decompress_safe
+
+#define LZO_EXTRA_BUFFER(len) ((len)/8 + 128 + 3) /* LZO 2.0 worst case size expansion. */
+
+/*
+ * Don't try to compress any packet smaller than this.
+ */
+#define COMPRESS_THRESHOLD 100
+
+/*
+ * Length of prepended prefix on LZO packets
+ */
+#define LZO_PREFIX_LEN 1
+
+/*
+ * Adaptive compress parameters
+ */
+#define AC_SAMP_SEC 2 /* number of seconds in sample period */
+#define AC_MIN_BYTES 1000 /* sample period must have at least n bytes
+ to be valid for testing */
+#define AC_SAVE_PCT 5 /* turn off compress if we didn't save at
+ least this % during sample period */
+#define AC_OFF_SEC 60 /* if we turn off compression, don't do sample
+ retest for n seconds */
+
+struct lzo_adaptive_compress {
+ bool compress_state;
+ time_t next;
+ int n_total;
+ int n_comp;
+};
+
+/*
+ * Compress and Uncompress routines.
+ */
+
+struct lzo_compress_workspace
+{
+ lzo_voidp wmem;
+ int wmem_size;
+ struct lzo_adaptive_compress ac;
+ unsigned int flags;
+ bool defined;
+
+ /* statistics */
+ counter_type pre_decompress;
+ counter_type post_decompress;
+ counter_type pre_compress;
+ counter_type post_compress;
+};
+
+void lzo_adjust_frame_parameters(struct frame *frame);
+
+void lzo_compress_init (struct lzo_compress_workspace *lzowork, unsigned int flags);
+
+void lzo_compress_uninit (struct lzo_compress_workspace *lzowork);
+
+void lzo_modify_flags (struct lzo_compress_workspace *lzowork, unsigned int flags);
+
+void lzo_compress (struct buffer *buf, struct buffer work,
+ struct lzo_compress_workspace *lzowork,
+ const struct frame* frame);
+
+void lzo_decompress (struct buffer *buf, struct buffer work,
+ struct lzo_compress_workspace *lzowork,
+ const struct frame* frame);
+
+void lzo_print_stats (const struct lzo_compress_workspace *lzo_compwork, struct status_output *so);
+
+static inline bool
+lzo_defined (const struct lzo_compress_workspace *lzowork)
+{
+ return lzowork->defined;
+}
+
+
+#endif /* USE_LZO */
+#endif
diff --git a/manage.c b/manage.c
new file mode 100644
index 0000000..820621e
--- /dev/null
+++ b/manage.c
@@ -0,0 +1,3160 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_MANAGEMENT
+
+#include "error.h"
+#include "fdmisc.h"
+#include "options.h"
+#include "sig.h"
+#include "event.h"
+#include "otime.h"
+#include "integer.h"
+#include "misc.h"
+#include "ssl.h"
+#include "common.h"
+#include "manage.h"
+
+#include "memdbg.h"
+
+#ifdef ENABLE_PKCS11
+#include "pkcs11.h"
+#endif
+
+#define MANAGEMENT_ECHO_PULL_INFO 0
+
+#if MANAGEMENT_ECHO_PULL_INFO
+#define MANAGEMENT_ECHO_FLAGS LOG_PRINT_INTVAL
+#else
+#define MANAGEMENT_ECHO_FLAGS 0
+#endif
+
+/* tag for blank username/password */
+static const char blank_up[] = "[[BLANK]]";
+
+struct management *management; /* GLOBAL */
+
+/* static forward declarations */
+static void man_output_standalone (struct management *man, volatile int *signal_received);
+static void man_reset_client_socket (struct management *man, const bool exiting);
+
+static void
+man_help ()
+{
+ msg (M_CLIENT, "Management Interface for %s", title_string);
+ msg (M_CLIENT, "Commands:");
+ msg (M_CLIENT, "auth-retry t : Auth failure retry mode (none,interact,nointeract).");
+ msg (M_CLIENT, "bytecount n : Show bytes in/out, update every n secs (0=off).");
+ msg (M_CLIENT, "echo [on|off] [N|all] : Like log, but only show messages in echo buffer.");
+ msg (M_CLIENT, "exit|quit : Close management session.");
+ msg (M_CLIENT, "forget-passwords : Forget passwords entered so far.");
+ msg (M_CLIENT, "help : Print this message.");
+ msg (M_CLIENT, "hold [on|off|release] : Set/show hold flag to on/off state, or");
+ msg (M_CLIENT, " release current hold and start tunnel.");
+ msg (M_CLIENT, "kill cn : Kill the client instance(s) having common name cn.");
+ msg (M_CLIENT, "kill IP:port : Kill the client instance connecting from IP:port.");
+ msg (M_CLIENT, "load-stats : Show global server load stats.");
+ msg (M_CLIENT, "log [on|off] [N|all] : Turn on/off realtime log display");
+ msg (M_CLIENT, " + show last N lines or 'all' for entire history.");
+ msg (M_CLIENT, "mute [n] : Set log mute level to n, or show level if n is absent.");
+ msg (M_CLIENT, "needok type action : Enter confirmation for NEED-OK request of 'type',");
+ msg (M_CLIENT, " where action = 'ok' or 'cancel'.");
+ msg (M_CLIENT, "needstr type action : Enter confirmation for NEED-STR request of 'type',");
+ msg (M_CLIENT, " where action is reply string.");
+ msg (M_CLIENT, "net : (Windows only) Show network info and routing table.");
+ msg (M_CLIENT, "password type p : Enter password p for a queried OpenVPN password.");
+ msg (M_CLIENT, "pid : Show process ID of the current OpenVPN process.");
+#ifdef ENABLE_PKCS11
+ msg (M_CLIENT, "pkcs11-id-count : Get number of available PKCS#11 identities.");
+ msg (M_CLIENT, "pkcs11-id-get index : Get PKCS#11 identity at index.");
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+ msg (M_CLIENT, "client-auth CID KID : Authenticate client-id/key-id CID/KID (MULTILINE)");
+ msg (M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID");
+ msg (M_CLIENT, "client-deny CID KID R [CR] : Deny auth client-id/key-id CID/KID with log reason");
+ msg (M_CLIENT, " text R and optional client reason text CR");
+ msg (M_CLIENT, "client-kill CID : Kill client instance CID");
+ msg (M_CLIENT, "env-filter [level] : Set env-var filter level");
+#ifdef MANAGEMENT_PF
+ msg (M_CLIENT, "client-pf CID : Define packet filter for client CID (MULTILINE)");
+#endif
+#endif
+ msg (M_CLIENT, "signal s : Send signal s to daemon,");
+ msg (M_CLIENT, " s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
+ msg (M_CLIENT, "state [on|off] [N|all] : Like log, but show state history.");
+ msg (M_CLIENT, "status [n] : Show current daemon status info using format #n.");
+ msg (M_CLIENT, "test n : Produce n lines of output for testing/debugging.");
+ msg (M_CLIENT, "username type u : Enter username u for a queried OpenVPN username.");
+ msg (M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent.");
+ msg (M_CLIENT, "version : Show current version number.");
+#if HTTP_PROXY_FALLBACK
+ msg (M_CLIENT, "http-proxy-fallback <server> <port> [flags] : Enter dynamic HTTP proxy fallback info.");
+ msg (M_CLIENT, "http-proxy-fallback-disable : Disable HTTP proxy fallback.");
+#endif
+ msg (M_CLIENT, "END");
+}
+
+static const char *
+man_state_name (const int state)
+{
+ switch (state)
+ {
+ case OPENVPN_STATE_INITIAL:
+ return "INITIAL";
+ case OPENVPN_STATE_CONNECTING:
+ return "CONNECTING";
+ case OPENVPN_STATE_WAIT:
+ return "WAIT";
+ case OPENVPN_STATE_AUTH:
+ return "AUTH";
+ case OPENVPN_STATE_GET_CONFIG:
+ return "GET_CONFIG";
+ case OPENVPN_STATE_ASSIGN_IP:
+ return "ASSIGN_IP";
+ case OPENVPN_STATE_ADD_ROUTES:
+ return "ADD_ROUTES";
+ case OPENVPN_STATE_CONNECTED:
+ return "CONNECTED";
+ case OPENVPN_STATE_RECONNECTING:
+ return "RECONNECTING";
+ case OPENVPN_STATE_EXITING:
+ return "EXITING";
+ case OPENVPN_STATE_RESOLVE:
+ return "RESOLVE";
+ case OPENVPN_STATE_TCP_CONNECT:
+ return "TCP_CONNECT";
+ default:
+ return "?";
+ }
+}
+
+static void
+man_welcome (struct management *man)
+{
+ msg (M_CLIENT, ">INFO:OpenVPN Management Interface Version %d -- type 'help' for more info",
+ MANAGEMENT_VERSION);
+ if (man->persist.special_state_msg)
+ msg (M_CLIENT, "%s", man->persist.special_state_msg);
+}
+
+static inline bool
+man_password_needed (struct management *man)
+{
+ return man->settings.up.defined && !man->connection.password_verified;
+}
+
+static void
+man_check_password (struct management *man, const char *line)
+{
+ if (man_password_needed (man))
+ {
+ if (streq (line, man->settings.up.password))
+ {
+ man->connection.password_verified = true;
+ msg (M_CLIENT, "SUCCESS: password is correct");
+ man_welcome (man);
+ }
+ else
+ {
+ man->connection.password_verified = false;
+ msg (M_CLIENT, "ERROR: bad password");
+ if (++man->connection.password_tries >= MANAGEMENT_N_PASSWORD_RETRIES)
+ {
+ msg (M_WARN, "MAN: client connection rejected after %d failed password attempts",
+ MANAGEMENT_N_PASSWORD_RETRIES);
+ man->connection.halt = true;
+ }
+ }
+ }
+}
+
+static void
+man_update_io_state (struct management *man)
+{
+ if (socket_defined (man->connection.sd_cli))
+ {
+ if (buffer_list_defined (man->connection.out))
+ {
+ man->connection.state = MS_CC_WAIT_WRITE;
+ }
+ else
+ {
+ man->connection.state = MS_CC_WAIT_READ;
+ }
+ }
+}
+
+static void
+man_output_list_push_finalize (struct management *man)
+{
+ if (management_connected (man))
+ {
+ man_update_io_state (man);
+ if (!man->persist.standalone_disabled)
+ {
+ volatile int signal_received = 0;
+ man_output_standalone (man, &signal_received);
+ }
+ }
+}
+
+static void
+man_output_list_push_str (struct management *man, const char *str)
+{
+ if (management_connected (man) && str)
+ {
+ buffer_list_push (man->connection.out, (const unsigned char *) str);
+ }
+}
+
+static void
+man_output_list_push (struct management *man, const char *str)
+{
+ man_output_list_push_str (man, str);
+ man_output_list_push_finalize (man);
+}
+
+static void
+man_prompt (struct management *man)
+{
+ if (man_password_needed (man))
+ man_output_list_push (man, "ENTER PASSWORD:");
+#if 0 /* should we use prompt? */
+ else
+ man_output_list_push (man, ">");
+#endif
+}
+
+static void
+man_delete_unix_socket (struct management *man)
+{
+#if UNIX_SOCK_SUPPORT
+ if ((man->settings.flags & (MF_UNIX_SOCK|MF_CONNECT_AS_CLIENT)) == MF_UNIX_SOCK)
+ socket_delete_unix (&man->settings.local_unix);
+#endif
+}
+
+static void
+man_close_socket (struct management *man, const socket_descriptor_t sd)
+{
+#ifndef WIN32
+ /*
+ * Windows doesn't need this because the ne32 event is permanently
+ * enabled at struct management scope.
+ */
+ if (man->persist.callback.delete_event)
+ (*man->persist.callback.delete_event) (man->persist.callback.arg, sd);
+#endif
+ openvpn_close_socket (sd);
+}
+
+static void
+virtual_output_callback_func (void *arg, const unsigned int flags, const char *str)
+{
+ struct management *man = (struct management *) arg;
+ static int recursive_level = 0; /* GLOBAL */
+ bool did_push = false;
+
+ if (!recursive_level) /* don't allow recursion */
+ {
+ struct gc_arena gc = gc_new ();
+ struct log_entry e;
+ const char *out = NULL;
+
+ ++recursive_level;
+
+ CLEAR (e);
+ update_time ();
+ e.timestamp = now;
+ e.u.msg_flags = flags;
+ e.string = str;
+
+ if (flags & M_FATAL)
+ man->persist.standalone_disabled = false;
+
+ if (flags != M_CLIENT)
+ log_history_add (man->persist.log, &e);
+
+ if (!man_password_needed (man))
+ {
+ if (flags == M_CLIENT)
+ out = log_entry_print (&e, LOG_PRINT_CRLF, &gc);
+ else if (man->connection.log_realtime)
+ out = log_entry_print (&e, LOG_PRINT_INT_DATE
+ | LOG_PRINT_MSG_FLAGS
+ | LOG_PRINT_LOG_PREFIX
+ | LOG_PRINT_CRLF, &gc);
+ if (out)
+ {
+ man_output_list_push_str (man, out);
+ did_push = true;
+ }
+ if (flags & M_FATAL)
+ {
+ out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc);
+ if (out)
+ {
+ man_output_list_push_str (man, out);
+ did_push = true;
+ man_reset_client_socket (man, true);
+ }
+ }
+ }
+
+ --recursive_level;
+ gc_free (&gc);
+ }
+
+ if (did_push)
+ man_output_list_push_finalize (man);
+}
+
+/*
+ * Given a signal, return the signal with possible remapping applied,
+ * or -1 if the signal should be ignored.
+ */
+static int
+man_mod_signal (const struct management *man, const int signum)
+{
+ const unsigned int flags = man->settings.mansig;
+ int s = signum;
+ if (s == SIGUSR1)
+ {
+ if (flags & MANSIG_MAP_USR1_TO_HUP)
+ s = SIGHUP;
+ if (flags & MANSIG_MAP_USR1_TO_TERM)
+ s = SIGTERM;
+ }
+ if (flags & MANSIG_IGNORE_USR1_HUP)
+ {
+ if (s == SIGHUP || s == SIGUSR1)
+ s = -1;
+ }
+ return s;
+}
+
+static void
+man_signal (struct management *man, const char *name)
+{
+ const int sig = parse_signal (name);
+ if (sig >= 0)
+ {
+ const int sig_mod = man_mod_signal (man, sig);
+ if (sig_mod >= 0)
+ {
+ throw_signal (sig_mod);
+ msg (M_CLIENT, "SUCCESS: signal %s thrown", signal_name (sig_mod, true));
+ }
+ else
+ {
+ if (man->persist.special_state_msg)
+ msg (M_CLIENT, "%s", man->persist.special_state_msg);
+ else
+ msg (M_CLIENT, "ERROR: signal '%s' is currently ignored", name);
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: signal '%s' is not a known signal type", name);
+ }
+}
+
+static void
+man_status (struct management *man, const int version, struct status_output *so)
+{
+ if (man->persist.callback.status)
+ {
+ (*man->persist.callback.status) (man->persist.callback.arg, version, so);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The 'status' command is not supported by the current daemon mode");
+ }
+}
+
+static void
+man_bytecount (struct management *man, const int update_seconds)
+{
+ if (update_seconds >= 0)
+ man->connection.bytecount_update_seconds = update_seconds;
+ else
+ man->connection.bytecount_update_seconds = 0;
+ msg (M_CLIENT, "SUCCESS: bytecount interval changed");
+}
+
+void
+man_bytecount_output_client (struct management *man)
+{
+ char in[32];
+ char out[32];
+ /* do in a roundabout way to work around possible mingw or mingw-glibc bug */
+ openvpn_snprintf (in, sizeof (in), counter_format, man->persist.bytes_in);
+ openvpn_snprintf (out, sizeof (out), counter_format, man->persist.bytes_out);
+ msg (M_CLIENT, ">BYTECOUNT:%s,%s", in, out);
+ man->connection.bytecount_last_update = now;
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+void
+man_bytecount_output_server (struct management *man,
+ const counter_type *bytes_in_total,
+ const counter_type *bytes_out_total,
+ struct man_def_auth_context *mdac)
+{
+ char in[32];
+ char out[32];
+ /* do in a roundabout way to work around possible mingw or mingw-glibc bug */
+ openvpn_snprintf (in, sizeof (in), counter_format, *bytes_in_total);
+ openvpn_snprintf (out, sizeof (out), counter_format, *bytes_out_total);
+ msg (M_CLIENT, ">BYTECOUNT_CLI:%lu,%s,%s", mdac->cid, in, out);
+ mdac->bytecount_last_update = now;
+}
+
+#endif
+
+static void
+man_kill (struct management *man, const char *victim)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (man->persist.callback.kill_by_cn && man->persist.callback.kill_by_addr)
+ {
+ struct buffer buf;
+ char p1[128];
+ char p2[128];
+ int n_killed;
+
+ buf_set_read (&buf, (uint8_t*) victim, strlen (victim) + 1);
+ buf_parse (&buf, ':', p1, sizeof (p1));
+ buf_parse (&buf, ':', p2, sizeof (p2));
+
+ if (strlen (p1) && strlen (p2))
+ {
+ /* IP:port specified */
+ bool status;
+ const in_addr_t addr = getaddr (GETADDR_HOST_ORDER|GETADDR_MSG_VIRT_OUT, p1, 0, &status, NULL);
+ if (status)
+ {
+ const int port = atoi (p2);
+ if (port > 0 && port < 65536)
+ {
+ n_killed = (*man->persist.callback.kill_by_addr) (man->persist.callback.arg, addr, port);
+ if (n_killed > 0)
+ {
+ msg (M_CLIENT, "SUCCESS: %d client(s) at address %s:%d killed",
+ n_killed,
+ print_in_addr_t (addr, 0, &gc),
+ port);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: client at address %s:%d not found",
+ print_in_addr_t (addr, 0, &gc),
+ port);
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: port number is out of range: %s", p2);
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: error parsing IP address: %s", p1);
+ }
+ }
+ else if (strlen (p1))
+ {
+ /* common name specified */
+ n_killed = (*man->persist.callback.kill_by_cn) (man->persist.callback.arg, p1);
+ if (n_killed > 0)
+ {
+ msg (M_CLIENT, "SUCCESS: common name '%s' found, %d client(s) killed", p1, n_killed);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: common name '%s' not found", p1);
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: kill parse");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The 'kill' command is not supported by the current daemon mode");
+ }
+
+ gc_free (&gc);
+}
+
+/*
+ * General-purpose history command handler
+ * for the log and echo commands.
+ */
+static void
+man_history (struct management *man,
+ const char *parm,
+ const char *type,
+ struct log_history *log,
+ bool *realtime,
+ const unsigned int lep_flags)
+{
+ struct gc_arena gc = gc_new ();
+ int n = 0;
+
+ if (streq (parm, "on"))
+ {
+ *realtime = true;
+ msg (M_CLIENT, "SUCCESS: real-time %s notification set to ON", type);
+ }
+ else if (streq (parm, "off"))
+ {
+ *realtime = false;
+ msg (M_CLIENT, "SUCCESS: real-time %s notification set to OFF", type);
+ }
+ else if (streq (parm, "all") || (n = atoi (parm)) > 0)
+ {
+ const int size = log_history_size (log);
+ const int start = (n ? n : size) - 1;
+ int i;
+
+ for (i = start; i >= 0; --i)
+ {
+ const struct log_entry *e = log_history_ref (log, i);
+ if (e)
+ {
+ const char *out = log_entry_print (e, lep_flags, &gc);
+ virtual_output_callback_func (man, M_CLIENT, out);
+ }
+ }
+ msg (M_CLIENT, "END");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: %s parameter must be 'on' or 'off' or some number n or 'all'", type);
+ }
+
+ gc_free (&gc);
+}
+
+static void
+man_log (struct management *man, const char *parm)
+{
+ man_history (man,
+ parm,
+ "log",
+ man->persist.log,
+ &man->connection.log_realtime,
+ LOG_PRINT_INT_DATE|LOG_PRINT_MSG_FLAGS);
+}
+
+static void
+man_echo (struct management *man, const char *parm)
+{
+ man_history (man,
+ parm,
+ "echo",
+ man->persist.echo,
+ &man->connection.echo_realtime,
+ LOG_PRINT_INT_DATE|MANAGEMENT_ECHO_FLAGS);
+}
+
+static void
+man_state (struct management *man, const char *parm)
+{
+ man_history (man,
+ parm,
+ "state",
+ man->persist.state,
+ &man->connection.state_realtime,
+ LOG_PRINT_INT_DATE|LOG_PRINT_STATE|
+ LOG_PRINT_LOCAL_IP|LOG_PRINT_REMOTE_IP);
+}
+
+static void
+man_up_finalize (struct management *man)
+{
+ switch (man->connection.up_query_mode)
+ {
+ case UP_QUERY_DISABLED:
+ man->connection.up_query.defined = false;
+ break;
+ case UP_QUERY_USER_PASS:
+ if (strlen (man->connection.up_query.username) && strlen (man->connection.up_query.password))
+ man->connection.up_query.defined = true;
+ break;
+ case UP_QUERY_PASS:
+ if (strlen (man->connection.up_query.password))
+ man->connection.up_query.defined = true;
+ break;
+ case UP_QUERY_NEED_OK:
+ if (strlen (man->connection.up_query.password))
+ man->connection.up_query.defined = true;
+ break;
+ case UP_QUERY_NEED_STR:
+ if (strlen (man->connection.up_query.password))
+ man->connection.up_query.defined = true;
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+static void
+man_query_user_pass (struct management *man,
+ const char *type,
+ const char *string,
+ const bool needed,
+ const char *prompt,
+ char *dest,
+ int len)
+{
+ if (needed)
+ {
+ ASSERT (man->connection.up_query_type);
+ if (streq (man->connection.up_query_type, type))
+ {
+ strncpynt (dest, string, len);
+ man_up_finalize (man);
+ msg (M_CLIENT, "SUCCESS: '%s' %s entered, but not yet verified",
+ type,
+ prompt);
+ }
+ else
+ msg (M_CLIENT, "ERROR: %s of type '%s' entered, but we need one of type '%s'",
+ prompt,
+ type,
+ man->connection.up_query_type);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: no %s is currently needed at this time", prompt);
+ }
+}
+
+static void
+man_query_username (struct management *man, const char *type, const char *string)
+{
+ const bool needed = (man->connection.up_query_mode == UP_QUERY_USER_PASS && man->connection.up_query_type);
+ man_query_user_pass (man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN);
+}
+
+static void
+man_query_password (struct management *man, const char *type, const char *string)
+{
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
+ || man->connection.up_query_mode == UP_QUERY_PASS)
+ && man->connection.up_query_type);
+ if (!string[0]) /* allow blank passwords to be passed through using the blank_up tag */
+ string = blank_up;
+ man_query_user_pass (man, type, string, needed, "password", man->connection.up_query.password, USER_PASS_LEN);
+}
+
+static void
+man_query_need_ok (struct management *man, const char *type, const char *action)
+{
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_NEED_OK) && man->connection.up_query_type);
+ man_query_user_pass (man, type, action, needed, "needok-confirmation", man->connection.up_query.password, USER_PASS_LEN);
+}
+
+static void
+man_query_need_str (struct management *man, const char *type, const char *action)
+{
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_NEED_STR) && man->connection.up_query_type);
+ man_query_user_pass (man, type, action, needed, "needstr-string", man->connection.up_query.password, USER_PASS_LEN);
+}
+
+static void
+man_forget_passwords (struct management *man)
+{
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ ssl_purge_auth ();
+ msg (M_CLIENT, "SUCCESS: Passwords were forgotten");
+#endif
+}
+
+static void
+man_net (struct management *man)
+{
+ if (man->persist.callback.show_net)
+ {
+ (*man->persist.callback.show_net) (man->persist.callback.arg, M_CLIENT);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The 'net' command is not supported by the current daemon mode");
+ }
+}
+
+#ifdef ENABLE_PKCS11
+
+static void
+man_pkcs11_id_count (struct management *man)
+{
+ msg (M_CLIENT, ">PKCS11ID-COUNT:%d", pkcs11_management_id_count ());
+}
+
+static void
+man_pkcs11_id_get (struct management *man, const int index)
+{
+ char *id = NULL;
+ char *base64 = NULL;
+
+ if (pkcs11_management_id_get (index, &id, &base64))
+ msg (M_CLIENT, ">PKCS11ID-ENTRY:'%d', ID:'%s', BLOB:'%s'", index, id, base64);
+ else
+ msg (M_CLIENT, ">PKCS11ID-ENTRY:'%d'", index);
+
+ if (id != NULL)
+ free (id);
+ if (base64 != NULL)
+ free (base64);
+}
+
+#endif
+
+static void
+man_hold (struct management *man, const char *cmd)
+{
+ if (cmd)
+ {
+ if (streq (cmd, "on"))
+ {
+ man->settings.flags |= MF_HOLD;
+ msg (M_CLIENT, "SUCCESS: hold flag set to ON");
+ }
+ else if (streq (cmd, "off"))
+ {
+ man->settings.flags &= ~MF_HOLD;
+ msg (M_CLIENT, "SUCCESS: hold flag set to OFF");
+ }
+ else if (streq (cmd, "release"))
+ {
+ man->persist.hold_release = true;
+ msg (M_CLIENT, "SUCCESS: hold release succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: bad hold command parameter");
+ }
+ }
+ else
+ msg (M_CLIENT, "SUCCESS: hold=%d", BOOL_CAST(man->settings.flags & MF_HOLD));
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static bool
+parse_cid (const char *str, unsigned long *cid)
+{
+ if (sscanf (str, "%lu", cid) == 1)
+ return true;
+ else
+ {
+ msg (M_CLIENT, "ERROR: cannot parse CID");
+ return false;
+ }
+}
+
+static bool
+parse_kid (const char *str, unsigned int *kid)
+{
+ if (sscanf (str, "%u", kid) == 1)
+ return true;
+ else
+ {
+ msg (M_CLIENT, "ERROR: cannot parse KID");
+ return false;
+ }
+}
+
+static void
+in_extra_reset (struct man_connection *mc, const bool new)
+{
+ if (mc)
+ {
+ if (!new)
+ {
+ mc->in_extra_cmd = IEC_UNDEF;
+ mc->in_extra_cid = 0;
+ mc->in_extra_kid = 0;
+ }
+ if (mc->in_extra)
+ {
+ buffer_list_free (mc->in_extra);
+ mc->in_extra = NULL;
+ }
+ if (new)
+ mc->in_extra = buffer_list_new (0);
+ }
+}
+
+static void
+in_extra_dispatch (struct management *man)
+{
+ switch (man->connection.in_extra_cmd)
+ {
+ case IEC_CLIENT_AUTH:
+ if (man->persist.callback.client_auth)
+ {
+ const bool status = (*man->persist.callback.client_auth)
+ (man->persist.callback.arg,
+ man->connection.in_extra_cid,
+ man->connection.in_extra_kid,
+ true,
+ NULL,
+ NULL,
+ man->connection.in_extra);
+ man->connection.in_extra = NULL;
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: client-auth command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: client-auth command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode");
+ }
+ break;
+#ifdef MANAGEMENT_PF
+ case IEC_CLIENT_PF:
+ if (man->persist.callback.client_pf)
+ {
+ const bool status = (*man->persist.callback.client_pf)
+ (man->persist.callback.arg,
+ man->connection.in_extra_cid,
+ man->connection.in_extra);
+ man->connection.in_extra = NULL;
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: client-pf command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: client-pf command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The client-pf command is not supported by the current daemon mode");
+ }
+ break;
+#endif
+ }
+ in_extra_reset (&man->connection, false);
+}
+
+static void
+man_client_auth (struct management *man, const char *cid_str, const char *kid_str, const bool extra)
+{
+ struct man_connection *mc = &man->connection;
+ mc->in_extra_cid = 0;
+ mc->in_extra_kid = 0;
+ if (parse_cid (cid_str, &mc->in_extra_cid)
+ && parse_kid (kid_str, &mc->in_extra_kid))
+ {
+ mc->in_extra_cmd = IEC_CLIENT_AUTH;
+ in_extra_reset (mc, true);
+ if (!extra)
+ in_extra_dispatch (man);
+ }
+}
+
+static void
+man_client_deny (struct management *man, const char *cid_str, const char *kid_str, const char *reason, const char *client_reason)
+{
+ unsigned long cid = 0;
+ unsigned int kid = 0;
+ if (parse_cid (cid_str, &cid) && parse_kid (kid_str, &kid))
+ {
+ if (man->persist.callback.client_auth)
+ {
+ const bool status = (*man->persist.callback.client_auth)
+ (man->persist.callback.arg,
+ cid,
+ kid,
+ false,
+ reason,
+ client_reason,
+ NULL);
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: client-deny command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: client-deny command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The client-deny command is not supported by the current daemon mode");
+ }
+ }
+}
+
+static void
+man_client_kill (struct management *man, const char *cid_str)
+{
+ unsigned long cid = 0;
+ if (parse_cid (cid_str, &cid))
+ {
+ if (man->persist.callback.kill_by_cid)
+ {
+ const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid);
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: client-kill command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: client-kill command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The client-kill command is not supported by the current daemon mode");
+ }
+ }
+}
+
+static void
+man_client_n_clients (struct management *man)
+{
+ if (man->persist.callback.n_clients)
+ {
+ const int nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
+ msg (M_CLIENT, "SUCCESS: nclients=%d", nclients);
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The nclients command is not supported by the current daemon mode");
+ }
+}
+
+static void
+man_env_filter (struct management *man, const int level)
+{
+ man->connection.env_filter_level = level;
+ msg (M_CLIENT, "SUCCESS: env_filter_level=%d", level);
+}
+
+#ifdef MANAGEMENT_PF
+
+static void
+man_client_pf (struct management *man, const char *cid_str)
+{
+ struct man_connection *mc = &man->connection;
+ mc->in_extra_cid = 0;
+ mc->in_extra_kid = 0;
+ if (parse_cid (cid_str, &mc->in_extra_cid))
+ {
+ mc->in_extra_cmd = IEC_CLIENT_PF;
+ in_extra_reset (mc, true);
+ }
+}
+
+#endif
+#endif
+
+static void
+man_load_stats (struct management *man)
+{
+ extern counter_type link_read_bytes_global;
+ extern counter_type link_write_bytes_global;
+ int nclients = 0;
+
+ if (man->persist.callback.n_clients)
+ nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
+ msg (M_CLIENT, "SUCCESS: nclients=%d,bytesin=" counter_format ",bytesout=" counter_format,
+ nclients,
+ link_read_bytes_global,
+ link_write_bytes_global);
+}
+
+#define MN_AT_LEAST (1<<0)
+
+static bool
+man_need (struct management *man, const char **p, const int n, unsigned int flags)
+{
+ int i;
+ ASSERT (p[0]);
+ for (i = 1; i <= n; ++i)
+ {
+ if (!p[i])
+ {
+ msg (M_CLIENT, "ERROR: the '%s' command requires %s%d parameter%s",
+ p[0],
+ (flags & MN_AT_LEAST) ? "at least " : "",
+ n,
+ n > 1 ? "s" : "");
+ return false;
+ }
+ }
+ return true;
+}
+
+#if HTTP_PROXY_FALLBACK
+
+static void
+man_http_proxy_fallback (struct management *man, const char *server, const char *port, const char *flags)
+{
+ if (man->persist.callback.http_proxy_fallback_cmd)
+ {
+ const bool status = (*man->persist.callback.http_proxy_fallback_cmd)(man->persist.callback.arg, server, port, flags);
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: proxy-fallback command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: proxy-fallback command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The proxy-fallback command is not supported by the current daemon mode");
+ }
+}
+
+#endif
+
+static void
+man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms)
+{
+ struct gc_arena gc = gc_new ();
+
+ ASSERT (p[0]);
+ if (streq (p[0], "exit") || streq (p[0], "quit"))
+ {
+ man->connection.halt = true;
+ goto done;
+ }
+ else if (streq (p[0], "help"))
+ {
+ man_help ();
+ }
+ else if (streq (p[0], "version"))
+ {
+ msg (M_CLIENT, "OpenVPN Version: %s", title_string);
+ msg (M_CLIENT, "Management Version: %d", MANAGEMENT_VERSION);
+ msg (M_CLIENT, "END");
+ }
+ else if (streq (p[0], "pid"))
+ {
+ msg (M_CLIENT, "SUCCESS: pid=%d", openvpn_getpid ());
+ }
+#ifdef MANAGEMENT_DEF_AUTH
+ else if (streq (p[0], "nclients"))
+ {
+ man_client_n_clients (man);
+ }
+ else if (streq (p[0], "env-filter"))
+ {
+ int level = 0;
+ if (p[1])
+ level = atoi (p[1]);
+ man_env_filter (man, level);
+ }
+#endif
+ else if (streq (p[0], "signal"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_signal (man, p[1]);
+ }
+ else if (streq (p[0], "load-stats"))
+ {
+ man_load_stats (man);
+ }
+ else if (streq (p[0], "status"))
+ {
+ int version = 0;
+ if (p[1])
+ version = atoi (p[1]);
+ man_status (man, version, so);
+ }
+ else if (streq (p[0], "kill"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_kill (man, p[1]);
+ }
+ else if (streq (p[0], "verb"))
+ {
+ if (p[1])
+ {
+ const int level = atoi(p[1]);
+ if (set_debug_level (level, 0))
+ msg (M_CLIENT, "SUCCESS: verb level changed");
+ else
+ msg (M_CLIENT, "ERROR: verb level is out of range");
+ }
+ else
+ msg (M_CLIENT, "SUCCESS: verb=%d", get_debug_level ());
+ }
+ else if (streq (p[0], "mute"))
+ {
+ if (p[1])
+ {
+ const int level = atoi(p[1]);
+ if (set_mute_cutoff (level))
+ msg (M_CLIENT, "SUCCESS: mute level changed");
+ else
+ msg (M_CLIENT, "ERROR: mute level is out of range");
+ }
+ else
+ msg (M_CLIENT, "SUCCESS: mute=%d", get_mute_cutoff ());
+ }
+ else if (streq (p[0], "auth-retry"))
+ {
+#if P2MP
+ if (p[1])
+ {
+ if (auth_retry_set (M_CLIENT, p[1]))
+ msg (M_CLIENT, "SUCCESS: auth-retry parameter changed");
+ else
+ msg (M_CLIENT, "ERROR: bad auth-retry parameter");
+ }
+ else
+ msg (M_CLIENT, "SUCCESS: auth-retry=%s", auth_retry_print ());
+#else
+ msg (M_CLIENT, "ERROR: auth-retry feature is unavailable");
+#endif
+ }
+ else if (streq (p[0], "state"))
+ {
+ if (!p[1])
+ {
+ man_state (man, "1");
+ }
+ else
+ {
+ if (p[1])
+ man_state (man, p[1]);
+ if (p[2])
+ man_state (man, p[2]);
+ }
+ }
+ else if (streq (p[0], "log"))
+ {
+ if (man_need (man, p, 1, MN_AT_LEAST))
+ {
+ if (p[1])
+ man_log (man, p[1]);
+ if (p[2])
+ man_log (man, p[2]);
+ }
+ }
+ else if (streq (p[0], "echo"))
+ {
+ if (man_need (man, p, 1, MN_AT_LEAST))
+ {
+ if (p[1])
+ man_echo (man, p[1]);
+ if (p[2])
+ man_echo (man, p[2]);
+ }
+ }
+ else if (streq (p[0], "username"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_query_username (man, p[1], p[2]);
+ }
+ else if (streq (p[0], "password"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_query_password (man, p[1], p[2]);
+ }
+ else if (streq (p[0], "forget-passwords"))
+ {
+ man_forget_passwords (man);
+ }
+ else if (streq (p[0], "needok"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_query_need_ok (man, p[1], p[2]);
+ }
+ else if (streq (p[0], "needstr"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_query_need_str (man, p[1], p[2]);
+ }
+ else if (streq (p[0], "net"))
+ {
+ man_net (man);
+ }
+ else if (streq (p[0], "hold"))
+ {
+ man_hold (man, p[1]);
+ }
+ else if (streq (p[0], "bytecount"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_bytecount (man, atoi(p[1]));
+ }
+#ifdef MANAGEMENT_DEF_AUTH
+ else if (streq (p[0], "client-kill"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_client_kill (man, p[1]);
+ }
+ else if (streq (p[0], "client-deny"))
+ {
+ if (man_need (man, p, 3, MN_AT_LEAST))
+ man_client_deny (man, p[1], p[2], p[3], p[4]);
+ }
+ else if (streq (p[0], "client-auth-nt"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_client_auth (man, p[1], p[2], false);
+ }
+ else if (streq (p[0], "client-auth"))
+ {
+ if (man_need (man, p, 2, 0))
+ man_client_auth (man, p[1], p[2], true);
+ }
+#ifdef MANAGEMENT_PF
+ else if (streq (p[0], "client-pf"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_client_pf (man, p[1]);
+ }
+#endif
+#endif
+#ifdef ENABLE_PKCS11
+ else if (streq (p[0], "pkcs11-id-count"))
+ {
+ man_pkcs11_id_count (man);
+ }
+ else if (streq (p[0], "pkcs11-id-get"))
+ {
+ if (man_need (man, p, 1, 0))
+ man_pkcs11_id_get (man, atoi(p[1]));
+ }
+#endif
+#if HTTP_PROXY_FALLBACK
+ else if (streq (p[0], "http-proxy-fallback"))
+ {
+ if (man_need (man, p, 2, MN_AT_LEAST))
+ man_http_proxy_fallback (man, p[1], p[2], p[3]);
+ }
+ else if (streq (p[0], "http-proxy-fallback-disable"))
+ {
+ man_http_proxy_fallback (man, NULL, NULL, NULL);
+ }
+#endif
+#if 1
+ else if (streq (p[0], "test"))
+ {
+ if (man_need (man, p, 1, 0))
+ {
+ int i;
+ const int n = atoi (p[1]);
+ for (i = 0; i < n; ++i)
+ {
+ msg (M_CLIENT, "[%d] The purpose of this command is to generate large amounts of output.", i);
+ }
+ }
+ }
+#endif
+ else
+ {
+ msg (M_CLIENT, "ERROR: unknown command, enter 'help' for more options");
+ }
+
+ done:
+ gc_free (&gc);
+}
+
+#ifdef WIN32
+
+static void
+man_start_ne32 (struct management *man)
+{
+ switch (man->connection.state)
+ {
+ case MS_LISTEN:
+ net_event_win32_start (&man->connection.ne32, FD_ACCEPT, man->connection.sd_top);
+ break;
+ case MS_CC_WAIT_READ:
+ case MS_CC_WAIT_WRITE:
+ net_event_win32_start (&man->connection.ne32, FD_READ|FD_WRITE|FD_CLOSE, man->connection.sd_cli);
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+static void
+man_stop_ne32 (struct management *man)
+{
+ net_event_win32_stop (&man->connection.ne32);
+}
+
+#endif
+
+static void
+man_record_peer_info (struct management *man)
+{
+ struct gc_arena gc = gc_new ();
+ if (man->settings.write_peer_info_file)
+ {
+ bool success = false;
+#ifdef HAVE_GETSOCKNAME
+ if (socket_defined (man->connection.sd_cli))
+ {
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof (addr);
+ int status;
+
+ CLEAR (addr);
+ status = getsockname (man->connection.sd_cli, (struct sockaddr *)&addr, &addrlen);
+ if (!status && addrlen == sizeof (addr))
+ {
+ const in_addr_t a = ntohl (addr.sin_addr.s_addr);
+ const int p = ntohs (addr.sin_port);
+ FILE *fp = fopen (man->settings.write_peer_info_file, "w");
+ if (fp)
+ {
+ fprintf (fp, "%s\n%d\n", print_in_addr_t (a, 0, &gc), p);
+ if (!fclose (fp))
+ success = true;
+ }
+ }
+ }
+#endif
+ if (!success)
+ {
+ msg (D_MANAGEMENT, "MANAGEMENT: failed to write peer info to file %s",
+ man->settings.write_peer_info_file);
+ throw_signal_soft (SIGTERM, "management-connect-failed");
+ }
+ }
+ gc_free (&gc);
+}
+
+static void
+man_connection_settings_reset (struct management *man)
+{
+ man->connection.state_realtime = false;
+ man->connection.log_realtime = false;
+ man->connection.echo_realtime = false;
+ man->connection.bytecount_update_seconds = 0;
+ man->connection.password_verified = false;
+ man->connection.password_tries = 0;
+ man->connection.halt = false;
+ man->connection.state = MS_CC_WAIT_WRITE;
+}
+
+static void
+man_new_connection_post (struct management *man, const char *description)
+{
+ struct gc_arena gc = gc_new ();
+
+ set_nonblock (man->connection.sd_cli);
+ set_cloexec (man->connection.sd_cli);
+
+ man_connection_settings_reset (man);
+
+#ifdef WIN32
+ man_start_ne32 (man);
+#endif
+
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ msg (D_MANAGEMENT, "MANAGEMENT: %s %s",
+ description,
+ sockaddr_unix_name (&man->settings.local_unix, "NULL"));
+ }
+ else
+#endif
+ msg (D_MANAGEMENT, "MANAGEMENT: %s %s",
+ description,
+ print_sockaddr (&man->settings.local, &gc));
+
+ buffer_list_reset (man->connection.out);
+
+ if (!man_password_needed (man))
+ man_welcome (man);
+ man_prompt (man);
+ man_update_io_state (man);
+
+ gc_free (&gc);
+}
+
+#if UNIX_SOCK_SUPPORT
+static bool
+man_verify_unix_peer_uid_gid (struct management *man, const socket_descriptor_t sd)
+{
+ if (socket_defined (sd) && (man->settings.client_uid != -1 || man->settings.client_gid != -1))
+ {
+ static const char err_prefix[] = "MANAGEMENT: unix domain socket client connection rejected --";
+ int uid, gid;
+ if (unix_socket_get_peer_uid_gid (man->connection.sd_cli, &uid, &gid))
+ {
+ if (man->settings.client_uid != -1 && man->settings.client_uid != uid)
+ {
+ msg (D_MANAGEMENT, "%s UID of socket peer (%d) doesn't match required value (%d) as given by --management-client-user",
+ err_prefix, uid, man->settings.client_uid);
+ return false;
+ }
+ if (man->settings.client_gid != -1 && man->settings.client_gid != gid)
+ {
+ msg (D_MANAGEMENT, "%s GID of socket peer (%d) doesn't match required value (%d) as given by --management-client-group",
+ err_prefix, gid, man->settings.client_gid);
+ return false;
+ }
+ }
+ else
+ {
+ msg (D_MANAGEMENT, "%s cannot get UID/GID of socket peer", err_prefix);
+ return false;
+ }
+ }
+ return true;
+}
+#endif
+
+static void
+man_accept (struct management *man)
+{
+ struct link_socket_actual act;
+ CLEAR (act);
+
+ /*
+ * Accept the TCP or Unix domain socket client.
+ */
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ struct sockaddr_un remote;
+ man->connection.sd_cli = socket_accept_unix (man->connection.sd_top, &remote);
+ if (!man_verify_unix_peer_uid_gid (man, man->connection.sd_cli))
+ sd_close (&man->connection.sd_cli);
+ }
+ else
+#endif
+ man->connection.sd_cli = socket_do_accept (man->connection.sd_top, &act, false);
+
+ if (socket_defined (man->connection.sd_cli))
+ {
+ man->connection.remote = act.dest;
+
+ if (socket_defined (man->connection.sd_top))
+ {
+#ifdef WIN32
+ man_stop_ne32 (man);
+#endif
+ }
+
+ man_new_connection_post (man, "Client connected from");
+ }
+}
+
+static void
+man_listen (struct management *man)
+{
+ struct gc_arena gc = gc_new ();
+
+ /*
+ * Initialize state
+ */
+ man->connection.state = MS_LISTEN;
+ man->connection.sd_cli = SOCKET_UNDEFINED;
+
+ /*
+ * Initialize listening socket
+ */
+ if (man->connection.sd_top == SOCKET_UNDEFINED)
+ {
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ man_delete_unix_socket (man);
+ man->connection.sd_top = create_socket_unix ();
+ socket_bind_unix (man->connection.sd_top, &man->settings.local_unix, "MANAGEMENT");
+ }
+ else
+#endif
+ {
+ man->connection.sd_top = create_socket_tcp ();
+ socket_bind (man->connection.sd_top, &man->settings.local, "MANAGEMENT");
+ }
+
+ /*
+ * Listen for connection
+ */
+ if (listen (man->connection.sd_top, 1))
+ msg (M_SOCKERR, "MANAGEMENT: listen() failed");
+
+ /*
+ * Set misc socket properties
+ */
+ set_nonblock (man->connection.sd_top);
+ set_cloexec (man->connection.sd_top);
+
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ msg (D_MANAGEMENT, "MANAGEMENT: unix domain socket listening on %s",
+ sockaddr_unix_name (&man->settings.local_unix, "NULL"));
+ }
+ else
+#endif
+ msg (D_MANAGEMENT, "MANAGEMENT: TCP Socket listening on %s",
+ print_sockaddr (&man->settings.local, &gc));
+ }
+
+#ifdef WIN32
+ man_start_ne32 (man);
+#endif
+
+ gc_free (&gc);
+}
+
+static void
+man_connect (struct management *man)
+{
+ struct gc_arena gc = gc_new ();
+ int status;
+ int signal_received = 0;
+
+ /*
+ * Initialize state
+ */
+ man->connection.state = MS_INITIAL;
+ man->connection.sd_top = SOCKET_UNDEFINED;
+
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ man->connection.sd_cli = create_socket_unix ();
+ status = socket_connect_unix (man->connection.sd_cli, &man->settings.local_unix);
+ if (!status && !man_verify_unix_peer_uid_gid (man, man->connection.sd_cli))
+ {
+#ifdef EPERM
+ status = EPERM;
+#else
+ status = 1;
+#endif
+ sd_close (&man->connection.sd_cli);
+ }
+ }
+ else
+#endif
+ {
+ man->connection.sd_cli = create_socket_tcp ();
+ status = openvpn_connect (man->connection.sd_cli,
+ &man->settings.local,
+ 5,
+ &signal_received);
+ }
+
+ if (signal_received)
+ {
+ throw_signal (signal_received);
+ goto done;
+ }
+
+ if (status)
+ {
+#if UNIX_SOCK_SUPPORT
+ if (man->settings.flags & MF_UNIX_SOCK)
+ {
+ msg (D_LINK_ERRORS,
+ "MANAGEMENT: connect to unix socket %s failed: %s",
+ sockaddr_unix_name (&man->settings.local_unix, "NULL"),
+ strerror_ts (status, &gc));
+ }
+ else
+#endif
+ msg (D_LINK_ERRORS,
+ "MANAGEMENT: connect to %s failed: %s",
+ print_sockaddr (&man->settings.local, &gc),
+ strerror_ts (status, &gc));
+ throw_signal_soft (SIGTERM, "management-connect-failed");
+ goto done;
+ }
+
+ man_record_peer_info (man);
+ man_new_connection_post (man, "Connected to management server at");
+
+ done:
+ gc_free (&gc);
+}
+
+static void
+man_reset_client_socket (struct management *man, const bool exiting)
+{
+ if (socket_defined (man->connection.sd_cli))
+ {
+#ifdef WIN32
+ man_stop_ne32 (man);
+#endif
+ man_close_socket (man, man->connection.sd_cli);
+ man->connection.sd_cli = SOCKET_UNDEFINED;
+ man->connection.state = MS_INITIAL;
+ command_line_reset (man->connection.in);
+ buffer_list_reset (man->connection.out);
+#ifdef MANAGEMENT_DEF_AUTH
+ in_extra_reset (&man->connection, false);
+#endif
+ msg (D_MANAGEMENT, "MANAGEMENT: Client disconnected");
+ }
+ if (!exiting)
+ {
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ if (man->settings.flags & MF_FORGET_DISCONNECT)
+ ssl_purge_auth ();
+#endif
+ if (man->settings.flags & MF_SIGNAL) {
+ int mysig = man_mod_signal (man, SIGUSR1);
+ if (mysig >= 0)
+ {
+ msg (D_MANAGEMENT, "MANAGEMENT: Triggering management signal");
+ throw_signal_soft (mysig, "management-disconnect");
+ }
+ }
+
+ if (man->settings.flags & MF_CONNECT_AS_CLIENT)
+ {
+ msg (D_MANAGEMENT, "MANAGEMENT: Triggering management exit");
+ throw_signal_soft (SIGTERM, "management-exit");
+ }
+ else
+ man_listen (man);
+ }
+}
+
+static void
+man_process_command (struct management *man, const char *line)
+{
+ struct gc_arena gc = gc_new ();
+ struct status_output *so;
+ int nparms;
+ char *parms[MAX_PARMS+1];
+
+ CLEAR (parms);
+ so = status_open (NULL, 0, -1, &man->persist.vout, 0);
+#ifdef MANAGEMENT_DEF_AUTH
+ in_extra_reset (&man->connection, false);
+#endif
+
+ if (man_password_needed (man))
+ {
+ man_check_password (man, line);
+ }
+ else
+ {
+ nparms = parse_line (line, parms, MAX_PARMS, "TCP", 0, M_CLIENT, &gc);
+ if (parms[0] && streq (parms[0], "password"))
+ msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD 'password [...]'");
+ else if (!streq (line, "load-stats"))
+ msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD '%s'", line);
+
+#if 0
+ /* DEBUGGING -- print args */
+ {
+ int i;
+ for (i = 0; i < nparms; ++i)
+ msg (M_INFO, "[%d] '%s'", i, parms[i]);
+ }
+#endif
+
+ if (nparms > 0)
+ man_dispatch_command (man, so, (const char **)parms, nparms);
+ }
+
+ CLEAR (parms);
+ status_close (so);
+ gc_free (&gc);
+}
+
+static bool
+man_io_error (struct management *man, const char *prefix)
+{
+ const int err = openvpn_errno_socket ();
+
+ if (!ignore_sys_error (err))
+ {
+ struct gc_arena gc = gc_new ();
+ msg (D_MANAGEMENT, "MANAGEMENT: TCP %s error: %s",
+ prefix,
+ strerror_ts (err, &gc));
+ gc_free (&gc);
+ return true;
+ }
+ else
+ return false;
+}
+
+static int
+man_read (struct management *man)
+{
+ /*
+ * read command line from socket
+ */
+ unsigned char buf[256];
+ int len = 0;
+
+ len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL);
+ if (len == 0)
+ {
+ man_reset_client_socket (man, false);
+ }
+ else if (len > 0)
+ {
+ bool processed_command = false;
+
+ ASSERT (len <= (int) sizeof (buf));
+ command_line_add (man->connection.in, buf, len);
+
+ /*
+ * Reset output object
+ */
+ buffer_list_reset (man->connection.out);
+
+ /*
+ * process command line if complete
+ */
+ {
+ const unsigned char *line;
+ while ((line = command_line_get (man->connection.in)))
+ {
+#ifdef MANAGEMENT_DEF_AUTH
+ if (man->connection.in_extra)
+ {
+ if (!strcmp ((char *)line, "END"))
+ {
+ in_extra_dispatch (man);
+ in_extra_reset (&man->connection, false);
+ }
+ else
+ {
+ buffer_list_push (man->connection.in_extra, line);
+ }
+ }
+ else
+#endif
+ man_process_command (man, (char *) line);
+ if (man->connection.halt)
+ break;
+ command_line_next (man->connection.in);
+ processed_command = true;
+ }
+ }
+
+ /*
+ * Reset output state to MS_CC_WAIT_(READ|WRITE)
+ */
+ if (man->connection.halt)
+ {
+ man_reset_client_socket (man, false);
+ len = 0;
+ }
+ else
+ {
+ if (processed_command)
+ man_prompt (man);
+ man_update_io_state (man);
+ }
+ }
+ else /* len < 0 */
+ {
+ if (man_io_error (man, "recv"))
+ man_reset_client_socket (man, false);
+ }
+ return len;
+}
+
+static int
+man_write (struct management *man)
+{
+ const int size_hint = 1024;
+ int sent = 0;
+ const struct buffer *buf;
+
+ buffer_list_aggregate(man->connection.out, size_hint);
+ buf = buffer_list_peek (man->connection.out);
+ if (buf && BLEN (buf))
+ {
+ const int len = min_int (size_hint, BLEN (buf));
+ sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL);
+ if (sent >= 0)
+ {
+ buffer_list_advance (man->connection.out, sent);
+ }
+ else if (sent < 0)
+ {
+ if (man_io_error (man, "send"))
+ man_reset_client_socket (man, false);
+ }
+ }
+
+ /*
+ * Reset output state to MS_CC_WAIT_(READ|WRITE)
+ */
+ man_update_io_state (man);
+
+ return sent;
+}
+
+static void
+man_connection_clear (struct man_connection *mc)
+{
+ CLEAR (*mc);
+
+ /* set initial state */
+ mc->state = MS_INITIAL;
+
+ /* clear socket descriptors */
+ mc->sd_top = SOCKET_UNDEFINED;
+ mc->sd_cli = SOCKET_UNDEFINED;
+}
+
+static void
+man_persist_init (struct management *man,
+ const int log_history_cache,
+ const int echo_buffer_size,
+ const int state_buffer_size)
+{
+ struct man_persist *mp = &man->persist;
+ if (!mp->defined)
+ {
+ CLEAR (*mp);
+
+ /* initialize log history store */
+ mp->log = log_history_init (log_history_cache);
+
+ /*
+ * Initialize virtual output object, so that functions
+ * which write to a virtual_output object can be redirected
+ * here to the management object.
+ */
+ mp->vout.func = virtual_output_callback_func;
+ mp->vout.arg = man;
+ mp->vout.flags_default = M_CLIENT;
+ msg_set_virtual_output (&mp->vout);
+
+ /*
+ * Initialize --echo list
+ */
+ man->persist.echo = log_history_init (echo_buffer_size);
+
+ /*
+ * Initialize --state list
+ */
+ man->persist.state = log_history_init (state_buffer_size);
+
+ mp->defined = true;
+ }
+}
+
+static void
+man_persist_close (struct man_persist *mp)
+{
+ if (mp->log)
+ {
+ msg_set_virtual_output (NULL);
+ log_history_close (mp->log);
+ }
+
+ if (mp->echo)
+ log_history_close (mp->echo);
+
+ if (mp->state)
+ log_history_close (mp->state);
+
+ CLEAR (*mp);
+}
+
+static void
+man_settings_init (struct man_settings *ms,
+ const char *addr,
+ const int port,
+ const char *pass_file,
+ const char *client_user,
+ const char *client_group,
+ const int log_history_cache,
+ const int echo_buffer_size,
+ const int state_buffer_size,
+ const char *write_peer_info_file,
+ const int remap_sigusr1,
+ const unsigned int flags)
+{
+ if (!ms->defined)
+ {
+ CLEAR (*ms);
+
+ ms->flags = flags;
+ ms->client_uid = -1;
+ ms->client_gid = -1;
+
+ /*
+ * Get username/password
+ */
+ if (pass_file)
+ get_user_pass (&ms->up, pass_file, "Management", GET_USER_PASS_PASSWORD_ONLY);
+
+ /*
+ * lookup client UID/GID if specified
+ */
+ if (client_user)
+ {
+ struct user_state s;
+ get_user (client_user, &s);
+ ms->client_uid = user_state_uid (&s);
+ msg (D_MANAGEMENT, "MANAGEMENT: client_uid=%d", ms->client_uid);
+ ASSERT (ms->client_uid >= 0);
+ }
+ if (client_group)
+ {
+ struct group_state s;
+ get_group (client_group, &s);
+ ms->client_gid = group_state_gid (&s);
+ msg (D_MANAGEMENT, "MANAGEMENT: client_gid=%d", ms->client_gid);
+ ASSERT (ms->client_gid >= 0);
+ }
+
+ ms->write_peer_info_file = string_alloc (write_peer_info_file, NULL);
+
+#if UNIX_SOCK_SUPPORT
+ if (ms->flags & MF_UNIX_SOCK)
+ sockaddr_unix_init (&ms->local_unix, addr);
+ else
+#endif
+ {
+ /*
+ * Initialize socket address
+ */
+ ms->local.sa.sin_family = AF_INET;
+ ms->local.sa.sin_addr.s_addr = 0;
+ ms->local.sa.sin_port = htons (port);
+
+ /*
+ * Run management over tunnel, or
+ * separate channel?
+ */
+ if (streq (addr, "tunnel") && !(flags & MF_CONNECT_AS_CLIENT))
+ {
+ ms->management_over_tunnel = true;
+ }
+ else
+ {
+ ms->local.sa.sin_addr.s_addr = getaddr
+ (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
+ }
+ }
+
+ /*
+ * Log history and echo buffer may need to be resized
+ */
+ ms->log_history_cache = log_history_cache;
+ ms->echo_buffer_size = echo_buffer_size;
+ ms->state_buffer_size = state_buffer_size;
+
+ /*
+ * Set remap sigusr1 flags
+ */
+ if (remap_sigusr1 == SIGHUP)
+ ms->mansig |= MANSIG_MAP_USR1_TO_HUP;
+ else if (remap_sigusr1 == SIGTERM)
+ ms->mansig |= MANSIG_MAP_USR1_TO_TERM;
+
+ ms->defined = true;
+ }
+}
+
+static void
+man_settings_close (struct man_settings *ms)
+{
+ free (ms->write_peer_info_file);
+ CLEAR (*ms);
+}
+
+
+static void
+man_connection_init (struct management *man)
+{
+ if (man->connection.state == MS_INITIAL)
+ {
+#ifdef WIN32
+ /*
+ * This object is a sort of TCP/IP helper
+ * for Windows.
+ */
+ net_event_win32_init (&man->connection.ne32);
+#endif
+
+ /*
+ * Allocate helper objects for command line input and
+ * command output from/to the socket.
+ */
+ man->connection.in = command_line_new (1024);
+ man->connection.out = buffer_list_new (0);
+
+ /*
+ * Initialize event set for standalone usage, when we are
+ * running outside of the primary event loop.
+ */
+ {
+ int maxevents = 1;
+ man->connection.es = event_set_init (&maxevents, EVENT_METHOD_FAST);
+ }
+
+ /*
+ * Listen/connect socket
+ */
+ if (man->settings.flags & MF_CONNECT_AS_CLIENT)
+ man_connect (man);
+ else
+ man_listen (man);
+ }
+}
+
+static void
+man_connection_close (struct management *man)
+{
+ struct man_connection *mc = &man->connection;
+
+ if (mc->es)
+ event_free (mc->es);
+#ifdef WIN32
+ net_event_win32_close (&mc->ne32);
+#endif
+ if (socket_defined (mc->sd_top))
+ {
+ man_close_socket (man, mc->sd_top);
+ man_delete_unix_socket (man);
+ }
+ if (socket_defined (mc->sd_cli))
+ man_close_socket (man, mc->sd_cli);
+ if (mc->in)
+ command_line_free (mc->in);
+ if (mc->out)
+ buffer_list_free (mc->out);
+#ifdef MANAGEMENT_DEF_AUTH
+ in_extra_reset (&man->connection, false);
+#endif
+ man_connection_clear (mc);
+}
+
+struct management *
+management_init (void)
+{
+ struct management *man;
+ ALLOC_OBJ_CLEAR (man, struct management);
+
+ man_persist_init (man,
+ MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
+ MANAGEMENT_ECHO_BUFFER_SIZE,
+ MANAGEMENT_STATE_BUFFER_SIZE);
+
+ man_connection_clear (&man->connection);
+
+ return man;
+}
+
+bool
+management_open (struct management *man,
+ const char *addr,
+ const int port,
+ const char *pass_file,
+ const char *client_user,
+ const char *client_group,
+ const int log_history_cache,
+ const int echo_buffer_size,
+ const int state_buffer_size,
+ const char *write_peer_info_file,
+ const int remap_sigusr1,
+ const unsigned int flags)
+{
+ bool ret = false;
+
+ /*
+ * Save the settings only if they have not
+ * been saved before.
+ */
+ man_settings_init (&man->settings,
+ addr,
+ port,
+ pass_file,
+ client_user,
+ client_group,
+ log_history_cache,
+ echo_buffer_size,
+ state_buffer_size,
+ write_peer_info_file,
+ remap_sigusr1,
+ flags);
+
+ /*
+ * The log is initially sized to MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
+ * but may be changed here. Ditto for echo and state buffers.
+ */
+ log_history_resize (man->persist.log, man->settings.log_history_cache);
+ log_history_resize (man->persist.echo, man->settings.echo_buffer_size);
+ log_history_resize (man->persist.state, man->settings.state_buffer_size);
+
+ /*
+ * If connection object is uninitialized and we are not doing
+ * over-the-tunnel management, then open (listening) connection.
+ */
+ if (man->connection.state == MS_INITIAL)
+ {
+ if (!man->settings.management_over_tunnel)
+ {
+ man_connection_init (man);
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+void
+management_close (struct management *man)
+{
+ man_connection_close (man);
+ man_settings_close (&man->settings);
+ man_persist_close (&man->persist);
+ free (man);
+}
+
+void
+management_set_callback (struct management *man,
+ const struct management_callback *cb)
+{
+ man->persist.standalone_disabled = true;
+ man->persist.callback = *cb;
+}
+
+void
+management_clear_callback (struct management *man)
+{
+ man->persist.standalone_disabled = false;
+ man->persist.hold_release = false;
+ CLEAR (man->persist.callback);
+ man_output_list_push_finalize (man); /* flush output queue */
+}
+
+void
+management_set_state (struct management *man,
+ const int state,
+ const char *detail,
+ const in_addr_t tun_local_ip,
+ const in_addr_t tun_remote_ip)
+{
+ if (man->persist.state && (!(man->settings.flags & MF_SERVER) || state < OPENVPN_STATE_CLIENT_BASE))
+ {
+ struct gc_arena gc = gc_new ();
+ struct log_entry e;
+ const char *out = NULL;
+
+ update_time ();
+ CLEAR (e);
+ e.timestamp = now;
+ e.u.state = state;
+ e.string = detail;
+ e.local_ip = tun_local_ip;
+ e.remote_ip = tun_remote_ip;
+
+ log_history_add (man->persist.state, &e);
+
+ if (man->connection.state_realtime)
+ out = log_entry_print (&e, LOG_PRINT_STATE_PREFIX
+ | LOG_PRINT_INT_DATE
+ | LOG_PRINT_STATE
+ | LOG_PRINT_LOCAL_IP
+ | LOG_PRINT_REMOTE_IP
+ | LOG_PRINT_CRLF
+ | LOG_ECHO_TO_LOG, &gc);
+
+ if (out)
+ man_output_list_push (man, out);
+
+ gc_free (&gc);
+ }
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static bool
+env_filter_match (const char *env_str, const int env_filter_level)
+{
+ static const char *env_names[] = {
+ "username=",
+ "password=",
+ "X509_0_CN=",
+ "tls_serial_0=",
+ "untrusted_ip=",
+ "ifconfig_local=",
+ "ifconfig_netmask=",
+ "daemon_start_time=",
+ "daemon_pid=",
+ "dev=",
+ "ifconfig_pool_remote_ip=",
+ "ifconfig_pool_netmask=",
+ "time_duration=",
+ "bytes_sent=",
+ "bytes_received="
+ };
+ if (env_filter_level >= 1)
+ {
+ size_t i;
+ for (i = 0; i < SIZE(env_names); ++i)
+ {
+ const char *en = env_names[i];
+ const size_t len = strlen(en);
+ if (strncmp(env_str, en, len) == 0)
+ return true;
+ }
+ return false;
+ }
+ else
+ return true;
+}
+
+static void
+man_output_env (const struct env_set *es, const bool tail, const int env_filter_level)
+{
+ if (es)
+ {
+ struct env_item *e;
+ for (e = es->list; e != NULL; e = e->next)
+ {
+ if (e->string && (!env_filter_level || env_filter_match(e->string, env_filter_level)))
+ msg (M_CLIENT, ">CLIENT:ENV,%s", e->string);
+ }
+ }
+ if (tail)
+ msg (M_CLIENT, ">CLIENT:ENV,END");
+}
+
+static void
+man_output_extra_env (struct management *man)
+{
+ struct gc_arena gc = gc_new ();
+ struct env_set *es = env_set_create (&gc);
+ if (man->persist.callback.n_clients)
+ {
+ const int nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
+ setenv_int (es, "n_clients", nclients);
+ }
+ man_output_env (es, false, man->connection.env_filter_level);
+ gc_free (&gc);
+}
+
+static bool
+validate_peer_info_line(const char *line)
+{
+ uint8_t c;
+ int state = 0;
+ while ((c=*line++))
+ {
+ switch (state)
+ {
+ case 0:
+ case 1:
+ if (c == '=' && state == 1)
+ state = 2;
+ else if (isalnum(c) || c == '_')
+ state = 1;
+ else
+ return false;
+ case 2:
+ if (isprint(c))
+ ;
+ else
+ return false;
+ }
+ }
+ return (state == 2);
+}
+
+static void
+man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac)
+{
+ char line[256];
+ if (man->persist.callback.get_peer_info)
+ {
+ const char *peer_info = (*man->persist.callback.get_peer_info) (man->persist.callback.arg, mdac->cid);
+ if (peer_info)
+ {
+ struct buffer buf;
+ buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info));
+ while (buf_parse (&buf, '\n', line, sizeof (line)))
+ {
+ chomp (line);
+ if (validate_peer_info_line(line))
+ {
+ msg (M_CLIENT, ">CLIENT:ENV,%s", line);
+ }
+ else
+ msg (D_MANAGEMENT, "validation failed on peer_info line received from client");
+ }
+ }
+ }
+}
+
+void
+management_notify_client_needing_auth (struct management *management,
+ const unsigned int mda_key_id,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es)
+{
+ if (!(mdac->flags & DAF_CONNECTION_CLOSED))
+ {
+ const char *mode = "CONNECT";
+ if (mdac->flags & DAF_CONNECTION_ESTABLISHED)
+ mode = "REAUTH";
+ msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
+ man_output_extra_env (management);
+ man_output_peer_info_env(management, mdac);
+ man_output_env (es, true, management->connection.env_filter_level);
+ mdac->flags |= DAF_INITIAL_AUTH;
+ }
+}
+
+void
+management_connection_established (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es)
+{
+ mdac->flags |= DAF_CONNECTION_ESTABLISHED;
+ msg (M_CLIENT, ">CLIENT:ESTABLISHED,%lu", mdac->cid);
+ man_output_extra_env (management);
+ man_output_env (es, true, management->connection.env_filter_level);
+}
+
+void
+management_notify_client_close (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es)
+{
+ if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
+ {
+ msg (M_CLIENT, ">CLIENT:DISCONNECT,%lu", mdac->cid);
+ man_output_env (es, true, management->connection.env_filter_level);
+ mdac->flags |= DAF_CONNECTION_CLOSED;
+ }
+}
+
+void
+management_learn_addr (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct mroute_addr *addr,
+ const bool primary)
+{
+ struct gc_arena gc = gc_new ();
+ if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
+ {
+ msg (M_CLIENT, ">CLIENT:ADDRESS,%lu,%s,%d",
+ mdac->cid,
+ mroute_addr_print_ex (addr, MAPF_SUBNET, &gc),
+ BOOL_CAST (primary));
+ }
+ gc_free (&gc);
+}
+
+#endif
+
+void
+management_echo (struct management *man, const char *string, const bool pull)
+{
+ if (man->persist.echo)
+ {
+ struct gc_arena gc = gc_new ();
+ struct log_entry e;
+ const char *out = NULL;
+
+ update_time ();
+ CLEAR (e);
+ e.timestamp = now;
+ e.string = string;
+ e.u.intval = BOOL_CAST (pull);
+
+ log_history_add (man->persist.echo, &e);
+
+ if (man->connection.echo_realtime)
+ out = log_entry_print (&e, LOG_PRINT_INT_DATE|LOG_PRINT_ECHO_PREFIX|LOG_PRINT_CRLF|MANAGEMENT_ECHO_FLAGS, &gc);
+
+ if (out)
+ man_output_list_push (man, out);
+
+ gc_free (&gc);
+ }
+}
+
+void
+management_post_tunnel_open (struct management *man, const in_addr_t tun_local_ip)
+{
+ /*
+ * If we are running management over the tunnel,
+ * this is the place to initialize the connection.
+ */
+ if (man->settings.management_over_tunnel
+ && man->connection.state == MS_INITIAL)
+ {
+ /* listen on our local TUN/TAP IP address */
+ man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
+ man_connection_init (man);
+ }
+
+}
+
+void
+management_pre_tunnel_close (struct management *man)
+{
+ if (man->settings.management_over_tunnel)
+ man_connection_close (man);
+}
+
+void
+management_auth_failure (struct management *man, const char *type, const char *reason)
+{
+ if (reason)
+ msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s' ['%s']", type, reason);
+ else
+ msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type);
+}
+
+static inline bool
+man_persist_state (unsigned int *persistent, const int n)
+{
+ if (persistent)
+ {
+ if (*persistent == (unsigned int)n)
+ return false;
+ *persistent = n;
+ }
+ return true;
+}
+
+#ifdef WIN32
+
+void
+management_socket_set (struct management *man,
+ struct event_set *es,
+ void *arg,
+ unsigned int *persistent)
+{
+ if (man->connection.state != MS_INITIAL)
+ {
+ event_t ev = net_event_win32_get_event (&man->connection.ne32);
+ net_event_win32_reset_write (&man->connection.ne32);
+
+ switch (man->connection.state)
+ {
+ case MS_LISTEN:
+ if (man_persist_state (persistent, 1))
+ event_ctl (es, ev, EVENT_READ, arg);
+ break;
+ case MS_CC_WAIT_READ:
+ if (man_persist_state (persistent, 2))
+ event_ctl (es, ev, EVENT_READ, arg);
+ break;
+ case MS_CC_WAIT_WRITE:
+ if (man_persist_state (persistent, 3))
+ event_ctl (es, ev, EVENT_READ|EVENT_WRITE, arg);
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+}
+
+void
+management_io (struct management *man)
+{
+ if (man->connection.state != MS_INITIAL)
+ {
+ long net_events;
+ net_event_win32_reset (&man->connection.ne32);
+ net_events = net_event_win32_get_event_mask (&man->connection.ne32);
+
+ if (net_events & FD_CLOSE)
+ {
+ man_reset_client_socket (man, false);
+ }
+ else
+ {
+ if (man->connection.state == MS_LISTEN)
+ {
+ if (net_events & FD_ACCEPT)
+ {
+ man_accept (man);
+ net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT);
+ }
+ }
+ else if (man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE)
+ {
+ if (net_events & FD_READ)
+ {
+ while (man_read (man) > 0)
+ ;
+ net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ);
+ }
+
+ if (net_events & FD_WRITE)
+ {
+ int status;
+ status = man_write (man);
+ if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
+ {
+ net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE);
+ }
+ }
+ }
+ }
+ }
+}
+
+#else
+
+void
+management_socket_set (struct management *man,
+ struct event_set *es,
+ void *arg,
+ unsigned int *persistent)
+{
+ switch (man->connection.state)
+ {
+ case MS_LISTEN:
+ if (man_persist_state (persistent, 1))
+ event_ctl (es, man->connection.sd_top, EVENT_READ, arg);
+ break;
+ case MS_CC_WAIT_READ:
+ if (man_persist_state (persistent, 2))
+ event_ctl (es, man->connection.sd_cli, EVENT_READ, arg);
+ break;
+ case MS_CC_WAIT_WRITE:
+ if (man_persist_state (persistent, 3))
+ event_ctl (es, man->connection.sd_cli, EVENT_WRITE, arg);
+ break;
+ case MS_INITIAL:
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+void
+management_io (struct management *man)
+{
+ switch (man->connection.state)
+ {
+ case MS_LISTEN:
+ man_accept (man);
+ break;
+ case MS_CC_WAIT_READ:
+ man_read (man);
+ break;
+ case MS_CC_WAIT_WRITE:
+ man_write (man);
+ break;
+ case MS_INITIAL:
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+#endif
+
+static inline bool
+man_standalone_ok (const struct management *man)
+{
+ return !man->settings.management_over_tunnel && man->connection.state != MS_INITIAL;
+}
+
+static bool
+man_check_for_signals (volatile int *signal_received)
+{
+ if (signal_received)
+ {
+ get_signal (signal_received);
+ if (*signal_received)
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Wait for socket I/O when outside primary event loop
+ */
+static int
+man_block (struct management *man, volatile int *signal_received, const time_t expire)
+{
+ struct timeval tv;
+ struct event_set_return esr;
+ int status = -1;
+
+ if (man_standalone_ok (man))
+ {
+ while (true)
+ {
+ event_reset (man->connection.es);
+ management_socket_set (man, man->connection.es, NULL, NULL);
+ tv.tv_usec = 0;
+ tv.tv_sec = 1;
+ if (man_check_for_signals (signal_received))
+ {
+ status = -1;
+ break;
+ }
+ status = event_wait (man->connection.es, &tv, &esr, 1);
+ update_time ();
+ if (man_check_for_signals (signal_received))
+ {
+ status = -1;
+ break;
+ }
+
+ if (status > 0)
+ break;
+ else if (expire && now >= expire)
+ {
+ /* set SIGINT signal if expiration time exceeded */
+ status = 0;
+ if (signal_received)
+ *signal_received = SIGINT;
+ break;
+ }
+ }
+ }
+ return status;
+}
+
+/*
+ * Perform management socket output outside primary event loop
+ */
+static void
+man_output_standalone (struct management *man, volatile int *signal_received)
+{
+ if (man_standalone_ok (man))
+ {
+ while (man->connection.state == MS_CC_WAIT_WRITE)
+ {
+ management_io (man);
+ if (man->connection.state == MS_CC_WAIT_WRITE)
+ man_block (man, signal_received, 0);
+ if (signal_received && *signal_received)
+ break;
+ }
+ }
+}
+
+/*
+ * Process management event loop outside primary event loop
+ */
+static int
+man_standalone_event_loop (struct management *man, volatile int *signal_received, const time_t expire)
+{
+ int status = -1;
+ if (man_standalone_ok (man))
+ {
+ status = man_block (man, signal_received, expire);
+ if (status > 0)
+ management_io (man);
+ }
+ return status;
+}
+
+#define MWCC_PASSWORD_WAIT (1<<0)
+#define MWCC_HOLD_WAIT (1<<1)
+
+/*
+ * Block until client connects
+ */
+static void
+man_wait_for_client_connection (struct management *man,
+ volatile int *signal_received,
+ const time_t expire,
+ unsigned int flags)
+{
+ ASSERT (man_standalone_ok (man));
+ if (man->connection.state == MS_LISTEN)
+ {
+ if (flags & MWCC_PASSWORD_WAIT)
+ msg (D_MANAGEMENT, "Need password(s) from management interface, waiting...");
+ if (flags & MWCC_HOLD_WAIT)
+ msg (D_MANAGEMENT, "Need hold release from management interface, waiting...");
+ do {
+ man_standalone_event_loop (man, signal_received, expire);
+ if (signal_received && *signal_received)
+ break;
+ } while (man->connection.state == MS_LISTEN || man_password_needed (man));
+ }
+}
+
+/*
+ * Process the management event loop for sec seconds
+ */
+void
+management_event_loop_n_seconds (struct management *man, int sec)
+{
+ if (man_standalone_ok (man))
+ {
+ volatile int signal_received = 0;
+ const bool standalone_disabled_save = man->persist.standalone_disabled;
+ time_t expire = 0;
+
+ man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
+
+ /* set expire time */
+ update_time ();
+ if (sec)
+ expire = now + sec;
+
+ /* if no client connection, wait for one */
+ man_wait_for_client_connection (man, &signal_received, expire, 0);
+ if (signal_received)
+ return;
+
+ /* run command processing event loop */
+ do
+ {
+ man_standalone_event_loop (man, &signal_received, expire);
+ if (!signal_received)
+ man_check_for_signals (&signal_received);
+ if (signal_received)
+ return;
+ } while (expire);
+
+ /* revert state */
+ man->persist.standalone_disabled = standalone_disabled_save;
+ }
+ else
+ {
+ sleep (sec);
+ }
+}
+
+/*
+ * Get a username/password from management channel in standalone mode.
+ */
+bool
+management_query_user_pass (struct management *man,
+ struct user_pass *up,
+ const char *type,
+ const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+ if (man_standalone_ok (man))
+ {
+ volatile int signal_received = 0;
+ const bool standalone_disabled_save = man->persist.standalone_disabled;
+ struct buffer alert_msg = alloc_buf_gc (128, &gc);
+ const char *alert_type = NULL;
+ const char *prefix = NULL;
+ unsigned int up_query_mode = 0;
+
+ ret = true;
+ man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
+ man->persist.special_state_msg = NULL;
+
+ CLEAR (man->connection.up_query);
+
+ if (flags & GET_USER_PASS_NEED_OK)
+ {
+ up_query_mode = UP_QUERY_NEED_OK;
+ prefix= "NEED-OK";
+ alert_type = "confirmation";
+ }
+ else if (flags & GET_USER_PASS_NEED_STR)
+ {
+ up_query_mode = UP_QUERY_NEED_STR;
+ prefix= "NEED-STR";
+ alert_type = "string";
+ }
+ else if (flags & GET_USER_PASS_PASSWORD_ONLY)
+ {
+ up_query_mode = UP_QUERY_PASS;
+ prefix = "PASSWORD";
+ alert_type = "password";
+ }
+ else
+ {
+ up_query_mode = UP_QUERY_USER_PASS;
+ prefix = "PASSWORD";
+ alert_type = "username/password";
+ }
+ buf_printf (&alert_msg, ">%s:Need '%s' %s",
+ prefix,
+ type,
+ alert_type);
+
+ if (flags & (GET_USER_PASS_NEED_OK | GET_USER_PASS_NEED_STR))
+ buf_printf (&alert_msg, " MSG:%s", up->username);
+
+ man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT);
+ if (signal_received)
+ ret = false;
+
+ if (ret)
+ {
+ man->persist.special_state_msg = BSTR (&alert_msg);
+ msg (M_CLIENT, "%s", man->persist.special_state_msg);
+
+ /* tell command line parser which info we need */
+ man->connection.up_query_mode = up_query_mode;
+ man->connection.up_query_type = type;
+
+ /* run command processing event loop until we get our username/password */
+ do
+ {
+ man_standalone_event_loop (man, &signal_received, 0);
+ if (!signal_received)
+ man_check_for_signals (&signal_received);
+ if (signal_received)
+ {
+ ret = false;
+ break;
+ }
+ } while (!man->connection.up_query.defined);
+ }
+
+ /* revert state */
+ man->connection.up_query_mode = UP_QUERY_DISABLED;
+ man->connection.up_query_type = NULL;
+ man->persist.standalone_disabled = standalone_disabled_save;
+ man->persist.special_state_msg = NULL;
+
+ /* pass through blank passwords */
+ if (!strcmp (man->connection.up_query.password, blank_up))
+ CLEAR (man->connection.up_query.password);
+
+ /*
+ * Transfer u/p to return object, zero any record
+ * we hold in the management object.
+ */
+ if (ret)
+ {
+ man->connection.up_query.nocache = up->nocache; /* preserve caller's nocache setting */
+ *up = man->connection.up_query;
+ }
+ CLEAR (man->connection.up_query);
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Return true if management_hold() would block
+ */
+bool
+management_would_hold (struct management *man)
+{
+ return (man->settings.flags & MF_HOLD) && !man->persist.hold_release && man_standalone_ok (man);
+}
+
+/*
+ * Return true if (from the management interface's perspective) OpenVPN should
+ * daemonize.
+ */
+bool
+management_should_daemonize (struct management *man)
+{
+ return management_would_hold (man) || (man->settings.flags & MF_QUERY_PASSWORDS);
+}
+
+/*
+ * If the hold flag is enabled, hibernate until a management client releases the hold.
+ * Return true if the caller should not sleep for an additional time interval.
+ */
+bool
+management_hold (struct management *man)
+{
+ if (management_would_hold (man))
+ {
+ volatile int signal_received = 0;
+ const bool standalone_disabled_save = man->persist.standalone_disabled;
+
+ man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
+ man->persist.special_state_msg = NULL;
+ man->settings.mansig |= MANSIG_IGNORE_USR1_HUP;
+
+ man_wait_for_client_connection (man, &signal_received, 0, MWCC_HOLD_WAIT);
+
+ if (!signal_received)
+ {
+ man->persist.special_state_msg = ">HOLD:Waiting for hold release";
+ msg (M_CLIENT, "%s", man->persist.special_state_msg);
+
+ /* run command processing event loop until we get our username/password */
+ do
+ {
+ man_standalone_event_loop (man, &signal_received, 0);
+ if (!signal_received)
+ man_check_for_signals (&signal_received);
+ if (signal_received)
+ break;
+ } while (!man->persist.hold_release);
+ }
+
+ /* revert state */
+ man->persist.standalone_disabled = standalone_disabled_save;
+ man->persist.special_state_msg = NULL;
+ man->settings.mansig &= ~MANSIG_IGNORE_USR1_HUP;
+
+ return true;
+ }
+ return false;
+}
+
+/*
+ * struct command_line
+ */
+
+struct command_line *
+command_line_new (const int buf_len)
+{
+ struct command_line *cl;
+ ALLOC_OBJ_CLEAR (cl, struct command_line);
+ cl->buf = alloc_buf (buf_len);
+ cl->residual = alloc_buf (buf_len);
+ return cl;
+}
+
+void
+command_line_reset (struct command_line *cl)
+{
+ buf_clear (&cl->buf);
+ buf_clear (&cl->residual);
+}
+
+void
+command_line_free (struct command_line *cl)
+{
+ command_line_reset (cl);
+ free_buf (&cl->buf);
+ free_buf (&cl->residual);
+ free (cl);
+}
+
+void
+command_line_add (struct command_line *cl, const unsigned char *buf, const int len)
+{
+ int i;
+ for (i = 0; i < len; ++i)
+ {
+ if (buf[i] && (isprint(buf[i]) || buf[i] == '\n'))
+ {
+ if (!buf_write_u8 (&cl->buf, buf[i]))
+ buf_clear (&cl->buf);
+ }
+ }
+}
+
+const unsigned char *
+command_line_get (struct command_line *cl)
+{
+ int i;
+ const unsigned char *ret = NULL;
+
+ i = buf_substring_len (&cl->buf, '\n');
+ if (i >= 0)
+ {
+ buf_copy_excess (&cl->residual, &cl->buf, i);
+ buf_chomp (&cl->buf);
+ ret = (const unsigned char *) BSTR (&cl->buf);
+ }
+ return ret;
+}
+
+void
+command_line_next (struct command_line *cl)
+{
+ buf_clear (&cl->buf);
+ buf_copy (&cl->buf, &cl->residual);
+ buf_clear (&cl->residual);
+}
+
+/*
+ * struct log_entry
+ */
+
+const char *
+log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (ERR_BUF_SIZE, gc);
+ if (flags & LOG_FATAL_NOTIFY)
+ buf_printf (&out, ">FATAL:");
+ if (flags & LOG_PRINT_LOG_PREFIX)
+ buf_printf (&out, ">LOG:");
+ if (flags & LOG_PRINT_ECHO_PREFIX)
+ buf_printf (&out, ">ECHO:");
+ if (flags & LOG_PRINT_STATE_PREFIX)
+ buf_printf (&out, ">STATE:");
+ if (flags & LOG_PRINT_INT_DATE)
+ buf_printf (&out, "%u,", (unsigned int)e->timestamp);
+ if (flags & LOG_PRINT_MSG_FLAGS)
+ buf_printf (&out, "%s,", msg_flags_string (e->u.msg_flags, gc));
+ if (flags & LOG_PRINT_STATE)
+ buf_printf (&out, "%s,", man_state_name (e->u.state));
+ if (flags & LOG_PRINT_INTVAL)
+ buf_printf (&out, "%d,", e->u.intval);
+ if (e->string)
+ buf_printf (&out, "%s", e->string);
+ if (flags & LOG_PRINT_LOCAL_IP)
+ buf_printf (&out, ",%s", print_in_addr_t (e->local_ip, IA_EMPTY_IF_UNDEF, gc));
+ if (flags & LOG_PRINT_REMOTE_IP)
+ buf_printf (&out, ",%s", print_in_addr_t (e->remote_ip, IA_EMPTY_IF_UNDEF, gc));
+ if (flags & LOG_ECHO_TO_LOG)
+ msg (D_MANAGEMENT, "MANAGEMENT: %s", BSTR (&out));
+ if (flags & LOG_PRINT_CRLF)
+ buf_printf (&out, "\r\n");
+ return BSTR (&out);
+}
+
+static void
+log_entry_free_contents (struct log_entry *e)
+{
+ if (e->string)
+ free ((char *)e->string);
+ CLEAR (*e);
+}
+
+/*
+ * struct log_history
+ */
+
+static inline int
+log_index (const struct log_history *h, int i)
+{
+ return modulo_add (h->base, i, h->capacity);
+}
+
+static void
+log_history_obj_init (struct log_history *h, int capacity)
+{
+ CLEAR (*h);
+ h->capacity = capacity;
+ ALLOC_ARRAY_CLEAR (h->array, struct log_entry, capacity);
+}
+
+struct log_history *
+log_history_init (const int capacity)
+{
+ struct log_history *h;
+ ASSERT (capacity > 0);
+ ALLOC_OBJ (h, struct log_history);
+ log_history_obj_init (h, capacity);
+ return h;
+}
+
+static void
+log_history_free_contents (struct log_history *h)
+{
+ int i;
+ for (i = 0; i < h->size; ++i)
+ log_entry_free_contents (&h->array[log_index(h, i)]);
+ free (h->array);
+}
+
+void
+log_history_close (struct log_history *h)
+{
+ log_history_free_contents (h);
+ free (h);
+}
+
+void
+log_history_add (struct log_history *h, const struct log_entry *le)
+{
+ struct log_entry *e;
+ ASSERT (h->size >= 0 && h->size <= h->capacity);
+ if (h->size == h->capacity)
+ {
+ e = &h->array[h->base];
+ log_entry_free_contents (e);
+ h->base = log_index (h, 1);
+ }
+ else
+ {
+ e = &h->array[log_index(h, h->size)];
+ ++h->size;
+ }
+
+ *e = *le;
+ e->string = string_alloc (le->string, NULL);
+}
+
+void
+log_history_resize (struct log_history *h, const int capacity)
+{
+ if (capacity != h->capacity)
+ {
+ struct log_history newlog;
+ int i;
+
+ ASSERT (capacity > 0);
+ log_history_obj_init (&newlog, capacity);
+
+ for (i = 0; i < h->size; ++i)
+ log_history_add (&newlog, &h->array[log_index(h, i)]);
+
+ log_history_free_contents (h);
+ *h = newlog;
+ }
+}
+
+const struct log_entry *
+log_history_ref (const struct log_history *h, const int index)
+{
+ if (index >= 0 && index < h->size)
+ return &h->array[log_index(h, (h->size - 1) - index)];
+ else
+ return NULL;
+}
+
+#if HTTP_PROXY_FALLBACK
+
+void
+management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint)
+{
+ if (remote_ip_hint)
+ msg (M_CLIENT, ">PROXY:%s,%s", type, remote_ip_hint);
+ else
+ msg (M_CLIENT, ">PROXY:%s", type);
+}
+
+#endif /* HTTP_PROXY_FALLBACK */
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_MANAGEMENT */
diff --git a/manage.h b/manage.h
new file mode 100644
index 0000000..6a9ccd8
--- /dev/null
+++ b/manage.h
@@ -0,0 +1,516 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MANAGE_H
+#define MANAGE_H
+
+#ifdef ENABLE_MANAGEMENT
+
+#include "misc.h"
+#include "event.h"
+#include "socket.h"
+#include "mroute.h"
+
+#define MANAGEMENT_VERSION 1
+#define MANAGEMENT_N_PASSWORD_RETRIES 3
+#define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE 100
+#define MANAGEMENT_ECHO_BUFFER_SIZE 100
+#define MANAGEMENT_STATE_BUFFER_SIZE 100
+
+/*
+ * Management-interface-based deferred authentication
+ */
+#ifdef MANAGEMENT_DEF_AUTH
+struct man_def_auth_context {
+ unsigned long cid;
+
+#define DAF_CONNECTION_ESTABLISHED (1<<0)
+#define DAF_CONNECTION_CLOSED (1<<1)
+#define DAF_INITIAL_AUTH (1<<2)
+ unsigned int flags;
+
+ unsigned int mda_key_id_counter;
+
+ time_t bytecount_last_update;
+};
+#endif
+
+/*
+ * Manage build-up of command line
+ */
+struct command_line
+{
+ struct buffer buf;
+ struct buffer residual;
+};
+
+struct command_line *command_line_new (const int buf_len);
+void command_line_free (struct command_line *cl);
+
+void command_line_add (struct command_line *cl, const unsigned char *buf, const int len);
+const unsigned char *command_line_get (struct command_line *cl);
+void command_line_reset (struct command_line *cl);
+void command_line_next (struct command_line *cl);
+
+/*
+ * Manage log file history
+ */
+
+union log_entry_union {
+ unsigned int msg_flags;
+ int state;
+ int intval;
+};
+
+struct log_entry
+{
+ time_t timestamp;
+ const char *string;
+ in_addr_t local_ip;
+ in_addr_t remote_ip;
+ union log_entry_union u;
+};
+
+#define LOG_PRINT_LOG_PREFIX (1<<0)
+#define LOG_PRINT_ECHO_PREFIX (1<<1)
+#define LOG_PRINT_STATE_PREFIX (1<<2)
+
+#define LOG_PRINT_INT_DATE (1<<3)
+#define LOG_PRINT_MSG_FLAGS (1<<4)
+#define LOG_PRINT_STATE (1<<5)
+#define LOG_PRINT_LOCAL_IP (1<<6)
+
+#define LOG_PRINT_CRLF (1<<7)
+#define LOG_FATAL_NOTIFY (1<<8)
+
+#define LOG_PRINT_INTVAL (1<<9)
+
+#define LOG_PRINT_REMOTE_IP (1<<10)
+
+#define LOG_ECHO_TO_LOG (1<<11)
+
+const char *log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena *gc);
+
+struct log_history
+{
+ int base;
+ int size;
+ int capacity;
+ struct log_entry *array;
+};
+
+struct log_history *log_history_init (const int capacity);
+void log_history_close (struct log_history *h);
+void log_history_add (struct log_history *h, const struct log_entry *le);
+void log_history_resize (struct log_history *h, const int capacity);
+const struct log_entry *log_history_ref (const struct log_history *h, const int index);
+
+static inline int
+log_history_size (const struct log_history *h)
+{
+ return h->size;
+}
+
+static inline int
+log_history_capacity (const struct log_history *h)
+{
+ return h->capacity;
+}
+
+/*
+ * Callbacks for 'status' and 'kill' commands.
+ * Also for management-based deferred authentication and packet filter.
+ */
+struct management_callback
+{
+ void *arg;
+
+# define MCF_SERVER (1<<0) /* is OpenVPN being run as a server? */
+ unsigned int flags;
+
+ void (*status) (void *arg, const int version, struct status_output *so);
+ void (*show_net) (void *arg, const int msglevel);
+ int (*kill_by_cn) (void *arg, const char *common_name);
+ int (*kill_by_addr) (void *arg, const in_addr_t addr, const int port);
+ void (*delete_event) (void *arg, event_t event);
+ int (*n_clients) (void *arg);
+#ifdef MANAGEMENT_DEF_AUTH
+ bool (*kill_by_cid) (void *arg, const unsigned long cid);
+ bool (*client_auth) (void *arg,
+ const unsigned long cid,
+ const unsigned int mda_key_id,
+ const bool auth,
+ const char *reason,
+ const char *client_reason,
+ struct buffer_list *cc_config); /* ownership transferred */
+ char *(*get_peer_info) (void *arg, const unsigned long cid);
+#endif
+#ifdef MANAGEMENT_PF
+ bool (*client_pf) (void *arg,
+ const unsigned long cid,
+ struct buffer_list *pf_config); /* ownership transferred */
+#endif
+#if HTTP_PROXY_FALLBACK
+ bool (*http_proxy_fallback_cmd) (void *arg, const char *server, const char *port, const char *flags);
+#endif
+};
+
+/*
+ * Management object, split into three components:
+ *
+ * struct man_persist : Data elements which are persistent across
+ * man_connection open and close.
+ *
+ * struct man_settings : management parameters.
+ *
+ * struct man_connection : created on socket binding and listen,
+ * deleted on socket unbind, may
+ * handle multiple sequential client
+ * connections.
+ */
+
+struct man_persist {
+ bool defined;
+
+ struct log_history *log;
+ struct virtual_output vout;
+
+ bool standalone_disabled;
+ struct management_callback callback;
+
+ struct log_history *echo; /* saved --echo strings */
+ struct log_history *state;
+
+ bool hold_release;
+
+ const char *special_state_msg;
+
+ counter_type bytes_in;
+ counter_type bytes_out;
+};
+
+struct man_settings {
+ bool defined;
+ unsigned int flags; /* MF_x flags */
+ struct openvpn_sockaddr local;
+#if UNIX_SOCK_SUPPORT
+ struct sockaddr_un local_unix;
+#endif
+ bool management_over_tunnel;
+ struct user_pass up;
+ int log_history_cache;
+ int echo_buffer_size;
+ int state_buffer_size;
+ char *write_peer_info_file;
+ int client_uid;
+ int client_gid;
+
+/* flags for handling the management interface "signal" command */
+# define MANSIG_IGNORE_USR1_HUP (1<<0)
+# define MANSIG_MAP_USR1_TO_HUP (1<<1)
+# define MANSIG_MAP_USR1_TO_TERM (1<<2)
+ unsigned int mansig;
+};
+
+/* up_query modes */
+#define UP_QUERY_DISABLED 0
+#define UP_QUERY_USER_PASS 1
+#define UP_QUERY_PASS 2
+#define UP_QUERY_NEED_OK 3
+#define UP_QUERY_NEED_STR 4
+
+/* states */
+#define MS_INITIAL 0 /* all sockets are closed */
+#define MS_LISTEN 1 /* no client is connected */
+#define MS_CC_WAIT_READ 2 /* client is connected, waiting for read on socket */
+#define MS_CC_WAIT_WRITE 3 /* client is connected, waiting for ability to write to socket */
+
+struct man_connection {
+ int state;
+
+ socket_descriptor_t sd_top;
+ socket_descriptor_t sd_cli;
+ struct openvpn_sockaddr remote;
+
+#ifdef WIN32
+ struct net_event_win32 ne32;
+#endif
+
+ bool halt;
+ bool password_verified;
+ int password_tries;
+
+ struct command_line *in;
+ struct buffer_list *out;
+
+#ifdef MANAGEMENT_DEF_AUTH
+# define IEC_UNDEF 0
+# define IEC_CLIENT_AUTH 1
+# define IEC_CLIENT_PF 2
+ int in_extra_cmd;
+ unsigned long in_extra_cid;
+ unsigned int in_extra_kid;
+ struct buffer_list *in_extra;
+ int env_filter_level;
+#endif
+ struct event_set *es;
+
+ bool state_realtime;
+ bool log_realtime;
+ bool echo_realtime;
+ int bytecount_update_seconds;
+ time_t bytecount_last_update;
+
+ const char *up_query_type;
+ int up_query_mode;
+ struct user_pass up_query;
+};
+
+struct management
+{
+ struct man_persist persist;
+ struct man_settings settings;
+ struct man_connection connection;
+};
+
+extern struct management *management;
+
+struct user_pass;
+
+struct management *management_init (void);
+
+/* management_open flags */
+# define MF_SERVER (1<<0)
+# define MF_QUERY_PASSWORDS (1<<1)
+# define MF_HOLD (1<<2)
+# define MF_SIGNAL (1<<3)
+# define MF_FORGET_DISCONNECT (1<<4)
+# define MF_CONNECT_AS_CLIENT (1<<5)
+#ifdef MANAGEMENT_DEF_AUTH
+# define MF_CLIENT_AUTH (1<<6)
+#endif
+#ifdef MANAGEMENT_PF
+# define MF_CLIENT_PF (1<<7)
+#endif
+# define MF_UNIX_SOCK (1<<8)
+
+bool management_open (struct management *man,
+ const char *addr,
+ const int port,
+ const char *pass_file,
+ const char *client_user,
+ const char *client_group,
+ const int log_history_cache,
+ const int echo_buffer_size,
+ const int state_buffer_size,
+ const char *write_peer_info_file,
+ const int remap_sigusr1,
+ const unsigned int flags);
+
+void management_close (struct management *man);
+
+void management_post_tunnel_open (struct management *man, const in_addr_t tun_local_ip);
+
+void management_pre_tunnel_close (struct management *man);
+
+void management_socket_set (struct management *man,
+ struct event_set *es,
+ void *arg,
+ unsigned int *persistent);
+
+void management_io (struct management *man);
+
+void management_set_callback (struct management *man,
+ const struct management_callback *cb);
+
+void management_clear_callback (struct management *man);
+
+bool management_query_user_pass (struct management *man, struct user_pass *up, const char *type, const unsigned int flags);
+
+bool management_should_daemonize (struct management *man);
+bool management_would_hold (struct management *man);
+bool management_hold (struct management *man);
+
+void management_event_loop_n_seconds (struct management *man, int sec);
+
+#ifdef MANAGEMENT_DEF_AUTH
+void management_notify_client_needing_auth (struct management *management,
+ const unsigned int auth_id,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es);
+
+void management_connection_established (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es);
+
+void management_notify_client_close (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct env_set *es);
+
+void management_learn_addr (struct management *management,
+ struct man_def_auth_context *mdac,
+ const struct mroute_addr *addr,
+ const bool primary);
+#endif
+
+static inline bool
+management_connected (const struct management *man)
+{
+ return man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE;
+}
+
+static inline bool
+management_query_user_pass_enabled (const struct management *man)
+{
+ return BOOL_CAST(man->settings.flags & MF_QUERY_PASSWORDS);
+}
+
+#ifdef MANAGEMENT_PF
+static inline bool
+management_enable_pf (const struct management *man)
+{
+ return man && BOOL_CAST(man->settings.flags & MF_CLIENT_PF);
+}
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+static inline bool
+management_enable_def_auth (const struct management *man)
+{
+ return man && BOOL_CAST(man->settings.flags & MF_CLIENT_AUTH);
+}
+#endif
+
+/*
+ * OpenVPN tells the management layer what state it's in
+ */
+
+/* client/server states */
+#define OPENVPN_STATE_INITIAL 0 /* Initial, undefined state */
+#define OPENVPN_STATE_CONNECTING 1 /* Management interface has been initialized */
+#define OPENVPN_STATE_ASSIGN_IP 2 /* Assigning IP address to virtual network interface */
+#define OPENVPN_STATE_ADD_ROUTES 3 /* Adding routes to system */
+#define OPENVPN_STATE_CONNECTED 4 /* Initialization sequence completed */
+#define OPENVPN_STATE_RECONNECTING 5 /* Restart */
+#define OPENVPN_STATE_EXITING 6 /* Exit */
+
+/* client-only states */
+#define OPENVPN_STATE_WAIT 7 /* Waiting for initial response from server */
+#define OPENVPN_STATE_AUTH 8 /* Authenticating with server */
+#define OPENVPN_STATE_GET_CONFIG 9 /* Downloading configuration from server */
+#define OPENVPN_STATE_RESOLVE 10 /* DNS lookup */
+#define OPENVPN_STATE_TCP_CONNECT 11 /* Connecting to TCP server */
+
+#define OPENVPN_STATE_CLIENT_BASE 7 /* Base index of client-only states */
+
+void management_set_state (struct management *man,
+ const int state,
+ const char *detail,
+ const in_addr_t tun_local_ip,
+ const in_addr_t tun_remote_ip);
+
+/*
+ * The management object keeps track of OpenVPN --echo
+ * parameters.
+ */
+void management_echo (struct management *man, const char *string, const bool pull);
+
+/*
+ * OpenVPN calls here to indicate a password failure
+ */
+
+void management_auth_failure (struct management *man, const char *type, const char *reason);
+
+/*
+ * These functions drive the bytecount in/out counters.
+ */
+
+void man_bytecount_output_client (struct management *man);
+
+static inline void
+man_bytecount_possible_output_client (struct management *man)
+{
+ if (man->connection.bytecount_update_seconds > 0
+ && now >= man->connection.bytecount_last_update
+ + man->connection.bytecount_update_seconds)
+ man_bytecount_output_client (man);
+}
+
+static inline void
+management_bytes_out_client (struct management *man, const int size)
+{
+ man->persist.bytes_out += size;
+ man_bytecount_possible_output_client (man);
+}
+
+static inline void
+management_bytes_in_client (struct management *man, const int size)
+{
+ man->persist.bytes_in += size;
+ man_bytecount_possible_output_client (man);
+}
+
+static inline void
+management_bytes_out (struct management *man, const int size)
+{
+ if (!(man->persist.callback.flags & MCF_SERVER))
+ management_bytes_out_client (man, size);
+}
+
+static inline void
+management_bytes_in (struct management *man, const int size)
+{
+ if (!(man->persist.callback.flags & MCF_SERVER))
+ management_bytes_in_client (man, size);
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static inline void
+management_bytes_server (struct management *man,
+ const counter_type *bytes_in_total,
+ const counter_type *bytes_out_total,
+ struct man_def_auth_context *mdac)
+{
+ void man_bytecount_output_server (struct management *man,
+ const counter_type *bytes_in_total,
+ const counter_type *bytes_out_total,
+ struct man_def_auth_context *mdac);
+
+ if (man->connection.bytecount_update_seconds > 0
+ && now >= mdac->bytecount_last_update + man->connection.bytecount_update_seconds
+ && (mdac->flags & (DAF_CONNECTION_ESTABLISHED|DAF_CONNECTION_CLOSED)) == DAF_CONNECTION_ESTABLISHED)
+ man_bytecount_output_server (man, bytes_in_total, bytes_out_total, mdac);
+}
+
+#endif /* MANAGEMENT_DEF_AUTH */
+
+#if HTTP_PROXY_FALLBACK
+
+void management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint);
+
+#endif /* HTTP_PROXY_FALLBACK */
+
+#endif
+#endif
diff --git a/management/management-notes.txt b/management/management-notes.txt
new file mode 100644
index 0000000..1f4cbd0
--- /dev/null
+++ b/management/management-notes.txt
@@ -0,0 +1,838 @@
+OpenVPN Management Interface Notes
+----------------------------------
+
+The OpenVPN Management interface allows OpenVPN to
+be administratively controlled from an external program via
+a TCP or unix domain socket.
+
+The interface has been specifically designed for developers
+who would like to programmatically or remotely control
+an OpenVPN daemon, and can be used when OpenVPN is running
+as a client or server.
+
+The management interface is implemented using a client/server TCP
+connection or unix domain socket where OpenVPN will listen on a
+provided IP address and port for incoming management client connections.
+
+The management protocol is currently cleartext without an explicit
+security layer. For this reason, it is recommended that the
+management interface either listen on a unix domain socket,
+localhost (127.0.0.1), or on the local VPN address. It's possible
+to remotely connect to the management interface over the VPN itself,
+though some capabilities will be limited in this mode, such as the
+ability to provide private key passwords.
+
+The management interface is enabled in the OpenVPN
+configuration file using the following directive:
+
+--management
+
+See the man page for documentation on this and related
+directives.
+
+Once OpenVPN has started with the management layer enabled,
+you can telnet to the management port (make sure to use
+a telnet client which understands "raw" mode).
+
+Once connected to the management port, you can use
+the "help" command to list all commands.
+
+COMMAND -- bytecount
+--------------------
+
+The bytecount command is used to request real-time notification
+of OpenVPN bandwidth usage.
+
+Command syntax:
+
+ bytecount n (where n > 0) -- set up automatic notification of
+ bandwidth usage once every n seconds
+ bytecount 0 -- turn off bytecount notifications
+
+If OpenVPN is running as a client, the bytecount notification
+will look like this:
+
+ >BYTECOUNT:{BYTES_IN},{BYTES_OUT}
+
+BYTES_IN is the number of bytes that have been received from
+the server and BYTES_OUT is the number of bytes that have been
+sent to the server.
+
+If OpenVPN is running as a server, the bytecount notification
+will look like this:
+
+ >BYTECOUNT_CLI:{CID},{BYTES_IN},{BYTES_OUT}
+
+CID is the Client ID, BYTES_IN is the number of bytes that have
+been received from the client and BYTES_OUT is the number of
+bytes that have been sent to the client.
+
+Note that when the bytecount command is used on the server, every
+connected client will report its bandwidth numbers once every n
+seconds.
+
+When the client disconnects, the final bandwidth numbers will be
+placed in the 'bytes_received' and 'bytes_sent' environmental variables
+as included in the >CLIENT:DISCONNECT notification.
+
+COMMAND -- echo
+---------------
+
+The echo capability is used to allow GUI-specific
+parameters to be either embedded in the OpenVPN config file
+or pushed to an OpenVPN client from a server.
+
+Command examples:
+
+ echo on -- turn on real-time notification of echo messages
+ echo all -- print the current echo history list
+ echo off -- turn off real-time notification of echo messages
+ echo on all -- atomically enable real-time notification,
+ plus show any messages in history buffer
+
+For example, suppose you are developing a OpenVPN GUI and
+you want to give the OpenVPN server the ability to ask
+the GUI to forget any saved passwords.
+
+In the OpenVPN server config file, add:
+
+ push "echo forget-passwords"
+
+When the OpenVPN client receives its pulled list of directives
+from the server, the "echo forget-passwords" directive will
+be in the list, and it will cause the management interface
+to save the "forget-passwords" string in its list of echo
+parameters.
+
+The management client can use "echo all" to output the full
+list of echoed parameters, "echo on" to turn on real-time
+notification of echoed parameters via the ">ECHO:" prefix,
+or "echo off" to turn off real-time notification.
+
+When the GUI connects to the OpenVPN management socket, it
+can issue an "echo all" command, which would produce output
+like this:
+
+ 1101519562,forget-passwords
+ END
+
+Essentially the echo command allowed us to pass parameters from
+the OpenVPN server to the OpenVPN client, and then to the
+management client (such as a GUI). The large integer is the
+unix date/time when the echo parameter was received.
+
+If the management client had issued the command "echo on",
+it would have enabled real-time notifications of echo
+parameters. In this case, our "forget-passwords" message
+would be output like this:
+
+ >ECHO:1101519562,forget-passwords
+
+Like the log command, the echo command can atomically show
+history while simultaneously activating real-time updates:
+
+ echo on all
+
+The size of the echo buffer is currently hardcoded to 100
+messages.
+
+COMMAND -- exit, quit
+---------------------
+
+Close the managment session, and resume listening on the
+management port for connections from other clients. Currently,
+the OpenVPN daemon can at most support a single management client
+any one time.
+
+COMMAND -- help
+---------------
+
+Print a summary of commands.
+
+COMMAND -- hold
+---------------
+
+The hold command can be used to manipulate the hold flag,
+or release OpenVPN from a hold state.
+
+If the hold flag is set on initial startup or
+restart, OpenVPN will hibernate prior to initializing
+the tunnel until the management interface receives
+a "hold release" command.
+
+The --management-hold directive of OpenVPN can be used
+to start OpenVPN with the hold flag set.
+
+The hold flag setting is persistent and will not
+be reset by restarts.
+
+OpenVPN will indicate that it is in a hold state by
+sending a real-time notification to the management
+client:
+
+ >HOLD:Waiting for hold release
+
+Command examples:
+
+ hold -- show current hold flag, 0=off, 1=on.
+ hold on -- turn on hold flag so that future restarts
+ will hold.
+ hold off -- turn off hold flag so that future restarts will
+ not hold.
+ hold release -- leave hold state and start OpenVPN, but
+ do not alter the current hold flag setting.
+
+COMMAND -- kill
+---------------
+
+In server mode, kill a particlar client instance.
+
+Command examples:
+
+ kill Test-Client -- kill the client instance having a
+ common name of "Test-Client".
+ kill 1.2.3.4:4000 -- kill the client instance having a
+ source address and port of 1.2.3.4:4000
+
+Use the "status" command to see which clients are connected.
+
+COMMAND -- log
+--------------
+
+Show the OpenVPN log file. Only the most recent n lines
+of the log file are cached by the management interface, where
+n is controlled by the OpenVPN --management-log-cache directive.
+
+Command examples:
+
+ log on -- Enable real-time output of log messages.
+ log all -- Show currently cached log file history.
+ log on all -- Atomically show all currently cached log file
+ history then enable real-time notification of
+ new log file messages.
+ log off -- Turn off real-time notification of log messages.
+ log 20 -- Show the most recent 20 lines of log file history.
+
+Real-time notification format:
+
+Real-time log messages begin with the ">LOG:" prefix followed
+by the following comma-separated fields:
+
+ (a) unix integer date/time,
+ (b) zero or more message flags in a single string:
+ I -- informational
+ F -- fatal error
+ N -- non-fatal error
+ W -- warning
+ D -- debug, and
+ (c) message text.
+
+COMMAND -- mute
+---------------
+
+Change the OpenVPN --mute parameter. The mute parameter is
+used to silence repeating messages of the same message
+category.
+
+Command examples:
+
+ mute 40 -- change the mute parameter to 40
+ mute -- show the current mute setting
+
+COMMAND -- net
+--------------
+
+(Windows Only) Produce output equivalent to the OpenVPN
+--show-net directive. The output includes OpenVPN's view
+of the system network adapter list and routing table based
+on information returned by the Windows IP helper API.
+
+COMMAND -- pid
+--------------
+
+Shows the process ID of the current OpenVPN process.
+
+COMMAND -- password and username
+--------------------------------
+
+ The password command is used to pass passwords to OpenVPN.
+
+ If OpenVPN is run with the --management-query-passwords
+ directive, it will query the management interface for RSA
+ private key passwords and the --auth-user-pass
+ username/password.
+
+ When OpenVPN needs a password from the management interface,
+ it will produce a real-time ">PASSWORD:" message.
+
+ Example 1:
+
+ >PASSWORD:Need 'Private Key' password
+
+ OpenVPN is indicating that it needs a password of type
+ "Private Key".
+
+ The management client should respond to this query as follows:
+
+ password "Private Key" foo
+
+ Example 2:
+
+ >PASSWORD:Need 'Auth' username/password
+
+ OpenVPN needs a --auth-user-pass password. The management
+ client should respond:
+
+ username "Auth" foo
+ password "Auth" bar
+
+ The username/password itself can be in quotes, and special
+ characters such as double quote or backslash must be escaped,
+ for example,
+
+ password "Private Key" "foo\"bar"
+
+ The escaping rules are the same as for the config file.
+ See the "Command Parsing" section below for more info.
+
+ The PASSWORD real-time message type can also be used to
+ indicate password or other types of authentication failure:
+
+ Example 3: The private key password is incorrect and OpenVPN
+ is exiting:
+
+ >PASSWORD:Verification Failed: 'Private Key'
+
+ Example 4: The --auth-user-pass username/password failed,
+ and OpenVPN is exiting:
+
+ >PASSWORD:Verification Failed: 'Auth'
+
+ Example 5: The --auth-user-pass username/password failed,
+ and the server provided a custom client-reason-text string
+ using the client-deny server-side management interface command.
+
+ >PASSWORD:Verification Failed: 'custom server-generated string'
+
+COMMAND -- forget-passwords
+---------------------------
+
+The forget-passwords command will cause the daemon to forget passwords
+entered during the session.
+
+Command example:
+
+ forget-passwords -- forget passwords entered so far.
+
+COMMAND -- signal
+-----------------
+
+The signal command will send a signal to the OpenVPN daemon.
+The signal can be one of SIGHUP, SIGTERM, SIGUSR1, or SIGUSR2.
+
+Command example:
+
+ signal SIGUSR1 -- send a SIGUSR1 signal to daemon
+
+COMMAND -- state
+----------------
+
+Show the current OpenVPN state, show state history, or
+enable real-time notification of state changes.
+
+These are the OpenVPN states:
+
+CONNECTING -- OpenVPN's initial state.
+WAIT -- (Client only) Waiting for initial response
+ from server.
+AUTH -- (Client only) Authenticating with server.
+GET_CONFIG -- (Client only) Downloading configuration options
+ from server.
+ASSIGN_IP -- Assigning IP address to virtual network
+ interface.
+ADD_ROUTES -- Adding routes to system.
+CONNECTED -- Initialization Sequence Completed.
+RECONNECTING -- A restart has occurred.
+EXITING -- A graceful exit is in progress.
+
+Command examples:
+
+ state -- Print current OpenVPN state.
+ state on -- Enable real-time notification of state changes.
+ state off -- Disable real-time notification of state changes.
+ state all -- Print current state history.
+ state 3 -- Print the 3 most recent state transitions.
+ state on all -- Atomically show state history while at the
+ same time enable real-time state notification
+ of future state transitions.
+
+The output format consists of 4 comma-separated parameters:
+ (a) the integer unix date/time,
+ (b) the state name,
+ (c) optional descriptive string (used mostly on RECONNECTING
+ and EXITING to show the reason for the disconnect),
+ (d) optional TUN/TAP local IP address (shown for ASSIGN_IP
+ and CONNECTED), and
+ (e) optional address of remote server (OpenVPN 2.1 or higher).
+
+Real-time state notifications will have a ">STATE:" prefix
+prepended to them.
+
+COMMAND -- status
+-----------------
+
+Show current daemon status information, in the same format as
+that produced by the OpenVPN --status directive.
+
+Command examples:
+
+status -- Show status information using the default status
+ format version.
+
+status 3 -- Show status information using the format of
+ --status-version 3.
+
+COMMAND -- username
+-------------------
+
+See the "password" section above.
+
+COMMAND -- verb
+---------------
+
+Change the OpenVPN --verb parameter. The verb parameter
+controls the output verbosity, and may range from 0 (no output)
+to 15 (maximum output). See the OpenVPN man page for additional
+info on verbosity levels.
+
+Command examples:
+
+ verb 4 -- change the verb parameter to 4
+ mute -- show the current verb setting
+
+COMMAND -- version
+------------------
+
+Show the current OpenVPN and Management Interface versions.
+
+
+COMMAND -- auth-retry
+---------------------
+
+Set the --auth-retry setting to control how OpenVPN responds to
+username/password authentication errors. See the manual page
+for more info.
+
+Command examples:
+
+ auth-retry interact -- Don't exit when bad username/passwords are entered.
+ Query for new input and retry.
+
+COMMAND -- needok (OpenVPN 2.1 or higher)
+------------------------------------------
+
+Confirm a ">NEED-OK" real-time notification, normally used by
+OpenVPN to block while waiting for a specific user action.
+
+Example:
+
+ OpenVPN needs the user to insert a cryptographic token,
+ so it sends a real-time notification:
+
+ >NEED-OK:Need 'token-insertion-request' confirmation MSG:Please insert your cryptographic token
+
+ The management client, if it is a GUI, can flash a dialog
+ box containing the text after the "MSG:" marker to the user.
+ When the user acknowledges the dialog box,
+ the management client can issue this command:
+
+ needok token-insertion-request ok
+ or
+ needok token-insertion-request cancel
+
+COMMAND -- needstr (OpenVPN 2.1 or higher)
+-------------------------------------------
+
+Confirm a ">NEED-STR" real-time notification, normally used by
+OpenVPN to block while waiting for a specific user input.
+
+Example:
+
+ OpenVPN needs the user to specify some input, so it sends a
+ real-time notification:
+
+ >NEED-STR:Need 'name' input MSG:Please specify your name
+
+ The management client, if it is a GUI, can flash a dialog
+ box containing the text after the "MSG:" marker to the user.
+ When the user acknowledges the dialog box,
+ the management client can issue this command:
+
+ needstr name "John"
+
+COMMAND -- pkcs11-id-count (OpenVPN 2.1 or higher)
+---------------------------------------------------
+
+Retrieve available number of certificates.
+
+Example:
+
+ pkcs11-id-count
+ >PKCS11ID-COUNT:5
+
+COMMAND -- pkcs11-id-get (OpenVPN 2.1 or higher)
+-------------------------------------------------
+
+Retrieve certificate by index, the ID string should be provided
+as PKCS#11 identity, the blob is BASE64 encoded certificate.
+
+Example:
+
+ pkcs11-id-get 1
+ PKCS11ID-ENTRY:'1', ID:'<snip>', BLOB:'<snip>'
+
+COMMAND -- client-auth (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request and specify
+"client-connect" configuration directives in a subsequent text block.
+
+The OpenVPN server should have been started with the
+--management-client-auth directive so that it will ask the management
+interface to approve client connections.
+
+
+ client-auth {CID} {KID}
+ line_1
+ line_2
+ ...
+ line_n
+ END
+
+CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
+notification for more info.
+
+line_1 to line_n -- client-connect configuration text block, as would be
+returned by a --client-connect script. The text block may be null, with
+"END" immediately following the "client-auth" line (using a null text
+block is equivalent to using the client-auth-nt command).
+
+A client-connect configuration text block contains OpenVPN directives
+that will be applied to the client instance object representing a newly
+connected client.
+
+COMMAND -- client-auth-nt (OpenVPN 2.1 or higher)
+--------------------------------------------------
+
+Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request without specifying
+client-connect configuration text.
+
+The OpenVPN server should have been started with the
+--management-client-auth directive so that it will ask the management
+interface to approve client connections.
+
+ client-auth-nt {CID} {KID}
+
+CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
+notification for more info.
+
+COMMAND -- client-deny (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Deny a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request.
+
+ client-deny {CID} {KID} "reason-text" ["client-reason-text"]
+
+CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
+notification for more info.
+
+reason-text: a human-readable message explaining why the authentication
+request was denied. This message will be output to the OpenVPN log
+file or syslog.
+
+client-reason-text: a message that will be sent to the client as
+part of the AUTH_FAILED message.
+
+Note that client-deny denies a specific Key ID (pertaining to a
+TLS renegotiation). A client-deny command issued in response to
+an initial TLS key negotiation (notified by ">CLIENT:CONNECT") will
+terminate the client session after returning "AUTH-FAILED" to the client.
+On the other hand, a client-deny command issued in response to
+a TLS renegotiation (">CLIENT:REAUTH") will invalidate the renegotiated
+key, however the TLS session associated with the currently active
+key will continue to live for up to --tran-window seconds before
+expiration.
+
+To immediately kill a client session, use "client-kill".
+
+COMMAND -- client-kill (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Immediately kill a client instance by CID.
+
+ client-kill {CID}
+
+CID -- client ID. See documentation for ">CLIENT:" notification for more
+info.
+
+COMMAND -- client-pf (OpenVPN 2.1 or higher)
+---------------------------------------------
+
+Push a packet filter file to a specific client.
+
+The OpenVPN server should have been started with the
+--management-client-pf directive so that it will require that
+VPN tunnel packets sent or received by client instances must
+conform to that client's packet filter configuration.
+
+ client-pf {CID}
+ line_1
+ line_2
+ ...
+ line_n
+ END
+
+CID -- client ID. See documentation for ">CLIENT:" notification for
+more info.
+
+line_1 to line_n -- the packet filter configuration file for this
+client.
+
+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 | "unknown"
+
+ 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 AND the --client-to-client
+ directive must have been specified in the OpenVPN server config.
+
+ SUBNETS refers to IP addresses or IP address subnets which this
+ client instance may connect to ('+') or is excluded ('-') from
+ connecting to, and applies to IPv4 and ARP packets. The special
+ "unknown" tag refers to packets of unknown type, i.e. a packet that
+ is not IPv4 or ARP.
+
+ DROP or ACCEPT defines default policy when there is no explicit match
+ for a common-name or subnet. The [END] tag must exist.
+
+ Notes:
+
+ * The SUBNETS section currently only supports IPv4 addresses and
+ subnets.
+
+ * A given client or subnet rule applies to both incoming and
+ outgoing packets.
+
+ * The CLIENTS list is order-invariant. Because the list is stored
+ as a hash-table, the order of the list does not affect its function.
+
+ * The SUBNETS table is scanned sequentially, and the first item to
+ match is chosen. Therefore the SUBNETS table is NOT order-invariant.
+
+ * No client-to-client communication is allowed unless the
+ --client-to-client configuration directive is enabled AND
+ the CLIENTS list of BOTH clients allows the communication.
+
+Example packet filter spec, as transmitted to the management interface:
+
+ client-pf 42
+ [CLIENTS ACCEPT]
+ -accounting
+ -enigma
+ [SUBNETS DROP]
+ -10.46.79.9
+ +10.0.0.0/8
+ [END]
+ END
+
+The above example sets the packet filter policy for the client
+identified by CID=42. This client may connect to all other clients
+except those having a common name of "accounting" or "enigma".
+The client may only interact with external IP addresses in the
+10.0.0.0/8 subnet, however access to 10.46.79.9 is specifically
+excluded.
+
+Another example packet filter spec, as transmitted to the
+management interface:
+
+ client-pf 99
+ [CLIENTS DENY]
+ +public
+ [SUBNETS ACCEPT]
+ +10.10.0.1
+ -10.0.0.0/8
+ -unknown
+ [END]
+ END
+
+The above example sets the packet filter policy for the client
+identified by CID=99. This client may not connect to any other
+clients except those having a common name of "public". It may
+interact with any external IP address except those in the
+10.0.0.0/8 netblock. However interaction with one address in
+the 10.0.0.0/8 netblock is allowed: 10.10.0.1. Also, the client
+may not interact with external IP addresses using an "unknown"
+protocol (i.e. one that is not IPv4 or ARP).
+
+OUTPUT FORMAT
+-------------
+
+(1) Command success/failure indicated by "SUCCESS: [text]" or
+ "ERROR: [text]".
+
+(2) For commands which print multiple lines of output,
+ the last line will be "END".
+
+(3) Real-time messages will be in the form ">[source]:[text]",
+ where source is "CLIENT", "ECHO", "FATAL", "HOLD", "INFO", "LOG",
+ "NEED-OK", "PASSWORD", or "STATE".
+
+REAL-TIME MESSAGE FORMAT
+------------------------
+
+The OpenVPN management interface produces two kinds of
+output: (a) output from a command, or (b) asynchronous,
+real-time output which can be generated at any time.
+
+Real-time messages start with a '>' character in the first
+column and are immediately followed by a type keyword
+indicating the type of real-time message. The following
+types are currently defined:
+
+BYTECOUNT -- Real-time bandwidth usage notification, as enabled
+ by "bytecount" command when OpenVPN is running as
+ a client.
+
+BYTECOUNT_CLI -- Real-time bandwidth usage notification per-client,
+ as enabled by "bytecount" command when OpenVPN is
+ running as a server.
+
+CLIENT -- Notification of client connections and disconnections
+ on an OpenVPN server. Enabled when OpenVPN is started
+ with the --management-client-auth option. CLIENT
+ notifications may be multi-line. See "The CLIENT
+ notification" section below for detailed info.
+
+ECHO -- Echo messages as controlled by the "echo" command.
+
+FATAL -- A fatal error which is output to the log file just
+ prior to OpenVPN exiting.
+
+HOLD -- Used to indicate that OpenVPN is in a holding state
+ and will not start until it receives a
+ "hold release" command.
+
+INFO -- Informational messages such as the welcome message.
+
+LOG -- Log message output as controlled by the "log" command.
+
+NEED-OK -- OpenVPN needs the end user to do something, such as
+ insert a cryptographic token. The "needok" command can
+ be used to tell OpenVPN to continue.
+
+NEED-STR -- OpenVPN needs information from end, such as
+ a certificate to use. The "needstr" command can
+ be used to tell OpenVPN to continue.
+
+PASSWORD -- Used to tell the management client that OpenVPN
+ needs a password, also to indicate password
+ verification failure.
+
+STATE -- Shows the current OpenVPN state, as controlled
+ by the "state" command.
+
+The CLIENT notification
+-----------------------
+
+The ">CLIENT:" notification is enabled by the --management-client-auth
+OpenVPN configuration directive that gives the management interface client
+the responsibility to authenticate OpenVPN clients after their client
+certificate has been verified. CLIENT notifications may be multi-line, and
+the sequentiality of a given CLIENT notification, its associated environmental
+variables, and the terminating ">CLIENT:ENV,END" line are guaranteed to be
+atomic.
+
+CLIENT notification types:
+
+(1) Notify new client connection ("CONNECT") or existing client TLS session
+ renegotiation ("REAUTH"). Information about the client is provided
+ by a list of environmental variables which are documented in the OpenVPN
+ man page. The environmental variables passed are equivalent to those
+ that would be passed to an --auth-user-pass-verify script.
+
+ >CLIENT:CONNECT|REAUTH,{CID},{KID}
+ >CLIENT:ENV,name1=val1
+ >CLIENT:ENV,name2=val2
+ >CLIENT:ENV,...
+ >CLIENT:ENV,END
+
+(2) Notify successful client authentication and session initiation.
+ Called after CONNECT.
+
+ >CLIENT:ESTABLISHED,{CID}
+ >CLIENT:ENV,name1=val1
+ >CLIENT:ENV,name2=val2
+ >CLIENT:ENV,...
+ >CLIENT:ENV,END
+
+(3) Notify existing client disconnection. The environmental variables passed
+ are equivalent to those that would be passed to a --client-disconnect
+ script.
+
+ >CLIENT:DISCONNECT,{CID}
+ >CLIENT:ENV,name1=val1
+ >CLIENT:ENV,name2=val2
+ >CLIENT:ENV,...
+ >CLIENT:ENV,END
+
+(4) Notify that a particular virtual address or subnet
+ is now associated with a specific client.
+
+ >CLIENT:ADDRESS,{CID},{ADDR},{PRI}
+
+Variables:
+
+CID -- Client ID, numerical ID for each connecting client, sequence = 0,1,2,...
+KID -- Key ID, numerical ID for the key associated with a given client TLS session,
+ sequence = 0,1,2,...
+PRI -- Primary (1) or Secondary (0) VPN address/subnet. All clients have at least
+ one primary IP address. Secondary address/subnets are associated with
+ client-specific "iroute" directives.
+ADDR -- IPv4 address/subnet in the form 1.2.3.4 or 1.2.3.0/255.255.255.0
+
+In the unlikely scenario of an extremely long-running OpenVPN server,
+CID and KID should be assumed to recycle to 0 after (2^32)-1, however this
+recycling behavior is guaranteed to be collision-free.
+
+Command Parsing
+---------------
+
+The management interface uses the same command line lexical analyzer
+as is used by the OpenVPN config file parser.
+
+Whitespace is a parameter separator.
+
+Double quotation or single quotation characters ("", '') can be used
+to enclose parameters containing whitespace.
+
+Backslash-based shell escaping is performed, using the following
+mappings, when not in single quotations:
+
+\\ Maps to a single backslash character (\).
+\" Pass a literal doublequote character ("), don't
+ interpret it as enclosing a parameter.
+\[SPACE] Pass a literal space or tab character, don't
+ interpret it as a parameter delimiter.
diff --git a/mbuf.c b/mbuf.c
new file mode 100644
index 0000000..0f36d3c
--- /dev/null
+++ b/mbuf.c
@@ -0,0 +1,169 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP
+
+#include "buffer.h"
+#include "error.h"
+#include "misc.h"
+#include "mbuf.h"
+
+#include "memdbg.h"
+
+struct mbuf_set *
+mbuf_init (unsigned int size)
+{
+ struct mbuf_set *ret;
+ ALLOC_OBJ_CLEAR (ret, struct mbuf_set);
+ ret->capacity = adjust_power_of_2 (size);
+ ALLOC_ARRAY (ret->array, struct mbuf_item, ret->capacity);
+ return ret;
+}
+
+void
+mbuf_free (struct mbuf_set *ms)
+{
+ if (ms)
+ {
+ int i;
+ for (i = 0; i < (int) ms->len; ++i)
+ {
+ struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)];
+ mbuf_free_buf (item->buffer);
+ }
+ free (ms->array);
+ free (ms);
+ }
+}
+
+struct mbuf_buffer *
+mbuf_alloc_buf (const struct buffer *buf)
+{
+ struct mbuf_buffer *ret;
+ ALLOC_OBJ (ret, struct mbuf_buffer);
+ ret->buf = clone_buf (buf);
+ ret->refcount = 1;
+ ret->flags = 0;
+ return ret;
+}
+
+void
+mbuf_free_buf (struct mbuf_buffer *mb)
+{
+ if (mb)
+ {
+ if (--mb->refcount <= 0)
+ {
+ free_buf (&mb->buf);
+ free (mb);
+ }
+ }
+}
+
+void
+mbuf_add_item (struct mbuf_set *ms, const struct mbuf_item *item)
+{
+ ASSERT (ms);
+ if (ms->len == ms->capacity)
+ {
+ struct mbuf_item rm;
+ ASSERT (mbuf_extract_item (ms, &rm));
+ mbuf_free_buf (rm.buffer);
+ msg (D_MULTI_DROPPED, "MBUF: mbuf packet dropped");
+ }
+
+ ASSERT (ms->len < ms->capacity);
+
+ ms->array[MBUF_INDEX(ms->head, ms->len, ms->capacity)] = *item;
+ if (++ms->len > ms->max_queued)
+ ms->max_queued = ms->len;
+ ++item->buffer->refcount;
+}
+
+bool
+mbuf_extract_item (struct mbuf_set *ms, struct mbuf_item *item)
+{
+ bool ret = false;
+ if (ms)
+ {
+ while (ms->len)
+ {
+ *item = ms->array[ms->head];
+ ms->head = MBUF_INDEX(ms->head, 1, ms->capacity);
+ --ms->len;
+ if (item->instance) /* ignore dereferenced instances */
+ {
+ ret = true;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+struct multi_instance *
+mbuf_peek_dowork (struct mbuf_set *ms)
+{
+ struct multi_instance *ret = NULL;
+ if (ms)
+ {
+ int i;
+ for (i = 0; i < (int) ms->len; ++i)
+ {
+ struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)];
+ if (item->instance)
+ {
+ ret = item->instance;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+void
+mbuf_dereference_instance (struct mbuf_set *ms, struct multi_instance *mi)
+{
+ if (ms)
+ {
+ int i;
+ for (i = 0; i < (int) ms->len; ++i)
+ {
+ struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)];
+ if (item->instance == mi)
+ {
+ mbuf_free_buf (item->buffer);
+ item->buffer = NULL;
+ item->instance = NULL;
+ msg (D_MBUF, "MBUF: dereferenced queued packet");
+ }
+ }
+ }
+}
+
+#else
+static void dummy(void) {}
+#endif /* P2MP */
diff --git a/mbuf.h b/mbuf.h
new file mode 100644
index 0000000..a0de679
--- /dev/null
+++ b/mbuf.h
@@ -0,0 +1,109 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MBUF_H
+#define MBUF_H
+
+/*
+ * Handle both multicast and broadcast functions.
+ */
+
+#if P2MP
+
+/* define this to enable special test mode */
+/*#define MBUF_TEST*/
+
+#include "basic.h"
+#include "buffer.h"
+
+struct multi_instance;
+
+#define MBUF_INDEX(head, offset, size) (((head) + (offset)) & ((size)-1))
+
+struct mbuf_buffer
+{
+ struct buffer buf;
+ int refcount;
+
+# define MF_UNICAST (1<<0)
+ unsigned int flags;
+};
+
+struct mbuf_item
+{
+ struct mbuf_buffer *buffer;
+ struct multi_instance *instance;
+};
+
+struct mbuf_set
+{
+ unsigned int head;
+ unsigned int len;
+ unsigned int capacity;
+ unsigned int max_queued;
+ struct mbuf_item *array;
+};
+
+struct mbuf_set *mbuf_init (unsigned int size);
+void mbuf_free (struct mbuf_set *ms);
+
+struct mbuf_buffer *mbuf_alloc_buf (const struct buffer *buf);
+void mbuf_free_buf (struct mbuf_buffer *mb);
+
+void mbuf_add_item (struct mbuf_set *ms, const struct mbuf_item *item);
+
+bool mbuf_extract_item (struct mbuf_set *ms, struct mbuf_item *item);
+
+void mbuf_dereference_instance (struct mbuf_set *ms, struct multi_instance *mi);
+
+static inline bool
+mbuf_defined (const struct mbuf_set *ms)
+{
+ return ms && ms->len;
+}
+
+static inline bool
+mbuf_len (const struct mbuf_set *ms)
+{
+ return ms->len;
+}
+
+static inline int
+mbuf_maximum_queued (const struct mbuf_set *ms)
+{
+ return (int) ms->max_queued;
+}
+
+static inline struct multi_instance *
+mbuf_peek (struct mbuf_set *ms)
+{
+ struct multi_instance *mbuf_peek_dowork (struct mbuf_set *ms);
+ if (mbuf_defined (ms))
+ return mbuf_peek_dowork (ms);
+ else
+ return NULL;
+}
+
+#endif
+#endif
diff --git a/memcmp.c b/memcmp.c
new file mode 100644
index 0000000..d921aac
--- /dev/null
+++ b/memcmp.c
@@ -0,0 +1,43 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "memdbg.h"
+
+int
+memcmp (const void *s1, const void *s2, size_t n)
+{
+ unsigned const char *p1 = s1, *p2 = s2;
+ int d;
+
+ if (n)
+ while (n-- > 0)
+ {
+ d = *p1++ - *p2++;
+ if (d != 0)
+ return d;
+ }
+ return 0;
+}
diff --git a/memdbg.h b/memdbg.h
new file mode 100644
index 0000000..1f6bb67
--- /dev/null
+++ b/memdbg.h
@@ -0,0 +1,114 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MEMDBG_H
+#define MEMDBG_H
+
+/*
+ * Valgrind debugging support.
+ *
+ * Valgrind is a great tool for debugging memory issues,
+ * though it seems to generate a lot of warnings in OpenSSL
+ * about uninitialized data. To silence these warnings,
+ * I've put together a suppressions file
+ * in debug/valgrind-suppress.
+ *
+ * Also, grep for VALGRIND_MAKE_READABLE in the OpenVPN source.
+ * Because valgrind thinks that some of the data passed from
+ * OpenSSL back to OpenVPN is tainted due to being sourced
+ * from uninitialized data, we need to untaint it before use --
+ * otherwise we will get a lot of useless warnings.
+ *
+ * valgrind --tool=memcheck --error-limit=no --suppressions=debug/valgrind-suppress --gen-suppressions=yes ./openvpn ...
+ */
+
+#ifdef USE_VALGRIND
+
+#include "valgrind/memcheck.h"
+
+#define VALGRIND_MAKE_READABLE(addr, len)
+
+#else
+
+#define VALGRIND_MAKE_READABLE(addr, len)
+
+#endif
+
+#ifdef DMALLOC /* see ./configure options to enable */
+
+/*
+ * See ./configure options to enable dmalloc
+ * support for memory leak checking.
+ *
+ * The dmalloc package can be downloaded from:
+ *
+ * http://dmalloc.com/
+ *
+ * When dmalloc is installed and enabled,
+ * use this command prior to running openvpn:
+ *
+ * dmalloc -l dlog -i 100 low -p log-unknown
+ *
+ * Also, put this in your .bashrc file:
+ *
+ * function dmalloc { eval `command dmalloc -b $*`; }
+ *
+ * Or take a more low-level approach:
+ *
+ * export DMALLOC_OPTIONS="debug=0x4e48503,inter=100,log=dlog"
+ *
+ * NOTE: When building dmalloc you need to add something
+ * like this to dmalloc's settings.h -- it will allocate a static
+ * buffer to be used as the malloc arena:
+ *
+ * #define INTERNAL_MEMORY_SPACE (1024 * 1024 * 50)
+ */
+
+#include "dmalloc.h"
+
+#define openvpn_dmalloc(file, line, size) dmalloc_malloc((file), (line), (size), DMALLOC_FUNC_MALLOC, 0, 0)
+
+/*
+ * This #define will put the line number of the log
+ * file position where leaked memory was allocated instead
+ * of the source code file and line number. Make sure
+ * to increase the size of dmalloc's info tables,
+ * (MEMORY_TABLE_SIZE in settings.h)
+ * otherwise it might get overwhelmed by the large
+ * number of unique file/line combinations.
+ */
+#if 0
+#undef malloc
+#define malloc(size) openvpn_dmalloc("logfile", x_msg_line_num, (size))
+#endif
+
+#endif /* DMALLOC */
+
+/*
+ * Force buffers to be zeroed after allocation.
+ * For debugging only.
+ */
+/*#define ZERO_BUFFER_ON_ALLOC*/
+
+#endif /* MEMDBG_H */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..4067d85
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,2378 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "misc.h"
+#include "base64.h"
+#include "tun.h"
+#include "error.h"
+#include "otime.h"
+#include "plugin.h"
+#include "options.h"
+#include "manage.h"
+#include "crypto.h"
+#include "route.h"
+#include "win32.h"
+
+#include "memdbg.h"
+
+#ifdef CONFIG_FEATURE_IPROUTE
+const char *iproute_path = IPROUTE_PATH; /* GLOBAL */
+#endif
+
+/* contains an SSEC_x value defined in misc.h */
+int script_security = SSEC_BUILT_IN; /* GLOBAL */
+
+/* contains SM_x value defined in misc.h */
+int script_method = SM_EXECVE; /* GLOBAL */
+
+/* Redefine the top level directory of the filesystem
+ to restrict access to files for security */
+void
+do_chroot (const char *path)
+{
+ if (path)
+ {
+#ifdef HAVE_CHROOT
+ const char *top = "/";
+ if (chroot (path))
+ msg (M_ERR, "chroot to '%s' failed", path);
+ if (openvpn_chdir (top))
+ msg (M_ERR, "cd to '%s' failed", top);
+ msg (M_INFO, "chroot to '%s' and cd to '%s' succeeded", path, top);
+#else
+ msg (M_FATAL, "Sorry but I can't chroot to '%s' because this operating system doesn't appear to support the chroot() system call", path);
+#endif
+ }
+}
+
+/* Get/Set UID of process */
+
+bool
+get_user (const char *username, struct user_state *state)
+{
+ bool ret = false;
+ CLEAR (*state);
+ if (username)
+ {
+#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
+ state->pw = getpwnam (username);
+ if (!state->pw)
+ msg (M_ERR, "failed to find UID for user %s", username);
+ state->username = username;
+ ret = true;
+#else
+ msg (M_FATAL, "cannot get UID for user %s -- platform lacks getpwname() or setuid() system calls", username);
+#endif
+ }
+ return ret;
+}
+
+void
+set_user (const struct user_state *state)
+{
+#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
+ if (state->username && state->pw)
+ {
+ if (setuid (state->pw->pw_uid))
+ msg (M_ERR, "setuid('%s') failed", state->username);
+ msg (M_INFO, "UID set to %s", state->username);
+ }
+#endif
+}
+
+/* Get/Set GID of process */
+
+bool
+get_group (const char *groupname, struct group_state *state)
+{
+ bool ret = false;
+ CLEAR (*state);
+ if (groupname)
+ {
+#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
+ state->gr = getgrnam (groupname);
+ if (!state->gr)
+ msg (M_ERR, "failed to find GID for group %s", groupname);
+ state->groupname = groupname;
+ ret = true;
+#else
+ msg (M_FATAL, "cannot get GID for group %s -- platform lacks getgrnam() or setgid() system calls", groupname);
+#endif
+ }
+ return ret;
+}
+
+void
+set_group (const struct group_state *state)
+{
+#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
+ if (state->groupname && state->gr)
+ {
+ if (setgid (state->gr->gr_gid))
+ msg (M_ERR, "setgid('%s') failed", state->groupname);
+ msg (M_INFO, "GID set to %s", state->groupname);
+#ifdef HAVE_SETGROUPS
+ {
+ gid_t gr_list[1];
+ gr_list[0] = state->gr->gr_gid;
+ if (setgroups (1, gr_list))
+ msg (M_ERR, "setgroups('%s') failed", state->groupname);
+ }
+#endif
+ }
+#endif
+}
+
+/* Change process priority */
+void
+set_nice (int niceval)
+{
+ if (niceval)
+ {
+#ifdef HAVE_NICE
+ errno = 0;
+ if (nice (niceval) < 0 && errno != 0)
+ msg (M_WARN | M_ERRNO, "WARNING: nice %d failed: %s", niceval, strerror(errno));
+ else
+ msg (M_INFO, "nice %d succeeded", niceval);
+#else
+ msg (M_WARN, "WARNING: nice %d failed (function not implemented)", niceval);
+#endif
+ }
+}
+
+/*
+ * Pass tunnel endpoint and MTU parms to a user-supplied script.
+ * Used to execute the up/down script/plugins.
+ */
+void
+run_up_down (const char *command,
+ const struct plugin_list *plugins,
+ int plugin_type,
+ const char *arg,
+ int tun_mtu,
+ int link_mtu,
+ const char *ifconfig_local,
+ const char* ifconfig_remote,
+ const char *context,
+ const char *signal_text,
+ const char *script_type,
+ struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (signal_text)
+ setenv_str (es, "signal", signal_text);
+ setenv_str (es, "script_context", context);
+ setenv_int (es, "tun_mtu", tun_mtu);
+ setenv_int (es, "link_mtu", link_mtu);
+ setenv_str (es, "dev", arg);
+
+ if (!ifconfig_local)
+ ifconfig_local = "";
+ if (!ifconfig_remote)
+ ifconfig_remote = "";
+ if (!context)
+ context = "";
+
+ if (plugin_defined (plugins, plugin_type))
+ {
+ struct argv argv = argv_new ();
+ ASSERT (arg);
+ argv_printf (&argv,
+ "%s %d %d %s %s %s",
+ arg,
+ tun_mtu, link_mtu,
+ ifconfig_local, ifconfig_remote,
+ context);
+
+ if (plugin_call (plugins, plugin_type, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ msg (M_FATAL, "ERROR: up/down plugin call failed");
+
+ argv_reset (&argv);
+ }
+
+ if (command)
+ {
+ struct argv argv = argv_new ();
+ ASSERT (arg);
+ setenv_str (es, "script_type", script_type);
+ argv_printf (&argv,
+ "%sc %s %d %d %s %s %s",
+ command,
+ arg,
+ tun_mtu, link_mtu,
+ ifconfig_local, ifconfig_remote,
+ context);
+ argv_msg (M_INFO, &argv);
+ openvpn_run_script (&argv, es, S_FATAL, "--up/--down");
+ argv_reset (&argv);
+ }
+
+ gc_free (&gc);
+}
+
+/* Get the file we will later write our process ID to */
+void
+get_pid_file (const char* filename, struct pid_state *state)
+{
+ CLEAR (*state);
+ if (filename)
+ {
+ state->fp = fopen (filename, "w");
+ if (!state->fp)
+ msg (M_ERR, "Open error on pid file %s", filename);
+ state->filename = filename;
+ }
+}
+
+/* Write our PID to a file */
+void
+write_pid (const struct pid_state *state)
+{
+ if (state->filename && state->fp)
+ {
+ unsigned int pid = openvpn_getpid ();
+ fprintf(state->fp, "%u\n", pid);
+ if (fclose (state->fp))
+ msg (M_ERR, "Close error on pid file %s", state->filename);
+ }
+}
+
+/* Get current PID */
+unsigned int
+openvpn_getpid ()
+{
+#ifdef WIN32
+ return (unsigned int) GetCurrentProcessId ();
+#else
+#ifdef HAVE_GETPID
+ return (unsigned int) getpid ();
+#else
+ return 0;
+#endif
+#endif
+}
+
+/* Disable paging */
+void
+do_mlockall(bool print_msg)
+{
+#ifdef HAVE_MLOCKALL
+ if (mlockall (MCL_CURRENT | MCL_FUTURE))
+ msg (M_WARN | M_ERRNO, "WARNING: mlockall call failed");
+ else if (print_msg)
+ msg (M_INFO, "mlockall call succeeded");
+#else
+ msg (M_WARN, "WARNING: mlockall call failed (function not implemented)");
+#endif
+}
+
+#ifndef HAVE_DAEMON
+
+int
+daemon(int nochdir, int noclose)
+{
+#if defined(HAVE_FORK) && defined(HAVE_SETSID)
+ switch (fork())
+ {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ openvpn_chdir ("/");
+
+ if (!noclose)
+ set_std_files_to_null (false);
+#else
+ msg (M_FATAL, "Sorry but I can't become a daemon because this operating system doesn't appear to support either the daemon() or fork() system calls");
+#endif
+ return (0);
+}
+
+#endif
+
+/*
+ * Set standard file descriptors to /dev/null
+ */
+void
+set_std_files_to_null (bool stdin_only)
+{
+#if defined(HAVE_DUP) && defined(HAVE_DUP2)
+ int fd;
+ if ((fd = open ("/dev/null", O_RDWR, 0)) != -1)
+ {
+ dup2 (fd, 0);
+ if (!stdin_only)
+ {
+ dup2 (fd, 1);
+ dup2 (fd, 2);
+ }
+ if (fd > 2)
+ close (fd);
+ }
+#endif
+}
+
+/*
+ * Wrapper for chdir library function
+ */
+int
+openvpn_chdir (const char* dir)
+{
+#ifdef HAVE_CHDIR
+ return chdir (dir);
+#else
+ return -1;
+#endif
+}
+
+/*
+ * dup inetd/xinetd socket descriptor and save
+ */
+
+int inetd_socket_descriptor = SOCKET_UNDEFINED; /* GLOBAL */
+
+void
+save_inetd_socket_descriptor (void)
+{
+ inetd_socket_descriptor = INETD_SOCKET_DESCRIPTOR;
+#if defined(HAVE_DUP) && defined(HAVE_DUP2)
+ /* use handle passed by inetd/xinetd */
+ if ((inetd_socket_descriptor = dup (INETD_SOCKET_DESCRIPTOR)) < 0)
+ msg (M_ERR, "INETD_SOCKET_DESCRIPTOR dup(%d) failed", INETD_SOCKET_DESCRIPTOR);
+ set_std_files_to_null (true);
+#endif
+}
+
+/*
+ * Warn if a given file is group/others accessible.
+ */
+void
+warn_if_group_others_accessible (const char* filename)
+{
+#ifndef WIN32
+#ifdef HAVE_STAT
+#if ENABLE_INLINE_FILES
+ if (strcmp (filename, INLINE_FILE_TAG))
+#endif
+ {
+ struct stat st;
+ if (stat (filename, &st))
+ {
+ msg (M_WARN | M_ERRNO, "WARNING: cannot stat file '%s'", filename);
+ }
+ else
+ {
+ if (st.st_mode & (S_IRWXG|S_IRWXO))
+ msg (M_WARN, "WARNING: file '%s' is group or others accessible", filename);
+ }
+ }
+#endif
+#endif
+}
+
+/*
+ * convert system() return into a success/failure value
+ */
+bool
+system_ok (int stat)
+{
+#ifdef WIN32
+ return stat == 0;
+#else
+ return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0;
+#endif
+}
+
+/*
+ * did system() call execute the given command?
+ */
+bool
+system_executed (int stat)
+{
+#ifdef WIN32
+ return stat != -1;
+#else
+ return stat != -1 && WEXITSTATUS (stat) != 127;
+#endif
+}
+
+/*
+ * Print an error message based on the status code returned by system().
+ */
+const char *
+system_error_message (int stat, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+#ifdef WIN32
+ if (stat == -1)
+ buf_printf (&out, "external program did not execute -- ");
+ buf_printf (&out, "returned error code %d", stat);
+#else
+ if (stat == -1)
+ buf_printf (&out, "external program fork failed");
+ else if (!WIFEXITED (stat))
+ buf_printf (&out, "external program did not exit normally");
+ else
+ {
+ const int cmd_ret = WEXITSTATUS (stat);
+ if (!cmd_ret)
+ buf_printf (&out, "external program exited normally");
+ else if (cmd_ret == 127)
+ buf_printf (&out, "could not execute external program");
+ else
+ buf_printf (&out, "external program exited with error status: %d", cmd_ret);
+ }
+#endif
+ return (const char *)out.data;
+}
+
+/*
+ * Wrapper around openvpn_execve
+ */
+bool
+openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message)
+{
+ struct gc_arena gc = gc_new ();
+ const int stat = openvpn_execve (a, es, flags);
+ int ret = false;
+
+ if (system_ok (stat))
+ ret = true;
+ else
+ {
+ if (error_message)
+ msg (((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s",
+ error_message,
+ system_error_message (stat, &gc));
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+bool
+openvpn_execve_allowed (const unsigned int flags)
+{
+ if (flags & S_SCRIPT)
+ return script_security >= SSEC_SCRIPTS;
+ else
+ return script_security >= SSEC_BUILT_IN;
+}
+
+
+#ifndef WIN32
+/*
+ * Run execve() inside a fork(). Designed to replicate the semantics of system() but
+ * in a safer way that doesn't require the invocation of a shell or the risks
+ * assocated with formatting and parsing a command line.
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ int ret = -1;
+ static bool warn_shown = false;
+
+ if (a && a->argv[0])
+ {
+#if defined(ENABLE_EXECVE)
+ if (openvpn_execve_allowed (flags))
+ {
+ if (script_method == SM_EXECVE)
+ {
+ const char *cmd = a->argv[0];
+ char *const *argv = a->argv;
+ char *const *envp = (char *const *)make_env_array (es, true, &gc);
+ pid_t pid;
+
+ pid = fork ();
+ if (pid == (pid_t)0) /* child side */
+ {
+ execve (cmd, argv, envp);
+ exit (127);
+ }
+ else if (pid < (pid_t)0) /* fork failed */
+ ;
+ else /* parent side */
+ {
+ if (waitpid (pid, &ret, 0) != pid)
+ ret = -1;
+ }
+ }
+ else if (script_method == SM_SYSTEM)
+ {
+ ret = openvpn_system (argv_system_str (a), es, flags);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ else if (!warn_shown && (script_security < SSEC_SCRIPTS))
+ {
+ msg (M_WARN, SCRIPT_SECURITY_WARNING);
+ warn_shown = true;
+ }
+#else
+ msg (M_WARN, "openvpn_execve: execve function not available");
+#endif
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: called with empty argv");
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+#endif
+
+/*
+ * Wrapper around the system() call.
+ */
+int
+openvpn_system (const char *command, const struct env_set *es, unsigned int flags)
+{
+#ifdef HAVE_SYSTEM
+ int ret;
+
+ perf_push (PERF_SCRIPT);
+
+ /*
+ * add env_set to environment.
+ */
+ if (flags & S_SCRIPT)
+ env_set_add_to_environment (es);
+
+
+ /* debugging */
+ dmsg (D_SCRIPT, "SYSTEM[%u] '%s'", flags, command);
+ if (flags & S_SCRIPT)
+ env_set_print (D_SCRIPT, es);
+
+ /*
+ * execute the command
+ */
+ ret = system (command);
+
+ /* debugging */
+ dmsg (D_SCRIPT, "SYSTEM return=%u", ret);
+
+ /*
+ * remove env_set from environment
+ */
+ if (flags & S_SCRIPT)
+ env_set_remove_from_environment (es);
+
+ perf_pop ();
+ return ret;
+
+#else
+ msg (M_FATAL, "Sorry but I can't execute the shell command '%s' because this operating system doesn't appear to support the system() call", command);
+ return -1; /* NOTREACHED */
+#endif
+}
+
+/*
+ * Initialize random number seed. random() is only used
+ * when "weak" random numbers are acceptable.
+ * OpenSSL routines are always used when cryptographically
+ * strong random numbers are required.
+ */
+
+void
+init_random_seed(void)
+{
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+
+ if (!gettimeofday (&tv, NULL))
+ {
+ const unsigned int seed = (unsigned int) tv.tv_sec ^ tv.tv_usec;
+ srandom (seed);
+ }
+#else /* HAVE_GETTIMEOFDAY */
+ const time_t current = time (NULL);
+ srandom ((unsigned int)current);
+#endif /* HAVE_GETTIMEOFDAY */
+}
+
+/* thread-safe strerror */
+
+const char *
+strerror_ts (int errnum, struct gc_arena *gc)
+{
+#ifdef HAVE_STRERROR
+ struct buffer out = alloc_buf_gc (256, gc);
+
+ buf_printf (&out, "%s", openvpn_strerror (errnum, gc));
+ return BSTR (&out);
+#else
+ return "[error string unavailable]";
+#endif
+}
+
+/*
+ * Set environmental variable (int or string).
+ *
+ * On Posix, we use putenv for portability,
+ * and put up with its painful semantics
+ * that require all the support code below.
+ */
+
+/* General-purpose environmental variable set functions */
+
+static char *
+construct_name_value (const char *name, const char *value, struct gc_arena *gc)
+{
+ struct buffer out;
+
+ ASSERT (name);
+ if (!value)
+ value = "";
+ out = alloc_buf_gc (strlen (name) + strlen (value) + 2, gc);
+ buf_printf (&out, "%s=%s", name, value);
+ return BSTR (&out);
+}
+
+bool
+deconstruct_name_value (const char *str, const char **name, const char **value, struct gc_arena *gc)
+{
+ char *cp;
+
+ ASSERT (str);
+ ASSERT (name && value);
+
+ *name = cp = string_alloc (str, gc);
+ *value = NULL;
+
+ while ((*cp))
+ {
+ if (*cp == '=' && !*value)
+ {
+ *cp = 0;
+ *value = cp + 1;
+ }
+ ++cp;
+ }
+ return *name && *value;
+}
+
+static bool
+env_string_equal (const char *s1, const char *s2)
+{
+ int c1, c2;
+ ASSERT (s1);
+ ASSERT (s2);
+
+ while (true)
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 == '=')
+ c1 = 0;
+ if (c2 == '=')
+ c2 = 0;
+ if (!c1 && !c2)
+ return true;
+ if (c1 != c2)
+ break;
+ }
+ return false;
+}
+
+static bool
+remove_env_item (const char *str, const bool do_free, struct env_item **list)
+{
+ struct env_item *current, *prev;
+
+ ASSERT (str);
+ ASSERT (list);
+
+ for (current = *list, prev = NULL; current != NULL; current = current->next)
+ {
+ if (env_string_equal (current->string, str))
+ {
+ if (prev)
+ prev->next = current->next;
+ else
+ *list = current->next;
+ if (do_free)
+ {
+ memset (current->string, 0, strlen (current->string));
+ free (current->string);
+ free (current);
+ }
+ return true;
+ }
+ prev = current;
+ }
+ return false;
+}
+
+static void
+add_env_item (char *str, const bool do_alloc, struct env_item **list, struct gc_arena *gc)
+{
+ struct env_item *item;
+
+ ASSERT (str);
+ ASSERT (list);
+
+ ALLOC_OBJ_GC (item, struct env_item, gc);
+ item->string = do_alloc ? string_alloc (str, gc): str;
+ item->next = *list;
+ *list = item;
+}
+
+/* struct env_set functions */
+
+static bool
+env_set_del_nolock (struct env_set *es, const char *str)
+{
+ return remove_env_item (str, es->gc == NULL, &es->list);
+}
+
+static void
+env_set_add_nolock (struct env_set *es, const char *str)
+{
+ remove_env_item (str, es->gc == NULL, &es->list);
+ add_env_item ((char *)str, true, &es->list, es->gc);
+}
+
+struct env_set *
+env_set_create (struct gc_arena *gc)
+{
+ struct env_set *es;
+ ALLOC_OBJ_CLEAR_GC (es, struct env_set, gc);
+ es->list = NULL;
+ es->gc = gc;
+ return es;
+}
+
+void
+env_set_destroy (struct env_set *es)
+{
+ if (es && es->gc == NULL)
+ {
+ struct env_item *e = es->list;
+ while (e)
+ {
+ struct env_item *next = e->next;
+ free (e->string);
+ free (e);
+ e = next;
+ }
+ free (es);
+ }
+}
+
+bool
+env_set_del (struct env_set *es, const char *str)
+{
+ bool ret;
+ ASSERT (es);
+ ASSERT (str);
+ ret = env_set_del_nolock (es, str);
+ return ret;
+}
+
+void
+env_set_add (struct env_set *es, const char *str)
+{
+ ASSERT (es);
+ ASSERT (str);
+ env_set_add_nolock (es, str);
+}
+
+void
+env_set_print (int msglevel, const struct env_set *es)
+{
+ if (check_debug_level (msglevel))
+ {
+ const struct env_item *e;
+ int i;
+
+ if (es)
+ {
+ e = es->list;
+ i = 0;
+
+ while (e)
+ {
+ if (env_safe_to_print (e->string))
+ msg (msglevel, "ENV [%d] '%s'", i, e->string);
+ ++i;
+ e = e->next;
+ }
+ }
+ }
+}
+
+void
+env_set_inherit (struct env_set *es, const struct env_set *src)
+{
+ const struct env_item *e;
+
+ ASSERT (es);
+
+ if (src)
+ {
+ e = src->list;
+ while (e)
+ {
+ env_set_add_nolock (es, e->string);
+ e = e->next;
+ }
+ }
+}
+
+void
+env_set_add_to_environment (const struct env_set *es)
+{
+ if (es)
+ {
+ struct gc_arena gc = gc_new ();
+ const struct env_item *e;
+
+ e = es->list;
+
+ while (e)
+ {
+ const char *name;
+ const char *value;
+
+ if (deconstruct_name_value (e->string, &name, &value, &gc))
+ setenv_str (NULL, name, value);
+
+ e = e->next;
+ }
+ gc_free (&gc);
+ }
+}
+
+void
+env_set_remove_from_environment (const struct env_set *es)
+{
+ if (es)
+ {
+ struct gc_arena gc = gc_new ();
+ const struct env_item *e;
+
+ e = es->list;
+
+ while (e)
+ {
+ const char *name;
+ const char *value;
+
+ if (deconstruct_name_value (e->string, &name, &value, &gc))
+ setenv_del (NULL, name);
+
+ e = e->next;
+ }
+ gc_free (&gc);
+ }
+}
+
+#ifdef HAVE_PUTENV
+
+/* companion functions to putenv */
+
+static struct env_item *global_env = NULL; /* GLOBAL */
+
+static void
+manage_env (char *str)
+{
+ remove_env_item (str, true, &global_env);
+ add_env_item (str, false, &global_env, NULL);
+}
+
+#endif
+
+/* add/modify/delete environmental strings */
+
+void
+setenv_counter (struct env_set *es, const char *name, counter_type value)
+{
+ char buf[64];
+ openvpn_snprintf (buf, sizeof(buf), counter_format, value);
+ setenv_str (es, name, buf);
+}
+
+void
+setenv_int (struct env_set *es, const char *name, int value)
+{
+ char buf[64];
+ openvpn_snprintf (buf, sizeof(buf), "%d", value);
+ setenv_str (es, name, buf);
+}
+
+void
+setenv_unsigned (struct env_set *es, const char *name, unsigned int value)
+{
+ char buf[64];
+ openvpn_snprintf (buf, sizeof(buf), "%u", value);
+ setenv_str (es, name, buf);
+}
+
+void
+setenv_str (struct env_set *es, const char *name, const char *value)
+{
+ setenv_str_ex (es, name, value, CC_NAME, 0, 0, CC_PRINT, 0, 0);
+}
+
+void
+setenv_str_safe (struct env_set *es, const char *name, const char *value)
+{
+ uint8_t b[64];
+ struct buffer buf;
+ buf_set_write (&buf, b, sizeof (b));
+ if (buf_printf (&buf, "OPENVPN_%s", name))
+ setenv_str (es, BSTR(&buf), value);
+ else
+ msg (M_WARN, "setenv_str_safe: name overflow");
+}
+
+void
+setenv_del (struct env_set *es, const char *name)
+{
+ ASSERT (name);
+ setenv_str (es, name, NULL);
+}
+
+void
+setenv_str_ex (struct env_set *es,
+ const char *name,
+ const char *value,
+ const unsigned int name_include,
+ const unsigned int name_exclude,
+ const char name_replace,
+ const unsigned int value_include,
+ const unsigned int value_exclude,
+ const char value_replace)
+{
+ struct gc_arena gc = gc_new ();
+ const char *name_tmp;
+ const char *val_tmp = NULL;
+
+ ASSERT (name && strlen (name) > 1);
+
+ name_tmp = string_mod_const (name, name_include, name_exclude, name_replace, &gc);
+
+ if (value)
+ val_tmp = string_mod_const (value, value_include, value_exclude, value_replace, &gc);
+
+ if (es)
+ {
+ if (val_tmp)
+ {
+ const char *str = construct_name_value (name_tmp, val_tmp, &gc);
+ env_set_add (es, str);
+ /*msg (M_INFO, "SETENV_ES '%s'", str);*/
+ }
+ else
+ env_set_del (es, name_tmp);
+ }
+ else
+ {
+#if defined(WIN32)
+ {
+ /*msg (M_INFO, "SetEnvironmentVariable '%s' '%s'", name_tmp, val_tmp ? val_tmp : "NULL");*/
+ if (!SetEnvironmentVariable (name_tmp, val_tmp))
+ msg (M_WARN | M_ERRNO, "SetEnvironmentVariable failed, name='%s', value='%s'",
+ name_tmp,
+ val_tmp ? val_tmp : "NULL");
+ }
+#elif defined(HAVE_PUTENV)
+ {
+ char *str = construct_name_value (name_tmp, val_tmp, NULL);
+ int status;
+
+ status = putenv (str);
+ /*msg (M_INFO, "PUTENV '%s'", str);*/
+ if (!status)
+ manage_env (str);
+ if (status)
+ msg (M_WARN | M_ERRNO, "putenv('%s') failed", str);
+ }
+#endif
+ }
+
+ gc_free (&gc);
+}
+
+/*
+ * Setenv functions that append an integer index to the name
+ */
+static const char *
+setenv_format_indexed_name (const char *name, const int i, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (strlen (name) + 16, gc);
+ if (i >= 0)
+ buf_printf (&out, "%s_%d", name, i);
+ else
+ buf_printf (&out, "%s", name);
+ return BSTR (&out);
+}
+
+void
+setenv_int_i (struct env_set *es, const char *name, const int value, const int i)
+{
+ struct gc_arena gc = gc_new ();
+ const char *name_str = setenv_format_indexed_name (name, i, &gc);
+ setenv_int (es, name_str, value);
+ gc_free (&gc);
+}
+
+void
+setenv_str_i (struct env_set *es, const char *name, const char *value, const int i)
+{
+ struct gc_arena gc = gc_new ();
+ const char *name_str = setenv_format_indexed_name (name, i, &gc);
+ setenv_str (es, name_str, value);
+ gc_free (&gc);
+}
+
+/*
+ * taken from busybox networking/ifupdown.c
+ */
+unsigned int
+count_bits(unsigned int a)
+{
+ unsigned int result;
+ result = (a & 0x55) + ((a >> 1) & 0x55);
+ result = (result & 0x33) + ((result >> 2) & 0x33);
+ return((result & 0x0F) + ((result >> 4) & 0x0F));
+}
+
+int
+count_netmask_bits(const char *dotted_quad)
+{
+ unsigned int result, a, b, c, d;
+ /* Found a netmask... Check if it is dotted quad */
+ if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+ return -1;
+ result = count_bits(a);
+ result += count_bits(b);
+ result += count_bits(c);
+ result += count_bits(d);
+ return ((int)result);
+}
+
+/*
+ * Go to sleep for n milliseconds.
+ */
+void
+sleep_milliseconds (unsigned int n)
+{
+#ifdef WIN32
+ Sleep (n);
+#else
+ struct timeval tv;
+ tv.tv_sec = n / 1000;
+ tv.tv_usec = (n % 1000) * 1000;
+ select (0, NULL, NULL, NULL, &tv);
+#endif
+}
+
+/*
+ * Go to sleep indefinitely.
+ */
+void
+sleep_until_signal (void)
+{
+#ifdef WIN32
+ ASSERT (0);
+#else
+ select (0, NULL, NULL, NULL, NULL);
+#endif
+}
+
+/* return true if filename can be opened for read */
+bool
+test_file (const char *filename)
+{
+ bool ret = false;
+ if (filename)
+ {
+ FILE *fp = fopen (filename, "r");
+ if (fp)
+ {
+ fclose (fp);
+ ret = true;
+ }
+ }
+
+ dmsg (D_TEST_FILE, "TEST FILE '%s' [%d]",
+ filename ? filename : "UNDEF",
+ ret);
+
+ return ret;
+}
+
+#ifdef USE_CRYPTO
+
+/* create a temporary filename in directory */
+const char *
+create_temp_file (const char *directory, const char *prefix, struct gc_arena *gc)
+{
+ static unsigned int counter;
+ struct buffer fname = alloc_buf_gc (256, gc);
+ int fd;
+ const char *retfname = NULL;
+ unsigned int attempts = 0;
+
+ do
+ {
+ uint8_t rndbytes[16];
+ const char *rndstr;
+
+ ++attempts;
+ ++counter;
+
+ prng_bytes (rndbytes, sizeof rndbytes);
+ rndstr = format_hex_ex (rndbytes, sizeof rndbytes, 40, 0, NULL, gc);
+ buf_printf (&fname, PACKAGE "_%s_%s.tmp", prefix, rndstr);
+
+ retfname = gen_path (directory, BSTR (&fname), gc);
+ if (!retfname)
+ {
+ msg (M_FATAL, "Failed to create temporary filename and path");
+ return NULL;
+ }
+
+ /* Atomically create the file. Errors out if the file already
+ exists. */
+ fd = open (retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
+ if (fd != -1)
+ {
+ close (fd);
+ return retfname;
+ }
+ else if (fd == -1 && errno != EEXIST)
+ {
+ /* Something else went wrong, no need to retry. */
+ struct gc_arena gcerr = gc_new ();
+ msg (M_FATAL, "Could not create temporary file '%s': %s",
+ retfname, strerror_ts (errno, &gcerr));
+ gc_free (&gcerr);
+ return NULL;
+ }
+ }
+ while (attempts < 6);
+
+ msg (M_FATAL, "Failed to create temporary file after %i attempts", attempts);
+ return NULL;
+}
+
+/*
+ * Add a random string to first DNS label of hostname to prevent DNS caching.
+ * For example, foo.bar.gov would be modified to <random-chars>.foo.bar.gov.
+ * Of course, this requires explicit support in the DNS server.
+ */
+const char *
+hostname_randomize(const char *hostname, struct gc_arena *gc)
+{
+# define n_rnd_bytes 6
+
+ char *hst = string_alloc(hostname, gc);
+ char *dot = strchr(hst, '.');
+
+ if (dot)
+ {
+ uint8_t rnd_bytes[n_rnd_bytes];
+ const char *rnd_str;
+ struct buffer hname = alloc_buf_gc (strlen(hostname)+sizeof(rnd_bytes)*2+4, gc);
+
+ *dot++ = '\0';
+ prng_bytes (rnd_bytes, sizeof (rnd_bytes));
+ rnd_str = format_hex_ex (rnd_bytes, sizeof (rnd_bytes), 40, 0, NULL, gc);
+ buf_printf(&hname, "%s-0x%s.%s", hst, rnd_str, dot);
+ return BSTR(&hname);
+ }
+ else
+ return hostname;
+# undef n_rnd_bytes
+}
+
+#else
+
+const char *
+hostname_randomize(const char *hostname, struct gc_arena *gc)
+{
+ msg (M_WARN, "WARNING: hostname randomization disabled when crypto support is not compiled");
+ return hostname;
+}
+
+#endif
+
+/*
+ * Put a directory and filename together.
+ */
+const char *
+gen_path (const char *directory, const char *filename, struct gc_arena *gc)
+{
+ const char *safe_filename = string_mod_const (filename, CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT, 0, '_', gc);
+
+ if (safe_filename
+ && strcmp (safe_filename, ".")
+ && strcmp (safe_filename, "..")
+#ifdef WIN32
+ && win_safe_filename (safe_filename)
+#endif
+ )
+ {
+ const size_t outsize = strlen(safe_filename) + (directory ? strlen (directory) : 0) + 16;
+ struct buffer out = alloc_buf_gc (outsize, gc);
+ char dirsep[2];
+
+ dirsep[0] = OS_SPECIFIC_DIRSEP;
+ dirsep[1] = '\0';
+
+ if (directory)
+ buf_printf (&out, "%s%s", directory, dirsep);
+ buf_printf (&out, "%s", safe_filename);
+
+ return BSTR (&out);
+ }
+ else
+ return NULL;
+}
+
+/* delete a file, return true if succeeded */
+bool
+delete_file (const char *filename)
+{
+#if defined(WIN32)
+ return (DeleteFile (filename) != 0);
+#elif defined(HAVE_UNLINK)
+ return (unlink (filename) == 0);
+#else
+ return false;
+#endif
+}
+
+bool
+absolute_pathname (const char *pathname)
+{
+ if (pathname)
+ {
+ const int c = pathname[0];
+#ifdef WIN32
+ return c == '\\' || (isalpha(c) && pathname[1] == ':' && pathname[2] == '\\');
+#else
+ return c == '/';
+#endif
+ }
+ else
+ return false;
+}
+
+#ifdef HAVE_GETPASS
+
+static FILE *
+open_tty (const bool write)
+{
+ FILE *ret;
+ ret = fopen ("/dev/tty", write ? "w" : "r");
+ if (!ret)
+ ret = write ? stderr : stdin;
+ return ret;
+}
+
+static void
+close_tty (FILE *fp)
+{
+ if (fp != stderr && fp != stdin)
+ fclose (fp);
+}
+
+#endif
+
+/*
+ * Get input from console
+ */
+bool
+get_console_input (const char *prompt, const bool echo, char *input, const int capacity)
+{
+ bool ret = false;
+ ASSERT (prompt);
+ ASSERT (input);
+ ASSERT (capacity > 0);
+ input[0] = '\0';
+
+#if defined(WIN32)
+ return get_console_input_win32 (prompt, echo, input, capacity);
+#elif defined(HAVE_GETPASS)
+ if (echo)
+ {
+ FILE *fp;
+
+ fp = open_tty (true);
+ fprintf (fp, "%s", prompt);
+ fflush (fp);
+ close_tty (fp);
+
+ fp = open_tty (false);
+ if (fgets (input, capacity, fp) != NULL)
+ {
+ chomp (input);
+ ret = true;
+ }
+ close_tty (fp);
+ }
+ else
+ {
+ char *gp = getpass (prompt);
+ if (gp)
+ {
+ strncpynt (input, gp, capacity);
+ memset (gp, 0, strlen (gp));
+ ret = true;
+ }
+ }
+#else
+ msg (M_FATAL, "Sorry, but I can't get console input on this OS");
+#endif
+ return ret;
+}
+
+/*
+ * Get and store a username/password
+ */
+
+bool
+get_user_pass_cr (struct user_pass *up,
+ const char *auth_file,
+ const char *prefix,
+ const unsigned int flags,
+ const char *auth_challenge)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (!up->defined)
+ {
+ const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin"));
+
+ if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
+ msg (M_WARN, "Note: previous '%s' credentials failed", prefix);
+
+#ifdef ENABLE_MANAGEMENT
+ /*
+ * Get username/password from management interface?
+ */
+ if (management
+ && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
+ && management_query_user_pass_enabled (management))
+ {
+ if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
+ management_auth_failure (management, prefix, "previous auth credentials failed");
+
+ if (!management_query_user_pass (management, up, prefix, flags))
+ {
+ if ((flags & GET_USER_PASS_NOFATAL) != 0)
+ return false;
+ else
+ msg (M_FATAL, "ERROR: could not read %s username/password/ok/string from management interface", prefix);
+ }
+ }
+ else
+#endif
+ /*
+ * Get NEED_OK confirmation from the console
+ */
+ if (flags & GET_USER_PASS_NEED_OK)
+ {
+ struct buffer user_prompt = alloc_buf_gc (128, &gc);
+
+ buf_printf (&user_prompt, "NEED-OK|%s|%s:", prefix, up->username);
+
+ if (!get_console_input (BSTR (&user_prompt), true, up->password, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix);
+
+ if (!strlen (up->password))
+ strcpy (up->password, "ok");
+ }
+
+ /*
+ * Get username/password from standard input?
+ */
+ else if (from_stdin)
+ {
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge)
+ {
+ struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc);
+ if (ac)
+ {
+ char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc);
+ struct buffer packed_resp;
+
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
+ msg (M_INFO, "CHALLENGE: %s", ac->challenge_text);
+ if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read challenge response from stdin");
+ strncpynt (up->username, ac->user, USER_PASS_LEN);
+ buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response);
+ }
+ else
+ {
+ msg (M_FATAL, "ERROR: received malformed challenge request from server");
+ }
+ }
+ else
+#endif
+ {
+ struct buffer user_prompt = alloc_buf_gc (128, &gc);
+ struct buffer pass_prompt = alloc_buf_gc (128, &gc);
+
+ buf_printf (&user_prompt, "Enter %s Username:", prefix);
+ buf_printf (&pass_prompt, "Enter %s Password:", prefix);
+
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
+ {
+ if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
+ if (strlen (up->username) == 0)
+ msg (M_FATAL, "ERROR: %s username is empty", prefix);
+ }
+
+ if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
+ }
+ }
+ else
+ {
+ /*
+ * Get username/password from a file.
+ */
+ FILE *fp;
+
+#ifndef ENABLE_PASSWORD_SAVE
+ /*
+ * Unless ENABLE_PASSWORD_SAVE is defined, don't allow sensitive passwords
+ * to be read from a file.
+ */
+ if (flags & GET_USER_PASS_SENSITIVE)
+ msg (M_FATAL, "Sorry, '%s' password cannot be read from a file", prefix);
+#endif
+
+ warn_if_group_others_accessible (auth_file);
+
+ fp = fopen (auth_file, "r");
+ if (!fp)
+ msg (M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file);
+
+ if (flags & GET_USER_PASS_PASSWORD_ONLY)
+ {
+ if (fgets (up->password, USER_PASS_LEN, fp) == NULL)
+ msg (M_FATAL, "Error reading password from %s authfile: %s",
+ prefix,
+ auth_file);
+ }
+ else
+ {
+ if (fgets (up->username, USER_PASS_LEN, fp) == NULL
+ || fgets (up->password, USER_PASS_LEN, fp) == NULL)
+ msg (M_FATAL, "Error reading username and password (must be on two consecutive lines) from %s authfile: %s",
+ prefix,
+ auth_file);
+ }
+
+ fclose (fp);
+
+ chomp (up->username);
+ chomp (up->password);
+
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen (up->username) == 0)
+ msg (M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file);
+ }
+
+ string_mod (up->username, CC_PRINT, CC_CRLF, 0);
+ string_mod (up->password, CC_PRINT, CC_CRLF, 0);
+
+ up->defined = true;
+ }
+
+#if 0
+ msg (M_INFO, "GET_USER_PASS %s u='%s' p='%s'", prefix, up->username, up->password);
+#endif
+
+ gc_free (&gc);
+
+ return true;
+}
+
+#ifdef ENABLE_CLIENT_CR
+
+/*
+ * Parse a challenge message returned along with AUTH_FAILED.
+ * The message is formatted as such:
+ *
+ * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
+ *
+ * flags: a series of optional, comma-separated flags:
+ * E : echo the response when the user types it
+ * R : a response is required
+ *
+ * state_id: an opaque string that should be returned to the server
+ * along with the response.
+ *
+ * username_base64 : the username formatted as base64
+ *
+ * challenge_text : the challenge text to be shown to the user
+ *
+ * Example challenge:
+ *
+ * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
+ *
+ * After showing the challenge_text and getting a response from the user
+ * (if R flag is specified), the client should submit the following
+ * auth creds back to the OpenVPN server:
+ *
+ * Username: [username decoded from username_base64]
+ * Password: CRV1::<state_id>::<response_text>
+ *
+ * Where state_id is taken from the challenge request and response_text
+ * is what the user entered in response to the challenge_text.
+ * If the R flag is not present, response_text may be the empty
+ * string.
+ *
+ * Example response (suppose the user enters "8675309" for the token PIN):
+ *
+ * Username: cr1 ("Y3Ix" base64 decoded)
+ * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+ */
+struct auth_challenge_info *
+get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
+{
+ if (auth_challenge)
+ {
+ struct auth_challenge_info *ac;
+ const int len = strlen (auth_challenge);
+ char *work = (char *) gc_malloc (len+1, false, gc);
+ char *cp;
+
+ struct buffer b;
+ buf_set_read (&b, (const uint8_t *)auth_challenge, len);
+
+ ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc);
+
+ /* parse prefix */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ if (strcmp(work, "CRV1"))
+ return NULL;
+
+ /* parse flags */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ for (cp = work; *cp != '\0'; ++cp)
+ {
+ const char c = *cp;
+ if (c == 'E')
+ ac->flags |= CR_ECHO;
+ else if (c == 'R')
+ ac->flags |= CR_RESPONSE;
+ }
+
+ /* parse state ID */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ ac->state_id = string_alloc(work, gc);
+
+ /* parse user name */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ ac->user = (char *) gc_malloc (strlen(work)+1, true, gc);
+ base64_decode(work, (void*)ac->user);
+
+ /* parse challenge text */
+ ac->challenge_text = string_alloc(BSTR(&b), gc);
+
+ return ac;
+ }
+ else
+ return NULL;
+}
+
+#endif
+
+#if AUTO_USERID
+
+static const char *
+get_platform_prefix (void)
+{
+#if defined(TARGET_LINUX)
+ return "L";
+#elif defined(TARGET_SOLARIS)
+ return "S";
+#elif defined(TARGET_OPENBSD)
+ return "O";
+#elif defined(TARGET_DARWIN)
+ return "M";
+#elif defined(TARGET_NETBSD)
+ return "N";
+#elif defined(TARGET_FREEBSD)
+ return "F";
+#elif defined(WIN32)
+ return "W";
+#else
+ return "X";
+#endif
+}
+
+void
+get_user_pass_auto_userid (struct user_pass *up, const char *tag)
+{
+ struct gc_arena gc = gc_new ();
+ MD5_CTX ctx;
+ struct buffer buf;
+ uint8_t macaddr[6];
+ static uint8_t digest [MD5_DIGEST_LENGTH];
+ static const uint8_t hashprefix[] = "AUTO_USERID_DIGEST";
+
+ CLEAR (*up);
+ buf_set_write (&buf, (uint8_t*)up->username, USER_PASS_LEN);
+ buf_printf (&buf, "%s", get_platform_prefix ());
+ if (get_default_gateway_mac_addr (macaddr))
+ {
+ dmsg (D_AUTO_USERID, "GUPAU: macaddr=%s", format_hex_ex (macaddr, sizeof (macaddr), 0, 1, ":", &gc));
+ MD5_Init (&ctx);
+ MD5_Update (&ctx, hashprefix, sizeof (hashprefix) - 1);
+ MD5_Update (&ctx, macaddr, sizeof (macaddr));
+ MD5_Final (digest, &ctx);
+ buf_printf (&buf, "%s", format_hex_ex (digest, sizeof (digest), 0, 256, " ", &gc));
+ }
+ else
+ {
+ buf_printf (&buf, "UNKNOWN");
+ }
+ if (tag && strcmp (tag, "stdin"))
+ buf_printf (&buf, "-%s", tag);
+ up->defined = true;
+ gc_free (&gc);
+
+ dmsg (D_AUTO_USERID, "GUPAU: AUTO_USERID: '%s'", up->username);
+}
+
+#endif
+
+void
+purge_user_pass (struct user_pass *up, const bool force)
+{
+ const bool nocache = up->nocache;
+ static bool warn_shown = false;
+ if (nocache || force)
+ {
+ CLEAR (*up);
+ up->nocache = nocache;
+ }
+ else if (!warn_shown)
+ {
+ msg (M_WARN, "WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this");
+ warn_shown = true;
+ }
+}
+
+/*
+ * Process string received by untrusted peer before
+ * printing to console or log file.
+ *
+ * Assumes that string has been null terminated.
+ */
+const char *
+safe_print (const char *str, struct gc_arena *gc)
+{
+ return string_mod_const (str, CC_PRINT, CC_CRLF, '.', gc);
+}
+
+static bool
+is_password_env_var (const char *str)
+{
+ return (strncmp (str, "password", 8) == 0);
+}
+
+bool
+env_allowed (const char *str)
+{
+ return (script_security >= SSEC_PW_ENV || !is_password_env_var (str));
+}
+
+bool
+env_safe_to_print (const char *str)
+{
+#ifndef UNSAFE_DEBUG
+ if (is_password_env_var (str))
+ return false;
+#endif
+ return true;
+}
+
+/* Make arrays of strings */
+
+const char **
+make_env_array (const struct env_set *es,
+ const bool check_allowed,
+ struct gc_arena *gc)
+{
+ char **ret = NULL;
+ struct env_item *e = NULL;
+ int i = 0, n = 0;
+
+ /* figure length of es */
+ if (es)
+ {
+ for (e = es->list; e != NULL; e = e->next)
+ ++n;
+ }
+
+ /* alloc return array */
+ ALLOC_ARRAY_CLEAR_GC (ret, char *, n+1, gc);
+
+ /* fill return array */
+ if (es)
+ {
+ i = 0;
+ for (e = es->list; e != NULL; e = e->next)
+ {
+ if (!check_allowed || env_allowed (e->string))
+ {
+ ASSERT (i < n);
+ ret[i++] = e->string;
+ }
+ }
+ }
+
+ ret[i] = NULL;
+ return (const char **)ret;
+}
+
+const char **
+make_arg_array (const char *first, const char *parms, struct gc_arena *gc)
+{
+ char **ret = NULL;
+ int base = 0;
+ const int max_parms = MAX_PARMS + 2;
+ int n = 0;
+
+ /* alloc return array */
+ ALLOC_ARRAY_CLEAR_GC (ret, char *, max_parms, gc);
+
+ /* process first parameter, if provided */
+ if (first)
+ {
+ ret[base++] = string_alloc (first, gc);
+ }
+
+ if (parms)
+ {
+ n = parse_line (parms, &ret[base], max_parms - base - 1, "make_arg_array", 0, M_WARN, gc);
+ ASSERT (n >= 0 && n + base + 1 <= max_parms);
+ }
+ ret[base + n] = NULL;
+
+ return (const char **)ret;
+}
+
+#if ENABLE_INLINE_FILES
+static const char **
+make_inline_array (const char *str, struct gc_arena *gc)
+{
+ char line[OPTION_LINE_SIZE];
+ struct buffer buf;
+ int len = 0;
+ char **ret = NULL;
+ int i = 0;
+
+ buf_set_read (&buf, (const uint8_t *) str, strlen (str));
+ while (buf_parse (&buf, '\n', line, sizeof (line)))
+ ++len;
+
+ /* alloc return array */
+ ALLOC_ARRAY_CLEAR_GC (ret, char *, len + 1, gc);
+
+ buf_set_read (&buf, (const uint8_t *) str, strlen(str));
+ while (buf_parse (&buf, '\n', line, sizeof (line)))
+ {
+ chomp (line);
+ ASSERT (i < len);
+ ret[i] = string_alloc (skip_leading_whitespace (line), gc);
+ ++i;
+ }
+ ASSERT (i <= len);
+ ret[i] = NULL;
+ return (const char **)ret;
+}
+#endif
+
+static const char **
+make_arg_copy (char **p, struct gc_arena *gc)
+{
+ char **ret = NULL;
+ const int len = string_array_len ((const char **)p);
+ const int max_parms = len + 1;
+ int i;
+
+ /* alloc return array */
+ ALLOC_ARRAY_CLEAR_GC (ret, char *, max_parms, gc);
+
+ for (i = 0; i < len; ++i)
+ ret[i] = p[i];
+
+ return (const char **)ret;
+}
+
+const char **
+make_extended_arg_array (char **p, struct gc_arena *gc)
+{
+ const int argc = string_array_len ((const char **)p);
+#if ENABLE_INLINE_FILES
+ if (!strcmp (p[0], INLINE_FILE_TAG) && argc == 2)
+ return make_inline_array (p[1], gc);
+ else
+#endif
+ if (argc == 0)
+ return make_arg_array (NULL, NULL, gc);
+ else if (argc == 1)
+ return make_arg_array (p[0], NULL, gc);
+ else if (argc == 2)
+ return make_arg_array (p[0], p[1], gc);
+ else
+ return make_arg_copy (p, gc);
+}
+
+void
+openvpn_sleep (const int n)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_event_loop_n_seconds (management, n);
+ return;
+ }
+#endif
+ sleep (n);
+}
+
+/*
+ * Return the next largest power of 2
+ * or u if u is a power of 2.
+ */
+size_t
+adjust_power_of_2 (size_t u)
+{
+ size_t ret = 1;
+
+ while (ret < u)
+ {
+ ret <<= 1;
+ ASSERT (ret > 0);
+ }
+
+ return ret;
+}
+
+/*
+ * 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.
+ */
+
+void
+argv_init (struct argv *a)
+{
+ a->capacity = 0;
+ a->argc = 0;
+ a->argv = NULL;
+ a->system_str = 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);
+ free (a->system_str);
+ 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 void
+argv_system_str_append (struct argv *a, const char *str, const bool enquote)
+{
+ if (str)
+ {
+ char *newstr;
+
+ /* compute length of new system_str */
+ size_t l = strlen (str) + 1; /* space for new string plus trailing '\0' */
+ if (a->system_str)
+ l += strlen (a->system_str) + 1; /* space for existing string + space (" ") separator */
+ if (enquote)
+ l += 2; /* space for two quotes */
+
+ /* build new system_str */
+ newstr = (char *) malloc (l);
+ newstr[0] = '\0';
+ check_malloc_return (newstr);
+ if (a->system_str)
+ {
+ strcpy (newstr, a->system_str);
+ strcat (newstr, " ");
+ }
+ if (enquote)
+ strcat (newstr, "\"");
+ strcat (newstr, str);
+ if (enquote)
+ strcat (newstr, "\"");
+ free (a->system_str);
+ a->system_str = newstr;
+ }
+}
+
+static char *
+argv_extract_cmd_name (const char *path)
+{
+ if (path)
+ {
+ const char *bn = openvpn_basename (path);
+ if (bn)
+ {
+ char *ret = string_alloc (bn, NULL);
+ char *dot = strrchr (ret, '.');
+ if (dot)
+ *dot = '\0';
+ if (ret[0] != '\0')
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+const char *
+argv_system_str (const struct argv *a)
+{
+ return a->system_str;
+}
+
+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));
+ r.system_str = string_alloc (a->system_str, NULL);
+ }
+ return r;
+}
+
+struct argv
+argv_insert_head (const struct argv *a, const char *head)
+{
+ struct argv r;
+ char *s;
+
+ r = argv_clone (a, 1);
+ r.argv[0] = string_alloc (head, NULL);
+ s = r.system_str;
+ r.system_str = string_alloc (head, NULL);
+ if (s)
+ {
+ argv_system_str_append (&r, s, false);
+ free (s);
+ }
+ return r;
+}
+
+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);
+}
+
+void
+argv_printf (struct argv *a, const char *format, ...)
+{
+ va_list arglist;
+ va_start (arglist, format);
+ argv_printf_arglist (a, format, 0, 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, APA_CAT, arglist);
+ va_end (arglist);
+}
+
+void
+argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist)
+{
+ struct gc_arena gc = gc_new ();
+ char *term;
+ const char *f = format;
+
+ if (!(flags & APA_CAT))
+ argv_reset (a);
+ 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));
+ argv_system_str_append (a, s, true);
+ }
+ else if (!strcmp (term, "%sc"))
+ {
+ char *s = va_arg (arglist, char *);
+ if (s)
+ {
+ int nparms;
+ char *parms[MAX_PARMS+1];
+ int i;
+
+ nparms = parse_line (s, parms, MAX_PARMS, "SCRIPT-ARGV", 0, D_ARGV_PARSE_CMD, &gc);
+ if (nparms)
+ {
+ for (i = 0; i < nparms; ++i)
+ argv_append (a, string_alloc (parms[i], NULL));
+ }
+ else
+ argv_append (a, string_alloc (s, NULL));
+
+ argv_system_str_append (a, s, false);
+ }
+ else
+ {
+ argv_append (a, string_alloc ("", NULL));
+ argv_system_str_append (a, "echo", false);
+ }
+ }
+ 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));
+ argv_system_str_append (a, numstr, false);
+ }
+ 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));
+ argv_system_str_append (a, numstr, false);
+ }
+ 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);
+ argv_system_str_append (a, combined, false);
+ }
+ }
+ else if (!strcmp (term, "%s%sc"))
+ {
+ char *s1 = va_arg (arglist, char *);
+ char *s2 = va_arg (arglist, char *);
+ char *combined;
+ char *cmd_name;
+
+ 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);
+
+ cmd_name = argv_extract_cmd_name (combined);
+ if (cmd_name)
+ {
+ argv_system_str_append (a, cmd_name, false);
+ free (cmd_name);
+ }
+ }
+ else
+ ASSERT (0);
+ free (term);
+ }
+ else
+ {
+ argv_append (a, term);
+ argv_system_str_append (a, term, false);
+ }
+ }
+ gc_free (&gc);
+}
+
+#ifdef ARGV_TEST
+void
+argv_test (void)
+{
+ struct gc_arena gc = gc_new ();
+ const char *s;
+
+ struct argv a;
+
+ argv_init (&a);
+ argv_printf (&a, "%sc foo bar %s", "c:\\\\src\\\\test\\\\jyargs.exe", "foo bar");
+ argv_msg_prefix (M_INFO, &a, "ARGV");
+ msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
+ //openvpn_execve_check (&a, NULL, 0, "command failed");
+
+ argv_printf (&a, "%sc %s %s", "c:\\\\src\\\\test files\\\\batargs.bat", "foo", "bar");
+ argv_msg_prefix (M_INFO, &a, "ARGV");
+ msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
+ //openvpn_execve_check (&a, NULL, 0, "command failed");
+
+ argv_printf (&a, "%s%sc foo bar %s %s/%d %d %u", "/foo", "/bar.exe", "one two", "1.2.3.4", 24, -69, 96);
+ argv_msg_prefix (M_INFO, &a, "ARGV");
+ msg (M_INFO, "ARGV-S: %s", argv_system_str(&a));
+ //openvpn_execve_check (&a, NULL, 0, "command failed");
+
+ argv_printf (&a, "this is a %s test of int %d unsigned %u", "FOO", -69, 42);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("PF: %s\n", s);
+ printf ("PF-S: %s\n", argv_system_str(&a));
+
+ {
+ struct argv b = argv_insert_head (&a, "MARK");
+ s = argv_str (&b, &gc, PA_BRACKET);
+ printf ("PF: %s\n", s);
+ printf ("PF-S: %s\n", argv_system_str(&b));
+ argv_reset (&b);
+ }
+
+ argv_printf (&a, "%sc foo bar %d", "\"multi term\" command following \\\"spaces", 99);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("PF: %s\n", s);
+ printf ("PF-S: %s\n", argv_system_str(&a));
+ argv_reset (&a);
+
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("PF: %s\n", s);
+ printf ("PF-S: %s\n", argv_system_str(&a));
+ argv_reset (&a);
+
+ argv_printf (&a, "foo bar %d", 99);
+ argv_printf_cat (&a, "bar %d foo %sc", 42, "nonesuch");
+ argv_printf_cat (&a, "cool %s %d u %s/%d end", "frood", 4, "hello", 7);
+ s = argv_str (&a, &gc, PA_BRACKET);
+ printf ("PF: %s\n", s);
+ printf ("PF-S: %s\n", argv_system_str(&a));
+ argv_reset (&a);
+
+#if 0
+ {
+ char line[512];
+ while (fgets (line, sizeof(line), stdin) != NULL)
+ {
+ char *term;
+ const char *f = line;
+ int i = 0;
+
+ while ((term = argv_term (&f)) != NULL)
+ {
+ printf ("[%d] '%s'\n", i, term);
+ ++i;
+ free (term);
+ }
+ }
+ }
+#endif
+
+ argv_reset (&a);
+ gc_free (&gc);
+}
+#endif
+
+const char *
+openvpn_basename (const char *path)
+{
+ const char *ret;
+ const int dirsep = OS_SPECIFIC_DIRSEP;
+
+ if (path)
+ {
+ ret = strrchr (path, dirsep);
+ if (ret && *ret)
+ ++ret;
+ else
+ ret = path;
+ if (*ret)
+ return ret;
+ }
+ return NULL;
+}
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..3f22ca0
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,422 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MISC_H
+#define MISC_H
+
+#include "basic.h"
+#include "common.h"
+#include "integer.h"
+#include "buffer.h"
+
+/* socket descriptor passed by inetd/xinetd server to us */
+#define INETD_SOCKET_DESCRIPTOR 0
+
+/* forward declarations */
+struct plugin_list;
+
+/* used by argv_x functions */
+struct argv {
+ size_t capacity;
+ size_t argc;
+ char **argv;
+ char *system_str;
+};
+
+/*
+ * Handle environmental variable lists
+ */
+
+struct env_item {
+ char *string;
+ struct env_item *next;
+};
+
+struct env_set {
+ struct gc_arena *gc;
+ struct env_item *list;
+};
+
+/* Get/Set UID of process */
+
+struct user_state {
+#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
+ const char *username;
+ struct passwd *pw;
+#else
+ int dummy;
+#endif
+};
+
+bool get_user (const char *username, struct user_state *state);
+void set_user (const struct user_state *state);
+
+/* Get/Set GID of process */
+
+struct group_state {
+#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
+ const char *groupname;
+ struct group *gr;
+#else
+ int dummy;
+#endif
+};
+
+bool get_group (const char *groupname, struct group_state *state);
+void set_group (const struct group_state *state);
+
+void set_nice (int niceval);
+void do_chroot (const char *path);
+
+void run_up_down (const char *command,
+ const struct plugin_list *plugins,
+ int plugin_type,
+ const char *arg,
+ int tun_mtu,
+ int link_mtu,
+ const char *ifconfig_local,
+ const char* ifconfig_remote,
+ const char *context,
+ const char *signal_text,
+ const char *script_type,
+ struct env_set *es);
+
+/* workspace for get_pid_file/write_pid */
+struct pid_state {
+ FILE *fp;
+ const char *filename;
+};
+
+void get_pid_file (const char* filename, struct pid_state *state);
+void write_pid (const struct pid_state *state);
+unsigned int openvpn_getpid (void);
+
+void do_mlockall (bool print_msg); /* Disable paging */
+
+#ifndef HAVE_DAEMON
+int daemon (int nochdir, int noclose);
+#endif
+
+/* check file protections */
+void warn_if_group_others_accessible(const char* filename);
+
+/* system flags */
+#define S_SCRIPT (1<<0)
+#define S_FATAL (1<<1)
+
+/* interpret the status code returned by system()/execve() */
+bool system_ok(int);
+bool system_executed (int stat);
+const char *system_error_message (int, struct gc_arena *gc);
+
+/* wrapper around the execve() call */
+int openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags);
+bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message);
+bool openvpn_execve_allowed (const unsigned int flags);
+int openvpn_system (const char *command, const struct env_set *es, unsigned int flags);
+
+static inline bool
+openvpn_run_script (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *hook)
+{
+ char msg[256];
+
+ openvpn_snprintf(msg, sizeof(msg), "WARNING: Failed running command (%s)", hook);
+ return openvpn_execve_check(a, es, flags | S_SCRIPT, msg);
+};
+
+#ifdef HAVE_STRERROR
+/* a thread-safe version of strerror */
+const char* strerror_ts (int errnum, struct gc_arena *gc);
+#endif
+
+/* Set standard file descriptors to /dev/null */
+void set_std_files_to_null (bool stdin_only);
+
+/* Wrapper for chdir library function */
+int openvpn_chdir (const char* dir);
+
+/* dup inetd/xinetd socket descriptor and save */
+extern int inetd_socket_descriptor;
+void save_inetd_socket_descriptor (void);
+
+/* init random() function, only used as source for weak random numbers, when !USE_CRYPTO */
+void init_random_seed(void);
+
+/* set/delete environmental variable */
+void setenv_str_ex (struct env_set *es,
+ const char *name,
+ const char *value,
+ const unsigned int name_include,
+ const unsigned int name_exclude,
+ const char name_replace,
+ const unsigned int value_include,
+ const unsigned int value_exclude,
+ const char value_replace);
+
+void setenv_counter (struct env_set *es, const char *name, counter_type value);
+void setenv_int (struct env_set *es, const char *name, int value);
+void setenv_unsigned (struct env_set *es, const char *name, unsigned int value);
+void setenv_str (struct env_set *es, const char *name, const char *value);
+void setenv_str_safe (struct env_set *es, const char *name, const char *value);
+void setenv_del (struct env_set *es, const char *name);
+
+void setenv_int_i (struct env_set *es, const char *name, const int value, const int i);
+void setenv_str_i (struct env_set *es, const char *name, const char *value, const int i);
+
+/* struct env_set functions */
+
+struct env_set *env_set_create (struct gc_arena *gc);
+void env_set_destroy (struct env_set *es);
+bool env_set_del (struct env_set *es, const char *str);
+void env_set_add (struct env_set *es, const char *str);
+
+void env_set_print (int msglevel, const struct env_set *es);
+
+void env_set_inherit (struct env_set *es, const struct env_set *src);
+
+void env_set_add_to_environment (const struct env_set *es);
+void env_set_remove_from_environment (const struct env_set *es);
+
+/* Make arrays of strings */
+
+const char **make_env_array (const struct env_set *es,
+ const bool check_allowed,
+ struct gc_arena *gc);
+
+const char **make_arg_array (const char *first, const char *parms, struct gc_arena *gc);
+const char **make_extended_arg_array (char **p, struct gc_arena *gc);
+
+/* convert netmasks for iproute2 */
+int count_netmask_bits(const char *);
+unsigned int count_bits(unsigned int );
+
+/* go to sleep for n milliseconds */
+void sleep_milliseconds (unsigned int n);
+
+/* go to sleep indefinitely */
+void sleep_until_signal (void);
+
+/* an analogue to the random() function, but use OpenSSL functions if available */
+#ifdef USE_CRYPTO
+long int get_random(void);
+#else
+#define get_random random
+#endif
+
+/* return true if filename can be opened for read */
+bool test_file (const char *filename);
+
+/* create a temporary file in directory, returns the filename of the created file */
+const char *create_temp_file (const char *directory, const char *prefix, struct gc_arena *gc);
+
+/* put a directory and filename together */
+const char *gen_path (const char *directory, const char *filename, struct gc_arena *gc);
+
+/* delete a file, return true if succeeded */
+bool delete_file (const char *filename);
+
+/* return true if pathname is absolute */
+bool absolute_pathname (const char *pathname);
+
+/* prepend a random prefix to hostname (need USE_CRYPTO) */
+const char *hostname_randomize(const char *hostname, struct gc_arena *gc);
+
+/*
+ * Get and store a username/password
+ */
+
+struct user_pass
+{
+ bool defined;
+ bool nocache;
+
+/* max length of username/password */
+# ifdef ENABLE_PKCS11
+# define USER_PASS_LEN 4096
+# else
+# define USER_PASS_LEN 128
+# endif
+ char username[USER_PASS_LEN];
+ char password[USER_PASS_LEN];
+};
+
+#ifdef ENABLE_CLIENT_CR
+/*
+ * Challenge response info on client as pushed by server.
+ */
+struct auth_challenge_info {
+# define CR_ECHO (1<<0) /* echo response when typed by user */
+# define CR_RESPONSE (1<<1) /* response needed */
+ unsigned int flags;
+
+ const char *user;
+ const char *state_id;
+ const char *challenge_text;
+};
+
+struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc);
+
+#else
+struct auth_challenge_info {};
+#endif
+
+bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity);
+
+/*
+ * Flags for get_user_pass and management_query_user_pass
+ */
+#define GET_USER_PASS_MANAGEMENT (1<<0)
+#define GET_USER_PASS_SENSITIVE (1<<1)
+#define GET_USER_PASS_PASSWORD_ONLY (1<<2)
+#define GET_USER_PASS_NEED_OK (1<<3)
+#define GET_USER_PASS_NOFATAL (1<<4)
+#define GET_USER_PASS_NEED_STR (1<<5)
+#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)
+
+bool get_user_pass_cr (struct user_pass *up,
+ const char *auth_file,
+ const char *prefix,
+ const unsigned int flags,
+ const char *auth_challenge);
+
+static inline bool
+get_user_pass (struct user_pass *up,
+ const char *auth_file,
+ const char *prefix,
+ const unsigned int flags)
+{
+ return get_user_pass_cr (up, auth_file, prefix, flags, NULL);
+}
+
+void fail_user_pass (const char *prefix,
+ const unsigned int flags,
+ const char *reason);
+
+void purge_user_pass (struct user_pass *up, const bool force);
+
+/*
+ * Process string received by untrusted peer before
+ * printing to console or log file.
+ * Assumes that string has been null terminated.
+ */
+const char *safe_print (const char *str, struct gc_arena *gc);
+
+/* returns true if environmental variable safe to print to log */
+bool env_safe_to_print (const char *str);
+
+/* returns true if environmental variable may be passed to an external program */
+bool env_allowed (const char *str);
+
+/*
+ * A sleep function that services the management layer for n
+ * seconds rather than doing nothing.
+ */
+void openvpn_sleep (const int n);
+
+void configure_path (void);
+
+#if AUTO_USERID
+void get_user_pass_auto_userid (struct user_pass *up, const char *tag);
+#endif
+
+/*
+ * /sbin/ip path, may be overridden
+ */
+#ifdef CONFIG_FEATURE_IPROUTE
+extern const char *iproute_path;
+#endif
+
+/* Script security */
+#define SSEC_NONE 0 /* strictly no calling of external programs */
+#define SSEC_BUILT_IN 1 /* only call built-in programs such as ifconfig, route, netsh, etc.*/
+#define SSEC_SCRIPTS 2 /* allow calling of built-in programs and user-defined scripts */
+#define SSEC_PW_ENV 3 /* allow calling of built-in programs and user-defined scripts that may receive a password as an environmental variable */
+extern int script_security; /* GLOBAL */
+
+#define SM_EXECVE 0 /* call external programs with execve() or CreateProcess() */
+#define SM_SYSTEM 1 /* call external programs with system() */
+extern int script_method; /* GLOBAL */
+
+/* return the next largest power of 2 */
+size_t adjust_power_of_2 (size_t u);
+
+/* return the basename of path */
+const char *openvpn_basename (const char *path);
+
+/*
+ * 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.
+ */
+void argv_init (struct argv *a);
+struct argv argv_new (void);
+void argv_reset (struct argv *a);
+char *argv_term (const char **f);
+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);
+const char *argv_system_str (const struct argv *a);
+
+#define APA_CAT (1<<0) /* concatentate onto existing struct argv list */
+void argv_printf_arglist (struct argv *a, const char *format, const unsigned int flags, va_list arglist);
+
+void argv_printf (struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+
+void argv_printf_cat (struct argv *a, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+
+/*
+ * Extract UID or GID
+ */
+
+static inline int
+user_state_uid (const struct user_state *s)
+{
+#if defined(HAVE_GETPWNAM) && defined(HAVE_SETUID)
+ if (s->pw)
+ return s->pw->pw_uid;
+#endif
+ return -1;
+}
+
+static inline int
+group_state_gid (const struct group_state *s)
+{
+#if defined(HAVE_GETGRNAM) && defined(HAVE_SETGID)
+ if (s->gr)
+ return s->gr->gr_gid;
+#endif
+ return -1;
+}
+
+#endif
diff --git a/missing b/missing
new file mode 100755
index 0000000..28055d2
--- /dev/null
+++ b/missing
@@ -0,0 +1,376 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# 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, 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, see <http://www.gnu.org/licenses/>.
+
+# 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.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ autom4te touch the output file, or create a stub one
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+# Now exit if we have it, but it failed. Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program). This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+ lex*|yacc*)
+ # Not GNU programs, they don't have --version.
+ ;;
+
+ tar*)
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ exit 1
+ fi
+ ;;
+
+ *)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ # Could not run --version or --help. This is probably someone
+ # running `$TOOL --version' or `$TOOL --help' to check whether
+ # $TOOL exists and not knowing $TOOL uses missing.
+ exit 1
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case $f in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te*)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison*|yacc*)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f y.tab.h; then
+ echo >y.tab.h
+ fi
+ if test ! -f y.tab.c; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex*|flex*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f lex.yy.c; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit $?
+ fi
+ ;;
+
+ makeinfo*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ # The file to touch is that specified with -o ...
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -z "$file"; then
+ # ... or it is the one specified with @setfilename ...
+ infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '
+ /^@setfilename/{
+ s/.* \([^ ]*\) *$/\1/
+ p
+ q
+ }' $infile`
+ # ... or it is derived from the source name (dir/f.texi becomes f.info)
+ test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+ fi
+ # If the file does not exist, the user really needs makeinfo;
+ # let's fail without touching anything.
+ test -f $file || exit 1
+ touch $file
+ ;;
+
+ tar*)
+ shift
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar "$@" && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar "$@" && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case $firstarg in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ case $firstarg in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/mroute.c b/mroute.c
new file mode 100644
index 0000000..3debd80
--- /dev/null
+++ b/mroute.c
@@ -0,0 +1,429 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "mroute.h"
+#include "proto.h"
+#include "error.h"
+#include "socket.h"
+
+#include "memdbg.h"
+
+void
+mroute_addr_init (struct mroute_addr *addr)
+{
+ CLEAR (*addr);
+}
+
+/*
+ * Ethernet multicast addresses.
+ */
+
+static inline bool
+is_mac_mcast_addr (const uint8_t *mac)
+{
+ return (bool) mac[0] & 1;
+}
+
+static inline bool
+is_mac_mcast_maddr (const struct mroute_addr *addr)
+{
+ return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr);
+}
+
+/*
+ * Don't learn certain addresses.
+ */
+bool
+mroute_learnable_address (const struct mroute_addr *addr)
+{
+ int i;
+ bool not_all_zeros = false;
+ bool not_all_ones = false;
+
+ for (i = 0; i < addr->len; ++i)
+ {
+ int b = addr->addr[i];
+ if (b != 0x00)
+ not_all_zeros = true;
+ if (b != 0xFF)
+ not_all_ones = true;
+ }
+ return not_all_zeros && not_all_ones && !is_mac_mcast_maddr (addr);
+}
+
+static inline void
+mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int mask)
+{
+ if (ma)
+ {
+ ma->type = MR_ADDR_IPV4 | mask;
+ ma->netbits = 0;
+ ma->len = 4;
+ *(in_addr_t*)ma->addr = src;
+ }
+}
+
+static inline bool
+mroute_is_mcast (const in_addr_t addr)
+{
+ return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
+}
+
+#ifdef ENABLE_PF
+
+static unsigned int
+mroute_extract_addr_arp (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ const struct buffer *buf)
+{
+ unsigned int ret = 0;
+ if (BLEN (buf) >= (int) sizeof (struct openvpn_arp))
+ {
+ const struct openvpn_arp *arp = (const struct openvpn_arp *) BPTR (buf);
+ if (arp->mac_addr_type == htons(0x0001)
+ && arp->proto_addr_type == htons(0x0800)
+ && arp->mac_addr_size == 0x06
+ && arp->proto_addr_size == 0x04)
+ {
+ mroute_get_in_addr_t (src, arp->ip_src, MR_ARP);
+ mroute_get_in_addr_t (dest, arp->ip_dest, MR_ARP);
+
+ /* multicast packet? */
+ if (mroute_is_mcast (arp->ip_dest))
+ ret |= MROUTE_EXTRACT_MCAST;
+
+ ret |= MROUTE_EXTRACT_SUCCEEDED;
+ }
+ }
+ return ret;
+}
+
+#endif
+
+unsigned int
+mroute_extract_addr_ipv4 (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ const struct buffer *buf)
+{
+ unsigned int ret = 0;
+ if (BLEN (buf) >= 1)
+ {
+ switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
+ {
+ case 4:
+ if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
+ {
+ const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR (buf);
+
+ mroute_get_in_addr_t (src, ip->saddr, 0);
+ mroute_get_in_addr_t (dest, ip->daddr, 0);
+
+ /* multicast packet? */
+ if (mroute_is_mcast (ip->daddr))
+ ret |= MROUTE_EXTRACT_MCAST;
+
+ /* IGMP message? */
+ if (ip->protocol == OPENVPN_IPPROTO_IGMP)
+ ret |= MROUTE_EXTRACT_IGMP;
+
+ ret |= MROUTE_EXTRACT_SUCCEEDED;
+ }
+ break;
+ case 6:
+ {
+ msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet");
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+unsigned int
+mroute_extract_addr_ether (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ struct mroute_addr *esrc,
+ struct mroute_addr *edest,
+ const struct buffer *buf)
+{
+ unsigned int ret = 0;
+ if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
+ {
+ const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf);
+ if (src)
+ {
+ src->type = MR_ADDR_ETHER;
+ src->netbits = 0;
+ src->len = 6;
+ memcpy (src->addr, eth->source, 6);
+ }
+ if (dest)
+ {
+ dest->type = MR_ADDR_ETHER;
+ dest->netbits = 0;
+ dest->len = 6;
+ memcpy (dest->addr, eth->dest, 6);
+
+ /* ethernet broadcast/multicast packet? */
+ if (is_mac_mcast_addr (eth->dest))
+ ret |= MROUTE_EXTRACT_BCAST;
+ }
+
+ ret |= MROUTE_EXTRACT_SUCCEEDED;
+
+#ifdef ENABLE_PF
+ if (esrc || edest)
+ {
+ struct buffer b = *buf;
+ if (buf_advance (&b, sizeof (struct openvpn_ethhdr)))
+ {
+ switch (ntohs (eth->proto))
+ {
+ case OPENVPN_ETH_P_IPV4:
+ ret |= (mroute_extract_addr_ipv4 (esrc, edest, &b) << MROUTE_SEC_SHIFT);
+ break;
+ case OPENVPN_ETH_P_ARP:
+ ret |= (mroute_extract_addr_arp (esrc, edest, &b) << MROUTE_SEC_SHIFT);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ return ret;
+}
+
+/*
+ * Translate a struct openvpn_sockaddr (osaddr)
+ * to a struct mroute_addr (addr).
+ */
+bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
+ const struct openvpn_sockaddr *osaddr,
+ bool use_port)
+{
+ if (osaddr->sa.sin_family == AF_INET)
+ {
+ if (use_port)
+ {
+ addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
+ addr->netbits = 0;
+ addr->len = 6;
+ memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+ memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
+ }
+ else
+ {
+ addr->type = MR_ADDR_IPV4;
+ addr->netbits = 0;
+ addr->len = 4;
+ memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+ }
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Zero off the host bits in an address, leaving
+ * only the network bits, using the netbits member of
+ * struct mroute_addr as the controlling parameter.
+ */
+void
+mroute_addr_mask_host_bits (struct mroute_addr *ma)
+{
+ in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
+ ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
+ addr &= netbits_to_netmask (ma->netbits);
+ *(in_addr_t*)ma->addr = htonl (addr);
+}
+
+/*
+ * The mroute_addr hash function takes into account the
+ * address type, number of bits in the network address,
+ * and the actual address.
+ */
+uint32_t
+mroute_addr_hash_function (const void *key, uint32_t iv)
+{
+ return hash_func (mroute_addr_hash_ptr ((const struct mroute_addr *) key),
+ mroute_addr_hash_len ((const struct mroute_addr *) key),
+ iv);
+}
+
+bool
+mroute_addr_compare_function (const void *key1, const void *key2)
+{
+ return mroute_addr_equal ((const struct mroute_addr *) key1,
+ (const struct mroute_addr *) key2);
+}
+
+const char *
+mroute_addr_print (const struct mroute_addr *ma,
+ struct gc_arena *gc)
+{
+ return mroute_addr_print_ex (ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
+}
+
+const char *
+mroute_addr_print_ex (const struct mroute_addr *ma,
+ const unsigned int flags,
+ struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ if (ma)
+ {
+ struct mroute_addr maddr = *ma;
+
+ switch (maddr.type & MR_ADDR_MASK)
+ {
+ case MR_ADDR_ETHER:
+ buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc));
+ break;
+ case MR_ADDR_IPV4:
+ {
+ struct buffer buf;
+ in_addr_t addr;
+ int port;
+ bool status;
+ buf_set_read (&buf, maddr.addr, maddr.len);
+ addr = buf_read_u32 (&buf, &status);
+ if (status)
+ {
+ if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
+ buf_printf (&out, "ARP/");
+ buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
+ if (maddr.type & MR_WITH_NETBITS)
+ {
+ if (flags & MAPF_SUBNET)
+ {
+ const in_addr_t netmask = netbits_to_netmask (maddr.netbits);
+ buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc));
+ }
+ else
+ buf_printf (&out, "/%d", maddr.netbits);
+ }
+ }
+ if (maddr.type & MR_WITH_PORT)
+ {
+ port = buf_read_u16 (&buf);
+ if (port >= 0)
+ buf_printf (&out, ":%d", port);
+ }
+ }
+ break;
+ case MR_ADDR_IPV6:
+ buf_printf (&out, "IPV6");
+ break;
+ default:
+ buf_printf (&out, "UNKNOWN");
+ break;
+ }
+ return BSTR (&out);
+ }
+ else
+ return "[NULL]";
+}
+
+/*
+ * mroute_helper's main job is keeping track of
+ * currently used CIDR netlengths, so we don't
+ * have to cycle through all 33.
+ */
+
+struct mroute_helper *
+mroute_helper_init (int ageable_ttl_secs)
+{
+ struct mroute_helper *mh;
+ ALLOC_OBJ_CLEAR (mh, struct mroute_helper);
+ mh->ageable_ttl_secs = ageable_ttl_secs;
+ return mh;
+}
+
+static void
+mroute_helper_regenerate (struct mroute_helper *mh)
+{
+ int i, j = 0;
+ for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i)
+ {
+ if (mh->net_len_refcount[i] > 0)
+ mh->net_len[j++] = (uint8_t) i;
+ }
+ mh->n_net_len = j;
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_MULTI_DEBUG))
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer out = alloc_buf_gc (256, &gc);
+ buf_printf (&out, "MROUTE CIDR netlen:");
+ for (i = 0; i < mh->n_net_len; ++i)
+ {
+ buf_printf (&out, " /%d", mh->net_len[i]);
+ }
+ dmsg (D_MULTI_DEBUG, "%s", BSTR (&out));
+ gc_free (&gc);
+ }
+#endif
+}
+
+void
+mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir)
+{
+ if (ir->netbits >= 0)
+ {
+ ASSERT (ir->netbits < MR_HELPER_NET_LEN);
+ ++mh->cache_generation;
+ ++mh->net_len_refcount[ir->netbits];
+ if (mh->net_len_refcount[ir->netbits] == 1)
+ mroute_helper_regenerate (mh);
+ }
+}
+
+void
+mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
+{
+ if (ir->netbits >= 0)
+ {
+ ASSERT (ir->netbits < MR_HELPER_NET_LEN);
+ ++mh->cache_generation;
+ --mh->net_len_refcount[ir->netbits];
+ ASSERT (mh->net_len_refcount[ir->netbits] >= 0);
+ if (!mh->net_len_refcount[ir->netbits])
+ mroute_helper_regenerate (mh);
+ }
+}
+
+void
+mroute_helper_free (struct mroute_helper *mh)
+{
+ free (mh);
+}
+
+#else
+static void dummy(void) {}
+#endif /* P2MP_SERVER */
diff --git a/mroute.h b/mroute.h
new file mode 100644
index 0000000..7265001
--- /dev/null
+++ b/mroute.h
@@ -0,0 +1,212 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MROUTE_H
+#define MROUTE_H
+
+#if P2MP_SERVER
+
+#include "buffer.h"
+#include "list.h"
+#include "route.h"
+
+#define IP_MCAST_SUBNET_MASK ((in_addr_t)240<<24)
+#define IP_MCAST_NETWORK ((in_addr_t)224<<24)
+
+/* Return status values for mroute_extract_addr_from_packet */
+
+#define MROUTE_EXTRACT_SUCCEEDED (1<<0)
+#define MROUTE_EXTRACT_BCAST (1<<1)
+#define MROUTE_EXTRACT_MCAST (1<<2)
+#define MROUTE_EXTRACT_IGMP (1<<3)
+
+#define MROUTE_SEC_EXTRACT_SUCCEEDED (1<<(0+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_BCAST (1<<(1+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_MCAST (1<<(2+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_IGMP (1<<(3+MROUTE_SEC_SHIFT))
+
+#define MROUTE_SEC_SHIFT 4
+
+/*
+ * Choose the largest address possible with
+ * any of our supported types, which is IPv6
+ * with port number.
+ */
+#define MR_MAX_ADDR_LEN 20
+
+/*
+ * Address Types
+ */
+#define MR_ADDR_NONE 0
+#define MR_ADDR_ETHER 1
+#define MR_ADDR_IPV4 2
+#define MR_ADDR_IPV6 3
+#define MR_ADDR_MASK 3
+
+/* Address type mask indicating that port # is part of address */
+#define MR_WITH_PORT 4
+
+/* Address type mask indicating that netbits is part of address */
+#define MR_WITH_NETBITS 8
+
+/* Indicates than IPv4 addr was extracted from ARP packet */
+#define MR_ARP 16
+
+struct mroute_addr {
+ uint8_t len; /* length of address */
+ uint8_t unused;
+ uint8_t type; /* MR_ADDR/MR_WITH flags */
+ uint8_t netbits; /* number of bits in network part of address,
+ valid if MR_WITH_NETBITS is set */
+ uint8_t addr[MR_MAX_ADDR_LEN]; /* actual address */
+};
+
+/*
+ * Number of bits in an address. Should be raised for IPv6.
+ */
+#define MR_HELPER_NET_LEN 32
+
+/*
+ * Used to help maintain CIDR routing table.
+ */
+struct mroute_helper {
+ unsigned int cache_generation; /* incremented when route added */
+ int ageable_ttl_secs; /* host route cache entry time-to-live*/
+ int n_net_len; /* length of net_len array */
+ uint8_t net_len[MR_HELPER_NET_LEN]; /* CIDR netlengths in descending order */
+ int net_len_refcount[MR_HELPER_NET_LEN]; /* refcount of each netlength */
+};
+
+struct openvpn_sockaddr;
+
+bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
+ const struct openvpn_sockaddr *osaddr,
+ bool use_port);
+
+bool mroute_learnable_address (const struct mroute_addr *addr);
+
+uint32_t mroute_addr_hash_function (const void *key, uint32_t iv);
+bool mroute_addr_compare_function (const void *key1, const void *key2);
+
+void mroute_addr_init (struct mroute_addr *addr);
+
+const char *mroute_addr_print (const struct mroute_addr *ma,
+ struct gc_arena *gc);
+
+#define MAPF_SUBNET (1<<0)
+#define MAPF_IA_EMPTY_IF_UNDEF (1<<1)
+#define MAPF_SHOW_ARP (1<<2)
+const char *mroute_addr_print_ex (const struct mroute_addr *ma,
+ const unsigned int flags,
+ struct gc_arena *gc);
+
+void mroute_addr_mask_host_bits (struct mroute_addr *ma);
+
+struct mroute_helper *mroute_helper_init (int ageable_ttl_secs);
+void mroute_helper_free (struct mroute_helper *mh);
+void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
+void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
+
+/*
+ * Given a raw packet in buf, return the src and dest
+ * addresses of the packet.
+ */
+static inline unsigned int
+mroute_extract_addr_from_packet (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ struct mroute_addr *esrc,
+ struct mroute_addr *edest,
+ const struct buffer *buf,
+ int tunnel_type)
+{
+ unsigned int mroute_extract_addr_ipv4 (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ const struct buffer *buf);
+
+ unsigned int mroute_extract_addr_ether (struct mroute_addr *src,
+ struct mroute_addr *dest,
+ struct mroute_addr *esrc,
+ struct mroute_addr *edest,
+ const struct buffer *buf);
+ unsigned int ret = 0;
+ verify_align_4 (buf);
+ if (tunnel_type == DEV_TYPE_TUN)
+ ret = mroute_extract_addr_ipv4 (src, dest, buf);
+ else if (tunnel_type == DEV_TYPE_TAP)
+ ret = mroute_extract_addr_ether (src, dest, esrc, edest, buf);
+ return ret;
+}
+
+static inline bool
+mroute_addr_equal (const struct mroute_addr *a1, const struct mroute_addr *a2)
+{
+ if (a1->type != a2->type)
+ return false;
+ if (a1->netbits != a2->netbits)
+ return false;
+ if (a1->len != a2->len)
+ return false;
+ return memcmp (a1->addr, a2->addr, a1->len) == 0;
+}
+
+static inline const uint8_t *
+mroute_addr_hash_ptr (const struct mroute_addr *a)
+{
+ /* NOTE: depends on ordering of struct mroute_addr */
+ return (uint8_t *) &a->type;
+}
+
+static inline uint32_t
+mroute_addr_hash_len (const struct mroute_addr *a)
+{
+ return (uint32_t) a->len + 2;
+}
+
+static inline void
+mroute_extract_in_addr_t (struct mroute_addr *dest, const in_addr_t src)
+{
+ dest->type = MR_ADDR_IPV4;
+ dest->netbits = 0;
+ dest->len = 4;
+ *(in_addr_t*)dest->addr = htonl (src);
+}
+
+static inline in_addr_t
+in_addr_t_from_mroute_addr (const struct mroute_addr *addr)
+{
+ if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4)
+ return ntohl(*(in_addr_t*)addr->addr);
+ else
+ return 0;
+}
+
+static inline void
+mroute_addr_reset (struct mroute_addr *ma)
+{
+ ma->len = 0;
+ ma->type = MR_ADDR_NONE;
+}
+
+#endif /* P2MP_SERVER */
+#endif /* MROUTE_H */
diff --git a/mss.c b/mss.c
new file mode 100644
index 0000000..660b62c
--- /dev/null
+++ b/mss.c
@@ -0,0 +1,114 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+#include "error.h"
+#include "mss.h"
+#include "memdbg.h"
+
+/*
+ * Lower MSS on TCP SYN packets to fix MTU
+ * problems which arise from protocol
+ * encapsulation.
+ */
+void
+mss_fixup (struct buffer *buf, int maxmss)
+{
+ const struct openvpn_iphdr *pip;
+ int hlen;
+
+ if (BLEN (buf) < (int) sizeof (struct openvpn_iphdr))
+ return;
+
+ verify_align_4 (buf);
+ pip = (struct openvpn_iphdr *) BPTR (buf);
+
+ hlen = OPENVPN_IPH_GET_LEN (pip->version_len);
+
+ if (pip->protocol == OPENVPN_IPPROTO_TCP
+ && ntohs (pip->tot_len) == BLEN (buf)
+ && (ntohs (pip->frag_off) & OPENVPN_IP_OFFMASK) == 0
+ && hlen <= BLEN (buf)
+ && BLEN (buf) - hlen
+ >= (int) sizeof (struct openvpn_tcphdr))
+ {
+ struct buffer newbuf = *buf;
+ if (buf_advance (&newbuf, hlen))
+ {
+ struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR (&newbuf);
+ if (tc->flags & OPENVPN_TCPH_SYN_MASK)
+ mss_fixup_dowork (&newbuf, (uint16_t) maxmss);
+ }
+ }
+}
+
+void
+mss_fixup_dowork (struct buffer *buf, uint16_t maxmss)
+{
+ int hlen, olen, optlen;
+ uint8_t *opt;
+ uint16_t *mss;
+ int accumulate;
+ struct openvpn_tcphdr *tc;
+
+ ASSERT (BLEN (buf) >= (int) sizeof (struct openvpn_tcphdr));
+
+ verify_align_4 (buf);
+ tc = (struct openvpn_tcphdr *) BPTR (buf);
+ hlen = OPENVPN_TCPH_GET_DOFF (tc->doff_res);
+
+ /* Invalid header length or header without options. */
+ if (hlen <= (int) sizeof (struct openvpn_tcphdr)
+ || hlen > BLEN (buf))
+ return;
+
+ for (olen = hlen - sizeof (struct openvpn_tcphdr),
+ opt = (uint8_t *)(tc + 1);
+ olen > 0;
+ olen -= optlen, opt += optlen) {
+ if (*opt == OPENVPN_TCPOPT_EOL)
+ break;
+ else if (*opt == OPENVPN_TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = *(opt + 1);
+ if (optlen <= 0 || optlen > olen)
+ break;
+ if (*opt == OPENVPN_TCPOPT_MAXSEG) {
+ if (optlen != OPENVPN_TCPOLEN_MAXSEG)
+ continue;
+ mss = (uint16_t *)(opt + 2);
+ if (ntohs (*mss) > maxmss) {
+ dmsg (D_MSS, "MSS: %d -> %d",
+ (int) ntohs (*mss),
+ (int) maxmss);
+ accumulate = *mss;
+ *mss = htons (maxmss);
+ accumulate -= *mss;
+ ADJUST_CHECKSUM (accumulate, tc->check);
+ }
+ }
+ }
+ }
+}
diff --git a/mss.h b/mss.h
new file mode 100644
index 0000000..0b290c3
--- /dev/null
+++ b/mss.h
@@ -0,0 +1,34 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MSS_H
+#define MSS_H
+
+#include "proto.h"
+#include "error.h"
+
+void mss_fixup (struct buffer *buf, int maxmss);
+void mss_fixup_dowork (struct buffer *buf, uint16_t maxmss);
+
+#endif
diff --git a/mtcp.c b/mtcp.c
new file mode 100644
index 0000000..314aa44
--- /dev/null
+++ b/mtcp.c
@@ -0,0 +1,718 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "multi.h"
+#include "forward-inline.h"
+
+#include "memdbg.h"
+
+/*
+ * TCP States
+ */
+#define TA_UNDEF 0
+#define TA_SOCKET_READ 1
+#define TA_SOCKET_READ_RESIDUAL 2
+#define TA_SOCKET_WRITE 3
+#define TA_SOCKET_WRITE_READY 4
+#define TA_SOCKET_WRITE_DEFERRED 5
+#define TA_TUN_READ 6
+#define TA_TUN_WRITE 7
+#define TA_INITIAL 8
+#define TA_TIMEOUT 9
+#define TA_TUN_WRITE_TIMEOUT 10
+
+/*
+ * Special tags passed to event.[ch] functions
+ */
+#define MTCP_SOCKET ((void*)1)
+#define MTCP_TUN ((void*)2)
+#define MTCP_SIG ((void*)3) /* Only on Windows */
+#ifdef ENABLE_MANAGEMENT
+# define MTCP_MANAGEMENT ((void*)4)
+#endif
+
+#define MTCP_N ((void*)16) /* upper bound on MTCP_x */
+
+struct ta_iow_flags
+{
+ unsigned int flags;
+ unsigned int ret;
+ unsigned int tun;
+ unsigned int sock;
+};
+
+static const char *
+pract (int action)
+{
+ switch (action)
+ {
+ case TA_UNDEF:
+ return "TA_UNDEF";
+ case TA_SOCKET_READ:
+ return "TA_SOCKET_READ";
+ case TA_SOCKET_READ_RESIDUAL:
+ return "TA_SOCKET_READ_RESIDUAL";
+ case TA_SOCKET_WRITE:
+ return "TA_SOCKET_WRITE";
+ case TA_SOCKET_WRITE_READY:
+ return "TA_SOCKET_WRITE_READY";
+ case TA_SOCKET_WRITE_DEFERRED:
+ return "TA_SOCKET_WRITE_DEFERRED";
+ case TA_TUN_READ:
+ return "TA_TUN_READ";
+ case TA_TUN_WRITE:
+ return "TA_TUN_WRITE";
+ case TA_INITIAL:
+ return "TA_INITIAL";
+ case TA_TIMEOUT:
+ return "TA_TIMEOUT";
+ case TA_TUN_WRITE_TIMEOUT:
+ return "TA_TUN_WRITE_TIMEOUT";
+ default:
+ return "?";
+ }
+}
+
+static struct multi_instance *
+multi_create_instance_tcp (struct multi_context *m)
+{
+ struct gc_arena gc = gc_new ();
+ struct multi_instance *mi = NULL;
+ struct hash *hash = m->hash;
+
+ mi = multi_create_instance (m, NULL);
+ if (mi)
+ {
+ struct hash_element *he;
+ const uint32_t hv = hash_value (hash, &mi->real);
+ struct hash_bucket *bucket = hash_bucket (hash, hv);
+
+ he = hash_lookup_fast (hash, bucket, &mi->real, hv);
+
+ if (he)
+ {
+ struct multi_instance *oldmi = (struct multi_instance *) he->value;
+ msg (D_MULTI_LOW, "MULTI TCP: new incoming client address matches existing client address -- new client takes precedence");
+ oldmi->did_real_hash = false;
+ multi_close_instance (m, oldmi, false);
+ he->key = &mi->real;
+ he->value = mi;
+ }
+ else
+ hash_add_fast (hash, bucket, &mi->real, hv, mi);
+
+ mi->did_real_hash = true;
+ }
+
+#ifdef ENABLE_DEBUG
+ if (mi)
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: instance added: %s", mroute_addr_print (&mi->real, &gc));
+ else
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: new client instance failed");
+#endif
+
+ gc_free (&gc);
+ ASSERT (!(mi && mi->halt));
+ return mi;
+}
+
+bool
+multi_tcp_instance_specific_init (struct multi_context *m, struct multi_instance *mi)
+{
+ /* buffer for queued TCP socket output packets */
+ mi->tcp_link_out_deferred = mbuf_init (m->top.options.n_bcast_buf);
+
+ ASSERT (mi->context.c2.link_socket);
+ ASSERT (mi->context.c2.link_socket->info.lsa);
+ ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
+ if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
+ {
+ msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
+ return false;
+ }
+ return true;
+}
+
+void
+multi_tcp_instance_specific_free (struct multi_instance *mi)
+{
+ mbuf_free (mi->tcp_link_out_deferred);
+}
+
+struct multi_tcp *
+multi_tcp_init (int maxevents, int *maxclients)
+{
+ struct multi_tcp *mtcp;
+ const int extra_events = BASE_N_EVENTS;
+
+ ASSERT (maxevents >= 1);
+ ASSERT (maxclients);
+
+ ALLOC_OBJ_CLEAR (mtcp, struct multi_tcp);
+ mtcp->maxevents = maxevents + extra_events;
+ mtcp->es = event_set_init (&mtcp->maxevents, 0);
+ wait_signal (mtcp->es, MTCP_SIG);
+ ALLOC_ARRAY (mtcp->esr, struct event_set_return, mtcp->maxevents);
+ *maxclients = max_int (min_int (mtcp->maxevents - extra_events, *maxclients), 1);
+ msg (D_MULTI_LOW, "MULTI: TCP INIT maxclients=%d maxevents=%d", *maxclients, mtcp->maxevents);
+ return mtcp;
+}
+
+void
+multi_tcp_delete_event (struct multi_tcp *mtcp, event_t event)
+{
+ if (mtcp && mtcp->es)
+ event_del (mtcp->es, event);
+}
+
+void
+multi_tcp_free (struct multi_tcp *mtcp)
+{
+ if (mtcp)
+ {
+ event_free (mtcp->es);
+ if (mtcp->esr)
+ free (mtcp->esr);
+ free (mtcp);
+ }
+}
+
+void
+multi_tcp_dereference_instance (struct multi_tcp *mtcp, struct multi_instance *mi)
+{
+ struct link_socket *ls = mi->context.c2.link_socket;
+ if (ls && mi->socket_set_called)
+ event_del (mtcp->es, socket_event_handle (ls));
+ mtcp->n_esr = 0;
+}
+
+static inline void
+multi_tcp_set_global_rw_flags (struct multi_context *m, struct multi_instance *mi)
+{
+ if (mi)
+ {
+ mi->socket_set_called = true;
+ socket_set (mi->context.c2.link_socket,
+ m->mtcp->es,
+ mbuf_defined (mi->tcp_link_out_deferred) ? EVENT_WRITE : EVENT_READ,
+ mi,
+ &mi->tcp_rwflags);
+ }
+}
+
+static inline int
+multi_tcp_wait (const struct context *c,
+ struct multi_tcp *mtcp)
+{
+ int status;
+ socket_set_listen_persistent (c->c2.link_socket, mtcp->es, MTCP_SOCKET);
+ tun_set (c->c1.tuntap, mtcp->es, EVENT_READ, MTCP_TUN, &mtcp->tun_rwflags);
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_socket_set (management, mtcp->es, MTCP_MANAGEMENT, &mtcp->management_persist_flags);
+#endif
+ status = event_wait (mtcp->es, &c->c2.timeval, mtcp->esr, mtcp->maxevents);
+ update_time ();
+ mtcp->n_esr = 0;
+ if (status > 0)
+ mtcp->n_esr = status;
+ return status;
+}
+
+static inline struct context *
+multi_tcp_context (struct multi_context *m, struct multi_instance *mi)
+{
+ if (mi)
+ return &mi->context;
+ else
+ return &m->top;
+}
+
+static bool
+multi_tcp_process_outgoing_link_ready (struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
+{
+ struct mbuf_item item;
+ bool ret = true;
+ ASSERT (mi);
+
+ /* extract from queue */
+ if (mbuf_extract_item (mi->tcp_link_out_deferred, &item)) /* ciphertext IP packet */
+ {
+ dmsg (D_MULTI_TCP, "MULTI TCP: transmitting previously deferred packet");
+
+ ASSERT (mi == item.instance);
+ mi->context.c2.to_link = item.buffer->buf;
+ ret = multi_process_outgoing_link_dowork (m, mi, mpp_flags);
+ if (!ret)
+ mi = NULL;
+ mbuf_free_buf (item.buffer);
+ }
+ return ret;
+}
+
+static bool
+multi_tcp_process_outgoing_link (struct multi_context *m, bool defer, const unsigned int mpp_flags)
+{
+ struct multi_instance *mi = multi_process_outgoing_link_pre (m);
+ bool ret = true;
+
+ if (mi)
+ {
+ if (defer || mbuf_defined (mi->tcp_link_out_deferred))
+ {
+ /* save to queue */
+ struct buffer *buf = &mi->context.c2.to_link;
+ if (BLEN (buf) > 0)
+ {
+ struct mbuf_buffer *mb = mbuf_alloc_buf (buf);
+ struct mbuf_item item;
+
+ set_prefix (mi);
+ dmsg (D_MULTI_TCP, "MULTI TCP: queuing deferred packet");
+ item.buffer = mb;
+ item.instance = mi;
+ mbuf_add_item (mi->tcp_link_out_deferred, &item);
+ mbuf_free_buf (mb);
+ buf_reset (buf);
+ ret = multi_process_post (m, mi, mpp_flags);
+ if (!ret)
+ mi = NULL;
+ clear_prefix ();
+ }
+ }
+ else
+ {
+ ret = multi_process_outgoing_link_dowork (m, mi, mpp_flags);
+ if (!ret)
+ mi = NULL;
+ }
+ }
+ return ret;
+}
+
+static int
+multi_tcp_wait_lite (struct multi_context *m, struct multi_instance *mi, const int action, bool *tun_input_pending)
+{
+ struct context *c = multi_tcp_context (m, mi);
+ unsigned int looking_for = 0;
+
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: multi_tcp_wait_lite a=%s mi=" ptr_format,
+ pract(action),
+ (ptr_type)mi);
+
+ tv_clear (&c->c2.timeval); /* ZERO-TIMEOUT */
+
+ switch (action)
+ {
+ case TA_TUN_READ:
+ looking_for = TUN_READ;
+ tun_input_pending = NULL;
+ io_wait (c, IOW_READ_TUN);
+ break;
+ case TA_SOCKET_READ:
+ looking_for = SOCKET_READ;
+ tun_input_pending = NULL;
+ io_wait (c, IOW_READ_LINK);
+ break;
+ case TA_TUN_WRITE:
+ looking_for = TUN_WRITE;
+ tun_input_pending = NULL;
+ c->c2.timeval.tv_sec = 1; /* For some reason, the Linux 2.2 TUN/TAP driver hits this timeout */
+ perf_push (PERF_PROC_OUT_TUN_MTCP);
+ io_wait (c, IOW_TO_TUN);
+ perf_pop ();
+ break;
+ case TA_SOCKET_WRITE:
+ looking_for = SOCKET_WRITE;
+ io_wait (c, IOW_TO_LINK|IOW_READ_TUN_FORCE);
+ break;
+ default:
+ msg (M_FATAL, "MULTI TCP: multi_tcp_wait_lite, unhandled action=%d", action);
+ }
+
+ if (tun_input_pending && (c->c2.event_set_status & TUN_READ))
+ *tun_input_pending = true;
+
+ if (c->c2.event_set_status & looking_for)
+ {
+ return action;
+ }
+ else
+ {
+ switch (action)
+ {
+ /* TCP socket output buffer is full */
+ case TA_SOCKET_WRITE:
+ return TA_SOCKET_WRITE_DEFERRED;
+
+ /* TUN device timed out on accepting write */
+ case TA_TUN_WRITE:
+ return TA_TUN_WRITE_TIMEOUT;
+ }
+
+ return TA_UNDEF;
+ }
+}
+
+static struct multi_instance *
+multi_tcp_dispatch (struct multi_context *m, struct multi_instance *mi, const int action)
+{
+ const unsigned int mpp_flags = MPP_PRE_SELECT|MPP_RECORD_TOUCH;
+ struct multi_instance *touched = mi;
+ m->mpp_touched = &touched;
+
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: multi_tcp_dispatch a=%s mi=" ptr_format,
+ pract(action),
+ (ptr_type)mi);
+
+ switch (action)
+ {
+ case TA_TUN_READ:
+ read_incoming_tun (&m->top);
+ if (!IS_SIG (&m->top))
+ multi_process_incoming_tun (m, mpp_flags);
+ break;
+ case TA_SOCKET_READ:
+ case TA_SOCKET_READ_RESIDUAL:
+ ASSERT (mi);
+ ASSERT (mi->context.c2.link_socket);
+ set_prefix (mi);
+ read_incoming_link (&mi->context);
+ clear_prefix ();
+ if (!IS_SIG (&mi->context))
+ {
+ multi_process_incoming_link (m, mi, mpp_flags);
+ if (!IS_SIG (&mi->context))
+ stream_buf_read_setup (mi->context.c2.link_socket);
+ }
+ break;
+ case TA_TIMEOUT:
+ multi_process_timeout (m, mpp_flags);
+ break;
+ case TA_TUN_WRITE:
+ multi_process_outgoing_tun (m, mpp_flags);
+ break;
+ case TA_TUN_WRITE_TIMEOUT:
+ multi_process_drop_outgoing_tun (m, mpp_flags);
+ break;
+ case TA_SOCKET_WRITE_READY:
+ ASSERT (mi);
+ multi_tcp_process_outgoing_link_ready (m, mi, mpp_flags);
+ break;
+ case TA_SOCKET_WRITE:
+ multi_tcp_process_outgoing_link (m, false, mpp_flags);
+ break;
+ case TA_SOCKET_WRITE_DEFERRED:
+ multi_tcp_process_outgoing_link (m, true, mpp_flags);
+ break;
+ case TA_INITIAL:
+ ASSERT (mi);
+ multi_tcp_set_global_rw_flags (m, mi);
+ multi_process_post (m, mi, mpp_flags);
+ break;
+ default:
+ msg (M_FATAL, "MULTI TCP: multi_tcp_dispatch, unhandled action=%d", action);
+ }
+
+ m->mpp_touched = NULL;
+ return touched;
+}
+
+int
+multi_tcp_post (struct multi_context *m, struct multi_instance *mi, const int action)
+{
+ struct context *c = multi_tcp_context (m, mi);
+ int newaction = TA_UNDEF;
+
+# define MTP_NONE 0
+# define MTP_TUN_OUT (1<<0)
+# define MTP_LINK_OUT (1<<1)
+ unsigned int flags = MTP_NONE;
+
+ if (TUN_OUT(c))
+ flags |= MTP_TUN_OUT;
+ if (LINK_OUT(c))
+ flags |= MTP_LINK_OUT;
+
+ switch (flags)
+ {
+ case MTP_TUN_OUT|MTP_LINK_OUT:
+ case MTP_TUN_OUT:
+ newaction = TA_TUN_WRITE;
+ break;
+ case MTP_LINK_OUT:
+ newaction = TA_SOCKET_WRITE;
+ break;
+ case MTP_NONE:
+ if (mi && socket_read_residual (c->c2.link_socket))
+ newaction = TA_SOCKET_READ_RESIDUAL;
+ else
+ multi_tcp_set_global_rw_flags (m, mi);
+ break;
+ default:
+ {
+ struct gc_arena gc = gc_new ();
+ msg (M_FATAL, "MULTI TCP: multi_tcp_post bad state, mi=%s flags=%d",
+ multi_instance_string (mi, false, &gc),
+ flags);
+ gc_free (&gc);
+ break;
+ }
+ }
+
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: multi_tcp_post %s -> %s",
+ pract(action),
+ pract(newaction));
+
+ return newaction;
+}
+
+static void
+multi_tcp_action (struct multi_context *m, struct multi_instance *mi, int action, bool poll)
+{
+ bool tun_input_pending = false;
+
+ do {
+ dmsg (D_MULTI_DEBUG, "MULTI TCP: multi_tcp_action a=%s p=%d",
+ pract(action),
+ poll);
+
+ /*
+ * If TA_SOCKET_READ_RESIDUAL, it means we still have pending
+ * input packets which were read by a prior TCP recv.
+ *
+ * Otherwise do a "lite" wait, which means we wait with 0 timeout
+ * on I/O events only related to the current instance, not
+ * the big list of events.
+ *
+ * On our first pass, poll will be false because we already know
+ * that input is available, and to call io_wait would be redundant.
+ */
+ if (poll && action != TA_SOCKET_READ_RESIDUAL)
+ {
+ const int orig_action = action;
+ action = multi_tcp_wait_lite (m, mi, action, &tun_input_pending);
+ if (action == TA_UNDEF)
+ msg (M_FATAL, "MULTI TCP: I/O wait required blocking in multi_tcp_action, action=%d", orig_action);
+ }
+
+ /*
+ * Dispatch the action
+ */
+ {
+ struct multi_instance *touched = multi_tcp_dispatch (m, mi, action);
+
+ /*
+ * Signal received or TCP connection
+ * reset by peer?
+ */
+ if (touched && IS_SIG (&touched->context))
+ {
+ if (mi == touched)
+ mi = NULL;
+ multi_close_instance_on_signal (m, touched);
+ }
+ }
+
+ /*
+ * If dispatch produced any pending output
+ * for a particular instance, point to
+ * that instance.
+ */
+ if (m->pending)
+ mi = m->pending;
+
+ /*
+ * Based on the effects of the action,
+ * such as generating pending output,
+ * possibly transition to a new action state.
+ */
+ action = multi_tcp_post (m, mi, action);
+
+ /*
+ * If we are finished processing the original action,
+ * check if we have any TUN input. If so, transition
+ * our action state to processing this input.
+ */
+ if (tun_input_pending && action == TA_UNDEF)
+ {
+ action = TA_TUN_READ;
+ mi = NULL;
+ tun_input_pending = false;
+ poll = false;
+ }
+ else
+ poll = true;
+
+ } while (action != TA_UNDEF);
+}
+
+static void
+multi_tcp_process_io (struct multi_context *m)
+{
+ struct multi_tcp *mtcp = m->mtcp;
+ int i;
+
+ for (i = 0; i < mtcp->n_esr; ++i)
+ {
+ struct event_set_return *e = &mtcp->esr[i];
+
+ /* incoming data for instance? */
+ if (e->arg >= MTCP_N)
+ {
+ struct multi_instance *mi = (struct multi_instance *) e->arg;
+ if (mi)
+ {
+ if (e->rwflags & EVENT_WRITE)
+ multi_tcp_action (m, mi, TA_SOCKET_WRITE_READY, false);
+ else if (e->rwflags & EVENT_READ)
+ multi_tcp_action (m, mi, TA_SOCKET_READ, false);
+ }
+ }
+ else
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (e->arg == MTCP_MANAGEMENT)
+ {
+ ASSERT (management);
+ management_io (management);
+ }
+ else
+#endif
+ /* incoming data on TUN? */
+ if (e->arg == MTCP_TUN)
+ {
+ if (e->rwflags & EVENT_WRITE)
+ multi_tcp_action (m, NULL, TA_TUN_WRITE, false);
+ else if (e->rwflags & EVENT_READ)
+ multi_tcp_action (m, NULL, TA_TUN_READ, false);
+ }
+ /* new incoming TCP client attempting to connect? */
+ else if (e->arg == MTCP_SOCKET)
+ {
+ struct multi_instance *mi;
+ ASSERT (m->top.c2.link_socket);
+ socket_reset_listen_persistent (m->top.c2.link_socket);
+ mi = multi_create_instance_tcp (m);
+ if (mi)
+ multi_tcp_action (m, mi, TA_INITIAL, false);
+ }
+ /* signal received? */
+ else if (e->arg == MTCP_SIG)
+ {
+ get_signal (&m->top.sig->signal_received);
+ }
+ }
+ if (IS_SIG (&m->top))
+ break;
+ }
+ mtcp->n_esr = 0;
+
+ /*
+ * Process queued mbuf packets destined for TCP socket
+ */
+ {
+ struct multi_instance *mi;
+ while (!IS_SIG (&m->top) && (mi = mbuf_peek (m->mbuf)) != NULL)
+ {
+ multi_tcp_action (m, mi, TA_SOCKET_WRITE, true);
+ }
+ }
+}
+
+/*
+ * Top level event loop for single-threaded operation.
+ * TCP mode.
+ */
+void
+tunnel_server_tcp (struct context *top)
+{
+ struct multi_context multi;
+ int status;
+
+ top->mode = CM_TOP;
+ context_clear_2 (top);
+
+ /* initialize top-tunnel instance */
+ init_instance_handle_signals (top, top->es, CC_HARD_USR1_TO_HUP);
+ if (IS_SIG (top))
+ return;
+
+ /* initialize global multi_context object */
+ multi_init (&multi, top, true, MC_SINGLE_THREADED);
+
+ /* initialize our cloned top object */
+ multi_top_init (&multi, top, true);
+
+ /* initialize management interface */
+ init_management_callback_multi (&multi);
+
+ /* finished with initialization */
+ initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto tcp-server */
+
+ /* per-packet event loop */
+ while (true)
+ {
+ perf_push (PERF_EVENT_LOOP);
+
+ /* wait on tun/socket list */
+ multi_get_timeout (&multi, &multi.top.c2.timeval);
+ status = multi_tcp_wait (&multi.top, multi.mtcp);
+ MULTI_CHECK_SIG (&multi);
+
+ /* check on status of coarse timers */
+ multi_process_per_second_timers (&multi);
+
+ /* timeout? */
+ if (status > 0)
+ {
+ /* process the I/O which triggered select */
+ multi_tcp_process_io (&multi);
+ MULTI_CHECK_SIG (&multi);
+ }
+ else if (status == 0)
+ {
+ multi_tcp_action (&multi, NULL, TA_TIMEOUT, false);
+ }
+
+ perf_pop ();
+ }
+
+ /* shut down management interface */
+ uninit_management_callback_multi (&multi);
+
+ /* save ifconfig-pool */
+ multi_ifconfig_pool_persist (&multi, true);
+
+ /* tear down tunnel instance (unless --persist-tun) */
+ multi_uninit (&multi);
+ multi_top_free (&multi);
+ close_instance (top);
+}
+
+#endif
diff --git a/mtcp.h b/mtcp.h
new file mode 100644
index 0000000..3585af4
--- /dev/null
+++ b/mtcp.h
@@ -0,0 +1,68 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * TCP specific code for --mode server
+ */
+
+#ifndef MTCP_H
+#define MTCP_H
+
+#if P2MP_SERVER
+
+#include "event.h"
+
+/*
+ * Extra state info needed for TCP mode
+ */
+struct multi_tcp
+{
+ struct event_set *es;
+ struct event_set_return *esr;
+ int n_esr;
+ int maxevents;
+ unsigned int tun_rwflags;
+#ifdef ENABLE_MANAGEMENT
+ unsigned int management_persist_flags;
+#endif
+};
+
+struct multi_instance;
+struct context;
+
+struct multi_tcp *multi_tcp_init (int maxevents, int *maxclients);
+void multi_tcp_free (struct multi_tcp *mtcp);
+void multi_tcp_dereference_instance (struct multi_tcp *mtcp, struct multi_instance *mi);
+
+bool multi_tcp_instance_specific_init (struct multi_context *m, struct multi_instance *mi);
+void multi_tcp_instance_specific_free (struct multi_instance *mi);
+
+void multi_tcp_link_out_deferred (struct multi_context *m, struct multi_instance *mi);
+
+void tunnel_server_tcp (struct context *top);
+
+void multi_tcp_delete_event (struct multi_tcp *mtcp, event_t event);
+
+#endif
+#endif
diff --git a/mtu.c b/mtu.c
new file mode 100644
index 0000000..494f939
--- /dev/null
+++ b/mtu.c
@@ -0,0 +1,290 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "common.h"
+#include "buffer.h"
+#include "error.h"
+#include "integer.h"
+#include "mtu.h"
+
+#include "memdbg.h"
+
+/* allocate a buffer for socket or tun layer */
+void
+alloc_buf_sock_tun (struct buffer *buf,
+ const struct frame *frame,
+ const bool tuntap_buffer,
+ const unsigned int align_mask)
+{
+ /* allocate buffer for overlapped I/O */
+ *buf = alloc_buf (BUF_SIZE (frame));
+ ASSERT (buf_init (buf, FRAME_HEADROOM_ADJ (frame, align_mask)));
+ buf->len = tuntap_buffer ? MAX_RW_SIZE_TUN (frame) : MAX_RW_SIZE_LINK (frame);
+ ASSERT (buf_safe (buf, 0));
+}
+
+void
+frame_finalize (struct frame *frame,
+ bool link_mtu_defined,
+ int link_mtu,
+ bool tun_mtu_defined,
+ int tun_mtu)
+{
+ /* Set link_mtu based on command line options */
+ if (tun_mtu_defined)
+ {
+ ASSERT (!link_mtu_defined);
+ frame->link_mtu = tun_mtu + TUN_LINK_DELTA (frame);
+ }
+ else
+ {
+ ASSERT (link_mtu_defined);
+ frame->link_mtu = link_mtu;
+ }
+
+ if (TUN_MTU_SIZE (frame) < TUN_MTU_MIN)
+ {
+ msg (M_WARN, "TUN MTU value (%d) must be at least %d", TUN_MTU_SIZE (frame), TUN_MTU_MIN);
+ frame_print (frame, M_FATAL, "MTU is too small");
+ }
+
+ frame->link_mtu_dynamic = frame->link_mtu;
+
+ frame->extra_buffer += PAYLOAD_ALIGN;
+}
+
+/*
+ * Set the tun MTU dynamically.
+ */
+void
+frame_set_mtu_dynamic (struct frame *frame, int mtu, unsigned int flags)
+{
+
+#ifdef ENABLE_DEBUG
+ const int orig_mtu = mtu;
+ const int orig_link_mtu_dynamic = frame->link_mtu_dynamic;
+#endif
+
+ ASSERT (mtu >= 0);
+
+ if (flags & SET_MTU_TUN)
+ mtu += TUN_LINK_DELTA (frame);
+
+ if (!(flags & SET_MTU_UPPER_BOUND) || mtu < frame->link_mtu_dynamic)
+ {
+ frame->link_mtu_dynamic = constrain_int (
+ mtu,
+ EXPANDED_SIZE_MIN (frame),
+ EXPANDED_SIZE (frame));
+ }
+
+ dmsg (D_MTU_DEBUG, "MTU DYNAMIC mtu=%d, flags=%u, %d -> %d",
+ orig_mtu,
+ flags,
+ orig_link_mtu_dynamic,
+ frame->link_mtu_dynamic);
+}
+
+/*
+ * Move extra_frame octets into extra_tun. Used by fragmenting code
+ * to adjust frame relative to its position in the buffer processing
+ * queue.
+ */
+void
+frame_subtract_extra (struct frame *frame, const struct frame *src)
+{
+ frame->extra_frame -= src->extra_frame;
+ frame->extra_tun += src->extra_frame;
+}
+
+void
+frame_print (const struct frame *frame,
+ int level,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer out = alloc_buf_gc (256, &gc);
+ if (prefix)
+ buf_printf (&out, "%s ", prefix);
+ buf_printf (&out, "[");
+ buf_printf (&out, " L:%d", frame->link_mtu);
+ buf_printf (&out, " D:%d", frame->link_mtu_dynamic);
+ buf_printf (&out, " EF:%d", frame->extra_frame);
+ buf_printf (&out, " EB:%d", frame->extra_buffer);
+ buf_printf (&out, " ET:%d", frame->extra_tun);
+ buf_printf (&out, " EL:%d", frame->extra_link);
+ if (frame->align_flags && frame->align_adjust)
+ buf_printf (&out, " AF:%u/%d", frame->align_flags, frame->align_adjust);
+ buf_printf (&out, " ]");
+
+ msg (level, "%s", out.data);
+ gc_free (&gc);
+}
+
+#define MTUDISC_NOT_SUPPORTED_MSG "--mtu-disc is not supported on this OS"
+
+void
+set_mtu_discover_type (int sd, int mtu_type)
+{
+ if (mtu_type >= 0)
+ {
+#if defined(HAVE_SETSOCKOPT) && defined(SOL_IP) && defined(IP_MTU_DISCOVER)
+ if (setsockopt
+ (sd, SOL_IP, IP_MTU_DISCOVER, &mtu_type, sizeof (mtu_type)))
+ msg (M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket",
+ mtu_type);
+#else
+ msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
+#endif
+ }
+}
+
+int
+translate_mtu_discover_type_name (const char *name)
+{
+#if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO)
+ if (!strcmp (name, "yes"))
+ return IP_PMTUDISC_DO;
+ if (!strcmp (name, "maybe"))
+ return IP_PMTUDISC_WANT;
+ if (!strcmp (name, "no"))
+ return IP_PMTUDISC_DONT;
+ msg (M_FATAL,
+ "invalid --mtu-disc type: '%s' -- valid types are 'yes', 'maybe', or 'no'",
+ name);
+#else
+ msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
+#endif
+ return -1; /* NOTREACHED */
+}
+
+#if EXTENDED_SOCKET_ERROR_CAPABILITY
+
+struct probehdr
+{
+ uint32_t ttl;
+ struct timeval tv;
+};
+
+const char *
+format_extended_socket_error (int fd, int *mtu, struct gc_arena *gc)
+{
+ int res;
+ struct probehdr rcvbuf;
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct sock_extended_err *e;
+ struct sockaddr_in addr;
+ struct buffer out = alloc_buf_gc (256, gc);
+ char *cbuf = (char *) gc_malloc (256, false, gc);
+
+ *mtu = 0;
+
+ while (true)
+ {
+ memset (&rcvbuf, -1, sizeof (rcvbuf));
+ iov.iov_base = &rcvbuf;
+ iov.iov_len = sizeof (rcvbuf);
+ msg.msg_name = (uint8_t *) &addr;
+ msg.msg_namelen = sizeof (addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = 256; /* size of cbuf */
+
+ res = recvmsg (fd, &msg, MSG_ERRQUEUE);
+ if (res < 0)
+ goto exit;
+
+ e = NULL;
+
+ for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_IP)
+ {
+ if (cmsg->cmsg_type == IP_RECVERR)
+ {
+ e = (struct sock_extended_err *) CMSG_DATA (cmsg);
+ }
+ else
+ {
+ buf_printf (&out ,"CMSG=%d|", cmsg->cmsg_type);
+ }
+ }
+ }
+ if (e == NULL)
+ {
+ buf_printf (&out, "NO-INFO|");
+ goto exit;
+ }
+
+ switch (e->ee_errno)
+ {
+ case ETIMEDOUT:
+ buf_printf (&out, "ETIMEDOUT|");
+ break;
+ case EMSGSIZE:
+ buf_printf (&out, "EMSGSIZE Path-MTU=%d|", e->ee_info);
+ *mtu = e->ee_info;
+ break;
+ case ECONNREFUSED:
+ buf_printf (&out, "ECONNREFUSED|");
+ break;
+ case EPROTO:
+ buf_printf (&out, "EPROTO|");
+ break;
+ case EHOSTUNREACH:
+ buf_printf (&out, "EHOSTUNREACH|");
+ break;
+ case ENETUNREACH:
+ buf_printf (&out, "ENETUNREACH|");
+ break;
+ case EACCES:
+ buf_printf (&out, "EACCES|");
+ break;
+ default:
+ buf_printf (&out, "UNKNOWN|");
+ break;
+ }
+ }
+
+ exit:
+ buf_rmtail (&out, '|');
+ return BSTR (&out);
+}
+
+void
+set_sock_extended_error_passing (int sd)
+{
+ int on = 1;
+ if (setsockopt (sd, SOL_IP, IP_RECVERR, &on, sizeof (on)))
+ msg (M_WARN | M_ERRNO,
+ "Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)");
+}
+
+#endif
diff --git a/mtu.h b/mtu.h
new file mode 100644
index 0000000..d9fa020
--- /dev/null
+++ b/mtu.h
@@ -0,0 +1,304 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MTU_H
+#define MTU_H
+
+#include "buffer.h"
+
+/*
+ *
+ * Packet maninipulation routes such as encrypt, decrypt, compress, decompress
+ * are passed a frame buffer that looks like this:
+ *
+ * [extra_frame bytes] [mtu bytes] [extra_frame_bytes] [compression overflow bytes]
+ * ^
+ * Pointer passed to function points here so that routine
+ * can make use of extra_frame bytes before pointer
+ * to prepend headers, etc.
+ *
+ * extra_frame bytes is large enough for all encryption related overhead.
+ *
+ * mtu bytes will be the MTU size set in the ifconfig statement that configures
+ * the TUN or TAP device such as:
+ *
+ * ifconfig $1 10.1.0.2 pointopoint 10.1.0.1 mtu 1450
+ *
+ * Compression overflow bytes is the worst-case size expansion that would be
+ * expected if we tried to compress mtu + extra_frame bytes of uncompressible data.
+ */
+
+/*
+ * Standard ethernet MTU
+ */
+#define ETHERNET_MTU 1500
+
+/*
+ * It is a fatal error if mtu is less than
+ * this value for tun device.
+ */
+#define TUN_MTU_MIN 100
+
+/*
+ * Default MTU of network over which tunnel data will pass by TCP/UDP.
+ */
+#define LINK_MTU_DEFAULT 1500
+
+/*
+ * Default MTU of tunnel device.
+ */
+#define TUN_MTU_DEFAULT 1500
+
+/*
+ * MTU Defaults for TAP devices
+ */
+#define TAP_MTU_EXTRA_DEFAULT 32
+
+/*
+ * Default MSSFIX value, used for reducing TCP MTU size
+ */
+#define MSSFIX_DEFAULT 1450
+
+/*
+ * Alignment of payload data such as IP packet or
+ * ethernet frame.
+ */
+#define PAYLOAD_ALIGN 4
+
+struct frame {
+ /*
+ * Maximum datagram size to be sent over the tunnel TCP/UDP channel.
+ */
+ int link_mtu;
+ int link_mtu_dynamic;
+
+ /*
+ * How many extra bytes might each subsystem (crypto, TLS, or, compression)
+ * add to frame in worst case?
+ *
+ * mtu + extra_frame = MTU of TCP/UDP transport
+ */
+ int extra_frame;
+
+ /*
+ * Worst case size added to internal buffer due to functions
+ * such as compression which can potentially expand the size of uncompressible
+ * data.
+ */
+ int extra_buffer;
+
+ /*
+ * Max number of bytes in excess of tun mtu size that we might read
+ * or write from TUN/TAP device.
+ */
+ int extra_tun;
+
+ /*
+ * Max number of bytes in excess of link mtu size that we might read
+ * or write from UDP/TCP link.
+ */
+ int extra_link;
+
+ /*
+ * Alignment control
+ */
+# define FRAME_HEADROOM_MARKER_DECRYPT (1<<0)
+# define FRAME_HEADROOM_MARKER_FRAGMENT (1<<1)
+# define FRAME_HEADROOM_MARKER_READ_LINK (1<<2)
+# define FRAME_HEADROOM_MARKER_READ_STREAM (1<<3)
+ unsigned int align_flags;
+ int align_adjust;
+};
+
+/* Routines which read struct frame should use the macros below */
+
+/*
+ * Overhead added to packet payload due to encapsulation
+ */
+#define EXTRA_FRAME(f) ((f)->extra_frame)
+
+/*
+ * Delta between tun payload size and final TCP/UDP datagram size
+ * (not including extra_link additions)
+ */
+#define TUN_LINK_DELTA(f) ((f)->extra_frame + (f)->extra_tun)
+
+/*
+ * This is the size to "ifconfig" the tun or tap device.
+ */
+#define TUN_MTU_SIZE(f) ((f)->link_mtu - TUN_LINK_DELTA(f))
+#define TUN_MTU_SIZE_DYNAMIC(f) ((f)->link_mtu_dynamic - TUN_LINK_DELTA(f))
+
+/*
+ * This is the maximum packet size that we need to be able to
+ * read from or write to a tun or tap device. For example,
+ * a tap device ifconfiged to an MTU of 1200 might actually want
+ * to return a packet size of 1214 on a read().
+ */
+#define PAYLOAD_SIZE(f) ((f)->link_mtu - (f)->extra_frame)
+#define PAYLOAD_SIZE_DYNAMIC(f) ((f)->link_mtu_dynamic - (f)->extra_frame)
+
+/*
+ * Max size of a payload packet after encryption, compression, etc.
+ * overhead is added.
+ */
+#define EXPANDED_SIZE(f) ((f)->link_mtu)
+#define EXPANDED_SIZE_DYNAMIC(f) ((f)->link_mtu_dynamic)
+#define EXPANDED_SIZE_MIN(f) (TUN_MTU_MIN + TUN_LINK_DELTA(f))
+
+/*
+ * These values are used as maximum size constraints
+ * on read() or write() from TUN/TAP device or TCP/UDP port.
+ */
+#define MAX_RW_SIZE_TUN(f) (PAYLOAD_SIZE(f))
+#define MAX_RW_SIZE_LINK(f) (EXPANDED_SIZE(f) + (f)->extra_link)
+
+/*
+ * Control buffer headroom allocations to allow for efficient prepending.
+ */
+#define FRAME_HEADROOM_BASE(f) (TUN_LINK_DELTA(f) + (f)->extra_buffer + (f)->extra_link)
+#define FRAME_HEADROOM(f) frame_headroom(f, 0)
+#define FRAME_HEADROOM_ADJ(f, fm) frame_headroom(f, fm)
+
+/*
+ * Max size of a buffer used to build a packet for output to
+ * the TCP/UDP port.
+ */
+#define BUF_SIZE(f) (TUN_MTU_SIZE(f) + FRAME_HEADROOM_BASE(f) * 2)
+
+/*
+ * Function prototypes.
+ */
+
+void frame_finalize (struct frame *frame,
+ bool link_mtu_defined,
+ int link_mtu,
+ bool tun_mtu_defined,
+ int tun_mtu);
+
+void frame_subtract_extra (struct frame *frame, const struct frame *src);
+
+void frame_print (const struct frame *frame,
+ int level,
+ const char *prefix);
+
+void set_mtu_discover_type (int sd, int mtu_type);
+int translate_mtu_discover_type_name (const char *name);
+
+/*
+ * frame_set_mtu_dynamic and flags
+ */
+
+#define SET_MTU_TUN (1<<0) /* use tun/tap rather than link sizing */
+#define SET_MTU_UPPER_BOUND (1<<1) /* only decrease dynamic MTU */
+
+void frame_set_mtu_dynamic (struct frame *frame, int mtu, unsigned int flags);
+
+/*
+ * allocate a buffer for socket or tun layer
+ */
+void alloc_buf_sock_tun (struct buffer *buf,
+ const struct frame *frame,
+ const bool tuntap_buffer,
+ const unsigned int align_mask);
+
+/*
+ * EXTENDED_SOCKET_ERROR_CAPABILITY functions -- print extra error info
+ * on socket errors, such as PMTU size. As of 2003.05.11, only works
+ * on Linux 2.4+.
+ */
+
+#if EXTENDED_SOCKET_ERROR_CAPABILITY
+
+void set_sock_extended_error_passing (int sd);
+const char *format_extended_socket_error (int fd, int *mtu, struct gc_arena *gc);
+
+#endif
+
+/*
+ * Calculate a starting offset into a buffer object, dealing with
+ * headroom and alignment issues.
+ */
+static inline int
+frame_headroom (const struct frame *f, const unsigned int flag_mask)
+{
+ const int offset = FRAME_HEADROOM_BASE (f);
+ const int adjust = (flag_mask & f->align_flags) ? f->align_adjust : 0;
+ const int delta = ((PAYLOAD_ALIGN << 24) - (offset + adjust)) & (PAYLOAD_ALIGN - 1);
+ return offset + delta;
+}
+
+/*
+ * frame member adjustment functions
+ */
+
+static inline void
+frame_add_to_extra_frame (struct frame *frame, const int increment)
+{
+ frame->extra_frame += increment;
+}
+
+static inline void
+frame_add_to_extra_tun (struct frame *frame, const int increment)
+{
+ frame->extra_tun += increment;
+}
+
+static inline void
+frame_add_to_extra_link (struct frame *frame, const int increment)
+{
+ frame->extra_link += increment;
+}
+
+static inline void
+frame_add_to_extra_buffer (struct frame *frame, const int increment)
+{
+ frame->extra_buffer += increment;
+}
+
+static inline void
+frame_add_to_align_adjust (struct frame *frame, const int increment)
+{
+ frame->align_adjust += increment;
+}
+
+static inline void
+frame_align_to_extra_frame (struct frame *frame)
+{
+ frame->align_adjust = frame->extra_frame + frame->extra_link;
+}
+
+static inline void
+frame_or_align_flags (struct frame *frame, const unsigned int flag_mask)
+{
+ frame->align_flags |= flag_mask;
+}
+
+static inline bool
+frame_defined (const struct frame *frame)
+{
+ return frame->link_mtu > 0;
+}
+
+#endif
diff --git a/mudp.c b/mudp.c
new file mode 100644
index 0000000..a478b29
--- /dev/null
+++ b/mudp.c
@@ -0,0 +1,277 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "multi.h"
+#include "forward-inline.h"
+
+#include "memdbg.h"
+
+/*
+ * Get a client instance based on real address. If
+ * the instance doesn't exist, create it while
+ * maintaining real address hash table atomicity.
+ */
+
+struct multi_instance *
+multi_get_create_instance_udp (struct multi_context *m)
+{
+ struct gc_arena gc = gc_new ();
+ struct mroute_addr real;
+ struct multi_instance *mi = NULL;
+ struct hash *hash = m->hash;
+
+ if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true))
+ {
+ struct hash_element *he;
+ const uint32_t hv = hash_value (hash, &real);
+ struct hash_bucket *bucket = hash_bucket (hash, hv);
+
+ he = hash_lookup_fast (hash, bucket, &real, hv);
+
+ if (he)
+ {
+ mi = (struct multi_instance *) he->value;
+ }
+ else
+ {
+ if (!m->top.c2.tls_auth_standalone
+ || tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf))
+ {
+ if (frequency_limit_event_allowed (m->new_connection_limiter))
+ {
+ mi = multi_create_instance (m, &real);
+ if (mi)
+ {
+ hash_add_fast (hash, bucket, &mi->real, hv, mi);
+ mi->did_real_hash = true;
+ }
+ }
+ else
+ {
+ msg (D_MULTI_ERRORS,
+ "MULTI: Connection from %s would exceed new connection frequency limit as controlled by --connect-freq",
+ mroute_addr_print (&real, &gc));
+ }
+ }
+ }
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_MULTI_DEBUG))
+ {
+ const char *status;
+
+ if (he && mi)
+ status = "[succeeded]";
+ else if (!he && mi)
+ status = "[created]";
+ else
+ status = "[failed]";
+
+ dmsg (D_MULTI_DEBUG, "GET INST BY REAL: %s %s",
+ mroute_addr_print (&real, &gc),
+ status);
+ }
+#endif
+ }
+
+ gc_free (&gc);
+ ASSERT (!(mi && mi->halt));
+ return mi;
+}
+
+/*
+ * Send a packet to TCP/UDP socket.
+ */
+static inline void
+multi_process_outgoing_link (struct multi_context *m, const unsigned int mpp_flags)
+{
+ struct multi_instance *mi = multi_process_outgoing_link_pre (m);
+ if (mi)
+ multi_process_outgoing_link_dowork (m, mi, mpp_flags);
+}
+
+/*
+ * Process an I/O event.
+ */
+static void
+multi_process_io_udp (struct multi_context *m)
+{
+ const unsigned int status = m->top.c2.event_set_status;
+ const unsigned int mpp_flags = m->top.c2.fast_io
+ ? (MPP_CONDITIONAL_PRE_SELECT | MPP_CLOSE_ON_SIGNAL)
+ : (MPP_PRE_SELECT | MPP_CLOSE_ON_SIGNAL);
+
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ char buf[16];
+ buf[0] = 0;
+ if (status & SOCKET_READ)
+ strcat (buf, "SR/");
+ else if (status & SOCKET_WRITE)
+ strcat (buf, "SW/");
+ else if (status & TUN_READ)
+ strcat (buf, "TR/");
+ else if (status & TUN_WRITE)
+ strcat (buf, "TW/");
+ printf ("IO %s\n", buf);
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+ if (status & (MANAGEMENT_READ|MANAGEMENT_WRITE))
+ {
+ ASSERT (management);
+ management_io (management);
+ }
+#endif
+
+ /* UDP port ready to accept write */
+ if (status & SOCKET_WRITE)
+ {
+ multi_process_outgoing_link (m, mpp_flags);
+ }
+ /* TUN device ready to accept write */
+ else if (status & TUN_WRITE)
+ {
+ multi_process_outgoing_tun (m, mpp_flags);
+ }
+ /* Incoming data on UDP port */
+ else if (status & SOCKET_READ)
+ {
+ read_incoming_link (&m->top);
+ multi_release_io_lock (m);
+ if (!IS_SIG (&m->top))
+ multi_process_incoming_link (m, NULL, mpp_flags);
+ }
+ /* Incoming data on TUN device */
+ else if (status & TUN_READ)
+ {
+ read_incoming_tun (&m->top);
+ multi_release_io_lock (m);
+ if (!IS_SIG (&m->top))
+ multi_process_incoming_tun (m, mpp_flags);
+ }
+}
+
+/*
+ * Return the io_wait() flags appropriate for
+ * a point-to-multipoint tunnel.
+ */
+static inline unsigned int
+p2mp_iow_flags (const struct multi_context *m)
+{
+ unsigned int flags = IOW_WAIT_SIGNAL;
+ if (m->pending)
+ {
+ if (TUN_OUT (&m->pending->context))
+ flags |= IOW_TO_TUN;
+ if (LINK_OUT (&m->pending->context))
+ flags |= IOW_TO_LINK;
+ }
+ else if (mbuf_defined (m->mbuf))
+ flags |= IOW_MBUF;
+ else
+ flags |= IOW_READ;
+
+ return flags;
+}
+
+/*
+ * Top level event loop for single-threaded operation.
+ * UDP mode.
+ */
+static void
+tunnel_server_udp_single_threaded (struct context *top)
+{
+ struct multi_context multi;
+
+ top->mode = CM_TOP;
+ context_clear_2 (top);
+
+ /* initialize top-tunnel instance */
+ init_instance_handle_signals (top, top->es, CC_HARD_USR1_TO_HUP);
+ if (IS_SIG (top))
+ return;
+
+ /* initialize global multi_context object */
+ multi_init (&multi, top, false, MC_SINGLE_THREADED);
+
+ /* initialize our cloned top object */
+ multi_top_init (&multi, top, true);
+
+ /* initialize management interface */
+ init_management_callback_multi (&multi);
+
+ /* finished with initialization */
+ initialization_sequence_completed (top, ISC_SERVER); /* --mode server --proto udp */
+
+ /* per-packet event loop */
+ while (true)
+ {
+ perf_push (PERF_EVENT_LOOP);
+
+ /* set up and do the io_wait() */
+ multi_get_timeout (&multi, &multi.top.c2.timeval);
+ io_wait (&multi.top, p2mp_iow_flags (&multi));
+ MULTI_CHECK_SIG (&multi);
+
+ /* check on status of coarse timers */
+ multi_process_per_second_timers (&multi);
+
+ /* timeout? */
+ if (multi.top.c2.event_set_status == ES_TIMEOUT)
+ {
+ multi_process_timeout (&multi, MPP_PRE_SELECT|MPP_CLOSE_ON_SIGNAL);
+ }
+ else
+ {
+ /* process I/O */
+ multi_process_io_udp (&multi);
+ MULTI_CHECK_SIG (&multi);
+ }
+
+ perf_pop ();
+ }
+
+ /* shut down management interface */
+ uninit_management_callback_multi (&multi);
+
+ /* save ifconfig-pool */
+ multi_ifconfig_pool_persist (&multi, true);
+
+ /* tear down tunnel instance (unless --persist-tun) */
+ multi_uninit (&multi);
+ multi_top_free (&multi);
+ close_instance (top);
+}
+
+void
+tunnel_server_udp (struct context *top)
+{
+ tunnel_server_udp_single_threaded (top);
+}
+
+#endif
diff --git a/mudp.h b/mudp.h
new file mode 100644
index 0000000..dc9cfde
--- /dev/null
+++ b/mudp.h
@@ -0,0 +1,42 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * UDP specific code for --mode server
+ */
+
+#ifndef MUDP_H
+#define MUDP_H
+
+#if P2MP_SERVER
+
+struct context;
+struct multi_context;
+
+void tunnel_server_udp (struct context *top);
+
+struct multi_instance *multi_get_create_instance_udp (struct multi_context *m);
+
+#endif
+#endif
diff --git a/multi.c b/multi.c
new file mode 100644
index 0000000..22c0a3f
--- /dev/null
+++ b/multi.c
@@ -0,0 +1,2692 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "multi.h"
+#include "push.h"
+#include "misc.h"
+#include "otime.h"
+#include "gremlin.h"
+
+#include "memdbg.h"
+
+#include "forward-inline.h"
+#include "pf-inline.h"
+
+/*#define MULTI_DEBUG_EVENT_LOOP*/
+
+#ifdef MULTI_DEBUG_EVENT_LOOP
+static const char *
+id (struct multi_instance *mi)
+{
+ if (mi)
+ return tls_common_name (mi->context.c2.tls_multi, false);
+ else
+ return "NULL";
+}
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+static void
+set_cc_config (struct multi_instance *mi, struct buffer_list *cc_config)
+{
+ if (mi->cc_config)
+ buffer_list_free (mi->cc_config);
+ mi->cc_config = cc_config;
+}
+#endif
+
+static bool
+learn_address_script (const struct multi_context *m,
+ const struct multi_instance *mi,
+ const char *op,
+ const struct mroute_addr *addr)
+{
+ struct gc_arena gc = gc_new ();
+ struct env_set *es;
+ bool ret = true;
+ struct plugin_list *plugins;
+
+ /* get environmental variable source */
+ if (mi && mi->context.c2.es)
+ es = mi->context.c2.es;
+ else
+ es = env_set_create (&gc);
+
+ /* get plugin source */
+ if (mi)
+ plugins = mi->context.plugins;
+ else
+ plugins = m->top.plugins;
+
+ if (plugin_defined (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS))
+ {
+ struct argv argv = argv_new ();
+ argv_printf (&argv, "%s %s",
+ op,
+ mroute_addr_print (addr, &gc));
+ if (mi)
+ argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+ if (plugin_call (plugins, OPENVPN_PLUGIN_LEARN_ADDRESS, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ {
+ msg (M_WARN, "WARNING: learn-address plugin call failed");
+ ret = false;
+ }
+ argv_reset (&argv);
+ }
+
+ if (m->top.options.learn_address_script)
+ {
+ struct argv argv = argv_new ();
+ setenv_str (es, "script_type", "learn-address");
+ argv_printf (&argv, "%sc %s %s",
+ m->top.options.learn_address_script,
+ op,
+ mroute_addr_print (addr, &gc));
+ if (mi)
+ argv_printf_cat (&argv, "%s", tls_common_name (mi->context.c2.tls_multi, false));
+ if (!openvpn_run_script (&argv, es, 0, "--learn-address"))
+ ret = false;
+ argv_reset (&argv);
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+void
+multi_ifconfig_pool_persist (struct multi_context *m, bool force)
+{
+ /* write pool data to file */
+ if (m->ifconfig_pool
+ && m->top.c1.ifconfig_pool_persist
+ && (force || ifconfig_pool_write_trigger (m->top.c1.ifconfig_pool_persist)))
+ {
+ ifconfig_pool_write (m->top.c1.ifconfig_pool_persist, m->ifconfig_pool);
+ }
+}
+
+static void
+multi_reap_range (const struct multi_context *m,
+ int start_bucket,
+ int end_bucket)
+{
+ struct gc_arena gc = gc_new ();
+ struct hash_iterator hi;
+ struct hash_element *he;
+
+ if (start_bucket < 0)
+ {
+ start_bucket = 0;
+ end_bucket = hash_n_buckets (m->vhash);
+ }
+
+ dmsg (D_MULTI_DEBUG, "MULTI: REAP range %d -> %d", start_bucket, end_bucket);
+ hash_iterator_init_range (m->vhash, &hi, start_bucket, end_bucket);
+ while ((he = hash_iterator_next (&hi)) != NULL)
+ {
+ struct multi_route *r = (struct multi_route *) he->value;
+ if (!multi_route_defined (m, r))
+ {
+ dmsg (D_MULTI_DEBUG, "MULTI: REAP DEL %s",
+ mroute_addr_print (&r->addr, &gc));
+ learn_address_script (m, NULL, "delete", &r->addr);
+ multi_route_del (r);
+ hash_iterator_delete_element (&hi);
+ }
+ }
+ hash_iterator_free (&hi);
+ gc_free (&gc);
+}
+
+static void
+multi_reap_all (const struct multi_context *m)
+{
+ multi_reap_range (m, -1, 0);
+}
+
+static struct multi_reap *
+multi_reap_new (int buckets_per_pass)
+{
+ struct multi_reap *mr;
+ ALLOC_OBJ (mr, struct multi_reap);
+ mr->bucket_base = 0;
+ mr->buckets_per_pass = buckets_per_pass;
+ mr->last_call = now;
+ return mr;
+}
+
+void
+multi_reap_process_dowork (const struct multi_context *m)
+{
+ struct multi_reap *mr = m->reaper;
+ if (mr->bucket_base >= hash_n_buckets (m->vhash))
+ mr->bucket_base = 0;
+ multi_reap_range (m, mr->bucket_base, mr->bucket_base + mr->buckets_per_pass);
+ mr->bucket_base += mr->buckets_per_pass;
+ mr->last_call = now;
+}
+
+static void
+multi_reap_free (struct multi_reap *mr)
+{
+ free (mr);
+}
+
+/*
+ * How many buckets in vhash to reap per pass.
+ */
+static int
+reap_buckets_per_pass (int n_buckets)
+{
+ return constrain_int (n_buckets / REAP_DIVISOR, REAP_MIN, REAP_MAX);
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static uint32_t
+cid_hash_function (const void *key, uint32_t iv)
+{
+ const unsigned long *k = (const unsigned long *)key;
+ return (uint32_t) *k;
+}
+
+static bool
+cid_compare_function (const void *key1, const void *key2)
+{
+ const unsigned long *k1 = (const unsigned long *)key1;
+ const unsigned long *k2 = (const unsigned long *)key2;
+ return *k1 == *k2;
+}
+
+#endif
+
+/*
+ * Main initialization function, init multi_context object.
+ */
+void
+multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int thread_mode)
+{
+ int dev = DEV_TYPE_UNDEF;
+
+ msg (D_MULTI_LOW, "MULTI: multi_init called, r=%d v=%d",
+ t->options.real_hash_size,
+ t->options.virtual_hash_size);
+
+ /*
+ * Get tun/tap/null device type
+ */
+ dev = dev_type_enum (t->options.dev, t->options.dev_type);
+
+ /*
+ * Init our multi_context object.
+ */
+ CLEAR (*m);
+
+ m->thread_mode = thread_mode;
+
+ /*
+ * Real address hash table (source port number is
+ * considered to be part of the address). Used
+ * to determine which client sent an incoming packet
+ * which is seen on the TCP/UDP socket.
+ */
+ m->hash = hash_init (t->options.real_hash_size,
+ get_random (),
+ mroute_addr_hash_function,
+ mroute_addr_compare_function);
+
+ /*
+ * Virtual address hash table. Used to determine
+ * which client to route a packet to.
+ */
+ m->vhash = hash_init (t->options.virtual_hash_size,
+ get_random (),
+ mroute_addr_hash_function,
+ mroute_addr_compare_function);
+
+ /*
+ * This hash table is a clone of m->hash but with a
+ * bucket size of one so that it can be used
+ * for fast iteration through the list.
+ */
+ m->iter = hash_init (1,
+ get_random (),
+ mroute_addr_hash_function,
+ mroute_addr_compare_function);
+
+#ifdef MANAGEMENT_DEF_AUTH
+ m->cid_hash = hash_init (t->options.real_hash_size,
+ 0,
+ cid_hash_function,
+ cid_compare_function);
+#endif
+
+ /*
+ * This is our scheduler, for time-based wakeup
+ * events.
+ */
+ m->schedule = schedule_init ();
+
+ /*
+ * Limit frequency of incoming connections to control
+ * DoS.
+ */
+ m->new_connection_limiter = frequency_limit_init (t->options.cf_max,
+ t->options.cf_per);
+
+ /*
+ * Allocate broadcast/multicast buffer list
+ */
+ m->mbuf = mbuf_init (t->options.n_bcast_buf);
+
+ /*
+ * Different status file format options are available
+ */
+ m->status_file_version = t->options.status_file_version;
+
+ /*
+ * Possibly allocate an ifconfig pool, do it
+ * differently based on whether a tun or tap style
+ * tunnel.
+ */
+ if (t->options.ifconfig_pool_defined)
+ {
+ if (dev == DEV_TYPE_TAP)
+ {
+ m->ifconfig_pool = ifconfig_pool_init (IFCONFIG_POOL_INDIV,
+ t->options.ifconfig_pool_start,
+ t->options.ifconfig_pool_end,
+ t->options.duplicate_cn);
+ }
+ else if (dev == DEV_TYPE_TUN)
+ {
+ m->ifconfig_pool = ifconfig_pool_init (
+ (t->options.topology == TOP_NET30) ? IFCONFIG_POOL_30NET : IFCONFIG_POOL_INDIV,
+ t->options.ifconfig_pool_start,
+ t->options.ifconfig_pool_end,
+ t->options.duplicate_cn);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ /* reload pool data from file */
+ if (t->c1.ifconfig_pool_persist)
+ ifconfig_pool_read (t->c1.ifconfig_pool_persist, m->ifconfig_pool);
+ }
+
+ /*
+ * Help us keep track of routing table.
+ */
+ m->route_helper = mroute_helper_init (MULTI_CACHE_ROUTE_TTL);
+
+ /*
+ * Initialize route and instance reaper.
+ */
+ m->reaper = multi_reap_new (reap_buckets_per_pass (t->options.virtual_hash_size));
+
+ /*
+ * Get local ifconfig address
+ */
+ CLEAR (m->local);
+ ASSERT (t->c1.tuntap);
+ mroute_extract_in_addr_t (&m->local, t->c1.tuntap->local);
+
+ /*
+ * Per-client limits
+ */
+ m->max_clients = t->options.max_clients;
+
+ /*
+ * Initialize multi-socket TCP I/O wait object
+ */
+ if (tcp_mode)
+ m->mtcp = multi_tcp_init (t->options.max_clients, &m->max_clients);
+ m->tcp_queue_limit = t->options.tcp_queue_limit;
+
+ /*
+ * Allow client <-> client communication, without going through
+ * tun/tap interface and network stack?
+ */
+ m->enable_c2c = t->options.enable_c2c;
+}
+
+const char *
+multi_instance_string (const struct multi_instance *mi, bool null, struct gc_arena *gc)
+{
+ if (mi)
+ {
+ struct buffer out = alloc_buf_gc (256, gc);
+ const char *cn = tls_common_name (mi->context.c2.tls_multi, true);
+
+ if (cn)
+ buf_printf (&out, "%s/", cn);
+ buf_printf (&out, "%s", mroute_addr_print (&mi->real, gc));
+ return BSTR (&out);
+ }
+ else if (null)
+ return NULL;
+ else
+ return "UNDEF";
+}
+
+void
+generate_prefix (struct multi_instance *mi)
+{
+ mi->msg_prefix = multi_instance_string (mi, true, &mi->gc);
+ set_prefix (mi);
+}
+
+void
+ungenerate_prefix (struct multi_instance *mi)
+{
+ mi->msg_prefix = NULL;
+ set_prefix (mi);
+}
+
+static const char *
+mi_prefix (const struct multi_instance *mi)
+{
+ if (mi && mi->msg_prefix)
+ return mi->msg_prefix;
+ else
+ return "UNDEF_I";
+}
+
+/*
+ * Tell the route helper about deleted iroutes so
+ * that it can update its mask of currently used
+ * CIDR netlengths.
+ */
+static void
+multi_del_iroutes (struct multi_context *m,
+ struct multi_instance *mi)
+{
+ const struct iroute *ir;
+ if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+ mroute_helper_del_iroute (m->route_helper, ir);
+ }
+}
+
+static void
+setenv_stats (struct context *c)
+{
+ setenv_counter (c->c2.es, "bytes_received", c->c2.link_read_bytes);
+ setenv_counter (c->c2.es, "bytes_sent", c->c2.link_write_bytes);
+}
+
+static void
+multi_client_disconnect_setenv (struct multi_context *m,
+ struct multi_instance *mi)
+{
+ /* setenv client real IP address */
+ setenv_trusted (mi->context.c2.es, get_link_socket_info (&mi->context));
+
+ /* setenv stats */
+ setenv_stats (&mi->context);
+
+ /* setenv connection duration */
+ {
+ const unsigned int duration = (unsigned int) now - mi->created;
+ setenv_unsigned (mi->context.c2.es, "time_duration", duration);
+ }
+}
+
+static void
+multi_client_disconnect_script (struct multi_context *m,
+ struct multi_instance *mi)
+{
+ if ((mi->context.c2.context_auth == CAS_SUCCEEDED && mi->connection_established_flag)
+ || mi->context.c2.context_auth == CAS_PARTIAL)
+ {
+ multi_client_disconnect_setenv (m, mi);
+
+ if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT))
+ {
+ if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_DISCONNECT, NULL, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ msg (M_WARN, "WARNING: client-disconnect plugin call failed");
+ }
+
+ if (mi->context.options.client_disconnect_script)
+ {
+ struct argv argv = argv_new ();
+ setenv_str (mi->context.c2.es, "script_type", "client-disconnect");
+ argv_printf (&argv, "%sc", mi->context.options.client_disconnect_script);
+ openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-disconnect");
+ argv_reset (&argv);
+ }
+#ifdef MANAGEMENT_DEF_AUTH
+ if (management)
+ management_notify_client_close (management, &mi->context.c2.mda_context, mi->context.c2.es);
+#endif
+
+ }
+}
+
+void
+multi_close_instance (struct multi_context *m,
+ struct multi_instance *mi,
+ bool shutdown)
+{
+ perf_push (PERF_MULTI_CLOSE_INSTANCE);
+
+ ASSERT (!mi->halt);
+ mi->halt = true;
+
+ dmsg (D_MULTI_DEBUG, "MULTI: multi_close_instance called");
+
+ /* adjust current client connection count */
+ m->n_clients += mi->n_clients_delta;
+ mi->n_clients_delta = 0;
+
+ /* prevent dangling pointers */
+ if (m->pending == mi)
+ multi_set_pending (m, NULL);
+ if (m->earliest_wakeup == mi)
+ m->earliest_wakeup = NULL;
+
+ if (!shutdown)
+ {
+ if (mi->did_real_hash)
+ {
+ ASSERT (hash_remove (m->hash, &mi->real));
+ }
+ if (mi->did_iter)
+ {
+ ASSERT (hash_remove (m->iter, &mi->real));
+ }
+#ifdef MANAGEMENT_DEF_AUTH
+ if (mi->did_cid_hash)
+ {
+ ASSERT (hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid));
+ }
+#endif
+
+ schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
+
+ ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, false);
+
+ if (mi->did_iroutes)
+ {
+ multi_del_iroutes (m, mi);
+ mi->did_iroutes = false;
+ }
+
+ if (m->mtcp)
+ multi_tcp_dereference_instance (m->mtcp, mi);
+
+ mbuf_dereference_instance (m->mbuf, mi);
+ }
+
+#ifdef MANAGEMENT_DEF_AUTH
+ set_cc_config (mi, NULL);
+#endif
+
+ multi_client_disconnect_script (m, mi);
+
+ if (mi->did_open_context)
+ close_context (&mi->context, SIGTERM, CC_GC_FREE);
+
+ multi_tcp_instance_specific_free (mi);
+
+ ungenerate_prefix (mi);
+
+ /*
+ * Don't actually delete the instance memory allocation yet,
+ * because virtual routes may still point to it. Let the
+ * vhash reaper deal with it.
+ */
+ multi_instance_dec_refcount (mi);
+
+ perf_pop ();
+}
+
+/*
+ * Called on shutdown or restart.
+ */
+void
+multi_uninit (struct multi_context *m)
+{
+ if (m->thread_mode & MC_WORK_THREAD)
+ {
+ multi_top_free (m);
+ m->thread_mode = MC_UNDEF;
+ }
+ else if (m->thread_mode)
+ {
+ if (m->hash)
+ {
+ struct hash_iterator hi;
+ struct hash_element *he;
+
+ hash_iterator_init (m->iter, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct multi_instance *mi = (struct multi_instance *) he->value;
+ mi->did_iter = false;
+ multi_close_instance (m, mi, true);
+ }
+ hash_iterator_free (&hi);
+
+ multi_reap_all (m);
+
+ hash_free (m->hash);
+ hash_free (m->vhash);
+ hash_free (m->iter);
+#ifdef MANAGEMENT_DEF_AUTH
+ hash_free (m->cid_hash);
+#endif
+ m->hash = NULL;
+
+ schedule_free (m->schedule);
+ mbuf_free (m->mbuf);
+ ifconfig_pool_free (m->ifconfig_pool);
+ frequency_limit_free (m->new_connection_limiter);
+ multi_reap_free (m->reaper);
+ mroute_helper_free (m->route_helper);
+ multi_tcp_free (m->mtcp);
+ m->thread_mode = MC_UNDEF;
+ }
+ }
+}
+
+/*
+ * Create a client instance object for a newly connected client.
+ */
+struct multi_instance *
+multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
+{
+ struct gc_arena gc = gc_new ();
+ struct multi_instance *mi;
+
+ perf_push (PERF_MULTI_CREATE_INSTANCE);
+
+ msg (D_MULTI_LOW, "MULTI: multi_create_instance called");
+
+ ALLOC_OBJ_CLEAR (mi, struct multi_instance);
+
+ mi->gc = gc_new ();
+ multi_instance_inc_refcount (mi);
+ mi->vaddr_handle = -1;
+ mi->created = now;
+ mroute_addr_init (&mi->real);
+
+ if (real)
+ {
+ mi->real = *real;
+ generate_prefix (mi);
+ }
+
+ mi->did_open_context = true;
+ inherit_context_child (&mi->context, &m->top);
+ if (IS_SIG (&mi->context))
+ goto err;
+
+ mi->context.c2.context_auth = CAS_PENDING;
+
+ if (hash_n_elements (m->hash) >= m->max_clients)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: new incoming connection would exceed maximum number of clients (%d)", m->max_clients);
+ goto err;
+ }
+
+ if (!real) /* TCP mode? */
+ {
+ if (!multi_tcp_instance_specific_init (m, mi))
+ goto err;
+ generate_prefix (mi);
+ }
+
+ if (!hash_add (m->iter, &mi->real, mi, false))
+ {
+ msg (D_MULTI_LOW, "MULTI: unable to add real address [%s] to iterator hash table",
+ mroute_addr_print (&mi->real, &gc));
+ goto err;
+ }
+ mi->did_iter = true;
+
+#ifdef MANAGEMENT_DEF_AUTH
+ do {
+ mi->context.c2.mda_context.cid = m->cid_counter++;
+ } while (!hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false));
+ mi->did_cid_hash = true;
+#endif
+
+ mi->context.c2.push_reply_deferred = true;
+
+ if (!multi_process_post (m, mi, MPP_PRE_SELECT))
+ {
+ msg (D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization");
+ goto err;
+ }
+
+ perf_pop ();
+ gc_free (&gc);
+ return mi;
+
+ err:
+ multi_close_instance (m, mi, false);
+ perf_pop ();
+ gc_free (&gc);
+ return NULL;
+}
+
+/*
+ * Dump tables -- triggered by SIGUSR2.
+ * If status file is defined, write to file.
+ * If status file is NULL, write to syslog.
+ */
+void
+multi_print_status (struct multi_context *m, struct status_output *so, const int version)
+{
+ if (m->hash)
+ {
+ struct gc_arena gc_top = gc_new ();
+ struct hash_iterator hi;
+ const struct hash_element *he;
+
+ status_reset (so);
+
+ if (version == 1) /* WAS: m->status_file_version */
+ {
+ /*
+ * Status file version 1
+ */
+ status_printf (so, "OpenVPN CLIENT LIST");
+ status_printf (so, "Updated,%s", time_string (0, 0, false, &gc_top));
+ status_printf (so, "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since");
+ hash_iterator_init (m->hash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct gc_arena gc = gc_new ();
+ const struct multi_instance *mi = (struct multi_instance *) he->value;
+
+ if (!mi->halt)
+ {
+ status_printf (so, "%s,%s," counter_format "," counter_format ",%s",
+ tls_common_name (mi->context.c2.tls_multi, false),
+ mroute_addr_print (&mi->real, &gc),
+ mi->context.c2.link_read_bytes,
+ mi->context.c2.link_write_bytes,
+ time_string (mi->created, 0, false, &gc));
+ }
+ gc_free (&gc);
+ }
+ hash_iterator_free (&hi);
+
+ status_printf (so, "ROUTING TABLE");
+ status_printf (so, "Virtual Address,Common Name,Real Address,Last Ref");
+ hash_iterator_init (m->vhash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct gc_arena gc = gc_new ();
+ const struct multi_route *route = (struct multi_route *) he->value;
+
+ if (multi_route_defined (m, route))
+ {
+ const struct multi_instance *mi = route->instance;
+ const struct mroute_addr *ma = &route->addr;
+ char flags[2] = {0, 0};
+
+ if (route->flags & MULTI_ROUTE_CACHE)
+ flags[0] = 'C';
+ status_printf (so, "%s%s,%s,%s,%s",
+ mroute_addr_print (ma, &gc),
+ flags,
+ tls_common_name (mi->context.c2.tls_multi, false),
+ mroute_addr_print (&mi->real, &gc),
+ time_string (route->last_reference, 0, false, &gc));
+ }
+ gc_free (&gc);
+ }
+ hash_iterator_free (&hi);
+
+ status_printf (so, "GLOBAL STATS");
+ if (m->mbuf)
+ status_printf (so, "Max bcast/mcast queue length,%d",
+ mbuf_maximum_queued (m->mbuf));
+
+ status_printf (so, "END");
+ }
+ else if (version == 2 || version == 3)
+ {
+ const char sep = (version == 3) ? '\t' : ',';
+
+ /*
+ * Status file version 2 and 3
+ */
+ status_printf (so, "TITLE%c%s", sep, title_string);
+ status_printf (so, "TIME%c%s%c%u", sep, time_string (now, 0, false, &gc_top), sep, (unsigned int)now);
+ status_printf (so, "HEADER%cCLIENT_LIST%cCommon Name%cReal Address%cVirtual Address%cBytes Received%cBytes Sent%cConnected Since%cConnected Since (time_t)",
+ sep, sep, sep, sep, sep, sep, sep, sep);
+ hash_iterator_init (m->hash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct gc_arena gc = gc_new ();
+ const struct multi_instance *mi = (struct multi_instance *) he->value;
+
+ if (!mi->halt)
+ {
+ status_printf (so, "CLIENT_LIST%c%s%c%s%c%s%c" counter_format "%c" counter_format "%c%s%c%u",
+ sep, tls_common_name (mi->context.c2.tls_multi, false),
+ sep, mroute_addr_print (&mi->real, &gc),
+ sep, print_in_addr_t (mi->reporting_addr, IA_EMPTY_IF_UNDEF, &gc),
+ sep, mi->context.c2.link_read_bytes,
+ sep, mi->context.c2.link_write_bytes,
+ sep, time_string (mi->created, 0, false, &gc),
+ sep, (unsigned int)mi->created);
+ }
+ gc_free (&gc);
+ }
+ hash_iterator_free (&hi);
+
+ status_printf (so, "HEADER%cROUTING_TABLE%cVirtual Address%cCommon Name%cReal Address%cLast Ref%cLast Ref (time_t)",
+ sep, sep, sep, sep, sep, sep);
+ hash_iterator_init (m->vhash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct gc_arena gc = gc_new ();
+ const struct multi_route *route = (struct multi_route *) he->value;
+
+ if (multi_route_defined (m, route))
+ {
+ const struct multi_instance *mi = route->instance;
+ const struct mroute_addr *ma = &route->addr;
+ char flags[2] = {0, 0};
+
+ if (route->flags & MULTI_ROUTE_CACHE)
+ flags[0] = 'C';
+ status_printf (so, "ROUTING_TABLE%c%s%s%c%s%c%s%c%s%c%u",
+ sep, mroute_addr_print (ma, &gc), flags,
+ sep, tls_common_name (mi->context.c2.tls_multi, false),
+ sep, mroute_addr_print (&mi->real, &gc),
+ sep, time_string (route->last_reference, 0, false, &gc),
+ sep, (unsigned int)route->last_reference);
+ }
+ gc_free (&gc);
+ }
+ hash_iterator_free (&hi);
+
+ if (m->mbuf)
+ status_printf (so, "GLOBAL_STATS%cMax bcast/mcast queue length%c%d",
+ sep, sep, mbuf_maximum_queued (m->mbuf));
+
+ status_printf (so, "END");
+ }
+ else
+ {
+ status_printf (so, "ERROR: bad status format version number");
+ }
+
+#ifdef PACKET_TRUNCATION_CHECK
+ {
+ status_printf (so, "HEADER,ERRORS,Common Name,TUN Read Trunc,TUN Write Trunc,Pre-encrypt Trunc,Post-decrypt Trunc");
+ hash_iterator_init (m->hash, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct gc_arena gc = gc_new ();
+ const struct multi_instance *mi = (struct multi_instance *) he->value;
+
+ if (!mi->halt)
+ {
+ status_printf (so, "ERRORS,%s," counter_format "," counter_format "," counter_format "," counter_format,
+ tls_common_name (mi->context.c2.tls_multi, false),
+ m->top.c2.n_trunc_tun_read,
+ mi->context.c2.n_trunc_tun_write,
+ mi->context.c2.n_trunc_pre_encrypt,
+ mi->context.c2.n_trunc_post_decrypt);
+ }
+ gc_free (&gc);
+ }
+ hash_iterator_free (&hi);
+ }
+#endif
+
+ status_flush (so);
+ gc_free (&gc_top);
+ }
+}
+
+/*
+ * Learn a virtual address or route.
+ * The learn will fail if the learn address
+ * script/plugin fails. In this case the
+ * return value may be != mi.
+ * Return the instance which owns this route,
+ * or NULL if none.
+ */
+static struct multi_instance *
+multi_learn_addr (struct multi_context *m,
+ struct multi_instance *mi,
+ const struct mroute_addr *addr,
+ const unsigned int flags)
+{
+ struct hash_element *he;
+ const uint32_t hv = hash_value (m->vhash, addr);
+ struct hash_bucket *bucket = hash_bucket (m->vhash, hv);
+ struct multi_route *oldroute = NULL;
+ struct multi_instance *owner = NULL;
+
+ /* if route currently exists, get the instance which owns it */
+ he = hash_lookup_fast (m->vhash, bucket, addr, hv);
+ if (he)
+ oldroute = (struct multi_route *) he->value;
+ if (oldroute && multi_route_defined (m, oldroute))
+ owner = oldroute->instance;
+
+ /* do we need to add address to hash table? */
+ if ((!owner || owner != mi)
+ && mroute_learnable_address (addr)
+ && !mroute_addr_equal (addr, &m->local))
+ {
+ struct gc_arena gc = gc_new ();
+ struct multi_route *newroute;
+ bool learn_succeeded = false;
+
+ ALLOC_OBJ (newroute, struct multi_route);
+ newroute->addr = *addr;
+ newroute->instance = mi;
+ newroute->flags = flags;
+ newroute->last_reference = now;
+ newroute->cache_generation = 0;
+
+ /* The cache is invalidated when cache_generation is incremented */
+ if (flags & MULTI_ROUTE_CACHE)
+ newroute->cache_generation = m->route_helper->cache_generation;
+
+ if (oldroute) /* route already exists? */
+ {
+ if (route_quota_test (m, mi) && learn_address_script (m, mi, "update", &newroute->addr))
+ {
+ learn_succeeded = true;
+ owner = mi;
+ multi_instance_inc_refcount (mi);
+ route_quota_inc (mi);
+
+ /* delete old route */
+ multi_route_del (oldroute);
+
+ /* modify hash table entry, replacing old route */
+ he->key = &newroute->addr;
+ he->value = newroute;
+ }
+ }
+ else
+ {
+ if (route_quota_test (m, mi) && learn_address_script (m, mi, "add", &newroute->addr))
+ {
+ learn_succeeded = true;
+ owner = mi;
+ multi_instance_inc_refcount (mi);
+ route_quota_inc (mi);
+
+ /* add new route */
+ hash_add_fast (m->vhash, bucket, &newroute->addr, hv, newroute);
+ }
+ }
+
+ msg (D_MULTI_LOW, "MULTI: Learn%s: %s -> %s",
+ learn_succeeded ? "" : " FAILED",
+ mroute_addr_print (&newroute->addr, &gc),
+ multi_instance_string (mi, false, &gc));
+
+ if (!learn_succeeded)
+ free (newroute);
+
+ gc_free (&gc);
+ }
+
+ return owner;
+}
+
+/*
+ * Get client instance based on virtual address.
+ */
+static struct multi_instance *
+multi_get_instance_by_virtual_addr (struct multi_context *m,
+ const struct mroute_addr *addr,
+ bool cidr_routing)
+{
+ struct multi_route *route;
+ struct multi_instance *ret = NULL;
+
+ /* check for local address */
+ if (mroute_addr_equal (addr, &m->local))
+ return NULL;
+
+ route = (struct multi_route *) hash_lookup (m->vhash, addr);
+
+ /* does host route (possible cached) exist? */
+ if (route && multi_route_defined (m, route))
+ {
+ struct multi_instance *mi = route->instance;
+ route->last_reference = now;
+ ret = mi;
+ }
+ else if (cidr_routing) /* do we need to regenerate a host route cache entry? */
+ {
+ struct mroute_helper *rh = m->route_helper;
+ struct mroute_addr tryaddr;
+ int i;
+
+ /* cycle through each CIDR length */
+ for (i = 0; i < rh->n_net_len; ++i)
+ {
+ tryaddr = *addr;
+ tryaddr.type |= MR_WITH_NETBITS;
+ tryaddr.netbits = rh->net_len[i];
+ mroute_addr_mask_host_bits (&tryaddr);
+
+ /* look up a possible route with netbits netmask */
+ route = (struct multi_route *) hash_lookup (m->vhash, &tryaddr);
+
+ if (route && multi_route_defined (m, route))
+ {
+ /* found an applicable route, cache host route */
+ struct multi_instance *mi = route->instance;
+ multi_learn_addr (m, mi, addr, MULTI_ROUTE_CACHE|MULTI_ROUTE_AGEABLE);
+ ret = mi;
+ break;
+ }
+ }
+ }
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_MULTI_DEBUG))
+ {
+ struct gc_arena gc = gc_new ();
+ const char *addr_text = mroute_addr_print (addr, &gc);
+ if (ret)
+ {
+ dmsg (D_MULTI_DEBUG, "GET INST BY VIRT: %s -> %s via %s",
+ addr_text,
+ multi_instance_string (ret, false, &gc),
+ mroute_addr_print (&route->addr, &gc));
+ }
+ else
+ {
+ dmsg (D_MULTI_DEBUG, "GET INST BY VIRT: %s [failed]",
+ addr_text);
+ }
+ gc_free (&gc);
+ }
+#endif
+
+ ASSERT (!(ret && ret->halt));
+ return ret;
+}
+
+/*
+ * Helper function to multi_learn_addr().
+ */
+static struct multi_instance *
+multi_learn_in_addr_t (struct multi_context *m,
+ struct multi_instance *mi,
+ in_addr_t a,
+ int netbits, /* -1 if host route, otherwise # of network bits in address */
+ bool primary)
+{
+ struct openvpn_sockaddr remote_si;
+ struct mroute_addr addr;
+
+ CLEAR (remote_si);
+ remote_si.sa.sin_family = AF_INET;
+ remote_si.sa.sin_addr.s_addr = htonl (a);
+ ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
+
+ if (netbits >= 0)
+ {
+ addr.type |= MR_WITH_NETBITS;
+ addr.netbits = (uint8_t) netbits;
+ }
+
+ {
+ struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
+#ifdef MANAGEMENT_DEF_AUTH
+ if (management && owner)
+ management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
+#endif
+ return owner;
+ }
+}
+
+/*
+ * A new client has connected, add routes (server -> client)
+ * to internal routing table.
+ */
+static void
+multi_add_iroutes (struct multi_context *m,
+ struct multi_instance *mi)
+{
+ struct gc_arena gc = gc_new ();
+ const struct iroute *ir;
+ if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ mi->did_iroutes = true;
+ for (ir = mi->context.options.iroutes; ir != NULL; ir = ir->next)
+ {
+ if (ir->netbits >= 0)
+ msg (D_MULTI_LOW, "MULTI: internal route %s/%d -> %s",
+ print_in_addr_t (ir->network, 0, &gc),
+ ir->netbits,
+ multi_instance_string (mi, false, &gc));
+ else
+ msg (D_MULTI_LOW, "MULTI: internal route %s -> %s",
+ print_in_addr_t (ir->network, 0, &gc),
+ multi_instance_string (mi, false, &gc));
+
+ mroute_helper_add_iroute (m->route_helper, ir);
+
+ multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Given an instance (new_mi), delete all other instances which use the
+ * same common name.
+ */
+static void
+multi_delete_dup (struct multi_context *m, struct multi_instance *new_mi)
+{
+ if (new_mi)
+ {
+ const char *new_cn = tls_common_name (new_mi->context.c2.tls_multi, true);
+ if (new_cn)
+ {
+ struct hash_iterator hi;
+ struct hash_element *he;
+ int count = 0;
+
+ hash_iterator_init (m->iter, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct multi_instance *mi = (struct multi_instance *) he->value;
+ if (mi != new_mi && !mi->halt)
+ {
+ const char *cn = tls_common_name (mi->context.c2.tls_multi, true);
+ if (cn && !strcmp (cn, new_cn))
+ {
+ mi->did_iter = false;
+ multi_close_instance (m, mi, false);
+ hash_iterator_delete_element (&hi);
+ ++count;
+ }
+ }
+ }
+ hash_iterator_free (&hi);
+
+ if (count)
+ msg (D_MULTI_LOW, "MULTI: new connection by client '%s' will cause previous active sessions by this client to be dropped. Remember to use the --duplicate-cn option if you want multiple clients using the same certificate or username to concurrently connect.", new_cn);
+ }
+ }
+}
+
+/*
+ * Ensure that endpoint to be pushed to client
+ * complies with --ifconfig-push-constraint directive.
+ */
+static bool
+ifconfig_push_constraint_satisfied (const struct context *c)
+{
+ const struct options *o = &c->options;
+ if (o->push_ifconfig_constraint_defined && c->c2.push_ifconfig_defined)
+ return (o->push_ifconfig_constraint_netmask & c->c2.push_ifconfig_local) == o->push_ifconfig_constraint_network;
+ else
+ return true;
+}
+
+/*
+ * Select a virtual address for a new client instance.
+ * Use an --ifconfig-push directive, if given (static IP).
+ * Otherwise use an --ifconfig-pool address (dynamic IP).
+ */
+static void
+multi_select_virtual_addr (struct multi_context *m, struct multi_instance *mi)
+{
+ struct gc_arena gc = gc_new ();
+
+ /*
+ * If ifconfig addresses were set by dynamic config file,
+ * release pool addresses, otherwise keep them.
+ */
+ if (mi->context.options.push_ifconfig_defined)
+ {
+ /* ifconfig addresses were set statically,
+ release dynamic allocation */
+ if (mi->vaddr_handle >= 0)
+ {
+ ifconfig_pool_release (m->ifconfig_pool, mi->vaddr_handle, true);
+ mi->vaddr_handle = -1;
+ }
+
+ mi->context.c2.push_ifconfig_defined = true;
+ mi->context.c2.push_ifconfig_local = mi->context.options.push_ifconfig_local;
+ mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.push_ifconfig_remote_netmask;
+ }
+ else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a pool address */
+ {
+ in_addr_t local=0, remote=0;
+ const char *cn = NULL;
+
+ if (!mi->context.options.duplicate_cn)
+ cn = tls_common_name (mi->context.c2.tls_multi, true);
+
+ mi->vaddr_handle = ifconfig_pool_acquire (m->ifconfig_pool, &local, &remote, cn);
+ if (mi->vaddr_handle >= 0)
+ {
+ const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
+ const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
+
+ /* set push_ifconfig_remote_netmask from pool ifconfig address(es) */
+ mi->context.c2.push_ifconfig_local = remote;
+ if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
+ {
+ mi->context.c2.push_ifconfig_remote_netmask = mi->context.options.ifconfig_pool_netmask;
+ if (!mi->context.c2.push_ifconfig_remote_netmask)
+ mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->remote_netmask;
+ }
+ else if (tunnel_type == DEV_TYPE_TUN)
+ {
+ if (tunnel_topology == TOP_P2P)
+ mi->context.c2.push_ifconfig_remote_netmask = mi->context.c1.tuntap->local;
+ else if (tunnel_topology == TOP_NET30)
+ mi->context.c2.push_ifconfig_remote_netmask = local;
+ }
+
+ if (mi->context.c2.push_ifconfig_remote_netmask)
+ mi->context.c2.push_ifconfig_defined = true;
+ else
+ msg (D_MULTI_ERRORS, "MULTI: no --ifconfig-pool netmask parameter is available to push to %s",
+ multi_instance_string (mi, false, &gc));
+ }
+ else
+ {
+ msg (D_MULTI_ERRORS, "MULTI: no free --ifconfig-pool addresses are available");
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Set virtual address environmental variables.
+ */
+static void
+multi_set_virtual_addr_env (struct multi_context *m, struct multi_instance *mi)
+{
+ setenv_del (mi->context.c2.es, "ifconfig_pool_local_ip");
+ setenv_del (mi->context.c2.es, "ifconfig_pool_remote_ip");
+ setenv_del (mi->context.c2.es, "ifconfig_pool_netmask");
+
+ if (mi->context.c2.push_ifconfig_defined)
+ {
+ const int tunnel_type = TUNNEL_TYPE (mi->context.c1.tuntap);
+ const int tunnel_topology = TUNNEL_TOPOLOGY (mi->context.c1.tuntap);
+
+ setenv_in_addr_t (mi->context.c2.es,
+ "ifconfig_pool_remote_ip",
+ mi->context.c2.push_ifconfig_local,
+ SA_SET_IF_NONZERO);
+
+ if (tunnel_type == DEV_TYPE_TAP || (tunnel_type == DEV_TYPE_TUN && tunnel_topology == TOP_SUBNET))
+ {
+ setenv_in_addr_t (mi->context.c2.es,
+ "ifconfig_pool_netmask",
+ mi->context.c2.push_ifconfig_remote_netmask,
+ SA_SET_IF_NONZERO);
+ }
+ else if (tunnel_type == DEV_TYPE_TUN)
+ {
+ setenv_in_addr_t (mi->context.c2.es,
+ "ifconfig_pool_local_ip",
+ mi->context.c2.push_ifconfig_remote_netmask,
+ SA_SET_IF_NONZERO);
+ }
+ }
+}
+
+/*
+ * Called after client-connect script is called
+ */
+static void
+multi_client_connect_post (struct multi_context *m,
+ struct multi_instance *mi,
+ const char *dc_file,
+ unsigned int option_permissions_mask,
+ unsigned int *option_types_found)
+{
+ /* Did script generate a dynamic config file? */
+ if (test_file (dc_file))
+ {
+ options_server_import (&mi->context.options,
+ dc_file,
+ D_IMPORT_ERRORS|M_OPTERR,
+ option_permissions_mask,
+ option_types_found,
+ mi->context.c2.es);
+
+ if (!delete_file (dc_file))
+ msg (D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
+ dc_file);
+
+ /*
+ * If the --client-connect script generates a config file
+ * with an --ifconfig-push directive, it will override any
+ * --ifconfig-push directive from the --client-config-dir
+ * directory or any --ifconfig-pool dynamic address.
+ */
+ multi_select_virtual_addr (m, mi);
+ multi_set_virtual_addr_env (m, mi);
+ }
+}
+
+#ifdef ENABLE_PLUGIN
+
+/*
+ * Called after client-connect plug-in is called
+ */
+static void
+multi_client_connect_post_plugin (struct multi_context *m,
+ struct multi_instance *mi,
+ const struct plugin_return *pr,
+ unsigned int option_permissions_mask,
+ unsigned int *option_types_found)
+{
+ struct plugin_return config;
+
+ plugin_return_get_column (pr, &config, "config");
+
+ /* Did script generate a dynamic config file? */
+ if (plugin_return_defined (&config))
+ {
+ int i;
+ for (i = 0; i < config.n; ++i)
+ {
+ if (config.list[i] && config.list[i]->value)
+ options_string_import (&mi->context.options,
+ config.list[i]->value,
+ D_IMPORT_ERRORS|M_OPTERR,
+ option_permissions_mask,
+ option_types_found,
+ mi->context.c2.es);
+ }
+
+ /*
+ * If the --client-connect script generates a config file
+ * with an --ifconfig-push directive, it will override any
+ * --ifconfig-push directive from the --client-config-dir
+ * directory or any --ifconfig-pool dynamic address.
+ */
+ multi_select_virtual_addr (m, mi);
+ multi_set_virtual_addr_env (m, mi);
+ }
+}
+
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+/*
+ * Called to load management-derived client-connect config
+ */
+static void
+multi_client_connect_mda (struct multi_context *m,
+ struct multi_instance *mi,
+ const struct buffer_list *config,
+ unsigned int option_permissions_mask,
+ unsigned int *option_types_found)
+{
+ if (config)
+ {
+ struct buffer_entry *be;
+
+ for (be = config->head; be != NULL; be = be->next)
+ {
+ const char *opt = BSTR(&be->buf);
+ options_string_import (&mi->context.options,
+ opt,
+ D_IMPORT_ERRORS|M_OPTERR,
+ option_permissions_mask,
+ option_types_found,
+ mi->context.c2.es);
+ }
+
+ /*
+ * If the --client-connect script generates a config file
+ * with an --ifconfig-push directive, it will override any
+ * --ifconfig-push directive from the --client-config-dir
+ * directory or any --ifconfig-pool dynamic address.
+ */
+ multi_select_virtual_addr (m, mi);
+ multi_set_virtual_addr_env (m, mi);
+ }
+}
+
+#endif
+
+static void
+multi_client_connect_setenv (struct multi_context *m,
+ struct multi_instance *mi)
+{
+ struct gc_arena gc = gc_new ();
+
+ /* setenv incoming cert common name for script */
+ setenv_str (mi->context.c2.es, "common_name", tls_common_name (mi->context.c2.tls_multi, true));
+
+ /* setenv client real IP address */
+ setenv_trusted (mi->context.c2.es, get_link_socket_info (&mi->context));
+
+ /* setenv client virtual IP address */
+ multi_set_virtual_addr_env (m, mi);
+
+ /* setenv connection time */
+ {
+ const char *created_ascii = time_string (mi->created, 0, false, &gc);
+ setenv_str (mi->context.c2.es, "time_ascii", created_ascii);
+ setenv_unsigned (mi->context.c2.es, "time_unix", (unsigned int)mi->created);
+ }
+
+ gc_free (&gc);
+}
+
+/*
+ * Called as soon as the SSL/TLS connection authenticates.
+ *
+ * Instance-specific directives to be processed:
+ *
+ * iroute start-ip end-ip
+ * ifconfig-push local remote-netmask
+ * push
+ */
+static void
+multi_connection_established (struct multi_context *m, struct multi_instance *mi)
+{
+ if (tls_authentication_status (mi->context.c2.tls_multi, 0) == TLS_AUTHENTICATION_SUCCEEDED)
+ {
+ struct gc_arena gc = gc_new ();
+ unsigned int option_types_found = 0;
+
+ const unsigned int option_permissions_mask =
+ OPT_P_INSTANCE
+ | OPT_P_INHERIT
+ | OPT_P_PUSH
+ | OPT_P_TIMER
+ | OPT_P_CONFIG
+ | OPT_P_ECHO
+ | OPT_P_COMP
+ | OPT_P_SOCKFLAGS;
+
+ int cc_succeeded = true; /* client connect script status */
+ int cc_succeeded_count = 0;
+
+ ASSERT (mi->context.c1.tuntap);
+
+ /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */
+ tls_lock_common_name (mi->context.c2.tls_multi);
+ tls_lock_cert_hash_set (mi->context.c2.tls_multi);
+
+ /* generate a msg() prefix for this client instance */
+ generate_prefix (mi);
+
+ /* delete instances of previous clients with same common-name */
+ if (!mi->context.options.duplicate_cn)
+ multi_delete_dup (m, mi);
+
+ /* reset pool handle to null */
+ mi->vaddr_handle = -1;
+
+ /*
+ * Try to source a dynamic config file from the
+ * --client-config-dir directory.
+ */
+ if (mi->context.options.client_config_dir)
+ {
+ const char *ccd_file;
+
+ ccd_file = gen_path (mi->context.options.client_config_dir,
+ tls_common_name (mi->context.c2.tls_multi, false),
+ &gc);
+
+ /* try common-name file */
+ if (test_file (ccd_file))
+ {
+ options_server_import (&mi->context.options,
+ ccd_file,
+ D_IMPORT_ERRORS|M_OPTERR,
+ option_permissions_mask,
+ &option_types_found,
+ mi->context.c2.es);
+ }
+ else /* try default file */
+ {
+ ccd_file = gen_path (mi->context.options.client_config_dir,
+ CCD_DEFAULT,
+ &gc);
+
+ if (test_file (ccd_file))
+ {
+ options_server_import (&mi->context.options,
+ ccd_file,
+ D_IMPORT_ERRORS|M_OPTERR,
+ option_permissions_mask,
+ &option_types_found,
+ mi->context.c2.es);
+ }
+ }
+ }
+
+ /*
+ * Select a virtual address from either --ifconfig-push in --client-config-dir file
+ * or --ifconfig-pool.
+ */
+ multi_select_virtual_addr (m, mi);
+
+ /* do --client-connect setenvs */
+ multi_client_connect_setenv (m, mi);
+
+#ifdef ENABLE_PLUGIN
+ /*
+ * Call client-connect plug-in.
+ */
+
+ /* deprecated callback, use a file for passing back return info */
+ if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
+ {
+ struct argv argv = argv_new ();
+ const char *dc_file = create_temp_file (mi->context.options.tmp_dir, "cc", &gc);
+
+ if( !dc_file ) {
+ cc_succeeded = false;
+ goto script_depr_failed;
+ }
+
+ argv_printf (&argv, "%s", dc_file);
+ if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ {
+ msg (M_WARN, "WARNING: client-connect plugin call failed");
+ cc_succeeded = false;
+ }
+ else
+ {
+ multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
+ ++cc_succeeded_count;
+ }
+ script_depr_failed:
+ argv_reset (&argv);
+ }
+
+ /* V2 callback, use a plugin_return struct for passing back return info */
+ if (plugin_defined (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2))
+ {
+ struct plugin_return pr;
+
+ plugin_return_init (&pr);
+
+ if (plugin_call (mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ {
+ msg (M_WARN, "WARNING: client-connect-v2 plugin call failed");
+ cc_succeeded = false;
+ }
+ else
+ {
+ multi_client_connect_post_plugin (m, mi, &pr, option_permissions_mask, &option_types_found);
+ ++cc_succeeded_count;
+ }
+
+ plugin_return_free (&pr);
+ }
+#endif
+
+ /*
+ * Run --client-connect script.
+ */
+ if (mi->context.options.client_connect_script && cc_succeeded)
+ {
+ struct argv argv = argv_new ();
+ const char *dc_file = NULL;
+
+ setenv_str (mi->context.c2.es, "script_type", "client-connect");
+
+ dc_file = create_temp_file (mi->context.options.tmp_dir, "cc", &gc);
+ if( !dc_file ) {
+ cc_succeeded = false;
+ goto script_failed;
+ }
+
+ argv_printf (&argv, "%sc %s",
+ mi->context.options.client_connect_script,
+ dc_file);
+
+ if (openvpn_run_script (&argv, mi->context.c2.es, 0, "--client-connect"))
+ {
+ multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found);
+ ++cc_succeeded_count;
+ }
+ else
+ cc_succeeded = false;
+ script_failed:
+ argv_reset (&argv);
+ }
+
+ /*
+ * Check for client-connect script left by management interface client
+ */
+#ifdef MANAGEMENT_DEF_AUTH
+ if (cc_succeeded && mi->cc_config)
+ {
+ multi_client_connect_mda (m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
+ ++cc_succeeded_count;
+ }
+#endif
+
+ /*
+ * Check for "disable" directive in client-config-dir file
+ * or config file generated by --client-connect script.
+ */
+ if (mi->context.options.disable)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: client has been rejected due to 'disable' directive");
+ cc_succeeded = false;
+ }
+
+ if (cc_succeeded)
+ {
+ /*
+ * Process sourced options.
+ */
+ do_deferred_options (&mi->context, option_types_found);
+
+ /*
+ * make sure we got ifconfig settings from somewhere
+ */
+ if (!mi->context.c2.push_ifconfig_defined)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: no dynamic or static remote --ifconfig address is available for %s",
+ multi_instance_string (mi, false, &gc));
+ }
+
+ /*
+ * make sure that ifconfig settings comply with constraints
+ */
+ if (!ifconfig_push_constraint_satisfied (&mi->context))
+ {
+ /* JYFIXME -- this should cause the connection to fail */
+ msg (D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s) violates tunnel network/netmask constraint (%s/%s)",
+ multi_instance_string (mi, false, &gc),
+ print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc),
+ print_in_addr_t (mi->context.options.push_ifconfig_constraint_network, 0, &gc),
+ print_in_addr_t (mi->context.options.push_ifconfig_constraint_netmask, 0, &gc));
+ }
+
+ /*
+ * For routed tunnels, set up internal route to endpoint
+ * plus add all iroute routes.
+ */
+ if (TUNNEL_TYPE (mi->context.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ if (mi->context.c2.push_ifconfig_defined)
+ {
+ multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1, true);
+ msg (D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
+ multi_instance_string (mi, false, &gc),
+ print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
+ }
+
+ /* add routes locally, pointing to new client, if
+ --iroute options have been specified */
+ multi_add_iroutes (m, mi);
+
+ /*
+ * iroutes represent subnets which are "owned" by a particular
+ * client. Therefore, do not actually push a route to a client
+ * if it matches one of the client's iroutes.
+ */
+ remove_iroutes_from_push_route_list (&mi->context.options);
+ }
+ else if (mi->context.options.iroutes)
+ {
+ msg (D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute only works with tun-style tunnels",
+ multi_instance_string (mi, false, &gc));
+ }
+
+ /* set our client's VPN endpoint for status reporting purposes */
+ mi->reporting_addr = mi->context.c2.push_ifconfig_local;
+
+ /* set context-level authentication flag */
+ mi->context.c2.context_auth = CAS_SUCCEEDED;
+ }
+ else
+ {
+ /* set context-level authentication flag */
+ mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
+ }
+
+ /* set flag so we don't get called again */
+ mi->connection_established_flag = true;
+
+ /* increment number of current authenticated clients */
+ ++m->n_clients;
+ --mi->n_clients_delta;
+
+#ifdef MANAGEMENT_DEF_AUTH
+ if (management)
+ management_connection_established (management, &mi->context.c2.mda_context, mi->context.c2.es);
+#endif
+
+ gc_free (&gc);
+ }
+
+ /*
+ * Reply now to client's PUSH_REQUEST query
+ */
+ mi->context.c2.push_reply_deferred = false;
+}
+
+/*
+ * Add a mbuf buffer to a particular
+ * instance.
+ */
+void
+multi_add_mbuf (struct multi_context *m,
+ struct multi_instance *mi,
+ struct mbuf_buffer *mb)
+{
+ if (multi_output_queue_ready (m, mi))
+ {
+ struct mbuf_item item;
+ item.buffer = mb;
+ item.instance = mi;
+ mbuf_add_item (m->mbuf, &item);
+ }
+ else
+ {
+ msg (D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_add_mbuf)");
+ }
+}
+
+/*
+ * Add a packet to a client instance output queue.
+ */
+static inline void
+multi_unicast (struct multi_context *m,
+ const struct buffer *buf,
+ struct multi_instance *mi)
+{
+ struct mbuf_buffer *mb;
+
+ if (BLEN (buf) > 0)
+ {
+ mb = mbuf_alloc_buf (buf);
+ mb->flags = MF_UNICAST;
+ multi_add_mbuf (m, mi, mb);
+ mbuf_free_buf (mb);
+ }
+}
+
+/*
+ * Broadcast a packet to all clients.
+ */
+static void
+multi_bcast (struct multi_context *m,
+ const struct buffer *buf,
+ const struct multi_instance *sender_instance,
+ const struct mroute_addr *sender_addr)
+{
+ struct hash_iterator hi;
+ struct hash_element *he;
+ struct multi_instance *mi;
+ struct mbuf_buffer *mb;
+
+ if (BLEN (buf) > 0)
+ {
+ perf_push (PERF_MULTI_BCAST);
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("BCAST len=%d\n", BLEN (buf));
+#endif
+ mb = mbuf_alloc_buf (buf);
+ hash_iterator_init (m->iter, &hi);
+
+ while ((he = hash_iterator_next (&hi)))
+ {
+ mi = (struct multi_instance *) he->value;
+ if (mi != sender_instance && !mi->halt)
+ {
+#ifdef ENABLE_PF
+ if (sender_instance)
+ {
+ if (!pf_c2c_test (&sender_instance->context, &mi->context, "bcast_c2c"))
+ {
+ msg (D_PF_DROPPED_BCAST, "PF: client[%s] -> client[%s] packet dropped by BCAST packet filter",
+ mi_prefix (sender_instance),
+ mi_prefix (mi));
+ continue;
+ }
+ }
+ if (sender_addr)
+ {
+ if (!pf_addr_test (&mi->context, sender_addr, "bcast_src_addr"))
+ {
+ struct gc_arena gc = gc_new ();
+ msg (D_PF_DROPPED_BCAST, "PF: addr[%s] -> client[%s] packet dropped by BCAST packet filter",
+ mroute_addr_print_ex (sender_addr, MAPF_SHOW_ARP, &gc),
+ mi_prefix (mi));
+ gc_free (&gc);
+ continue;
+ }
+ }
+#endif
+ multi_add_mbuf (m, mi, mb);
+ }
+ }
+
+ hash_iterator_free (&hi);
+ mbuf_free_buf (mb);
+ perf_pop ();
+ }
+}
+
+/*
+ * Given a time delta, indicating that we wish to be
+ * awoken by the scheduler at time now + delta, figure
+ * a sigma parameter (in microseconds) that represents
+ * a sort of fuzz factor around delta, so that we're
+ * really telling the scheduler to wake us up any time
+ * between now + delta - sigma and now + delta + sigma.
+ *
+ * The sigma parameter helps the scheduler to run more efficiently.
+ * Sigma should be no larger than TV_WITHIN_SIGMA_MAX_USEC
+ */
+static inline unsigned int
+compute_wakeup_sigma (const struct timeval *delta)
+{
+ if (delta->tv_sec < 1)
+ {
+ /* if < 1 sec, fuzz = # of microseconds / 8 */
+ return delta->tv_usec >> 3;
+ }
+ else
+ {
+ /* if < 10 minutes, fuzz = 13.1% of timeout */
+ if (delta->tv_sec < 600)
+ return delta->tv_sec << 17;
+ else
+ return 120000000; /* if >= 10 minutes, fuzz = 2 minutes */
+ }
+}
+
+static void
+multi_schedule_context_wakeup (struct multi_context *m, struct multi_instance *mi)
+{
+ /* calculate an absolute wakeup time */
+ ASSERT (!openvpn_gettimeofday (&mi->wakeup, NULL));
+ tv_add (&mi->wakeup, &mi->context.c2.timeval);
+
+ /* tell scheduler to wake us up at some point in the future */
+ schedule_add_entry (m->schedule,
+ (struct schedule_entry *) mi,
+ &mi->wakeup,
+ compute_wakeup_sigma (&mi->context.c2.timeval));
+}
+
+/*
+ * Figure instance-specific timers, convert
+ * earliest to absolute time in mi->wakeup,
+ * call scheduler with our future wakeup time.
+ *
+ * Also close context on signal.
+ */
+bool
+multi_process_post (struct multi_context *m, struct multi_instance *mi, const unsigned int flags)
+{
+ bool ret = true;
+
+ if (!IS_SIG (&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT (&mi->context))))
+ {
+ /* figure timeouts and fetch possible outgoing
+ to_link packets (such as ping or TLS control) */
+ pre_select (&mi->context);
+
+ if (!IS_SIG (&mi->context))
+ {
+ /* tell scheduler to wake us up at some point in the future */
+ multi_schedule_context_wakeup(m, mi);
+
+ /* connection is "established" when SSL/TLS key negotiation succeeds
+ and (if specified) auth user/pass succeeds */
+ if (!mi->connection_established_flag && CONNECTION_ESTABLISHED (&mi->context))
+ multi_connection_established (m, mi);
+ }
+ }
+
+ if (IS_SIG (&mi->context))
+ {
+ if (flags & MPP_CLOSE_ON_SIGNAL)
+ {
+ multi_close_instance_on_signal (m, mi);
+ ret = false;
+ }
+ }
+ else
+ {
+ /* continue to pend on output? */
+ multi_set_pending (m, ANY_OUT (&mi->context) ? mi : NULL);
+
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("POST %s[%d] to=%d lo=%d/%d w=%d/%d\n",
+ id(mi),
+ (int) (mi == m->pending),
+ mi ? mi->context.c2.to_tun.len : -1,
+ mi ? mi->context.c2.to_link.len : -1,
+ (mi && mi->context.c2.fragment) ? mi->context.c2.fragment->outgoing.len : -1,
+ (int)mi->context.c2.timeval.tv_sec,
+ (int)mi->context.c2.timeval.tv_usec);
+#endif
+ }
+
+ if ((flags & MPP_RECORD_TOUCH) && m->mpp_touched)
+ *m->mpp_touched = mi;
+
+ return ret;
+}
+
+/*
+ * Process packets in the TCP/UDP socket -> TUN/TAP interface direction,
+ * i.e. client -> server direction.
+ */
+bool
+multi_process_incoming_link (struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
+{
+ struct gc_arena gc = gc_new ();
+
+ struct context *c;
+ struct mroute_addr src, dest;
+ unsigned int mroute_flags;
+ struct multi_instance *mi;
+ bool ret = true;
+
+ if (m->pending)
+ return true;
+
+ if (!instance)
+ {
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("TCP/UDP -> TUN [%d]\n", BLEN (&m->top.c2.buf));
+#endif
+ multi_set_pending (m, multi_get_create_instance_udp (m));
+ }
+ else
+ multi_set_pending (m, instance);
+
+ if (m->pending)
+ {
+ set_prefix (m->pending);
+
+ /* get instance context */
+ c = &m->pending->context;
+
+ if (!instance)
+ {
+ /* transfer packet pointer from top-level context buffer to instance */
+ c->c2.buf = m->top.c2.buf;
+
+ /* transfer from-addr from top-level context buffer to instance */
+ c->c2.from = m->top.c2.from;
+ }
+
+ if (BLEN (&c->c2.buf) > 0)
+ {
+ /* decrypt in instance context */
+ process_incoming_link (c);
+
+ if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TUN)
+ {
+ /* extract packet source and dest addresses */
+ mroute_flags = mroute_extract_addr_from_packet (&src,
+ &dest,
+ NULL,
+ NULL,
+ &c->c2.to_tun,
+ DEV_TYPE_TUN);
+
+ /* drop packet if extract failed */
+ if (!(mroute_flags & MROUTE_EXTRACT_SUCCEEDED))
+ {
+ c->c2.to_tun.len = 0;
+ }
+ /* make sure that source address is associated with this client */
+ else if (multi_get_instance_by_virtual_addr (m, &src, true) != m->pending)
+ {
+ msg (D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
+ mroute_addr_print (&src, &gc));
+ c->c2.to_tun.len = 0;
+ }
+ /* client-to-client communication enabled? */
+ else if (m->enable_c2c)
+ {
+ /* multicast? */
+ if (mroute_flags & MROUTE_EXTRACT_MCAST)
+ {
+ /* for now, treat multicast as broadcast */
+ multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
+ }
+ else /* possible client to client routing */
+ {
+ ASSERT (!(mroute_flags & MROUTE_EXTRACT_BCAST));
+ mi = multi_get_instance_by_virtual_addr (m, &dest, true);
+
+ /* if dest addr is a known client, route to it */
+ if (mi)
+ {
+#ifdef ENABLE_PF
+ if (!pf_c2c_test (c, &mi->context, "tun_c2c"))
+ {
+ msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
+ mi_prefix (mi));
+ }
+ else
+#endif
+ {
+ multi_unicast (m, &c->c2.to_tun, mi);
+ register_activity (c, BLEN(&c->c2.to_tun));
+ }
+ c->c2.to_tun.len = 0;
+ }
+ }
+ }
+#ifdef ENABLE_PF
+ if (c->c2.to_tun.len && !pf_addr_test (c, &dest, "tun_dest_addr"))
+ {
+ msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
+ mroute_addr_print_ex (&dest, MAPF_SHOW_ARP, &gc));
+ c->c2.to_tun.len = 0;
+ }
+#endif
+ }
+ else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP)
+ {
+#ifdef ENABLE_PF
+ struct mroute_addr edest;
+ mroute_addr_reset (&edest);
+#endif
+ /* extract packet source and dest addresses */
+ mroute_flags = mroute_extract_addr_from_packet (&src,
+ &dest,
+ NULL,
+#ifdef ENABLE_PF
+ &edest,
+#else
+ NULL,
+#endif
+ &c->c2.to_tun,
+ DEV_TYPE_TAP);
+
+ if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
+ {
+ if (multi_learn_addr (m, m->pending, &src, 0) == m->pending)
+ {
+ /* check for broadcast */
+ if (m->enable_c2c)
+ {
+ if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
+ {
+ multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
+ }
+ else /* try client-to-client routing */
+ {
+ mi = multi_get_instance_by_virtual_addr (m, &dest, false);
+
+ /* if dest addr is a known client, route to it */
+ if (mi)
+ {
+#ifdef ENABLE_PF
+ if (!pf_c2c_test (c, &mi->context, "tap_c2c"))
+ {
+ msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
+ mi_prefix (mi));
+ }
+ else
+#endif
+ {
+ multi_unicast (m, &c->c2.to_tun, mi);
+ register_activity (c, BLEN(&c->c2.to_tun));
+ }
+ c->c2.to_tun.len = 0;
+ }
+ }
+ }
+#ifdef ENABLE_PF
+ if (c->c2.to_tun.len && !pf_addr_test (c, &edest, "tap_dest_addr"))
+ {
+ msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
+ mroute_addr_print_ex (&edest, MAPF_SHOW_ARP, &gc));
+ c->c2.to_tun.len = 0;
+ }
+#endif
+ }
+ else
+ {
+ msg (D_MULTI_DROPPED, "MULTI: bad source address from client [%s], packet dropped",
+ mroute_addr_print (&src, &gc));
+ c->c2.to_tun.len = 0;
+ }
+ }
+ else
+ {
+ c->c2.to_tun.len = 0;
+ }
+ }
+ }
+
+ /* postprocess and set wakeup */
+ ret = multi_process_post (m, m->pending, mpp_flags);
+
+ clear_prefix ();
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Process packets in the TUN/TAP interface -> TCP/UDP socket direction,
+ * i.e. server -> client direction.
+ */
+bool
+multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flags)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = true;
+
+ if (BLEN (&m->top.c2.buf) > 0)
+ {
+ unsigned int mroute_flags;
+ struct mroute_addr src, dest;
+ const int dev_type = TUNNEL_TYPE (m->top.c1.tuntap);
+
+#ifdef ENABLE_PF
+ struct mroute_addr esrc, *e1, *e2;
+ if (dev_type == DEV_TYPE_TUN)
+ {
+ e1 = NULL;
+ e2 = &src;
+ }
+ else
+ {
+ e1 = e2 = &esrc;
+ mroute_addr_reset (&esrc);
+ }
+#endif
+
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("TUN -> TCP/UDP [%d]\n", BLEN (&m->top.c2.buf));
+#endif
+
+ if (m->pending)
+ return true;
+
+ /*
+ * Route an incoming tun/tap packet to
+ * the appropriate multi_instance object.
+ */
+
+ mroute_flags = mroute_extract_addr_from_packet (&src,
+ &dest,
+#ifdef ENABLE_PF
+ e1,
+#else
+ NULL,
+#endif
+ NULL,
+ &m->top.c2.buf,
+ dev_type);
+
+ if (mroute_flags & MROUTE_EXTRACT_SUCCEEDED)
+ {
+ struct context *c;
+
+ /* broadcast or multicast dest addr? */
+ if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
+ {
+ /* for now, treat multicast as broadcast */
+#ifdef ENABLE_PF
+ multi_bcast (m, &m->top.c2.buf, NULL, e2);
+#else
+ multi_bcast (m, &m->top.c2.buf, NULL, NULL);
+#endif
+ }
+ else
+ {
+ multi_set_pending (m, multi_get_instance_by_virtual_addr (m, &dest, dev_type == DEV_TYPE_TUN));
+
+ if (m->pending)
+ {
+ /* get instance context */
+ c = &m->pending->context;
+
+ set_prefix (m->pending);
+
+#ifdef ENABLE_PF
+ if (!pf_addr_test (c, e2, "tun_tap_src_addr"))
+ {
+ msg (D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter",
+ mroute_addr_print_ex (&src, MAPF_SHOW_ARP, &gc));
+ buf_reset_len (&c->c2.buf);
+ }
+ else
+#endif
+ {
+ if (multi_output_queue_ready (m, m->pending))
+ {
+ /* transfer packet pointer from top-level context buffer to instance */
+ c->c2.buf = m->top.c2.buf;
+ }
+ else
+ {
+ /* drop packet */
+ msg (D_MULTI_DROPPED, "MULTI: packet dropped due to output saturation (multi_process_incoming_tun)");
+ buf_reset_len (&c->c2.buf);
+ }
+ }
+
+ /* encrypt in instance context */
+ process_incoming_tun (c);
+
+ /* postprocess and set wakeup */
+ ret = multi_process_post (m, m->pending, mpp_flags);
+
+ clear_prefix ();
+ }
+ }
+ }
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Process a possible client-to-client/bcast/mcast message in the
+ * queue.
+ */
+struct multi_instance *
+multi_get_queue (struct mbuf_set *ms)
+{
+ struct mbuf_item item;
+
+ if (mbuf_extract_item (ms, &item)) /* cleartext IP packet */
+ {
+ unsigned int pipv4_flags = PIPV4_PASSTOS;
+
+ set_prefix (item.instance);
+ item.instance->context.c2.buf = item.buffer->buf;
+ if (item.buffer->flags & MF_UNICAST) /* --mssfix doesn't make sense for broadcast or multicast */
+ pipv4_flags |= PIPV4_MSSFIX;
+ process_ipv4_header (&item.instance->context, pipv4_flags, &item.instance->context.c2.buf);
+ encrypt_sign (&item.instance->context, true);
+ mbuf_free_buf (item.buffer);
+
+ dmsg (D_MULTI_DEBUG, "MULTI: C2C/MCAST/BCAST");
+
+ clear_prefix ();
+ return item.instance;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/*
+ * Called when an I/O wait times out. Usually means that a particular
+ * client instance object needs timer-based service.
+ */
+bool
+multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags)
+{
+ bool ret = true;
+
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("%s -> TIMEOUT\n", id(m->earliest_wakeup));
+#endif
+
+ /* instance marked for wakeup? */
+ if (m->earliest_wakeup)
+ {
+ set_prefix (m->earliest_wakeup);
+ ret = multi_process_post (m, m->earliest_wakeup, mpp_flags);
+ m->earliest_wakeup = NULL;
+ clear_prefix ();
+ }
+ return ret;
+}
+
+/*
+ * Drop a TUN/TAP outgoing packet..
+ */
+void
+multi_process_drop_outgoing_tun (struct multi_context *m, const unsigned int mpp_flags)
+{
+ struct multi_instance *mi = m->pending;
+
+ ASSERT (mi);
+
+ set_prefix (mi);
+
+ msg (D_MULTI_ERRORS, "MULTI: Outgoing TUN queue full, dropped packet len=%d",
+ mi->context.c2.to_tun.len);
+
+ buf_reset (&mi->context.c2.to_tun);
+
+ multi_process_post (m, mi, mpp_flags);
+ clear_prefix ();
+}
+
+/*
+ * Per-client route quota management
+ */
+
+void
+route_quota_exceeded (const struct multi_context *m, const struct multi_instance *mi)
+{
+ struct gc_arena gc = gc_new ();
+ msg (D_ROUTE_QUOTA, "MULTI ROUTE: route quota (%d) exceeded for %s (see --max-routes-per-client option)",
+ mi->context.options.max_routes_per_client,
+ multi_instance_string (mi, false, &gc));
+ gc_free (&gc);
+}
+
+#ifdef ENABLE_DEBUG
+/*
+ * Flood clients with random packets
+ */
+static void
+gremlin_flood_clients (struct multi_context *m)
+{
+ const int level = GREMLIN_PACKET_FLOOD_LEVEL (m->top.options.gremlin);
+ if (level)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer buf = alloc_buf_gc (BUF_SIZE (&m->top.c2.frame), &gc);
+ struct packet_flood_parms parm = get_packet_flood_parms (level);
+ int i;
+
+ ASSERT (buf_init (&buf, FRAME_HEADROOM (&m->top.c2.frame)));
+ parm.packet_size = min_int (parm.packet_size, MAX_RW_SIZE_TUN (&m->top.c2.frame));
+
+ msg (D_GREMLIN, "GREMLIN_FLOOD_CLIENTS: flooding clients with %d packets of size %d",
+ parm.n_packets,
+ parm.packet_size);
+
+ for (i = 0; i < parm.packet_size; ++i)
+ ASSERT (buf_write_u8 (&buf, get_random () & 0xFF));
+
+ for (i = 0; i < parm.n_packets; ++i)
+ multi_bcast (m, &buf, NULL, NULL);
+
+ gc_free (&gc);
+ }
+}
+#endif
+
+/*
+ * Process timers in the top-level context
+ */
+void
+multi_process_per_second_timers_dowork (struct multi_context *m)
+{
+ /* possibly reap instances/routes in vhash */
+ multi_reap_process (m);
+
+ /* possibly print to status log */
+ if (m->top.c1.status_output)
+ {
+ if (status_trigger (m->top.c1.status_output))
+ multi_print_status (m, m->top.c1.status_output, m->status_file_version);
+ }
+
+ /* possibly flush ifconfig-pool file */
+ multi_ifconfig_pool_persist (m, false);
+
+#ifdef ENABLE_DEBUG
+ gremlin_flood_clients (m);
+#endif
+}
+
+void
+multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers)
+{
+ inherit_context_top (&m->top, top);
+ m->top.c2.buffers = NULL;
+ if (alloc_buffers)
+ m->top.c2.buffers = init_context_buffers (&top->c2.frame);
+}
+
+void
+multi_top_free (struct multi_context *m)
+{
+ close_context (&m->top, -1, CC_GC_FREE);
+ free_context_buffers (m->top.c2.buffers);
+}
+
+/*
+ * Return true if event loop should break,
+ * false if it should continue.
+ */
+bool
+multi_process_signal (struct multi_context *m)
+{
+ if (m->top.sig->signal_received == SIGUSR2)
+ {
+ struct status_output *so = status_open (NULL, 0, M_INFO, NULL, 0);
+ multi_print_status (m, so, m->status_file_version);
+ status_close (so);
+ m->top.sig->signal_received = 0;
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Called when an instance should be closed due to the
+ * reception of a soft signal.
+ */
+void
+multi_close_instance_on_signal (struct multi_context *m, struct multi_instance *mi)
+{
+ remap_signal (&mi->context);
+ set_prefix (mi);
+ print_signal (mi->context.sig, "client-instance", D_MULTI_LOW);
+ clear_prefix ();
+ multi_close_instance (m, mi, false);
+}
+
+static void
+multi_signal_instance (struct multi_context *m, struct multi_instance *mi, const int sig)
+{
+ mi->context.sig->signal_received = sig;
+ multi_close_instance_on_signal (m, mi);
+}
+
+/*
+ * Management subsystem callbacks
+ */
+
+#ifdef ENABLE_MANAGEMENT
+
+static void
+management_callback_status (void *arg, const int version, struct status_output *so)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+
+ if (!version)
+ multi_print_status (m, so, m->status_file_version);
+ else
+ multi_print_status (m, so, version);
+}
+
+static int
+management_callback_n_clients (void *arg)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ return m->n_clients;
+}
+
+static int
+management_callback_kill_by_cn (void *arg, const char *del_cn)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct hash_iterator hi;
+ struct hash_element *he;
+ int count = 0;
+
+ hash_iterator_init (m->iter, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct multi_instance *mi = (struct multi_instance *) he->value;
+ if (!mi->halt)
+ {
+ const char *cn = tls_common_name (mi->context.c2.tls_multi, false);
+ if (cn && !strcmp (cn, del_cn))
+ {
+ multi_signal_instance (m, mi, SIGTERM);
+ ++count;
+ }
+ }
+ }
+ hash_iterator_free (&hi);
+ return count;
+}
+
+static int
+management_callback_kill_by_addr (void *arg, const in_addr_t addr, const int port)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct hash_iterator hi;
+ struct hash_element *he;
+ struct openvpn_sockaddr saddr;
+ struct mroute_addr maddr;
+ int count = 0;
+
+ CLEAR (saddr);
+ saddr.sa.sin_family = AF_INET;
+ saddr.sa.sin_addr.s_addr = htonl (addr);
+ saddr.sa.sin_port = htons (port);
+ if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
+ {
+ hash_iterator_init (m->iter, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct multi_instance *mi = (struct multi_instance *) he->value;
+ if (!mi->halt && mroute_addr_equal (&maddr, &mi->real))
+ {
+ multi_signal_instance (m, mi, SIGTERM);
+ ++count;
+ }
+ }
+ hash_iterator_free (&hi);
+ }
+ return count;
+}
+
+static void
+management_delete_event (void *arg, event_t event)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ if (m->mtcp)
+ multi_tcp_delete_event (m->mtcp, event);
+}
+
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static struct multi_instance *
+lookup_by_cid (struct multi_context *m, const unsigned long cid)
+{
+ if (m)
+ {
+ struct multi_instance *mi = (struct multi_instance *) hash_lookup (m->cid_hash, &cid);
+ if (mi && !mi->halt)
+ return mi;
+ }
+ return NULL;
+}
+
+static bool
+management_kill_by_cid (void *arg, const unsigned long cid)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct multi_instance *mi = lookup_by_cid (m, cid);
+ if (mi)
+ {
+ send_restart (&mi->context); /* was: multi_signal_instance (m, mi, SIGTERM); */
+ return true;
+ }
+ else
+ return false;
+}
+
+static bool
+management_client_auth (void *arg,
+ const unsigned long cid,
+ const unsigned int mda_key_id,
+ const bool auth,
+ const char *reason,
+ const char *client_reason,
+ struct buffer_list *cc_config) /* ownership transferred */
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct multi_instance *mi = lookup_by_cid (m, cid);
+ bool cc_config_owned = true;
+ bool ret = false;
+
+ if (mi)
+ {
+ ret = tls_authenticate_key (mi->context.c2.tls_multi, mda_key_id, auth, client_reason);
+ if (ret)
+ {
+ if (auth)
+ {
+ if (!mi->connection_established_flag)
+ {
+ set_cc_config (mi, cc_config);
+ cc_config_owned = false;
+ }
+ }
+ else
+ {
+ if (reason)
+ msg (D_MULTI_LOW, "MULTI: connection rejected: %s, CLI:%s", reason, np(client_reason));
+ if (mi->connection_established_flag)
+ {
+ send_auth_failed (&mi->context, client_reason); /* mid-session reauth failed */
+ multi_schedule_context_wakeup(m, mi);
+ }
+ }
+ }
+ }
+ if (cc_config_owned && cc_config)
+ buffer_list_free (cc_config);
+ return ret;
+}
+
+static char *
+management_get_peer_info (void *arg, const unsigned long cid)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct multi_instance *mi = lookup_by_cid (m, cid);
+ char *ret = NULL;
+
+ if (mi)
+ ret = tls_get_peer_info (mi->context.c2.tls_multi);
+
+ return ret;
+}
+
+#endif
+
+#ifdef MANAGEMENT_PF
+static bool
+management_client_pf (void *arg,
+ const unsigned long cid,
+ struct buffer_list *pf_config) /* ownership transferred */
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct multi_instance *mi = lookup_by_cid (m, cid);
+ bool ret = false;
+
+ if (mi && pf_config)
+ ret = pf_load_from_buffer_list (&mi->context, pf_config);
+
+ if (pf_config)
+ buffer_list_free (pf_config);
+ return ret;
+}
+#endif
+
+void
+init_management_callback_multi (struct multi_context *m)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ struct management_callback cb;
+ CLEAR (cb);
+ cb.arg = m;
+ cb.flags = MCF_SERVER;
+ cb.status = management_callback_status;
+ cb.show_net = management_show_net_callback;
+ cb.kill_by_cn = management_callback_kill_by_cn;
+ cb.kill_by_addr = management_callback_kill_by_addr;
+ cb.delete_event = management_delete_event;
+ cb.n_clients = management_callback_n_clients;
+#ifdef MANAGEMENT_DEF_AUTH
+ cb.kill_by_cid = management_kill_by_cid;
+ cb.client_auth = management_client_auth;
+ cb.get_peer_info = management_get_peer_info;
+#endif
+#ifdef MANAGEMENT_PF
+ cb.client_pf = management_client_pf;
+#endif
+ management_set_callback (management, &cb);
+ }
+#endif
+}
+
+void
+uninit_management_callback_multi (struct multi_context *m)
+{
+ uninit_management_callback ();
+}
+
+/*
+ * Top level event loop.
+ */
+void
+tunnel_server (struct context *top)
+{
+ ASSERT (top->options.mode == MODE_SERVER);
+
+ switch (top->options.ce.proto) {
+ case PROTO_UDPv4:
+ tunnel_server_udp (top);
+ break;
+ case PROTO_TCPv4_SERVER:
+ tunnel_server_tcp (top);
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+#else
+static void dummy(void) {}
+#endif /* P2MP_SERVER */
diff --git a/multi.h b/multi.h
new file mode 100644
index 0000000..08964a2
--- /dev/null
+++ b/multi.h
@@ -0,0 +1,450 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MULTI_H
+#define MULTI_H
+
+#if P2MP_SERVER
+
+#include "init.h"
+#include "forward.h"
+#include "mroute.h"
+#include "mbuf.h"
+#include "list.h"
+#include "schedule.h"
+#include "pool.h"
+#include "mudp.h"
+#include "mtcp.h"
+#include "perf.h"
+
+/*
+ * Walk (don't run) through the routing table,
+ * deleting old entries, and possibly multi_instance
+ * structs as well which have been marked for deletion.
+ */
+struct multi_reap
+{
+ int bucket_base;
+ int buckets_per_pass;
+ time_t last_call;
+};
+
+/*
+ * One multi_instance object per client instance.
+ */
+struct multi_instance {
+ struct schedule_entry se; /* this must be the first element of the structure */
+ struct gc_arena gc;
+ bool defined;
+ bool halt;
+ int refcount;
+ int route_count; /* number of routes (including cached routes) owned by this instance */
+ time_t created;
+ struct timeval wakeup; /* absolute time */
+ struct mroute_addr real;
+ ifconfig_pool_handle vaddr_handle;
+ const char *msg_prefix;
+
+ /* queued outgoing data in Server/TCP mode */
+ unsigned int tcp_rwflags;
+ struct mbuf_set *tcp_link_out_deferred;
+ bool socket_set_called;
+
+ in_addr_t reporting_addr; /* IP address shown in status listing */
+
+ bool did_open_context;
+ bool did_real_hash;
+ bool did_iter;
+#ifdef MANAGEMENT_DEF_AUTH
+ bool did_cid_hash;
+ struct buffer_list *cc_config;
+#endif
+ bool connection_established_flag;
+ bool did_iroutes;
+ int n_clients_delta; /* added to multi_context.n_clients when instance is closed */
+
+ struct context context;
+};
+
+/*
+ * One multi_context object per server daemon thread.
+ */
+struct multi_context {
+# define MC_UNDEF 0
+# define MC_SINGLE_THREADED (1<<0)
+# define MC_MULTI_THREADED_MASTER (1<<1)
+# define MC_MULTI_THREADED_WORKER (1<<2)
+# define MC_MULTI_THREADED_SCHEDULER (1<<3)
+# define MC_WORK_THREAD (MC_MULTI_THREADED_WORKER|MC_MULTI_THREADED_SCHEDULER)
+ int thread_mode;
+
+ struct hash *hash; /* client instances indexed by real address */
+ struct hash *vhash; /* client instances indexed by virtual address */
+ struct hash *iter; /* like real address hash but optimized for iteration */
+ struct schedule *schedule;
+ struct mbuf_set *mbuf;
+ struct multi_tcp *mtcp;
+ struct ifconfig_pool *ifconfig_pool;
+ struct frequency_limit *new_connection_limiter;
+ struct mroute_helper *route_helper;
+ struct multi_reap *reaper;
+ struct mroute_addr local;
+ bool enable_c2c;
+ int max_clients;
+ int tcp_queue_limit;
+ int status_file_version;
+ int n_clients; /* current number of authenticated clients */
+
+#ifdef MANAGEMENT_DEF_AUTH
+ struct hash *cid_hash;
+ unsigned long cid_counter;
+#endif
+
+ struct multi_instance *pending;
+ struct multi_instance *earliest_wakeup;
+ struct multi_instance **mpp_touched;
+ struct context_buffers *context_buffers;
+ time_t per_second_trigger;
+
+ struct context top;
+};
+
+/*
+ * Host route
+ */
+struct multi_route
+{
+ struct mroute_addr addr;
+ struct multi_instance *instance;
+
+# define MULTI_ROUTE_CACHE (1<<0)
+# define MULTI_ROUTE_AGEABLE (1<<1)
+ unsigned int flags;
+
+ unsigned int cache_generation;
+ time_t last_reference;
+};
+
+/*
+ * top level function, called by openvpn.c
+ */
+void tunnel_server (struct context *top);
+
+const char *multi_instance_string (const struct multi_instance *mi, bool null, struct gc_arena *gc);
+
+/*
+ * Called by mtcp.c, mudp.c, or other (to be written) protocol drivers
+ */
+
+void multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int thread_mode);
+void multi_uninit (struct multi_context *m);
+
+void multi_top_init (struct multi_context *m, const struct context *top, const bool alloc_buffers);
+void multi_top_free (struct multi_context *m);
+
+struct multi_instance *multi_create_instance (struct multi_context *m, const struct mroute_addr *real);
+void multi_close_instance (struct multi_context *m, struct multi_instance *mi, bool shutdown);
+
+bool multi_process_timeout (struct multi_context *m, const unsigned int mpp_flags);
+
+#define MPP_PRE_SELECT (1<<0)
+#define MPP_CONDITIONAL_PRE_SELECT (1<<1)
+#define MPP_CLOSE_ON_SIGNAL (1<<2)
+#define MPP_RECORD_TOUCH (1<<3)
+bool multi_process_post (struct multi_context *m, struct multi_instance *mi, const unsigned int flags);
+
+bool multi_process_incoming_link (struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags);
+bool multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flags);
+
+void multi_process_drop_outgoing_tun (struct multi_context *m, const unsigned int mpp_flags);
+
+void multi_print_status (struct multi_context *m, struct status_output *so, const int version);
+
+struct multi_instance *multi_get_queue (struct mbuf_set *ms);
+
+void multi_add_mbuf (struct multi_context *m,
+ struct multi_instance *mi,
+ struct mbuf_buffer *mb);
+
+void multi_ifconfig_pool_persist (struct multi_context *m, bool force);
+
+bool multi_process_signal (struct multi_context *m);
+
+void multi_close_instance_on_signal (struct multi_context *m, struct multi_instance *mi);
+
+void init_management_callback_multi (struct multi_context *m);
+void uninit_management_callback_multi (struct multi_context *m);
+
+/*
+ * Return true if our output queue is not full
+ */
+static inline bool
+multi_output_queue_ready (const struct multi_context *m,
+ const struct multi_instance *mi)
+{
+ if (mi->tcp_link_out_deferred)
+ return mbuf_len (mi->tcp_link_out_deferred) <= m->tcp_queue_limit;
+ else
+ return true;
+}
+
+/*
+ * Determine which instance has pending output
+ * and prepare the output for sending in
+ * the to_link buffer.
+ */
+static inline struct multi_instance *
+multi_process_outgoing_link_pre (struct multi_context *m)
+{
+ struct multi_instance *mi = NULL;
+
+ if (m->pending)
+ mi = m->pending;
+ else if (mbuf_defined (m->mbuf))
+ mi = multi_get_queue (m->mbuf);
+ return mi;
+}
+
+/*
+ * Per-client route quota management
+ */
+
+void route_quota_exceeded (const struct multi_context *m, const struct multi_instance *mi);
+
+static inline void
+route_quota_inc (struct multi_instance *mi)
+{
+ ++mi->route_count;
+}
+
+static inline void
+route_quota_dec (struct multi_instance *mi)
+{
+ --mi->route_count;
+}
+
+/* can we add a new route? */
+static inline bool
+route_quota_test (const struct multi_context *m, const struct multi_instance *mi)
+{
+ if (mi->route_count >= mi->context.options.max_routes_per_client)
+ {
+ route_quota_exceeded (m, mi);
+ return false;
+ }
+ else
+ return true;
+}
+
+/*
+ * Instance reference counting
+ */
+
+static inline void
+multi_instance_inc_refcount (struct multi_instance *mi)
+{
+ ++mi->refcount;
+}
+
+static inline void
+multi_instance_dec_refcount (struct multi_instance *mi)
+{
+ if (--mi->refcount <= 0)
+ {
+ gc_free (&mi->gc);
+ free (mi);
+ }
+}
+
+static inline void
+multi_route_del (struct multi_route *route)
+{
+ struct multi_instance *mi = route->instance;
+ route_quota_dec (mi);
+ multi_instance_dec_refcount (mi);
+ free (route);
+}
+
+static inline bool
+multi_route_defined (const struct multi_context *m,
+ const struct multi_route *r)
+{
+ if (r->instance->halt)
+ return false;
+ else if ((r->flags & MULTI_ROUTE_CACHE)
+ && r->cache_generation != m->route_helper->cache_generation)
+ return false;
+ else if ((r->flags & MULTI_ROUTE_AGEABLE)
+ && r->last_reference + m->route_helper->ageable_ttl_secs < now)
+ return false;
+ else
+ return true;
+}
+
+/*
+ * Set a msg() function prefix with our current client instance ID.
+ */
+
+static inline void
+set_prefix (struct multi_instance *mi)
+{
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ if (mi->msg_prefix)
+ printf ("[%s]\n", mi->msg_prefix);
+#endif
+ msg_set_prefix (mi->msg_prefix);
+}
+
+static inline void
+clear_prefix (void)
+{
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("[NULL]\n");
+#endif
+ msg_set_prefix (NULL);
+}
+
+/*
+ * Instance Reaper
+ *
+ * Reaper constants. The reaper is the process where the virtual address
+ * and virtual route hash table is scanned for dead entries which are
+ * then removed. The hash table could potentially be quite large, so we
+ * don't want to reap in a single pass.
+ */
+
+#define REAP_MAX_WAKEUP 10 /* Do reap pass at least once per n seconds */
+#define REAP_DIVISOR 256 /* How many passes to cover whole hash table */
+#define REAP_MIN 16 /* Minimum number of buckets per pass */
+#define REAP_MAX 1024 /* Maximum number of buckets per pass */
+
+/*
+ * Mark a cached host route for deletion after this
+ * many seconds without any references.
+ */
+#define MULTI_CACHE_ROUTE_TTL 60
+
+static inline void
+multi_reap_process (const struct multi_context *m)
+{
+ void multi_reap_process_dowork (const struct multi_context *m);
+ if (m->reaper->last_call != now)
+ multi_reap_process_dowork (m);
+}
+
+static inline void
+multi_process_per_second_timers (struct multi_context *m)
+{
+ if (m->per_second_trigger != now)
+ {
+ void multi_process_per_second_timers_dowork (struct multi_context *m);
+ multi_process_per_second_timers_dowork (m);
+ m->per_second_trigger = now;
+ }
+}
+
+/*
+ * Compute earliest timeout expiry from the set of
+ * all instances. Output:
+ *
+ * m->earliest_wakeup : instance needing the earliest service.
+ * dest : earliest timeout as a delta in relation
+ * to current time.
+ */
+static inline void
+multi_get_timeout (struct multi_context *m, struct timeval *dest)
+{
+ struct timeval tv, current;
+
+ CLEAR (tv);
+ m->earliest_wakeup = (struct multi_instance *) schedule_get_earliest_wakeup (m->schedule, &tv);
+ if (m->earliest_wakeup)
+ {
+ ASSERT (!openvpn_gettimeofday (&current, NULL));
+ tv_delta (dest, &current, &tv);
+ if (dest->tv_sec >= REAP_MAX_WAKEUP)
+ {
+ m->earliest_wakeup = NULL;
+ dest->tv_sec = REAP_MAX_WAKEUP;
+ dest->tv_usec = 0;
+ }
+ }
+ else
+ {
+ dest->tv_sec = REAP_MAX_WAKEUP;
+ dest->tv_usec = 0;
+ }
+}
+
+/*
+ * Send a packet to TUN/TAP interface.
+ */
+static inline bool
+multi_process_outgoing_tun (struct multi_context *m, const unsigned int mpp_flags)
+{
+ struct multi_instance *mi = m->pending;
+ bool ret = true;
+
+ ASSERT (mi);
+#ifdef MULTI_DEBUG_EVENT_LOOP
+ printf ("%s -> TUN len=%d\n",
+ id(mi),
+ mi->context.c2.to_tun.len);
+#endif
+ set_prefix (mi);
+ process_outgoing_tun (&mi->context);
+ ret = multi_process_post (m, mi, mpp_flags);
+ clear_prefix ();
+ return ret;
+}
+
+static inline bool
+multi_process_outgoing_link_dowork (struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
+{
+ bool ret = true;
+ set_prefix (mi);
+ process_outgoing_link (&mi->context);
+ ret = multi_process_post (m, mi, mpp_flags);
+ clear_prefix ();
+ return ret;
+}
+
+/*
+ * Check for signals.
+ */
+#define MULTI_CHECK_SIG(m) EVENT_LOOP_CHECK_SIGNAL (&(m)->top, multi_process_signal, (m))
+
+static inline void
+multi_set_pending (struct multi_context *m, struct multi_instance *mi)
+{
+ m->pending = mi;
+}
+
+static inline void
+multi_release_io_lock (struct multi_context *m)
+{
+}
+
+#endif /* P2MP_SERVER */
+#endif /* MULTI_H */
diff --git a/ntlm.c b/ntlm.c
new file mode 100644
index 0000000..0453358
--- /dev/null
+++ b/ntlm.c
@@ -0,0 +1,353 @@
+/*
+ * ntlm proxy support for OpenVPN
+ *
+ * Copyright (C) 2004 William Preston
+ *
+ * *NTLMv2 support and domain name parsing by Miroslav Zajic, Nextsoft s.r.o.*
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if NTLM
+
+#include "common.h"
+#include "buffer.h"
+#include "misc.h"
+#include "socket.h"
+#include "fdmisc.h"
+#include "proxy.h"
+#include "ntlm.h"
+#include "base64.h"
+#include "crypto.h"
+
+#include "memdbg.h"
+
+
+/* 64bit datatype macros */
+#ifdef _MSC_VER
+ /* MS compilers */
+# define UINTEGER64 __int64
+# define UINT64(c) c ## Ui64
+#else
+ /* Non MS compilers */
+# define UINTEGER64 unsigned long long
+# define UINT64(c) c ## LL
+#endif
+
+
+
+
+static void
+create_des_keys(const unsigned char *hash, unsigned char *key)
+{
+ key[0] = hash[0];
+ key[1] = ((hash[0]&1)<<7)|(hash[1]>>1);
+ key[2] = ((hash[1]&3)<<6)|(hash[2]>>2);
+ key[3] = ((hash[2]&7)<<5)|(hash[3]>>3);
+ key[4] = ((hash[3]&15)<<4)|(hash[4]>>4);
+ key[5] = ((hash[4]&31)<<3)|(hash[5]>>5);
+ key[6] = ((hash[5]&63)<<2)|(hash[6]>>6);
+ key[7] = ((hash[6]&127)<<1);
+ des_set_odd_parity((des_cblock *)key);
+}
+
+static void
+gen_md4_hash (const char* data, int data_len, char *result)
+{
+ /* result is 16 byte md4 hash */
+
+ MD4_CTX c;
+ char md[16];
+
+ MD4_Init (&c);
+ MD4_Update (&c, data, data_len);
+ MD4_Final ((unsigned char *)md, &c);
+
+ memcpy (result, md, 16);
+}
+
+static void
+gen_hmac_md5 (const char* data, int data_len, const char* key, int key_len,char *result)
+{
+ unsigned int len;
+
+ HMAC_CTX c;
+ HMAC_Init (&c, key, key_len, EVP_md5());
+ HMAC_Update (&c, (const unsigned char *)data, data_len);
+ HMAC_Final (&c, (unsigned char *)result, &len);
+ HMAC_CTX_cleanup(&c);
+}
+
+static void
+gen_timestamp (unsigned char *timestamp)
+{
+ /* Copies 8 bytes long timestamp into "timestamp" buffer.
+ * Timestamp is Little-endian, 64-bit signed value representing the number of tenths of a microsecond since January 1, 1601.
+ */
+
+ UINTEGER64 timestamp_ull;
+
+ timestamp_ull = openvpn_time(NULL);
+ timestamp_ull = (timestamp_ull + UINT64(11644473600)) * UINT64(10000000);
+
+ /* store little endian value */
+ timestamp[0]= timestamp_ull & UINT64(0xFF);
+ timestamp[1]= (timestamp_ull >> 8) & UINT64(0xFF);
+ timestamp[2]= (timestamp_ull >> 16) & UINT64(0xFF);
+ timestamp[3]= (timestamp_ull >> 24) & UINT64(0xFF);
+ timestamp[4]= (timestamp_ull >> 32) & UINT64(0xFF);
+ timestamp[5]= (timestamp_ull >> 40) & UINT64(0xFF);
+ timestamp[6]= (timestamp_ull >> 48) & UINT64(0xFF);
+ timestamp[7]= (timestamp_ull >> 56) & UINT64(0xFF);
+}
+
+static void
+gen_nonce (unsigned char *nonce)
+{
+ /* Generates 8 random bytes to be used as client nonce */
+ int i;
+
+ for(i=0;i<8;i++){
+ nonce[i] = (unsigned char)get_random();
+ }
+}
+
+unsigned char *my_strupr(unsigned char *str)
+{
+ /* converts string to uppercase in place */
+ unsigned char *tmp = str;;
+
+ do *str = toupper(*str); while (*(++str));
+ return tmp;
+}
+
+static int
+unicodize (char *dst, const char *src)
+{
+ /* not really unicode... */
+ int i = 0;
+ do
+ {
+ dst[i++] = *src;
+ dst[i++] = 0;
+ }
+ while (*src++);
+
+ return i;
+}
+
+static void
+add_security_buffer(int sb_offset, void *data, int length, unsigned char *msg_buf, int *msg_bufpos)
+{
+ /* Adds security buffer data to a message and sets security buffer's offset and length */
+ msg_buf[sb_offset] = (unsigned char)length;
+ msg_buf[sb_offset + 2] = msg_buf[sb_offset];
+ msg_buf[sb_offset + 4] = (unsigned char)(*msg_bufpos & 0xff);
+ msg_buf[sb_offset + 5] = (unsigned char)((*msg_bufpos >> 8) & 0xff);
+ memcpy(&msg_buf[*msg_bufpos], data, msg_buf[sb_offset]);
+ *msg_bufpos += length;
+}
+
+const char *
+ntlm_phase_1 (const struct http_proxy_info *p, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (96, gc);
+ /* try a minimal NTLM handshake
+ *
+ * http://davenport.sourceforge.net/ntlm.html
+ *
+ * This message contains only the NTLMSSP signature,
+ * the NTLM message type,
+ * and the minimal set of flags (Negotiate NTLM and Negotiate OEM).
+ *
+ */
+ buf_printf (&out, "%s", "TlRMTVNTUAABAAAAAgIAAA==");
+ return (BSTR (&out));
+}
+
+const char *
+ntlm_phase_3 (const struct http_proxy_info *p, const char *phase_2, struct gc_arena *gc)
+{
+ /* NTLM handshake
+ *
+ * http://davenport.sourceforge.net/ntlm.html
+ *
+ */
+
+ char pwbuf[sizeof (p->up.password) * 2]; /* for unicode password */
+ char buf2[128]; /* decoded reply from proxy */
+ unsigned char phase3[464];
+
+ char md4_hash[21];
+ char challenge[8], ntlm_response[24];
+ int i, ret_val;
+ des_cblock key1, key2, key3;
+ des_key_schedule sched1, sched2, sched3;
+
+ char ntlmv2_response[144];
+ char userdomain_u[256]; /* for uppercase unicode username and domain */
+ char userdomain[128]; /* the same as previous but ascii */
+ char ntlmv2_hash[16];
+ char ntlmv2_hmacmd5[16];
+ char *ntlmv2_blob = ntlmv2_response + 16; /* inside ntlmv2_response, length: 128 */
+ int ntlmv2_blob_size=0;
+ int phase3_bufpos = 0x40; /* offset to next security buffer data to be added */
+ size_t len;
+
+ char domain[128];
+ char username[128];
+ char *separator;
+
+ bool ntlmv2_enabled = (p->auth_method == HTTP_AUTH_NTLM2);
+
+ CLEAR (buf2);
+
+ ASSERT (strlen (p->up.username) > 0);
+ ASSERT (strlen (p->up.password) > 0);
+
+ /* username parsing */
+ separator = strchr(p->up.username, '\\');
+ if (separator == NULL) {
+ strncpy(username, p->up.username, sizeof(username)-1);
+ username[sizeof(username)-1]=0;
+ domain[0]=0;
+ } else {
+ strncpy(username, separator+1, sizeof(username)-1);
+ username[sizeof(username)-1]=0;
+ len = separator - p->up.username;
+ if (len > sizeof(domain) - 1) len = sizeof(domain) - 1;
+ strncpy(domain, p->up.username, len);
+ domain[len]=0;
+ }
+
+
+ /* fill 1st 16 bytes with md4 hash, disregard terminating null */
+ gen_md4_hash (pwbuf, unicodize (pwbuf, p->up.password) - 2, md4_hash);
+
+ /* pad to 21 bytes */
+ memset (md4_hash + 16, 0, 5);
+
+ ret_val = base64_decode( phase_2, (void *)buf2);
+ if (ret_val < 0)
+ return NULL;
+
+ /* we can be sure that phase_2 is less than 128
+ * therefore buf2 needs to be (3/4 * 128) */
+
+ /* extract the challenge from bytes 24-31 */
+ for (i=0; i<8; i++)
+ {
+ challenge[i] = buf2[i+24];
+ }
+
+ if (ntlmv2_enabled){ /* Generate NTLMv2 response */
+ int tib_len;
+
+ /* NTLMv2 hash */
+ my_strupr((unsigned char *)strcpy(userdomain, username));
+ if (strlen(username) + strlen(domain) < sizeof(userdomain))
+ strcat(userdomain, domain);
+ else
+ msg (M_INFO, "Warning: Username or domain too long");
+ unicodize (userdomain_u, userdomain);
+ gen_hmac_md5(userdomain_u, 2 * strlen(userdomain), md4_hash, 16, ntlmv2_hash);
+
+ /* NTLMv2 Blob */
+ memset(ntlmv2_blob, 0, 128); /* Clear blob buffer */
+ ntlmv2_blob[0x00]=1; /* Signature */
+ ntlmv2_blob[0x01]=1; /* Signature */
+ ntlmv2_blob[0x04]=0; /* Reserved */
+ gen_timestamp((unsigned char *)&ntlmv2_blob[0x08]); /* 64-bit Timestamp */
+ gen_nonce((unsigned char *)&ntlmv2_blob[0x10]); /* 64-bit Client Nonce */
+ ntlmv2_blob[0x18]=0; /* Unknown, zero should work */
+
+ /* Add target information block to the blob */
+ if (( *((long *)&buf2[0x14]) & 0x00800000) == 0x00800000){ /* Check for Target Information block */
+ tib_len = buf2[0x28];/* Get Target Information block size */
+ if (tib_len > 96) tib_len = 96;
+ {
+ char *tib_ptr = buf2 + buf2[0x2c]; /* Get Target Information block pointer */
+ memcpy(&ntlmv2_blob[0x1c], tib_ptr, tib_len); /* Copy Target Information block into the blob */
+ }
+ } else {
+ tib_len = 0;
+ }
+
+ ntlmv2_blob[0x1c + tib_len] = 0; /* Unknown, zero works */
+
+ /* Get blob length */
+ ntlmv2_blob_size = 0x20 + tib_len;
+
+ /* Add challenge from message 2 */
+ memcpy(&ntlmv2_response[8], challenge, 8);
+
+ /* hmac-md5 */
+ gen_hmac_md5(&ntlmv2_response[8], ntlmv2_blob_size + 8, ntlmv2_hash, 16, ntlmv2_hmacmd5);
+
+ /* Add hmac-md5 result to the blob */
+ memcpy(ntlmv2_response, ntlmv2_hmacmd5, 16); /* Note: This overwrites challenge previously written at ntlmv2_response[8..15] */
+
+ } else { /* Generate NTLM response */
+
+ create_des_keys ((unsigned char *)md4_hash, key1);
+ des_set_key_unchecked ((des_cblock *)key1, sched1);
+ des_ecb_encrypt ((des_cblock *)challenge, (des_cblock *)ntlm_response, sched1, DES_ENCRYPT);
+
+ create_des_keys ((unsigned char *)&(md4_hash[7]), key2);
+ des_set_key_unchecked ((des_cblock *)key2, sched2);
+ des_ecb_encrypt ((des_cblock *)challenge, (des_cblock *)&(ntlm_response[8]), sched2, DES_ENCRYPT);
+
+ create_des_keys ((unsigned char *)&(md4_hash[14]), key3);
+ des_set_key_unchecked ((des_cblock *)key3, sched3);
+ des_ecb_encrypt ((des_cblock *)challenge, (des_cblock *)&(ntlm_response[16]), sched3, DES_ENCRYPT);
+ }
+
+
+ memset (phase3, 0, sizeof (phase3)); /* clear reply */
+
+ strcpy ((char *)phase3, "NTLMSSP\0"); /* signature */
+ phase3[8] = 3; /* type 3 */
+
+ if (ntlmv2_enabled){ /* NTLMv2 response */
+ add_security_buffer(0x14, ntlmv2_response, ntlmv2_blob_size + 16, phase3, &phase3_bufpos);
+ }else{ /* NTLM response */
+ add_security_buffer(0x14, ntlm_response, 24, phase3, &phase3_bufpos);
+ }
+
+ /* username in ascii */
+ add_security_buffer(0x24, username, strlen (username), phase3, &phase3_bufpos);
+
+ /* Set domain. If <domain> is empty, default domain will be used (i.e. proxy's domain) */
+ add_security_buffer(0x1c, domain, strlen (domain), phase3, &phase3_bufpos);
+
+
+ /* other security buffers will be empty */
+ phase3[0x10] = phase3_bufpos; /* lm not used */
+ phase3[0x30] = phase3_bufpos; /* no workstation name supplied */
+ phase3[0x38] = phase3_bufpos; /* no session key */
+
+ /* flags */
+ phase3[0x3c] = 0x02; /* negotiate oem */
+ phase3[0x3d] = 0x02; /* negotiate ntlm */
+
+ return ((const char *)make_base64_string2 ((unsigned char *)phase3, phase3_bufpos, gc));
+}
+
+#else
+static void dummy(void) {}
+#endif
diff --git a/ntlm.h b/ntlm.h
new file mode 100644
index 0000000..77903b0
--- /dev/null
+++ b/ntlm.h
@@ -0,0 +1,11 @@
+#ifndef NTLM_H
+#define NTLM_H
+
+#if NTLM
+
+const char *ntlm_phase_1 (const struct http_proxy_info *p, struct gc_arena *gc);
+const char *ntlm_phase_3 (const struct http_proxy_info *p, const char *phase_2, struct gc_arena *gc);
+
+#endif
+
+#endif
diff --git a/occ-inline.h b/occ-inline.h
new file mode 100644
index 0000000..516eb4d
--- /dev/null
+++ b/occ-inline.h
@@ -0,0 +1,85 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OCC_INLINE_H
+#define OCC_INLINE_H
+
+#ifdef ENABLE_OCC
+
+/*
+ * Inline functions
+ */
+
+static inline int
+occ_reset_op ()
+{
+ return -1;
+}
+
+/*
+ * Should we send an OCC_REQUEST message?
+ */
+static inline void
+check_send_occ_req (struct context *c)
+{
+ void check_send_occ_req_dowork (struct context *c);
+ if (event_timeout_defined (&c->c2.occ_interval)
+ && event_timeout_trigger (&c->c2.occ_interval,
+ &c->c2.timeval,
+ (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0))
+ check_send_occ_req_dowork (c);
+}
+
+/*
+ * Should we send an MTU load test?
+ */
+static inline void
+check_send_occ_load_test (struct context *c)
+{
+ void check_send_occ_load_test_dowork (struct context *c);
+ if (event_timeout_defined (&c->c2.occ_mtu_load_test_interval)
+ && event_timeout_trigger (&c->c2.occ_mtu_load_test_interval,
+ &c->c2.timeval,
+ (!TO_LINK_DEF(c) && c->c2.occ_op < 0) ? ETT_DEFAULT : 0))
+ check_send_occ_load_test_dowork (c);
+}
+
+/*
+ * Should we send an OCC message?
+ */
+static inline void
+check_send_occ_msg (struct context *c)
+{
+ void check_send_occ_msg_dowork (struct context *c);
+ if (c->c2.occ_op >= 0)
+ {
+ if (!TO_LINK_DEF(c))
+ check_send_occ_msg_dowork (c);
+ else
+ tv_clear (&c->c2.timeval); /* ZERO-TIMEOUT */
+ }
+}
+
+#endif
+#endif
diff --git a/occ.c b/occ.c
new file mode 100644
index 0000000..b84b18d
--- /dev/null
+++ b/occ.c
@@ -0,0 +1,393 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_OCC
+
+#include "occ.h"
+
+#include "memdbg.h"
+
+#include "forward-inline.h"
+#include "occ-inline.h"
+
+/*
+ * This random string identifies an OpenVPN
+ * Configuration Control packet.
+ * It should be of sufficient length and randomness
+ * so as not to collide with other tunnel data.
+ *
+ * The OCC protocol is as follows:
+ *
+ * occ_magic -- (16 octets)
+ *
+ * type [OCC_REQUEST | OCC_REPLY] (1 octet)
+ * null terminated options string if OCC_REPLY (variable)
+ *
+ * When encryption is used, the OCC packet
+ * is encapsulated within the encrypted
+ * envelope.
+ *
+ * OCC_STRING_SIZE must be set to sizeof (occ_magic)
+ */
+
+const uint8_t occ_magic[] = {
+ 0x28, 0x7f, 0x34, 0x6b, 0xd4, 0xef, 0x7a, 0x81,
+ 0x2d, 0x56, 0xb8, 0xd3, 0xaf, 0xc5, 0x45, 0x9c
+};
+
+static const struct mtu_load_test mtu_load_test_sequence[] = {
+
+ {OCC_MTU_LOAD_REQUEST, -1000},
+ {OCC_MTU_LOAD, -1000},
+ {OCC_MTU_LOAD_REQUEST, -1000},
+ {OCC_MTU_LOAD, -1000},
+ {OCC_MTU_LOAD_REQUEST, -1000},
+ {OCC_MTU_LOAD, -1000},
+
+ {OCC_MTU_LOAD_REQUEST, -750},
+ {OCC_MTU_LOAD, -750},
+ {OCC_MTU_LOAD_REQUEST, -750},
+ {OCC_MTU_LOAD, -750},
+ {OCC_MTU_LOAD_REQUEST, -750},
+ {OCC_MTU_LOAD, -750},
+
+ {OCC_MTU_LOAD_REQUEST, -500},
+ {OCC_MTU_LOAD, -500},
+ {OCC_MTU_LOAD_REQUEST, -500},
+ {OCC_MTU_LOAD, -500},
+ {OCC_MTU_LOAD_REQUEST, -500},
+ {OCC_MTU_LOAD, -500},
+
+ {OCC_MTU_LOAD_REQUEST, -400},
+ {OCC_MTU_LOAD, -400},
+ {OCC_MTU_LOAD_REQUEST, -400},
+ {OCC_MTU_LOAD, -400},
+ {OCC_MTU_LOAD_REQUEST, -400},
+ {OCC_MTU_LOAD, -400},
+
+ {OCC_MTU_LOAD_REQUEST, -300},
+ {OCC_MTU_LOAD, -300},
+ {OCC_MTU_LOAD_REQUEST, -300},
+ {OCC_MTU_LOAD, -300},
+ {OCC_MTU_LOAD_REQUEST, -300},
+ {OCC_MTU_LOAD, -300},
+
+ {OCC_MTU_LOAD_REQUEST, -200},
+ {OCC_MTU_LOAD, -200},
+ {OCC_MTU_LOAD_REQUEST, -200},
+ {OCC_MTU_LOAD, -200},
+ {OCC_MTU_LOAD_REQUEST, -200},
+ {OCC_MTU_LOAD, -200},
+
+ {OCC_MTU_LOAD_REQUEST, -150},
+ {OCC_MTU_LOAD, -150},
+ {OCC_MTU_LOAD_REQUEST, -150},
+ {OCC_MTU_LOAD, -150},
+ {OCC_MTU_LOAD_REQUEST, -150},
+ {OCC_MTU_LOAD, -150},
+
+ {OCC_MTU_LOAD_REQUEST, -100},
+ {OCC_MTU_LOAD, -100},
+ {OCC_MTU_LOAD_REQUEST, -100},
+ {OCC_MTU_LOAD, -100},
+ {OCC_MTU_LOAD_REQUEST, -100},
+ {OCC_MTU_LOAD, -100},
+
+ {OCC_MTU_LOAD_REQUEST, -50},
+ {OCC_MTU_LOAD, -50},
+ {OCC_MTU_LOAD_REQUEST, -50},
+ {OCC_MTU_LOAD, -50},
+ {OCC_MTU_LOAD_REQUEST, -50},
+ {OCC_MTU_LOAD, -50},
+
+ {OCC_MTU_LOAD_REQUEST, 0},
+ {OCC_MTU_LOAD, 0},
+ {OCC_MTU_LOAD_REQUEST, 0},
+ {OCC_MTU_LOAD, 0},
+ {OCC_MTU_LOAD_REQUEST, 0},
+ {OCC_MTU_LOAD, 0},
+
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+ {OCC_MTU_REQUEST, 0},
+
+ {-1, 0}
+};
+
+void
+check_send_occ_req_dowork (struct context *c)
+{
+ if (++c->c2.occ_n_tries >= OCC_N_TRIES)
+ {
+ if (c->options.ce.remote)
+ /*
+ * No OCC_REPLY from peer after repeated attempts.
+ * Give up.
+ */
+ msg (D_SHOW_OCC,
+ "NOTE: failed to obtain options consistency info from peer -- "
+ "this could occur if the remote peer is running a version of "
+ PACKAGE_NAME
+ " before 1.5-beta8 or if there is a network connectivity problem, and will not necessarily prevent "
+ PACKAGE_NAME
+ " from running (" counter_format " bytes received from peer, " counter_format
+ " bytes authenticated data channel traffic) -- you can disable the options consistency "
+ "check with --disable-occ.",
+ c->c2.link_read_bytes,
+ c->c2.link_read_bytes_auth);
+ event_timeout_clear (&c->c2.occ_interval);
+ }
+ else
+ {
+ c->c2.occ_op = OCC_REQUEST;
+
+ /*
+ * If we don't hear back from peer, send another
+ * OCC_REQUEST in OCC_INTERVAL_SECONDS.
+ */
+ event_timeout_reset (&c->c2.occ_interval);
+ }
+}
+
+void
+check_send_occ_load_test_dowork (struct context *c)
+{
+ if (CONNECTION_ESTABLISHED (c))
+ {
+ const struct mtu_load_test *entry;
+
+ if (!c->c2.occ_mtu_load_n_tries)
+ msg (M_INFO,
+ "NOTE: Beginning empirical MTU test -- results should be available in 3 to 4 minutes.");
+
+ entry = &mtu_load_test_sequence[c->c2.occ_mtu_load_n_tries++];
+ if (entry->op >= 0)
+ {
+ c->c2.occ_op = entry->op;
+ c->c2.occ_mtu_load_size =
+ EXPANDED_SIZE (&c->c2.frame) + entry->delta;
+ }
+ else
+ {
+ msg (M_INFO,
+ "NOTE: failed to empirically measure MTU (requires " PACKAGE_NAME " 1.5 or higher at other end of connection).");
+ event_timeout_clear (&c->c2.occ_mtu_load_test_interval);
+ c->c2.occ_mtu_load_n_tries = 0;
+ }
+ }
+}
+
+void
+check_send_occ_msg_dowork (struct context *c)
+{
+ bool doit = false;
+
+ c->c2.buf = c->c2.buffers->aux_buf;
+ ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame)));
+ ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)));
+ ASSERT (buf_write (&c->c2.buf, occ_magic, OCC_STRING_SIZE));
+
+ switch (c->c2.occ_op)
+ {
+ case OCC_REQUEST:
+ if (!buf_write_u8 (&c->c2.buf, OCC_REQUEST))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_REQUEST");
+ doit = true;
+ break;
+
+ case OCC_REPLY:
+ if (!c->c2.options_string_local)
+ break;
+ if (!buf_write_u8 (&c->c2.buf, OCC_REPLY))
+ break;
+ if (!buf_write (&c->c2.buf, c->c2.options_string_local,
+ strlen (c->c2.options_string_local) + 1))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_REPLY");
+ doit = true;
+ break;
+
+ case OCC_MTU_REQUEST:
+ if (!buf_write_u8 (&c->c2.buf, OCC_MTU_REQUEST))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_REQUEST");
+ doit = true;
+ break;
+
+ case OCC_MTU_REPLY:
+ if (!buf_write_u8 (&c->c2.buf, OCC_MTU_REPLY))
+ break;
+ if (!buf_write_u16 (&c->c2.buf, c->c2.max_recv_size_local))
+ break;
+ if (!buf_write_u16 (&c->c2.buf, c->c2.max_send_size_local))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_REPLY");
+ doit = true;
+ break;
+
+ case OCC_MTU_LOAD_REQUEST:
+ if (!buf_write_u8 (&c->c2.buf, OCC_MTU_LOAD_REQUEST))
+ break;
+ if (!buf_write_u16 (&c->c2.buf, c->c2.occ_mtu_load_size))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_LOAD_REQUEST");
+ doit = true;
+ break;
+
+ case OCC_MTU_LOAD:
+ {
+ int need_to_add;
+
+ if (!buf_write_u8 (&c->c2.buf, OCC_MTU_LOAD))
+ break;
+ need_to_add = min_int (c->c2.occ_mtu_load_size, EXPANDED_SIZE (&c->c2.frame))
+ - OCC_STRING_SIZE
+ - sizeof (uint8_t)
+ - EXTRA_FRAME (&c->c2.frame);
+
+ while (need_to_add > 0)
+ {
+ /*
+ * Fill the load test packet with pseudo-random bytes.
+ */
+ if (!buf_write_u8 (&c->c2.buf, get_random () & 0xFF))
+ break;
+ --need_to_add;
+ }
+ dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_LOAD min_int(%d-%d-%d-%d,%d) size=%d",
+ c->c2.occ_mtu_load_size,
+ OCC_STRING_SIZE,
+ (int) sizeof (uint8_t),
+ EXTRA_FRAME (&c->c2.frame),
+ MAX_RW_SIZE_TUN (&c->c2.frame),
+ BLEN (&c->c2.buf));
+ doit = true;
+ }
+ break;
+
+ case OCC_EXIT:
+ if (!buf_write_u8 (&c->c2.buf, OCC_EXIT))
+ break;
+ dmsg (D_PACKET_CONTENT, "SENT OCC_EXIT");
+ doit = true;
+ break;
+ }
+
+ if (doit)
+ {
+ /*
+ * We will treat the packet like any other outgoing packet,
+ * compress, encrypt, sign, etc.
+ */
+ encrypt_sign (c, true);
+ }
+
+ c->c2.occ_op = -1;
+}
+
+void
+process_received_occ_msg (struct context *c)
+{
+ ASSERT (buf_advance (&c->c2.buf, OCC_STRING_SIZE));
+ switch (buf_read_u8 (&c->c2.buf))
+ {
+ case OCC_REQUEST:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_REQUEST");
+ c->c2.occ_op = OCC_REPLY;
+ break;
+
+ case OCC_MTU_REQUEST:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_REQUEST");
+ c->c2.occ_op = OCC_MTU_REPLY;
+ break;
+
+ case OCC_MTU_LOAD_REQUEST:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_LOAD_REQUEST");
+ c->c2.occ_mtu_load_size = buf_read_u16 (&c->c2.buf);
+ if (c->c2.occ_mtu_load_size >= 0)
+ c->c2.occ_op = OCC_MTU_LOAD;
+ break;
+
+ case OCC_REPLY:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_REPLY");
+ if (c->options.occ && !TLS_MODE (c) && c->c2.options_string_remote)
+ {
+ if (!options_cmp_equal_safe ((char *) BPTR (&c->c2.buf),
+ c->c2.options_string_remote,
+ c->c2.buf.len))
+ {
+ options_warning_safe ((char *) BPTR (&c->c2.buf),
+ c->c2.options_string_remote,
+ c->c2.buf.len);
+ }
+ }
+ event_timeout_clear (&c->c2.occ_interval);
+ break;
+
+ case OCC_MTU_REPLY:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_REPLY");
+ c->c2.max_recv_size_remote = buf_read_u16 (&c->c2.buf);
+ c->c2.max_send_size_remote = buf_read_u16 (&c->c2.buf);
+ if (c->options.mtu_test
+ && c->c2.max_recv_size_remote > 0
+ && c->c2.max_send_size_remote > 0)
+ {
+ msg (M_INFO, "NOTE: Empirical MTU test completed [Tried,Actual] local->remote=[%d,%d] remote->local=[%d,%d]",
+ c->c2.max_send_size_local,
+ c->c2.max_recv_size_remote,
+ c->c2.max_send_size_remote,
+ c->c2.max_recv_size_local);
+ if (!c->options.fragment
+ && c->options.ce.proto == PROTO_UDPv4
+ && c->c2.max_send_size_local > TUN_MTU_MIN
+ && (c->c2.max_recv_size_remote < c->c2.max_send_size_local
+ || c->c2.max_recv_size_local < c->c2.max_send_size_remote))
+ msg (M_INFO, "NOTE: This connection is unable to accomodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.",
+ c->c2.max_send_size_local);
+ }
+ event_timeout_clear (&c->c2.occ_mtu_load_test_interval);
+ break;
+
+ case OCC_EXIT:
+ dmsg (D_PACKET_CONTENT, "RECEIVED OCC_EXIT");
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "remote-exit";
+ break;
+ }
+ c->c2.buf.len = 0; /* don't pass packet on */
+}
+
+#else
+static void dummy(void) {}
+#endif
diff --git a/occ.h b/occ.h
new file mode 100644
index 0000000..5d88cc9
--- /dev/null
+++ b/occ.h
@@ -0,0 +1,95 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OCC_H
+#define OCC_H
+
+#ifdef ENABLE_OCC
+
+#include "forward.h"
+
+/* OCC_STRING_SIZE must be set to sizeof (occ_magic) */
+#define OCC_STRING_SIZE 16
+
+/*
+ * OCC (OpenVPN Configuration Control) protocol opcodes.
+ */
+
+#define OCC_REQUEST 0 /* request options string from peer */
+#define OCC_REPLY 1 /* deliver options string to peer */
+
+/*
+ * Send an OCC_REQUEST once every OCC_INTERVAL
+ * seconds until a reply is received.
+ *
+ * If we haven't received a reply after
+ * OCC_N_TRIES, give up.
+ */
+#define OCC_INTERVAL_SECONDS 10
+#define OCC_N_TRIES 12
+
+/*
+ * Other OCC protocol opcodes used to estimate the MTU empirically.
+ */
+#define OCC_MTU_LOAD_REQUEST 2 /* Ask peer to send a big packet to us */
+#define OCC_MTU_LOAD 3 /* Send a big packet to peer */
+#define OCC_MTU_REQUEST 4 /* Ask peer to tell us the largest
+ packet it has received from us so far */
+#define OCC_MTU_REPLY 5 /* Send largest packet size to peer */
+
+/*
+ * Process one command from mtu_load_test_sequence
+ * once every n seconds, if --mtu-test is specified.
+ */
+#define OCC_MTU_LOAD_INTERVAL_SECONDS 3
+
+/*
+ * Send an exit message to remote.
+ */
+#define OCC_EXIT 6
+
+/*
+ * Used to conduct a load test command sequence
+ * of UDP connection for empirical MTU measurement.
+ */
+struct mtu_load_test
+{
+ int op; /* OCC opcode to send to peer */
+ int delta; /* determine packet size to send by using
+ this delta against currently
+ configured MTU */
+};
+
+extern const uint8_t occ_magic[];
+
+static inline bool
+is_occ_msg (const struct buffer* buf)
+{
+ return buf_string_match_head (buf, occ_magic, OCC_STRING_SIZE);
+}
+
+void process_received_occ_msg (struct context *c);
+
+#endif
+#endif
diff --git a/openvpn-plugin.h b/openvpn-plugin.h
new file mode 100644
index 0000000..173a0c1
--- /dev/null
+++ b/openvpn-plugin.h
@@ -0,0 +1,461 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define OPENVPN_PLUGIN_VERSION 2
+
+/*
+ * 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_N 12
+
+/*
+ * 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(__MINGW32_VERSION) && !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
+
+/*
+ * 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;
+};
+
+/*
+ * 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_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[]);
diff --git a/openvpn.8 b/openvpn.8
new file mode 100644
index 0000000..67a9779
--- /dev/null
+++ b/openvpn.8
@@ -0,0 +1,5967 @@
+.\" 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+.\" distribution); if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+.\"
+.\" Manual page for openvpn
+.\
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.\
+.\" .nf -- no formatting
+.\" .fi -- resume formatting
+.\" .ft 3 -- boldface
+.\" .ft -- normal face
+.\" .in +|-{n} -- indent
+.\"
+.TH openvpn 8 "17 November 2008"
+.\"*********************************************************
+.SH NAME
+openvpn \- secure IP tunnel daemon.
+.\"*********************************************************
+.SH SYNOPSIS
+.ft 3
+openvpn [ options ... ]
+.ft
+.\"*********************************************************
+.SH INTRODUCTION
+.LP
+OpenVPN is an open source VPN daemon by James Yonan.
+Because OpenVPN tries to
+be a universal VPN tool offering a great deal of flexibility,
+there are a lot of options on this manual page.
+If you're new to OpenVPN, you might want to skip ahead to the
+examples section where you will see how to construct simple
+VPNs on the command line without even needing a configuration file.
+
+Also note that there's more documentation and examples on
+the OpenVPN web site:
+.I http://openvpn.net/
+
+And if you would like to see a shorter version of this manual,
+see the openvpn usage message which can be obtained by
+running
+.B openvpn
+without any parameters.
+.\"*********************************************************
+.SH DESCRIPTION
+.LP
+OpenVPN is a robust and highly flexible VPN daemon.
+OpenVPN supports SSL/TLS security, ethernet bridging,
+TCP or UDP tunnel transport through proxies or NAT,
+support for dynamic IP addresses and DHCP,
+scalability to hundreds or thousands of users,
+and portability to most major OS platforms.
+
+OpenVPN is tightly bound to the OpenSSL library, and derives much
+of its crypto capabilities from it.
+
+OpenVPN supports
+conventional encryption
+using a pre-shared secret key
+.B (Static Key mode)
+or
+public key security
+.B (SSL/TLS mode)
+using client & server certificates.
+OpenVPN also
+supports non-encrypted TCP/UDP tunnels.
+
+OpenVPN is designed to work with the
+.B TUN/TAP
+virtual networking interface that exists on most platforms.
+
+Overall, OpenVPN aims to offer many of the key features of IPSec but
+with a relatively lightweight footprint.
+.\"*********************************************************
+.SH OPTIONS
+OpenVPN allows any option to be placed either on the command line
+or in a configuration file. Though all command line options are preceded
+by a double-leading-dash ("\-\-"), this prefix can be removed when
+an option is placed in a configuration file.
+.\"*********************************************************
+.TP
+.B \-\-help
+Show options.
+.\"*********************************************************
+.TP
+.B \-\-config file
+Load additional config options from
+.B file
+where each line corresponds to one command line option,
+but with the leading '\-\-' removed.
+
+If
+.B \-\-config file
+is the only option to the openvpn command,
+the
+.B \-\-config
+can be removed, and the command can be given as
+.B openvpn file
+
+Note that
+configuration files can be nested to a reasonable depth.
+
+Double quotation or single quotation characters ("", '')
+can be used to enclose single parameters containing whitespace,
+and "#" or ";" characters in the first column
+can be used to denote comments.
+
+Note that OpenVPN 2.0 and higher performs backslash-based shell
+escaping for characters not in single quotations,
+so the following mappings should be observed:
+
+.nf
+.ft 3
+.in +4
+\\\\ Maps to a single backslash character (\\).
+\\" Pass a literal doublequote character ("), don't
+ interpret it as enclosing a parameter.
+\\[SPACE] Pass a literal space or tab character, don't
+ interpret it as a parameter delimiter.
+.in -4
+.ft
+.fi
+
+For example on Windows, use double backslashes to
+represent pathnames:
+
+.nf
+.ft 3
+.in +4
+secret "c:\\\\OpenVPN\\\\secret.key"
+.in -4
+.ft
+.fi
+
+For examples of configuration files,
+see
+.I http://openvpn.net/examples.html
+
+Here is an example configuration file:
+
+.nf
+.ft 3
+.in +4
+#
+# Sample OpenVPN configuration file for
+# using a pre-shared static key.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+dev tun
+
+# Our remote peer
+remote mypeer.mydomain
+
+# 10.1.0.1 is our local VPN endpoint
+# 10.1.0.2 is our remote VPN endpoint
+ifconfig 10.1.0.1 10.1.0.2
+
+# Our pre-shared static key
+secret static.key
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.SS Tunnel Options:
+.TP
+.B \-\-mode m
+Set OpenVPN major mode. By default, OpenVPN runs in
+point-to-point mode ("p2p"). OpenVPN 2.0 introduces
+a new mode ("server") which implements a multi-client
+server capability.
+.\"*********************************************************
+.TP
+.B \-\-local host
+Local host name or IP address for bind.
+If specified, OpenVPN will bind to this address only.
+If unspecified, OpenVPN will bind to all interfaces.
+.\"*********************************************************
+.TP
+.B \-\-remote host [port] [proto]
+Remote host name or IP address. On the client, multiple
+.B \-\-remote
+options may be specified for redundancy, each referring
+to a different OpenVPN server. Specifying multiple
+.B \-\-remote
+options for this purpose is a special case of the more
+general connection-profile feature. See the
+.B <connection>
+documentation below.
+
+The OpenVPN client will try to connect to a server at
+.B host:port
+in the order specified by the list of
+.B \-\-remote
+options.
+
+.B proto
+indicates the protocol to use when connecting with the
+remote, and may be "tcp" or "udp".
+
+The client will move on to the next host in the list,
+in the event of connection failure.
+Note that at any given time, the OpenVPN client
+will at most be connected to
+one server.
+
+Note that since UDP is connectionless, connection failure
+is defined by the
+.B \-\-ping
+and
+.B \-\-ping-restart
+options.
+
+Note the following corner case: If you use multiple
+.B \-\-remote
+options, AND you are dropping root privileges on
+the client with
+.B \-\-user
+and/or
+.B \-\-group,
+AND the client is running a non-Windows OS, if the client needs
+to switch to a different server, and that server pushes
+back different TUN/TAP or route settings, the client may lack
+the necessary privileges to close and reopen the TUN/TAP interface.
+This could cause the client to exit with a fatal error.
+
+If
+.B \-\-remote
+is unspecified, OpenVPN will listen
+for packets from any IP address, but will not act on those packets unless
+they pass all authentication tests. This requirement for authentication
+is binding on all potential peers, even those from known and supposedly
+trusted IP addresses (it is very easy to forge a source IP address on
+a UDP packet).
+
+When used in TCP mode,
+.B \-\-remote
+will act as a filter, rejecting connections from any host which does
+not match
+.B host.
+
+If
+.B host
+is a DNS name which resolves to multiple IP addresses,
+one will be randomly
+chosen, providing a sort of basic load-balancing and
+failover capability.
+.\"*********************************************************
+.TP
+.B \-\-remote-random-hostname
+Add a random string (6 characters) to first DNS label of hostname to prevent
+DNS caching. For example, "foo.bar.gov" would be modified to
+"<random-chars>.foo.bar.gov".
+.\"*********************************************************
+.TP
+.B <connection>
+Define a client connection
+profile. Client connection profiles are groups of OpenVPN options that
+describe how to connect to a given OpenVPN server. Client connection
+profiles are specified within an OpenVPN configuration file, and
+each profile is bracketed by
+.B <connection>
+and
+.B </connection>.
+
+An OpenVPN client will try each connection profile sequentially
+until it achieves a successful connection.
+
+.B \-\-remote-random
+can be used to initially "scramble" the connection
+list.
+
+Here is an example of connection profile usage:
+
+.nf
+.ft 3
+.in +4
+client
+dev tun
+
+<connection>
+remote 198.19.34.56 1194 udp
+</connection>
+
+<connection>
+remote 198.19.34.56 443 tcp
+</connection>
+
+<connection>
+remote 198.19.34.56 443 tcp
+http-proxy 192.168.0.8 8080
+http-proxy-retry
+</connection>
+
+<connection>
+remote 198.19.36.99 443 tcp
+http-proxy 192.168.0.8 8080
+http-proxy-retry
+</connection>
+
+persist-key
+persist-tun
+pkcs12 client.p12
+ns-cert-type server
+verb 3
+.in -4
+.ft
+.fi
+
+First we try to connect to a server at 198.19.34.56:1194 using UDP.
+If that fails, we then try to connect to 198.19.34.56:443 using TCP.
+If that also fails, then try connecting through an HTTP proxy at
+192.168.0.8:8080 to 198.19.34.56:443 using TCP. Finally, try to
+connect through the same proxy to a server at 198.19.36.99:443
+using TCP.
+
+The following OpenVPN options may be used inside of
+a
+.B <connection>
+block:
+
+.B bind,
+.B connect-retry,
+.B connect-retry-max,
+.B connect-timeout,
+.B float,
+.B http-proxy,
+.B http-proxy-option,
+.B http-proxy-retry,
+.B http-proxy-timeout,
+.B local,
+.B lport,
+.B nobind,
+.B port,
+.B proto,
+.B remote,
+.B rport,
+.B socks-proxy, and
+.B socks-proxy-retry.
+
+A defaulting mechanism exists for specifying options to apply to
+all
+.B <connection>
+profiles. If any of the above options (with the exception of
+.B remote
+) appear outside of a
+.B <connection>
+block, but in a configuration file which has one or more
+.B <connection>
+blocks, the option setting will be used as a default for
+.B <connection>
+blocks which follow it in the configuration file.
+
+For example, suppose the
+.B nobind
+option were placed in the sample configuration file above, near
+the top of the file, before the first
+.B <connection>
+block. The effect would be as if
+.B nobind
+were declared in all
+.B <connection>
+blocks below it.
+.\"*********************************************************
+.TP
+.B \-\-proto-force p
+When iterating through connection profiles,
+only consider profiles using protocol
+.B p
+('tcp'|'udp').
+.\"*********************************************************
+.TP
+.B \-\-remote-random
+When multiple
+.B \-\-remote
+address/ports are specified, or if connection profiles are being
+used, initially randomize the order of the list
+as a kind of basic load-balancing measure.
+.\"*********************************************************
+.TP
+.B \-\-proto p
+Use protocol
+.B p
+for communicating with remote host.
+.B p
+can be
+.B udp,
+.B tcp-client,
+or
+.B tcp-server.
+
+The default protocol is
+.B udp
+when
+.B \-\-proto
+is not specified.
+
+For UDP operation,
+.B \-\-proto udp
+should be specified on both peers.
+
+For TCP operation, one peer must use
+.B \-\-proto tcp-server
+and the other must use
+.B \-\-proto tcp-client.
+A peer started with
+.B tcp-server
+will wait indefinitely for an incoming connection. A peer
+started with
+.B tcp-client
+will attempt to connect, and if that fails, will sleep for 5
+seconds (adjustable via the
+.B \-\-connect-retry
+option) and try again infinite or up to N retries (adjustable via the
+.B \-\-connect-retry-max
+option). Both TCP client and server will simulate
+a SIGUSR1 restart signal if either side resets the connection.
+
+OpenVPN is designed to operate optimally over UDP, but TCP capability is provided
+for situations where UDP cannot be used.
+In comparison with UDP, TCP will usually be
+somewhat less efficient and less robust when used over unreliable or congested
+networks.
+
+This article outlines some of problems with tunneling IP over TCP:
+
+.I http://sites.inka.de/sites/bigred/devel/tcp-tcp.html
+
+There are certain cases, however, where using TCP may be advantageous from
+a security and robustness perspective, such as tunneling non-IP or
+application-level UDP protocols, or tunneling protocols which don't
+possess a built-in reliability layer.
+.\"*********************************************************
+.TP
+.B \-\-connect-retry n
+For
+.B \-\-proto tcp-client,
+take
+.B n
+as the
+number of seconds to wait
+between connection retries (default=5).
+.\"*********************************************************
+.TP
+.B \-\-connect-timeout n
+For
+.B \-\-proto tcp-client,
+set connection timeout to
+.B n
+seconds (default=10).
+.\"*********************************************************
+.TP
+.B \-\-connect-retry-max n
+For
+.B \-\-proto tcp-client,
+take
+.B n
+as the
+number of retries of connection attempt (default=infinite).
+.\"*********************************************************
+.TP
+.B \-\-auto-proxy
+Try to sense HTTP or SOCKS proxy settings automatically.
+If no settings are present, a direct connection will be attempted.
+If both HTTP and SOCKS settings are present, HTTP will be preferred.
+If the HTTP proxy server requires a password, it will be queried from
+stdin or the management interface. If the underlying OS doesn't support an API for
+returning proxy settings, a direct connection will be attempted.
+Currently, only Windows clients support this option via the
+InternetQueryOption API.
+This option exists in OpenVPN 2.1 or higher.
+.\"*********************************************************
+.TP
+.B \-\-show-proxy-settings
+Show sensed HTTP or SOCKS proxy settings. Currently, only Windows clients
+support this option.
+.\"*********************************************************
+.TP
+.B \-\-http-proxy server port [authfile|'auto'|'auto-nct'] [auth-method]
+Connect to remote host through an HTTP proxy at address
+.B server
+and port
+.B port.
+If HTTP Proxy-Authenticate is required,
+.B authfile
+is a file containing a username and password on 2 lines, or
+"stdin" to prompt from console.
+
+.B auth-method
+should be one of "none", "basic", or "ntlm".
+
+HTTP Digest authentication is supported as well, but only via
+the
+.B auto
+or
+.B auto-nct
+flags (below).
+
+The
+.B auto
+flag causes OpenVPN to automatically determine the
+.B auth-method
+and query stdin or the management interface for
+username/password credentials, if required. This flag
+exists on OpenVPN 2.1 or higher.
+
+The
+.B auto-nct
+flag (no clear-text auth) instructs OpenVPN to automatically
+determine the authentication method, but to reject weak
+authentication protocols such as HTTP Basic Authentication.
+.\"*********************************************************
+.TP
+.B \-\-http-proxy-retry
+Retry indefinitely on HTTP proxy errors. If an HTTP proxy error
+occurs, simulate a SIGUSR1 reset.
+.\"*********************************************************
+.TP
+.B \-\-http-proxy-timeout n
+Set proxy timeout to
+.B n
+seconds, default=5.
+.\"*********************************************************
+.TP
+.B \-\-http-proxy-option type [parm]
+Set extended HTTP proxy options.
+Repeat to set multiple options.
+
+.B VERSION version \-\-
+Set HTTP version number to
+.B version
+(default=1.0).
+
+.B AGENT user-agent \-\-
+Set HTTP "User-Agent" string to
+.B user-agent.
+.\"*********************************************************
+.TP
+.B \-\-socks-proxy server [port]
+Connect to remote host through a Socks5 proxy at address
+.B server
+and port
+.B port
+(default=1080).
+.\"*********************************************************
+.TP
+.B \-\-socks-proxy-retry
+Retry indefinitely on Socks proxy errors. If a Socks proxy error
+occurs, simulate a SIGUSR1 reset.
+.\"*********************************************************
+.TP
+.B \-\-resolv-retry n
+If hostname resolve fails for
+.B \-\-remote,
+retry resolve for
+.B n
+seconds before failing.
+
+Set
+.B n
+to "infinite" to retry indefinitely.
+
+By default,
+.B \-\-resolv-retry infinite
+is enabled. You can disable by setting n=0.
+.\"*********************************************************
+.TP
+.B \-\-float
+Allow remote peer to change its IP address and/or port number, such as due to
+DHCP (this is the default if
+.B \-\-remote
+is not used).
+.B \-\-float
+when specified with
+.B \-\-remote
+allows an OpenVPN session to initially connect to a peer
+at a known address, however if packets arrive from a new
+address and pass all authentication tests, the new address
+will take control of the session. This is useful when
+you are connecting to a peer which holds a dynamic address
+such as a dial-in user or DHCP client.
+
+Essentially,
+.B \-\-float
+tells OpenVPN to accept authenticated packets
+from any address, not only the address which was specified in the
+.B \-\-remote
+option.
+.\"*********************************************************
+.TP
+.B \-\-ipchange cmd
+Execute shell command
+.B cmd
+when our remote ip-address is initially authenticated or
+changes.
+
+Execute as:
+
+.B cmd ip_address port_number
+
+Don't use
+.B \-\-ipchange
+in
+.B \-\-mode server
+mode. Use a
+.B \-\-client-connect
+script instead.
+
+See the "Environmental Variables" section below for
+additional parameters passed as environmental variables.
+
+Note that
+.B cmd
+can be a shell command with multiple arguments, in which
+case all OpenVPN-generated arguments will be appended
+to
+.B cmd
+to build a command line which will be passed to the script.
+
+If you are running in a dynamic IP address environment where
+the IP addresses of either peer could change without notice,
+you can use this script, for example, to edit the
+.I /etc/hosts
+file with the current address of the peer. The script will
+be run every time the remote peer changes its IP address.
+
+Similarly if
+.I our
+IP address changes due to DHCP, we should configure
+our IP address change script (see man page for
+.BR dhcpcd (8)
+) to deliver a
+.B SIGHUP
+or
+.B SIGUSR1
+signal to OpenVPN. OpenVPN will then
+reestablish a connection with its most recently authenticated
+peer on its new IP address.
+.\"*********************************************************
+.TP
+.B \-\-port port
+TCP/UDP port number for both local and remote. The current
+default of 1194 represents the official IANA port number
+assignment for OpenVPN and has been used since version 2.0-beta17.
+Previous versions used port 5000 as the default.
+.\"*********************************************************
+.TP
+.B \-\-lport port
+TCP/UDP port number for bind.
+.\"*********************************************************
+.TP
+.B \-\-rport port
+TCP/UDP port number for remote.
+.\"*********************************************************
+.TP
+.B \-\-bind
+Bind to local address and port. This is the default unless any of
+.B \-\-proto tcp-client
+,
+.B \-\-http-proxy
+or
+.B \-\-socks-proxy
+are used.
+.\"*********************************************************
+.TP
+.B \-\-nobind
+Do not bind to local address and port. The IP stack will allocate
+a dynamic port for returning packets. Since the value of the dynamic port
+could not be known in advance by a peer, this option is only suitable for
+peers which will be initiating connections by using the
+.B \-\-remote
+option.
+.\"*********************************************************
+.TP
+.B \-\-dev tunX | tapX | null
+TUN/TAP virtual network device (
+.B X
+can be omitted for a dynamic device.)
+
+See examples section below
+for an example on setting up a TUN device.
+
+You must use either tun devices on both ends of the connection
+or tap devices on both ends. You cannot mix them, as they
+represent different underlying network layers.
+
+.B tun
+devices encapsulate IPv4 or IPv6 (OSI Layer 3) while
+.B tap
+devices encapsulate Ethernet 802.3 (OSI Layer 2).
+.\"*********************************************************
+.TP
+.B \-\-dev-type device-type
+Which device type are we using?
+.B device-type
+should be
+.B tun
+(OSI Layer 3)
+or
+.B tap
+(OSI Layer 2).
+Use this option only if the TUN/TAP device used with
+.B \-\-dev
+does not begin with
+.B tun
+or
+.B tap.
+.\"*********************************************************
+.TP
+.B \-\-topology mode
+Configure virtual addressing topology when running in
+.B \-\-dev tun
+mode. This directive has no meaning in
+.B \-\-dev tap
+mode, which always uses a
+.B subnet
+topology.
+
+If you set this directive on the server, the
+.B \-\-server
+and
+.B \-\-server-bridge
+directives will automatically push your chosen topology setting to clients
+as well. This directive can also be manually pushed to clients. Like the
+.B \-\-dev
+directive, this directive must always be compatible between client and server.
+
+.B mode
+can be one of:
+
+.B net30 \-\-
+Use a point-to-point topology, by allocating one /30 subnet per client.
+This is designed to allow point-to-point semantics when some
+or all of the connecting clients might be Windows systems. This is the
+default on OpenVPN 2.0.
+
+.B p2p \-\-
+Use a point-to-point topology where the remote endpoint of the client's
+tun interface always points to the local endpoint of the server's tun interface.
+This mode allocates a single IP address per connecting client.
+Only use
+when none of the connecting clients are Windows systems. This mode
+is functionally equivalent to the
+.B \-\-ifconfig-pool-linear
+directive which is available in OpenVPN 2.0 and is now deprecated.
+
+.B subnet \-\-
+Use a subnet rather than a point-to-point topology by
+configuring the tun interface with a local IP address and subnet mask,
+similar to the topology used in
+.B \-\-dev tap
+and ethernet bridging mode.
+This mode allocates a single IP address per connecting client and works on
+Windows as well. Only available when server and clients are OpenVPN 2.1 or
+higher, or OpenVPN 2.0.x which has been manually patched with the
+.B \-\-topology
+directive code. When used on Windows, requires version 8.2 or higher
+of the TAP-Win32 driver. When used on *nix, requires that the tun
+driver supports an
+.BR ifconfig (8)
+command which sets a subnet instead of a remote endpoint IP address.
+
+This option exists in OpenVPN 2.1 or higher.
+.\"*********************************************************
+.TP
+.B \-\-tun-ipv6
+Build a tun link capable of forwarding IPv6 traffic.
+Should be used in conjunction with
+.B \-\-dev tun
+or
+.B \-\-dev tunX.
+A warning will be displayed
+if no specific IPv6 TUN support for your OS has been compiled into OpenVPN.
+.\"*********************************************************
+.TP
+.B \-\-dev-node node
+Explicitly set the device node rather than using
+/dev/net/tun, /dev/tun, /dev/tap, etc. If OpenVPN
+cannot figure out whether
+.B node
+is a TUN or TAP device based on the name, you should
+also specify
+.B \-\-dev-type tun
+or
+.B \-\-dev-type tap.
+
+On Windows systems, select the TAP-Win32 adapter which
+is named
+.B node
+in the Network Connections Control Panel or the
+raw GUID of the adapter enclosed by braces.
+The
+.B \-\-show-adapters
+option under Windows can also be used
+to enumerate all available TAP-Win32
+adapters and will show both the network
+connections control panel name and the GUID for
+each TAP-Win32 adapter.
+.TP
+.B \-\-lladdr address
+Specify the link layer address, more commonly known as the MAC address.
+Only applied to TAP devices.
+.\"*********************************************************
+.TP
+.B \-\-iproute cmd
+Set alternate command to execute instead of default iproute2 command.
+May be used in order to execute OpenVPN in unprivileged environment.
+.\"*********************************************************
+.TP
+.B \-\-ifconfig l rn
+Set TUN/TAP adapter parameters.
+.B l
+is the IP address of the local VPN endpoint.
+For TUN devices,
+.B rn
+is the IP address of the remote VPN endpoint.
+For TAP devices,
+.B rn
+is the subnet mask of the virtual ethernet segment
+which is being created or connected to.
+
+For TUN devices, which facilitate virtual
+point-to-point IP connections,
+the proper usage of
+.B \-\-ifconfig
+is to use two private IP addresses
+which are not a member of any
+existing subnet which is in use.
+The IP addresses may be consecutive
+and should have their order reversed
+on the remote peer. After the VPN
+is established, by pinging
+.B rn,
+you will be pinging across the VPN.
+
+For TAP devices, which provide
+the ability to create virtual
+ethernet segments,
+.B \-\-ifconfig
+is used to set an IP address and
+subnet mask just as a physical
+ethernet adapter would be
+similarly configured. If you are
+attempting to connect to a remote
+ethernet bridge, the IP address
+and subnet should be set to values
+which would be valid on the
+the bridged ethernet segment (note
+also that DHCP can be used for the
+same purpose).
+
+This option, while primarily a proxy for the
+.BR ifconfig (8)
+command, is designed to simplify TUN/TAP
+tunnel configuration by providing a
+standard interface to the different
+ifconfig implementations on different
+platforms.
+
+.B \-\-ifconfig
+parameters which are IP addresses can
+also be specified as a DNS or /etc/hosts
+file resolvable name.
+
+For TAP devices,
+.B \-\-ifconfig
+should not be used if the TAP interface will be
+getting an IP address lease from a DHCP
+server.
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-noexec
+Don't actually execute ifconfig/netsh commands, instead
+pass
+.B \-\-ifconfig
+parameters to scripts using environmental variables.
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-nowarn
+Don't output an options consistency check warning
+if the
+.B \-\-ifconfig
+option on this side of the
+connection doesn't match the remote side. This is useful
+when you want to retain the overall benefits of the
+options consistency check (also see
+.B \-\-disable-occ
+option) while only disabling the ifconfig component of
+the check.
+
+For example,
+if you have a configuration where the local host uses
+.B \-\-ifconfig
+but the remote host does not, use
+.B \-\-ifconfig-nowarn
+on the local host.
+
+This option will also silence warnings about potential
+address conflicts which occasionally annoy more experienced
+users by triggering "false positive" warnings.
+.\"*********************************************************
+.TP
+.B \-\-route network/IP [netmask] [gateway] [metric]
+Add route to routing table after connection is established.
+Multiple routes can be specified. Routes will be
+automatically torn down in reverse order prior to
+TUN/TAP device close.
+
+This option is intended as
+a convenience proxy for the
+.BR route (8)
+shell command,
+while at the same time providing portable semantics
+across OpenVPN's platform space.
+
+.B netmask
+default \-\- 255.255.255.255
+
+.B gateway
+default \-\- taken from
+.B \-\-route-gateway
+or the second parameter to
+.B \-\-ifconfig
+when
+.B \-\-dev tun
+is specified.
+
+.B metric
+default \-\- taken from
+.B \-\-route-metric
+otherwise 0.
+
+The default can be specified by leaving an option blank or setting
+it to "default".
+
+The
+.B network
+and
+.B gateway
+parameters can
+also be specified as a DNS or /etc/hosts
+file resolvable name, or as one of three special keywords:
+
+.B vpn_gateway
+\-\- The remote VPN endpoint address
+(derived either from
+.B \-\-route-gateway
+or the second parameter to
+.B \-\-ifconfig
+when
+.B \-\-dev tun
+is specified).
+
+.B net_gateway
+\-\- The pre-existing IP default gateway, read from the routing
+table (not supported on all OSes).
+
+.B remote_host
+\-\- The
+.B \-\-remote
+address if OpenVPN is being run in client mode, and is undefined in server mode.
+.\"*********************************************************
+.TP
+.B \-\-max-routes n
+Allow a maximum number of n
+.B \-\-route
+options to be specified, either in the local configuration file,
+or pulled from an OpenVPN server. By default, n=100.
+.\"*********************************************************
+.TP
+.B \-\-route-gateway gw|'dhcp'
+Specify a default gateway
+.B gw
+for use with
+.B \-\-route.
+
+If
+.B dhcp
+is specified as the parameter,
+the gateway address will be extracted from a DHCP
+negotiation with the OpenVPN server-side LAN.
+.\"*********************************************************
+.TP
+.B \-\-route-metric m
+Specify a default metric
+.B m
+for use with
+.B \-\-route.
+.\"*********************************************************
+.TP
+.B \-\-route-delay [n] [w]
+Delay
+.B n
+seconds (default=0) after connection
+establishment, before adding routes. If
+.B n
+is 0, routes will be added immediately upon connection
+establishment. If
+.B \-\-route-delay
+is omitted, routes will be added immediately after TUN/TAP device
+open and
+.B \-\-up
+script execution, before any
+.B \-\-user
+or
+.B \-\-group
+privilege downgrade (or
+.B \-\-chroot
+execution.)
+
+This option is designed to be useful in scenarios where DHCP is
+used to set
+tap adapter addresses. The delay will give the DHCP handshake
+time to complete before routes are added.
+
+On Windows,
+.B \-\-route-delay
+tries to be more intelligent by waiting
+.B w
+seconds (w=30 by default)
+for the TAP-Win32 adapter to come up before adding routes.
+.\"*********************************************************
+.TP
+.B \-\-route-up cmd
+Execute shell command
+.B cmd
+after routes are added, subject to
+.B \-\-route-delay.
+
+See the "Environmental Variables" section below for
+additional parameters passed as environmental variables.
+
+Note that
+.B cmd
+can be a shell command with multiple arguments.
+.\"*********************************************************
+.TP
+.B \-\-route-noexec
+Don't add or remove routes automatically. Instead pass routes to
+.B \-\-route-up
+script using environmental variables.
+.\"*********************************************************
+.TP
+.B \-\-route-nopull
+When used with
+.B \-\-client
+or
+.B \-\-pull,
+accept options pushed by server EXCEPT for routes.
+
+When used on the client, this option effectively bars the
+server from adding routes to the client's routing table,
+however note that this option still allows the server
+to set the TCP/IP properties of the client's TUN/TAP interface.
+.\"*********************************************************
+.TP
+.B \-\-allow-pull-fqdn
+Allow client to pull DNS names from server (rather than being limited
+to IP address) for
+.B \-\-ifconfig,
+.B \-\-route,
+and
+.B \-\-route-gateway.
+.\"*********************************************************
+.TP
+.B \-\-redirect-gateway flags...
+(Experimental) Automatically execute routing commands to cause all outgoing IP traffic
+to be redirected over the VPN.
+
+This option performs three steps:
+
+.B (1)
+Create a static route for the
+.B \-\-remote
+address which forwards to the pre-existing default gateway.
+This is done so that
+.B (3)
+will not create a routing loop.
+
+.B (2)
+Delete the default gateway route.
+
+.B (3)
+Set the new default gateway to be the VPN endpoint address (derived either from
+.B \-\-route-gateway
+or the second parameter to
+.B \-\-ifconfig
+when
+.B \-\-dev tun
+is specified).
+
+When the tunnel is torn down, all of the above steps are reversed so
+that the original default route is restored.
+
+Option flags:
+
+.B local \-\-
+Add the
+.B local
+flag if both OpenVPN servers are directly connected via a common subnet,
+such as with wireless. The
+.B local
+flag will cause step
+.B 1
+above to be omitted.
+
+.B def1 \-\-
+Use this flag to override
+the default gateway by using 0.0.0.0/1 and 128.0.0.0/1
+rather than 0.0.0.0/0. This has the benefit of overriding
+but not wiping out the original default gateway.
+
+.B bypass-dhcp \-\-
+Add a direct route to the DHCP server (if it is non-local) which
+bypasses the tunnel
+(Available on Windows clients, may not be available
+on non-Windows clients).
+
+.B bypass-dns \-\-
+Add a direct route to the DNS server(s) (if they are non-local) which
+bypasses the tunnel
+(Available on Windows clients, may not be available
+on non-Windows clients).
+
+Using the def1 flag is highly recommended.
+.\"*********************************************************
+.TP
+.B \-\-redirect-private [flags]
+Like \-\-redirect-gateway, but omit actually changing the default
+gateway. Useful when pushing private subnets.
+.\"*********************************************************
+.TP
+.B \-\-link-mtu n
+Sets an upper bound on the size of UDP packets which are sent
+between OpenVPN peers. It's best not to set this parameter unless
+you know what you're doing.
+.\"*********************************************************
+.TP
+.B \-\-tun-mtu n
+Take the TUN device MTU to be
+.B n
+and derive the link MTU
+from it (default=1500). In most cases, you will probably want to
+leave this parameter set to its default value.
+
+The MTU (Maximum Transmission Units) is
+the maximum datagram size in bytes that can be sent unfragmented
+over a particular network path. OpenVPN requires that packets
+on the control or data channels be sent unfragmented.
+
+MTU problems often manifest themselves as connections which
+hang during periods of active usage.
+
+It's best to use the
+.B \-\-fragment
+and/or
+.B \-\-mssfix
+options to deal with MTU sizing issues.
+.\"*********************************************************
+.TP
+.B \-\-tun-mtu-extra n
+Assume that the TUN/TAP device might return as many as
+.B n
+bytes more than the
+.B \-\-tun-mtu
+size on read. This parameter defaults to 0, which is sufficient for
+most TUN devices. TAP devices may introduce additional overhead in excess
+of the MTU size, and a setting of 32 is the default when TAP devices are used.
+This parameter only controls internal OpenVPN buffer sizing,
+so there is no transmission overhead associated with using a larger value.
+.\"*********************************************************
+.TP
+.B \-\-mtu-disc type
+Should we do Path MTU discovery on TCP/UDP channel? Only supported on OSes such
+as Linux that supports the necessary system call to set.
+
+.B 'no'
+\-\- Never send DF (Don't Fragment) frames
+.br
+.B 'maybe'
+\-\- Use per-route hints
+.br
+.B 'yes'
+\-\- Always DF (Don't Fragment)
+.br
+.\"*********************************************************
+.TP
+.B \-\-mtu-test
+To empirically measure MTU on connection startup,
+add the
+.B \-\-mtu-test
+option to your configuration.
+OpenVPN will send ping packets of various sizes
+to the remote peer and measure the largest packets
+which were successfully received. The
+.B \-\-mtu-test
+process normally takes about 3 minutes to complete.
+.\"*********************************************************
+.TP
+.B \-\-fragment max
+Enable internal datagram fragmentation so
+that no UDP datagrams are sent which
+are larger than
+.B max
+bytes.
+
+The
+.B max
+parameter is interpreted in the same way as the
+.B \-\-link-mtu
+parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including
+the UDP header itself.
+
+The
+.B \-\-fragment
+option only makes sense when you are using the UDP protocol (
+.B \-\-proto udp
+).
+
+.B \-\-fragment
+adds 4 bytes of overhead per datagram.
+
+See the
+.B \-\-mssfix
+option below for an important related option to
+.B \-\-fragment.
+
+It should also be noted that this option is not meant to replace
+UDP fragmentation at the IP stack level. It is only meant as a
+last resort when path MTU discovery is broken. Using this option
+is less efficient than fixing path MTU discovery for your IP link and
+using native IP fragmentation instead.
+
+Having said that, there are circumstances where using OpenVPN's
+internal fragmentation capability may be your only option, such
+as tunneling a UDP multicast stream which requires fragmentation.
+.\"*********************************************************
+.TP
+.B \-\-mssfix max
+Announce to TCP sessions running over the tunnel that they should limit
+their send packet sizes such that after OpenVPN has encapsulated them,
+the resulting UDP packet size that OpenVPN sends to its peer will not
+exceed
+.B max
+bytes. The default value is
+.B 1450.
+
+The
+.B max
+parameter is interpreted in the same way as the
+.B \-\-link-mtu
+parameter, i.e. the UDP packet size after encapsulation
+overhead has been added in, but not including
+the UDP header itself.
+
+The
+.B \-\-mssfix
+option only makes sense when you are using the UDP protocol
+for OpenVPN peer-to-peer communication, i.e.
+.B \-\-proto udp.
+
+.B \-\-mssfix
+and
+.B \-\-fragment
+can be ideally used together, where
+.B \-\-mssfix
+will try to keep TCP from needing
+packet fragmentation in the first place,
+and if big packets come through anyhow
+(from protocols other than TCP),
+.B \-\-fragment
+will internally fragment them.
+
+Both
+.B \-\-fragment
+and
+.B \-\-mssfix
+are designed to work around cases where Path MTU discovery
+is broken on the network path between OpenVPN peers.
+
+The usual symptom of such a breakdown is an OpenVPN
+connection which successfully starts, but then stalls
+during active usage.
+
+If
+.B \-\-fragment
+and
+.B \-\-mssfix
+are used together,
+.B \-\-mssfix
+will take its default
+.B max
+parameter from the
+.B \-\-fragment max
+option.
+
+Therefore, one could lower the maximum UDP packet size
+to 1300 (a good first try for solving MTU-related
+connection problems) with the following options:
+
+.B \-\-tun-mtu 1500 \-\-fragment 1300 \-\-mssfix
+.\"*********************************************************
+.TP
+.B \-\-sndbuf size
+Set the TCP/UDP socket send buffer size.
+Currently defaults to 65536 bytes.
+.\"*********************************************************
+.TP
+.B \-\-rcvbuf size
+Set the TCP/UDP socket receive buffer size.
+Currently defaults to 65536 bytes.
+.\"*********************************************************
+.TP
+.B \-\-socket-flags flags...
+Apply the given flags to the OpenVPN transport socket.
+Currently, only
+.B TCP_NODELAY
+is supported.
+
+The
+.B TCP_NODELAY
+socket flag is useful in TCP mode, and causes the kernel
+to send tunnel packets immediately over the TCP connection without
+trying to group several smaller packets into a larger packet.
+This can result in a considerably improvement in latency.
+
+This option is pushable from server to client, and should be used
+on both client and server for maximum effect.
+.\"*********************************************************
+.TP
+.B \-\-txqueuelen n
+(Linux only) Set the TX queue length on the TUN/TAP interface.
+Currently defaults to 100.
+.\"*********************************************************
+.TP
+.B \-\-shaper n
+Limit bandwidth of outgoing tunnel data to
+.B n
+bytes per second on the TCP/UDP port.
+If you want to limit the bandwidth
+in both directions, use this option on both peers.
+
+OpenVPN uses the following algorithm to implement
+traffic shaping: Given a shaper rate of
+.I n
+bytes per second, after a datagram write of
+.I b
+bytes is queued on the TCP/UDP port, wait a minimum of
+.I (b / n)
+seconds before queuing the next write.
+
+It should be noted that OpenVPN supports multiple
+tunnels between the same two peers, allowing you
+to construct full-speed and reduced bandwidth tunnels
+at the same time,
+routing low-priority data such as off-site backups
+over the reduced bandwidth tunnel, and other data
+over the full-speed tunnel.
+
+Also note that for low bandwidth tunnels
+(under 1000 bytes per second), you should probably
+use lower MTU values as well (see above), otherwise
+the packet latency will grow so large as to trigger
+timeouts in the TLS layer and TCP connections running
+over the tunnel.
+
+OpenVPN allows
+.B n
+to be between 100 bytes/sec and 100 Mbytes/sec.
+.\"*********************************************************
+.TP
+.B \-\-inactive n [bytes]
+Causes OpenVPN to exit after
+.B n
+seconds of inactivity on the TUN/TAP device. The time length of
+inactivity is measured since the last incoming or outgoing tunnel
+packet. The default value is 0 seconds, which disables this feature.
+
+If the optional
+.B bytes
+parameter is included,
+exit if less than
+.B bytes
+of combined in/out traffic are produced on the tun/tap device
+in
+.B n
+seconds.
+
+In any case, OpenVPN's internal ping packets (which are just
+keepalives) and TLS control packets are not considered
+"activity", nor are they counted as traffic, as they are used
+internally by OpenVPN and are not an indication of actual user
+activity.
+.\"*********************************************************
+.TP
+.B \-\-ping n
+Ping remote over the TCP/UDP control channel
+if no packets have been sent for at least
+.B n
+seconds (specify
+.B \-\-ping
+on both peers to cause ping packets to be sent in both directions since
+OpenVPN ping packets are not echoed like IP ping packets).
+When used in one of OpenVPN's secure modes (where
+.B \-\-secret, \-\-tls-server,
+or
+.B \-\-tls-client
+is specified), the ping packet
+will be cryptographically secure.
+
+This option has two intended uses:
+
+(1) Compatibility
+with stateful firewalls. The periodic ping will ensure that
+a stateful firewall rule which allows OpenVPN UDP packets to
+pass will not time out.
+
+(2) To provide a basis for the remote to test the existence
+of its peer using the
+.B \-\-ping-exit
+option.
+.\"*********************************************************
+.TP
+.B \-\-ping-exit n
+Causes OpenVPN to exit after
+.B n
+seconds pass without reception of a ping
+or other packet from remote.
+This option can be combined with
+.B \-\-inactive, \-\-ping,
+and
+.B \-\-ping-exit
+to create a two-tiered inactivity disconnect.
+
+For example,
+
+.B openvpn [options...] \-\-inactive 3600 \-\-ping 10 \-\-ping-exit 60
+
+when used on both peers will cause OpenVPN to exit within 60
+seconds if its peer disconnects, but will exit after one
+hour if no actual tunnel data is exchanged.
+.\"*********************************************************
+.TP
+.B \-\-ping-restart n
+Similar to
+.B \-\-ping-exit,
+but trigger a
+.B SIGUSR1
+restart after
+.B n
+seconds pass without reception of a ping
+or other packet from remote.
+
+This option is useful in cases
+where the remote peer has a dynamic IP address and
+a low-TTL DNS name is used to track the IP address using
+a service such as
+.I http://dyndns.org/
++ a dynamic DNS client such
+as
+.B ddclient.
+
+If the peer cannot be reached, a restart will be triggered, causing
+the hostname used with
+.B \-\-remote
+to be re-resolved (if
+.B \-\-resolv-retry
+is also specified).
+
+In server mode,
+.B \-\-ping-restart, \-\-inactive,
+or any other type of internally generated signal will always be
+applied to
+individual client instance objects, never to whole server itself.
+Note also in server mode that any internally generated signal
+which would normally cause a restart, will cause the deletion
+of the client instance object instead.
+
+In client mode, the
+.B \-\-ping-restart
+parameter is set to 120 seconds by default. This default will
+hold until the client pulls a replacement value from the server, based on
+the
+.B \-\-keepalive
+setting in the server configuration.
+To disable the 120 second default, set
+.B \-\-ping-restart 0
+on the client.
+
+See the signals section below for more information
+on
+.B SIGUSR1.
+
+Note that the behavior of
+.B SIGUSR1
+can be modified by the
+.B \-\-persist-tun, \-\-persist-key, \-\-persist-local-ip,
+and
+.B \-\-persist-remote-ip
+options.
+
+Also note that
+.B \-\-ping-exit
+and
+.B \-\-ping-restart
+are mutually exclusive and cannot be used together.
+.\"*********************************************************
+.TP
+.B \-\-keepalive n m
+A helper directive designed to simplify the expression of
+.B \-\-ping
+and
+.B \-\-ping-restart
+in server mode configurations.
+
+For example,
+.B \-\-keepalive 10 60
+expands as follows:
+
+.nf
+.ft 3
+.in +4
+ if mode server:
+ ping 10
+ ping-restart 120
+ push "ping 10"
+ push "ping-restart 60"
+ else
+ ping 10
+ ping-restart 60
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.TP
+.B \-\-ping-timer-rem
+Run the
+.B \-\-ping-exit
+/
+.B \-\-ping-restart
+timer only if we have a remote address. Use this option if you are
+starting the daemon in listen mode (i.e. without an explicit
+.B \-\-remote
+peer), and you don't want to start clocking timeouts until a remote
+peer connects.
+.\"*********************************************************
+.TP
+.B \-\-persist-tun
+Don't close and reopen TUN/TAP device or run up/down scripts
+across
+.B SIGUSR1
+or
+.B \-\-ping-restart
+restarts.
+
+.B SIGUSR1
+is a restart signal similar to
+.B SIGHUP,
+but which offers finer-grained control over
+reset options.
+.\"*********************************************************
+.TP
+.B \-\-persist-key
+Don't re-read key files across
+.B SIGUSR1
+or
+.B \-\-ping-restart.
+
+This option can be combined with
+.B \-\-user nobody
+to allow restarts triggered by the
+.B SIGUSR1
+signal.
+Normally if you drop root privileges in OpenVPN,
+the daemon cannot be restarted since it will now be unable to re-read protected
+key files.
+
+This option solves the problem by persisting keys across
+.B SIGUSR1
+resets, so they don't need to be re-read.
+.\"*********************************************************
+.TP
+.B \-\-persist-local-ip
+Preserve initially resolved local IP address and port number
+across
+.B SIGUSR1
+or
+.B \-\-ping-restart
+restarts.
+.\"*********************************************************
+.TP
+.B \-\-persist-remote-ip
+Preserve most recently authenticated remote IP address and port number
+across
+.B SIGUSR1
+or
+.B \-\-ping-restart
+restarts.
+.\"*********************************************************
+.TP
+.B \-\-mlock
+Disable paging by calling the POSIX mlockall function.
+Requires that OpenVPN be initially run as root (though
+OpenVPN can subsequently downgrade its UID using the
+.B \-\-user
+option).
+
+Using this option ensures that key material and tunnel
+data are never written to disk due to virtual
+memory paging operations which occur under most
+modern operating systems. It ensures that even if an
+attacker was able to crack the box running OpenVPN, he
+would not be able to scan the system swap file to
+recover previously used
+ephemeral keys, which are used for a period of time
+governed by the
+.B \-\-reneg
+options (see below), then are discarded.
+
+The downside
+of using
+.B \-\-mlock
+is that it will reduce the amount of physical
+memory available to other applications.
+.\"*********************************************************
+.TP
+.B \-\-up cmd
+Shell command to run after successful TUN/TAP device open
+(pre
+.B \-\-user
+UID change). The up script is useful for specifying route
+commands which route IP traffic destined for
+private subnets which exist at the other
+end of the VPN connection into the tunnel.
+
+For
+.B \-\-dev tun
+execute as:
+
+.B cmd tun_dev tun_mtu link_mtu ifconfig_local_ip ifconfig_remote_ip [ init | restart ]
+
+For
+.B \-\-dev tap
+execute as:
+
+.B cmd tap_dev tap_mtu link_mtu ifconfig_local_ip ifconfig_netmask [ init | restart ]
+
+See the "Environmental Variables" section below for
+additional parameters passed as environmental variables.
+
+Note that
+.B cmd
+can be a shell command with multiple arguments, in which
+case all OpenVPN-generated arguments will be appended
+to
+.B cmd
+to build a command line which will be passed to the shell.
+
+Typically,
+.B cmd
+will run a script to add routes to the tunnel.
+
+Normally the up script is called after the TUN/TAP device is opened.
+In this context, the last command line parameter passed to the script
+will be
+.I init.
+If the
+.B \-\-up-restart
+option is also used, the up script will be called for restarts as
+well. A restart is considered to be a partial reinitialization
+of OpenVPN where the TUN/TAP instance is preserved (the
+.B \-\-persist-tun
+option will enable such preservation). A restart
+can be generated by a SIGUSR1 signal, a
+.B \-\-ping-restart
+timeout, or a connection reset when the TCP protocol is enabled
+with the
+.B \-\-proto
+option. If a restart occurs, and
+.B \-\-up-restart
+has been specified, the up script will be called with
+.I restart
+as the last parameter.
+
+The following standalone example shows how the
+.B \-\-up
+script can be called in both an initialization and restart context.
+(NOTE: for security reasons, don't run the following example unless UDP port
+9999 is blocked by your firewall. Also, the example will run indefinitely,
+so you should abort with control-c).
+
+.B openvpn \-\-dev tun \-\-port 9999 \-\-verb 4 \-\-ping-restart 10 \-\-up 'echo up' \-\-down 'echo down' \-\-persist-tun \-\-up-restart
+
+Note that OpenVPN also provides the
+.B \-\-ifconfig
+option to automatically ifconfig the TUN device,
+eliminating the need to define an
+.B \-\-up
+script, unless you also want to configure routes
+in the
+.B \-\-up
+script.
+
+If
+.B \-\-ifconfig
+is also specified, OpenVPN will pass the ifconfig local
+and remote endpoints on the command line to the
+.B \-\-up
+script so that they can be used to configure routes such as:
+
+.B route add -net 10.0.0.0 netmask 255.255.255.0 gw $5
+.\"*********************************************************
+.TP
+.B \-\-up-delay
+Delay TUN/TAP open and possible
+.B \-\-up
+script execution
+until after TCP/UDP connection establishment with peer.
+
+In
+.B \-\-proto udp
+mode, this option normally requires the use of
+.B \-\-ping
+to allow connection initiation to be sensed in the absence
+of tunnel data, since UDP is a "connectionless" protocol.
+
+On Windows, this option will delay the TAP-Win32 media state
+transitioning to "connected" until connection establishment,
+i.e. the receipt of the first authenticated packet from the peer.
+.\"*********************************************************
+.TP
+.B \-\-down cmd
+Shell command to run after TUN/TAP device close
+(post
+.B \-\-user
+UID change and/or
+.B \-\-chroot
+). Called with the same parameters and environmental
+variables as the
+.B \-\-up
+option above.
+
+Note that if you reduce privileges by using
+.B \-\-user
+and/or
+.B \-\-group,
+your
+.B \-\-down
+script will also run at reduced privilege.
+.\"*********************************************************
+.TP
+.B \-\-down-pre
+Call
+.B \-\-down
+cmd/script before, rather than after, TUN/TAP close.
+.\"*********************************************************
+.TP
+.B \-\-up-restart
+Enable the
+.B \-\-up
+and
+.B \-\-down
+scripts to be called for restarts as well as initial program start.
+This option is described more fully above in the
+.B \-\-up
+option documentation.
+.\"*********************************************************
+.TP
+.B \-\-setenv name value
+Set a custom environmental variable
+.B name=value
+to pass to script.
+.\"*********************************************************
+.TP
+.B \-\-setenv FORWARD_COMPATIBLE 1
+Relax config file syntax checking so that unknown directives
+will trigger a warning but not a fatal error,
+on the assumption that a given unknown directive might be valid
+in future OpenVPN versions.
+
+This option should be used with caution, as there are good security
+reasons for having OpenVPN fail if it detects problems in a
+config file. Having said that, there are valid reasons for wanting
+new software features to gracefully degrade when encountered by
+older software versions.
+.\"*********************************************************
+.TP
+.B \-\-setenv-safe name value
+Set a custom environmental variable
+.B OPENVPN_name=value
+to pass to script.
+
+This directive is designed to be pushed by the server to clients,
+and the prepending of "OPENVPN_" to the environmental variable
+is a safety precaution to prevent a LD_PRELOAD style attack
+from a malicious or compromised server.
+.\"*********************************************************
+.TP
+.B \-\-script-security level [method]
+This directive offers policy-level control over OpenVPN's usage of external programs
+and scripts. Lower
+.B level
+values are more restrictive, higher values are more permissive. Settings for
+.B level:
+
+.B 0 \-\-
+Strictly no calling of external programs.
+.br
+.B 1 \-\-
+(Default) Only call built-in executables such as ifconfig, ip, route, or netsh.
+.br
+.B 2 \-\-
+Allow calling of built-in executables and user-defined scripts.
+.br
+.B 3 \-\-
+Allow passwords to be passed to scripts via environmental variables (potentially unsafe).
+
+The
+.B method
+parameter indicates how OpenVPN should call external commands and scripts.
+Settings for
+.B method:
+
+.B execve \-\-
+(default) Use execve() function on Unix family OSes and CreateProcess() on Windows.
+.br
+.B system \-\-
+Use system() function (deprecated and less safe since the external program command
+line is subject to shell expansion).
+
+The
+.B \-\-script-security
+option was introduced in OpenVPN 2.1_rc9. For configuration file compatibility
+with previous OpenVPN versions, use:
+.B \-\-script-security 3 system
+.\"*********************************************************
+.TP
+.B \-\-disable-occ
+Don't output a warning message if option inconsistencies are detected between
+peers. An example of an option inconsistency would be where one peer uses
+.B \-\-dev tun
+while the other peer uses
+.B \-\-dev tap.
+
+Use of this option is discouraged, but is provided as
+a temporary fix in situations where a recent version of OpenVPN must
+connect to an old version.
+.\"*********************************************************
+.TP
+.B \-\-user user
+Change the user ID of the OpenVPN process to
+.B user
+after initialization, dropping privileges in the process.
+This option is useful to protect the system
+in the event that some hostile party was able to gain control of
+an OpenVPN session. Though OpenVPN's security features make
+this unlikely, it is provided as a second line of defense.
+
+By setting
+.B user
+to
+.I nobody
+or somebody similarly unprivileged, the hostile party would be
+limited in what damage they could cause. Of course once
+you take away privileges, you cannot return them
+to an OpenVPN session. This means, for example, that if
+you want to reset an OpenVPN daemon with a
+.B SIGUSR1
+signal
+(for example in response
+to a DHCP reset), you should make use of one or more of the
+.B \-\-persist
+options to ensure that OpenVPN doesn't need to execute any privileged
+operations in order to restart (such as re-reading key files
+or running
+.BR ifconfig
+on the TUN device).
+.\"*********************************************************
+.TP
+.B \-\-group group
+Similar to the
+.B \-\-user
+option,
+this option changes the group ID of the OpenVPN process to
+.B group
+after initialization.
+.\"*********************************************************
+.TP
+.B \-\-cd dir
+Change directory to
+.B dir
+prior to reading any files such as
+configuration files, key files, scripts, etc.
+.B dir
+should be an absolute path, with a leading "/",
+and without any references
+to the current directory such as "." or "..".
+
+This option is useful when you are running
+OpenVPN in
+.B \-\-daemon
+mode, and you want to consolidate all of
+your OpenVPN control files in one location.
+.\"*********************************************************
+.TP
+.B \-\-chroot dir
+Chroot to
+.B dir
+after initialization.
+.B \-\-chroot
+essentially redefines
+.B dir
+as being the top
+level directory tree (/). OpenVPN will therefore
+be unable to access any files outside this tree.
+This can be desirable from a security standpoint.
+
+Since the chroot operation is delayed until after
+initialization, most OpenVPN options that reference
+files will operate in a pre-chroot context.
+
+In many cases, the
+.B dir
+parameter can point to an empty directory, however
+complications can result when scripts or restarts
+are executed after the chroot operation.
+.\"*********************************************************
+.TP
+.B \-\-setcon context
+Apply SELinux
+.B context
+after initialization. This
+essentially provides the ability to restrict OpenVPN's
+rights to only network I/O operations, thanks to
+SELinux. This goes further than
+.B \-\-user
+and
+.B \-\-chroot
+in that those two, while being great security features,
+unfortunately do not protect against privilege escalation
+by exploitation of a vulnerable system call. You can of
+course combine all three, but please note that since
+setcon requires access to /proc you will have to provide
+it inside the chroot directory (e.g. with mount \-\-bind).
+
+Since the setcon operation is delayed until after
+initialization, OpenVPN can be restricted to just
+network-related system calls, whereas by applying the
+context before startup (such as the OpenVPN one provided
+in the SELinux Reference Policies) you will have to
+allow many things required only during initialization.
+
+Like with chroot, complications can result when scripts
+or restarts are executed after the setcon operation,
+which is why you should really consider using the
+.B \-\-persist-key
+and
+.B \-\-persist-tun
+options.
+.\"*********************************************************
+.TP
+.B \-\-daemon [progname]
+Become a daemon after all initialization functions are completed.
+This option will cause all message and error output to
+be sent to the syslog file (such as /var/log/messages),
+except for the output of shell scripts and
+ifconfig commands,
+which will go to /dev/null unless otherwise redirected.
+The syslog redirection occurs immediately at the point
+that
+.B \-\-daemon
+is parsed on the command line even though
+the daemonization point occurs later. If one of the
+.B \-\-log
+options is present, it will supercede syslog
+redirection.
+
+The optional
+.B progname
+parameter will cause OpenVPN to report its program name
+to the system logger as
+.B progname.
+This can be useful in linking OpenVPN messages
+in the syslog file with specific tunnels.
+When unspecified,
+.B progname
+defaults to "openvpn".
+
+When OpenVPN is run with the
+.B \-\-daemon
+option, it will try to delay daemonization until the majority of initialization
+functions which are capable of generating fatal errors are complete. This means
+that initialization scripts can test the return status of the
+openvpn command for a fairly reliable indication of whether the command
+has correctly initialized and entered the packet forwarding event loop.
+
+In OpenVPN, the vast majority of errors which occur after initialization are non-fatal.
+.\"*********************************************************
+.TP
+.B \-\-syslog [progname]
+Direct log output to system logger, but do not become a daemon.
+See
+.B \-\-daemon
+directive above for description of
+.B progname
+parameter.
+.\"*********************************************************
+.TP
+.B \-\-passtos
+Set the TOS field of the tunnel packet to what the payload's TOS is.
+.\"*********************************************************
+.TP
+.B \-\-inetd [wait|nowait] [progname]
+Use this option when OpenVPN is being run from the inetd or
+.BR xinetd(8)
+server.
+
+The
+.B wait/nowait
+option must match what is specified in the inetd/xinetd
+config file. The
+.B nowait
+mode can only be used with
+.B \-\-proto tcp-server.
+The default is
+.B wait.
+The
+.B nowait
+mode can be used to instantiate the OpenVPN daemon as a classic TCP server,
+where client connection requests are serviced on a single
+port number. For additional information on this kind of configuration,
+see the OpenVPN FAQ:
+.I http://openvpn.net/faq.html#oneport
+
+This option precludes the use of
+.B \-\-daemon, \-\-local,
+or
+.B \-\-remote.
+Note that this option causes message and error output to be handled in the same
+way as the
+.B \-\-daemon
+option. The optional
+.B progname
+parameter is also handled exactly as in
+.B \-\-daemon.
+
+Also note that in
+.B wait
+mode, each OpenVPN tunnel requires a separate TCP/UDP port and
+a separate inetd or xinetd entry. See the OpenVPN 1.x HOWTO for an example
+on using OpenVPN with xinetd:
+.I http://openvpn.net/1xhowto.html
+.\"*********************************************************
+.TP
+.B \-\-log file
+Output logging messages to
+.B file,
+including output to stdout/stderr which
+is generated by called scripts.
+If
+.B file
+already exists it will be truncated.
+This option takes effect
+immediately when it is parsed in the command line
+and will supercede syslog output if
+.B \-\-daemon
+or
+.B \-\-inetd
+is also specified.
+This option is persistent over the entire course of
+an OpenVPN instantiation and will not be reset by SIGHUP,
+SIGUSR1, or
+.B \-\-ping-restart.
+
+Note that on Windows, when OpenVPN is started as a service,
+logging occurs by default without the need to specify
+this option.
+.\"*********************************************************
+.TP
+.B \-\-log-append file
+Append logging messages to
+.B file.
+If
+.B file
+does not exist, it will be created.
+This option behaves exactly like
+.B \-\-log
+except that it appends to rather
+than truncating the log file.
+.\"*********************************************************
+.TP
+.B \-\-suppress-timestamps
+Avoid writing timestamps to log messages, even when they
+otherwise would be prepended. In particular, this applies to
+log messages sent to stdout.
+.\"*********************************************************
+.TP
+.B \-\-writepid file
+Write OpenVPN's main process ID to
+.B file.
+.\"*********************************************************
+.TP
+.B \-\-nice n
+Change process priority after initialization
+(
+.B n
+greater than 0 is lower priority,
+.B n
+less than zero is higher priority).
+.\"*********************************************************
+.\".TP
+.\".B \-\-nice-work n
+.\"Change priority of background TLS work thread. The TLS thread
+.\"feature is enabled when OpenVPN is built
+.\"with pthread support, and you are running OpenVPN
+.\"in TLS mode (i.e. with
+.\".B \-\-tls-client
+.\"or
+.\".B \-\-tls-server
+.\"specified).
+.\"
+.\"Using a TLS thread offloads the CPU-intensive process of SSL/TLS-based
+.\"key exchange to a background thread so that it does not become
+.\"a latency bottleneck in the tunnel packet forwarding process.
+.\"
+.\"The parameter
+.\".B n
+.\"is interpreted exactly as with the
+.\".B \-\-nice
+.\"option above, but in relation to the work thread rather
+.\"than the main thread.
+.\"*********************************************************
+.TP
+.B \-\-fast-io
+(Experimental) Optimize TUN/TAP/UDP I/O writes by avoiding
+a call to poll/epoll/select prior to the write operation. The purpose
+of such a call would normally be to block until the device
+or socket is ready to accept the write. Such blocking is unnecessary
+on some platforms which don't support write blocking on UDP sockets
+or TUN/TAP devices. In such cases, one can optimize the event loop
+by avoiding the poll/epoll/select call, improving CPU efficiency
+by 5% to 10%.
+
+This option can only be used on non-Windows systems, when
+.B \-\-proto udp
+is specified, and when
+.B \-\-shaper
+is NOT specified.
+.\"*********************************************************
+.TP
+.B \-\-multihome
+Configure a multi-homed UDP server. This option can be used when
+OpenVPN has been configured to listen on all interfaces, and will
+attempt to bind client sessions to the interface on which packets
+are being received, so that outgoing packets will be sent out
+of the same interface. Note that this option is only relevant for
+UDP servers and currently is only implemented on Linux.
+
+Note: clients connecting to a
+.B \-\-multihome
+server should always use the
+.B \-\-nobind
+option.
+.\"*********************************************************
+.TP
+.B \-\-echo [parms...]
+Echo
+.B parms
+to log output.
+
+Designed to be used to send messages to a controlling application
+which is receiving the OpenVPN log output.
+.\"*********************************************************
+.TP
+.B \-\-remap-usr1 signal
+Control whether internally or externally
+generated SIGUSR1 signals are remapped to
+SIGHUP (restart without persisting state) or
+SIGTERM (exit).
+
+.B signal
+can be set to "SIGHUP" or "SIGTERM". By default, no remapping
+occurs.
+.\"*********************************************************
+.TP
+.B \-\-verb n
+Set output verbosity to
+.B n
+(default=1). Each level shows all info from the previous levels.
+Level 3 is recommended if you want a good summary
+of what's happening without being swamped by output.
+
+.B 0 \-\-
+No output except fatal errors.
+.br
+.B 1 to 4 \-\-
+Normal usage range.
+.br
+.B 5 \-\-
+Output
+.B R
+and
+.B W
+characters to the console for each packet read and write, uppercase is
+used for TCP/UDP packets and lowercase is used for TUN/TAP packets.
+.br
+.B 6 to 11 \-\-
+Debug info range (see errlevel.h for additional
+information on debug levels).
+.\"*********************************************************
+.TP
+.B \-\-status file [n]
+Write operational status to
+.B file
+every
+.B n
+seconds.
+
+Status can also be written to the syslog by sending a
+.B SIGUSR2
+signal.
+.\"*********************************************************
+.TP
+.B \-\-status-version [n]
+Choose the status file format version number. Currently
+.B n
+can be 1, 2, or 3 and defaults to 1.
+.\"*********************************************************
+.TP
+.B \-\-mute n
+Log at most
+.B n
+consecutive messages in the same category. This is useful to
+limit repetitive logging of similar message types.
+.\"*********************************************************
+.TP
+.B \-\-comp-lzo [mode]
+Use fast LZO compression \-\- may add up to 1 byte per
+packet for incompressible data.
+.B mode
+may be "yes", "no", or "adaptive" (default).
+
+In a server mode setup, it is possible to selectively turn
+compression on or off for individual clients.
+
+First, make sure the client-side config file enables selective
+compression by having at least one
+.B \-\-comp-lzo
+directive, such as
+.B \-\-comp-lzo no.
+This will turn off compression by default,
+but allow a future directive push from the server to
+dynamically change the
+on/off/adaptive setting.
+
+Next in a
+.B \-\-client-config-dir
+file, specify the compression setting for the client,
+for example:
+
+.nf
+.ft 3
+.in +4
+comp-lzo yes
+push "comp-lzo yes"
+.in -4
+.ft
+.fi
+
+The first line sets the
+.B comp-lzo
+setting for the server
+side of the link, the second sets the client side.
+.\"*********************************************************
+.TP
+.B \-\-comp-noadapt
+When used in conjunction with
+.B \-\-comp-lzo,
+this option will disable OpenVPN's adaptive compression algorithm.
+Normally, adaptive compression is enabled with
+.B \-\-comp-lzo.
+
+Adaptive compression tries to optimize the case where you have
+compression enabled, but you are sending predominantly uncompressible
+(or pre-compressed) packets over the tunnel, such as an FTP or rsync transfer
+of a large, compressed file. With adaptive compression,
+OpenVPN will periodically sample the compression process to measure its
+efficiency. If the data being sent over the tunnel is already compressed,
+the compression efficiency will be very low, triggering openvpn to disable
+compression for a period of time until the next re-sample test.
+.\"*********************************************************
+.TP
+.B \-\-management IP port [pw-file]
+Enable a TCP server on
+.B IP:port
+to handle daemon management functions.
+.B pw-file,
+if specified,
+is a password file (password on first line)
+or "stdin" to prompt from standard input. The password
+provided will set the password which TCP clients will need
+to provide in order to access management functions.
+
+The management interface can also listen on a unix domain socket,
+for those platforms that support it. To use a unix domain socket, specify
+the unix socket pathname in place of
+.B IP
+and set
+.B port
+to 'unix'. While the default behavior is to create a unix domain socket
+that may be connected to by any process, the
+.B \-\-management-client-user
+and
+.B \-\-management-client-group
+directives can be used to restrict access.
+
+The management interface provides a special mode where the TCP
+management link can operate over the tunnel itself. To enable this mode,
+set
+.B IP
+= "tunnel". Tunnel mode will cause the management interface
+to listen for a TCP connection on the local VPN address of the
+TUN/TAP interface.
+
+While the management port is designed for programmatic control
+of OpenVPN by other applications, it is possible to telnet
+to the port, using a telnet client in "raw" mode. Once connected,
+type "help" for a list of commands.
+
+For detailed documentation on the management interface, see
+the management-notes.txt file in the
+.B management
+folder of
+the OpenVPN source distribution.
+
+It is strongly recommended that
+.B IP
+be set to 127.0.0.1
+(localhost) to restrict accessibility of the management
+server to local clients.
+.TP
+.B \-\-management-client
+Management interface will connect as a TCP client to
+.B IP:port
+specified by
+.B \-\-management
+rather than listen as a TCP server.
+.\"*********************************************************
+.TP
+.B \-\-management-query-passwords
+Query management channel for private key password and
+.B \-\-auth-user-pass
+username/password. Only query the management channel
+for inputs which ordinarily would have been queried from the
+console.
+.\"*********************************************************
+.TP
+.B \-\-management-forget-disconnect
+Make OpenVPN forget passwords when management session
+disconnects.
+
+This directive does not affect the
+.B \-\-http-proxy
+username/password. It is always cached.
+.\"*********************************************************
+.TP
+.B \-\-management-hold
+Start OpenVPN in a hibernating state, until a client
+of the management interface explicitly starts it
+with the
+.B hold release
+command.
+.\"*********************************************************
+.TP
+.B \-\-management-signal
+Send SIGUSR1 signal to OpenVPN if management session disconnects.
+This is useful when you wish to disconnect an OpenVPN session on
+user logoff.
+.\"*********************************************************
+.TP
+.B \-\-management-log-cache n
+Cache the most recent
+.B n
+lines of log file history for usage
+by the management channel.
+.\"*********************************************************
+.TP
+.B \-\-management-client-auth
+Gives management interface client the responsibility
+to authenticate clients after their client certificate
+has been verified. See management-notes.txt in OpenVPN
+distribution for detailed notes.
+.\"*********************************************************
+.TP
+.B \-\-management-client-pf
+Management interface clients must specify a packet
+filter file for each connecting client. See management-notes.txt
+in OpenVPN distribution for detailed notes.
+.\"*********************************************************
+.TP
+.B \-\-management-client-user u
+When the management interface is listening on a unix domain socket,
+only allow connections from user
+.B u.
+.\"*********************************************************
+.TP
+.B \-\-management-client-group g
+When the management interface is listening on a unix domain socket,
+only allow connections from group
+.B g.
+.\"*********************************************************
+.TP
+.B \-\-plugin module-pathname [init-string]
+Load plug-in module from the file
+.B module-pathname,
+passing
+.B init-string
+as an argument
+to the module initialization function. Multiple
+plugin modules may be loaded into one OpenVPN
+process.
+
+For more information and examples on how to build OpenVPN
+plug-in modules, see the README file in the
+.B plugin
+folder of the OpenVPN source distribution.
+
+If you are using an RPM install of OpenVPN, see
+/usr/share/openvpn/plugin. The documentation is
+in
+.B doc
+and the actual plugin modules are in
+.B lib.
+
+Multiple plugin modules can be cascaded, and modules can be
+used in tandem with scripts. The modules will be called by
+OpenVPN in the order that they are declared in the config
+file. If both a plugin and script are configured for the same
+callback, the script will be called last. If the
+return code of the module/script controls an authentication
+function (such as tls-verify, auth-user-pass-verify, or
+client-connect), then
+every module and script must return success (0) in order for
+the connection to be authenticated.
+.\"*********************************************************
+.SS Server Mode
+Starting with OpenVPN 2.0, a multi-client TCP/UDP server mode
+is supported, and can be enabled with the
+.B \-\-mode server
+option. In server mode, OpenVPN will listen on a single
+port for incoming client connections. All client
+connections will be routed through a single tun or tap
+interface. This mode is designed for scalability and should
+be able to support hundreds or even thousands of clients
+on sufficiently fast hardware. SSL/TLS authentication must
+be used in this mode.
+.\"*********************************************************
+.TP
+.B \-\-server network netmask
+A helper directive designed to simplify the configuration
+of OpenVPN's server mode. This directive will set up an
+OpenVPN server which will allocate addresses to clients
+out of the given network/netmask. The server itself
+will take the ".1" address of the given network
+for use as the server-side endpoint of the local
+TUN/TAP interface.
+
+For example,
+.B \-\-server 10.8.0.0 255.255.255.0
+expands as follows:
+
+.nf
+.ft 3
+.in +4
+ mode server
+ tls-server
+ push "topology [topology]"
+
+ if dev tun AND (topology == net30 OR topology == p2p):
+ ifconfig 10.8.0.1 10.8.0.2
+ if !nopool:
+ ifconfig-pool 10.8.0.4 10.8.0.251
+ route 10.8.0.0 255.255.255.0
+ if client-to-client:
+ push "route 10.8.0.0 255.255.255.0"
+ else if topology == net30:
+ push "route 10.8.0.1"
+
+ if dev tap OR (dev tun AND topology == subnet):
+ ifconfig 10.8.0.1 255.255.255.0
+ if !nopool:
+ ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0
+ push "route-gateway 10.8.0.1"
+.in -4
+.ft
+.fi
+
+Don't use
+.B \-\-server
+if you are ethernet bridging. Use
+.B \-\-server-bridge
+instead.
+.\"*********************************************************
+.TP
+.B \-\-server-bridge gateway netmask pool-start-IP pool-end-IP
+.TP
+.B \-\-server-bridge ['nogw']
+
+A helper directive similar to
+.B \-\-server
+which is designed to simplify the configuration
+of OpenVPN's server mode in ethernet bridging configurations.
+
+If
+.B \-\-server-bridge
+is used without any parameters, it will enable a DHCP-proxy
+mode, where connecting OpenVPN clients will receive an IP
+address for their TAP adapter from the DHCP server running
+on the OpenVPN server-side LAN.
+Note that only clients that support
+the binding of a DHCP client with the TAP adapter (such as
+Windows) can support this mode. The optional
+.B nogw
+flag (advanced) indicates that gateway information should not be
+pushed to the client.
+
+To configure ethernet bridging, you
+must first use your OS's bridging capability
+to bridge the TAP interface with the ethernet
+NIC interface. For example, on Linux this is done
+with the
+.B brctl
+tool, and with Windows XP it is done in the Network
+Connections Panel by selecting the ethernet and
+TAP adapters and right-clicking on "Bridge Connections".
+
+Next you you must manually set the
+IP/netmask on the bridge interface. The
+.B gateway
+and
+.B netmask
+parameters to
+.B \-\-server-bridge
+can be set to either the IP/netmask of the
+bridge interface, or the IP/netmask of the
+default gateway/router on the bridged
+subnet.
+
+Finally, set aside a IP range in the bridged
+subnet,
+denoted by
+.B pool-start-IP
+and
+.B pool-end-IP,
+for OpenVPN to allocate to connecting
+clients.
+
+For example,
+.B server-bridge 10.8.0.4 255.255.255.0 10.8.0.128 10.8.0.254
+expands as follows:
+
+.nf
+.ft 3
+.in +4
+mode server
+tls-server
+
+ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
+push "route-gateway 10.8.0.4"
+.in -4
+.ft
+.fi
+
+In another example,
+.B \-\-server-bridge
+(without parameters) expands as follows:
+
+.nf
+.ft 3
+.in +4
+mode server
+tls-server
+
+push "route-gateway dhcp"
+.in -4
+.ft
+.fi
+
+Or
+.B \-\-server-bridge nogw
+expands as follows:
+
+.nf
+.ft 3
+.in +4
+mode server
+tls-server
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.TP
+.B \-\-push "option"
+Push a config file option back to the client for remote
+execution. Note that
+.B
+option
+must be enclosed in double quotes (""). The client must specify
+.B \-\-pull
+in its config file. The set of options which can be
+pushed is limited by both feasibility and security.
+Some options such as those which would execute scripts
+are banned, since they would effectively allow a compromised
+server to execute arbitrary code on the client.
+Other options such as TLS or MTU parameters
+cannot be pushed because the client needs to know
+them before the connection to the server can be initiated.
+
+This is a partial list of options which can currently be pushed:
+.B \-\-route, \-\-route-gateway, \-\-route-delay, \-\-redirect-gateway,
+.B \-\-ip-win32, \-\-dhcp-option,
+.B \-\-inactive, \-\-ping, \-\-ping-exit, \-\-ping-restart,
+.B \-\-setenv,
+.B \-\-persist-key, \-\-persist-tun, \-\-echo,
+.B \-\-comp-lzo,
+.B \-\-socket-flags,
+.B \-\-sndbuf, \-\-rcvbuf
+.\"*********************************************************
+.TP
+.B \-\-push-reset
+Don't inherit the global push list for a specific client instance.
+Specify this option in a client-specific context such
+as with a
+.B \-\-client-config-dir
+configuration file. This option will ignore
+.B \-\-push
+options at the global config file level.
+.\"*********************************************************
+.TP
+.B \-\-disable
+Disable a particular client (based on the common name)
+from connecting. Don't use this option to disable a client
+due to key or password compromise. Use a CRL (certificate
+revocation list) instead (see the
+.B \-\-crl-verify
+option).
+
+This option must be associated with a specific client instance,
+which means that it must be specified either in a client
+instance config file using
+.B \-\-client-config-dir
+or dynamically generated using a
+.B \-\-client-connect
+script.
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-pool start-IP end-IP [netmask]
+Set aside a pool of subnets to be
+dynamically allocated to connecting clients, similar
+to a DHCP server. For tun-style
+tunnels, each client will be given a /30 subnet (for
+interoperability with Windows clients). For tap-style
+tunnels, individual addresses will be allocated, and the
+optional
+.B netmask
+parameter will also be pushed to clients.
+
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-pool-persist file [seconds]
+Persist/unpersist ifconfig-pool
+data to
+.B file,
+at
+.B seconds
+intervals (default=600), as well as on program startup and
+shutdown.
+
+The goal of this option is to provide a long-term association
+between clients (denoted by their common name) and the virtual
+IP address assigned to them from the ifconfig-pool.
+Maintaining a long-term
+association is good for clients because it allows them
+to effectively use the
+.B \-\-persist-tun
+option.
+
+.B file
+is a comma-delimited ASCII file, formatted as
+<Common-Name>,<IP-address>.
+
+If
+.B seconds
+= 0,
+.B file
+will be treated as read-only. This is useful if
+you would like to treat
+.B file
+as a configuration file.
+
+Note that the entries in this file are treated by OpenVPN as
+suggestions only, based on past associations between
+a common name and IP address. They do not guarantee that the given common
+name will always receive the given IP address. If you want guaranteed
+assignment, use
+.B \-\-ifconfig-push
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-pool-linear
+Modifies the
+.B \-\-ifconfig-pool
+directive to
+allocate individual TUN interface addresses for
+clients rather than /30 subnets. NOTE: This option
+is incompatible with Windows clients.
+
+This option is deprecated, and should be replaced with
+.B \-\-topology p2p
+which is functionally equivalent.
+.\"*********************************************************
+.TP
+.B \-\-ifconfig-push local remote-netmask
+Push virtual IP endpoints for client tunnel,
+overriding the \-\-ifconfig-pool dynamic allocation.
+
+The parameters
+.B local
+and
+.B remote-netmask
+are set according to the
+.B \-\-ifconfig
+directive which you want to execute on the client machine to
+configure the remote end of the tunnel. Note that the parameters
+.B local
+and
+.B remote-netmask
+are from the perspective of the client, not the server. They may be
+DNS names rather than IP addresses, in which case they will be resolved
+on the server at the time of client connection.
+
+This option must be associated with a specific client instance,
+which means that it must be specified either in a client
+instance config file using
+.B \-\-client-config-dir
+or dynamically generated using a
+.B \-\-client-connect
+script.
+
+Remember also to include a
+.B \-\-route
+directive in the main OpenVPN config file which encloses
+.B local,
+so that the kernel will know to route it
+to the server's TUN/TAP interface.
+
+OpenVPN's internal client IP address selection algorithm works as
+follows:
+
+.B 1
+\-\- Use
+.B \-\-client-connect script
+generated file for static IP (first choice).
+.br
+.B 2
+\-\- Use
+.B \-\-client-config-dir
+file for static IP (next choice).
+.br
+.B 3
+\-\- Use
+.B \-\-ifconfig-pool
+allocation for dynamic IP (last choice).
+.br
+.\"*********************************************************
+.TP
+.B \-\-iroute network [netmask]
+Generate an internal route to a specific
+client. The
+.B netmask
+parameter, if omitted, defaults to 255.255.255.255.
+
+This directive can be used to route a fixed subnet from
+the server to a particular client, regardless
+of where the client is connecting from. Remember
+that you must also add the route to the system
+routing table as well (such as by using the
+.B \-\-route
+directive). The reason why two routes are needed
+is that the
+.B \-\-route
+directive routes the packet from the kernel
+to OpenVPN. Once in OpenVPN, the
+.B \-\-iroute
+directive routes to the specific client.
+
+This option must be specified either in a client
+instance config file using
+.B \-\-client-config-dir
+or dynamically generated using a
+.B \-\-client-connect
+script.
+
+The
+.B \-\-iroute
+directive also has an important interaction with
+.B \-\-push
+"route ...".
+.B \-\-iroute
+essentially defines a subnet which is owned by a
+particular client (we will call this client A).
+If you would like other clients to be able to reach A's
+subnet, you can use
+.B \-\-push
+"route ..."
+together with
+.B \-\-client-to-client
+to effect this. In order for all clients to see
+A's subnet, OpenVPN must push this route to all clients
+EXCEPT for A, since the subnet is already owned by A.
+OpenVPN accomplishes this by not
+not pushing a route to a client
+if it matches one of the client's iroutes.
+.\"*********************************************************
+.TP
+.B \-\-client-to-client
+Because the OpenVPN server mode handles multiple clients
+through a single tun or tap interface, it is effectively
+a router. The
+.B \-\-client-to-client
+flag tells OpenVPN to internally route client-to-client
+traffic rather than pushing all client-originating traffic
+to the TUN/TAP interface.
+
+When this option is used, each client will "see" the other
+clients which are currently connected. Otherwise, each
+client will only see the server. Don't use this option
+if you want to firewall tunnel traffic using
+custom, per-client rules.
+.\"*********************************************************
+.TP
+.B \-\-duplicate-cn
+Allow multiple clients with the same common name to concurrently connect.
+In the absence of this option, OpenVPN will disconnect a client instance
+upon connection of a new client having the same common name.
+.\"*********************************************************
+.TP
+.B \-\-client-connect script
+Run
+.B script
+on client connection. The script is passed the common name
+and IP address of the just-authenticated client
+as environmental variables (see environmental variable section
+below). The script is also passed
+the pathname of a freshly created temporary file as $1
+(i.e. the first command line argument), to be used by the script
+to pass dynamically generated config file directives back to OpenVPN.
+
+If the script wants to generate a dynamic config file
+to be applied on the server when the client connects,
+it should write it to the file named by $1.
+
+See the
+.B \-\-client-config-dir
+option below for options which
+can be legally used in a dynamically generated config file.
+
+Note that the return value of
+.B script
+is significant. If
+.B script
+returns a non-zero error status, it will cause the client
+to be disconnected.
+.\"*********************************************************
+.TP
+.B \-\-client-disconnect
+Like
+.B \-\-client-connect
+but called on client instance shutdown. Will not be called
+unless the
+.B \-\-client-connect
+script and plugins (if defined)
+were previously called on this instance with
+successful (0) status returns.
+
+The exception to this rule is if the
+.B \-\-client-disconnect
+script or plugins are cascaded, and at least one client-connect
+function succeeded, then ALL of the client-disconnect functions for
+scripts and plugins will be called on client instance object deletion,
+even in cases where some of the related client-connect functions returned
+an error status.
+.B
+.\"*********************************************************
+.TP
+.B \-\-client-config-dir dir
+Specify a directory
+.B dir
+for custom client config files. After
+a connecting client has been authenticated, OpenVPN will
+look in this directory for a file having the same name
+as the client's X509 common name. If a matching file
+exists, it will be opened and parsed for client-specific
+configuration options. If no matching file is found, OpenVPN
+will instead try to open and parse a default file called
+"DEFAULT", which may be provided but is not required. Note that
+the configuration files must be readable by the OpenVPN process
+after it has dropped it's root privileges.
+
+This file can specify a fixed IP address for a given
+client using
+.B \-\-ifconfig-push,
+as well as fixed subnets owned by the client using
+.B \-\-iroute.
+
+One of the useful properties of this option is that it
+allows client configuration files to be conveniently
+created, edited, or removed while the server is live,
+without needing to restart the server.
+
+The following
+options are legal in a client-specific context:
+.B \-\-push, \-\-push-reset, \-\-iroute, \-\-ifconfig-push,
+and
+.B \-\-config.
+.\"*********************************************************
+.TP
+.B \-\-ccd-exclusive
+Require, as a
+condition of authentication, that a connecting client has a
+.B \-\-client-config-dir
+file.
+.\"*********************************************************
+.TP
+.B \-\-tmp-dir dir
+Specify a directory
+.B dir
+for temporary files. This directory will be used by
+openvpn processes and script to communicate temporary
+data with openvpn main process. Note that
+the directory must be writable by the OpenVPN process
+after it has dropped it's root privileges.
+
+This directory will be used by in the following cases:
+
+*
+.B \-\-client-connect
+scripts to dynamically generate client-specific
+configuration files.
+
+*
+.B OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
+plugin hook to return success/failure via auth_control_file
+when using deferred auth method
+
+*
+.B OPENVPN_PLUGIN_ENABLE_PF
+plugin hook to pass filtering rules via pf_file
+.\"*********************************************************
+.TP
+.B \-\-hash-size r v
+Set the size of the real address hash table to
+.B r
+and the virtual address table to
+.B v.
+By default, both tables are sized at 256 buckets.
+.\"*********************************************************
+.TP
+.B \-\-bcast-buffers n
+Allocate
+.B n
+buffers for broadcast datagrams (default=256).
+.\"*********************************************************
+.TP
+.B \-\-tcp-queue-limit n
+Maximum number of output packets queued before TCP (default=64).
+
+When OpenVPN is tunneling data from a TUN/TAP device to a
+remote client over a TCP connection, it is possible that the TUN/TAP device
+might produce data at a faster rate than the TCP connection
+can support. When the number of output packets queued before sending to
+the TCP socket reaches this limit for a given client connection,
+OpenVPN will start to drop outgoing packets directed
+at this client.
+.\"*********************************************************
+.TP
+.B \-\-tcp-nodelay
+This macro sets the TCP_NODELAY socket flag on the server
+as well as pushes it to connecting clients. The TCP_NODELAY
+flag disables the Nagle algorithm on TCP sockets causing
+packets to be transmitted immediately with low latency,
+rather than waiting a short period of time in order
+to aggregate several packets into a larger containing
+packet. In VPN applications over TCP, TCP_NODELAY
+is generally a good latency optimization.
+
+The macro expands as follows:
+
+.nf
+.ft 3
+.in +4
+ if mode server:
+ socket-flags TCP_NODELAY
+ push "socket-flags TCP_NODELAY"
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.TP
+.B \-\-max-clients n
+Limit server to a maximum of
+.B n
+concurrent clients.
+.\"*********************************************************
+.TP
+.B \-\-max-routes-per-client n
+Allow a maximum of
+.B n
+internal routes per client (default=256).
+This is designed to
+help contain DoS attacks where an authenticated client floods the
+server with packets appearing to come from many unique MAC addresses,
+forcing the server to deplete
+virtual memory as its internal routing table expands.
+This directive can be used in a
+.B \-\-client-config-dir
+file or auto-generated by a
+.B \-\-client-connect
+script to override the global value for a particular client.
+
+Note that this
+directive affects OpenVPN's internal routing table, not the
+kernel routing table.
+.\"*********************************************************
+.TP
+.B \-\-connect-freq n sec
+Allow a maximum of
+.B n
+new connections per
+.B sec
+seconds from clients. This is designed to contain DoS attacks which flood
+the server with connection requests using certificates which
+will ultimately fail to authenticate.
+
+This is an imperfect solution however, because in a real
+DoS scenario, legitimate connections might also be refused.
+
+For the best protection against DoS attacks in server mode,
+use
+.B \-\-proto udp
+and
+.B \-\-tls-auth.
+.\"*********************************************************
+.TP
+.B \-\-learn-address cmd
+Run script or shell command
+.B cmd
+to validate client virtual addresses or routes.
+
+.B cmd
+will be executed with 3 parameters:
+
+.B [1] operation \-\-
+"add", "update", or "delete" based on whether or not
+the address is being added to, modified, or deleted from
+OpenVPN's internal routing table.
+.br
+.B [2] address \-\-
+The address being learned or unlearned. This can be
+an IPv4 address such as "198.162.10.14", an IPv4 subnet
+such as "198.162.10.0/24", or an ethernet MAC address (when
+.B \-\-dev tap
+is being used) such as "00:FF:01:02:03:04".
+.br
+.B [3] common name \-\-
+The common name on the certificate associated with the
+client linked to this address. Only present for "add"
+or "update" operations, not "delete".
+
+On "add" or "update" methods, if the script returns
+a failure code (non-zero), OpenVPN will reject the address
+and will not modify its internal routing table.
+
+Normally, the
+.B cmd
+script will use the information provided above to set
+appropriate firewall entries on the VPN TUN/TAP interface.
+Since OpenVPN provides the association between virtual IP
+or MAC address and the client's authenticated common name,
+it allows a user-defined script to configure firewall access
+policies with regard to the client's high-level common name,
+rather than the low level client virtual addresses.
+.\"*********************************************************
+.TP
+.B \-\-auth-user-pass-verify script method
+Require the client to provide a username/password (possibly
+in addition to a client certificate) for authentication.
+
+OpenVPN will execute
+.B script
+as a shell command to validate the username/password
+provided by the client.
+
+If
+.B method
+is set to "via-env", OpenVPN will call
+.B script
+with the environmental variables
+.B username
+and
+.B password
+set to the username/password strings provided by the client.
+Be aware that this method is insecure on some platforms which
+make the environment of a process publicly visible to other
+unprivileged processes.
+
+If
+.B method
+is set to "via-file", OpenVPN will write the username and
+password to the first two lines of a temporary file. The filename
+will be passed as an argument to
+.B script,
+and the file will be automatically deleted by OpenVPN after
+the script returns. The location of the temporary file is
+controlled by the
+.B \-\-tmp-dir
+option, and will default to the current directory if unspecified.
+For security, consider setting
+.B \-\-tmp-dir
+to a volatile storage medium such as
+.B /dev/shm
+(if available) to prevent the username/password file from touching the hard drive.
+
+The script should examine the username
+and password,
+returning a success exit code (0) if the
+client's authentication request is to be accepted, or a failure
+code (1) to reject the client.
+
+This directive is designed to enable a plugin-style interface
+for extending OpenVPN's authentication capabilities.
+
+To protect against a client passing a maliciously formed
+username or password string, the username string must
+consist only of these characters: alphanumeric, underbar
+('_'), dash ('-'), dot ('.'), or at ('@'). The password
+string can consist of any printable characters except for
+CR or LF. Any illegal characters in either the username
+or password string will be converted to underbar ('_').
+
+Care must be taken by any user-defined scripts to avoid
+creating a security vulnerability in the way that these
+strings are handled. Never use these strings in such a way
+that they might be escaped or evaluated by a shell interpreter.
+
+For a sample script that performs PAM authentication, see
+.B sample-scripts/auth-pam.pl
+in the OpenVPN source distribution.
+.\"*********************************************************
+.TP
+.B \-\-opt-verify
+Clients that connect with options that are incompatible
+with those of the server will be disconnected.
+
+Options that will be compared for compatibility include
+dev-type, link-mtu, tun-mtu, proto, tun-ipv6, ifconfig,
+comp-lzo, fragment, keydir, cipher, auth, keysize, secret,
+no-replay, no-iv, tls-auth, key-method, tls-server, and tls-client.
+
+This option requires that
+.B \-\-disable-occ
+NOT be used.
+.\"*********************************************************
+.TP
+.B \-\-auth-user-pass-optional
+Allow connections by clients that do not specify a username/password.
+Normally, when
+.B \-\-auth-user-pass-verify
+or
+.B \-\-management-client-auth
+is specified (or an authentication plugin module), the
+OpenVPN server daemon will require connecting clients to specify a
+username and password. This option makes the submission of a username/password
+by clients optional, passing the responsibility to the user-defined authentication
+module/script to accept or deny the client based on other factors
+(such as the setting of X509 certificate fields). When this option is used,
+and a connecting client does not submit a username/password, the user-defined
+authentication module/script will see the username and password as being set
+to empty strings (""). The authentication module/script MUST have logic
+to detect this condition and respond accordingly.
+.\"*********************************************************
+.TP
+.B \-\-client-cert-not-required
+Don't require client certificate, client will authenticate
+using username/password only. Be aware that using this directive
+is less secure than requiring certificates from all clients.
+
+If you use this directive, the
+entire responsibility of authentication will rest on your
+.B \-\-auth-user-pass-verify
+script, so keep in mind that bugs in your script
+could potentially compromise the security of your VPN.
+
+If you don't use this directive, but you also specify an
+.B \-\-auth-user-pass-verify
+script, then OpenVPN will perform double authentication. The
+client certificate verification AND the
+.B \-\-auth-user-pass-verify
+script will need to succeed in order for a client to be
+authenticated and accepted onto the VPN.
+.\"*********************************************************
+.TP
+.B \-\-username-as-common-name
+For
+.B \-\-auth-user-pass-verify
+authentication, use
+the authenticated username as the common name,
+rather than the common name from the client cert.
+.\"*********************************************************
+.TP
+.B \-\-no-name-remapping
+Allow Common Name, X509 Subject, and username strings to include
+any printable character including space, but excluding control
+characters such as tab, newline, and carriage-return.
+
+By default, OpenVPN will remap
+any character other than alphanumeric, underbar ('_'), dash
+('-'), dot ('.'), and slash ('/') to underbar ('_'). The X509
+Subject string as returned by the
+.B tls_id
+environmental variable, can additionally contain colon (':') or
+equal ('=').
+
+While name remapping is performed for security reasons to reduce
+the possibility of introducing string expansion security vulnerabilities
+in user-defined authentication
+scripts, this option is provided for those cases where it is desirable to
+disable the remapping feature. Don't use this option unless you
+know what you are doing!
+.\"*********************************************************
+.TP
+.B \-\-port-share host port
+When run in TCP server mode, share the OpenVPN port with
+another application, such as an HTTPS server. If OpenVPN
+senses a connection to its port which is using a non-OpenVPN
+protocol, it will proxy the connection to the server at
+.B host:port.
+Currently only designed to work with HTTP/HTTPS,
+though it would be theoretically possible to extend to
+other protocols such as ssh.
+
+Not implemented on Windows.
+.\"*********************************************************
+.SS Client Mode
+Use client mode when connecting to an OpenVPN server
+which has
+.B \-\-server, \-\-server-bridge,
+or
+.B \-\-mode server
+in it's configuration.
+.\"*********************************************************
+.TP
+.B \-\-client
+A helper directive designed to simplify the configuration
+of OpenVPN's client mode. This directive is equivalent to:
+
+.nf
+.ft 3
+.in +4
+ pull
+ tls-client
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.TP
+.B \-\-pull
+This option must be used on a client which is connecting
+to a multi-client server. It indicates to OpenVPN that it
+should accept options pushed by the server, provided they
+are part of the legal set of pushable options (note that the
+.B \-\-pull
+option is implied by
+.B \-\-client
+).
+
+In particular,
+.B \-\-pull
+allows the server to push routes to the client, so you should
+not use
+.B \-\-pull
+or
+.B \-\-client
+in situations where you don't trust the server to have control
+over the client's routing table.
+.\"*********************************************************
+.TP
+.B \-\-auth-user-pass [up]
+Authenticate with server using username/password.
+.B up
+is a file containing username/password on 2 lines (Note: OpenVPN
+will only read passwords from a file if it has been built
+with the \-\-enable-password-save configure option, or on Windows
+by defining ENABLE_PASSWORD_SAVE in win/settings.in).
+
+If
+.B up
+is omitted, username/password will be prompted from the
+console.
+
+The server configuration must specify an
+.B \-\-auth-user-pass-verify
+script to verify the username/password provided by
+the client.
+.\"*********************************************************
+.TP
+.B \-\-auth-retry type
+Controls how OpenVPN responds to username/password verification
+errors such as the client-side response to an AUTH_FAILED message from the server
+or verification failure of the private key password.
+
+Normally used to prevent auth errors from being fatal
+on the client side, and to permit username/password requeries in case
+of error.
+
+An AUTH_FAILED message is generated by the server if the client
+fails
+.B \-\-auth-user-pass
+authentication, or if the server-side
+.B \-\-client-connect
+script returns an error status when the client
+tries to connect.
+
+.B type
+can be one of:
+
+.B none \-\-
+Client will exit with a fatal error (this is the default).
+.br
+.B nointeract \-\-
+Client will retry the connection without requerying for an
+.B \-\-auth-user-pass
+username/password. Use this option for unattended clients.
+.br
+.B interact \-\-
+Client will requery for an
+.B \-\-auth-user-pass
+username/password and/or private key password before attempting a reconnection.
+
+Note that while this option cannot be pushed, it can be controlled
+from the management interface.
+.\"*********************************************************
+.TP
+.B \-\-server-poll-timeout n
+when polling possible remote servers to connect to
+in a round-robin fashion, spend no more than
+.B n
+seconds waiting for a response before trying the next server.
+.\"*********************************************************
+.TP
+.B \-\-explicit-exit-notify [n]
+In UDP client mode or point-to-point mode, send server/peer an exit notification
+if tunnel is restarted or OpenVPN process is exited. In client mode, on
+exit/restart, this
+option will tell the server to immediately close its client instance object
+rather than waiting for a timeout. The
+.B n
+parameter (default=1) controls the maximum number of attempts that the client
+will try to resend the exit notification message. OpenVPN will not send any exit
+notifications unless this option is enabled.
+.\"*********************************************************
+.SS Data Channel Encryption Options:
+These options are meaningful for both Static & TLS-negotiated key modes
+(must be compatible between peers).
+.\"*********************************************************
+.TP
+.B \-\-secret file [direction]
+Enable Static Key encryption mode (non-TLS).
+Use pre-shared secret
+.B file
+which was generated with
+.B \-\-genkey.
+
+The optional
+.B direction
+parameter enables the use of 4 distinct keys
+(HMAC-send, cipher-encrypt, HMAC-receive, cipher-decrypt), so that
+each data flow direction has a different set of HMAC and cipher keys.
+This has a number of desirable security properties including
+eliminating certain kinds of DoS and message replay attacks.
+
+When the
+.B direction
+parameter is omitted, 2 keys are used bidirectionally, one for HMAC
+and the other for encryption/decryption.
+
+The
+.B direction
+parameter should always be complementary on either side of the connection,
+i.e. one side should use "0" and the other should use "1", or both sides
+should omit it altogether.
+
+The
+.B direction
+parameter requires that
+.B file
+contains a 2048 bit key. While pre-1.5 versions of OpenVPN
+generate 1024 bit key files, any version of OpenVPN which
+supports the
+.B direction
+parameter, will also support 2048 bit key file generation
+using the
+.B \-\-genkey
+option.
+
+Static key encryption mode has certain advantages,
+the primary being ease of configuration.
+
+There are no certificates
+or certificate authorities or complicated negotiation handshakes and protocols.
+The only requirement is that you have a pre-existing secure channel with
+your peer (such as
+.B ssh
+) to initially copy the key. This requirement, along with the
+fact that your key never changes unless you manually generate a new one,
+makes it somewhat less secure than TLS mode (see below). If an attacker
+manages to steal your key, everything that was ever encrypted with
+it is compromised. Contrast that to the perfect forward secrecy features of
+TLS mode (using Diffie Hellman key exchange), where even if an attacker
+was able to steal your private key, he would gain no information to help
+him decrypt past sessions.
+
+Another advantageous aspect of Static Key encryption mode is that
+it is a handshake-free protocol
+without any distinguishing signature or feature
+(such as a header or protocol handshake sequence)
+that would mark the ciphertext packets as being
+generated by OpenVPN. Anyone eavesdropping on the wire
+would see nothing
+but random-looking data.
+.\"*********************************************************
+.TP
+.B \-\-auth alg
+Authenticate packets with HMAC using message
+digest algorithm
+.B alg.
+(The default is
+.B SHA1
+).
+HMAC is a commonly used message authentication algorithm (MAC) that uses
+a data string, a secure hash algorithm, and a key, to produce
+a digital signature.
+
+OpenVPN's usage of HMAC is to first encrypt a packet, then HMAC the resulting ciphertext.
+
+In static-key encryption mode, the HMAC key
+is included in the key file generated by
+.B \-\-genkey.
+In TLS mode, the HMAC key is dynamically generated and shared
+between peers via the TLS control channel. If OpenVPN receives a packet with
+a bad HMAC it will drop the packet.
+HMAC usually adds 16 or 20 bytes per packet.
+Set
+.B alg=none
+to disable authentication.
+
+For more information on HMAC see
+.I http://www.cs.ucsd.edu/users/mihir/papers/hmac.html
+.\"*********************************************************
+.TP
+.B \-\-cipher alg
+Encrypt packets with cipher algorithm
+.B alg.
+The default is
+.B BF-CBC,
+an abbreviation for Blowfish in Cipher Block Chaining mode.
+Blowfish has the advantages of being fast, very secure, and allowing key sizes
+of up to 448 bits. Blowfish is designed to be used in situations where
+keys are changed infrequently.
+
+For more information on blowfish, see
+.I http://www.counterpane.com/blowfish.html
+
+To see other ciphers that are available with
+OpenVPN, use the
+.B \-\-show-ciphers
+option.
+
+OpenVPN supports the CBC, CFB, and OFB cipher modes,
+however CBC is recommended and CFB and OFB should
+be considered advanced modes.
+
+Set
+.B alg=none
+to disable encryption.
+.\"*********************************************************
+.TP
+.B \-\-keysize n
+Size of cipher key in bits (optional).
+If unspecified, defaults to cipher-specific default. The
+.B \-\-show-ciphers
+option (see below) shows all available OpenSSL ciphers,
+their default key sizes, and whether the key size can
+be changed. Use care in changing a cipher's default
+key size. Many ciphers have not been extensively
+cryptanalyzed with non-standard key lengths, and a
+larger key may offer no real guarantee of greater
+security, or may even reduce security.
+.\"*********************************************************
+.TP
+.B \-\-prng alg [nsl]
+(Advanced) For PRNG (Pseudo-random number generator),
+use digest algorithm
+.B alg
+(default=sha1), and set
+.B nsl
+(default=16)
+to the size in bytes of the nonce secret length (between 16 and 64).
+
+Set
+.B alg=none
+to disable the PRNG and use the OpenSSL RAND_bytes function
+instead for all of OpenVPN's pseudo-random number needs.
+.\"*********************************************************
+.TP
+.B \-\-engine [engine-name]
+Enable OpenSSL hardware-based crypto engine functionality.
+
+If
+.B engine-name
+is specified,
+use a specific crypto engine. Use the
+.B \-\-show-engines
+standalone option to list the crypto engines which are
+supported by OpenSSL.
+.\"*********************************************************
+.TP
+.B \-\-no-replay
+(Advanced) Disable OpenVPN's protection against replay attacks.
+Don't use this option unless you are prepared to make
+a tradeoff of greater efficiency in exchange for less
+security.
+
+OpenVPN provides datagram replay protection by default.
+
+Replay protection is accomplished
+by tagging each outgoing datagram with an identifier
+that is guaranteed to be unique for the key being used.
+The peer that receives the datagram will check for
+the uniqueness of the identifier. If the identifier
+was already received in a previous datagram, OpenVPN
+will drop the packet. Replay protection is important
+to defeat attacks such as a SYN flood attack, where
+the attacker listens in the wire, intercepts a TCP
+SYN packet (identifying it by the context in which
+it occurs in relation to other packets), then floods
+the receiving peer with copies of this packet.
+
+OpenVPN's replay protection is implemented in slightly
+different ways, depending on the key management mode
+you have selected.
+
+In Static Key mode
+or when using an CFB or OFB mode cipher, OpenVPN uses a
+64 bit unique identifier that combines a time stamp with
+an incrementing sequence number.
+
+When using TLS mode for key exchange and a CBC cipher
+mode, OpenVPN uses only a 32 bit sequence number without
+a time stamp, since OpenVPN can guarantee the uniqueness
+of this value for each key. As in IPSec, if the sequence number is
+close to wrapping back to zero, OpenVPN will trigger
+a new key exchange.
+
+To check for replays, OpenVPN uses
+the
+.I sliding window
+algorithm used
+by IPSec.
+.\"*********************************************************
+.TP
+.B \-\-replay-window n [t]
+Use a replay protection sliding-window of size
+.B n
+and a time window of
+.B t
+seconds.
+
+By default
+.B n
+is 64 (the IPSec default) and
+.B t
+is 15 seconds.
+
+This option is only relevant in UDP mode, i.e.
+when either
+.B \-\-proto udp
+is specifed, or no
+.B \-\-proto
+option is specified.
+
+When OpenVPN tunnels IP packets over UDP, there is the possibility that
+packets might be dropped or delivered out of order. Because OpenVPN, like IPSec,
+is emulating the physical network layer,
+it will accept an out-of-order packet sequence, and
+will deliver such packets in the same order they were received to
+the TCP/IP protocol stack, provided they satisfy several constraints.
+
+.B (a)
+The packet cannot be a replay (unless
+.B \-\-no-replay
+is specified, which disables replay protection altogether).
+
+.B (b)
+If a packet arrives out of order, it will only be accepted if the difference
+between its sequence number and the highest sequence number received
+so far is less than
+.B n.
+
+.B (c)
+If a packet arrives out of order, it will only be accepted if it arrives no later
+than
+.B t
+seconds after any packet containing a higher sequence number.
+
+If you are using a network link with a large pipeline (meaning that
+the product of bandwidth and latency is high), you may want to use
+a larger value for
+.B n.
+Satellite links in particular often require this.
+
+If you run OpenVPN at
+.B \-\-verb 4,
+you will see the message "Replay-window backtrack occurred [x]"
+every time the maximum sequence number backtrack seen thus far
+increases. This can be used to calibrate
+.B n.
+
+There is some controversy on the appropriate method of handling packet
+reordering at the security layer.
+
+Namely, to what extent should the
+security layer protect the encapsulated protocol from attacks which masquerade
+as the kinds of normal packet loss and reordering that occur over IP networks?
+
+The IPSec and OpenVPN approach is to allow packet reordering within a certain
+fixed sequence number window.
+
+OpenVPN adds to the IPSec model by limiting the window size in time as well as
+sequence space.
+
+OpenVPN also adds TCP transport as an option (not offered by IPSec) in which
+case OpenVPN can adopt a very strict attitude towards message deletion and
+reordering: Don't allow it. Since TCP guarantees reliability, any packet
+loss or reordering event can be assumed to be an attack.
+
+In this sense, it could be argued that TCP tunnel transport is preferred when
+tunneling non-IP or UDP application protocols which might be vulnerable to a
+message deletion or reordering attack which falls within the normal
+operational parameters of IP networks.
+
+So I would make the statement that one should never tunnel a non-IP protocol
+or UDP application protocol over UDP, if the protocol might be vulnerable to a
+message deletion or reordering attack that falls within the normal operating
+parameters of what is to be expected from the physical IP layer. The problem
+is easily fixed by simply using TCP as the VPN transport layer.
+.\"*********************************************************
+.TP
+.B \-\-mute-replay-warnings
+Silence the output of replay warnings, which are a common
+false alarm on WiFi networks. This option preserves
+the security of the replay protection code without
+the verbosity associated with warnings about duplicate
+packets.
+.\"*********************************************************
+.TP
+.B \-\-replay-persist file
+Persist replay-protection state across sessions using
+.B file
+to save and reload the state.
+
+This option will strengthen protection against replay attacks,
+especially when you are using OpenVPN in a dynamic context (such
+as with
+.B \-\-inetd)
+when OpenVPN sessions are frequently started and stopped.
+
+This option will keep a disk copy of the current replay protection
+state (i.e. the most recent packet timestamp and sequence number
+received from the remote peer), so that if an OpenVPN session
+is stopped and restarted, it will reject any replays of packets
+which were already received by the prior session.
+
+This option only makes sense when replay protection is enabled
+(the default) and you are using either
+.B \-\-secret
+(shared-secret key mode) or TLS mode with
+.B \-\-tls-auth.
+.\"*********************************************************
+.TP
+.B \-\-no-iv
+(Advanced) Disable OpenVPN's use of IV (cipher initialization vector).
+Don't use this option unless you are prepared to make
+a tradeoff of greater efficiency in exchange for less
+security.
+
+OpenVPN uses an IV by default, and requires it for CFB and
+OFB cipher modes (which are totally insecure without it).
+Using an IV is important for security when multiple
+messages are being encrypted/decrypted with the same key.
+
+IV is implemented differently depending on the cipher mode used.
+
+In CBC mode, OpenVPN uses a pseudo-random IV for each packet.
+
+In CFB/OFB mode, OpenVPN uses a unique sequence number and time stamp
+as the IV. In fact, in CFB/OFB mode, OpenVPN uses a datagram
+space-saving optimization that uses the unique identifier for
+datagram replay protection as the IV.
+.\"*********************************************************
+.TP
+.B \-\-test-crypto
+Do a self-test of OpenVPN's crypto options by encrypting and
+decrypting test packets using the data channel encryption options
+specified above. This option does not require a peer to function,
+and therefore can be specified without
+.B \-\-dev
+or
+.B \-\-remote.
+
+The typical usage of
+.B \-\-test-crypto
+would be something like this:
+
+.B openvpn \-\-test-crypto \-\-secret key
+
+or
+
+.B openvpn \-\-test-crypto \-\-secret key \-\-verb 9
+
+This option is very useful to test OpenVPN after it has been ported to
+a new platform, or to isolate problems in the compiler, OpenSSL
+crypto library, or OpenVPN's crypto code. Since it is a self-test mode,
+problems with encryption and authentication can be debugged independently
+of network and tunnel issues.
+.\"*********************************************************
+.SS TLS Mode Options:
+TLS mode is the most powerful crypto mode of OpenVPN in both security and flexibility.
+TLS mode works by establishing control and
+data channels which are multiplexed over a single TCP/UDP port. OpenVPN initiates
+a TLS session over the control channel and uses it to exchange cipher
+and HMAC keys to protect the data channel. TLS mode uses a robust reliability
+layer over the UDP connection for all control channel communication, while
+the data channel, over which encrypted tunnel data passes, is forwarded without
+any mediation. The result is the best of both worlds: a fast data channel
+that forwards over UDP with only the overhead of encrypt,
+decrypt, and HMAC functions,
+and a control channel that provides all of the security features of TLS,
+including certificate-based authentication and Diffie Hellman forward secrecy.
+
+To use TLS mode, each peer that runs OpenVPN should have its own local
+certificate/key pair (
+.B \-\-cert
+and
+.B \-\-key
+), signed by the root certificate which is specified
+in
+.B \-\-ca.
+
+When two OpenVPN peers connect, each presents its local certificate to the
+other. Each peer will then check that its partner peer presented a
+certificate which was signed by the master root certificate as specified in
+.B \-\-ca.
+
+If that check on both peers succeeds, then the TLS negotiation
+will succeed, both OpenVPN
+peers will exchange temporary session keys, and the tunnel will begin
+passing data.
+
+The OpenVPN distribution contains a set of scripts for
+managing RSA certificates & keys,
+located in the
+.I easy-rsa
+subdirectory.
+
+The easy-rsa package is also rendered in web form here:
+.I http://openvpn.net/easyrsa.html
+.\"*********************************************************
+.TP
+.B \-\-tls-server
+Enable TLS and assume server role during TLS handshake. Note that
+OpenVPN is designed as a peer-to-peer application. The designation
+of client or server is only for the purpose of negotiating the TLS
+control channel.
+.\"*********************************************************
+.TP
+.B \-\-tls-client
+Enable TLS and assume client role during TLS handshake.
+.\"*********************************************************
+.TP
+.B \-\-ca file
+Certificate authority (CA) file in .pem format, also referred to as the
+.I root
+certificate. This file can have multiple
+certificates in .pem format, concatenated together. You can construct your own
+certificate authority certificate and private key by using a command such as:
+
+.B openssl req -nodes -new -x509 -keyout ca.key -out ca.crt
+
+Then edit your openssl.cnf file and edit the
+.B certificate
+variable to point to your new root certificate
+.B ca.crt.
+
+For testing purposes only, the OpenVPN distribution includes a sample
+CA certificate (ca.crt).
+Of course you should never use
+the test certificates and test keys distributed with OpenVPN in a
+production environment, since by virtue of the fact that
+they are distributed with OpenVPN, they are totally insecure.
+.\"*********************************************************
+.TP
+.B \-\-capath dir
+Directory containing trusted certificates (CAs and CRLs).
+Available with OpenSSL version >= 0.9.7 dev.
+.\"*********************************************************
+.TP
+.B \-\-dh file
+File containing Diffie Hellman parameters
+in .pem format (required for
+.B \-\-tls-server
+only). Use
+
+.B openssl dhparam -out dh1024.pem 1024
+
+to generate your own, or use the existing dh1024.pem file
+included with the OpenVPN distribution. Diffie Hellman parameters
+may be considered public.
+.\"*********************************************************
+.TP
+.B \-\-cert file
+Local peer's signed certificate in .pem format \-\- must be signed
+by a certificate authority whose certificate is in
+.B \-\-ca file.
+Each peer in an OpenVPN link running in TLS mode should have its own
+certificate and private key file. In addition, each certificate should
+have been signed by the key of a certificate
+authority whose public key resides in the
+.B \-\-ca
+certificate authority file.
+You can easily make your own certificate authority (see above) or pay money
+to use a commercial service such as thawte.com (in which case you will be
+helping to finance the world's second space tourist :).
+To generate a certificate,
+you can use a command such as:
+
+.B openssl req -nodes -new -keyout mycert.key -out mycert.csr
+
+If your certificate authority private key lives on another machine, copy
+the certificate signing request (mycert.csr) to this other machine (this can
+be done over an insecure channel such as email). Now sign the certificate
+with a command such as:
+
+.B openssl ca -out mycert.crt -in mycert.csr
+
+Now copy the certificate (mycert.crt)
+back to the peer which initially generated the .csr file (this
+can be over a public medium).
+Note that the
+.B openssl ca
+command reads the location of the certificate authority key from its
+configuration file such as
+.B /usr/share/ssl/openssl.cnf
+\-\- note also
+that for certificate authority functions, you must set up the files
+.B index.txt
+(may be empty) and
+.B serial
+(initialize to
+.B
+01
+).
+.\"*********************************************************
+.TP
+.B \-\-key file
+Local peer's private key in .pem format. Use the private key which was generated
+when you built your peer's certificate (see
+.B -cert file
+above).
+.\"*********************************************************
+.TP
+.B \-\-pkcs12 file
+Specify a PKCS #12 file containing local private key,
+local certificate, and root CA certificate.
+This option can be used instead of
+.B \-\-ca, \-\-cert,
+and
+.B \-\-key.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-cert-private [0|1]...
+Set if access to certificate object should be performed after login.
+Every provider has its own setting.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-id name
+Specify the serialized certificate id to be used. The id can be gotten
+by the standalone
+.B \-\-show-pkcs11-ids
+option.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-id-management
+Acquire PKCS#11 id from management interface. In this case a NEED-STR 'pkcs11-id-request'
+real-time message will be triggered, application may use pkcs11-id-count command to
+retrieve available number of certificates, and pkcs11-id-get command to retrieve certificate
+id and certificate body.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-pin-cache seconds
+Specify how many seconds the PIN can be cached, the default is until the token is removed.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-protected-authentication [0|1]...
+Use PKCS#11 protected authentication path, useful for biometric and external
+keypad devices.
+Every provider has its own setting.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-providers provider...
+Specify a RSA Security Inc. PKCS #11 Cryptographic Token Interface (Cryptoki) providers
+to load.
+This option can be used instead of
+.B \-\-cert, \-\-key,
+and
+.B \-\-pkcs12.
+.\"*********************************************************
+.TP
+.B \-\-pkcs11-private-mode mode...
+Specify which method to use in order to perform private key operations.
+A different mode can be specified for each provider.
+Mode is encoded as hex number, and can be a mask one of the following:
+
+.B 0
+(default) \-\- Try to determind automatically.
+.br
+.B 1
+\-\- Use sign.
+.br
+.B 2
+\-\- Use sign recover.
+.br
+.B 4
+\-\- Use decrypt.
+.br
+.B 8
+\-\- Use unwrap.
+.br
+.\"*********************************************************
+.TP
+.B \-\-cryptoapicert select-string
+Load the certificate and private key from the
+Windows Certificate System Store (Windows Only).
+
+Use this option instead of
+.B \-\-cert
+and
+.B \-\-key.
+
+This makes
+it possible to use any smart card, supported by Windows, but also any
+kind of certificate, residing in the Cert Store, where you have access to
+the private key. This option has been tested with a couple of different
+smart cards (GemSAFE, Cryptoflex, and Swedish Post Office eID) on the
+client side, and also an imported PKCS12 software certificate on the
+server side.
+
+To select a certificate, based on a substring search in the
+certificate's subject:
+
+.B cryptoapicert
+"SUBJ:Peter Runestig"
+
+To select a certificate, based on certificate's thumbprint:
+
+.B cryptoapicert
+"THUMB:f6 49 24 41 01 b4 ..."
+
+The thumbprint hex string can easily be copy-and-pasted from the Windows
+Certificate Store GUI.
+
+.\"*********************************************************
+.TP
+.B \-\-key-method m
+Use data channel key negotiation method
+.B m.
+The key method must match on both sides of the connection.
+
+After OpenVPN negotiates a TLS session, a new set of keys
+for protecting the tunnel data channel is generated and
+exchanged over the TLS session.
+
+In method 1 (the default for OpenVPN 1.x), both sides generate
+random encrypt and HMAC-send keys which are forwarded to
+the other host over the TLS channel.
+
+In method 2, (the default for OpenVPN 2.0)
+the client generates a random key. Both client
+and server also generate some random seed material. All key source
+material is exchanged over the TLS channel. The actual
+keys are generated using the TLS PRF function, taking source
+entropy from both client and server. Method 2 is designed to
+closely parallel the key generation process used by TLS 1.0.
+
+Note that in TLS mode, two separate levels
+of keying occur:
+
+(1) The TLS connection is initially negotiated, with both sides
+of the connection producing certificates and verifying the certificate
+(or other authentication info provided) of
+the other side. The
+.B \-\-key-method
+parameter has no effect on this process.
+
+(2) After the TLS connection is established, the tunnel session keys are
+separately negotiated over the existing secure TLS channel. Here,
+.B \-\-key-method
+determines the derivation of the tunnel session keys.
+.\"*********************************************************
+.TP
+.B \-\-tls-cipher l
+A list
+.B l
+of allowable TLS ciphers delimited by a colon (":").
+If you require a high level of security,
+you may want to set this parameter manually, to prevent a
+version rollback attack where a man-in-the-middle attacker tries
+to force two peers to negotiate to the lowest level
+of security they both support.
+Use
+.B \-\-show-tls
+to see a list of supported TLS ciphers.
+.\"*********************************************************
+.TP
+.B \-\-tls-timeout n
+Packet retransmit timeout on TLS control channel
+if no acknowledgment from remote within
+.B n
+seconds (default=2). When OpenVPN sends a control
+packet to its peer, it will expect to receive an
+acknowledgement within
+.B n
+seconds or it will retransmit the packet, subject
+to a TCP-like exponential backoff algorithm. This parameter
+only applies to control channel packets. Data channel
+packets (which carry encrypted tunnel data) are never
+acknowledged, sequenced, or retransmitted by OpenVPN because
+the higher level network protocols running on top of the tunnel
+such as TCP expect this role to be left to them.
+.\"*********************************************************
+.TP
+.B \-\-reneg-bytes n
+Renegotiate data channel key after
+.B n
+bytes sent or received (disabled by default).
+OpenVPN allows the lifetime of a key
+to be expressed as a number of bytes encrypted/decrypted, a number of packets, or
+a number of seconds. A key renegotiation will be forced
+if any of these three criteria are met by either peer.
+.\"*********************************************************
+.TP
+.B \-\-reneg-pkts n
+Renegotiate data channel key after
+.B n
+packets sent and received (disabled by default).
+.\"*********************************************************
+.TP
+.B \-\-reneg-sec n
+Renegotiate data channel key after
+.B n
+seconds (default=3600).
+
+When using dual-factor authentication, note that this default value may
+cause the end user to be challenged to reauthorize once per hour.
+
+Also, keep in mind that this option can be used on both the client and server,
+and whichever uses the lower value will be the one to trigger the renegotiation.
+A common mistake is to set
+.B \-\-reneg-sec
+to a higher value on either the client or server, while the other side of the connection
+is still using the default value of 3600 seconds, meaning that the renegotiation will
+still occur once per 3600 seconds. The solution is to increase \-\-reneg-sec on both the
+client and server, or set it to 0 on one side of the connection (to disable), and to
+your chosen value on the other side.
+.\"*********************************************************
+.TP
+.B \-\-hand-window n
+Handshake Window \-\- the TLS-based key exchange must finalize within
+.B n
+seconds
+of handshake initiation by any peer (default = 60 seconds).
+If the handshake fails
+we will attempt to reset our connection with our peer and try again.
+Even in the event of handshake failure we will still use
+our expiring key for up to
+.B \-\-tran-window
+seconds to maintain continuity of transmission of tunnel
+data.
+.\"*********************************************************
+.TP
+.B \-\-tran-window n
+Transition window \-\- our old key can live this many seconds
+after a new a key renegotiation begins (default = 3600 seconds).
+This feature allows for a graceful transition from old to new
+key, and removes the key renegotiation sequence from the critical
+path of tunnel data forwarding.
+.\"*********************************************************
+.TP
+.B \-\-single-session
+After initially connecting to a remote peer, disallow any new connections.
+Using this
+option means that a remote peer cannot connect, disconnect, and then
+reconnect.
+
+If the daemon is reset by a signal or
+.B \-\-ping-restart,
+it will allow one new connection.
+
+.B \-\-single-session
+can be used with
+.B \-\-ping-exit
+or
+.B \-\-inactive
+to create a single dynamic session that will exit when finished.
+.\"*********************************************************
+.TP
+.B \-\-tls-exit
+Exit on TLS negotiation failure.
+.\"*********************************************************
+.TP
+.B \-\-tls-auth file [direction]
+Add an additional layer of HMAC authentication on top of the TLS
+control channel to protect against DoS attacks.
+
+In a nutshell,
+.B \-\-tls-auth
+enables a kind of "HMAC firewall" on OpenVPN's TCP/UDP port,
+where TLS control channel packets
+bearing an incorrect HMAC signature can be dropped immediately without
+response.
+
+.B file
+(required) is a key file which can be in one of two formats:
+
+.B (1)
+An OpenVPN static key file generated by
+.B \-\-genkey
+(required if
+.B direction
+parameter is used).
+
+.B (2)
+A freeform passphrase file. In this case the HMAC key will
+be derived by taking a secure hash of this file, similar to
+the
+.BR md5sum (1)
+or
+.BR sha1sum (1)
+commands.
+
+OpenVPN will first try format (1), and if the file fails to parse as
+a static key file, format (2) will be used.
+
+See the
+.B \-\-secret
+option for more information on the optional
+.B direction
+parameter.
+
+.B \-\-tls-auth
+is recommended when you are running OpenVPN in a mode where
+it is listening for packets from any IP address, such as when
+.B \-\-remote
+is not specified, or
+.B \-\-remote
+is specified with
+.B \-\-float.
+
+The rationale for
+this feature is as follows. TLS requires a multi-packet exchange
+before it is able to authenticate a peer. During this time
+before authentication, OpenVPN is allocating resources (memory
+and CPU) to this potential peer. The potential peer is also
+exposing many parts of OpenVPN and the OpenSSL library to the packets
+it is sending. Most successful network attacks today seek
+to either exploit bugs in programs (such as buffer overflow attacks) or
+force a program to consume so many resources that it becomes unusable.
+Of course the first line of defense is always to produce clean,
+well-audited code. OpenVPN has been written with buffer overflow
+attack prevention as a top priority.
+But as history has shown, many of the most widely used
+network applications have, from time to time,
+fallen to buffer overflow attacks.
+
+So as a second line of defense, OpenVPN offers
+this special layer of authentication on top of the TLS control channel so that
+every packet on the control channel is authenticated by an
+HMAC signature and a unique ID for replay protection.
+This signature will also help protect against DoS (Denial of Service) attacks.
+An important rule of thumb in reducing vulnerability to DoS attacks is to
+minimize the amount of resources a potential, but as yet unauthenticated,
+client is able to consume.
+
+.B \-\-tls-auth
+does this by signing every TLS control channel packet with an HMAC signature,
+including packets which are sent before the TLS level has had a chance
+to authenticate the peer.
+The result is that packets without
+the correct signature can be dropped immediately upon reception,
+before they have a chance to consume additional system resources
+such as by initiating a TLS handshake.
+.B \-\-tls-auth
+can be strengthened by adding the
+.B \-\-replay-persist
+option which will keep OpenVPN's replay protection state
+in a file so that it is not lost across restarts.
+
+It should be emphasized that this feature is optional and that the
+passphrase/key file used with
+.B \-\-tls-auth
+gives a peer nothing more than the power to initiate a TLS
+handshake. It is not used to encrypt or authenticate any tunnel data.
+.\"*********************************************************
+.TP
+.B \-\-askpass [file]
+Get certificate password from console or
+.B file
+before we daemonize.
+
+For the extremely
+security conscious, it is possible to protect your private key with
+a password. Of course this means that every time the OpenVPN
+daemon is started you must be there to type the password. The
+.B \-\-askpass
+option allows you to start OpenVPN from the command line. It will
+query you for a password before it daemonizes. To protect a private
+key with a password you should omit the
+.B -nodes
+option when you use the
+.B openssl
+command line tool to manage certificates and private keys.
+
+If
+.B file
+is specified, read the password from the first line of
+.B file.
+Keep in mind that storing your password in a file
+to a certain extent invalidates the extra security provided by
+using an encrypted key (Note: OpenVPN
+will only read passwords from a file if it has been built
+with the \-\-enable-password-save configure option, or on Windows
+by defining ENABLE_PASSWORD_SAVE in win/settings.in).
+.\"*********************************************************
+.TP
+.B \-\-auth-nocache
+Don't cache
+.B \-\-askpass
+or
+.B \-\-auth-user-pass
+username/passwords in virtual memory.
+
+If specified, this directive will cause OpenVPN to immediately
+forget username/password inputs after they are used. As a result,
+when OpenVPN needs a username/password, it will prompt for input
+from stdin, which may be multiple times during the duration of an
+OpenVPN session.
+
+This directive does not affect the
+.B \-\-http-proxy
+username/password. It is always cached.
+.\"*********************************************************
+.TP
+.B \-\-tls-verify cmd
+Execute shell command
+.B cmd
+to verify the X509 name of a
+pending TLS connection that has otherwise passed all other
+tests of certification (except for revocation via
+.B \-\-crl-verify
+directive; the revocation test occurs after the
+.B \-\-tls-verify
+test).
+
+.B cmd
+should return 0 to allow the TLS handshake to proceed, or 1 to fail.
+
+Note that
+.B cmd
+is a command line and as such may (if enclosed in quotes) contain
+whitespace separated arguments. The first word of
+.B cmd
+is the shell command to execute and the remaining words are its
+arguments.
+When
+.B cmd
+is executed two arguments are appended, as follows:
+
+.B cmd certificate_depth X509_NAME_oneline
+
+These arguments are, respectively, the current certificate depth and
+the X509 common name (cn) of the peer.
+
+This feature is useful if the peer you want to trust has a certificate
+which was signed by a certificate authority who also signed many
+other certificates, where you don't necessarily want to trust all of them,
+but rather be selective about which
+peer certificate you will accept. This feature allows you to write a script
+which will test the X509 name on a certificate and decide whether or
+not it should be accepted. For a simple perl script which will test
+the common name field on the certificate, see the file
+.B verify-cn
+in the OpenVPN distribution.
+
+See the "Environmental Variables" section below for
+additional parameters passed as environmental variables.
+.\"*********************************************************
+.TP
+.B \-\-tls-export-cert directory
+Store the certificates the clients uses upon connection to this
+directory. This will be done before --tls-verify is called. The
+certificates will use a temporary name and will be deleted when
+the tls-verify script returns. The file name used for the certificate
+is available via the peer_cert environment variable.
+.\"*********************************************************
+.TP
+.B \-\-x509-username-field fieldname
+Field in x509 certificate subject to be used as username (default=CN).
+.B Fieldname
+will be uppercased before matching. When this option is used, the
+--tls-remote option will match against the chosen fieldname instead
+of the CN.
+.\"*********************************************************
+.TP
+.B \-\-tls-remote name
+Accept connections only from a host with X509 name
+or common name equal to
+.B name.
+The remote host must also pass all other tests
+of verification.
+
+.B NOTE:
+Because tls-remote may test against a common name prefix,
+only use this option when you are using OpenVPN with a custom CA
+certificate that is under your control.
+Never use this option when your client certificates are signed by
+a third party, such as a commercial web CA.
+
+Name can also be a common name prefix, for example if you
+want a client to only accept connections to "Server-1",
+"Server-2", etc., you can simply use
+.B \-\-tls-remote Server
+
+Using a common name prefix is a useful alternative to managing
+a CRL (Certificate Revocation List) on the client, since it allows the client
+to refuse all certificates except for those associated
+with designated servers.
+
+.B \-\-tls-remote
+is a useful replacement for the
+.B \-\-tls-verify
+option to verify the remote host, because
+.B \-\-tls-remote
+works in a
+.B \-\-chroot
+environment too.
+.\"*********************************************************
+.TP
+.B \-\-ns-cert-type client|server
+Require that peer certificate was signed with an explicit
+.B nsCertType
+designation of "client" or "server".
+
+This is a useful security option for clients, to ensure that
+the host they connect with is a designated server.
+
+See the easy-rsa/build-key-server script for an example
+of how to generate a certificate with the
+.B nsCertType
+field set to "server".
+
+If the server certificate's nsCertType field is set
+to "server", then the clients can verify this with
+.B \-\-ns-cert-type server.
+
+This is an important security precaution to protect against
+a man-in-the-middle attack where an authorized client
+attempts to connect to another client by impersonating the server.
+The attack is easily prevented by having clients verify
+the server certificate using any one of
+.B \-\-ns-cert-type, \-\-tls-remote,
+or
+.B \-\-tls-verify.
+.\"*********************************************************
+.TP
+.B \-\-remote-cert-ku v...
+Require that peer certificate was signed with an explicit
+.B key usage.
+
+This is a useful security option for clients, to ensure that
+the host they connect to is a designated server.
+
+The key usage should be encoded in hex, more than one key
+usage can be specified.
+.\"*********************************************************
+.TP
+.B \-\-remote-cert-eku oid
+Require that peer certificate was signed with an explicit
+.B extended key usage.
+
+This is a useful security option for clients, to ensure that
+the host they connect to is a designated server.
+
+The extended key usage should be encoded in oid notation, or
+OpenSSL symbolic representation.
+.\"*********************************************************
+.TP
+.B \-\-remote-cert-tls client|server
+Require that peer certificate was signed with an explicit
+.B key usage
+and
+.B extended key usage
+based on RFC3280 TLS rules.
+
+This is a useful security option for clients, to ensure that
+the host they connect to is a designated server.
+
+The
+.B \-\-remote-cert-tls client
+option is equivalent to
+.B
+\-\-remote-cert-ku 80 08 88 \-\-remote-cert-eku "TLS Web Client Authentication"
+
+The key usage is digitalSignature and/or keyAgreement.
+
+The
+.B \-\-remote-cert-tls server
+option is equivalent to
+.B
+\-\-remote-cert-ku a0 88 \-\-remote-cert-eku "TLS Web Server Authentication"
+
+The key usage is digitalSignature and ( keyEncipherment or keyAgreement ).
+
+This is an important security precaution to protect against
+a man-in-the-middle attack where an authorized client
+attempts to connect to another client by impersonating the server.
+The attack is easily prevented by having clients verify
+the server certificate using any one of
+.B \-\-remote-cert-tls, \-\-tls-remote,
+or
+.B \-\-tls-verify.
+.\"*********************************************************
+.TP
+.B \-\-crl-verify crl
+Check peer certificate against the file
+.B crl
+in PEM format.
+
+A CRL (certificate revocation list) is used when a particular key is
+compromised but when the overall PKI is still intact.
+
+Suppose you had a PKI consisting of a CA, root certificate, and a number of
+client certificates. Suppose a laptop computer containing a client key and
+certificate was stolen. By adding the stolen certificate to the CRL file,
+you could reject any connection which attempts to use it, while preserving the
+overall integrity of the PKI.
+
+The only time when it would be necessary to rebuild the entire PKI from scratch would be
+if the root certificate key itself was compromised.
+.\"*********************************************************
+.SS SSL Library information:
+.\"*********************************************************
+.TP
+.B \-\-show-ciphers
+(Standalone)
+Show all cipher algorithms to use with the
+.B \-\-cipher
+option.
+.\"*********************************************************
+.TP
+.B \-\-show-digests
+(Standalone)
+Show all message digest algorithms to use with the
+.B \-\-auth
+option.
+.\"*********************************************************
+.TP
+.B \-\-show-tls
+(Standalone)
+Show all TLS ciphers (TLS used only as a control channel). The TLS
+ciphers will be sorted from highest preference (most secure) to
+lowest.
+.\"*********************************************************
+.TP
+.B \-\-show-engines
+(Standalone)
+Show currently available hardware-based crypto acceleration
+engines supported by the OpenSSL library.
+.\"*********************************************************
+.SS Generate a random key:
+Used only for non-TLS static key encryption mode.
+.\"*********************************************************
+.TP
+.B \-\-genkey
+(Standalone)
+Generate a random key to be used as a shared secret,
+for use with the
+.B \-\-secret
+option. This file must be shared with the
+peer over a pre-existing secure channel such as
+.BR scp (1)
+.
+.\"*********************************************************
+.TP
+.B \-\-secret file
+Write key to
+.B file.
+.\"*********************************************************
+.SS TUN/TAP persistent tunnel config mode:
+Available with linux 2.4.7+. These options comprise a standalone mode
+of OpenVPN which can be used to create and delete persistent tunnels.
+.\"*********************************************************
+.TP
+.B \-\-mktun
+(Standalone)
+Create a persistent tunnel on platforms which support them such
+as Linux. Normally TUN/TAP tunnels exist only for
+the period of time that an application has them open. This option
+takes advantage of the TUN/TAP driver's ability to build persistent
+tunnels that live through multiple instantiations of OpenVPN and die
+only when they are deleted or the machine is rebooted.
+
+One of the advantages of persistent tunnels is that they eliminate the
+need for separate
+.B \-\-up
+and
+.B \-\-down
+scripts to run the appropriate
+.BR ifconfig (8)
+and
+.BR route (8)
+commands. These commands can be placed in the the same shell script
+which starts or terminates an OpenVPN session.
+
+Another advantage is that open connections through the TUN/TAP-based tunnel
+will not be reset if the OpenVPN peer restarts. This can be useful to
+provide uninterrupted connectivity through the tunnel in the event of a DHCP
+reset of the peer's public IP address (see the
+.B \-\-ipchange
+option above).
+
+One disadvantage of persistent tunnels is that it is harder to automatically
+configure their MTU value (see
+.B \-\-link-mtu
+and
+.B \-\-tun-mtu
+above).
+
+On some platforms such as Windows, TAP-Win32 tunnels are persistent by
+default.
+.\"*********************************************************
+.TP
+.B \-\-rmtun
+(Standalone)
+Remove a persistent tunnel.
+.\"*********************************************************
+.TP
+.B \-\-dev tunX | tapX
+TUN/TAP device
+.\"*********************************************************
+.TP
+.B \-\-user user
+Optional user to be owner of this tunnel.
+.\"*********************************************************
+.TP
+.B \-\-group group
+Optional group to be owner of this tunnel.
+.\"*********************************************************
+.SS Windows-Specific Options:
+.\"*********************************************************
+.TP
+.B \-\-win-sys path|'env'
+Set the Windows system directory pathname to use when looking for system
+executables such as
+.B route.exe
+and
+.B netsh.exe.
+By default, if this directive is
+not specified, the pathname will be set to "C:\\WINDOWS"
+
+The special string
+.B 'env'
+indicates that the pathname should be read from the
+.B SystemRoot
+environmental variable.
+.\"*********************************************************
+.TP
+.B \-\-ip-win32 method
+When using
+.B \-\-ifconfig
+on Windows, set the TAP-Win32 adapter
+IP address and netmask using
+.B method.
+Don't use this option unless you are also using
+.B \-\-ifconfig.
+
+.B manual \-\-
+Don't set the IP address or netmask automatically.
+Instead output a message
+to the console telling the user to configure the
+adapter manually and indicating the IP/netmask which
+OpenVPN expects the adapter to be set to.
+
+.B dynamic [offset] [lease-time] \-\-
+Automatically set the IP address and netmask by replying to
+DHCP query messages generated by the kernel. This mode is
+probably the "cleanest" solution
+for setting the TCP/IP properties since it uses the well-known
+DHCP protocol. There are, however, two prerequisites for using
+this mode: (1) The TCP/IP properties for the TAP-Win32
+adapter must be set to "Obtain an IP address automatically," and
+(2) OpenVPN needs to claim an IP address in the subnet for use
+as the virtual DHCP server address. By default in
+.B \-\-dev tap
+mode, OpenVPN will
+take the normally unused first address in the subnet. For example,
+if your subnet is 192.168.4.0 netmask 255.255.255.0, then
+OpenVPN will take the IP address 192.168.4.0 to use as the
+virtual DHCP server address. In
+.B \-\-dev tun
+mode, OpenVPN will cause the DHCP server to masquerade as if it were
+coming from the remote endpoint. The optional offset parameter is
+an integer which is > -256 and < 256 and which defaults to 0.
+If offset is positive, the DHCP server will masquerade as the IP
+address at network address + offset.
+If offset is negative, the DHCP server will masquerade as the IP
+address at broadcast address + offset. The Windows
+.B ipconfig /all
+command can be used to show what Windows thinks the DHCP server
+address is. OpenVPN will "claim" this address, so make sure to
+use a free address. Having said that, different OpenVPN instantiations,
+including different ends of the same connection, can share the same
+virtual DHCP server address. The
+.B lease-time
+parameter controls the lease time of the DHCP assignment given to
+the TAP-Win32 adapter, and is denoted in seconds.
+Normally a very long lease time is preferred
+because it prevents routes involving the TAP-Win32 adapter from
+being lost when the system goes to sleep. The default
+lease time is one year.
+
+.B netsh \-\-
+Automatically set the IP address and netmask using
+the Windows command-line "netsh"
+command. This method appears to work correctly on
+Windows XP but not Windows 2000.
+
+.B ipapi \-\-
+Automatically set the IP address and netmask using the
+Windows IP Helper API. This approach
+does not have ideal semantics, though testing has indicated
+that it works okay in practice. If you use this option,
+it is best to leave the TCP/IP properties for the TAP-Win32
+adapter in their default state, i.e. "Obtain an IP address
+automatically."
+
+.B adaptive \-\-
+(Default) Try
+.B dynamic
+method initially and fail over to
+.B netsh
+if the DHCP negotiation with the TAP-Win32 adapter does
+not succeed in 20 seconds. Such failures have been known
+to occur when certain third-party firewall packages installed
+on the client machine block the DHCP negotiation used by
+the TAP-Win32 adapter.
+Note that if the
+.B netsh
+failover occurs, the TAP-Win32 adapter
+TCP/IP properties will be reset from DHCP to static, and this
+will cause future OpenVPN startups using the
+.B adaptive
+mode to use
+.B netsh
+immediately, rather than trying
+.B dynamic
+first. To "unstick" the
+.B adaptive
+mode from using
+.B netsh,
+run OpenVPN at least once using the
+.B dynamic
+mode to restore the TAP-Win32 adapter TCP/IP properties
+to a DHCP configuration.
+.\"*********************************************************
+.TP
+.B \-\-route-method m
+Which method
+.B m
+to use for adding routes on Windows?
+
+.B adaptive
+(default) \-\- Try IP helper API first. If that fails, fall
+back to the route.exe shell command.
+.br
+.B ipapi
+\-\- Use IP helper API.
+.br
+.B exe
+\-\- Call the route.exe shell command.
+.\"*********************************************************
+.TP
+.B \-\-dhcp-option type [parm]
+Set extended TAP-Win32 TCP/IP properties, must
+be used with
+.B \-\-ip-win32 dynamic
+or
+.B \-\-ip-win32 adaptive.
+This option can be used to set additional TCP/IP properties
+on the TAP-Win32 adapter, and is particularly useful for
+configuring an OpenVPN client to access a Samba server
+across the VPN.
+
+.B DOMAIN name \-\-
+Set Connection-specific DNS Suffix.
+
+.B DNS addr \-\-
+Set primary domain name server address. Repeat
+this option to set secondary DNS server addresses.
+
+.B WINS addr \-\-
+Set primary WINS server address (NetBIOS over TCP/IP Name Server).
+Repeat this option to set secondary WINS server addresses.
+
+.B NBDD addr \-\-
+Set primary NBDD server address (NetBIOS over TCP/IP Datagram Distribution Server)
+Repeat this option
+to set secondary NBDD server addresses.
+
+.B NTP addr \-\-
+Set primary NTP server address (Network Time Protocol).
+Repeat this option
+to set secondary NTP server addresses.
+
+.B NBT type \-\-
+Set NetBIOS over TCP/IP Node type. Possible options:
+.B 1
+= b-node (broadcasts),
+.B 2
+= p-node (point-to-point
+name queries to a WINS server),
+.B 4
+= m-node (broadcast
+then query name server), and
+.B 8
+= h-node (query name server, then broadcast).
+
+.B NBS scope-id \-\-
+Set NetBIOS over TCP/IP Scope. A NetBIOS Scope ID provides an extended
+naming service for the NetBIOS over TCP/IP (Known as NBT) module. The
+primary purpose of a NetBIOS scope ID is to isolate NetBIOS traffic on
+a single network to only those nodes with the same NetBIOS scope ID.
+The NetBIOS scope ID is a character string that is appended to the NetBIOS
+name. The NetBIOS scope ID on two hosts must match, or the two hosts
+will not be able to communicate. The NetBIOS Scope ID also allows
+computers to use the same computer name, as they have different
+scope IDs. The Scope ID becomes a part of the NetBIOS name, making the name unique.
+(This description of NetBIOS scopes courtesy of NeonSurge@abyss.com)
+
+.B DISABLE-NBT \-\-
+Disable Netbios-over-TCP/IP.
+
+Note that if
+.B \-\-dhcp-option
+is pushed via
+.B \-\-push
+to a non-windows client, the option will be saved in the client's
+environment before the up script is called, under
+the name "foreign_option_{n}".
+.\"*********************************************************
+.TP
+.B \-\-tap-sleep n
+Cause OpenVPN to sleep for
+.B n
+seconds immediately after the TAP-Win32 adapter state
+is set to "connected".
+
+This option is intended to be used to troubleshoot problems
+with the
+.B \-\-ifconfig
+and
+.B \-\-ip-win32
+options, and is used to give
+the TAP-Win32 adapter time to come up before
+Windows IP Helper API operations are applied to it.
+.\"*********************************************************
+.TP
+.B \-\-show-net-up
+Output OpenVPN's view of the system routing table and network
+adapter list to the syslog or log file after the TUN/TAP adapter
+has been brought up and any routes have been added.
+.\"*********************************************************
+.TP
+.B \-\-dhcp-renew
+Ask Windows to renew the TAP adapter lease on startup.
+This option is normally unnecessary, as Windows automatically
+triggers a DHCP renegotiation on the TAP adapter when it
+comes up, however if you set the TAP-Win32 adapter
+Media Status property to "Always Connected", you may need this
+flag.
+.\"*********************************************************
+.TP
+.B \-\-dhcp-release
+Ask Windows to release the TAP adapter lease on shutdown.
+This option has the same caveats as
+.B \-\-dhcp-renew
+above.
+.\"*********************************************************
+.TP
+.B \-\-register-dns
+Run net stop dnscache, net start dnscache, ipconfig /flushdns
+and ipconfig /registerdns on connection initiation.
+This is known to kick Windows into
+recognizing pushed DNS servers.
+.\"*********************************************************
+.TP
+.B \-\-pause-exit
+Put up a "press any key to continue" message on the console prior
+to OpenVPN program exit. This option is automatically used by the
+Windows explorer when OpenVPN is run on a configuration
+file using the right-click explorer menu.
+.\"*********************************************************
+.TP
+.B \-\-service exit-event [0|1]
+Should be used when OpenVPN is being automatically executed by another
+program in such
+a context that no interaction with the user via display or keyboard
+is possible. In general, end-users should never need to explicitly
+use this option, as it is automatically added by the OpenVPN service wrapper
+when a given OpenVPN configuration is being run as a service.
+
+.B exit-event
+is the name of a Windows global event object, and OpenVPN will continuously
+monitor the state of this event object and exit when it becomes signaled.
+
+The second parameter indicates the initial state of
+.B exit-event
+and normally defaults to 0.
+
+Multiple OpenVPN processes can be simultaneously executed with the same
+.B exit-event
+parameter. In any case, the controlling process can signal
+.B exit-event,
+causing all such OpenVPN processes to exit.
+
+When executing an OpenVPN process using the
+.B \-\-service
+directive, OpenVPN will probably not have a console
+window to output status/error
+messages, therefore it is useful to use
+.B \-\-log
+or
+.B \-\-log-append
+to write these messages to a file.
+.\"*********************************************************
+.TP
+.B \-\-show-adapters
+(Standalone)
+Show available TAP-Win32 adapters which can be selected using the
+.B \-\-dev-node
+option. On non-Windows systems, the
+.BR ifconfig (8)
+command provides similar functionality.
+.\"*********************************************************
+.TP
+.B \-\-allow-nonadmin [TAP-adapter]
+(Standalone)
+Set
+.B TAP-adapter
+to allow access from non-administrative accounts. If
+.B TAP-adapter
+is omitted, all TAP adapters on the system will be configured to allow
+non-admin access.
+The non-admin access setting will only persist for the length of time that
+the TAP-Win32 device object and driver remain loaded, and will need
+to be re-enabled after a reboot, or if the driver is unloaded
+and reloaded.
+This directive can only be used by an administrator.
+.\"*********************************************************
+.TP
+.B \-\-show-valid-subnets
+(Standalone)
+Show valid subnets for
+.B \-\-dev tun
+emulation. Since the TAP-Win32 driver
+exports an ethernet interface to Windows, and since TUN devices are
+point-to-point in nature, it is necessary for the TAP-Win32 driver
+to impose certain constraints on TUN endpoint address selection.
+
+Namely, the point-to-point endpoints used in TUN device emulation
+must be the middle two addresses of a /30 subnet (netmask 255.255.255.252).
+.\"*********************************************************
+.TP
+.B \-\-show-net
+(Standalone)
+Show OpenVPN's view of the system routing table and network
+adapter list.
+.\"*********************************************************
+.SS PKCS#11 Standalone Options:
+.\"*********************************************************
+.TP
+.B \-\-show-pkcs11-ids provider [cert_private]
+(Standalone)
+Show PKCS#11 token object list. Specify cert_private as 1
+if certificates are stored as private objects.
+
+.B \-\-verb
+option can be used BEFORE this option to produce debugging information.
+.\"*********************************************************
+.SH SCRIPTING AND ENVIRONMENTAL VARIABLES
+OpenVPN exports a series
+of environmental variables for use by user-defined scripts.
+.\"*********************************************************
+.SS Script Order of Execution
+.\"*********************************************************
+.TP
+.B \-\-up
+Executed after TCP/UDP socket bind and TUN/TAP open.
+.\"*********************************************************
+.TP
+.B \-\-tls-verify
+Executed when we have a still untrusted remote peer.
+.\"*********************************************************
+.TP
+.B \-\-ipchange
+Executed after connection authentication, or remote IP address change.
+.\"*********************************************************
+.TP
+.B \-\-client-connect
+Executed in
+.B \-\-mode server
+mode immediately after client authentication.
+.\"*********************************************************
+.TP
+.B \-\-route-up
+Executed after connection authentication, either
+immediately after, or some number of seconds after
+as defined by the
+.B \-\-route-delay
+option.
+.\"*********************************************************
+.TP
+.B \-\-client-disconnect
+Executed in
+.B \-\-mode server
+mode on client instance shutdown.
+.\"*********************************************************
+.TP
+.B \-\-down
+Executed after TCP/UDP and TUN/TAP close.
+.\"*********************************************************
+.TP
+.B \-\-learn-address
+Executed in
+.B \-\-mode server
+mode whenever an IPv4 address/route or MAC address is added to OpenVPN's
+internal routing table.
+.\"*********************************************************
+.TP
+.B \-\-auth-user-pass-verify
+Executed in
+.B \-\-mode server
+mode on new client connections, when the client is
+still untrusted.
+.\"*********************************************************
+.SS String Types and Remapping
+In certain cases, OpenVPN will perform remapping of characters
+in strings. Essentially, any characters outside the set of
+permitted characters for each string type will be converted
+to underbar ('_').
+
+.B Q:
+Why is string remapping necessary?
+
+.B A:
+It's an important security feature to prevent the malicious coding of
+strings from untrusted sources to be passed as parameters to scripts,
+saved in the environment, used as a common name, translated to a filename,
+etc.
+
+.B Q:
+Can string remapping be disabled?
+
+.B A:
+Yes, by using the
+.B \-\-no-name-remapping
+option, however this should be considered an advanced option.
+
+Here is a brief rundown of OpenVPN's current string types and the
+permitted character class for each string:
+
+.B X509 Names:
+Alphanumeric, underbar ('_'), dash ('-'), dot ('.'), at
+('@'), colon (':'), slash ('/'), and equal ('='). Alphanumeric is defined
+as a character which will cause the C library isalnum() function to return
+true.
+
+.B Common Names:
+Alphanumeric, underbar ('_'), dash ('-'), dot ('.'), and at
+('@').
+
+.B \-\-auth-user-pass username:
+Same as Common Name, with one exception: starting with OpenVPN 2.0.1,
+the username is passed to the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY plugin in its raw form,
+without string remapping.
+
+.B \-\-auth-user-pass password:
+Any "printable" character except CR or LF.
+Printable is defined to be a character which will cause the C library
+isprint() function to return true.
+
+.B \-\-client-config-dir filename as derived from common name or username:
+Alphanumeric, underbar ('_'), dash ('-'), and dot ('.') except for "." or
+".." as standalone strings. As of 2.0.1-rc6, the at ('@') character has
+been added as well for compatibility with the common name character class.
+
+.B Environmental variable names:
+Alphanumeric or underbar ('_').
+
+.B Environmental variable values:
+Any printable character.
+
+For all cases, characters in a string which are not members of the legal
+character class for that string type will be remapped to underbar ('_').
+.\"*********************************************************
+.SS Environmental Variables
+Once set, a variable is persisted
+indefinitely until it is reset by a new value or a restart,
+
+As of OpenVPN 2.0-beta12, in server mode, environmental
+variables set by OpenVPN
+are scoped according to the client objects
+they are
+associated with, so there should not be any issues with
+scripts having access to stale, previously set variables
+which refer to different client instances.
+.\"*********************************************************
+.TP
+.B bytes_received
+Total number of bytes received from client during VPN session.
+Set prior to execution of the
+.B \-\-client-disconnect
+script.
+.\"*********************************************************
+.TP
+.B bytes_sent
+Total number of bytes sent to client during VPN session.
+Set prior to execution of the
+.B \-\-client-disconnect
+script.
+.\"*********************************************************
+.TP
+.B common_name
+The X509 common name of an authenticated client.
+Set prior to execution of
+.B \-\-client-connect, \-\-client-disconnect,
+and
+.B \-\-auth-user-pass-verify
+scripts.
+.\"*********************************************************
+.TP
+.B config
+Name of first
+.B \-\-config
+file.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B daemon
+Set to "1" if the
+.B \-\-daemon
+directive is specified, or "0" otherwise.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B daemon_log_redirect
+Set to "1" if the
+.B \-\-log
+or
+.B \-\-log-append
+directives are specified, or "0" otherwise.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B dev
+The actual name of the TUN/TAP device, including
+a unit number if it exists.
+Set prior to
+.B \-\-up
+or
+.B \-\-down
+script execution.
+.\"*********************************************************
+.TP
+.B foreign_option_{n}
+An option pushed via
+.B \-\-push
+to a client which does not natively support it,
+such as
+.B \-\-dhcp-option
+on a non-Windows system, will be recorded to this
+environmental variable sequence prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B ifconfig_broadcast
+The broadcast address for the virtual
+ethernet segment which is derived from the
+.B \-\-ifconfig
+option when
+.B \-\-dev tap
+is used.
+Set prior to OpenVPN calling the
+.I ifconfig
+or
+.I netsh
+(windows version of ifconfig) commands which
+normally occurs prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B ifconfig_local
+The local VPN endpoint IP address specified in the
+.B \-\-ifconfig
+option (first parameter).
+Set prior to OpenVPN calling the
+.I ifconfig
+or
+.I netsh
+(windows version of ifconfig) commands which
+normally occurs prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B ifconfig_remote
+The remote VPN endpoint IP address specified in the
+.B \-\-ifconfig
+option (second parameter) when
+.B \-\-dev tun
+is used.
+Set prior to OpenVPN calling the
+.I ifconfig
+or
+.I netsh
+(windows version of ifconfig) commands which
+normally occurs prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B ifconfig_netmask
+The subnet mask of the virtual ethernet segment
+that is specified as the second parameter to
+.B \-\-ifconfig
+when
+.B \-\-dev tap
+is being used.
+Set prior to OpenVPN calling the
+.I ifconfig
+or
+.I netsh
+(windows version of ifconfig) commands which
+normally occurs prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B ifconfig_pool_local_ip
+The local
+virtual IP address for the TUN/TAP tunnel taken from an
+.B \-\-ifconfig-push
+directive if specified, or otherwise from
+the ifconfig pool (controlled by the
+.B \-\-ifconfig-pool
+config file directive).
+Only set for
+.B \-\-dev tun
+tunnels.
+This option is set on the server prior to execution
+of the
+.B \-\-client-connect
+and
+.B \-\-client-disconnect
+scripts.
+.\"*********************************************************
+.TP
+.B ifconfig_pool_netmask
+The
+virtual IP netmask for the TUN/TAP tunnel taken from an
+.B \-\-ifconfig-push
+directive if specified, or otherwise from
+the ifconfig pool (controlled by the
+.B \-\-ifconfig-pool
+config file directive).
+Only set for
+.B \-\-dev tap
+tunnels.
+This option is set on the server prior to execution
+of the
+.B \-\-client-connect
+and
+.B \-\-client-disconnect
+scripts.
+.\"*********************************************************
+.TP
+.B ifconfig_pool_remote_ip
+The remote
+virtual IP address for the TUN/TAP tunnel taken from an
+.B \-\-ifconfig-push
+directive if specified, or otherwise from
+the ifconfig pool (controlled by the
+.B \-\-ifconfig-pool
+config file directive).
+This option is set on the server prior to execution
+of the
+.B \-\-client-connect
+and
+.B \-\-client-disconnect
+scripts.
+.\"*********************************************************
+.TP
+.B link_mtu
+The maximum packet size (not including the IP header)
+of tunnel data in UDP tunnel transport mode.
+Set prior to
+.B \-\-up
+or
+.B \-\-down
+script execution.
+.\"*********************************************************
+.TP
+.B local
+The
+.B \-\-local
+parameter.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B local_port
+The local port number, specified by
+.B \-\-port
+or
+.B \-\-lport.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B password
+The password provided by a connecting client.
+Set prior to
+.B \-\-auth-user-pass-verify
+script execution only when the
+.B via-env
+modifier is specified, and deleted from the environment
+after the script returns.
+.\"*********************************************************
+.TP
+.B proto
+The
+.B \-\-proto
+parameter.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B remote_{n}
+The
+.B \-\-remote
+parameter.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B remote_port_{n}
+The remote port number, specified by
+.B \-\-port
+or
+.B \-\-rport.
+Set on program initiation and reset on SIGHUP.
+.\"*********************************************************
+.TP
+.B route_net_gateway
+The pre-existing default IP gateway in the system routing
+table.
+Set prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B route_vpn_gateway
+The default gateway used by
+.B \-\-route
+options, as specified in either the
+.B \-\-route-gateway
+option or the second parameter to
+.B \-\-ifconfig
+when
+.B \-\-dev tun
+is specified.
+Set prior to
+.B \-\-up
+script execution.
+.\"*********************************************************
+.TP
+.B route_{parm}_{n}
+A set of variables which define each route to be added, and
+are set prior to
+.B \-\-up
+script execution.
+
+.B parm
+will be one of "network", "netmask", "gateway", or "metric".
+
+.B n
+is the OpenVPN route number, starting from 1.
+
+If the network or gateway are resolvable DNS names,
+their IP address translations will be recorded rather
+than their names as denoted on the command line
+or configuration file.
+.\"*********************************************************
+.TP
+.B peer_cert
+Temporary file name containing the client certificate upon
+connection. Useful in conjunction with --tls-verify
+.\"*********************************************************
+.TP
+.B script_context
+Set to "init" or "restart" prior to up/down script execution.
+For more information, see
+documentation for
+.B \-\-up.
+.\"*********************************************************
+.TP
+.B script_type
+Prior to execution of any script, this variable is set to the type of
+script being run. It can be one of the following:
+.B up, down, ipchange, route-up, tls-verify, auth-user-pass-verify,
+.B client-connect, client-disconnect,
+or
+.B learn-address.
+.\"*********************************************************
+.TP
+.B signal
+The reason for exit or restart. Can be one of
+.B sigusr1, sighup, sigterm, sigint, inactive
+(controlled by
+.B \-\-inactive
+option),
+.B ping-exit
+(controlled by
+.B \-\-ping-exit
+option),
+.B ping-restart
+(controlled by
+.B \-\-ping-restart
+option),
+.B connection-reset
+(triggered on TCP connection reset),
+.B error,
+or
+.B unknown
+(unknown signal). This variable is set just prior to down script execution.
+.\"*********************************************************
+.TP
+.B time_ascii
+Client connection timestamp, formatted as a human-readable
+time string.
+Set prior to execution of the
+.B \-\-client-connect
+script.
+.\"*********************************************************
+.TP
+.B time_duration
+The duration (in seconds) of the client session which is now
+disconnecting.
+Set prior to execution of the
+.B \-\-client-disconnect
+script.
+.\"*********************************************************
+.TP
+.B time_unix
+Client connection timestamp, formatted as a unix integer
+date/time value.
+Set prior to execution of the
+.B \-\-client-connect
+script.
+.\"*********************************************************
+.TP
+.B tls_id_{n}
+A series of certificate fields from the remote peer,
+where
+.B n
+is the verification level. Only set for TLS connections. Set prior
+to execution of
+.B \-\-tls-verify
+script.
+.\"*********************************************************
+.TP
+.B tls_serial_{n}
+The serial number of the certificate from the remote peer,
+where
+.B n
+is the verification level. Only set for TLS connections. Set prior
+to execution of
+.B \-\-tls-verify
+script. This is in the form of a hex string like "37AB46E0", which is
+suitable for doing serial-based OCSP queries (with OpenSSL, you have
+to prepend "0x" to the string). If something goes wrong while reading
+the value from the certificate it will be an empty string, so your
+code should check that.
+See the contrib/OCSP_check/OCSP_check.sh script for an example.
+.\"*********************************************************
+.TP
+.B tun_mtu
+The MTU of the TUN/TAP device.
+Set prior to
+.B \-\-up
+or
+.B \-\-down
+script execution.
+.\"*********************************************************
+.TP
+.B trusted_ip
+Actual IP address of connecting client or peer which has been authenticated.
+Set prior to execution of
+.B \-\-ipchange, \-\-client-connect,
+and
+.B \-\-client-disconnect
+scripts.
+.\"*********************************************************
+.TP
+.B trusted_port
+Actual port number of connecting client or peer which has been authenticated.
+Set prior to execution of
+.B \-\-ipchange, \-\-client-connect,
+and
+.B \-\-client-disconnect
+scripts.
+.\"*********************************************************
+.TP
+.B untrusted_ip
+Actual IP address of connecting client or peer which has not been authenticated
+yet. Sometimes used to
+.B nmap
+the connecting host in a
+.B \-\-tls-verify
+script to ensure it is firewalled properly.
+Set prior to execution of
+.B \-\-tls-verify
+and
+.B \-\-auth-user-pass-verify
+scripts.
+.\"*********************************************************
+.TP
+.B untrusted_port
+Actual port number of connecting client or peer which has not been authenticated
+yet.
+Set prior to execution of
+.B \-\-tls-verify
+and
+.B \-\-auth-user-pass-verify
+scripts.
+.\"*********************************************************
+.TP
+.B username
+The username provided by a connecting client.
+Set prior to
+.B \-\-auth-user-pass-verify
+script execution only when the
+.B via-env
+modifier is specified.
+.\"*********************************************************
+.TP
+.B X509_{n}_{subject_field}
+An X509 subject field from the remote peer certificate,
+where
+.B n
+is the verification level. Only set for TLS connections. Set prior
+to execution of
+.B \-\-tls-verify
+script. This variable is similar to
+.B tls_id_{n}
+except the component X509 subject fields are broken out, and
+no string remapping occurs on these field values (except for remapping
+of control characters to "_").
+For example, the following variables would be set on the
+OpenVPN server using the sample client certificate
+in sample-keys (client.crt).
+Note that the verification level is 0 for the client certificate
+and 1 for the CA certificate.
+
+.nf
+.ft 3
+.in +4
+X509_0_emailAddress=me@myhost.mydomain
+X509_0_CN=Test-Client
+X509_0_O=OpenVPN-TEST
+X509_0_ST=NA
+X509_0_C=KG
+X509_1_emailAddress=me@myhost.mydomain
+X509_1_O=OpenVPN-TEST
+X509_1_L=BISHKEK
+X509_1_ST=NA
+X509_1_C=KG
+.in -4
+.ft
+.fi
+.\"*********************************************************
+.SH SIGNALS
+.TP
+.B SIGHUP
+Cause OpenVPN to close all TUN/TAP and
+network connections,
+restart, re-read the configuration file (if any),
+and reopen TUN/TAP and network connections.
+.\"*********************************************************
+.TP
+.B SIGUSR1
+Like
+.B SIGHUP,
+except don't re-read configuration file, and possibly don't close and reopen TUN/TAP
+device, re-read key files, preserve local IP address/port, or preserve most recently authenticated
+remote IP address/port based on
+.B \-\-persist-tun, \-\-persist-key, \-\-persist-local-ip,
+and
+.B \-\-persist-remote-ip
+options respectively (see above).
+
+This signal may also be internally generated by a timeout condition, governed
+by the
+.B \-\-ping-restart
+option.
+
+This signal, when combined with
+.B \-\-persist-remote-ip,
+may be
+sent when the underlying parameters of the host's network interface change
+such as when the host is a DHCP client and is assigned a new IP address.
+See
+.B \-\-ipchange
+above for more information.
+.\"*********************************************************
+.TP
+.B SIGUSR2
+Causes OpenVPN to display its current statistics (to the syslog
+file if
+.B \-\-daemon
+is used, or stdout otherwise).
+.\"*********************************************************
+.TP
+.B SIGINT, SIGTERM
+Causes OpenVPN to exit gracefully.
+.\"*********************************************************
+.SH TUN/TAP DRIVER SETUP
+If you are running Linux 2.4.7 or higher, you probably have the TUN/TAP driver
+already installed. If so, there are still a few things you need to do:
+
+Make device:
+.B mknod /dev/net/tun c 10 200
+
+Load driver:
+.B modprobe tun
+.\"*********************************************************
+.SH EXAMPLES
+Prior to running these examples, you should have OpenVPN installed on two
+machines with network connectivity between them. If you have not
+yet installed OpenVPN, consult the INSTALL file included in the OpenVPN
+distribution.
+.\"*********************************************************
+.SS TUN/TAP Setup:
+If you are using Linux 2.4 or higher,
+make the tun device node and load the tun module:
+.IP
+.B mknod /dev/net/tun c 10 200
+.LP
+.IP
+.B modprobe tun
+.LP
+If you installed from RPM, the
+.B mknod
+step may be omitted, because the RPM install does that for you.
+
+Only Linux 2.4 and newer are supported.
+
+For other platforms, consult the INSTALL file at
+.I http://openvpn.net/install.html
+for more information.
+.\"*********************************************************
+.SS Firewall Setup:
+If firewalls exist between
+the two machines, they should be set to forward UDP port 1194
+in both directions. If you do not have control over the firewalls
+between the two machines, you may still be able to use OpenVPN by adding
+.B \-\-ping 15
+to each of the
+.B openvpn
+commands used below in the examples (this will cause each peer to send out
+a UDP ping to its remote peer once every 15 seconds which will cause many
+stateful firewalls to forward packets in both directions
+without an explicit firewall rule).
+
+If you are using a Linux iptables-based firewall, you may need to enter
+the following command to allow incoming packets on the TUN device:
+.IP
+.B iptables -A INPUT -i tun+ -j ACCEPT
+.LP
+See the firewalls section below for more information on configuring firewalls
+for use with OpenVPN.
+.\"*********************************************************
+.SS VPN Address Setup:
+For purposes
+of our example, our two machines will be called
+.B may.kg
+and
+.B june.kg.
+If you are constructing a VPN over the internet, then replace
+.B may.kg
+and
+.B june.kg
+with the internet hostname or IP address that each machine will use
+to contact the other over the internet.
+
+Now we will choose the tunnel endpoints. Tunnel endpoints are
+private IP addresses that only have meaning in the context of
+the VPN. Each machine will use the tunnel endpoint of the other
+machine to access it over the VPN. In our example,
+the tunnel endpoint for may.kg
+will be 10.4.0.1 and for june.kg, 10.4.0.2.
+
+Once the VPN is established, you have essentially
+created a secure alternate path between the two hosts
+which is addressed by using the tunnel endpoints. You can
+control which network
+traffic passes between the hosts
+(a) over the VPN or (b) independently of the VPN, by choosing whether to use
+(a) the VPN endpoint address or (b) the public internet address,
+to access the remote host. For example if you are on may.kg and you wish to connect to june.kg
+via
+.B ssh
+without using the VPN (since
+.B ssh
+has its own built-in security) you would use the command
+.B ssh june.kg.
+However in the same scenario, you could also use the command
+.B telnet 10.4.0.2
+to create a telnet session with june.kg over the VPN, that would
+use the VPN to secure the session rather than
+.B ssh.
+
+You can use any address you wish for the
+tunnel endpoints
+but make sure that they are private addresses
+(such as those that begin with 10 or 192.168) and that they are
+not part of any existing subnet on the networks of
+either peer, unless you are bridging. If you use an address that is part of
+your local subnet for either of the tunnel endpoints,
+you will get a weird feedback loop.
+.\"*********************************************************
+.SS Example 1: A simple tunnel without security
+.LP
+On may:
+.IP
+.B openvpn \-\-remote june.kg \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 9
+.LP
+On june:
+.IP
+.B openvpn \-\-remote may.kg \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 9
+.LP
+Now verify the tunnel is working by pinging across the tunnel.
+.LP
+On may:
+.IP
+.B ping 10.4.0.2
+.LP
+On june:
+.IP
+.B ping 10.4.0.1
+.LP
+The
+.B \-\-verb 9
+option will produce verbose output, similar to the
+.BR tcpdump (8)
+program. Omit the
+.B \-\-verb 9
+option to have OpenVPN run quietly.
+.\"*********************************************************
+.SS Example 2: A tunnel with static-key security (i.e. using a pre-shared secret)
+First build a static key on may.
+.IP
+.B openvpn \-\-genkey \-\-secret key
+.LP
+This command will build a random key file called
+.B key
+(in ascii format).
+Now copy
+.B key
+to june over a secure medium such as by
+using the
+.BR scp (1)
+program.
+.LP
+On may:
+.IP
+.B openvpn \-\-remote june.kg \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-verb 5 \-\-secret key
+.LP
+On june:
+.IP
+.B openvpn \-\-remote may.kg \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-verb 5 \-\-secret key
+.LP
+Now verify the tunnel is working by pinging across the tunnel.
+.LP
+On may:
+.IP
+.B ping 10.4.0.2
+.LP
+On june:
+.IP
+.B ping 10.4.0.1
+.\"*********************************************************
+.SS Example 3: A tunnel with full TLS-based security
+For this test, we will designate
+.B may
+as the TLS client and
+.B june
+as the TLS server.
+.I Note that client or server designation only has meaning for the TLS subsystem. It has no bearing on OpenVPN's peer-to-peer, UDP-based communication model.
+
+First, build a separate certificate/key pair
+for both may and june (see above where
+.B \-\-cert
+is discussed for more info). Then construct
+Diffie Hellman parameters (see above where
+.B \-\-dh
+is discussed for more info). You can also use the
+included test files client.crt, client.key,
+server.crt, server.key and ca.crt.
+The .crt files are certificates/public-keys, the .key
+files are private keys, and ca.crt is a certification
+authority who has signed both
+client.crt and server.crt. For Diffie Hellman
+parameters you can use the included file dh1024.pem.
+.I Note that all client, server, and certificate authority certificates and keys included in the OpenVPN distribution are totally insecure and should be used for testing only.
+.LP
+On may:
+.IP
+.B openvpn \-\-remote june.kg \-\-dev tun1 \-\-ifconfig 10.4.0.1 10.4.0.2 \-\-tls-client \-\-ca ca.crt \-\-cert client.crt \-\-key client.key \-\-reneg-sec 60 \-\-verb 5
+.LP
+On june:
+.IP
+.B openvpn \-\-remote may.kg \-\-dev tun1 \-\-ifconfig 10.4.0.2 10.4.0.1 \-\-tls-server \-\-dh dh1024.pem \-\-ca ca.crt \-\-cert server.crt \-\-key server.key \-\-reneg-sec 60 \-\-verb 5
+.LP
+Now verify the tunnel is working by pinging across the tunnel.
+.LP
+On may:
+.IP
+.B ping 10.4.0.2
+.LP
+On june:
+.IP
+.B ping 10.4.0.1
+.LP
+Notice the
+.B \-\-reneg-sec 60
+option we used above. That tells OpenVPN to renegotiate
+the data channel keys every minute.
+Since we used
+.B \-\-verb 5
+above, you will see status information on each new key negotiation.
+
+For production operations, a key renegotiation interval of 60 seconds
+is probably too frequent. Omit the
+.B \-\-reneg-sec 60
+option to use OpenVPN's default key renegotiation interval of one hour.
+.\"*********************************************************
+.SS Routing:
+Assuming you can ping across the tunnel,
+the next step is to route a real subnet over
+the secure tunnel. Suppose that may and june have two network
+interfaces each, one connected
+to the internet, and the other to a private
+network. Our goal is to securely connect
+both private networks. We will assume that may's private subnet
+is 10.0.0.0/24 and june's is 10.0.1.0/24.
+.LP
+First, ensure that IP forwarding is enabled on both peers.
+On Linux, enable routing:
+.IP
+.B echo 1 > /proc/sys/net/ipv4/ip_forward
+.LP
+and enable TUN packet forwarding through the firewall:
+.IP
+.B iptables -A FORWARD -i tun+ -j ACCEPT
+.LP
+On may:
+.IP
+.B route add -net 10.0.1.0 netmask 255.255.255.0 gw 10.4.0.2
+.LP
+On june:
+.IP
+.B route add -net 10.0.0.0 netmask 255.255.255.0 gw 10.4.0.1
+.LP
+Now any machine on the 10.0.0.0/24 subnet can
+access any machine on the 10.0.1.0/24 subnet
+over the secure tunnel (or vice versa).
+
+In a production environment, you could put the route command(s)
+in a shell script and execute with the
+.B \-\-up
+option.
+.\"*********************************************************
+.SH FIREWALLS
+OpenVPN's usage of a single UDP port makes it fairly firewall-friendly.
+You should add an entry to your firewall rules to allow incoming OpenVPN
+packets. On Linux 2.4+:
+.IP
+.B iptables -A INPUT -p udp -s 1.2.3.4 \-\-dport 1194 -j ACCEPT
+.LP
+This will allow incoming packets on UDP port 1194 (OpenVPN's default UDP port)
+from an OpenVPN peer at 1.2.3.4.
+
+If you are using HMAC-based packet authentication (the default in any of
+OpenVPN's secure modes), having the firewall filter on source
+address can be considered optional, since HMAC packet authentication
+is a much more secure method of verifying the authenticity of
+a packet source. In that case:
+.IP
+.B iptables -A INPUT -p udp \-\-dport 1194 -j ACCEPT
+.LP
+would be adequate and would not render the host inflexible with
+respect to its peer having a dynamic IP address.
+
+OpenVPN also works well on stateful firewalls. In some cases, you may
+not need to add any static rules to the firewall list if you are
+using a stateful firewall that knows how to track UDP connections.
+If you specify
+.B \-\-ping n,
+OpenVPN will be guaranteed
+to send a packet to its peer at least once every
+.B n
+seconds. If
+.B n
+is less than the stateful firewall connection timeout, you can
+maintain an OpenVPN connection indefinitely without explicit
+firewall rules.
+
+You should also add firewall rules to allow incoming IP traffic on
+TUN or TAP devices such as:
+.IP
+.B iptables -A INPUT -i tun+ -j ACCEPT
+.LP
+to allow input packets from tun devices,
+.IP
+.B iptables -A FORWARD -i tun+ -j ACCEPT
+.LP
+to allow input packets from tun devices to be forwarded to
+other hosts on the local network,
+.IP
+.B iptables -A INPUT -i tap+ -j ACCEPT
+.LP
+to allow input packets from tap devices, and
+.IP
+.B iptables -A FORWARD -i tap+ -j ACCEPT
+.LP
+to allow input packets from tap devices to be forwarded to
+other hosts on the local network.
+
+These rules are secure if you use packet authentication,
+since no incoming packets will arrive on a TUN or TAP
+virtual device
+unless they first pass an HMAC authentication test.
+.\"*********************************************************
+.SH FAQ
+.I http://openvpn.net/faq.html
+.\"*********************************************************
+.SH HOWTO
+For a more comprehensive guide to setting up OpenVPN
+in a production setting, see the OpenVPN HOWTO at
+.I http://openvpn.net/howto.html
+.\"*********************************************************
+.SH PROTOCOL
+For a description of OpenVPN's underlying protocol,
+see
+.I http://openvpn.net/security.html
+.\"*********************************************************
+.SH WEB
+OpenVPN's web site is at
+.I http://openvpn.net/
+
+Go here to download the latest version of OpenVPN, subscribe
+to the mailing lists, read the mailing list
+archives, or browse the SVN repository.
+.\"*********************************************************
+.SH BUGS
+Report all bugs to the OpenVPN team <info@openvpn.net>.
+.\"*********************************************************
+.SH "SEE ALSO"
+.BR dhcpcd (8),
+.BR ifconfig (8),
+.BR openssl (1),
+.BR route (8),
+.BR scp (1)
+.BR ssh (1)
+.\"*********************************************************
+.SH NOTES
+.LP
+This product includes software developed by the
+OpenSSL Project (
+.I http://www.openssl.org/
+)
+
+For more information on the TLS protocol, see
+.I http://www.ietf.org/rfc/rfc2246.txt
+
+For more information on the LZO real-time compression library see
+.I http://www.oberhumer.com/opensource/lzo/
+.\"*********************************************************
+.SH COPYRIGHT
+Copyright (C) 2002-2010 OpenVPN Technologies, 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.
+.\"*********************************************************
+.SH AUTHORS
+James Yonan <jim@yonan.net>
diff --git a/openvpn.c b/openvpn.c
new file mode 100644
index 0000000..99b343b
--- /dev/null
+++ b/openvpn.c
@@ -0,0 +1,250 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "init.h"
+#include "forward.h"
+#include "multi.h"
+#include "win32.h"
+
+#include "memdbg.h"
+
+#include "forward-inline.h"
+
+#define P2P_CHECK_SIG() EVENT_LOOP_CHECK_SIGNAL (c, process_signal_p2p, c);
+
+static bool
+process_signal_p2p (struct context *c)
+{
+ remap_signal (c);
+ return process_signal (c);
+}
+
+static void
+tunnel_point_to_point (struct context *c)
+{
+ context_clear_2 (c);
+
+ /* set point-to-point mode */
+ c->mode = CM_P2P;
+
+ /* initialize tunnel instance */
+ init_instance_handle_signals (c, c->es, CC_HARD_USR1_TO_HUP);
+ if (IS_SIG (c))
+ return;
+
+ /* main event loop */
+ while (true)
+ {
+ perf_push (PERF_EVENT_LOOP);
+
+ /* process timers, TLS, etc. */
+ pre_select (c);
+ P2P_CHECK_SIG();
+
+ /* set up and do the I/O wait */
+ io_wait (c, p2p_iow_flags (c));
+ P2P_CHECK_SIG();
+
+ /* timeout? */
+ if (c->c2.event_set_status == ES_TIMEOUT)
+ {
+ perf_pop ();
+ continue;
+ }
+
+ /* process the I/O which triggered select */
+ process_io (c);
+ P2P_CHECK_SIG();
+
+ perf_pop ();
+ }
+
+ uninit_management_callback ();
+
+ /* tear down tunnel instance (unless --persist-tun) */
+ close_instance (c);
+}
+
+#undef PROCESS_SIGNAL_P2P
+
+int
+main (int argc, char *argv[])
+{
+ struct context c;
+
+#if PEDANTIC
+ fprintf (stderr, "Sorry, I was built with --enable-pedantic and I am incapable of doing any real work!\n");
+ return 1;
+#endif
+
+ CLEAR (c);
+
+ /* signify first time for components which can
+ only be initialized once per program instantiation. */
+ c.first_time = true;
+
+ /* initialize program-wide statics */
+ if (init_static ())
+ {
+ /*
+ * This loop is initially executed on startup and then
+ * once per SIGHUP.
+ */
+ do
+ {
+ /* enter pre-initialization mode with regard to signal handling */
+ pre_init_signal_catch ();
+
+ /* zero context struct but leave first_time member alone */
+ context_clear_all_except_first_time (&c);
+
+ /* static signal info object */
+ CLEAR (siginfo_static);
+ c.sig = &siginfo_static;
+
+ /* initialize garbage collector scoped to context object */
+ gc_init (&c.gc);
+
+ /* initialize environmental variable store */
+ c.es = env_set_create (NULL);
+#ifdef WIN32
+ env_set_add_win32 (c.es);
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+ /* initialize management subsystem */
+ init_management (&c);
+#endif
+
+ /* initialize options to default state */
+ init_options (&c.options, true);
+
+ /* parse command line options, and read configuration file */
+ parse_argv (&c.options, argc, argv, M_USAGE, OPT_P_DEFAULT, NULL, c.es);
+
+#ifdef ENABLE_PLUGIN
+ /* plugins may contribute options configuration */
+ init_verb_mute (&c, IVM_LEVEL_1);
+ init_plugins (&c);
+ open_plugins (&c, true, OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE);
+#endif
+
+ /* init verbosity and mute levels */
+ init_verb_mute (&c, IVM_LEVEL_1);
+
+ /* set dev options */
+ init_options_dev (&c.options);
+
+ /* openssl print info? */
+ if (print_openssl_info (&c.options))
+ break;
+
+ /* --genkey mode? */
+ if (do_genkey (&c.options))
+ break;
+
+ /* tun/tap persist command? */
+ if (do_persist_tuntap (&c.options))
+ break;
+
+ /* sanity check on options */
+ options_postprocess (&c.options);
+
+ /* show all option settings */
+ show_settings (&c.options);
+
+ /* print version number */
+ msg (M_INFO, "%s", title_string);
+
+ /* misc stuff */
+ pre_setup (&c.options);
+
+ /* test crypto? */
+ if (do_test_crypto (&c.options))
+ break;
+
+#ifdef ENABLE_MANAGEMENT
+ /* open management subsystem */
+ if (!open_management (&c))
+ break;
+#endif
+
+ /* set certain options as environmental variables */
+ setenv_settings (c.es, &c.options);
+
+ /* finish context init */
+ context_init_1 (&c);
+
+ do
+ {
+ /* run tunnel depending on mode */
+ switch (c.options.mode)
+ {
+ case MODE_POINT_TO_POINT:
+ tunnel_point_to_point (&c);
+ break;
+#if P2MP_SERVER
+ case MODE_SERVER:
+ tunnel_server (&c);
+ break;
+#endif
+ default:
+ ASSERT (0);
+ }
+
+ /* indicates first iteration -- has program-wide scope */
+ c.first_time = false;
+
+ /* any signals received? */
+ if (IS_SIG (&c))
+ print_signal (c.sig, NULL, M_INFO);
+
+ /* pass restart status to management subsystem */
+ signal_restart_status (c.sig);
+ }
+ while (c.sig->signal_received == SIGUSR1);
+
+ uninit_options (&c.options);
+ gc_reset (&c.gc);
+ }
+ while (c.sig->signal_received == SIGHUP);
+ }
+
+ context_gc_free (&c);
+
+ env_set_destroy (c.es);
+
+#ifdef ENABLE_MANAGEMENT
+ /* close management interface */
+ close_management ();
+#endif
+
+ /* uninitialize program-wide statics */
+ uninit_static ();
+
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ return 0; /* NOTREACHED */
+}
diff --git a/openvpn.h b/openvpn.h
new file mode 100644
index 0000000..641bf93
--- /dev/null
+++ b/openvpn.h
@@ -0,0 +1,550 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OPENVPN_H
+#define OPENVPN_H
+
+#include "buffer.h"
+#include "options.h"
+#include "socket.h"
+#include "crypto.h"
+#include "ssl.h"
+#include "packet_id.h"
+#include "lzo.h"
+#include "tun.h"
+#include "interval.h"
+#include "status.h"
+#include "fragment.h"
+#include "shaper.h"
+#include "route.h"
+#include "proxy.h"
+#include "socks.h"
+#include "sig.h"
+#include "misc.h"
+#include "mbuf.h"
+#include "pool.h"
+#include "plugin.h"
+#include "manage.h"
+#include "pf.h"
+
+/*
+ * Our global key schedules, packaged thusly
+ * to facilitate --persist-key.
+ */
+
+struct key_schedule
+{
+#ifdef USE_CRYPTO
+ /* which cipher, HMAC digest, and key sizes are we using? */
+ struct key_type key_type;
+
+ /* pre-shared static key, read from a file */
+ struct key_ctx_bi static_key;
+
+#ifdef USE_SSL
+ /* our global SSL context */
+ SSL_CTX *ssl_ctx;
+
+ /* optional authentication HMAC key for TLS control channel */
+ struct key_ctx_bi tls_auth_key;
+
+#endif /* USE_SSL */
+#else /* USE_CRYPTO */
+ int dummy;
+#endif /* USE_CRYPTO */
+};
+
+/*
+ * struct packet_id_persist should be empty if we are not
+ * building with crypto.
+ */
+#ifndef PACKET_ID_H
+struct packet_id_persist
+{
+ int dummy;
+};
+static inline void
+packet_id_persist_init (struct packet_id_persist *p)
+{
+}
+#endif
+
+/*
+ * Packet processing buffers.
+ */
+struct context_buffers
+{
+ /* miscellaneous buffer, used by ping, occ, etc. */
+ struct buffer aux_buf;
+
+ /* workspace buffers used by crypto routines */
+#ifdef USE_CRYPTO
+ struct buffer encrypt_buf;
+ struct buffer decrypt_buf;
+#endif
+
+ /* workspace buffers for LZO compression */
+#ifdef USE_LZO
+ struct buffer lzo_compress_buf;
+ struct buffer lzo_decompress_buf;
+#endif
+
+ /*
+ * Buffers used to read from TUN device
+ * and TCP/UDP port.
+ */
+ struct buffer read_link_buf;
+ struct buffer read_tun_buf;
+};
+
+/*
+ * always-persistent context variables
+ */
+struct context_persist
+{
+ int restart_sleep_seconds;
+};
+
+/*
+ * level 0 context contains data related to
+ * once-per OpenVPN instantiation events
+ * such as daemonization.
+ */
+struct context_0
+{
+ /* workspace for get_pid_file/write_pid */
+ struct pid_state pid_state;
+
+ /* workspace for --user/--group */
+ bool uid_gid_specified;
+ bool uid_gid_set;
+ struct user_state user_state;
+ struct group_state group_state;
+};
+
+/*
+ * Contains the persist-across-restart OpenVPN tunnel instance state.
+ * Reset only for SIGHUP restarts.
+ */
+struct context_1
+{
+ /* local and remote addresses */
+ struct link_socket_addr link_socket_addr;
+
+ /* tunnel session keys */
+ struct key_schedule ks;
+
+ /* persist crypto sequence number to/from file */
+ struct packet_id_persist pid_persist;
+
+ /* TUN/TAP interface */
+ struct tuntap *tuntap;
+ bool tuntap_owned;
+
+ /* list of --route directives */
+ struct route_list *route_list;
+
+ /* --status file */
+ struct status_output *status_output;
+ bool status_output_owned;
+
+#ifdef ENABLE_HTTP_PROXY
+ /* HTTP proxy object */
+ struct http_proxy_info *http_proxy;
+ bool http_proxy_owned;
+#endif
+
+#ifdef ENABLE_SOCKS
+ /* SOCKS proxy object */
+ struct socks_proxy_info *socks_proxy;
+ bool socks_proxy_owned;
+#endif
+
+#if P2MP
+
+#if P2MP_SERVER
+ /* persist --ifconfig-pool db to file */
+ struct ifconfig_pool_persist *ifconfig_pool_persist;
+ bool ifconfig_pool_persist_owned;
+#endif
+
+ /* if client mode, hash of option strings we pulled from server */
+ struct md5_digest pulled_options_digest_save;
+
+ /* save user/pass for authentication */
+ struct user_pass *auth_user_pass;
+#endif
+};
+
+/*
+ * Contains the OpenVPN tunnel instance state, wiped across
+ * SIGUSR1 and SIGHUP restarts.
+ */
+struct context_2
+{
+ /* garbage collection arena for context_2 scope */
+ struct gc_arena gc;
+
+ /* our global wait events */
+ struct event_set *event_set;
+ int event_set_max;
+ bool event_set_owned;
+
+ /* event flags returned by io_wait */
+# define SOCKET_READ (1<<0)
+# define SOCKET_WRITE (1<<1)
+# define TUN_READ (1<<2)
+# define TUN_WRITE (1<<3)
+# define ES_ERROR (1<<4)
+# define ES_TIMEOUT (1<<5)
+# ifdef ENABLE_MANAGEMENT
+# define MANAGEMENT_READ (1<<6)
+# define MANAGEMENT_WRITE (1<<7)
+# endif
+
+ unsigned int event_set_status;
+
+ struct link_socket *link_socket; /* socket used for TCP/UDP connection to remote */
+ bool link_socket_owned;
+ struct link_socket_info *link_socket_info;
+ const struct link_socket *accept_from; /* possibly do accept() on a parent link_socket */
+
+ struct link_socket_actual *to_link_addr; /* IP address of remote */
+ struct link_socket_actual from; /* address of incoming datagram */
+
+ /* MTU frame parameters */
+ struct frame frame;
+
+#ifdef ENABLE_FRAGMENT
+ /* Object to handle advanced MTU negotiation and datagram fragmentation */
+ struct fragment_master *fragment;
+ struct frame frame_fragment;
+ struct frame frame_fragment_omit;
+#endif
+
+#ifdef HAVE_GETTIMEOFDAY
+ /*
+ * Traffic shaper object.
+ */
+ struct shaper shaper;
+#endif
+
+ /*
+ * Statistics
+ */
+ counter_type tun_read_bytes;
+ counter_type tun_write_bytes;
+ counter_type link_read_bytes;
+ counter_type link_read_bytes_auth;
+ counter_type link_write_bytes;
+#ifdef PACKET_TRUNCATION_CHECK
+ counter_type n_trunc_tun_read;
+ counter_type n_trunc_tun_write;
+ counter_type n_trunc_pre_encrypt;
+ counter_type n_trunc_post_decrypt;
+#endif
+
+ /*
+ * Timer objects for ping and inactivity
+ * timeout features.
+ */
+ struct event_timeout wait_for_connect;
+ struct event_timeout ping_send_interval;
+ struct event_timeout ping_rec_interval;
+
+ /* --inactive */
+ struct event_timeout inactivity_interval;
+ int inactivity_bytes;
+
+#ifdef ENABLE_OCC
+ /* the option strings must match across peers */
+ char *options_string_local;
+ char *options_string_remote;
+
+ int occ_op; /* INIT to -1 */
+ int occ_n_tries;
+ struct event_timeout occ_interval;
+#endif
+
+ /*
+ * Keep track of maximum packet size received so far
+ * (of authenticated packets).
+ */
+ int original_recv_size; /* temporary */
+ int max_recv_size_local; /* max packet size received */
+ int max_recv_size_remote; /* max packet size received by remote */
+ int max_send_size_local; /* max packet size sent */
+ int max_send_size_remote; /* max packet size sent by remote */
+
+#ifdef ENABLE_OCC
+ /* remote wants us to send back a load test packet of this size */
+ int occ_mtu_load_size;
+
+ struct event_timeout occ_mtu_load_test_interval;
+ int occ_mtu_load_n_tries;
+#endif
+
+#ifdef USE_CRYPTO
+
+ /*
+ * TLS-mode crypto objects.
+ */
+#ifdef USE_SSL
+
+ /* master OpenVPN SSL/TLS object */
+ struct tls_multi *tls_multi;
+
+ /* check --tls-auth signature without needing
+ a full-size tls_multi object */
+ struct tls_auth_standalone *tls_auth_standalone;
+
+ /* used to optimize calls to tls_multi_process */
+ struct interval tmp_int;
+
+ /* throw this signal on TLS errors */
+ int tls_exit_signal;
+
+#endif /* USE_SSL */
+
+ /* passed to encrypt or decrypt, contains all
+ crypto-related command line options related
+ to data channel encryption/decryption */
+ struct crypto_options crypto_options;
+
+ /* used to keep track of data channel packet sequence numbers */
+ struct packet_id packet_id;
+ struct event_timeout packet_id_persist_interval;
+
+#endif /* USE_CRYPTO */
+
+ /*
+ * LZO compression library workspace.
+ */
+#ifdef USE_LZO
+ struct lzo_compress_workspace lzo_compwork;
+#endif
+
+ /*
+ * Buffers used for packet processing.
+ */
+ struct context_buffers *buffers;
+ bool buffers_owned; /* if true, we should free all buffers on close */
+
+ /*
+ * These buffers don't actually allocate storage, they are used
+ * as pointers to the allocated buffers in
+ * struct context_buffers.
+ */
+ struct buffer buf;
+ struct buffer to_tun;
+ struct buffer to_link;
+
+ /*
+ * IPv4 TUN device?
+ */
+ bool ipv4_tun;
+
+ /* should we print R|W|r|w to console on packet transfers? */
+ bool log_rw;
+
+ /* route stuff */
+ struct event_timeout route_wakeup;
+ struct event_timeout route_wakeup_expire;
+
+ /* did we open tun/tap dev during this cycle? */
+ bool did_open_tun;
+
+ /*
+ * Event loop info
+ */
+
+ /* how long to wait on link/tun read before we will need to be serviced */
+ struct timeval timeval;
+
+ /* next wakeup for processing coarse timers (>1 sec resolution) */
+ time_t coarse_timer_wakeup;
+
+ /* maintain a random delta to add to timeouts to avoid contexts
+ waking up simultaneously */
+ time_t update_timeout_random_component;
+ struct timeval timeout_random_component;
+
+ /* indicates that the do_up_delay function has run */
+ bool do_up_ran;
+
+#ifdef ENABLE_OCC
+ /* indicates that we have received a SIGTERM when
+ options->explicit_exit_notification is enabled,
+ but we have not exited yet */
+ time_t explicit_exit_notification_time_wait;
+ struct event_timeout explicit_exit_notification_interval;
+#endif
+
+ /* environmental variables to pass to scripts */
+ struct env_set *es;
+ bool es_owned;
+
+ /* don't wait for TUN/TAP/UDP to be ready to accept write */
+ bool fast_io;
+
+#if P2MP
+
+#if P2MP_SERVER
+ /* --ifconfig endpoints to be pushed to client */
+ bool push_reply_deferred;
+ bool push_ifconfig_defined;
+ in_addr_t push_ifconfig_local;
+ in_addr_t push_ifconfig_remote_netmask;
+
+ /* client authentication state, CAS_SUCCEEDED must be 0 */
+# define CAS_SUCCEEDED 0
+# define CAS_PENDING 1
+# define CAS_FAILED 2
+# define CAS_PARTIAL 3 /* at least one client-connect script/plugin
+ succeeded while a later one in the chain failed */
+ int context_auth;
+#endif
+
+ struct event_timeout push_request_interval;
+ bool did_pre_pull_restore;
+
+ /* hash of pulled options, so we can compare when options change */
+ struct md5_state pulled_options_state;
+ struct md5_digest pulled_options_digest;
+
+ struct event_timeout server_poll_interval;
+
+ struct event_timeout scheduled_exit;
+ int scheduled_exit_signal;
+#endif
+
+ /* packet filter */
+#ifdef ENABLE_PF
+ struct pf_context pf;
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+ struct man_def_auth_context mda_context;
+#endif
+};
+
+/*
+ * Contains all state information for one tunnel.
+ */
+struct context
+{
+ /* command line or config file options */
+ struct options options;
+
+ /* true on initial VPN iteration */
+ bool first_time;
+
+ /* context modes */
+# define CM_P2P 0 /* standalone point-to-point session or client */
+# define CM_TOP 1 /* top level of a multi-client or point-to-multipoint server */
+# define CM_TOP_CLONE 2 /* clone of a CM_TOP context for one thread */
+# define CM_CHILD_UDP 3 /* child context of a CM_TOP or CM_THREAD */
+# define CM_CHILD_TCP 4 /* child context of a CM_TOP or CM_THREAD */
+ int mode;
+
+ /* garbage collection for context scope
+ allocations */
+ struct gc_arena gc;
+
+ /* environmental variable settings */
+ struct env_set *es;
+
+ /* signal info */
+ struct signal_info *sig;
+
+ /* shared object plugins */
+ struct plugin_list *plugins;
+ bool plugins_owned;
+
+ /* set to true after we daemonize */
+ bool did_we_daemonize;
+
+ /* persistent across SIGHUP */
+ struct context_persist persist;
+
+ /* level 0 context contains data related to
+ once-per OpenVPN instantiation events
+ such as daemonization */
+ struct context_0 *c0;
+
+ /* level 1 context is preserved for
+ SIGUSR1 restarts, but initialized
+ for SIGHUP restarts */
+ struct context_1 c1;
+
+ /* level 2 context is initialized for all
+ restarts (SIGUSR1 and SIGHUP) */
+ struct context_2 c2;
+};
+
+/*
+ * Check for a signal when inside an event loop
+ */
+#define EVENT_LOOP_CHECK_SIGNAL(c, func, arg) \
+ if (IS_SIG (c)) \
+ { \
+ const int brk = func (arg); \
+ perf_pop (); \
+ if (brk) \
+ break; \
+ else \
+ continue; \
+ }
+
+/*
+ * Macros for referencing objects which may not
+ * have been compiled in.
+ */
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+#define TLS_MODE(c) ((c)->c2.tls_multi != NULL)
+#define PROTO_DUMP_FLAGS (check_debug_level (D_LINK_RW_VERBOSE) ? (PD_SHOW_DATA|PD_VERBOSE) : 0)
+#define PROTO_DUMP(buf, gc) protocol_dump((buf), \
+ PROTO_DUMP_FLAGS | \
+ (c->c2.tls_multi ? PD_TLS : 0) | \
+ (c->options.tls_auth_file ? c->c1.ks.key_type.hmac_length : 0), \
+ gc)
+#else
+#define TLS_MODE(c) (false)
+#define PROTO_DUMP(buf, gc) format_hex (BPTR (buf), BLEN (buf), 80, gc)
+#endif
+
+#ifdef USE_CRYPTO
+#define MD5SUM(buf, len, gc) md5sum((buf), (len), 0, (gc))
+#else
+#define MD5SUM(buf, len, gc) "[unavailable]"
+#endif
+
+#ifdef USE_CRYPTO
+#define CIPHER_ENABLED(c) (c->c1.ks.key_type.cipher != NULL)
+#else
+#define CIPHER_ENABLED(c) (false)
+#endif
+
+#endif
diff --git a/openvpn.spec b/openvpn.spec
new file mode 100644
index 0000000..7e2ae9d
--- /dev/null
+++ b/openvpn.spec
@@ -0,0 +1,281 @@
+# OpenVPN spec file, used to drive rpmbuild
+
+# OPTIONS
+#
+# Disable LZO
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'without_lzo 1'
+#
+# Disable PAM plugin
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'without_pam 1'
+#
+# Allow passwords to be read from files
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'with_password_save 1'
+#
+# Use this on RH9 and RHEL3
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'with_kerberos 1'
+
+Summary: OpenVPN is a robust and highly flexible VPN daemon by James Yonan.
+Name: openvpn
+Version: 2.2.1
+Release: 1
+URL: http://openvpn.net/
+Source0: http://prdownloads.sourceforge.net/openvpn/%{name}-%{version}.tar.gz
+
+License: GPL
+Group: Applications/Internet
+Vendor: James Yonan <jim@yonan.net>
+Packager: James Yonan <jim@yonan.net>
+BuildRoot: %{_tmppath}/%{name}-%(id -un)
+
+#
+# Include dependencies manually
+#
+
+AutoReq: 0
+
+BuildRequires: openssl-devel >= 0.9.6
+Requires: openssl >= 0.9.6
+
+%if "%{_vendor}" == "Mandrakesoft"
+%{!?without_lzo:BuildRequires: liblzo1-devel >= 1.07}
+%{!?without_lzo:Requires: liblzo1 >= 1.07}
+%else
+%if "%{_vendor}" == "MandrakeSoft"
+%{!?without_lzo:BuildRequires: liblzo1-devel >= 1.07}
+%{!?without_lzo:Requires: liblzo1 >= 1.07}
+%else
+%{!?without_lzo:BuildRequires: lzo-devel >= 1.07}
+%{!?without_lzo:Requires: lzo >= 1.07}
+%endif
+%endif
+
+%{!?without_pam:BuildRequires: pam-devel}
+%{!?without_pam:Requires: pam}
+
+%{!?with_pkcs11:BuildRequires: pkcs11-helper-devel}
+%{!?with_pkcs11:Requires: pkcs11-helper}
+
+#
+# Description
+#
+
+%description
+OpenVPN is a robust and highly flexible VPN daemon by James Yonan.
+OpenVPN supports SSL/TLS security,
+ethernet bridging,
+TCP or UDP tunnel transport through proxies or NAT,
+support for dynamic IP addresses and DHCP,
+scalability to hundreds or thousands of users,
+and portability to most major OS platforms.
+
+#
+# Define vendor type
+#
+
+%if "%{_vendor}" == "suse" || "%{_vendor}" == "pc"
+%define VENDOR SuSE
+%else
+%define VENDOR %_vendor
+%endif
+
+#
+# Should we build the auth-pam module?
+#
+
+%define build_auth_pam 1
+%{?without_pam:%define build_auth_pam 0}
+
+#
+# Other definitions
+#
+
+%define debug_package %{nil}
+
+#
+# Build OpenVPN binary
+#
+
+%prep
+%setup -q
+
+%build
+%configure --disable-dependency-tracking %{?with_password_save:--enable-password-save} %{?without_lzo:--disable-lzo} %{?with_kerberos:--with-ssl-headers=/usr/kerberos/include}
+%__make
+%__strip %{name}
+
+# Build down-root plugin
+pushd plugin/down-root
+%__make
+popd
+
+# Build auth-pam plugin
+%if %{build_auth_pam}
+pushd plugin/auth-pam
+%__make
+popd
+%endif
+
+#
+# Installation section
+#
+
+%install
+[ %{buildroot} != "/" ] && rm -rf %{buildroot}
+
+# Install man page
+%__install -c -d -m 755 %{buildroot}%{_mandir}/man8
+%__install -c -m 755 %{name}.8 %{buildroot}%{_mandir}/man8
+
+# Install binary
+%__install -c -d -m 755 %{buildroot}%{_sbindir}
+%__install -c -m 755 %{name} %{buildroot}%{_sbindir}
+
+# Install init script
+%if "%{VENDOR}" == "SuSE"
+%__install -c -d -m 755 %{buildroot}/etc/init.d
+%__sed -e 's#openvpn=\"/usr/local/sbin/openvpn\"#openvpn=\"/usr/sbin/openvpn\"#' < suse/%{name}.init > %{_tmppath}/%{name}.init
+%__install -c -m 755 %{_tmppath}/%{name}.init %{buildroot}/etc/init.d/%{name}
+%__rm %{_tmppath}/%{name}.init
+%else
+%__install -c -d -m 755 %{buildroot}/etc/rc.d/init.d
+%__install -c -m 755 sample-scripts/%{name}.init %{buildroot}/etc/rc.d/init.d/%{name}
+%endif
+
+# Install /etc/openvpn
+%__install -c -d -m 755 %{buildroot}/etc/%{name}
+
+#
+# Build /usr/share/openvpn
+#
+
+%__mkdir_p %{buildroot}%{_datadir}/%{name}
+
+#
+# Install the plugins
+#
+
+%__mkdir_p %{buildroot}%{_datadir}/%{name}/plugin/lib
+
+for pi in auth-pam down-root; do
+ %__mv -f plugin/$pi/README plugin/README.$pi
+ if [ -e plugin/$pi/openvpn-$pi.so ]; then
+ %__install -c -m 755 plugin/$pi/openvpn-$pi.so %{buildroot}%{_datadir}/openvpn/plugin/lib/openvpn-$pi.so
+ fi
+done
+
+%__mv -f plugin/README plugin/README.plugins
+
+#
+# Clean section
+#
+
+%clean
+[ %{buildroot} != "/" ] && rm -rf %{buildroot}
+
+#
+# On Linux 2.4, make the device node
+#
+
+%post
+case "`uname -r`" in
+2.4*)
+ /bin/mkdir /dev/net >/dev/null 2>&1
+ /bin/mknod /dev/net/tun c 10 200 >/dev/null 2>&1
+ ;;
+esac
+
+#
+# Handle the init script
+#
+
+/sbin/chkconfig --add %{name}
+%if "%{VENDOR}" == "SuSE"
+/etc/init.d/openvpn restart
+%else
+/sbin/service %{name} condrestart
+%endif
+%preun
+if [ "$1" = 0 ]
+then
+ %if "%{VENDOR}" == "SuSE"
+ /etc/init.d/openvpn stop
+ %else
+ /sbin/service %{name} stop
+ %endif
+ /sbin/chkconfig --del %{name}
+fi
+
+#
+# Files section
+#
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS ChangeLog COPYING COPYRIGHT.GPL INSTALL NEWS PORTS README
+%{_mandir}/man8/%{name}.8*
+%{_sbindir}/%{name}
+%{_datadir}/%{name}
+%dir /etc/%{name}
+%if "%{VENDOR}" == "SuSE"
+/etc/init.d/%{name}
+%else
+/etc/rc.d/init.d/%{name}
+%endif
+
+# Install extra %doc stuff
+%doc contrib/ easy-rsa/ sample-*/ plugin/README.*
+
+%changelog
+* Thu Jul 30 2009 David Sommerseth <dazo@users.sourceforge.net>
+- Removed management/ directory from %doc
+
+* Thu Dec 14 2006 Alon Bar-Lev
+- Added with_pkcs11
+
+* Mon Aug 2 2005 James Yonan
+- Fixed build problem with --define 'without_pam 1'
+
+* Mon Apr 4 2005 James Yonan
+- Moved some files from /usr/share/openvpn to %doc for compatibility
+ with Dag Wieers' RPM repository
+
+* Sat Mar 12 2005 Tom Walsh
+- Added MandrakeSoft liblzo1 require
+
+* Fri Dec 10 2004 James Yonan
+- Added AutoReq: 0 for manual dependencies
+
+* Fri Dec 10 2004 James Yonan
+- Packaged the plugins
+
+* Sun Nov 7 2004 Umberto Nicoletti
+- SuSE support
+
+* Wed Aug 18 2004 Bishop Clark (LC957) <bishop@platypus.bc.ca>
+- restrict what we claim in /etc/ to avoid ownership conflicts
+
+* Sun Feb 23 2003 Matthias Andree <matthias.andree@gmx.de> 1.3.2.14-1.
+- Have the version number filled in by autoconf.
+
+* Wed Jul 10 2002 James Yonan <jim@yonan.net> 1.3.1-1
+- Fixed %preun to only remove service on final uninstall
+
+* Mon Jun 17 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.2.2-1
+- Added condrestart to openvpn.spec & openvpn.init.
+
+* Wed May 22 2002 James Yonan <jim@yonan.net> 1.2.0-1
+- Added mknod for Linux 2.4.
+
+* Wed May 15 2002 Doug Keller <dsk@voidstar.dyndns.org> 1.1.1.16-2
+- Added init scripts
+- Added conf file support
+
+* Mon May 13 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.1.1.14-1
+- Added new directories for config examples and such
+
+* Sun May 12 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.1.1.13-1
+- Updated buildroot directive and cleanup command
+- added easy-rsa utilities
+
+* Mon Mar 25 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.0-1
+- Initial build.
diff --git a/openvpn.spec.in b/openvpn.spec.in
new file mode 100644
index 0000000..c5178e9
--- /dev/null
+++ b/openvpn.spec.in
@@ -0,0 +1,281 @@
+# OpenVPN spec file, used to drive rpmbuild
+
+# OPTIONS
+#
+# Disable LZO
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'without_lzo 1'
+#
+# Disable PAM plugin
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'without_pam 1'
+#
+# Allow passwords to be read from files
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'with_password_save 1'
+#
+# Use this on RH9 and RHEL3
+# rpmbuild -tb [openvpn.x.tar.gz] --define 'with_kerberos 1'
+
+Summary: OpenVPN is a robust and highly flexible VPN daemon by James Yonan.
+Name: @PACKAGE@
+Version: @VERSION@
+Release: 1
+URL: http://openvpn.net/
+Source0: http://prdownloads.sourceforge.net/openvpn/%{name}-%{version}.tar.gz
+
+License: GPL
+Group: Applications/Internet
+Vendor: James Yonan <jim@yonan.net>
+Packager: James Yonan <jim@yonan.net>
+BuildRoot: %{_tmppath}/%{name}-%(id -un)
+
+#
+# Include dependencies manually
+#
+
+AutoReq: 0
+
+BuildRequires: openssl-devel >= 0.9.6
+Requires: openssl >= 0.9.6
+
+%if "%{_vendor}" == "Mandrakesoft"
+%{!?without_lzo:BuildRequires: liblzo1-devel >= 1.07}
+%{!?without_lzo:Requires: liblzo1 >= 1.07}
+%else
+%if "%{_vendor}" == "MandrakeSoft"
+%{!?without_lzo:BuildRequires: liblzo1-devel >= 1.07}
+%{!?without_lzo:Requires: liblzo1 >= 1.07}
+%else
+%{!?without_lzo:BuildRequires: lzo-devel >= 1.07}
+%{!?without_lzo:Requires: lzo >= 1.07}
+%endif
+%endif
+
+%{!?without_pam:BuildRequires: pam-devel}
+%{!?without_pam:Requires: pam}
+
+%{!?with_pkcs11:BuildRequires: pkcs11-helper-devel}
+%{!?with_pkcs11:Requires: pkcs11-helper}
+
+#
+# Description
+#
+
+%description
+OpenVPN is a robust and highly flexible VPN daemon by James Yonan.
+OpenVPN supports SSL/TLS security,
+ethernet bridging,
+TCP or UDP tunnel transport through proxies or NAT,
+support for dynamic IP addresses and DHCP,
+scalability to hundreds or thousands of users,
+and portability to most major OS platforms.
+
+#
+# Define vendor type
+#
+
+%if "%{_vendor}" == "suse" || "%{_vendor}" == "pc"
+%define VENDOR SuSE
+%else
+%define VENDOR %_vendor
+%endif
+
+#
+# Should we build the auth-pam module?
+#
+
+%define build_auth_pam 1
+%{?without_pam:%define build_auth_pam 0}
+
+#
+# Other definitions
+#
+
+%define debug_package %{nil}
+
+#
+# Build OpenVPN binary
+#
+
+%prep
+%setup -q
+
+%build
+%configure --disable-dependency-tracking %{?with_password_save:--enable-password-save} %{?without_lzo:--disable-lzo} %{?with_kerberos:--with-ssl-headers=/usr/kerberos/include}
+%__make
+%__strip %{name}
+
+# Build down-root plugin
+pushd plugin/down-root
+%__make
+popd
+
+# Build auth-pam plugin
+%if %{build_auth_pam}
+pushd plugin/auth-pam
+%__make
+popd
+%endif
+
+#
+# Installation section
+#
+
+%install
+[ %{buildroot} != "/" ] && rm -rf %{buildroot}
+
+# Install man page
+%__install -c -d -m 755 %{buildroot}%{_mandir}/man8
+%__install -c -m 755 %{name}.8 %{buildroot}%{_mandir}/man8
+
+# Install binary
+%__install -c -d -m 755 %{buildroot}%{_sbindir}
+%__install -c -m 755 %{name} %{buildroot}%{_sbindir}
+
+# Install init script
+%if "%{VENDOR}" == "SuSE"
+%__install -c -d -m 755 %{buildroot}/etc/init.d
+%__sed -e 's#openvpn=\"/usr/local/sbin/openvpn\"#openvpn=\"/usr/sbin/openvpn\"#' < suse/%{name}.init > %{_tmppath}/%{name}.init
+%__install -c -m 755 %{_tmppath}/%{name}.init %{buildroot}/etc/init.d/%{name}
+%__rm %{_tmppath}/%{name}.init
+%else
+%__install -c -d -m 755 %{buildroot}/etc/rc.d/init.d
+%__install -c -m 755 sample-scripts/%{name}.init %{buildroot}/etc/rc.d/init.d/%{name}
+%endif
+
+# Install /etc/openvpn
+%__install -c -d -m 755 %{buildroot}/etc/%{name}
+
+#
+# Build /usr/share/openvpn
+#
+
+%__mkdir_p %{buildroot}%{_datadir}/%{name}
+
+#
+# Install the plugins
+#
+
+%__mkdir_p %{buildroot}%{_datadir}/%{name}/plugin/lib
+
+for pi in auth-pam down-root; do
+ %__mv -f plugin/$pi/README plugin/README.$pi
+ if [ -e plugin/$pi/openvpn-$pi.so ]; then
+ %__install -c -m 755 plugin/$pi/openvpn-$pi.so %{buildroot}%{_datadir}/openvpn/plugin/lib/openvpn-$pi.so
+ fi
+done
+
+%__mv -f plugin/README plugin/README.plugins
+
+#
+# Clean section
+#
+
+%clean
+[ %{buildroot} != "/" ] && rm -rf %{buildroot}
+
+#
+# On Linux 2.4, make the device node
+#
+
+%post
+case "`uname -r`" in
+2.4*)
+ /bin/mkdir /dev/net >/dev/null 2>&1
+ /bin/mknod /dev/net/tun c 10 200 >/dev/null 2>&1
+ ;;
+esac
+
+#
+# Handle the init script
+#
+
+/sbin/chkconfig --add %{name}
+%if "%{VENDOR}" == "SuSE"
+/etc/init.d/openvpn restart
+%else
+/sbin/service %{name} condrestart
+%endif
+%preun
+if [ "$1" = 0 ]
+then
+ %if "%{VENDOR}" == "SuSE"
+ /etc/init.d/openvpn stop
+ %else
+ /sbin/service %{name} stop
+ %endif
+ /sbin/chkconfig --del %{name}
+fi
+
+#
+# Files section
+#
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS ChangeLog COPYING COPYRIGHT.GPL INSTALL NEWS PORTS README
+%{_mandir}/man8/%{name}.8*
+%{_sbindir}/%{name}
+%{_datadir}/%{name}
+%dir /etc/%{name}
+%if "%{VENDOR}" == "SuSE"
+/etc/init.d/%{name}
+%else
+/etc/rc.d/init.d/%{name}
+%endif
+
+# Install extra %doc stuff
+%doc contrib/ easy-rsa/ sample-*/ plugin/README.*
+
+%changelog
+* Thu Jul 30 2009 David Sommerseth <dazo@users.sourceforge.net>
+- Removed management/ directory from %doc
+
+* Thu Dec 14 2006 Alon Bar-Lev
+- Added with_pkcs11
+
+* Mon Aug 2 2005 James Yonan
+- Fixed build problem with --define 'without_pam 1'
+
+* Mon Apr 4 2005 James Yonan
+- Moved some files from /usr/share/openvpn to %doc for compatibility
+ with Dag Wieers' RPM repository
+
+* Sat Mar 12 2005 Tom Walsh
+- Added MandrakeSoft liblzo1 require
+
+* Fri Dec 10 2004 James Yonan
+- Added AutoReq: 0 for manual dependencies
+
+* Fri Dec 10 2004 James Yonan
+- Packaged the plugins
+
+* Sun Nov 7 2004 Umberto Nicoletti
+- SuSE support
+
+* Wed Aug 18 2004 Bishop Clark (LC957) <bishop@platypus.bc.ca>
+- restrict what we claim in /etc/ to avoid ownership conflicts
+
+* Sun Feb 23 2003 Matthias Andree <matthias.andree@gmx.de> 1.3.2.14-1.
+- Have the version number filled in by autoconf.
+
+* Wed Jul 10 2002 James Yonan <jim@yonan.net> 1.3.1-1
+- Fixed %preun to only remove service on final uninstall
+
+* Mon Jun 17 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.2.2-1
+- Added condrestart to openvpn.spec & openvpn.init.
+
+* Wed May 22 2002 James Yonan <jim@yonan.net> 1.2.0-1
+- Added mknod for Linux 2.4.
+
+* Wed May 15 2002 Doug Keller <dsk@voidstar.dyndns.org> 1.1.1.16-2
+- Added init scripts
+- Added conf file support
+
+* Mon May 13 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.1.1.14-1
+- Added new directories for config examples and such
+
+* Sun May 12 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.1.1.13-1
+- Updated buildroot directive and cleanup command
+- added easy-rsa utilities
+
+* Mon Mar 25 2002 bishop clark (LC957) <bishop@platypus.bc.ca> 1.0-1
+- Initial build.
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..7a5e35d
--- /dev/null
+++ b/options.c
@@ -0,0 +1,6020 @@
+/*
+ * 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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * Additions for eurephia plugin done by:
+ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2009
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 2004-01-28: Added Socks5 proxy support
+ * (Christof Meerwald, http://cmeerw.org)
+ */
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "error.h"
+#include "common.h"
+#include "shaper.h"
+#include "crypto.h"
+#include "ssl.h"
+#include "options.h"
+#include "misc.h"
+#include "socket.h"
+#include "packet_id.h"
+#include "pkcs11.h"
+#include "win32.h"
+#include "push.h"
+#include "pool.h"
+#include "helper.h"
+#include "manage.h"
+#include "forward.h"
+#include "configure.h"
+#include <ctype.h>
+
+#include "memdbg.h"
+
+const char title_string[] =
+ PACKAGE_STRING
+ " " TARGET_ALIAS
+#ifdef USE_CRYPTO
+#ifdef USE_SSL
+ " [SSL]"
+#else
+ " [CRYPTO]"
+#endif
+#endif
+#ifdef USE_LZO
+ " [LZO" LZO_VERSION_NUM "]"
+#endif
+#if EPOLL
+ " [EPOLL]"
+#endif
+#ifdef PRODUCT_TAP_DEBUG
+ " [TAPDBG]"
+#endif
+#ifdef ENABLE_PKCS11
+ " [PKCS11]"
+#endif
+#ifdef ENABLE_EUREPHIA
+ " [eurephia]"
+#endif
+ " built on " __DATE__
+;
+
+#ifndef ENABLE_SMALL
+
+static const char usage_message[] =
+ "%s\n"
+ "\n"
+ "General Options:\n"
+ "--config file : Read configuration options from file.\n"
+ "--help : Show options.\n"
+ "--version : Show copyright and version information.\n"
+ "\n"
+ "Tunnel Options:\n"
+ "--local host : Local host name or ip address. Implies --bind.\n"
+ "--remote host [port] : Remote host name or ip address.\n"
+ "--remote-random : If multiple --remote options specified, choose one randomly.\n"
+ "--remote-random-hostname : Add a random string to remote DNS name.\n"
+ "--mode m : Major mode, m = 'p2p' (default, point-to-point) or 'server'.\n"
+ "--proto p : Use protocol p for communicating with peer.\n"
+ " p = udp (default), tcp-server, or tcp-client\n"
+ "--proto-force p : only consider protocol p in list of connection profiles.\n"
+ "--connect-retry n : For --proto tcp-client, number of seconds to wait\n"
+ " between connection retries (default=%d).\n"
+ "--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n"
+ "--connect-retry-max n : Maximum connection attempt retries, default infinite.\n"
+#ifdef GENERAL_PROXY_SUPPORT
+ "--auto-proxy : Try to sense proxy settings (or lack thereof) automatically.\n"
+ "--show-proxy-settings : Show sensed proxy settings.\n"
+#endif
+#ifdef ENABLE_HTTP_PROXY
+ "--http-proxy s p [up] [auth] : Connect to remote host\n"
+ " through an HTTP proxy at address s and port p.\n"
+ " If proxy authentication is required,\n"
+ " up is a file containing username/password on 2 lines, or\n"
+ " 'stdin' to prompt from console. Add auth='ntlm' if\n"
+ " the proxy requires NTLM authentication.\n"
+ "--http-proxy s p 'auto[-nct]' : Like the above directive, but automatically\n"
+ " determine auth method and query for username/password\n"
+ " if needed. auto-nct disables weak proxy auth methods.\n"
+ "--http-proxy-retry : Retry indefinitely on HTTP proxy errors.\n"
+ "--http-proxy-timeout n : Proxy timeout in seconds, default=5.\n"
+ "--http-proxy-option type [parm] : Set extended HTTP proxy options.\n"
+ " Repeat to set multiple options.\n"
+ " VERSION version (default=1.0)\n"
+ " AGENT user-agent\n"
+#endif
+#ifdef ENABLE_SOCKS
+ "--socks-proxy s [p] [up] : Connect to remote host through a Socks5 proxy at\n"
+ " address s and port p (default port = 1080).\n"
+ " If proxy authentication is required,\n"
+ " up is a file containing username/password on 2 lines, or\n"
+ " 'stdin' to prompt for console.\n"
+ "--socks-proxy-retry : Retry indefinitely on Socks proxy errors.\n"
+#endif
+ "--resolv-retry n: If hostname resolve fails for --remote, retry\n"
+ " resolve for n seconds before failing (disabled by default).\n"
+ " Set n=\"infinite\" to retry indefinitely.\n"
+ "--float : Allow remote to change its IP address/port, such as through\n"
+ " DHCP (this is the default if --remote is not used).\n"
+ "--ipchange cmd : Execute shell command cmd on remote ip address initial\n"
+ " setting or change -- execute as: cmd ip-address port#\n"
+ "--port port : TCP/UDP port # for both local and remote.\n"
+ "--lport port : TCP/UDP port # for local (default=%d). Implies --bind.\n"
+ "--rport port : TCP/UDP port # for remote (default=%d).\n"
+ "--bind : Bind to local address and port. (This is the default unless\n"
+ " --proto tcp-client"
+#ifdef ENABLE_HTTP_PROXY
+ " or --http-proxy"
+#endif
+#ifdef ENABLE_SOCKS
+ " or --socks-proxy"
+#endif
+ " is used).\n"
+ "--nobind : Do not bind to local address and port.\n"
+ "--dev tunX|tapX : tun/tap device (X can be omitted for dynamic device.\n"
+ "--dev-type dt : Which device type are we using? (dt = tun or tap) Use\n"
+ " this option only if the tun/tap device used with --dev\n"
+ " does not begin with \"tun\" or \"tap\".\n"
+ "--dev-node node : Explicitly set the device node rather than using\n"
+ " /dev/net/tun, /dev/tun, /dev/tap, etc.\n"
+ "--lladdr hw : Set the link layer address of the tap device.\n"
+ "--topology t : Set --dev tun topology: 'net30', 'p2p', or 'subnet'.\n"
+ "--tun-ipv6 : Build tun link capable of forwarding IPv6 traffic.\n"
+#ifdef CONFIG_FEATURE_IPROUTE
+ "--iproute cmd : Use this command instead of default " IPROUTE_PATH ".\n"
+#endif
+ "--ifconfig l rn : TUN: configure device to use IP address l as a local\n"
+ " endpoint and rn as a remote endpoint. l & rn should be\n"
+ " swapped on the other peer. l & rn must be private\n"
+ " addresses outside of the subnets used by either peer.\n"
+ " TAP: configure device to use IP address l as a local\n"
+ " endpoint and rn as a subnet mask.\n"
+ "--ifconfig-noexec : Don't actually execute ifconfig/netsh command, instead\n"
+ " pass --ifconfig parms by environment to scripts.\n"
+ "--ifconfig-nowarn : Don't warn if the --ifconfig option on this side of the\n"
+ " connection doesn't match the remote side.\n"
+ "--route network [netmask] [gateway] [metric] :\n"
+ " Add route to routing table after connection\n"
+ " is established. Multiple routes can be specified.\n"
+ " netmask default: 255.255.255.255\n"
+ " gateway default: taken from --route-gateway or --ifconfig\n"
+ " Specify default by leaving blank or setting to \"nil\".\n"
+ "--max-routes n : Specify the maximum number of routes that may be defined\n"
+ " or pulled from a server.\n"
+ "--route-gateway gw|'dhcp' : Specify a default gateway for use with --route.\n"
+ "--route-metric m : Specify a default metric for use with --route.\n"
+ "--route-delay n [w] : Delay n seconds after connection initiation before\n"
+ " adding routes (may be 0). If not specified, routes will\n"
+ " be added immediately after tun/tap open. On Windows, wait\n"
+ " up to w seconds for TUN/TAP adapter to come up.\n"
+ "--route-up cmd : Execute shell cmd after routes are added.\n"
+ "--route-noexec : Don't add routes automatically. Instead pass routes to\n"
+ " --route-up script using environmental variables.\n"
+ "--route-nopull : When used with --client or --pull, accept options pushed\n"
+ " by server EXCEPT for routes.\n"
+ "--allow-pull-fqdn : Allow client to pull DNS names from server for\n"
+ " --ifconfig, --route, and --route-gateway.\n"
+ "--redirect-gateway [flags]: Automatically execute routing\n"
+ " commands to redirect all outgoing IP traffic through the\n"
+ " VPN. Add 'local' flag if both " PACKAGE_NAME " servers are directly\n"
+ " connected via a common subnet, such as with WiFi.\n"
+ " Add 'def1' flag to set default route using using 0.0.0.0/1\n"
+ " and 128.0.0.0/1 rather than 0.0.0.0/0. Add 'bypass-dhcp'\n"
+ " flag to add a direct route to DHCP server, bypassing tunnel.\n"
+ " Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
+ "--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
+ " the default gateway. Useful when pushing private subnets.\n"
+#ifdef ENABLE_PUSH_PEER_INFO
+ "--push-peer-info : (client only) push client info to server.\n"
+#endif
+ "--setenv name value : Set a custom environmental variable to pass to script.\n"
+ "--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to allow\n"
+ " directives for future OpenVPN versions to be ignored.\n"
+ "--script-security level mode : mode='execve' (default) or 'system', level=\n"
+ " 0 -- strictly no calling of external programs\n"
+ " 1 -- (default) only call built-ins such as ifconfig\n"
+ " 2 -- allow calling of built-ins and scripts\n"
+ " 3 -- allow password to be passed to scripts via env\n"
+ "--shaper n : Restrict output to peer to n bytes per second.\n"
+ "--keepalive n m : Helper option for setting timeouts in server mode. Send\n"
+ " ping once every n seconds, restart if ping not received\n"
+ " for m seconds.\n"
+ "--inactive n [bytes] : Exit after n seconds of activity on tun/tap device\n"
+ " produces a combined in/out byte count < bytes.\n"
+ "--ping-exit n : Exit if n seconds pass without reception of remote ping.\n"
+ "--ping-restart n: Restart if n seconds pass without reception of remote ping.\n"
+ "--ping-timer-rem: Run the --ping-exit/--ping-restart timer only if we have a\n"
+ " remote address.\n"
+ "--ping n : Ping remote once every n seconds over TCP/UDP port.\n"
+#if ENABLE_IP_PKTINFO
+ "--multihome : Configure a multi-homed UDP server.\n"
+#endif
+ "--fast-io : (experimental) Optimize TUN/TAP/UDP writes.\n"
+ "--remap-usr1 s : On SIGUSR1 signals, remap signal (s='SIGHUP' or 'SIGTERM').\n"
+ "--persist-tun : Keep tun/tap device open across SIGUSR1 or --ping-restart.\n"
+ "--persist-remote-ip : Keep remote IP address across SIGUSR1 or --ping-restart.\n"
+ "--persist-local-ip : Keep local IP address across SIGUSR1 or --ping-restart.\n"
+ "--persist-key : Don't re-read key files across SIGUSR1 or --ping-restart.\n"
+#if PASSTOS_CAPABILITY
+ "--passtos : TOS passthrough (applies to IPv4 only).\n"
+#endif
+ "--tun-mtu n : Take the tun/tap device MTU to be n and derive the\n"
+ " TCP/UDP MTU from it (default=%d).\n"
+ "--tun-mtu-extra n : Assume that tun/tap device might return as many\n"
+ " as n bytes more than the tun-mtu size on read\n"
+ " (default TUN=0 TAP=%d).\n"
+ "--link-mtu n : Take the TCP/UDP device MTU to be n and derive the tun MTU\n"
+ " from it.\n"
+ "--mtu-disc type : Should we do Path MTU discovery on TCP/UDP channel?\n"
+ " 'no' -- Never send DF (Don't Fragment) frames\n"
+ " 'maybe' -- Use per-route hints\n"
+ " 'yes' -- Always DF (Don't Fragment)\n"
+#ifdef ENABLE_OCC
+ "--mtu-test : Empirically measure and report MTU.\n"
+#endif
+#ifdef ENABLE_FRAGMENT
+ "--fragment max : Enable internal datagram fragmentation so that no UDP\n"
+ " datagrams are sent which are larger than max bytes.\n"
+ " Adds 4 bytes of overhead per datagram.\n"
+#endif
+ "--mssfix [n] : Set upper bound on TCP MSS, default = tun-mtu size\n"
+ " or --fragment max value, whichever is lower.\n"
+ "--sndbuf size : Set the TCP/UDP send buffer size.\n"
+ "--rcvbuf size : Set the TCP/UDP receive buffer size.\n"
+ "--txqueuelen n : Set the tun/tap TX queue length to n (Linux only).\n"
+ "--mlock : Disable Paging -- ensures key material and tunnel\n"
+ " data will never be written to disk.\n"
+ "--up cmd : Shell cmd to execute after successful tun device open.\n"
+ " Execute as: cmd tun/tap-dev tun-mtu link-mtu \\\n"
+ " ifconfig-local-ip ifconfig-remote-ip\n"
+ " (pre --user or --group UID/GID change)\n"
+ "--up-delay : Delay tun/tap open and possible --up script execution\n"
+ " until after TCP/UDP connection establishment with peer.\n"
+ "--down cmd : Shell cmd to run after tun device close.\n"
+ " (post --user/--group UID/GID change and/or --chroot)\n"
+ " (script parameters are same as --up option)\n"
+ "--down-pre : Call --down cmd/script before TUN/TAP close.\n"
+ "--up-restart : Run up/down scripts for all restarts including those\n"
+ " caused by --ping-restart or SIGUSR1\n"
+ "--user user : Set UID to user after initialization.\n"
+ "--group group : Set GID to group after initialization.\n"
+ "--chroot dir : Chroot to this directory after initialization.\n"
+#ifdef HAVE_SETCON
+ "--setcon context: Apply this SELinux context after initialization.\n"
+#endif
+ "--cd dir : Change to this directory before initialization.\n"
+ "--daemon [name] : Become a daemon after initialization.\n"
+ " The optional 'name' parameter will be passed\n"
+ " as the program name to the system logger.\n"
+ "--syslog [name] : Output to syslog, but do not become a daemon.\n"
+ " See --daemon above for a description of the 'name' parm.\n"
+ "--inetd [name] ['wait'|'nowait'] : Run as an inetd or xinetd server.\n"
+ " See --daemon above for a description of the 'name' parm.\n"
+ "--log file : Output log to file which is created/truncated on open.\n"
+ "--log-append file : Append log to file, or create file if nonexistent.\n"
+ "--suppress-timestamps : Don't log timestamps to stdout/stderr.\n"
+ "--writepid file : Write main process ID to file.\n"
+ "--nice n : Change process priority (>0 = lower, <0 = higher).\n"
+ "--echo [parms ...] : Echo parameters to log output.\n"
+ "--verb n : Set output verbosity to n (default=%d):\n"
+ " (Level 3 is recommended if you want a good summary\n"
+ " of what's happening without being swamped by output).\n"
+ " : 0 -- no output except fatal errors\n"
+ " : 1 -- startup info + connection initiated messages +\n"
+ " non-fatal encryption & net errors\n"
+ " : 2,3 -- show TLS negotiations & route info\n"
+ " : 4 -- show parameters\n"
+ " : 5 -- show 'RrWw' chars on console for each packet sent\n"
+ " and received from TCP/UDP (caps) or tun/tap (lc)\n"
+ " : 6 to 11 -- debug messages of increasing verbosity\n"
+ "--mute n : Log at most n consecutive messages in the same category.\n"
+ "--status file n : Write operational status to file every n seconds.\n"
+ "--status-version [n] : Choose the status file format version number.\n"
+ " Currently, n can be 1, 2, or 3 (default=1).\n"
+#ifdef ENABLE_OCC
+ "--disable-occ : Disable options consistency check between peers.\n"
+#endif
+#ifdef ENABLE_DEBUG
+ "--gremlin mask : Special stress testing mode (for debugging only).\n"
+#endif
+#ifdef USE_LZO
+ "--comp-lzo : Use fast LZO compression -- may add up to 1 byte per\n"
+ " packet for uncompressible data.\n"
+ "--comp-noadapt : Don't use adaptive compression when --comp-lzo\n"
+ " is specified.\n"
+#endif
+#ifdef ENABLE_MANAGEMENT
+ "--management ip port [pass] : Enable a TCP server on ip:port to handle\n"
+ " management functions. pass is a password file\n"
+ " or 'stdin' to prompt from console.\n"
+#if UNIX_SOCK_SUPPORT
+ " To listen on a unix domain socket, specific the pathname\n"
+ " in place of ip and use 'unix' as the port number.\n"
+#endif
+ "--management-client : Management interface will connect as a TCP client to\n"
+ " ip/port rather than listen as a TCP server.\n"
+ "--management-query-passwords : Query management channel for private key\n"
+ " and auth-user-pass passwords.\n"
+ "--management-hold : Start " PACKAGE_NAME " in a hibernating state, until a client\n"
+ " of the management interface explicitly starts it.\n"
+ "--management-signal : Issue SIGUSR1 when management disconnect event occurs.\n"
+ "--management-forget-disconnect : Forget passwords when management disconnect\n"
+ " event occurs.\n"
+ "--management-log-cache n : Cache n lines of log file history for usage\n"
+ " by the management channel.\n"
+#if UNIX_SOCK_SUPPORT
+ "--management-client-user u : When management interface is a unix socket, only\n"
+ " allow connections from user u.\n"
+ "--management-client-group g : When management interface is a unix socket, only\n"
+ " allow connections from group g.\n"
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+ "--management-client-auth : gives management interface client the responsibility\n"
+ " to authenticate clients after their client certificate\n"
+ " has been verified.\n"
+#endif
+#ifdef MANAGEMENT_PF
+ "--management-client-pf : management interface clients must specify a packet\n"
+ " filter file for each connecting client.\n"
+#endif
+#endif
+#ifdef ENABLE_PLUGIN
+ "--plugin m [str]: Load plug-in module m passing str as an argument\n"
+ " to its initialization function.\n"
+#endif
+#if P2MP
+#if P2MP_SERVER
+ "\n"
+ "Multi-Client Server options (when --mode server is used):\n"
+ "--server network netmask : Helper option to easily configure server mode.\n"
+ "--server-bridge [IP netmask pool-start-IP pool-end-IP] : Helper option to\n"
+ " easily configure ethernet bridging server mode.\n"
+ "--push \"option\" : Push a config file option back to the peer for remote\n"
+ " execution. Peer must specify --pull in its config file.\n"
+ "--push-reset : Don't inherit global push list for specific\n"
+ " client instance.\n"
+ "--ifconfig-pool start-IP end-IP [netmask] : Set aside a pool of subnets\n"
+ " to be dynamically allocated to connecting clients.\n"
+ "--ifconfig-pool-linear : Use individual addresses rather than /30 subnets\n"
+ " in tun mode. Not compatible with Windows clients.\n"
+ "--ifconfig-pool-persist file [seconds] : Persist/unpersist ifconfig-pool\n"
+ " data to file, at seconds intervals (default=600).\n"
+ " If seconds=0, file will be treated as read-only.\n"
+ "--ifconfig-push local remote-netmask : Push an ifconfig option to remote,\n"
+ " overrides --ifconfig-pool dynamic allocation.\n"
+ " Only valid in a client-specific config file.\n"
+ "--iroute network [netmask] : Route subnet to client.\n"
+ " Sets up internal routes only.\n"
+ " Only valid in a client-specific config file.\n"
+ "--disable : Client is disabled.\n"
+ " Only valid in a client-specific config file.\n"
+ "--client-cert-not-required : Don't require client certificate, client\n"
+ " will authenticate using username/password.\n"
+ "--username-as-common-name : For auth-user-pass authentication, use\n"
+ " the authenticated username as the common name,\n"
+ " rather than the common name from the client cert.\n"
+ "--auth-user-pass-verify cmd method: Query client for username/password and\n"
+ " run script cmd to verify. If method='via-env', pass\n"
+ " user/pass via environment, if method='via-file', pass\n"
+ " user/pass via temporary file.\n"
+ "--opt-verify : Clients that connect with options that are incompatible\n"
+ " with those of the server will be disconnected.\n"
+ "--auth-user-pass-optional : Allow connections by clients that don't\n"
+ " specify a username/password.\n"
+ "--no-name-remapping : Allow Common Name and X509 Subject to include\n"
+ " any printable character.\n"
+ "--client-to-client : Internally route client-to-client traffic.\n"
+ "--duplicate-cn : Allow multiple clients with the same common name to\n"
+ " concurrently connect.\n"
+ "--client-connect cmd : Run script cmd on client connection.\n"
+ "--client-disconnect cmd : Run script cmd on client disconnection.\n"
+ "--client-config-dir dir : Directory for custom client config files.\n"
+ "--ccd-exclusive : Refuse connection unless custom client config is found.\n"
+ "--tmp-dir dir : Temporary directory, used for --client-connect return file and plugin communication.\n"
+ "--hash-size r v : Set the size of the real address hash table to r and the\n"
+ " virtual address table to v.\n"
+ "--bcast-buffers n : Allocate n broadcast buffers.\n"
+ "--tcp-queue-limit n : Maximum number of queued TCP output packets.\n"
+ "--tcp-nodelay : Macro that sets TCP_NODELAY socket flag on the server\n"
+ " as well as pushes it to connecting clients.\n"
+ "--learn-address cmd : Run script cmd to validate client virtual addresses.\n"
+ "--connect-freq n s : Allow a maximum of n new connections per s seconds.\n"
+ "--max-clients n : Allow a maximum of n simultaneously connected clients.\n"
+ "--max-routes-per-client n : Allow a maximum of n internal routes per client.\n"
+#if PORT_SHARE
+ "--port-share host port : When run in TCP mode, proxy incoming HTTPS sessions\n"
+ " to a web server at host:port.\n"
+#endif
+#endif
+ "\n"
+ "Client options (when connecting to a multi-client server):\n"
+ "--client : Helper option to easily configure client mode.\n"
+ "--auth-user-pass [up] : Authenticate with server using username/password.\n"
+ " up is a file containing username/password on 2 lines,\n"
+ " or omit to prompt from console.\n"
+ "--pull : Accept certain config file options from the peer as if they\n"
+ " were part of the local config file. Must be specified\n"
+ " when connecting to a '--mode server' remote host.\n"
+ "--auth-retry t : How to handle auth failures. Set t to\n"
+ " none (default), interact, or nointeract.\n"
+ "--server-poll-timeout n : when polling possible remote servers to connect to\n"
+ " in a round-robin fashion, spend no more than n seconds\n"
+ " waiting for a response before trying the next server.\n"
+#endif
+#ifdef ENABLE_OCC
+ "--explicit-exit-notify [n] : On exit/restart, send exit signal to\n"
+ " server/remote. n = # of retries, default=1.\n"
+#endif
+#ifdef USE_CRYPTO
+ "\n"
+ "Data Channel Encryption Options (must be compatible between peers):\n"
+ "(These options are meaningful for both Static Key & TLS-mode)\n"
+ "--secret f [d] : Enable Static Key encryption mode (non-TLS).\n"
+ " Use shared secret file f, generate with --genkey.\n"
+ " The optional d parameter controls key directionality.\n"
+ " If d is specified, use separate keys for each\n"
+ " direction, set d=0 on one side of the connection,\n"
+ " and d=1 on the other side.\n"
+ "--auth alg : Authenticate packets with HMAC using message\n"
+ " digest algorithm alg (default=%s).\n"
+ " (usually adds 16 or 20 bytes per packet)\n"
+ " Set alg=none to disable authentication.\n"
+ "--cipher alg : Encrypt packets with cipher algorithm alg\n"
+ " (default=%s).\n"
+ " Set alg=none to disable encryption.\n"
+ "--prng alg [nsl] : For PRNG, use digest algorithm alg, and\n"
+ " nonce_secret_len=nsl. Set alg=none to disable PRNG.\n"
+#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
+ "--keysize n : Size of cipher key in bits (optional).\n"
+ " If unspecified, defaults to cipher-specific default.\n"
+#endif
+ "--engine [name] : Enable OpenSSL hardware crypto engine functionality.\n"
+ "--no-replay : Disable replay protection.\n"
+ "--mute-replay-warnings : Silence the output of replay warnings to log file.\n"
+ "--replay-window n [t] : Use a replay protection sliding window of size n\n"
+ " and a time window of t seconds.\n"
+ " Default n=%d t=%d\n"
+ "--no-iv : Disable cipher IV -- only allowed with CBC mode ciphers.\n"
+ "--replay-persist file : Persist replay-protection state across sessions\n"
+ " using file.\n"
+ "--test-crypto : Run a self-test of crypto features enabled.\n"
+ " For debugging only.\n"
+#ifdef USE_SSL
+ "\n"
+ "TLS Key Negotiation Options:\n"
+ "(These options are meaningful only for TLS-mode)\n"
+ "--tls-server : Enable TLS and assume server role during TLS handshake.\n"
+ "--tls-client : Enable TLS and assume client role during TLS handshake.\n"
+ "--key-method m : Data channel key exchange method. m should be a method\n"
+ " number, such as 1 (default), 2, etc.\n"
+ "--ca file : Certificate authority file in .pem format containing\n"
+ " root certificate.\n"
+ "--capath dir : A directory of trusted certificates (CAs"
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ " and CRLs).\n"
+#else
+ ").\n"
+ " WARNING: no support of CRL available with this version.\n"
+#endif
+ "--dh file : File containing Diffie Hellman parameters\n"
+ " in .pem format (for --tls-server only).\n"
+ " Use \"openssl dhparam -out dh1024.pem 1024\" to generate.\n"
+ "--cert file : Local certificate in .pem format -- must be signed\n"
+ " by a Certificate Authority in --ca file.\n"
+ "--key file : Local private key in .pem format.\n"
+ "--pkcs12 file : PKCS#12 file containing local private key, local certificate\n"
+ " and optionally the root CA certificate.\n"
+#ifdef ENABLE_X509ALTUSERNAME
+ "--x509-username-field : Field used in x509 certificate to be username.\n"
+ " Default is CN.\n"
+#endif
+#ifdef WIN32
+ "--cryptoapicert select-string : Load the certificate and private key from the\n"
+ " Windows Certificate System Store.\n"
+#endif
+ "--tls-cipher l : A list l of allowable TLS ciphers separated by : (optional).\n"
+ " : Use --show-tls to see a list of supported TLS ciphers.\n"
+ "--tls-timeout n : Packet retransmit timeout on TLS control channel\n"
+ " if no ACK from remote within n seconds (default=%d).\n"
+ "--reneg-bytes n : Renegotiate data chan. key after n bytes sent and recvd.\n"
+ "--reneg-pkts n : Renegotiate data chan. key after n packets sent and recvd.\n"
+ "--reneg-sec n : Renegotiate data chan. key after n seconds (default=%d).\n"
+ "--hand-window n : Data channel key exchange must finalize within n seconds\n"
+ " of handshake initiation by any peer (default=%d).\n"
+ "--tran-window n : Transition window -- old key can live this many seconds\n"
+ " after new key renegotiation begins (default=%d).\n"
+ "--single-session: Allow only one session (reset state on restart).\n"
+ "--tls-exit : Exit on TLS negotiation failure.\n"
+ "--tls-auth f [d]: Add an additional layer of authentication on top of the TLS\n"
+ " control channel to protect against DoS attacks.\n"
+ " f (required) is a shared-secret passphrase file.\n"
+ " The optional d parameter controls key directionality,\n"
+ " see --secret option for more info.\n"
+ "--askpass [file]: Get PEM password from controlling tty before we daemonize.\n"
+ "--auth-nocache : Don't cache --askpass or --auth-user-pass passwords.\n"
+ "--crl-verify crl: Check peer certificate against a CRL.\n"
+ "--tls-verify cmd: Execute shell command cmd to verify the X509 name of a\n"
+ " pending TLS connection that has otherwise passed all other\n"
+ " tests of certification. cmd should return 0 to allow\n"
+ " TLS handshake to proceed, or 1 to fail. (cmd is\n"
+ " executed as 'cmd certificate_depth X509_NAME_oneline')\n"
+ "--tls-export-cert [directory] : Get peer cert in PEM format and store it \n"
+ " in an openvpn temporary file in [directory]. Peer cert is \n"
+ " stored before tls-verify script execution and deleted after.\n"
+ "--tls-remote x509name: Accept connections only from a host with X509 name\n"
+ " x509name. The remote host must also pass all other tests\n"
+ " of verification.\n"
+ "--ns-cert-type t: Require that peer certificate was signed with an explicit\n"
+ " nsCertType designation t = 'client' | 'server'.\n"
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ "--remote-cert-ku v ... : Require that the peer certificate was signed with\n"
+ " explicit key usage, you can specify more than one value.\n"
+ " value should be given in hex format.\n"
+ "--remote-cert-eku oid : Require that the peer certificate was signed with\n"
+ " explicit extended key usage. Extended key usage can be encoded\n"
+ " as an object identifier or OpenSSL string representation.\n"
+ "--remote-cert-tls t: Require that peer certificate was signed with explicit\n"
+ " key usage and extended key usage based on RFC3280 TLS rules.\n"
+ " t = 'client' | 'server'.\n"
+#endif /* OPENSSL_VERSION_NUMBER */
+#endif /* USE_SSL */
+#ifdef ENABLE_PKCS11
+ "\n"
+ "PKCS#11 Options:\n"
+ "--pkcs11-providers provider ... : PKCS#11 provider to load.\n"
+ "--pkcs11-protected-authentication [0|1] ... : Use PKCS#11 protected authentication\n"
+ " path. Set for each provider.\n"
+ "--pkcs11-private-mode hex ... : PKCS#11 private key mode mask.\n"
+ " 0 : Try to determind automatically (default).\n"
+ " 1 : Use Sign.\n"
+ " 2 : Use SignRecover.\n"
+ " 4 : Use Decrypt.\n"
+ " 8 : Use Unwrap.\n"
+ "--pkcs11-cert-private [0|1] ... : Set if login should be performed before\n"
+ " certificate can be accessed. Set for each provider.\n"
+ "--pkcs11-pin-cache seconds : Number of seconds to cache PIN. The default is -1\n"
+ " cache until token is removed.\n"
+ "--pkcs11-id-management : Acquire identity from management interface.\n"
+ "--pkcs11-id serialized-id 'id' : Identity to use, get using standalone --show-pkcs11-ids\n"
+#endif /* ENABLE_PKCS11 */
+ "\n"
+ "SSL Library information:\n"
+ "--show-ciphers : Show cipher algorithms to use with --cipher option.\n"
+ "--show-digests : Show message digest algorithms to use with --auth option.\n"
+ "--show-engines : Show hardware crypto accelerator engines (if available).\n"
+#ifdef USE_SSL
+ "--show-tls : Show all TLS ciphers (TLS used only as a control channel).\n"
+#endif
+#ifdef WIN32
+ "\n"
+ "Windows Specific:\n"
+ "--win-sys path|'env' : Pathname of Windows system directory, C:\\WINDOWS by default.\n"
+ " If specified as 'env', read the pathname from SystemRoot env var.\n"
+ "--ip-win32 method : When using --ifconfig on Windows, set TAP-Win32 adapter\n"
+ " IP address using method = manual, netsh, ipapi,\n"
+ " dynamic, or adaptive (default = adaptive).\n"
+ " Dynamic method allows two optional parameters:\n"
+ " offset: DHCP server address offset (> -256 and < 256).\n"
+ " If 0, use network address, if >0, take nth\n"
+ " address forward from network address, if <0,\n"
+ " take nth address backward from broadcast\n"
+ " address.\n"
+ " Default is 0.\n"
+ " lease-time: Lease time in seconds.\n"
+ " Default is one year.\n"
+ "--route-method : Which method to use for adding routes on Windows?\n"
+ " adaptive (default) -- Try ipapi then fall back to exe.\n"
+ " ipapi -- Use IP helper API.\n"
+ " exe -- Call the route.exe shell command.\n"
+ "--dhcp-option type [parm] : Set extended TAP-Win32 properties, must\n"
+ " be used with --ip-win32 dynamic. For options\n"
+ " which allow multiple addresses,\n"
+ " --dhcp-option must be repeated.\n"
+ " DOMAIN name : Set DNS suffix\n"
+ " DNS addr : Set domain name server address(es)\n"
+ " NTP : Set NTP server address(es)\n"
+ " NBDD : Set NBDD server address(es)\n"
+ " WINS addr : Set WINS server address(es)\n"
+ " NBT type : Set NetBIOS over TCP/IP Node type\n"
+ " 1: B, 2: P, 4: M, 8: H\n"
+ " NBS id : Set NetBIOS scope ID\n"
+ " DISABLE-NBT : Disable Netbios-over-TCP/IP.\n"
+ "--dhcp-renew : Ask Windows to renew the TAP adapter lease on startup.\n"
+ "--dhcp-pre-release : Ask Windows to release the previous TAP adapter lease on\n"
+" startup.\n"
+ "--dhcp-release : Ask Windows to release the TAP adapter lease on shutdown.\n"
+ "--register-dns : Run net stop dnscache, net start dnscache, ipconfig /flushdns\n"
+ " and ipconfig /registerdns on connection initiation.\n"
+ "--tap-sleep n : Sleep for n seconds after TAP adapter open before\n"
+ " attempting to set adapter properties.\n"
+ "--pause-exit : When run from a console window, pause before exiting.\n"
+ "--service ex [0|1] : For use when " PACKAGE_NAME " is being instantiated by a\n"
+ " service, and should not be used directly by end-users.\n"
+ " ex is the name of an event object which, when\n"
+ " signaled, will cause " PACKAGE_NAME " to exit. A second\n"
+ " optional parameter controls the initial state of ex.\n"
+ "--show-net-up : Show " PACKAGE_NAME "'s view of routing table and net adapter list\n"
+ " after TAP adapter is up and routes have been added.\n"
+ "Windows Standalone Options:\n"
+ "\n"
+ "--show-adapters : Show all TAP-Win32 adapters.\n"
+ "--show-net : Show " PACKAGE_NAME "'s view of routing table and net adapter list.\n"
+ "--show-valid-subnets : Show valid subnets for --dev tun emulation.\n"
+ "--allow-nonadmin [TAP-adapter] : Allow " PACKAGE_NAME " running without admin privileges\n"
+ " to access TAP adapter.\n"
+#endif
+ "\n"
+ "Generate a random key (only for non-TLS static key encryption mode):\n"
+ "--genkey : Generate a random key to be used as a shared secret,\n"
+ " for use with the --secret option.\n"
+ "--secret file : Write key to file.\n"
+#endif /* USE_CRYPTO */
+#ifdef TUNSETPERSIST
+ "\n"
+ "Tun/tap config mode (available with linux 2.4+):\n"
+ "--mktun : Create a persistent tunnel.\n"
+ "--rmtun : Remove a persistent tunnel.\n"
+ "--dev tunX|tapX : tun/tap device\n"
+ "--dev-type dt : Device type. See tunnel options above for details.\n"
+ "--user user : User to set privilege to.\n"
+ "--group group : Group to set privilege to.\n"
+#endif
+#ifdef ENABLE_PKCS11
+ "\n"
+ "PKCS#11 standalone options:\n"
+ "--show-pkcs11-ids provider [cert_private] : Show PKCS#11 available ids.\n"
+ " --verb option can be added *BEFORE* this.\n"
+#endif /* ENABLE_PKCS11 */
+ ;
+
+#endif /* !ENABLE_SMALL */
+
+/*
+ * This is where the options defaults go.
+ * Any option not explicitly set here
+ * will be set to 0.
+ */
+void
+init_options (struct options *o, const bool init_gc)
+{
+ CLEAR (*o);
+ if (init_gc)
+ {
+ gc_init (&o->gc);
+ o->gc_owned = true;
+ }
+ o->mode = MODE_POINT_TO_POINT;
+ o->topology = TOP_NET30;
+ o->ce.proto = PROTO_UDPv4;
+ o->ce.connect_retry_seconds = 5;
+ o->ce.connect_timeout = 10;
+ o->ce.connect_retry_max = 0;
+ o->ce.local_port = o->ce.remote_port = OPENVPN_PORT;
+ o->verbosity = 1;
+ o->status_file_update_freq = 60;
+ o->status_file_version = 1;
+ o->ce.bind_local = true;
+ o->tun_mtu = TUN_MTU_DEFAULT;
+ o->link_mtu = LINK_MTU_DEFAULT;
+ o->mtu_discover_type = -1;
+ o->mssfix = MSSFIX_DEFAULT;
+ o->route_delay_window = 30;
+ o->max_routes = MAX_ROUTES_DEFAULT;
+ o->resolve_retry_seconds = RESOLV_RETRY_INFINITE;
+ o->proto_force = -1;
+#ifdef ENABLE_OCC
+ o->occ = true;
+#endif
+#ifdef ENABLE_MANAGEMENT
+ o->management_log_history_cache = 250;
+ o->management_echo_buffer_size = 100;
+ o->management_state_buffer_size = 100;
+#endif
+#ifdef TUNSETPERSIST
+ o->persist_mode = 1;
+#endif
+#ifndef WIN32
+ o->rcvbuf = 65536;
+ o->sndbuf = 65536;
+#endif
+#ifdef TARGET_LINUX
+ o->tuntap_options.txqueuelen = 100;
+#endif
+#ifdef WIN32
+#if 0
+ o->tuntap_options.ip_win32_type = IPW32_SET_ADAPTIVE;
+#else
+ o->tuntap_options.ip_win32_type = IPW32_SET_DHCP_MASQ;
+#endif
+ o->tuntap_options.dhcp_lease_time = 31536000; /* one year */
+ o->tuntap_options.dhcp_masq_offset = 0; /* use network address as internal DHCP server address */
+ o->route_method = ROUTE_METHOD_ADAPTIVE;
+#endif
+#if P2MP_SERVER
+ o->real_hash_size = 256;
+ o->virtual_hash_size = 256;
+ o->n_bcast_buf = 256;
+ o->tcp_queue_limit = 64;
+ o->max_clients = 1024;
+ o->max_routes_per_client = 256;
+ o->ifconfig_pool_persist_refresh_freq = 600;
+#endif
+#if P2MP
+ o->scheduled_exit_interval = 5;
+ o->server_poll_timeout = 0;
+#endif
+#ifdef USE_CRYPTO
+ o->ciphername = "BF-CBC";
+ o->ciphername_defined = true;
+ o->authname = "SHA1";
+ o->authname_defined = true;
+ o->prng_hash = "SHA1";
+ o->prng_nonce_secret_len = 16;
+ o->replay = true;
+ o->replay_window = DEFAULT_SEQ_BACKTRACK;
+ o->replay_time = DEFAULT_TIME_BACKTRACK;
+ o->use_iv = true;
+ o->key_direction = KEY_DIRECTION_BIDIRECTIONAL;
+#ifdef USE_SSL
+ o->key_method = 2;
+ o->tls_timeout = 2;
+ o->renegotiate_seconds = 3600;
+ o->handshake_window = 60;
+ o->transition_window = 3600;
+#ifdef ENABLE_X509ALTUSERNAME
+ o->x509_username_field = X509_USERNAME_FIELD_DEFAULT;
+#endif
+#endif /* USE_SSL */
+#endif /* USE_CRYPTO */
+#ifdef ENABLE_PKCS11
+ o->pkcs11_pin_cache_period = -1;
+#endif /* ENABLE_PKCS11 */
+
+ /* Set default --tmp-dir */
+#ifdef WIN32
+ /* On Windows, find temp dir via enviroment variables */
+ o->tmp_dir = win_get_tempdir();
+#else
+ /* Non-windows platforms use $TMPDIR, and if not set, default to '/tmp' */
+ o->tmp_dir = getenv("TMPDIR");
+ if( !o->tmp_dir ) {
+ o->tmp_dir = "/tmp";
+ }
+#endif /* WIN32 */
+}
+
+void
+uninit_options (struct options *o)
+{
+ if (o->gc_owned)
+ {
+ gc_free (&o->gc);
+ }
+}
+
+#ifdef ENABLE_DEBUG
+
+#define SHOW_PARM(name, value, format) msg(D_SHOW_PARMS, " " #name " = " format, (value))
+#define SHOW_STR(var) SHOW_PARM(var, (o->var ? o->var : "[UNDEF]"), "'%s'")
+#define SHOW_INT(var) SHOW_PARM(var, o->var, "%d")
+#define SHOW_UINT(var) SHOW_PARM(var, o->var, "%u")
+#define SHOW_UNSIGNED(var) SHOW_PARM(var, o->var, "0x%08x")
+#define SHOW_BOOL(var) SHOW_PARM(var, (o->var ? "ENABLED" : "DISABLED"), "%s");
+
+#endif
+
+void
+setenv_connection_entry (struct env_set *es,
+ const struct connection_entry *e,
+ const int i)
+{
+ setenv_str_i (es, "proto", proto2ascii (e->proto, false), i);
+ setenv_str_i (es, "local", e->local, i);
+ setenv_int_i (es, "local_port", e->local_port, i);
+ setenv_str_i (es, "remote", e->remote, i);
+ setenv_int_i (es, "remote_port", e->remote_port, i);
+
+#ifdef ENABLE_HTTP_PROXY
+ if (e->http_proxy_options)
+ {
+ setenv_str_i (es, "http_proxy_server", e->http_proxy_options->server, i);
+ setenv_int_i (es, "http_proxy_port", e->http_proxy_options->port, i);
+ }
+#endif
+#ifdef ENABLE_SOCKS
+ if (e->socks_proxy_server)
+ {
+ setenv_str_i (es, "socks_proxy_server", e->socks_proxy_server, i);
+ setenv_int_i (es, "socks_proxy_port", e->socks_proxy_port, i);
+ }
+#endif
+}
+
+void
+setenv_settings (struct env_set *es, const struct options *o)
+{
+ setenv_str (es, "config", o->config);
+ setenv_int (es, "verb", o->verbosity);
+ setenv_int (es, "daemon", o->daemon);
+ setenv_int (es, "daemon_log_redirect", o->log);
+ setenv_unsigned (es, "daemon_start_time", time(NULL));
+ setenv_int (es, "daemon_pid", openvpn_getpid());
+
+#ifdef ENABLE_CONNECTION
+ if (o->connection_list)
+ {
+ int i;
+ for (i = 0; i < o->connection_list->len; ++i)
+ setenv_connection_entry (es, o->connection_list->array[i], i+1);
+ }
+ else
+#endif
+ setenv_connection_entry (es, &o->ce, 1);
+}
+
+static in_addr_t
+get_ip_addr (const char *ip_string, int msglevel, bool *error)
+{
+ unsigned int flags = GETADDR_HOST_ORDER;
+ bool succeeded = false;
+ in_addr_t ret;
+
+ if (msglevel & M_FATAL)
+ flags |= GETADDR_FATAL;
+
+ ret = getaddr (flags, ip_string, 0, &succeeded, NULL);
+ if (!succeeded && error)
+ *error = true;
+ return ret;
+}
+
+static char *
+string_substitute (const char *src, int from, int to, struct gc_arena *gc)
+{
+ char *ret = (char *) gc_malloc (strlen (src) + 1, true, gc);
+ char *dest = ret;
+ char c;
+
+ do
+ {
+ c = *src++;
+ if (c == from)
+ c = to;
+ *dest++ = c;
+ }
+ while (c);
+ return ret;
+}
+
+bool
+is_persist_option (const struct options *o)
+{
+ return o->persist_tun
+ || o->persist_key
+ || o->persist_local_ip
+ || o->persist_remote_ip
+ ;
+}
+
+bool
+is_stateful_restart (const struct options *o)
+{
+ return is_persist_option (o) || connection_list_defined (o);
+}
+
+#ifdef WIN32
+
+#ifdef ENABLE_DEBUG
+
+static void
+show_dhcp_option_addrs (const char *name, const in_addr_t *array, int len)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ for (i = 0; i < len; ++i)
+ {
+ msg (D_SHOW_PARMS, " %s[%d] = %s",
+ name,
+ i,
+ print_in_addr_t (array[i], 0, &gc));
+ }
+ gc_free (&gc);
+}
+
+static void
+show_tuntap_options (const struct tuntap_options *o)
+{
+ SHOW_BOOL (ip_win32_defined);
+ SHOW_INT (ip_win32_type);
+ SHOW_INT (dhcp_masq_offset);
+ SHOW_INT (dhcp_lease_time);
+ SHOW_INT (tap_sleep);
+ SHOW_BOOL (dhcp_options);
+ SHOW_BOOL (dhcp_renew);
+ SHOW_BOOL (dhcp_pre_release);
+ SHOW_BOOL (dhcp_release);
+ SHOW_STR (domain);
+ SHOW_STR (netbios_scope);
+ SHOW_INT (netbios_node_type);
+ SHOW_BOOL (disable_nbt);
+
+ show_dhcp_option_addrs ("DNS", o->dns, o->dns_len);
+ show_dhcp_option_addrs ("WINS", o->wins, o->wins_len);
+ show_dhcp_option_addrs ("NTP", o->ntp, o->ntp_len);
+ show_dhcp_option_addrs ("NBDD", o->nbdd, o->nbdd_len);
+}
+
+#endif
+
+static void
+dhcp_option_address_parse (const char *name, const char *parm, in_addr_t *array, int *len, int msglevel)
+{
+ if (*len >= N_DHCP_ADDR)
+ {
+ msg (msglevel, "--dhcp-option %s: maximum of %d %s servers can be specified",
+ name,
+ N_DHCP_ADDR,
+ name);
+ }
+ else
+ {
+ if (ip_addr_dotted_quad_safe (parm)) /* FQDN -- IP address only */
+ {
+ bool error = false;
+ const in_addr_t addr = get_ip_addr (parm, msglevel, &error);
+ if (!error)
+ array[(*len)++] = addr;
+ }
+ else
+ {
+ msg (msglevel, "dhcp-option parameter %s '%s' must be an IP address", name, parm);
+ }
+ }
+}
+
+#endif
+
+#if P2MP
+
+#ifdef ENABLE_DEBUG
+
+static void
+show_p2mp_parms (const struct options *o)
+{
+ struct gc_arena gc = gc_new ();
+
+#if P2MP_SERVER
+ msg (D_SHOW_PARMS, " server_network = %s", print_in_addr_t (o->server_network, 0, &gc));
+ msg (D_SHOW_PARMS, " server_netmask = %s", print_in_addr_t (o->server_netmask, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_ip = %s", print_in_addr_t (o->server_bridge_ip, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_netmask = %s", print_in_addr_t (o->server_bridge_netmask, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_pool_start = %s", print_in_addr_t (o->server_bridge_pool_start, 0, &gc));
+ msg (D_SHOW_PARMS, " server_bridge_pool_end = %s", print_in_addr_t (o->server_bridge_pool_end, 0, &gc));
+ if (o->push_list.head)
+ {
+ const struct push_entry *e = o->push_list.head;
+ while (e)
+ {
+ if (e->enable)
+ msg (D_SHOW_PARMS, " push_entry = '%s'", e->option);
+ e = e->next;
+ }
+ }
+ SHOW_BOOL (ifconfig_pool_defined);
+ msg (D_SHOW_PARMS, " ifconfig_pool_start = %s", print_in_addr_t (o->ifconfig_pool_start, 0, &gc));
+ msg (D_SHOW_PARMS, " ifconfig_pool_end = %s", print_in_addr_t (o->ifconfig_pool_end, 0, &gc));
+ msg (D_SHOW_PARMS, " ifconfig_pool_netmask = %s", print_in_addr_t (o->ifconfig_pool_netmask, 0, &gc));
+ SHOW_STR (ifconfig_pool_persist_filename);
+ SHOW_INT (ifconfig_pool_persist_refresh_freq);
+ SHOW_INT (n_bcast_buf);
+ SHOW_INT (tcp_queue_limit);
+ SHOW_INT (real_hash_size);
+ SHOW_INT (virtual_hash_size);
+ SHOW_STR (client_connect_script);
+ SHOW_STR (learn_address_script);
+ SHOW_STR (client_disconnect_script);
+ SHOW_STR (client_config_dir);
+ SHOW_BOOL (ccd_exclusive);
+ SHOW_STR (tmp_dir);
+ SHOW_BOOL (push_ifconfig_defined);
+ msg (D_SHOW_PARMS, " push_ifconfig_local = %s", print_in_addr_t (o->push_ifconfig_local, 0, &gc));
+ msg (D_SHOW_PARMS, " push_ifconfig_remote_netmask = %s", print_in_addr_t (o->push_ifconfig_remote_netmask, 0, &gc));
+ SHOW_BOOL (enable_c2c);
+ SHOW_BOOL (duplicate_cn);
+ SHOW_INT (cf_max);
+ SHOW_INT (cf_per);
+ SHOW_INT (max_clients);
+ SHOW_INT (max_routes_per_client);
+ SHOW_STR (auth_user_pass_verify_script);
+ SHOW_BOOL (auth_user_pass_verify_script_via_file);
+ SHOW_INT (ssl_flags);
+#if PORT_SHARE
+ SHOW_STR (port_share_host);
+ SHOW_INT (port_share_port);
+#endif
+#endif /* P2MP_SERVER */
+
+ SHOW_BOOL (client);
+ SHOW_BOOL (pull);
+ SHOW_STR (auth_user_pass_file);
+
+ gc_free (&gc);
+}
+
+#endif /* ENABLE_DEBUG */
+
+#if P2MP_SERVER
+
+static void
+option_iroute (struct options *o,
+ const char *network_str,
+ const char *netmask_str,
+ int msglevel)
+{
+ struct iroute *ir;
+
+ ALLOC_OBJ_GC (ir, struct iroute, &o->gc);
+ ir->network = getaddr (GETADDR_HOST_ORDER, network_str, 0, NULL, NULL);
+ ir->netbits = -1;
+
+ if (netmask_str)
+ {
+ const in_addr_t netmask = getaddr (GETADDR_HOST_ORDER, netmask_str, 0, NULL, NULL);
+ if (!netmask_to_netbits (ir->network, netmask, &ir->netbits))
+ {
+ msg (msglevel, "in --iroute %s %s : Bad network/subnet specification",
+ network_str,
+ netmask_str);
+ return;
+ }
+ }
+
+ ir->next = o->iroutes;
+ o->iroutes = ir;
+}
+
+#endif /* P2MP_SERVER */
+#endif /* P2MP */
+
+#if defined(ENABLE_HTTP_PROXY) && defined(ENABLE_DEBUG)
+static void
+show_http_proxy_options (const struct http_proxy_options *o)
+{
+ msg (D_SHOW_PARMS, "BEGIN http_proxy");
+ SHOW_STR (server);
+ SHOW_INT (port);
+ SHOW_STR (auth_method_string);
+ SHOW_STR (auth_file);
+ SHOW_BOOL (retry);
+ SHOW_INT (timeout);
+ SHOW_STR (http_version);
+ SHOW_STR (user_agent);
+ msg (D_SHOW_PARMS, "END http_proxy");
+}
+#endif
+
+void
+options_detach (struct options *o)
+{
+ gc_detach (&o->gc);
+ o->routes = NULL;
+#if P2MP_SERVER
+ clone_push_list(o);
+#endif
+}
+
+void
+rol_check_alloc (struct options *options)
+{
+ if (!options->routes)
+ options->routes = new_route_option_list (options->max_routes, &options->gc);
+}
+
+#ifdef ENABLE_DEBUG
+static void
+show_connection_entry (const struct connection_entry *o)
+{
+ msg (D_SHOW_PARMS, " proto = %s", proto2ascii (o->proto, false));
+ SHOW_STR (local);
+ SHOW_INT (local_port);
+ SHOW_STR (remote);
+ SHOW_INT (remote_port);
+ SHOW_BOOL (remote_float);
+ SHOW_BOOL (bind_defined);
+ SHOW_BOOL (bind_local);
+ SHOW_INT (connect_retry_seconds);
+ SHOW_INT (connect_timeout);
+ SHOW_INT (connect_retry_max);
+
+#ifdef ENABLE_HTTP_PROXY
+ if (o->http_proxy_options)
+ show_http_proxy_options (o->http_proxy_options);
+#endif
+#ifdef ENABLE_SOCKS
+ SHOW_STR (socks_proxy_server);
+ SHOW_INT (socks_proxy_port);
+ SHOW_BOOL (socks_proxy_retry);
+#endif
+}
+
+static void
+show_connection_entries (const struct options *o)
+{
+ msg (D_SHOW_PARMS, "Connection profiles [default]:");
+ show_connection_entry (&o->ce);
+#ifdef ENABLE_CONNECTION
+ if (o->connection_list)
+ {
+ const struct connection_list *l = o->connection_list;
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ msg (D_SHOW_PARMS, "Connection profiles [%d]:", i);
+ show_connection_entry (l->array[i]);
+ }
+ }
+#endif
+ msg (D_SHOW_PARMS, "Connection profiles END");
+}
+
+#endif
+
+void
+show_settings (const struct options *o)
+{
+#ifdef ENABLE_DEBUG
+ msg (D_SHOW_PARMS, "Current Parameter Settings:");
+
+ SHOW_STR (config);
+
+ SHOW_INT (mode);
+
+#ifdef TUNSETPERSIST
+ SHOW_BOOL (persist_config);
+ SHOW_INT (persist_mode);
+#endif
+
+#ifdef USE_CRYPTO
+ SHOW_BOOL (show_ciphers);
+ SHOW_BOOL (show_digests);
+ SHOW_BOOL (show_engines);
+ SHOW_BOOL (genkey);
+#ifdef USE_SSL
+ SHOW_STR (key_pass_file);
+ SHOW_BOOL (show_tls_ciphers);
+#endif
+#endif
+
+ show_connection_entries (o);
+
+ SHOW_BOOL (remote_random);
+
+ SHOW_STR (ipchange);
+ SHOW_STR (dev);
+ SHOW_STR (dev_type);
+ SHOW_STR (dev_node);
+ SHOW_STR (lladdr);
+ SHOW_INT (topology);
+ SHOW_BOOL (tun_ipv6);
+ SHOW_STR (ifconfig_local);
+ SHOW_STR (ifconfig_remote_netmask);
+ SHOW_BOOL (ifconfig_noexec);
+ SHOW_BOOL (ifconfig_nowarn);
+
+#ifdef HAVE_GETTIMEOFDAY
+ SHOW_INT (shaper);
+#endif
+ SHOW_INT (tun_mtu);
+ SHOW_BOOL (tun_mtu_defined);
+ SHOW_INT (link_mtu);
+ SHOW_BOOL (link_mtu_defined);
+ SHOW_INT (tun_mtu_extra);
+ SHOW_BOOL (tun_mtu_extra_defined);
+
+#ifdef ENABLE_FRAGMENT
+ SHOW_INT (fragment);
+#endif
+
+ SHOW_INT (mtu_discover_type);
+
+#ifdef ENABLE_OCC
+ SHOW_INT (mtu_test);
+#endif
+
+ SHOW_BOOL (mlock);
+
+ SHOW_INT (keepalive_ping);
+ SHOW_INT (keepalive_timeout);
+ SHOW_INT (inactivity_timeout);
+ SHOW_INT (ping_send_timeout);
+ SHOW_INT (ping_rec_timeout);
+ SHOW_INT (ping_rec_timeout_action);
+ SHOW_BOOL (ping_timer_remote);
+ SHOW_INT (remap_sigusr1);
+#ifdef ENABLE_OCC
+ SHOW_INT (explicit_exit_notification);
+#endif
+ SHOW_BOOL (persist_tun);
+ SHOW_BOOL (persist_local_ip);
+ SHOW_BOOL (persist_remote_ip);
+ SHOW_BOOL (persist_key);
+
+ SHOW_INT (mssfix);
+
+#if PASSTOS_CAPABILITY
+ SHOW_BOOL (passtos);
+#endif
+
+ SHOW_INT (resolve_retry_seconds);
+
+ SHOW_STR (username);
+ SHOW_STR (groupname);
+ SHOW_STR (chroot_dir);
+ SHOW_STR (cd_dir);
+#ifdef HAVE_SETCON
+ SHOW_STR (selinux_context);
+#endif
+ SHOW_STR (writepid);
+ SHOW_STR (up_script);
+ SHOW_STR (down_script);
+ SHOW_BOOL (down_pre);
+ SHOW_BOOL (up_restart);
+ SHOW_BOOL (up_delay);
+ SHOW_BOOL (daemon);
+ SHOW_INT (inetd);
+ SHOW_BOOL (log);
+ SHOW_BOOL (suppress_timestamps);
+ SHOW_INT (nice);
+ SHOW_INT (verbosity);
+ SHOW_INT (mute);
+#ifdef ENABLE_DEBUG
+ SHOW_INT (gremlin);
+#endif
+ SHOW_STR (status_file);
+ SHOW_INT (status_file_version);
+ SHOW_INT (status_file_update_freq);
+
+#ifdef ENABLE_OCC
+ SHOW_BOOL (occ);
+#endif
+ SHOW_INT (rcvbuf);
+ SHOW_INT (sndbuf);
+ SHOW_INT (sockflags);
+
+ SHOW_BOOL (fast_io);
+
+#ifdef USE_LZO
+ SHOW_INT (lzo);
+#endif
+
+ SHOW_STR (route_script);
+ SHOW_STR (route_default_gateway);
+ SHOW_INT (route_default_metric);
+ SHOW_BOOL (route_noexec);
+ SHOW_INT (route_delay);
+ SHOW_INT (route_delay_window);
+ SHOW_BOOL (route_delay_defined);
+ SHOW_BOOL (route_nopull);
+ SHOW_BOOL (route_gateway_via_dhcp);
+ SHOW_INT (max_routes);
+ SHOW_BOOL (allow_pull_fqdn);
+ if (o->routes)
+ print_route_options (o->routes, D_SHOW_PARMS);
+
+#ifdef ENABLE_MANAGEMENT
+ SHOW_STR (management_addr);
+ SHOW_INT (management_port);
+ SHOW_STR (management_user_pass);
+ SHOW_INT (management_log_history_cache);
+ SHOW_INT (management_echo_buffer_size);
+ SHOW_STR (management_write_peer_info_file);
+ SHOW_STR (management_client_user);
+ SHOW_STR (management_client_group);
+ SHOW_INT (management_flags);
+#endif
+#ifdef ENABLE_PLUGIN
+ if (o->plugin_list)
+ plugin_option_list_print (o->plugin_list, D_SHOW_PARMS);
+#endif
+
+#ifdef USE_CRYPTO
+ SHOW_STR (shared_secret_file);
+ SHOW_INT (key_direction);
+ SHOW_BOOL (ciphername_defined);
+ SHOW_STR (ciphername);
+ SHOW_BOOL (authname_defined);
+ SHOW_STR (authname);
+ SHOW_STR (prng_hash);
+ SHOW_INT (prng_nonce_secret_len);
+ SHOW_INT (keysize);
+ SHOW_BOOL (engine);
+ SHOW_BOOL (replay);
+ SHOW_BOOL (mute_replay_warnings);
+ SHOW_INT (replay_window);
+ SHOW_INT (replay_time);
+ SHOW_STR (packet_id_file);
+ SHOW_BOOL (use_iv);
+ SHOW_BOOL (test_crypto);
+
+#ifdef USE_SSL
+ SHOW_BOOL (tls_server);
+ SHOW_BOOL (tls_client);
+ SHOW_INT (key_method);
+ SHOW_STR (ca_file);
+ SHOW_STR (ca_path);
+ SHOW_STR (dh_file);
+ SHOW_STR (cert_file);
+ SHOW_STR (priv_key_file);
+ SHOW_STR (pkcs12_file);
+#ifdef WIN32
+ SHOW_STR (cryptoapi_cert);
+#endif
+ SHOW_STR (cipher_list);
+ SHOW_STR (tls_verify);
+ SHOW_STR (tls_export_cert);
+ SHOW_STR (tls_remote);
+ SHOW_STR (crl_file);
+ SHOW_INT (ns_cert_type);
+ {
+ int i;
+ for (i=0;i<MAX_PARMS;i++)
+ SHOW_INT (remote_cert_ku[i]);
+ }
+ SHOW_STR (remote_cert_eku);
+
+ SHOW_INT (tls_timeout);
+
+ SHOW_INT (renegotiate_bytes);
+ SHOW_INT (renegotiate_packets);
+ SHOW_INT (renegotiate_seconds);
+
+ SHOW_INT (handshake_window);
+ SHOW_INT (transition_window);
+
+ SHOW_BOOL (single_session);
+#ifdef ENABLE_PUSH_PEER_INFO
+ SHOW_BOOL (push_peer_info);
+#endif
+ SHOW_BOOL (tls_exit);
+
+ SHOW_STR (tls_auth_file);
+#endif
+#endif
+
+#ifdef ENABLE_PKCS11
+ {
+ int i;
+ for (i=0;i<MAX_PARMS && o->pkcs11_providers[i] != NULL;i++)
+ SHOW_PARM (pkcs11_providers, o->pkcs11_providers[i], "%s");
+ }
+ {
+ int i;
+ for (i=0;i<MAX_PARMS;i++)
+ SHOW_PARM (pkcs11_protected_authentication, o->pkcs11_protected_authentication[i] ? "ENABLED" : "DISABLED", "%s");
+ }
+ {
+ int i;
+ for (i=0;i<MAX_PARMS;i++)
+ SHOW_PARM (pkcs11_private_mode, o->pkcs11_private_mode[i], "%08x");
+ }
+ {
+ int i;
+ for (i=0;i<MAX_PARMS;i++)
+ SHOW_PARM (pkcs11_cert_private, o->pkcs11_cert_private[i] ? "ENABLED" : "DISABLED", "%s");
+ }
+ SHOW_INT (pkcs11_pin_cache_period);
+ SHOW_STR (pkcs11_id);
+ SHOW_BOOL (pkcs11_id_management);
+#endif /* ENABLE_PKCS11 */
+
+#if P2MP
+ show_p2mp_parms (o);
+#endif
+
+#ifdef WIN32
+ SHOW_BOOL (show_net_up);
+ SHOW_INT (route_method);
+ show_tuntap_options (&o->tuntap_options);
+#endif
+#endif
+}
+
+#undef SHOW_PARM
+#undef SHOW_STR
+#undef SHOW_INT
+#undef SHOW_BOOL
+
+#ifdef ENABLE_HTTP_PROXY
+
+struct http_proxy_options *
+init_http_options_if_undefined (struct options *o)
+{
+ if (!o->ce.http_proxy_options)
+ {
+ ALLOC_OBJ_CLEAR_GC (o->ce.http_proxy_options, struct http_proxy_options, &o->gc);
+ /* http proxy defaults */
+ o->ce.http_proxy_options->timeout = 5;
+ o->ce.http_proxy_options->http_version = "1.0";
+ }
+ return o->ce.http_proxy_options;
+}
+
+#endif
+
+#if HTTP_PROXY_FALLBACK
+
+static struct http_proxy_options *
+parse_http_proxy_override (const char *server,
+ const char *port,
+ const char *flags,
+ const int msglevel,
+ struct gc_arena *gc)
+{
+ if (server && port)
+ {
+ struct http_proxy_options *ho;
+ const int int_port = atoi(port);
+
+ if (!legal_ipv4_port (int_port))
+ {
+ msg (msglevel, "Bad http-proxy port number: %s", port);
+ return NULL;
+ }
+
+ ALLOC_OBJ_CLEAR_GC (ho, struct http_proxy_options, gc);
+ ho->server = string_alloc(server, gc);
+ ho->port = int_port;
+ ho->retry = true;
+ ho->timeout = 5;
+ if (flags && !strcmp(flags, "nct"))
+ ho->auth_retry = PAR_NCT;
+ else
+ ho->auth_retry = PAR_ALL;
+ ho->http_version = "1.0";
+ ho->user_agent = "OpenVPN-Autoproxy/1.0";
+ return ho;
+ }
+ else
+ return NULL;
+}
+
+struct http_proxy_options *
+parse_http_proxy_fallback (struct context *c,
+ const char *server,
+ const char *port,
+ const char *flags,
+ const int msglevel)
+{
+ struct gc_arena gc = gc_new ();
+ struct http_proxy_options *ret = NULL;
+ struct http_proxy_options *hp = parse_http_proxy_override(server, port, flags, msglevel, &gc);
+ if (hp)
+ {
+ struct hpo_store *hpos = c->options.hpo_store;
+ if (!hpos)
+ {
+ ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc);
+ c->options.hpo_store = hpos;
+ }
+ hpos->hpo = *hp;
+ hpos->hpo.server = hpos->server;
+ strncpynt(hpos->server, hp->server, sizeof(hpos->server));
+ ret = &hpos->hpo;
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+static void
+http_proxy_warn(const char *name)
+{
+ msg (M_WARN, "Note: option %s ignored because no TCP-based connection profiles are defined", name);
+}
+
+void
+options_postprocess_http_proxy_fallback (struct options *o)
+{
+ struct connection_list *l = o->connection_list;
+ if (l)
+ {
+ int i;
+ for (i = 0; i < l->len; ++i)
+ {
+ struct connection_entry *ce = l->array[i];
+ if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4)
+ {
+ if (l->len < CONNECTION_LIST_SIZE)
+ {
+ struct connection_entry *newce;
+ ALLOC_OBJ_GC (newce, struct connection_entry, &o->gc);
+ *newce = *ce;
+ newce->flags |= CE_HTTP_PROXY_FALLBACK;
+ newce->http_proxy_options = NULL;
+ newce->ce_http_proxy_fallback_timestamp = 0;
+ l->array[l->len++] = newce;
+ }
+ return;
+ }
+ }
+ }
+ http_proxy_warn("http-proxy-fallback");
+}
+
+void
+options_postprocess_http_proxy_override (struct options *o)
+{
+ const struct connection_list *l = o->connection_list;
+ if (l)
+ {
+ int i;
+ bool succeed = false;
+ for (i = 0; i < l->len; ++i)
+ {
+ struct connection_entry *ce = l->array[i];
+ if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4)
+ {
+ ce->http_proxy_options = o->http_proxy_override;
+ succeed = true;
+ }
+ }
+ if (succeed)
+ {
+ for (i = 0; i < l->len; ++i)
+ {
+ struct connection_entry *ce = l->array[i];
+ if (ce->proto == PROTO_UDPv4)
+ {
+ ce->flags |= CE_DISABLED;
+ }
+ }
+ }
+ else
+ {
+ http_proxy_warn("http-proxy-override");
+ }
+ }
+}
+
+#endif
+
+#if ENABLE_CONNECTION
+
+static struct connection_list *
+alloc_connection_list_if_undef (struct options *options)
+{
+ if (!options->connection_list)
+ ALLOC_OBJ_CLEAR_GC (options->connection_list, struct connection_list, &options->gc);
+ return options->connection_list;
+}
+
+static struct connection_entry *
+alloc_connection_entry (struct options *options, const int msglevel)
+{
+ struct connection_list *l = alloc_connection_list_if_undef (options);
+ struct connection_entry *e;
+
+ if (l->len >= CONNECTION_LIST_SIZE)
+ {
+ msg (msglevel, "Maximum number of 'connection' options (%d) exceeded", CONNECTION_LIST_SIZE);
+ return NULL;
+ }
+ ALLOC_OBJ_GC (e, struct connection_entry, &options->gc);
+ l->array[l->len++] = e;
+ return e;
+}
+
+static struct remote_list *
+alloc_remote_list_if_undef (struct options *options)
+{
+ if (!options->remote_list)
+ ALLOC_OBJ_CLEAR_GC (options->remote_list, struct remote_list, &options->gc);
+ return options->remote_list;
+}
+
+static struct remote_entry *
+alloc_remote_entry (struct options *options, const int msglevel)
+{
+ struct remote_list *l = alloc_remote_list_if_undef (options);
+ struct remote_entry *e;
+
+ if (l->len >= CONNECTION_LIST_SIZE)
+ {
+ msg (msglevel, "Maximum number of 'remote' options (%d) exceeded", CONNECTION_LIST_SIZE);
+ return NULL;
+ }
+ ALLOC_OBJ_GC (e, struct remote_entry, &options->gc);
+ l->array[l->len++] = e;
+ return e;
+}
+
+#endif
+
+void
+connection_entry_load_re (struct connection_entry *ce, const struct remote_entry *re)
+{
+ if (re->remote)
+ ce->remote = re->remote;
+ if (re->remote_port >= 0)
+ ce->remote_port = re->remote_port;
+ if (re->proto >= 0)
+ ce->proto = re->proto;
+}
+
+static void
+options_postprocess_verify_ce (const struct options *options, const struct connection_entry *ce)
+{
+ struct options defaults;
+ int dev = DEV_TYPE_UNDEF;
+ bool pull = false;
+
+ init_options (&defaults, true);
+
+#ifdef USE_CRYPTO
+ if (options->test_crypto)
+ {
+ notnull (options->shared_secret_file, "key file (--secret)");
+ }
+ else
+#endif
+ notnull (options->dev, "TUN/TAP device (--dev)");
+
+ /*
+ * Get tun/tap/null device type
+ */
+ dev = dev_type_enum (options->dev, options->dev_type);
+
+ /*
+ * If "proto tcp" is specified, make sure we know whether it is
+ * tcp-client or tcp-server.
+ */
+ if (ce->proto == PROTO_TCPv4)
+ msg (M_USAGE, "--proto tcp is ambiguous in this context. Please specify --proto tcp-server or --proto tcp-client");
+
+ /*
+ * Sanity check on daemon/inetd modes
+ */
+
+ if (options->daemon && options->inetd)
+ msg (M_USAGE, "only one of --daemon or --inetd may be specified");
+
+ if (options->inetd && (ce->local || ce->remote))
+ msg (M_USAGE, "--local or --remote cannot be used with --inetd");
+
+ if (options->inetd && ce->proto == PROTO_TCPv4_CLIENT)
+ msg (M_USAGE, "--proto tcp-client cannot be used with --inetd");
+
+ if (options->inetd == INETD_NOWAIT && ce->proto != PROTO_TCPv4_SERVER)
+ msg (M_USAGE, "--inetd nowait can only be used with --proto tcp-server");
+
+ if (options->inetd == INETD_NOWAIT
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ && !(options->tls_server || options->tls_client)
+#endif
+ )
+ msg (M_USAGE, "--inetd nowait can only be used in TLS mode");
+
+ if (options->inetd == INETD_NOWAIT && dev != DEV_TYPE_TAP)
+ msg (M_USAGE, "--inetd nowait only makes sense in --dev tap mode");
+
+
+ if (options->lladdr && dev != DEV_TYPE_TAP)
+ msg (M_USAGE, "--lladdr can only be used in --dev tap mode");
+
+ /*
+ * Sanity check on TCP mode options
+ */
+
+ if (ce->connect_retry_defined && ce->proto != PROTO_TCPv4_CLIENT)
+ msg (M_USAGE, "--connect-retry doesn't make sense unless also used with --proto tcp-client");
+
+ if (ce->connect_timeout_defined && ce->proto != PROTO_TCPv4_CLIENT)
+ msg (M_USAGE, "--connect-timeout doesn't make sense unless also used with --proto tcp-client");
+
+ /*
+ * Sanity check on MTU parameters
+ */
+ if (options->tun_mtu_defined && options->link_mtu_defined)
+ msg (M_USAGE, "only one of --tun-mtu or --link-mtu may be defined (note that --ifconfig implies --link-mtu %d)", LINK_MTU_DEFAULT);
+
+#ifdef ENABLE_OCC
+ if (ce->proto != PROTO_UDPv4 && options->mtu_test)
+ msg (M_USAGE, "--mtu-test only makes sense with --proto udp");
+#endif
+
+ /* will we be pulling options from server? */
+#if P2MP
+ pull = options->pull;
+#endif
+
+ /*
+ * Sanity check on --local, --remote, and --ifconfig
+ */
+
+ if (string_defined_equal (ce->local, ce->remote)
+ && ce->local_port == ce->remote_port)
+ msg (M_USAGE, "--remote and --local addresses are the same");
+
+ if (string_defined_equal (ce->remote, options->ifconfig_local)
+ || string_defined_equal (ce->remote, options->ifconfig_remote_netmask))
+ msg (M_USAGE, "--local and --remote addresses must be distinct from --ifconfig addresses");
+
+ if (string_defined_equal (ce->local, options->ifconfig_local)
+ || string_defined_equal (ce->local, options->ifconfig_remote_netmask))
+ msg (M_USAGE, "--local addresses must be distinct from --ifconfig addresses");
+
+ if (string_defined_equal (options->ifconfig_local, options->ifconfig_remote_netmask))
+ msg (M_USAGE, "local and remote/netmask --ifconfig addresses must be different");
+
+ if (ce->bind_defined && !ce->bind_local)
+ msg (M_USAGE, "--bind and --nobind can't be used together");
+
+ if (ce->local && !ce->bind_local)
+ msg (M_USAGE, "--local and --nobind don't make sense when used together");
+
+ if (ce->local_port_defined && !ce->bind_local)
+ msg (M_USAGE, "--lport and --nobind don't make sense when used together");
+
+ if (!ce->remote && !ce->bind_local)
+ msg (M_USAGE, "--nobind doesn't make sense unless used with --remote");
+
+ /*
+ * Check for consistency of management options
+ */
+#ifdef ENABLE_MANAGEMENT
+ if (!options->management_addr &&
+ (options->management_flags
+ || options->management_write_peer_info_file
+ || options->management_log_history_cache != defaults.management_log_history_cache))
+ msg (M_USAGE, "--management is not specified, however one or more options which modify the behavior of --management were specified");
+
+ if ((options->management_client_user || options->management_client_group)
+ && !(options->management_flags & MF_UNIX_SOCK))
+ msg (M_USAGE, "--management-client-(user|group) can only be used on unix domain sockets");
+#endif
+
+ /*
+ * Windows-specific options.
+ */
+
+#ifdef WIN32
+ if (dev == DEV_TYPE_TUN && !(pull || (options->ifconfig_local && options->ifconfig_remote_netmask)))
+ msg (M_USAGE, "On Windows, --ifconfig is required when --dev tun is used");
+
+ if ((options->tuntap_options.ip_win32_defined)
+ && !(pull || (options->ifconfig_local && options->ifconfig_remote_netmask)))
+ msg (M_USAGE, "On Windows, --ip-win32 doesn't make sense unless --ifconfig is also used");
+
+ if (options->tuntap_options.dhcp_options
+ && options->tuntap_options.ip_win32_type != IPW32_SET_DHCP_MASQ
+ && options->tuntap_options.ip_win32_type != IPW32_SET_ADAPTIVE)
+ msg (M_USAGE, "--dhcp-options requires --ip-win32 dynamic or adaptive");
+#endif
+
+ /*
+ * Check that protocol options make sense.
+ */
+
+#ifdef ENABLE_FRAGMENT
+ if (ce->proto != PROTO_UDPv4 && options->fragment)
+ msg (M_USAGE, "--fragment can only be used with --proto udp");
+#endif
+
+#ifdef ENABLE_OCC
+ if (ce->proto != PROTO_UDPv4 && options->explicit_exit_notification)
+ msg (M_USAGE, "--explicit-exit-notify can only be used with --proto udp");
+#endif
+
+ if (!ce->remote && ce->proto == PROTO_TCPv4_CLIENT)
+ msg (M_USAGE, "--remote MUST be used in TCP Client mode");
+
+#ifdef ENABLE_HTTP_PROXY
+ if ((ce->http_proxy_options || options->auto_proxy_info) && ce->proto != PROTO_TCPv4_CLIENT)
+ msg (M_USAGE, "--http-proxy or --auto-proxy MUST be used in TCP Client mode (i.e. --proto tcp-client)");
+#endif
+
+#if defined(ENABLE_HTTP_PROXY) && defined(ENABLE_SOCKS)
+ if (ce->http_proxy_options && ce->socks_proxy_server)
+ msg (M_USAGE, "--http-proxy can not be used together with --socks-proxy");
+#endif
+
+#ifdef ENABLE_SOCKS
+ if (ce->socks_proxy_server && ce->proto == PROTO_TCPv4_SERVER)
+ msg (M_USAGE, "--socks-proxy can not be used in TCP Server mode");
+#endif
+
+ if (ce->proto == PROTO_TCPv4_SERVER && connection_list_defined (options))
+ msg (M_USAGE, "TCP server mode allows at most one --remote address");
+
+#if P2MP_SERVER
+
+ /*
+ * Check consistency of --mode server options.
+ */
+ if (options->mode == MODE_SERVER)
+ {
+ if (!(dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP))
+ msg (M_USAGE, "--mode server only works with --dev tun or --dev tap");
+ if (options->pull)
+ msg (M_USAGE, "--pull cannot be used with --mode server");
+ if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+#if PORT_SHARE
+ if ((options->port_share_host || options->port_share_port) && ce->proto != PROTO_TCPv4_SERVER)
+ msg (M_USAGE, "--port-share only works in TCP server mode (--proto tcp-server)");
+#endif
+ if (!options->tls_server)
+ msg (M_USAGE, "--mode server requires --tls-server");
+ if (ce->remote)
+ msg (M_USAGE, "--remote cannot be used with --mode server");
+ if (!ce->bind_local)
+ msg (M_USAGE, "--nobind cannot be used with --mode server");
+#ifdef ENABLE_HTTP_PROXY
+ if (ce->http_proxy_options)
+ msg (M_USAGE, "--http-proxy cannot be used with --mode server");
+#endif
+#ifdef ENABLE_SOCKS
+ if (ce->socks_proxy_server)
+ msg (M_USAGE, "--socks-proxy cannot be used with --mode server");
+#endif
+#ifdef ENABLE_CONNECTION
+ if (options->connection_list)
+ msg (M_USAGE, "<connection> cannot be used with --mode server");
+#endif
+ if (options->tun_ipv6)
+ msg (M_USAGE, "--tun-ipv6 cannot be used with --mode server");
+ if (options->shaper)
+ msg (M_USAGE, "--shaper cannot be used with --mode server");
+ if (options->inetd)
+ msg (M_USAGE, "--inetd cannot be used with --mode server");
+ if (options->ipchange)
+ msg (M_USAGE, "--ipchange cannot be used with --mode server (use --client-connect instead)");
+ if (!(ce->proto == PROTO_UDPv4 || ce->proto == PROTO_TCPv4_SERVER))
+ msg (M_USAGE, "--mode server currently only supports --proto udp or --proto tcp-server");
+ if (ce->proto != PROTO_UDPv4 && (options->cf_max || options->cf_per))
+ msg (M_USAGE, "--connect-freq only works with --mode server --proto udp. Try --max-clients instead.");
+ if (!(dev == DEV_TYPE_TAP || (dev == DEV_TYPE_TUN && options->topology == TOP_SUBNET)) && options->ifconfig_pool_netmask)
+ msg (M_USAGE, "The third parameter to --ifconfig-pool (netmask) is only valid in --dev tap mode");
+#ifdef ENABLE_OCC
+ if (options->explicit_exit_notification)
+ msg (M_USAGE, "--explicit-exit-notify cannot be used with --mode server");
+#endif
+ if (options->routes && (options->routes->flags & RG_ENABLE))
+ msg (M_USAGE, "--redirect-gateway cannot be used with --mode server (however --push \"redirect-gateway\" is fine)");
+ if (options->route_delay_defined)
+ msg (M_USAGE, "--route-delay cannot be used with --mode server");
+ if (options->up_delay)
+ msg (M_USAGE, "--up-delay cannot be used with --mode server");
+ if (!options->ifconfig_pool_defined && options->ifconfig_pool_persist_filename)
+ msg (M_USAGE, "--ifconfig-pool-persist must be used with --ifconfig-pool");
+ if (options->auth_user_pass_file)
+ msg (M_USAGE, "--auth-user-pass cannot be used with --mode server (it should be used on the client side only)");
+ if (options->ccd_exclusive && !options->client_config_dir)
+ msg (M_USAGE, "--ccd-exclusive must be used with --client-config-dir");
+ if (options->key_method != 2)
+ msg (M_USAGE, "--mode server requires --key-method 2");
+
+ {
+ const bool ccnr = (options->auth_user_pass_verify_script
+ || PLUGIN_OPTION_LIST (options)
+ || MAN_CLIENT_AUTH_ENABLED (options));
+ const char *postfix = "must be used with --management-client-auth, an --auth-user-pass-verify script, or plugin";
+ if ((options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) && !ccnr)
+ msg (M_USAGE, "--client-cert-not-required %s", postfix);
+ if ((options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && !ccnr)
+ msg (M_USAGE, "--username-as-common-name %s", postfix);
+ if ((options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) && !ccnr)
+ msg (M_USAGE, "--auth-user-pass-optional %s", postfix);
+ }
+
+ if ((options->ssl_flags & SSLF_NO_NAME_REMAPPING) && script_method == SM_SYSTEM)
+ msg (M_USAGE, "--script-security method='system' cannot be combined with --no-name-remapping");
+ }
+ else
+ {
+ /*
+ * When not in server mode, err if parameters are
+ * specified which require --mode server.
+ */
+ if (options->ifconfig_pool_defined || options->ifconfig_pool_persist_filename)
+ msg (M_USAGE, "--ifconfig-pool/--ifconfig-pool-persist requires --mode server");
+ if (options->real_hash_size != defaults.real_hash_size
+ || options->virtual_hash_size != defaults.virtual_hash_size)
+ msg (M_USAGE, "--hash-size requires --mode server");
+ if (options->learn_address_script)
+ msg (M_USAGE, "--learn-address requires --mode server");
+ if (options->client_connect_script)
+ msg (M_USAGE, "--client-connect requires --mode server");
+ if (options->client_disconnect_script)
+ msg (M_USAGE, "--client-disconnect requires --mode server");
+ if (options->client_config_dir || options->ccd_exclusive)
+ msg (M_USAGE, "--client-config-dir/--ccd-exclusive requires --mode server");
+ if (options->enable_c2c)
+ msg (M_USAGE, "--client-to-client requires --mode server");
+ if (options->duplicate_cn)
+ msg (M_USAGE, "--duplicate-cn requires --mode server");
+ if (options->cf_max || options->cf_per)
+ msg (M_USAGE, "--connect-freq requires --mode server");
+ if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)
+ msg (M_USAGE, "--client-cert-not-required requires --mode server");
+ if (options->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME)
+ msg (M_USAGE, "--username-as-common-name requires --mode server");
+ if (options->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL)
+ msg (M_USAGE, "--auth-user-pass-optional requires --mode server");
+ if (options->ssl_flags & SSLF_NO_NAME_REMAPPING)
+ msg (M_USAGE, "--no-name-remapping requires --mode server");
+ if (options->ssl_flags & SSLF_OPT_VERIFY)
+ msg (M_USAGE, "--opt-verify requires --mode server");
+ if (options->server_flags & SF_TCP_NODELAY_HELPER)
+ msg (M_USAGE, "--tcp-nodelay requires --mode server");
+ if (options->auth_user_pass_verify_script)
+ msg (M_USAGE, "--auth-user-pass-verify requires --mode server");
+#if PORT_SHARE
+ if (options->port_share_host || options->port_share_port)
+ msg (M_USAGE, "--port-share requires TCP server mode (--mode server --proto tcp-server)");
+#endif
+
+ }
+#endif /* P2MP_SERVER */
+
+#ifdef USE_CRYPTO
+
+ /*
+ * Check consistency of replay options
+ */
+ if ((ce->proto != PROTO_UDPv4)
+ && (options->replay_window != defaults.replay_window
+ || options->replay_time != defaults.replay_time))
+ msg (M_USAGE, "--replay-window only makes sense with --proto udp");
+
+ if (!options->replay
+ && (options->replay_window != defaults.replay_window
+ || options->replay_time != defaults.replay_time))
+ msg (M_USAGE, "--replay-window doesn't make sense when replay protection is disabled with --no-replay");
+
+ /*
+ * SSL/TLS mode sanity checks.
+ */
+
+#ifdef USE_SSL
+ if (options->tls_server + options->tls_client +
+ (options->shared_secret_file != NULL) > 1)
+ msg (M_USAGE, "specify only one of --tls-server, --tls-client, or --secret");
+
+ if (options->tls_server)
+ {
+ notnull (options->dh_file, "DH file (--dh)");
+ }
+ if (options->tls_server || options->tls_client)
+ {
+#ifdef ENABLE_PKCS11
+ if (options->pkcs11_providers[0])
+ {
+ notnull (options->ca_file, "CA file (--ca)");
+
+ if (options->pkcs11_id_management && options->pkcs11_id != NULL)
+ msg(M_USAGE, "Parameter --pkcs11-id cannot be used when --pkcs11-id-management is also specified.");
+ if (!options->pkcs11_id_management && options->pkcs11_id == NULL)
+ msg(M_USAGE, "Parameter --pkcs11-id or --pkcs11-id-management should be specified.");
+ if (options->cert_file)
+ msg(M_USAGE, "Parameter --cert cannot be used when --pkcs11-provider is also specified.");
+ if (options->priv_key_file)
+ msg(M_USAGE, "Parameter --key cannot be used when --pkcs11-provider is also specified.");
+ if (options->pkcs12_file)
+ msg(M_USAGE, "Parameter --pkcs12 cannot be used when --pkcs11-provider is also specified.");
+#ifdef WIN32
+ if (options->cryptoapi_cert)
+ msg(M_USAGE, "Parameter --cryptoapicert cannot be used when --pkcs11-provider is also specified.");
+#endif
+ }
+ else
+#endif
+#ifdef WIN32
+ if (options->cryptoapi_cert)
+ {
+ if ((!(options->ca_file)) && (!(options->ca_path)))
+ msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)");
+ if (options->cert_file)
+ msg(M_USAGE, "Parameter --cert cannot be used when --cryptoapicert is also specified.");
+ if (options->priv_key_file)
+ msg(M_USAGE, "Parameter --key cannot be used when --cryptoapicert is also specified.");
+ if (options->pkcs12_file)
+ msg(M_USAGE, "Parameter --pkcs12 cannot be used when --cryptoapicert is also specified.");
+ }
+ else
+#endif
+ if (options->pkcs12_file)
+ {
+ if (options->ca_path)
+ msg(M_USAGE, "Parameter --capath cannot be used when --pkcs12 is also specified.");
+ if (options->cert_file)
+ msg(M_USAGE, "Parameter --cert cannot be used when --pkcs12 is also specified.");
+ if (options->priv_key_file)
+ msg(M_USAGE, "Parameter --key cannot be used when --pkcs12 is also specified.");
+ }
+ else
+ {
+ if ((!(options->ca_file)) && (!(options->ca_path)))
+ msg(M_USAGE, "You must define CA file (--ca) or CA path (--capath)");
+ if (pull)
+ {
+ const int sum = (options->cert_file != NULL) + (options->priv_key_file != NULL);
+ if (sum == 0)
+ {
+#if P2MP
+ if (!options->auth_user_pass_file)
+#endif
+ msg (M_USAGE, "No client-side authentication method is specified. You must use either --cert/--key, --pkcs12, or --auth-user-pass");
+ }
+ else if (sum == 2)
+ ;
+ else
+ {
+ msg (M_USAGE, "If you use one of --cert or --key, you must use them both");
+ }
+ }
+ else
+ {
+ notnull (options->cert_file, "certificate file (--cert) or PKCS#12 file (--pkcs12)");
+ notnull (options->priv_key_file, "private key file (--key) or PKCS#12 file (--pkcs12)");
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Make sure user doesn't specify any TLS options
+ * when in non-TLS mode.
+ */
+
+#define MUST_BE_UNDEF(parm) if (options->parm != defaults.parm) msg (M_USAGE, err, #parm);
+
+ const char err[] = "Parameter %s can only be specified in TLS-mode, i.e. where --tls-server or --tls-client is also specified.";
+
+ MUST_BE_UNDEF (ca_file);
+ MUST_BE_UNDEF (ca_path);
+ MUST_BE_UNDEF (dh_file);
+ MUST_BE_UNDEF (cert_file);
+ MUST_BE_UNDEF (priv_key_file);
+ MUST_BE_UNDEF (pkcs12_file);
+ MUST_BE_UNDEF (cipher_list);
+ MUST_BE_UNDEF (tls_verify);
+ MUST_BE_UNDEF (tls_export_cert);
+ MUST_BE_UNDEF (tls_remote);
+ MUST_BE_UNDEF (tls_timeout);
+ MUST_BE_UNDEF (renegotiate_bytes);
+ MUST_BE_UNDEF (renegotiate_packets);
+ MUST_BE_UNDEF (renegotiate_seconds);
+ MUST_BE_UNDEF (handshake_window);
+ MUST_BE_UNDEF (transition_window);
+ MUST_BE_UNDEF (tls_auth_file);
+ MUST_BE_UNDEF (single_session);
+#ifdef ENABLE_PUSH_PEER_INFO
+ MUST_BE_UNDEF (push_peer_info);
+#endif
+ MUST_BE_UNDEF (tls_exit);
+ MUST_BE_UNDEF (crl_file);
+ MUST_BE_UNDEF (key_method);
+ MUST_BE_UNDEF (ns_cert_type);
+ MUST_BE_UNDEF (remote_cert_ku[0]);
+ MUST_BE_UNDEF (remote_cert_eku);
+#ifdef ENABLE_PKCS11
+ MUST_BE_UNDEF (pkcs11_providers[0]);
+ MUST_BE_UNDEF (pkcs11_private_mode[0]);
+ MUST_BE_UNDEF (pkcs11_id);
+ MUST_BE_UNDEF (pkcs11_id_management);
+#endif
+
+ if (pull)
+ msg (M_USAGE, err, "--pull");
+ }
+#undef MUST_BE_UNDEF
+#endif /* USE_CRYPTO */
+#endif /* USE_SSL */
+
+#if P2MP
+ if (options->auth_user_pass_file && !options->pull)
+ msg (M_USAGE, "--auth-user-pass requires --pull");
+#endif
+
+ uninit_options (&defaults);
+}
+
+static void
+options_postprocess_mutate_ce (struct options *o, struct connection_entry *ce)
+{
+#if P2MP_SERVER
+ if (o->server_defined || o->server_bridge_defined || o->server_bridge_proxy_dhcp)
+ {
+ if (ce->proto == PROTO_TCPv4)
+ ce->proto = PROTO_TCPv4_SERVER;
+ }
+#endif
+#if P2MP
+ if (o->client)
+ {
+ if (ce->proto == PROTO_TCPv4)
+ ce->proto = PROTO_TCPv4_CLIENT;
+ }
+#endif
+
+ if (ce->proto == PROTO_TCPv4_CLIENT && !ce->local && !ce->local_port_defined && !ce->bind_defined)
+ ce->bind_local = false;
+
+#ifdef ENABLE_SOCKS
+ if (ce->proto == PROTO_UDPv4 && ce->socks_proxy_server && !ce->local && !ce->local_port_defined && !ce->bind_defined)
+ ce->bind_local = false;
+#endif
+
+ if (!ce->bind_local)
+ ce->local_port = 0;
+
+ /* if protocol forcing is enabled, disable all protocols except for the forced one */
+ if (o->proto_force >= 0 && is_proto_tcp(o->proto_force) != is_proto_tcp(ce->proto))
+ ce->flags |= CE_DISABLED;
+}
+
+static void
+options_postprocess_mutate_invariant (struct options *options)
+{
+ const int dev = dev_type_enum (options->dev, options->dev_type);
+
+ /*
+ * If --mssfix is supplied without a parameter, default
+ * it to --fragment value, if --fragment is specified.
+ */
+ if (options->mssfix_default)
+ {
+#ifdef ENABLE_FRAGMENT
+ if (options->fragment)
+ options->mssfix = options->fragment;
+#else
+ msg (M_USAGE, "--mssfix must specify a parameter");
+#endif
+ }
+
+ /*
+ * In forking TCP server mode, you don't need to ifconfig
+ * the tap device (the assumption is that it will be bridged).
+ */
+ if (options->inetd == INETD_NOWAIT)
+ options->ifconfig_noexec = true;
+
+ /*
+ * Set MTU defaults
+ */
+ {
+ if (!options->tun_mtu_defined && !options->link_mtu_defined)
+ {
+ options->tun_mtu_defined = true;
+ }
+ if ((dev == DEV_TYPE_TAP) && !options->tun_mtu_extra_defined)
+ {
+ options->tun_mtu_extra_defined = true;
+ options->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT;
+ }
+ }
+
+#ifdef WIN32
+ if ((dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP) && !options->route_delay_defined)
+ {
+ if (options->mode == MODE_POINT_TO_POINT)
+ {
+ options->route_delay_defined = true;
+ options->route_delay = 5; /* Vista sometimes has a race without this */
+ }
+ }
+
+ if (options->ifconfig_noexec)
+ {
+ options->tuntap_options.ip_win32_type = IPW32_SET_MANUAL;
+ options->ifconfig_noexec = false;
+ }
+#endif
+
+#if P2MP_SERVER
+ /*
+ * Check consistency of --mode server options.
+ */
+ if (options->mode == MODE_SERVER)
+ {
+#ifdef WIN32
+ /*
+ * We need to explicitly set --tap-sleep because
+ * we do not schedule event timers in the top-level context.
+ */
+ options->tuntap_options.tap_sleep = 10;
+ if (options->route_delay_defined && options->route_delay)
+ options->tuntap_options.tap_sleep = options->route_delay;
+ options->route_delay_defined = false;
+#endif
+ }
+#endif
+}
+
+static void
+options_postprocess_verify (const struct options *o)
+{
+#ifdef ENABLE_CONNECTION
+ if (o->connection_list)
+ {
+ int i;
+ for (i = 0; i < o->connection_list->len; ++i)
+ options_postprocess_verify_ce (o, o->connection_list->array[i]);
+ }
+ else
+#endif
+ options_postprocess_verify_ce (o, &o->ce);
+}
+
+static void
+options_postprocess_mutate (struct options *o)
+{
+ /*
+ * Process helper-type options which map to other, more complex
+ * sequences of options.
+ */
+ helper_client_server (o);
+ helper_keepalive (o);
+ helper_tcp_nodelay (o);
+
+ options_postprocess_mutate_invariant (o);
+
+#ifdef ENABLE_CONNECTION
+ if (o->remote_list && !o->connection_list)
+ {
+ /*
+ * For compatibility with 2.0.x, map multiple --remote options
+ * into connection list (connection lists added in 2.1).
+ */
+ if (o->remote_list->len > 1 || o->force_connection_list)
+ {
+ const struct remote_list *rl = o->remote_list;
+ int i;
+ for (i = 0; i < rl->len; ++i)
+ {
+ const struct remote_entry *re = rl->array[i];
+ struct connection_entry ce = o->ce;
+ struct connection_entry *ace;
+
+ ASSERT (re->remote);
+ connection_entry_load_re (&ce, re);
+ ace = alloc_connection_entry (o, M_USAGE);
+ ASSERT (ace);
+ *ace = ce;
+ }
+ }
+ else if (o->remote_list->len == 1) /* one --remote option specified */
+ {
+ connection_entry_load_re (&o->ce, o->remote_list->array[0]);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ if (o->connection_list)
+ {
+ int i;
+ for (i = 0; i < o->connection_list->len; ++i)
+ options_postprocess_mutate_ce (o, o->connection_list->array[i]);
+
+#if HTTP_PROXY_FALLBACK
+ if (o->http_proxy_override)
+ options_postprocess_http_proxy_override(o);
+ else if (o->http_proxy_fallback)
+ options_postprocess_http_proxy_fallback(o);
+#endif
+ }
+ else
+#endif
+ options_postprocess_mutate_ce (o, &o->ce);
+
+#if P2MP
+ /*
+ * Save certain parms before modifying options via --pull
+ */
+ pre_pull_save (o);
+#endif
+}
+
+/*
+ * Sanity check on options.
+ * Also set some options based on other
+ * options.
+ */
+void
+options_postprocess (struct options *options)
+{
+ options_postprocess_mutate (options);
+ options_postprocess_verify (options);
+}
+
+#if P2MP
+
+/*
+ * Save/Restore certain option defaults before --pull is applied.
+ */
+
+void
+pre_pull_save (struct options *o)
+{
+ if (o->pull)
+ {
+ ALLOC_OBJ_CLEAR_GC (o->pre_pull, struct options_pre_pull, &o->gc);
+ o->pre_pull->tuntap_options = o->tuntap_options;
+ o->pre_pull->tuntap_options_defined = true;
+ o->pre_pull->foreign_option_index = o->foreign_option_index;
+ if (o->routes)
+ {
+ o->pre_pull->routes = clone_route_option_list(o->routes, &o->gc);
+ o->pre_pull->routes_defined = true;
+ }
+ }
+}
+
+void
+pre_pull_restore (struct options *o)
+{
+ const struct options_pre_pull *pp = o->pre_pull;
+ if (pp)
+ {
+ CLEAR (o->tuntap_options);
+ if (pp->tuntap_options_defined)
+ o->tuntap_options = pp->tuntap_options;
+
+ if (pp->routes_defined)
+ {
+ rol_check_alloc (o);
+ copy_route_option_list (o->routes, pp->routes);
+ }
+ else
+ o->routes = NULL;
+
+ o->foreign_option_index = pp->foreign_option_index;
+ }
+
+ o->push_continuation = 0;
+}
+
+#endif
+
+#ifdef ENABLE_OCC
+
+/*
+ * Build an options string to represent data channel encryption options.
+ * This string must match exactly between peers. The keysize is checked
+ * separately by read_key().
+ *
+ * The following options must match on both peers:
+ *
+ * Tunnel options:
+ *
+ * --dev tun|tap [unit number need not match]
+ * --dev-type tun|tap
+ * --link-mtu
+ * --udp-mtu
+ * --tun-mtu
+ * --proto udp
+ * --proto tcp-client [matched with --proto tcp-server
+ * on the other end of the connection]
+ * --proto tcp-server [matched with --proto tcp-client on
+ * the other end of the connection]
+ * --tun-ipv6
+ * --ifconfig x y [matched with --ifconfig y x on
+ * the other end of the connection]
+ *
+ * --comp-lzo
+ * --fragment
+ *
+ * Crypto Options:
+ *
+ * --cipher
+ * --auth
+ * --keysize
+ * --secret
+ * --no-replay
+ * --no-iv
+ *
+ * SSL Options:
+ *
+ * --tls-auth
+ * --tls-client [matched with --tls-server on
+ * the other end of the connection]
+ * --tls-server [matched with --tls-client on
+ * the other end of the connection]
+ */
+
+char *
+options_string (const struct options *o,
+ const struct frame *frame,
+ struct tuntap *tt,
+ bool remote,
+ struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf (OPTION_LINE_SIZE);
+ bool tt_local = false;
+
+ buf_printf (&out, "V4");
+
+ /*
+ * Tunnel Options
+ */
+
+ buf_printf (&out, ",dev-type %s", dev_type_string (o->dev, o->dev_type));
+ buf_printf (&out, ",link-mtu %d", EXPANDED_SIZE (frame));
+ buf_printf (&out, ",tun-mtu %d", PAYLOAD_SIZE (frame));
+ buf_printf (&out, ",proto %s", proto2ascii (proto_remote (o->ce.proto, remote), true));
+ if (o->tun_ipv6)
+ buf_printf (&out, ",tun-ipv6");
+
+ /*
+ * Try to get ifconfig parameters into the options string.
+ * If tt is undefined, make a temporary instantiation.
+ */
+ if (!tt)
+ {
+ tt = init_tun (o->dev,
+ o->dev_type,
+ o->topology,
+ o->ifconfig_local,
+ o->ifconfig_remote_netmask,
+ (in_addr_t)0,
+ (in_addr_t)0,
+ false,
+ NULL);
+ if (tt)
+ tt_local = true;
+ }
+
+ if (tt && o->mode == MODE_POINT_TO_POINT && !PULL_DEFINED(o))
+ {
+ const char *ios = ifconfig_options_string (tt, remote, o->ifconfig_nowarn, gc);
+ if (ios && strlen (ios))
+ buf_printf (&out, ",ifconfig %s", ios);
+ }
+ if (tt_local)
+ {
+ free (tt);
+ tt = NULL;
+ }
+
+#ifdef USE_LZO
+ if (o->lzo & LZO_SELECTED)
+ buf_printf (&out, ",comp-lzo");
+#endif
+
+#ifdef ENABLE_FRAGMENT
+ if (o->fragment)
+ buf_printf (&out, ",mtu-dynamic");
+#endif
+
+#ifdef USE_CRYPTO
+
+#ifdef USE_SSL
+#define TLS_CLIENT (o->tls_client)
+#define TLS_SERVER (o->tls_server)
+#else
+#define TLS_CLIENT (false)
+#define TLS_SERVER (false)
+#endif
+
+ /*
+ * Key direction
+ */
+ {
+ const char *kd = keydirection2ascii (o->key_direction, remote);
+ if (kd)
+ buf_printf (&out, ",keydir %s", kd);
+ }
+
+ /*
+ * Crypto Options
+ */
+ if (o->shared_secret_file || TLS_CLIENT || TLS_SERVER)
+ {
+ struct key_type kt;
+
+ ASSERT ((o->shared_secret_file != NULL)
+ + (TLS_CLIENT == true)
+ + (TLS_SERVER == true)
+ <= 1);
+
+ init_key_type (&kt, o->ciphername, o->ciphername_defined,
+ o->authname, o->authname_defined,
+ o->keysize, true, false);
+
+ buf_printf (&out, ",cipher %s", kt_cipher_name (&kt));
+ buf_printf (&out, ",auth %s", kt_digest_name (&kt));
+ buf_printf (&out, ",keysize %d", kt_key_size (&kt));
+ if (o->shared_secret_file)
+ buf_printf (&out, ",secret");
+ if (!o->replay)
+ buf_printf (&out, ",no-replay");
+ if (!o->use_iv)
+ buf_printf (&out, ",no-iv");
+ }
+
+#ifdef USE_SSL
+ /*
+ * SSL Options
+ */
+ {
+ if (TLS_CLIENT || TLS_SERVER)
+ {
+ if (o->tls_auth_file)
+ buf_printf (&out, ",tls-auth");
+
+ if (o->key_method > 1)
+ buf_printf (&out, ",key-method %d", o->key_method);
+ }
+
+ if (remote)
+ {
+ if (TLS_CLIENT)
+ buf_printf (&out, ",tls-server");
+ else if (TLS_SERVER)
+ buf_printf (&out, ",tls-client");
+ }
+ else
+ {
+ if (TLS_CLIENT)
+ buf_printf (&out, ",tls-client");
+ else if (TLS_SERVER)
+ buf_printf (&out, ",tls-server");
+ }
+ }
+#endif /* USE_SSL */
+
+#undef TLS_CLIENT
+#undef TLS_SERVER
+
+#endif /* USE_CRYPTO */
+
+ return BSTR (&out);
+}
+
+/*
+ * Compare option strings for equality.
+ * If the first two chars of the strings differ, it means that
+ * we are looking at different versions of the options string,
+ * therefore don't compare them and return true.
+ */
+
+bool
+options_cmp_equal (char *actual, const char *expected)
+{
+ return options_cmp_equal_safe (actual, expected, strlen (actual) + 1);
+}
+
+void
+options_warning (char *actual, const char *expected)
+{
+ options_warning_safe (actual, expected, strlen (actual) + 1);
+}
+
+static const char *
+options_warning_extract_parm1 (const char *option_string,
+ struct gc_arena *gc_ret)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer b = string_alloc_buf (option_string, &gc);
+ char *p = gc_malloc (OPTION_PARM_SIZE, false, &gc);
+ const char *ret;
+
+ buf_parse (&b, ' ', p, OPTION_PARM_SIZE);
+ ret = string_alloc (p, gc_ret);
+ gc_free (&gc);
+ return ret;
+}
+
+static void
+options_warning_safe_scan2 (const int msglevel,
+ const int delim,
+ const bool report_inconsistent,
+ const char *p1,
+ const struct buffer *b2_src,
+ const char *b1_name,
+ const char *b2_name)
+{
+ if (strlen (p1) > 0)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer b2 = *b2_src;
+ const char *p1_prefix = options_warning_extract_parm1 (p1, &gc);
+ char *p2 = gc_malloc (OPTION_PARM_SIZE, false, &gc);
+
+ while (buf_parse (&b2, delim, p2, OPTION_PARM_SIZE))
+ {
+ if (strlen (p2))
+ {
+ const char *p2_prefix = options_warning_extract_parm1 (p2, &gc);
+
+ if (!strcmp (p1, p2))
+ goto done;
+ if (!strcmp (p1_prefix, p2_prefix))
+ {
+ if (report_inconsistent)
+ msg (msglevel, "WARNING: '%s' is used inconsistently, %s='%s', %s='%s'",
+ safe_print (p1_prefix, &gc),
+ b1_name,
+ safe_print (p1, &gc),
+ b2_name,
+ safe_print (p2, &gc));
+ goto done;
+ }
+ }
+ }
+
+ msg (msglevel, "WARNING: '%s' is present in %s config but missing in %s config, %s='%s'",
+ safe_print (p1_prefix, &gc),
+ b1_name,
+ b2_name,
+ b1_name,
+ safe_print (p1, &gc));
+
+ done:
+ gc_free (&gc);
+ }
+}
+
+static void
+options_warning_safe_scan1 (const int msglevel,
+ const int delim,
+ const bool report_inconsistent,
+ const struct buffer *b1_src,
+ const struct buffer *b2_src,
+ const char *b1_name,
+ const char *b2_name)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer b = *b1_src;
+ char *p = gc_malloc (OPTION_PARM_SIZE, true, &gc);
+
+ while (buf_parse (&b, delim, p, OPTION_PARM_SIZE))
+ options_warning_safe_scan2 (msglevel, delim, report_inconsistent, p, b2_src, b1_name, b2_name);
+
+ gc_free (&gc);
+}
+
+static void
+options_warning_safe_ml (const int msglevel, char *actual, const char *expected, size_t actual_n)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (actual_n > 0)
+ {
+ struct buffer local = alloc_buf_gc (OPTION_PARM_SIZE + 16, &gc);
+ struct buffer remote = alloc_buf_gc (OPTION_PARM_SIZE + 16, &gc);
+ actual[actual_n - 1] = 0;
+
+ buf_printf (&local, "version %s", expected);
+ buf_printf (&remote, "version %s", actual);
+
+ options_warning_safe_scan1 (msglevel, ',', true,
+ &local, &remote,
+ "local", "remote");
+
+ options_warning_safe_scan1 (msglevel, ',', false,
+ &remote, &local,
+ "remote", "local");
+ }
+
+ gc_free (&gc);
+}
+
+bool
+options_cmp_equal_safe (char *actual, const char *expected, size_t actual_n)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = true;
+
+ if (actual_n > 0)
+ {
+ actual[actual_n - 1] = 0;
+#ifndef STRICT_OPTIONS_CHECK
+ if (strncmp (actual, expected, 2))
+ {
+ msg (D_SHOW_OCC, "NOTE: Options consistency check may be skewed by version differences");
+ options_warning_safe_ml (D_SHOW_OCC, actual, expected, actual_n);
+ }
+ else
+#endif
+ ret = !strcmp (actual, expected);
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+void
+options_warning_safe (char *actual, const char *expected, size_t actual_n)
+{
+ options_warning_safe_ml (M_WARN, actual, expected, actual_n);
+}
+
+const char *
+options_string_version (const char* s, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (4, gc);
+ strncpynt ((char *) BPTR (&out), s, 3);
+ return BSTR (&out);
+}
+
+#endif /* ENABLE_OCC */
+
+static void
+foreign_option (struct options *o, char *argv[], int len, struct env_set *es)
+{
+ if (len > 0)
+ {
+ struct gc_arena gc = gc_new();
+ struct buffer name = alloc_buf_gc (OPTION_PARM_SIZE, &gc);
+ struct buffer value = alloc_buf_gc (OPTION_PARM_SIZE, &gc);
+ int i;
+ bool first = true;
+ bool good = true;
+
+ good &= buf_printf (&name, "foreign_option_%d", o->foreign_option_index + 1);
+ ++o->foreign_option_index;
+ for (i = 0; i < len; ++i)
+ {
+ if (argv[i])
+ {
+ if (!first)
+ good &= buf_printf (&value, " ");
+ good &= buf_printf (&value, "%s", argv[i]);
+ first = false;
+ }
+ }
+ if (good)
+ setenv_str (es, BSTR(&name), BSTR(&value));
+ else
+ msg (M_WARN, "foreign_option: name/value overflow");
+ gc_free (&gc);
+ }
+}
+
+/*
+ * parse/print topology coding
+ */
+
+int
+parse_topology (const char *str, const int msglevel)
+{
+ if (streq (str, "net30"))
+ return TOP_NET30;
+ else if (streq (str, "p2p"))
+ return TOP_P2P;
+ else if (streq (str, "subnet"))
+ return TOP_SUBNET;
+ else
+ {
+ msg (msglevel, "--topology must be net30, p2p, or subnet");
+ return TOP_UNDEF;
+ }
+}
+
+const char *
+print_topology (const int topology)
+{
+ switch (topology)
+ {
+ case TOP_UNDEF:
+ return "undef";
+ case TOP_NET30:
+ return "net30";
+ case TOP_P2P:
+ return "p2p";
+ case TOP_SUBNET:
+ return "subnet";
+ default:
+ return "unknown";
+ }
+}
+
+#if P2MP
+
+/*
+ * Manage auth-retry variable
+ */
+
+static int global_auth_retry; /* GLOBAL */
+
+int
+auth_retry_get (void)
+{
+ return global_auth_retry;
+}
+
+bool
+auth_retry_set (const int msglevel, const char *option)
+{
+ if (streq (option, "interact"))
+ global_auth_retry = AR_INTERACT;
+ else if (streq (option, "nointeract"))
+ global_auth_retry = AR_NOINTERACT;
+ else if (streq (option, "none"))
+ global_auth_retry = AR_NONE;
+ else
+ {
+ msg (msglevel, "--auth-retry method must be 'interact', 'nointeract', or 'none'");
+ return false;
+ }
+ return true;
+}
+
+const char *
+auth_retry_print (void)
+{
+ switch (global_auth_retry)
+ {
+ case AR_NONE:
+ return "none";
+ case AR_NOINTERACT:
+ return "nointeract";
+ case AR_INTERACT:
+ return "interact";
+ default:
+ return "???";
+ }
+}
+
+#endif
+
+/*
+ * Print the help message.
+ */
+static void
+usage (void)
+{
+ FILE *fp = msg_fp(0);
+
+#ifdef ENABLE_SMALL
+
+ fprintf (fp, "Usage message not available\n");
+
+#else
+
+ struct options o;
+ init_options (&o, true);
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+ fprintf (fp, usage_message,
+ title_string,
+ o.ce.connect_retry_seconds,
+ o.ce.local_port, o.ce.remote_port,
+ TUN_MTU_DEFAULT, TAP_MTU_EXTRA_DEFAULT,
+ o.verbosity,
+ o.authname, o.ciphername,
+ o.replay_window, o.replay_time,
+ o.tls_timeout, o.renegotiate_seconds,
+ o.handshake_window, o.transition_window);
+#elif defined(USE_CRYPTO)
+ fprintf (fp, usage_message,
+ title_string,
+ o.ce.connect_retry_seconds,
+ o.ce.local_port, o.ce.remote_port,
+ TUN_MTU_DEFAULT, TAP_MTU_EXTRA_DEFAULT,
+ o.verbosity,
+ o.authname, o.ciphername,
+ o.replay_window, o.replay_time);
+#else
+ fprintf (fp, usage_message,
+ title_string,
+ o.ce.connect_retry_seconds,
+ o.ce.local_port, o.ce.remote_port,
+ TUN_MTU_DEFAULT, TAP_MTU_EXTRA_DEFAULT,
+ o.verbosity);
+#endif
+ fflush(fp);
+
+#endif /* ENABLE_SMALL */
+
+ openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */
+}
+
+void
+usage_small (void)
+{
+ msg (M_WARN|M_NOPREFIX, "Use --help for more information.");
+ openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */
+}
+
+static void
+usage_version (void)
+{
+ msg (M_INFO|M_NOPREFIX, "%s", title_string);
+ msg (M_INFO|M_NOPREFIX, "Originally developed by James Yonan");
+ msg (M_INFO|M_NOPREFIX, "Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>");
+#ifndef ENABLE_SMALL
+#ifdef CONFIGURE_CALL
+ msg (M_INFO|M_NOPREFIX, "\n%s\n", CONFIGURE_CALL);
+#endif
+#ifdef CONFIGURE_DEFINES
+ msg (M_INFO|M_NOPREFIX, "Compile time defines: %s", CONFIGURE_DEFINES);
+#endif
+#endif
+ openvpn_exit (OPENVPN_EXIT_STATUS_USAGE); /* exit point */
+}
+
+void
+notnull (const char *arg, const char *description)
+{
+ if (!arg)
+ msg (M_USAGE, "You must define %s", description);
+}
+
+bool
+string_defined_equal (const char *s1, const char *s2)
+{
+ if (s1 && s2)
+ return !strcmp (s1, s2);
+ else
+ return false;
+}
+
+#if 0
+static void
+ping_rec_err (int msglevel)
+{
+ msg (msglevel, "only one of --ping-exit or --ping-restart options may be specified");
+}
+#endif
+
+static int
+positive_atoi (const char *str)
+{
+ const int i = atoi (str);
+ return i < 0 ? 0 : i;
+}
+
+#ifdef WIN32 /* This function is only used when compiling on Windows */
+static unsigned int
+atou (const char *str)
+{
+ unsigned int val = 0;
+ sscanf (str, "%u", &val);
+ return val;
+}
+#endif
+
+static inline bool
+space (unsigned char c)
+{
+ return c == '\0' || isspace (c);
+}
+
+int
+parse_line (const char *line,
+ char *p[],
+ const int n,
+ const char *file,
+ const int line_num,
+ int msglevel,
+ struct gc_arena *gc)
+{
+ const int STATE_INITIAL = 0;
+ const int STATE_READING_QUOTED_PARM = 1;
+ const int STATE_READING_UNQUOTED_PARM = 2;
+ const int STATE_DONE = 3;
+ const int STATE_READING_SQUOTED_PARM = 4;
+
+ const char *error_prefix = "";
+
+ int ret = 0;
+ const char *c = line;
+ int state = STATE_INITIAL;
+ bool backslash = false;
+ char in, out;
+
+ char parm[OPTION_PARM_SIZE];
+ unsigned int parm_len = 0;
+
+ msglevel &= ~M_OPTERR;
+
+ if (msglevel & M_MSG_VIRT_OUT)
+ error_prefix = "ERROR: ";
+
+ do
+ {
+ in = *c;
+ out = 0;
+
+ if (!backslash && in == '\\' && state != STATE_READING_SQUOTED_PARM)
+ {
+ backslash = true;
+ }
+ else
+ {
+ if (state == STATE_INITIAL)
+ {
+ if (!space (in))
+ {
+ if (in == ';' || in == '#') /* comment */
+ break;
+ if (!backslash && in == '\"')
+ state = STATE_READING_QUOTED_PARM;
+ else if (!backslash && in == '\'')
+ state = STATE_READING_SQUOTED_PARM;
+ else
+ {
+ out = in;
+ state = STATE_READING_UNQUOTED_PARM;
+ }
+ }
+ }
+ else if (state == STATE_READING_UNQUOTED_PARM)
+ {
+ if (!backslash && space (in))
+ state = STATE_DONE;
+ else
+ out = in;
+ }
+ else if (state == STATE_READING_QUOTED_PARM)
+ {
+ if (!backslash && in == '\"')
+ state = STATE_DONE;
+ else
+ out = in;
+ }
+ else if (state == STATE_READING_SQUOTED_PARM)
+ {
+ if (in == '\'')
+ state = STATE_DONE;
+ else
+ out = in;
+ }
+ if (state == STATE_DONE)
+ {
+ /* ASSERT (parm_len > 0); */
+ p[ret] = gc_malloc (parm_len + 1, true, gc);
+ memcpy (p[ret], parm, parm_len);
+ p[ret][parm_len] = '\0';
+ state = STATE_INITIAL;
+ parm_len = 0;
+ ++ret;
+ }
+
+ if (backslash && out)
+ {
+ if (!(out == '\\' || out == '\"' || space (out)))
+ {
+#ifdef ENABLE_SMALL
+ msg (msglevel, "%sOptions warning: Bad backslash ('\\') usage in %s:%d", error_prefix, file, line_num);
+#else
+ msg (msglevel, "%sOptions warning: Bad backslash ('\\') usage in %s:%d: remember that backslashes are treated as shell-escapes and if you need to pass backslash characters as part of a Windows filename, you should use double backslashes such as \"c:\\\\" PACKAGE "\\\\static.key\"", error_prefix, file, line_num);
+#endif
+ return 0;
+ }
+ }
+ backslash = false;
+ }
+
+ /* store parameter character */
+ if (out)
+ {
+ if (parm_len >= SIZE (parm))
+ {
+ parm[SIZE (parm) - 1] = 0;
+ msg (msglevel, "%sOptions error: Parameter at %s:%d is too long (%d chars max): %s",
+ error_prefix, file, line_num, (int) SIZE (parm), parm);
+ return 0;
+ }
+ parm[parm_len++] = out;
+ }
+
+ /* avoid overflow if too many parms in one config file line */
+ if (ret >= n)
+ break;
+
+ } while (*c++ != '\0');
+
+ if (state == STATE_READING_QUOTED_PARM)
+ {
+ msg (msglevel, "%sOptions error: No closing quotation (\") in %s:%d", error_prefix, file, line_num);
+ return 0;
+ }
+ if (state == STATE_READING_SQUOTED_PARM)
+ {
+ msg (msglevel, "%sOptions error: No closing single quotation (\') in %s:%d", error_prefix, file, line_num);
+ return 0;
+ }
+ if (state != STATE_INITIAL)
+ {
+ msg (msglevel, "%sOptions error: Residual parse state (%d) in %s:%d", error_prefix, state, file, line_num);
+ return 0;
+ }
+#if 0
+ {
+ int i;
+ for (i = 0; i < ret; ++i)
+ {
+ msg (M_INFO|M_NOPREFIX, "%s:%d ARG[%d] '%s'", file, line_num, i, p[i]);
+ }
+ }
+#endif
+ return ret;
+}
+
+static void
+bypass_doubledash (char **p)
+{
+ if (strlen (*p) >= 3 && !strncmp (*p, "--", 2))
+ *p += 2;
+}
+
+#if ENABLE_INLINE_FILES
+
+struct in_src {
+# define IS_TYPE_FP 1
+# define IS_TYPE_BUF 2
+ int type;
+ union {
+ FILE *fp;
+ struct buffer *multiline;
+ } u;
+};
+
+static bool
+in_src_get (const struct in_src *is, char *line, const int size)
+{
+ if (is->type == IS_TYPE_FP)
+ {
+ return BOOL_CAST (fgets (line, size, is->u.fp));
+ }
+ else if (is->type == IS_TYPE_BUF)
+ {
+ bool status = buf_parse (is->u.multiline, '\n', line, size);
+ if ((int) strlen (line) + 1 < size)
+ strcat (line, "\n");
+ return status;
+ }
+ else
+ {
+ ASSERT (0);
+ return false;
+ }
+}
+
+static char *
+read_inline_file (struct in_src *is, const char *close_tag, struct gc_arena *gc)
+{
+ char line[OPTION_LINE_SIZE];
+ struct buffer buf = alloc_buf (10000);
+ char *ret;
+ while (in_src_get (is, line, sizeof (line)))
+ {
+ if (!strncmp (line, close_tag, strlen (close_tag)))
+ break;
+ buf_printf (&buf, "%s", line);
+ }
+ ret = string_alloc (BSTR (&buf), gc);
+ buf_clear (&buf);
+ free_buf (&buf);
+ CLEAR (line);
+ return ret;
+}
+
+static bool
+check_inline_file (struct in_src *is, char *p[], struct gc_arena *gc)
+{
+ bool ret = false;
+ if (p[0] && !p[1])
+ {
+ char *arg = p[0];
+ if (arg[0] == '<' && arg[strlen(arg)-1] == '>')
+ {
+ struct buffer close_tag;
+ arg[strlen(arg)-1] = '\0';
+ p[0] = string_alloc (arg+1, gc);
+ p[1] = string_alloc (INLINE_FILE_TAG, gc);
+ close_tag = alloc_buf (strlen(p[0]) + 4);
+ buf_printf (&close_tag, "</%s>", p[0]);
+ p[2] = read_inline_file (is, BSTR (&close_tag), gc);
+ p[3] = NULL;
+ free_buf (&close_tag);
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+static bool
+check_inline_file_via_fp (FILE *fp, char *p[], struct gc_arena *gc)
+{
+ struct in_src is;
+ is.type = IS_TYPE_FP;
+ is.u.fp = fp;
+ return check_inline_file (&is, p, gc);
+}
+
+static bool
+check_inline_file_via_buf (struct buffer *multiline, char *p[], struct gc_arena *gc)
+{
+ struct in_src is;
+ is.type = IS_TYPE_BUF;
+ is.u.multiline = multiline;
+ return check_inline_file (&is, p, gc);
+}
+
+#endif
+
+static void
+add_option (struct options *options,
+ char *p[],
+ const char *file,
+ int line,
+ const int level,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es);
+
+static void
+read_config_file (struct options *options,
+ const char *file,
+ int level,
+ const char *top_file,
+ const int top_line,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ const int max_recursive_levels = 10;
+ FILE *fp;
+ int line_num;
+ char line[OPTION_LINE_SIZE];
+ char *p[MAX_PARMS];
+
+ ++level;
+ if (level <= max_recursive_levels)
+ {
+ if (streq (file, "stdin"))
+ fp = stdin;
+ else
+ fp = fopen (file, "r");
+ if (fp)
+ {
+ line_num = 0;
+ while (fgets(line, sizeof (line), fp))
+ {
+ CLEAR (p);
+ ++line_num;
+ if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc))
+ {
+ bypass_doubledash (&p[0]);
+#if ENABLE_INLINE_FILES
+ check_inline_file_via_fp (fp, p, &options->gc);
+#endif
+ add_option (options, p, file, line_num, level, msglevel, permission_mask, option_types_found, es);
+ }
+ }
+ if (fp != stdin)
+ fclose (fp);
+ }
+ else
+ {
+ msg (msglevel, "In %s:%d: Error opening configuration file: %s", top_file, top_line, file);
+ }
+ }
+ else
+ {
+ msg (msglevel, "In %s:%d: Maximum recursive include levels exceeded in include attempt of file %s -- probably you have a configuration file that tries to include itself.", top_file, top_line, file);
+ }
+ CLEAR (line);
+ CLEAR (p);
+}
+
+static void
+read_config_string (const char *prefix,
+ struct options *options,
+ const char *config,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ char line[OPTION_LINE_SIZE];
+ struct buffer multiline;
+ int line_num = 0;
+
+ buf_set_read (&multiline, (uint8_t*)config, strlen (config));
+
+ while (buf_parse (&multiline, '\n', line, sizeof (line)))
+ {
+ char *p[MAX_PARMS];
+ CLEAR (p);
+ ++line_num;
+ if (parse_line (line, p, SIZE (p), prefix, line_num, msglevel, &options->gc))
+ {
+ bypass_doubledash (&p[0]);
+#if ENABLE_INLINE_FILES
+ check_inline_file_via_buf (&multiline, p, &options->gc);
+#endif
+ add_option (options, p, NULL, line_num, 0, msglevel, permission_mask, option_types_found, es);
+ }
+ CLEAR (p);
+ }
+ CLEAR (line);
+}
+
+void
+parse_argv (struct options *options,
+ const int argc,
+ char *argv[],
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ int i, j;
+
+ /* usage message */
+ if (argc <= 1)
+ usage ();
+
+ /* config filename specified only? */
+ if (argc == 2 && strncmp (argv[1], "--", 2))
+ {
+ char *p[MAX_PARMS];
+ CLEAR (p);
+ p[0] = "config";
+ p[1] = argv[1];
+ add_option (options, p, NULL, 0, 0, msglevel, permission_mask, option_types_found, es);
+ }
+ else
+ {
+ /* parse command line */
+ for (i = 1; i < argc; ++i)
+ {
+ char *p[MAX_PARMS];
+ CLEAR (p);
+ p[0] = argv[i];
+ if (strncmp(p[0], "--", 2))
+ {
+ msg (msglevel, "I'm trying to parse \"%s\" as an --option parameter but I don't see a leading '--'", p[0]);
+ }
+ else
+ p[0] += 2;
+
+ for (j = 1; j < MAX_PARMS; ++j)
+ {
+ if (i + j < argc)
+ {
+ char *arg = argv[i + j];
+ if (strncmp (arg, "--", 2))
+ p[j] = arg;
+ else
+ break;
+ }
+ }
+ add_option (options, p, NULL, 0, 0, msglevel, permission_mask, option_types_found, es);
+ i += j - 1;
+ }
+ }
+}
+
+bool
+apply_push_options (struct options *options,
+ struct buffer *buf,
+ unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ char line[OPTION_PARM_SIZE];
+ int line_num = 0;
+ const char *file = "[PUSH-OPTIONS]";
+ const int msglevel = D_PUSH_ERRORS|M_OPTERR;
+
+ while (buf_parse (buf, ',', line, sizeof (line)))
+ {
+ char *p[MAX_PARMS];
+ CLEAR (p);
+ ++line_num;
+ if (parse_line (line, p, SIZE (p), file, line_num, msglevel, &options->gc))
+ {
+ add_option (options, p, file, line_num, 0, msglevel, permission_mask, option_types_found, es);
+ }
+ }
+ return true;
+}
+
+void
+options_server_import (struct options *o,
+ const char *filename,
+ int msglevel,
+ unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ msg (D_PUSH, "OPTIONS IMPORT: reading client specific options from: %s", filename);
+ read_config_file (o,
+ filename,
+ 0,
+ filename,
+ 0,
+ msglevel,
+ permission_mask,
+ option_types_found,
+ es);
+}
+
+void options_string_import (struct options *options,
+ const char *config,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ read_config_string ("[CONFIG-STRING]", options, config, msglevel, permission_mask, option_types_found, es);
+}
+
+#if P2MP
+
+#define VERIFY_PERMISSION(mask) { if (!verify_permission(p[0], (mask), permission_mask, option_types_found, msglevel)) goto err; }
+
+static bool
+verify_permission (const char *name,
+ const unsigned int type,
+ const unsigned int allowed,
+ unsigned int *found,
+ const int msglevel)
+{
+ if (!(type & allowed))
+ {
+ msg (msglevel, "option '%s' cannot be used in this context", name);
+ return false;
+ }
+ else
+ {
+ if (found)
+ *found |= type;
+ return true;
+ }
+}
+
+#else
+
+#define VERIFY_PERMISSION(mask)
+
+#endif
+
+/*
+ * Check that an option doesn't have too
+ * many parameters.
+ */
+
+#define NM_QUOTE_HINT (1<<0)
+
+static bool
+no_more_than_n_args (const int msglevel,
+ char *p[],
+ const int max,
+ const unsigned int flags)
+{
+ const int len = string_array_len ((const char **)p);
+
+ if (!len)
+ return false;
+
+ if (len > max)
+ {
+ msg (msglevel, "the --%s directive should have at most %d parameter%s.%s",
+ p[0],
+ max - 1,
+ max >= 3 ? "s" : "",
+ (flags & NM_QUOTE_HINT) ? " To pass a list of arguments as one of the parameters, try enclosing them in double quotes (\"\")." : "");
+ return false;
+ }
+ else
+ return true;
+}
+
+static inline int
+msglevel_forward_compatible (struct options *options, const int msglevel)
+{
+ return options->forward_compatible ? M_WARN : msglevel;
+}
+
+static void
+warn_multiple_script (const char *script, const char *type) {
+ if (script) {
+ msg (M_WARN, "Multiple --%s scripts defined. "
+ "The previously configured script is overridden.", type);
+ }
+}
+
+
+static void
+add_option (struct options *options,
+ char *p[],
+ const char *file,
+ int line,
+ const int level,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+ const bool pull_mode = BOOL_CAST (permission_mask & OPT_P_PULL_MODE);
+ int msglevel_fc = msglevel_forward_compatible (options, msglevel);
+
+ ASSERT (MAX_PARMS >= 5);
+ if (!file)
+ {
+ file = "[CMD-LINE]";
+ line = 1;
+ }
+ if (streq (p[0], "help"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ usage ();
+ }
+ if (streq (p[0], "version"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ usage_version ();
+ }
+ else if (streq (p[0], "config") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_CONFIG);
+
+ /* save first config file only in options */
+ if (!options->config)
+ options->config = p[1];
+
+ read_config_file (options, p[1], level, file, line, msglevel, permission_mask, option_types_found, es);
+ }
+#if 0
+ else if (streq (p[0], "foreign-option") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ foreign_option (options, p, 3, es);
+ }
+#endif
+ else if (streq (p[0], "echo") || streq (p[0], "parameter"))
+ {
+ struct buffer string = alloc_buf_gc (OPTION_PARM_SIZE, &gc);
+ int j;
+ bool good = true;
+
+ VERIFY_PERMISSION (OPT_P_ECHO);
+
+ for (j = 1; j < MAX_PARMS; ++j)
+ {
+ if (!p[j])
+ break;
+ if (j > 1)
+ good &= buf_printf (&string, " ");
+ good &= buf_printf (&string, "%s", p[j]);
+ }
+ if (good)
+ {
+ msg (M_INFO, "%s:%s",
+ pull_mode ? "ECHO-PULL" : "ECHO",
+ BSTR (&string));
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_echo (management, BSTR (&string), pull_mode);
+#endif
+ }
+ else
+ msg (M_WARN, "echo/parameter option overflow");
+ }
+#ifdef ENABLE_MANAGEMENT
+ else if (streq (p[0], "management") && p[1] && p[2])
+ {
+ int port = 0;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[2], "unix"))
+ {
+#if UNIX_SOCK_SUPPORT
+ options->management_flags |= MF_UNIX_SOCK;
+#else
+ msg (msglevel, "MANAGEMENT: this platform does not support unix domain sockets");
+ goto err;
+#endif
+ }
+ else
+ {
+ port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "port number associated with --management directive is out of range");
+ goto err;
+ }
+ }
+
+ options->management_addr = p[1];
+ options->management_port = port;
+ if (p[3])
+ {
+ options->management_user_pass = p[3];
+ }
+ }
+ else if (streq (p[0], "management-client-user") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_client_user = p[1];
+ }
+ else if (streq (p[0], "management-client-group") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_client_group = p[1];
+ }
+ else if (streq (p[0], "management-query-passwords"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_QUERY_PASSWORDS;
+ }
+ else if (streq (p[0], "management-hold"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_HOLD;
+ }
+ else if (streq (p[0], "management-signal"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_SIGNAL;
+ }
+ else if (streq (p[0], "management-forget-disconnect"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_FORGET_DISCONNECT;
+ }
+ else if (streq (p[0], "management-client"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_CONNECT_AS_CLIENT;
+ options->management_write_peer_info_file = p[1];
+ }
+#ifdef MANAGEMENT_DEF_AUTH
+ else if (streq (p[0], "management-client-auth"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_CLIENT_AUTH;
+ }
+#endif
+#ifdef MANAGEMENT_PF
+ else if (streq (p[0], "management-client-pf"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= (MF_CLIENT_PF | MF_CLIENT_AUTH);
+ }
+#endif
+ else if (streq (p[0], "management-log-cache") && p[1])
+ {
+ int cache;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ cache = atoi (p[1]);
+ if (cache < 1)
+ {
+ msg (msglevel, "--management-log-cache parameter is out of range");
+ goto err;
+ }
+ options->management_log_history_cache = cache;
+ }
+#endif
+#ifdef ENABLE_PLUGIN
+ else if (streq (p[0], "plugin") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_PLUGIN);
+ if (!options->plugin_list)
+ options->plugin_list = plugin_option_list_new (&options->gc);
+ if (!plugin_option_list_add (options->plugin_list, &p[1], &options->gc))
+ {
+ msg (msglevel, "plugin add failed: %s", p[1]);
+ goto err;
+ }
+ }
+#endif
+ else if (streq (p[0], "mode") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "p2p"))
+ options->mode = MODE_POINT_TO_POINT;
+#if P2MP_SERVER
+ else if (streq (p[1], "server"))
+ options->mode = MODE_SERVER;
+#endif
+ else
+ {
+ msg (msglevel, "Bad --mode parameter: %s", p[1]);
+ goto err;
+ }
+ }
+ else if (streq (p[0], "dev") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->dev = p[1];
+ }
+ else if (streq (p[0], "dev-type") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->dev_type = p[1];
+ }
+ else if (streq (p[0], "dev-node") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->dev_node = p[1];
+ }
+ else if (streq (p[0], "lladdr") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ if (mac_addr_safe (p[1])) /* MAC address only */
+ options->lladdr = p[1];
+ else
+ {
+ msg (msglevel, "lladdr parm '%s' must be a MAC address", p[1]);
+ goto err;
+ }
+ }
+ else if (streq (p[0], "topology") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ options->topology = parse_topology (p[1], msglevel);
+ }
+ else if (streq (p[0], "tun-ipv6"))
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ options->tun_ipv6 = true;
+ }
+#ifdef CONFIG_FEATURE_IPROUTE
+ else if (streq (p[0], "iproute") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ iproute_path = p[1];
+ }
+#endif
+ else if (streq (p[0], "ifconfig") && p[1] && p[2])
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ if (ip_or_dns_addr_safe (p[1], options->allow_pull_fqdn) && ip_or_dns_addr_safe (p[2], options->allow_pull_fqdn)) /* FQDN -- may be DNS name */
+ {
+ options->ifconfig_local = p[1];
+ options->ifconfig_remote_netmask = p[2];
+ }
+ else
+ {
+ msg (msglevel, "ifconfig parms '%s' and '%s' must be valid addresses", p[1], p[2]);
+ goto err;
+ }
+ }
+ else if (streq (p[0], "ifconfig-noexec"))
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ options->ifconfig_noexec = true;
+ }
+ else if (streq (p[0], "ifconfig-nowarn"))
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ options->ifconfig_nowarn = true;
+ }
+ else if (streq (p[0], "local") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.local = p[1];
+ }
+ else if (streq (p[0], "remote-random"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->remote_random = true;
+ }
+#if ENABLE_CONNECTION
+ else if (streq (p[0], "connection") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ struct options sub;
+ struct connection_entry *e;
+
+ init_options (&sub, true);
+ sub.ce = options->ce;
+ read_config_string ("[CONNECTION-OPTIONS]", &sub, p[2], msglevel, OPT_P_CONNECTION, option_types_found, es);
+ if (!sub.ce.remote)
+ {
+ msg (msglevel, "Each 'connection' block must contain exactly one 'remote' directive");
+ goto err;
+ }
+
+ e = alloc_connection_entry (options, msglevel);
+ if (!e)
+ goto err;
+ *e = sub.ce;
+ gc_transfer (&options->gc, &sub.gc);
+ uninit_options (&sub);
+ }
+ }
+#endif
+#ifdef ENABLE_CONNECTION
+ else if (streq (p[0], "remote-ip-hint") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->remote_ip_hint = p[1];
+ }
+#endif
+#if HTTP_PROXY_FALLBACK
+ else if (streq (p[0], "http-proxy-fallback"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->http_proxy_fallback = true;
+ options->force_connection_list = true;
+ }
+ else if (streq (p[0], "http-proxy-override") && p[1] && p[2])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->http_proxy_override = parse_http_proxy_override(p[1], p[2], p[3], msglevel, &options->gc);
+ if (!options->http_proxy_override)
+ goto err;
+ options->force_connection_list = true;
+ }
+#endif
+ else if (streq (p[0], "remote") && p[1])
+ {
+ struct remote_entry re;
+ re.remote = NULL;
+ re.remote_port = re.proto = -1;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ re.remote = p[1];
+ if (p[2])
+ {
+ const int port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "remote: port number associated with host %s is out of range", p[1]);
+ goto err;
+ }
+ re.remote_port = port;
+ if (p[3])
+ {
+ const int proto = ascii2proto (p[3]);
+ if (proto < 0)
+ {
+ msg (msglevel, "remote: bad protocol associated with host %s: '%s'", p[1], p[3]);
+ goto err;
+ }
+ re.proto = proto;
+ }
+ }
+#ifdef ENABLE_CONNECTION
+ if (permission_mask & OPT_P_GENERAL)
+ {
+ struct remote_entry *e = alloc_remote_entry (options, msglevel);
+ if (!e)
+ goto err;
+ *e = re;
+ }
+ else if (permission_mask & OPT_P_CONNECTION)
+#endif
+ {
+ connection_entry_load_re (&options->ce, &re);
+ }
+ }
+ else if (streq (p[0], "resolv-retry") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "infinite"))
+ options->resolve_retry_seconds = RESOLV_RETRY_INFINITE;
+ else
+ options->resolve_retry_seconds = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "connect-retry") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.connect_retry_seconds = positive_atoi (p[1]);
+ options->ce.connect_retry_defined = true;
+ }
+ else if (streq (p[0], "connect-timeout") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.connect_timeout = positive_atoi (p[1]);
+ options->ce.connect_timeout_defined = true;
+ }
+ else if (streq (p[0], "connect-retry-max") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.connect_retry_max = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "ipchange") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->ipchange, "ipchange");
+ options->ipchange = string_substitute (p[1], ',', ' ', &options->gc);
+ }
+ else if (streq (p[0], "float"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.remote_float = true;
+ }
+#ifdef ENABLE_DEBUG
+ else if (streq (p[0], "gremlin") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->gremlin = positive_atoi (p[1]);
+ }
+#endif
+ else if (streq (p[0], "chroot") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->chroot_dir = p[1];
+ }
+ else if (streq (p[0], "cd") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (openvpn_chdir (p[1]))
+ {
+ msg (M_ERR, "cd to '%s' failed", p[1]);
+ goto err;
+ }
+ options->cd_dir = p[1];
+ }
+#ifdef HAVE_SETCON
+ else if (streq (p[0], "setcon") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->selinux_context = p[1];
+ }
+#endif
+ else if (streq (p[0], "writepid") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->writepid = p[1];
+ }
+ else if (streq (p[0], "up") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->up_script, "up");
+ options->up_script = p[1];
+ }
+ else if (streq (p[0], "down") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->down_script, "down");
+ options->down_script = p[1];
+ }
+ else if (streq (p[0], "down-pre"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->down_pre = true;
+ }
+ else if (streq (p[0], "up-delay"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->up_delay = true;
+ }
+ else if (streq (p[0], "up-restart"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->up_restart = true;
+ }
+ else if (streq (p[0], "syslog"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ open_syslog (p[1], false);
+ }
+ else if (streq (p[0], "daemon"))
+ {
+ bool didit = false;
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (!options->daemon)
+ {
+ options->daemon = didit = true;
+ open_syslog (p[1], false);
+ }
+ if (p[1])
+ {
+ if (!didit)
+ {
+ msg (M_WARN, "WARNING: Multiple --daemon directives specified, ignoring --daemon %s. (Note that initscripts sometimes add their own --daemon directive.)", p[1]);
+ goto err;
+ }
+ }
+ }
+ else if (streq (p[0], "inetd"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (!options->inetd)
+ {
+ int z;
+ const char *name = NULL;
+ const char *opterr = "when --inetd is used with two parameters, one of them must be 'wait' or 'nowait' and the other must be a daemon name to use for system logging";
+
+ options->inetd = -1;
+
+ for (z = 1; z <= 2; ++z)
+ {
+ if (p[z])
+ {
+ if (streq (p[z], "wait"))
+ {
+ if (options->inetd != -1)
+ {
+ msg (msglevel, "%s", opterr);
+ goto err;
+ }
+ else
+ options->inetd = INETD_WAIT;
+ }
+ else if (streq (p[z], "nowait"))
+ {
+ if (options->inetd != -1)
+ {
+ msg (msglevel, "%s", opterr);
+ goto err;
+ }
+ else
+ options->inetd = INETD_NOWAIT;
+ }
+ else
+ {
+ if (name != NULL)
+ {
+ msg (msglevel, "%s", opterr);
+ goto err;
+ }
+ name = p[z];
+ }
+ }
+ }
+
+ /* default */
+ if (options->inetd == -1)
+ options->inetd = INETD_WAIT;
+
+ save_inetd_socket_descriptor ();
+ open_syslog (name, true);
+ }
+ }
+ else if (streq (p[0], "log") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->log = true;
+ redirect_stdout_stderr (p[1], false);
+ }
+ else if (streq (p[0], "suppress-timestamps"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->suppress_timestamps = true;
+ set_suppress_timestamps(true);
+ }
+ else if (streq (p[0], "log-append") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->log = true;
+ redirect_stdout_stderr (p[1], true);
+ }
+ else if (streq (p[0], "mlock"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->mlock = true;
+ }
+#if ENABLE_IP_PKTINFO
+ else if (streq (p[0], "multihome"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->sockflags |= SF_USE_IP_PKTINFO;
+ }
+#endif
+ else if (streq (p[0], "verb") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MESSAGES);
+ options->verbosity = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "mute") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MESSAGES);
+ options->mute = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "errors-to-stderr"))
+ {
+ VERIFY_PERMISSION (OPT_P_MESSAGES);
+ errors_to_stderr();
+ }
+ else if (streq (p[0], "status") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->status_file = p[1];
+ if (p[2])
+ {
+ options->status_file_update_freq = positive_atoi (p[2]);
+ }
+ }
+ else if (streq (p[0], "status-version") && p[1])
+ {
+ int version;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ version = atoi (p[1]);
+ if (version < 1 || version > 3)
+ {
+ msg (msglevel, "--status-version must be 1 to 3");
+ goto err;
+ }
+ options->status_file_version = version;
+ }
+ else if (streq (p[0], "remap-usr1") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "SIGHUP"))
+ options->remap_sigusr1 = SIGHUP;
+ else if (streq (p[1], "SIGTERM"))
+ options->remap_sigusr1 = SIGTERM;
+ else
+ {
+ msg (msglevel, "--remap-usr1 parm must be 'SIGHUP' or 'SIGTERM'");
+ goto err;
+ }
+ }
+ else if ((streq (p[0], "link-mtu") || streq (p[0], "udp-mtu")) && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MTU);
+ options->link_mtu = positive_atoi (p[1]);
+ options->link_mtu_defined = true;
+ }
+ else if (streq (p[0], "tun-mtu") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MTU);
+ options->tun_mtu = positive_atoi (p[1]);
+ options->tun_mtu_defined = true;
+ }
+ else if (streq (p[0], "tun-mtu-extra") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MTU);
+ options->tun_mtu_extra = positive_atoi (p[1]);
+ options->tun_mtu_extra_defined = true;
+ }
+#ifdef ENABLE_FRAGMENT
+ else if (streq (p[0], "mtu-dynamic"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ msg (msglevel, "--mtu-dynamic has been replaced by --fragment");
+ goto err;
+ }
+ else if (streq (p[0], "fragment") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MTU);
+ options->fragment = positive_atoi (p[1]);
+ }
+#endif
+ else if (streq (p[0], "mtu-disc") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_MTU);
+ options->mtu_discover_type = translate_mtu_discover_type_name (p[1]);
+ }
+#ifdef ENABLE_OCC
+ else if (streq (p[0], "mtu-test"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->mtu_test = true;
+ }
+#endif
+ else if (streq (p[0], "nice") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_NICE);
+ options->nice = atoi (p[1]);
+ }
+ else if (streq (p[0], "rcvbuf") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SOCKBUF);
+ options->rcvbuf = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "sndbuf") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SOCKBUF);
+ options->sndbuf = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "socket-flags"))
+ {
+ int j;
+ VERIFY_PERMISSION (OPT_P_SOCKFLAGS);
+ for (j = 1; j < MAX_PARMS && p[j]; ++j)
+ {
+ if (streq (p[j], "TCP_NODELAY"))
+ options->sockflags |= SF_TCP_NODELAY;
+ else
+ msg (msglevel, "unknown socket flag: %s", p[j]);
+ }
+ }
+ else if (streq (p[0], "txqueuelen") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+#ifdef TARGET_LINUX
+ options->tuntap_options.txqueuelen = positive_atoi (p[1]);
+#else
+ msg (msglevel, "--txqueuelen not supported on this OS");
+ goto err;
+#endif
+ }
+ else if (streq (p[0], "shaper") && p[1])
+ {
+#ifdef HAVE_GETTIMEOFDAY
+ int shaper;
+
+ VERIFY_PERMISSION (OPT_P_SHAPER);
+ shaper = atoi (p[1]);
+ if (shaper < SHAPER_MIN || shaper > SHAPER_MAX)
+ {
+ msg (msglevel, "Bad shaper value, must be between %d and %d",
+ SHAPER_MIN, SHAPER_MAX);
+ goto err;
+ }
+ options->shaper = shaper;
+#else /* HAVE_GETTIMEOFDAY */
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ msg (msglevel, "--shaper requires the gettimeofday() function which is missing");
+ goto err;
+#endif /* HAVE_GETTIMEOFDAY */
+ }
+ else if (streq (p[0], "port") && p[1])
+ {
+ int port;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ port = atoi (p[1]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "Bad port number: %s", p[1]);
+ goto err;
+ }
+ options->ce.port_option_used = true;
+ options->ce.local_port = options->ce.remote_port = port;
+ }
+ else if (streq (p[0], "lport") && p[1])
+ {
+ int port;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ port = atoi (p[1]);
+ if ((port != 0) && !legal_ipv4_port (port))
+ {
+ msg (msglevel, "Bad local port number: %s", p[1]);
+ goto err;
+ }
+ options->ce.local_port_defined = true;
+ options->ce.port_option_used = true;
+ options->ce.local_port = port;
+ }
+ else if (streq (p[0], "rport") && p[1])
+ {
+ int port;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ port = atoi (p[1]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "Bad remote port number: %s", p[1]);
+ goto err;
+ }
+ options->ce.port_option_used = true;
+ options->ce.remote_port = port;
+ }
+ else if (streq (p[0], "bind"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.bind_defined = true;
+ }
+ else if (streq (p[0], "nobind"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.bind_local = false;
+ }
+ else if (streq (p[0], "fast-io"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->fast_io = true;
+ }
+ else if (streq (p[0], "inactive") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TIMER);
+ options->inactivity_timeout = positive_atoi (p[1]);
+ if (p[2])
+ options->inactivity_minimum_bytes = positive_atoi (p[2]);
+ }
+ else if (streq (p[0], "proto") && p[1])
+ {
+ int proto;
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ proto = ascii2proto (p[1]);
+ if (proto < 0)
+ {
+ msg (msglevel, "Bad protocol: '%s'. Allowed protocols with --proto option: %s",
+ p[1],
+ proto2ascii_all (&gc));
+ goto err;
+ }
+ options->ce.proto = proto;
+ }
+ else if (streq (p[0], "proto-force") && p[1])
+ {
+ int proto_force;
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ proto_force = ascii2proto (p[1]);
+ if (proto_force < 0)
+ {
+ msg (msglevel, "Bad --proto-force protocol: '%s'", p[1]);
+ goto err;
+ }
+ options->proto_force = proto_force;
+ options->force_connection_list = true;
+ }
+#ifdef GENERAL_PROXY_SUPPORT
+ else if (streq (p[0], "auto-proxy"))
+ {
+ char *error = NULL;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->auto_proxy_info = get_proxy_settings (&error, &options->gc);
+ if (error)
+ msg (M_WARN, "PROXY: %s", error);
+ }
+ else if (streq (p[0], "show-proxy-settings"))
+ {
+ struct auto_proxy_info *pi;
+ char *error = NULL;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ pi = get_proxy_settings (&error, &options->gc);
+ if (pi)
+ {
+ msg (M_INFO|M_NOPREFIX, "HTTP Server: %s", np(pi->http.server));
+ msg (M_INFO|M_NOPREFIX, "HTTP Port: %d", pi->http.port);
+ msg (M_INFO|M_NOPREFIX, "SOCKS Server: %s", np(pi->socks.server));
+ msg (M_INFO|M_NOPREFIX, "SOCKS Port: %d", pi->socks.port);
+ }
+ if (error)
+ msg (msglevel, "Proxy error: %s", error);
+#ifdef WIN32
+ show_win_proxy_settings (M_INFO|M_NOPREFIX);
+#endif
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+#endif /* GENERAL_PROXY_SUPPORT */
+#ifdef ENABLE_HTTP_PROXY
+ else if (streq (p[0], "http-proxy") && p[1])
+ {
+ struct http_proxy_options *ho;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+
+ {
+ int port;
+ if (!p[2])
+ {
+ msg (msglevel, "http-proxy port number not defined");
+ goto err;
+ }
+ port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "Bad http-proxy port number: %s", p[2]);
+ goto err;
+ }
+
+ ho = init_http_options_if_undefined (options);
+
+ ho->server = p[1];
+ ho->port = port;
+ }
+
+ if (p[3])
+ {
+ /* auto -- try to figure out proxy addr, port, and type automatically */
+ /* semiauto -- given proxy addr:port, try to figure out type automatically */
+ /* (auto|semiauto)-nct -- disable proxy auth cleartext protocols (i.e. basic auth) */
+ if (streq (p[3], "auto"))
+ ho->auth_retry = PAR_ALL;
+ else if (streq (p[3], "auto-nct"))
+ ho->auth_retry = PAR_NCT;
+ else
+ {
+ ho->auth_method_string = "basic";
+ ho->auth_file = p[3];
+
+ if (p[4])
+ {
+ ho->auth_method_string = p[4];
+ }
+ }
+ }
+ else
+ {
+ ho->auth_method_string = "none";
+ }
+ }
+ else if (streq (p[0], "http-proxy-retry"))
+ {
+ struct http_proxy_options *ho;
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ ho = init_http_options_if_undefined (options);
+ ho->retry = true;
+ }
+ else if (streq (p[0], "http-proxy-timeout") && p[1])
+ {
+ struct http_proxy_options *ho;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ ho = init_http_options_if_undefined (options);
+ ho->timeout = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "http-proxy-option") && p[1])
+ {
+ struct http_proxy_options *ho;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ ho = init_http_options_if_undefined (options);
+
+ if (streq (p[1], "VERSION") && p[2])
+ {
+ ho->http_version = p[2];
+ }
+ else if (streq (p[1], "AGENT") && p[2])
+ {
+ ho->user_agent = p[2];
+ }
+ else
+ {
+ msg (msglevel, "Bad http-proxy-option or missing parameter: '%s'", p[1]);
+ }
+ }
+#endif
+#ifdef ENABLE_SOCKS
+ else if (streq (p[0], "socks-proxy") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+
+ if (p[2])
+ {
+ int port;
+ port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "Bad socks-proxy port number: %s", p[2]);
+ goto err;
+ }
+ options->ce.socks_proxy_port = port;
+ }
+ else
+ {
+ options->ce.socks_proxy_port = 1080;
+ }
+ options->ce.socks_proxy_server = p[1];
+ options->ce.socks_proxy_authfile = p[3]; /* might be NULL */
+ }
+ else if (streq (p[0], "socks-proxy-retry"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
+ options->ce.socks_proxy_retry = true;
+ }
+#endif
+ else if (streq (p[0], "keepalive") && p[1] && p[2])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->keepalive_ping = atoi (p[1]);
+ options->keepalive_timeout = atoi (p[2]);
+ }
+ else if (streq (p[0], "ping") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TIMER);
+ options->ping_send_timeout = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "ping-exit") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TIMER);
+ options->ping_rec_timeout = positive_atoi (p[1]);
+ options->ping_rec_timeout_action = PING_EXIT;
+ }
+ else if (streq (p[0], "ping-restart") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TIMER);
+ options->ping_rec_timeout = positive_atoi (p[1]);
+ options->ping_rec_timeout_action = PING_RESTART;
+ }
+ else if (streq (p[0], "ping-timer-rem"))
+ {
+ VERIFY_PERMISSION (OPT_P_TIMER);
+ options->ping_timer_remote = true;
+ }
+#ifdef ENABLE_OCC
+ else if (streq (p[0], "explicit-exit-notify"))
+ {
+ VERIFY_PERMISSION (OPT_P_EXPLICIT_NOTIFY);
+ if (p[1])
+ {
+ options->explicit_exit_notification = positive_atoi (p[1]);
+ }
+ else
+ {
+ options->explicit_exit_notification = 1;
+ }
+ }
+#endif
+ else if (streq (p[0], "persist-tun"))
+ {
+ VERIFY_PERMISSION (OPT_P_PERSIST);
+ options->persist_tun = true;
+ }
+ else if (streq (p[0], "persist-key"))
+ {
+ VERIFY_PERMISSION (OPT_P_PERSIST);
+ options->persist_key = true;
+ }
+ else if (streq (p[0], "persist-local-ip"))
+ {
+ VERIFY_PERMISSION (OPT_P_PERSIST_IP);
+ options->persist_local_ip = true;
+ }
+ else if (streq (p[0], "persist-remote-ip"))
+ {
+ VERIFY_PERMISSION (OPT_P_PERSIST_IP);
+ options->persist_remote_ip = true;
+ }
+ else if (streq (p[0], "route") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE);
+ rol_check_alloc (options);
+ if (pull_mode)
+ {
+ if (!ip_or_dns_addr_safe (p[1], options->allow_pull_fqdn) && !is_special_addr (p[1])) /* FQDN -- may be DNS name */
+ {
+ msg (msglevel, "route parameter network/IP '%s' must be a valid address", p[1]);
+ goto err;
+ }
+ if (p[2] && !ip_addr_dotted_quad_safe (p[2])) /* FQDN -- must be IP address */
+ {
+ msg (msglevel, "route parameter netmask '%s' must be an IP address", p[2]);
+ goto err;
+ }
+ if (p[3] && !ip_or_dns_addr_safe (p[3], options->allow_pull_fqdn) && !is_special_addr (p[3])) /* FQDN -- may be DNS name */
+ {
+ msg (msglevel, "route parameter gateway '%s' must be a valid address", p[3]);
+ goto err;
+ }
+ }
+ add_route_to_option_list (options->routes, p[1], p[2], p[3], p[4]);
+ }
+ else if (streq (p[0], "max-routes") && p[1])
+ {
+ int max_routes;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ max_routes = atoi (p[1]);
+ if (max_routes < 0 || max_routes > 100000000)
+ {
+ msg (msglevel, "--max-routes parameter is out of range");
+ goto err;
+ }
+ options->max_routes = max_routes;
+ }
+ else if (streq (p[0], "route-gateway") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
+ if (streq (p[1], "dhcp"))
+ {
+ options->route_gateway_via_dhcp = true;
+ }
+ else
+ {
+ if (ip_or_dns_addr_safe (p[1], options->allow_pull_fqdn) || is_special_addr (p[1])) /* FQDN -- may be DNS name */
+ {
+ options->route_default_gateway = p[1];
+ }
+ else
+ {
+ msg (msglevel, "route-gateway parm '%s' must be a valid address", p[1]);
+ goto err;
+ }
+ }
+ }
+ else if (streq (p[0], "route-metric") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE);
+ options->route_default_metric = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "route-delay"))
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
+ options->route_delay_defined = true;
+ if (p[1])
+ {
+ options->route_delay = positive_atoi (p[1]);
+ if (p[2])
+ {
+ options->route_delay_window = positive_atoi (p[2]);
+ }
+ }
+ else
+ {
+ options->route_delay = 0;
+ }
+ }
+ else if (streq (p[0], "route-up") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->route_script, "route-up");
+ options->route_script = p[1];
+ }
+ else if (streq (p[0], "route-noexec"))
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ options->route_noexec = true;
+ }
+ else if (streq (p[0], "route-nopull"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->route_nopull = true;
+ }
+ else if (streq (p[0], "allow-pull-fqdn"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->allow_pull_fqdn = true;
+ }
+ else if (streq (p[0], "redirect-gateway") || streq (p[0], "redirect-private"))
+ {
+ int j;
+ VERIFY_PERMISSION (OPT_P_ROUTE);
+ rol_check_alloc (options);
+ if (streq (p[0], "redirect-gateway"))
+ options->routes->flags |= RG_REROUTE_GW;
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ {
+ if (streq (p[j], "local"))
+ options->routes->flags |= RG_LOCAL;
+ else if (streq (p[j], "autolocal"))
+ options->routes->flags |= RG_AUTO_LOCAL;
+ else if (streq (p[j], "def1"))
+ options->routes->flags |= RG_DEF1;
+ else if (streq (p[j], "bypass-dhcp"))
+ options->routes->flags |= RG_BYPASS_DHCP;
+ else if (streq (p[j], "bypass-dns"))
+ options->routes->flags |= RG_BYPASS_DNS;
+ else
+ {
+ msg (msglevel, "unknown --%s flag: %s", p[0], p[j]);
+ goto err;
+ }
+ }
+ options->routes->flags |= RG_ENABLE;
+ }
+ else if (streq (p[0], "remote-random-hostname"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->sockflags |= SF_HOST_RANDOMIZE;
+ }
+ else if (streq (p[0], "setenv") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "REMOTE_RANDOM_HOSTNAME"))
+ {
+ options->sockflags |= SF_HOST_RANDOMIZE;
+ }
+ else if (streq (p[1], "GENERIC_CONFIG"))
+ {
+ msg (msglevel, "this is a generic configuration and cannot directly be used");
+ goto err;
+ }
+#if P2MP
+ else if (streq (p[1], "SERVER_POLL_TIMEOUT") && p[2])
+ {
+ options->server_poll_timeout = positive_atoi(p[2]);
+ }
+#endif
+ else
+ {
+ if (streq (p[1], "FORWARD_COMPATIBLE") && p[2] && streq (p[2], "1"))
+ {
+ options->forward_compatible = true;
+ msglevel_fc = msglevel_forward_compatible (options, msglevel);
+ }
+ setenv_str (es, p[1], p[2] ? p[2] : "");
+ }
+ }
+ else if (streq (p[0], "setenv-safe") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SETENV);
+ setenv_str_safe (es, p[1], p[2] ? p[2] : "");
+ }
+ else if (streq (p[0], "script-security") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ script_security = atoi (p[1]);
+ if (p[2])
+ {
+ if (streq (p[2], "execve"))
+ script_method = SM_EXECVE;
+ else if (streq (p[2], "system"))
+ script_method = SM_SYSTEM;
+ else
+ {
+ msg (msglevel, "unknown --script-security method: %s", p[2]);
+ goto err;
+ }
+ }
+ else
+ script_method = SM_EXECVE;
+ }
+ else if (streq (p[0], "mssfix"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[1])
+ {
+ options->mssfix = positive_atoi (p[1]);
+ }
+ else
+ options->mssfix_default = true;
+
+ }
+#ifdef ENABLE_OCC
+ else if (streq (p[0], "disable-occ"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->occ = false;
+ }
+#endif
+#if P2MP
+#if P2MP_SERVER
+ else if (streq (p[0], "server") && p[1] && p[2])
+ {
+ const int lev = M_WARN;
+ bool error = false;
+ in_addr_t network, netmask;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ network = get_ip_addr (p[1], lev, &error);
+ netmask = get_ip_addr (p[2], lev, &error);
+ if (error || !network || !netmask)
+ {
+ msg (msglevel, "error parsing --server parameters");
+ goto err;
+ }
+ options->server_defined = true;
+ options->server_network = network;
+ options->server_netmask = netmask;
+
+ if (p[3])
+ {
+ if (streq (p[3], "nopool"))
+ options->server_flags |= SF_NOPOOL;
+ else
+ {
+ msg (msglevel, "error parsing --server: %s is not a recognized flag", p[3]);
+ goto err;
+ }
+ }
+ }
+ else if (streq (p[0], "server-bridge") && p[1] && p[2] && p[3] && p[4])
+ {
+ const int lev = M_WARN;
+ bool error = false;
+ in_addr_t ip, netmask, pool_start, pool_end;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ ip = get_ip_addr (p[1], lev, &error);
+ netmask = get_ip_addr (p[2], lev, &error);
+ pool_start = get_ip_addr (p[3], lev, &error);
+ pool_end = get_ip_addr (p[4], lev, &error);
+ if (error || !ip || !netmask || !pool_start || !pool_end)
+ {
+ msg (msglevel, "error parsing --server-bridge parameters");
+ goto err;
+ }
+ options->server_bridge_defined = true;
+ options->server_bridge_ip = ip;
+ options->server_bridge_netmask = netmask;
+ options->server_bridge_pool_start = pool_start;
+ options->server_bridge_pool_end = pool_end;
+ }
+ else if (streq (p[0], "server-bridge") && p[1] && streq (p[1], "nogw"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->server_bridge_proxy_dhcp = true;
+ options->server_flags |= SF_NO_PUSH_ROUTE_GATEWAY;
+ }
+ else if (streq (p[0], "server-bridge") && !p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->server_bridge_proxy_dhcp = true;
+ }
+ else if (streq (p[0], "push") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_PUSH);
+ push_options (options, &p[1], msglevel, &options->gc);
+ }
+ else if (streq (p[0], "push-reset"))
+ {
+ VERIFY_PERMISSION (OPT_P_INSTANCE);
+ push_reset (options);
+ }
+ else if (streq (p[0], "ifconfig-pool") && p[1] && p[2])
+ {
+ const int lev = M_WARN;
+ bool error = false;
+ in_addr_t start, end, netmask=0;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ start = get_ip_addr (p[1], lev, &error);
+ end = get_ip_addr (p[2], lev, &error);
+ if (p[3])
+ {
+ netmask = get_ip_addr (p[3], lev, &error);
+ }
+ if (error)
+ {
+ msg (msglevel, "error parsing --ifconfig-pool parameters");
+ goto err;
+ }
+ if (!ifconfig_pool_verify_range (msglevel, start, end))
+ goto err;
+
+ options->ifconfig_pool_defined = true;
+ options->ifconfig_pool_start = start;
+ options->ifconfig_pool_end = end;
+ if (netmask)
+ options->ifconfig_pool_netmask = netmask;
+ }
+ else if (streq (p[0], "ifconfig-pool-persist") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ifconfig_pool_persist_filename = p[1];
+ if (p[2])
+ {
+ options->ifconfig_pool_persist_refresh_freq = positive_atoi (p[2]);
+ }
+ }
+ else if (streq (p[0], "ifconfig-pool-linear"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->topology = TOP_P2P;
+ }
+ else if (streq (p[0], "hash-size") && p[1] && p[2])
+ {
+ int real, virtual;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ real = atoi (p[1]);
+ virtual = atoi (p[2]);
+ if (real < 1 || virtual < 1)
+ {
+ msg (msglevel, "--hash-size sizes must be >= 1 (preferably a power of 2)");
+ goto err;
+ }
+ options->real_hash_size = real;
+ options->virtual_hash_size = real;
+ }
+ else if (streq (p[0], "connect-freq") && p[1] && p[2])
+ {
+ int cf_max, cf_per;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ cf_max = atoi (p[1]);
+ cf_per = atoi (p[2]);
+ if (cf_max < 0 || cf_per < 0)
+ {
+ msg (msglevel, "--connect-freq parms must be > 0");
+ goto err;
+ }
+ options->cf_max = cf_max;
+ options->cf_per = cf_per;
+ }
+ else if (streq (p[0], "max-clients") && p[1])
+ {
+ int max_clients;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ max_clients = atoi (p[1]);
+ if (max_clients < 0)
+ {
+ msg (msglevel, "--max-clients must be at least 1");
+ goto err;
+ }
+ options->max_clients = max_clients;
+ }
+ else if (streq (p[0], "max-routes-per-client") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_INHERIT);
+ options->max_routes_per_client = max_int (atoi (p[1]), 1);
+ }
+ else if (streq (p[0], "client-cert-not-required"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ssl_flags |= SSLF_CLIENT_CERT_NOT_REQUIRED;
+ }
+ else if (streq (p[0], "username-as-common-name"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ssl_flags |= SSLF_USERNAME_AS_COMMON_NAME;
+ }
+ else if (streq (p[0], "auth-user-pass-optional"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ssl_flags |= SSLF_AUTH_USER_PASS_OPTIONAL;
+ }
+ else if (streq (p[0], "no-name-remapping"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ssl_flags |= SSLF_NO_NAME_REMAPPING;
+ }
+ else if (streq (p[0], "opt-verify"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ssl_flags |= SSLF_OPT_VERIFY;
+ }
+ else if (streq (p[0], "auth-user-pass-verify") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 3, NM_QUOTE_HINT))
+ goto err;
+ if (p[2])
+ {
+ if (streq (p[2], "via-env"))
+ options->auth_user_pass_verify_script_via_file = false;
+ else if (streq (p[2], "via-file"))
+ options->auth_user_pass_verify_script_via_file = true;
+ else
+ {
+ msg (msglevel, "second parm to --auth-user-pass-verify must be 'via-env' or 'via-file'");
+ goto err;
+ }
+ }
+ else
+ {
+ msg (msglevel, "--auth-user-pass-verify requires a second parameter ('via-env' or 'via-file')");
+ goto err;
+ }
+ warn_multiple_script (options->auth_user_pass_verify_script, "auth-user-pass-verify");
+ options->auth_user_pass_verify_script = p[1];
+ }
+ else if (streq (p[0], "client-connect") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->client_connect_script, "client-connect");
+ options->client_connect_script = p[1];
+ }
+ else if (streq (p[0], "client-disconnect") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->client_disconnect_script, "client-disconnect");
+ options->client_disconnect_script = p[1];
+ }
+ else if (streq (p[0], "learn-address") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->learn_address_script, "learn-address");
+ options->learn_address_script = p[1];
+ }
+ else if (streq (p[0], "tmp-dir") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tmp_dir = p[1];
+ }
+ else if (streq (p[0], "client-config-dir") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->client_config_dir = p[1];
+ }
+ else if (streq (p[0], "ccd-exclusive"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ccd_exclusive = true;
+ }
+ else if (streq (p[0], "bcast-buffers") && p[1])
+ {
+ int n_bcast_buf;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ n_bcast_buf = atoi (p[1]);
+ if (n_bcast_buf < 1)
+ msg (msglevel, "--bcast-buffers parameter must be > 0");
+ options->n_bcast_buf = n_bcast_buf;
+ }
+ else if (streq (p[0], "tcp-queue-limit") && p[1])
+ {
+ int tcp_queue_limit;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ tcp_queue_limit = atoi (p[1]);
+ if (tcp_queue_limit < 1)
+ msg (msglevel, "--tcp-queue-limit parameter must be > 0");
+ options->tcp_queue_limit = tcp_queue_limit;
+ }
+#if PORT_SHARE
+ else if (streq (p[0], "port-share") && p[1] && p[2])
+ {
+ int port;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ port = atoi (p[2]);
+ if (!legal_ipv4_port (port))
+ {
+ msg (msglevel, "port number associated with --port-share directive is out of range");
+ goto err;
+ }
+
+ options->port_share_host = p[1];
+ options->port_share_port = port;
+ }
+#endif
+ else if (streq (p[0], "client-to-client"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->enable_c2c = true;
+ }
+ else if (streq (p[0], "duplicate-cn"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->duplicate_cn = true;
+ }
+ else if (streq (p[0], "iroute") && p[1])
+ {
+ const char *netmask = NULL;
+
+ VERIFY_PERMISSION (OPT_P_INSTANCE);
+ if (p[2])
+ {
+ netmask = p[2];
+ }
+ option_iroute (options, p[1], netmask, msglevel);
+ }
+ else if (streq (p[0], "ifconfig-push") && p[1] && p[2])
+ {
+ in_addr_t local, remote_netmask;
+
+ VERIFY_PERMISSION (OPT_P_INSTANCE);
+ local = getaddr (GETADDR_HOST_ORDER|GETADDR_RESOLVE, p[1], 0, NULL, NULL);
+ remote_netmask = getaddr (GETADDR_HOST_ORDER|GETADDR_RESOLVE, p[2], 0, NULL, NULL);
+ if (local && remote_netmask)
+ {
+ options->push_ifconfig_defined = true;
+ options->push_ifconfig_local = local;
+ options->push_ifconfig_remote_netmask = remote_netmask;
+ }
+ else
+ {
+ msg (msglevel, "cannot parse --ifconfig-push addresses");
+ goto err;
+ }
+ }
+ else if (streq (p[0], "ifconfig-push-constraint") && p[1] && p[2])
+ {
+ in_addr_t network, netmask;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ network = getaddr (GETADDR_HOST_ORDER|GETADDR_RESOLVE, p[1], 0, NULL, NULL);
+ netmask = getaddr (GETADDR_HOST_ORDER, p[2], 0, NULL, NULL);
+ if (network && netmask)
+ {
+ options->push_ifconfig_constraint_defined = true;
+ options->push_ifconfig_constraint_network = network;
+ options->push_ifconfig_constraint_netmask = netmask;
+ }
+ else
+ {
+ msg (msglevel, "cannot parse --ifconfig-push-constraint addresses");
+ goto err;
+ }
+ }
+ else if (streq (p[0], "disable"))
+ {
+ VERIFY_PERMISSION (OPT_P_INSTANCE);
+ options->disable = true;
+ }
+ else if (streq (p[0], "tcp-nodelay"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->server_flags |= SF_TCP_NODELAY_HELPER;
+ }
+#endif /* P2MP_SERVER */
+
+ else if (streq (p[0], "client"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->client = true;
+ }
+ else if (streq (p[0], "pull"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pull = true;
+ }
+ else if (streq (p[0], "push-continuation") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_PULL_MODE);
+ options->push_continuation = atoi(p[1]);
+ }
+ else if (streq (p[0], "server-poll-timeout") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->server_poll_timeout = positive_atoi(p[1]);
+ }
+ else if (streq (p[0], "auth-user-pass"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[1])
+ {
+ options->auth_user_pass_file = p[1];
+ }
+ else
+ options->auth_user_pass_file = "stdin";
+ }
+ else if (streq (p[0], "auth-retry") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ auth_retry_set (msglevel, p[1]);
+ }
+#endif
+#ifdef WIN32
+ else if (streq (p[0], "win-sys") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "env"))
+ set_win_sys_path_via_env (es);
+ else
+ set_win_sys_path (p[1], es);
+ }
+ else if (streq (p[0], "route-method") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
+ if (streq (p[1], "adaptive"))
+ options->route_method = ROUTE_METHOD_ADAPTIVE;
+ else if (streq (p[1], "ipapi"))
+ options->route_method = ROUTE_METHOD_IPAPI;
+ else if (streq (p[1], "exe"))
+ options->route_method = ROUTE_METHOD_EXE;
+ else
+ {
+ msg (msglevel, "--route method must be 'adaptive', 'ipapi', or 'exe'");
+ goto err;
+ }
+ }
+ else if (streq (p[0], "ip-win32") && p[1])
+ {
+ const int index = ascii2ipset (p[1]);
+ struct tuntap_options *to = &options->tuntap_options;
+
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+
+ if (index < 0)
+ {
+ msg (msglevel,
+ "Bad --ip-win32 method: '%s'. Allowed methods: %s",
+ p[1],
+ ipset2ascii_all (&gc));
+ goto err;
+ }
+
+ if (index == IPW32_SET_ADAPTIVE)
+ options->route_delay_window = IPW32_SET_ADAPTIVE_DELAY_WINDOW;
+
+ if (index == IPW32_SET_DHCP_MASQ)
+ {
+ if (p[2])
+ {
+ if (!streq (p[2], "default"))
+ {
+ int offset = atoi (p[2]);
+
+ if (!(offset > -256 && offset < 256))
+ {
+ msg (msglevel, "--ip-win32 dynamic [offset] [lease-time]: offset (%d) must be > -256 and < 256", offset);
+ goto err;
+ }
+
+ to->dhcp_masq_custom_offset = true;
+ to->dhcp_masq_offset = offset;
+ }
+
+ if (p[3])
+ {
+ const int min_lease = 30;
+ int lease_time;
+ lease_time = atoi (p[3]);
+ if (lease_time < min_lease)
+ {
+ msg (msglevel, "--ip-win32 dynamic [offset] [lease-time]: lease time parameter (%d) must be at least %d seconds", lease_time, min_lease);
+ goto err;
+ }
+ to->dhcp_lease_time = lease_time;
+ }
+ }
+ }
+ to->ip_win32_type = index;
+ to->ip_win32_defined = true;
+ }
+ else if (streq (p[0], "dhcp-option") && p[1])
+ {
+ struct tuntap_options *o = &options->tuntap_options;
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+
+ if (streq (p[1], "DOMAIN") && p[2])
+ {
+ o->domain = p[2];
+ }
+ else if (streq (p[1], "NBS") && p[2])
+ {
+ o->netbios_scope = p[2];
+ }
+ else if (streq (p[1], "NBT") && p[2])
+ {
+ int t;
+ t = atoi (p[2]);
+ if (!(t == 1 || t == 2 || t == 4 || t == 8))
+ {
+ msg (msglevel, "--dhcp-option NBT: parameter (%d) must be 1, 2, 4, or 8", t);
+ goto err;
+ }
+ o->netbios_node_type = t;
+ }
+ else if (streq (p[1], "DNS") && p[2])
+ {
+ dhcp_option_address_parse ("DNS", p[2], o->dns, &o->dns_len, msglevel);
+ }
+ else if (streq (p[1], "WINS") && p[2])
+ {
+ dhcp_option_address_parse ("WINS", p[2], o->wins, &o->wins_len, msglevel);
+ }
+ else if (streq (p[1], "NTP") && p[2])
+ {
+ dhcp_option_address_parse ("NTP", p[2], o->ntp, &o->ntp_len, msglevel);
+ }
+ else if (streq (p[1], "NBDD") && p[2])
+ {
+ dhcp_option_address_parse ("NBDD", p[2], o->nbdd, &o->nbdd_len, msglevel);
+ }
+ else if (streq (p[1], "DISABLE-NBT"))
+ {
+ o->disable_nbt = 1;
+ }
+ else
+ {
+ msg (msglevel, "--dhcp-option: unknown option type '%s' or missing parameter", p[1]);
+ goto err;
+ }
+ o->dhcp_options = true;
+ }
+ else if (streq (p[0], "show-adapters"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ show_tap_win32_adapters (M_INFO|M_NOPREFIX, M_WARN|M_NOPREFIX);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "show-net"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ show_routes (M_INFO|M_NOPREFIX);
+ show_adapters (M_INFO|M_NOPREFIX);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "show-net-up"))
+ {
+ VERIFY_PERMISSION (OPT_P_UP);
+ options->show_net_up = true;
+ }
+ else if (streq (p[0], "tap-sleep") && p[1])
+ {
+ int s;
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ s = atoi (p[1]);
+ if (s < 0 || s >= 256)
+ {
+ msg (msglevel, "--tap-sleep parameter must be between 0 and 255");
+ goto err;
+ }
+ options->tuntap_options.tap_sleep = s;
+ }
+ else if (streq (p[0], "dhcp-renew"))
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ options->tuntap_options.dhcp_renew = true;
+ }
+ else if (streq (p[0], "dhcp-pre-release"))
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ options->tuntap_options.dhcp_pre_release = true;
+ }
+ else if (streq (p[0], "dhcp-release"))
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ options->tuntap_options.dhcp_release = true;
+ }
+ else if (streq (p[0], "dhcp-internal") && p[1]) /* standalone method for internal use */
+ {
+ unsigned int adapter_index;
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ set_debug_level (options->verbosity, SDL_CONSTRAIN);
+ adapter_index = atou (p[1]);
+ sleep (options->tuntap_options.tap_sleep);
+ if (options->tuntap_options.dhcp_pre_release)
+ dhcp_release_by_adapter_index (adapter_index);
+ if (options->tuntap_options.dhcp_renew)
+ dhcp_renew_by_adapter_index (adapter_index);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "register-dns"))
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ options->tuntap_options.register_dns = true;
+ }
+ else if (streq (p[0], "rdns-internal"))
+ /* standalone method for internal use
+ *
+ * (if --register-dns is set, openvpn needs to call itself in a
+ * sub-process to execute the required functions in a non-blocking
+ * way, and uses --rdns-internal to signal that to itself)
+ */
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ set_debug_level (options->verbosity, SDL_CONSTRAIN);
+ if (options->tuntap_options.register_dns)
+ ipconfig_register_dns (NULL);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "show-valid-subnets"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ show_valid_win32_tun_subnets ();
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "pause-exit"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ set_pause_exit_win32 ();
+ }
+ else if (streq (p[0], "service") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->exit_event_name = p[1];
+ if (p[2])
+ {
+ options->exit_event_initial_state = (atoi(p[2]) != 0);
+ }
+ }
+ else if (streq (p[0], "allow-nonadmin"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ tap_allow_nonadmin_access (p[1]);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "user") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ msg (M_WARN, "NOTE: --user option is not implemented on Windows");
+ }
+ else if (streq (p[0], "group") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ msg (M_WARN, "NOTE: --group option is not implemented on Windows");
+ }
+#else
+ else if (streq (p[0], "user") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->username = p[1];
+ }
+ else if (streq (p[0], "group") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->groupname = p[1];
+ }
+ else if (streq (p[0], "dhcp-option") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_IPWIN32);
+ foreign_option (options, p, 3, es);
+ }
+ else if (streq (p[0], "route-method") && p[1]) /* ignore when pushed to non-Windows OS */
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
+ }
+#endif
+#if PASSTOS_CAPABILITY
+ else if (streq (p[0], "passtos"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->passtos = true;
+ }
+#endif
+#ifdef USE_LZO
+ else if (streq (p[0], "comp-lzo"))
+ {
+ VERIFY_PERMISSION (OPT_P_COMP);
+ if (p[1])
+ {
+ if (streq (p[1], "yes"))
+ options->lzo = LZO_SELECTED|LZO_ON;
+ else if (streq (p[1], "no"))
+ options->lzo = LZO_SELECTED;
+ else if (streq (p[1], "adaptive"))
+ options->lzo = LZO_SELECTED|LZO_ON|LZO_ADAPTIVE;
+ else
+ {
+ msg (msglevel, "bad comp-lzo option: %s -- must be 'yes', 'no', or 'adaptive'", p[1]);
+ goto err;
+ }
+ }
+ else
+ options->lzo = LZO_SELECTED|LZO_ON|LZO_ADAPTIVE;
+ }
+ else if (streq (p[0], "comp-noadapt"))
+ {
+ VERIFY_PERMISSION (OPT_P_COMP);
+ options->lzo &= ~LZO_ADAPTIVE;
+ }
+#endif /* USE_LZO */
+#ifdef USE_CRYPTO
+ else if (streq (p[0], "show-ciphers"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->show_ciphers = true;
+ }
+ else if (streq (p[0], "show-digests"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->show_digests = true;
+ }
+ else if (streq (p[0], "show-engines"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->show_engines = true;
+ }
+ else if (streq (p[0], "key-direction") && p[1])
+ {
+ int key_direction;
+
+ key_direction = ascii2keydirection (msglevel, p[1]);
+ if (key_direction >= 0)
+ options->key_direction = key_direction;
+ else
+ goto err;
+ }
+ else if (streq (p[0], "secret") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->shared_secret_file_inline = p[2];
+ }
+ else
+#endif
+ if (p[2])
+ {
+ int key_direction;
+
+ key_direction = ascii2keydirection (msglevel, p[2]);
+ if (key_direction >= 0)
+ options->key_direction = key_direction;
+ else
+ goto err;
+ }
+ options->shared_secret_file = p[1];
+ }
+ else if (streq (p[0], "genkey"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->genkey = true;
+ }
+ else if (streq (p[0], "auth") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->authname_defined = true;
+ options->authname = p[1];
+ if (streq (options->authname, "none"))
+ {
+ options->authname_defined = false;
+ options->authname = NULL;
+ }
+ }
+ else if (streq (p[0], "auth"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->authname_defined = true;
+ }
+ else if (streq (p[0], "cipher") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->ciphername_defined = true;
+ options->ciphername = p[1];
+ if (streq (options->ciphername, "none"))
+ {
+ options->ciphername_defined = false;
+ options->ciphername = NULL;
+ }
+ }
+ else if (streq (p[0], "cipher"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->ciphername_defined = true;
+ }
+ else if (streq (p[0], "prng") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ if (streq (p[1], "none"))
+ options->prng_hash = NULL;
+ else
+ options->prng_hash = p[1];
+ if (p[2])
+ {
+ const int sl = atoi (p[2]);
+ if (sl >= NONCE_SECRET_LEN_MIN && sl <= NONCE_SECRET_LEN_MAX)
+ {
+ options->prng_nonce_secret_len = sl;
+ }
+ else
+ {
+ msg (msglevel, "prng parameter nonce_secret_len must be between %d and %d",
+ NONCE_SECRET_LEN_MIN, NONCE_SECRET_LEN_MAX);
+ goto err;
+ }
+ }
+ }
+ else if (streq (p[0], "no-replay"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->replay = false;
+ }
+ else if (streq (p[0], "replay-window"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ if (p[1])
+ {
+ int replay_window;
+
+ replay_window = atoi (p[1]);
+ if (!(MIN_SEQ_BACKTRACK <= replay_window && replay_window <= MAX_SEQ_BACKTRACK))
+ {
+ msg (msglevel, "replay-window window size parameter (%d) must be between %d and %d",
+ replay_window,
+ MIN_SEQ_BACKTRACK,
+ MAX_SEQ_BACKTRACK);
+ goto err;
+ }
+ options->replay_window = replay_window;
+
+ if (p[2])
+ {
+ int replay_time;
+
+ replay_time = atoi (p[2]);
+ if (!(MIN_TIME_BACKTRACK <= replay_time && replay_time <= MAX_TIME_BACKTRACK))
+ {
+ msg (msglevel, "replay-window time window parameter (%d) must be between %d and %d",
+ replay_time,
+ MIN_TIME_BACKTRACK,
+ MAX_TIME_BACKTRACK);
+ goto err;
+ }
+ options->replay_time = replay_time;
+ }
+ }
+ else
+ {
+ msg (msglevel, "replay-window option is missing window size parameter");
+ goto err;
+ }
+ }
+ else if (streq (p[0], "mute-replay-warnings"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->mute_replay_warnings = true;
+ }
+ else if (streq (p[0], "no-iv"))
+ {
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ options->use_iv = false;
+ }
+ else if (streq (p[0], "replay-persist") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->packet_id_file = p[1];
+ }
+ else if (streq (p[0], "test-crypto"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->test_crypto = true;
+ }
+ else if (streq (p[0], "engine"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[1])
+ {
+ options->engine = p[1];
+ }
+ else
+ options->engine = "auto";
+ }
+#ifdef HAVE_EVP_CIPHER_CTX_SET_KEY_LENGTH
+ else if (streq (p[0], "keysize") && p[1])
+ {
+ int keysize;
+
+ VERIFY_PERMISSION (OPT_P_CRYPTO);
+ keysize = atoi (p[1]) / 8;
+ if (keysize < 0 || keysize > MAX_CIPHER_KEY_LENGTH)
+ {
+ msg (msglevel, "Bad keysize: %s", p[1]);
+ goto err;
+ }
+ options->keysize = keysize;
+ }
+#endif
+#ifdef USE_SSL
+ else if (streq (p[0], "show-tls"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->show_tls_ciphers = true;
+ }
+ else if (streq (p[0], "tls-server"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tls_server = true;
+ }
+ else if (streq (p[0], "tls-client"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tls_client = true;
+ }
+ else if (streq (p[0], "ca") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ca_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->ca_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "capath") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->ca_path = p[1];
+ }
+ else if (streq (p[0], "dh") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->dh_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->dh_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "cert") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->cert_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->cert_file_inline = p[2];
+ }
+#endif
+ }
+#ifdef WIN32
+ else if (streq (p[0], "cryptoapicert") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->cryptoapi_cert = p[1];
+ }
+#endif
+ else if (streq (p[0], "key") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->priv_key_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->priv_key_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "pkcs12") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs12_file = p[1];
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->pkcs12_file_inline = p[2];
+ }
+#endif
+ }
+ else if (streq (p[0], "askpass"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (p[1])
+ {
+ options->key_pass_file = p[1];
+ }
+ else
+ options->key_pass_file = "stdin";
+ }
+ else if (streq (p[0], "auth-nocache"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ ssl_set_auth_nocache ();
+ }
+ else if (streq (p[0], "single-session"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->single_session = true;
+ }
+#ifdef ENABLE_PUSH_PEER_INFO
+ else if (streq (p[0], "push-peer-info"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->push_peer_info = true;
+ }
+#endif
+ else if (streq (p[0], "tls-exit"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tls_exit = true;
+ }
+ else if (streq (p[0], "tls-cipher") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->cipher_list = p[1];
+ }
+ else if (streq (p[0], "crl-verify") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->crl_file = p[1];
+ }
+ else if (streq (p[0], "tls-verify") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SCRIPT);
+ if (!no_more_than_n_args (msglevel, p, 2, NM_QUOTE_HINT))
+ goto err;
+ warn_multiple_script (options->tls_verify, "tls-verify");
+ options->tls_verify = string_substitute (p[1], ',', ' ', &options->gc);
+ }
+ else if (streq (p[0], "tls-export-cert") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tls_export_cert = p[1];
+ }
+ else if (streq (p[0], "tls-remote") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->tls_remote = p[1];
+ }
+ else if (streq (p[0], "ns-cert-type") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ if (streq (p[1], "server"))
+ options->ns_cert_type = NS_SSL_SERVER;
+ else if (streq (p[1], "client"))
+ options->ns_cert_type = NS_SSL_CLIENT;
+ else
+ {
+ msg (msglevel, "--ns-cert-type must be 'client' or 'server'");
+ goto err;
+ }
+ }
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ else if (streq (p[0], "remote-cert-ku"))
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ sscanf (p[j], "%x", &(options->remote_cert_ku[j-1]));
+ }
+ else if (streq (p[0], "remote-cert-eku") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->remote_cert_eku = p[1];
+ }
+ else if (streq (p[0], "remote-cert-tls") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ if (streq (p[1], "server"))
+ {
+ options->remote_cert_ku[0] = 0xa0;
+ options->remote_cert_ku[1] = 0x88;
+ options->remote_cert_eku = "TLS Web Server Authentication";
+ }
+ else if (streq (p[1], "client"))
+ {
+ options->remote_cert_ku[0] = 0x80;
+ options->remote_cert_ku[1] = 0x08;
+ options->remote_cert_ku[2] = 0x88;
+ options->remote_cert_eku = "TLS Web Client Authentication";
+ }
+ else
+ {
+ msg (msglevel, "--remote-cert-tls must be 'client' or 'server'");
+ goto err;
+ }
+ }
+#endif /* OPENSSL_VERSION_NUMBER */
+ else if (streq (p[0], "tls-timeout") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->tls_timeout = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "reneg-bytes") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->renegotiate_bytes = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "reneg-pkts") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->renegotiate_packets = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "reneg-sec") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->renegotiate_seconds = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "hand-window") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->handshake_window = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "tran-window") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_TLS_PARMS);
+ options->transition_window = positive_atoi (p[1]);
+ }
+ else if (streq (p[0], "tls-auth") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+#if ENABLE_INLINE_FILES
+ if (streq (p[1], INLINE_FILE_TAG) && p[2])
+ {
+ options->tls_auth_file_inline = p[2];
+ }
+ else
+#endif
+ if (p[2])
+ {
+ int key_direction;
+
+ key_direction = ascii2keydirection (msglevel, p[2]);
+ if (key_direction >= 0)
+ options->key_direction = key_direction;
+ else
+ goto err;
+ }
+ options->tls_auth_file = p[1];
+ }
+ else if (streq (p[0], "key-method") && p[1])
+ {
+ int key_method;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ key_method = atoi (p[1]);
+ if (key_method < KEY_METHOD_MIN || key_method > KEY_METHOD_MAX)
+ {
+ msg (msglevel, "key_method parameter (%d) must be >= %d and <= %d",
+ key_method,
+ KEY_METHOD_MIN,
+ KEY_METHOD_MAX);
+ goto err;
+ }
+ options->key_method = key_method;
+ }
+#ifdef ENABLE_X509ALTUSERNAME
+ else if (streq (p[0], "x509-username-field") && p[1])
+ {
+ char *s = p[1];
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ while ((*s = toupper(*s)) != '\0') s++; /* Uppercase if necessary */
+ options->x509_username_field = p[1];
+ }
+#endif /* ENABLE_X509ALTUSERNAME */
+#endif /* USE_SSL */
+#endif /* USE_CRYPTO */
+#ifdef ENABLE_PKCS11
+ else if (streq (p[0], "show-pkcs11-ids") && p[1])
+ {
+ char *provider = p[1];
+ bool cert_private = (p[2] == NULL ? false : ( atoi (p[2]) != 0 ));
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ set_debug_level (options->verbosity, SDL_CONSTRAIN);
+ show_pkcs11_ids (provider, cert_private);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
+ else if (streq (p[0], "pkcs11-providers") && p[1])
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ options->pkcs11_providers[j-1] = p[j];
+ }
+ else if (streq (p[0], "pkcs11-protected-authentication"))
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ options->pkcs11_protected_authentication[j-1] = atoi (p[j]) != 0 ? 1 : 0;
+ }
+ else if (streq (p[0], "pkcs11-private-mode") && p[1])
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ sscanf (p[j], "%x", &(options->pkcs11_private_mode[j-1]));
+ }
+ else if (streq (p[0], "pkcs11-cert-private"))
+ {
+ int j;
+
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+
+ for (j = 1; j < MAX_PARMS && p[j] != NULL; ++j)
+ options->pkcs11_cert_private[j-1] = atoi (p[j]) != 0 ? 1 : 0;
+ }
+ else if (streq (p[0], "pkcs11-pin-cache") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs11_pin_cache_period = atoi (p[1]);
+ }
+ else if (streq (p[0], "pkcs11-id") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs11_id = p[1];
+ }
+ else if (streq (p[0], "pkcs11-id-management"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->pkcs11_id_management = true;
+ }
+#endif
+#ifdef TUNSETPERSIST
+ else if (streq (p[0], "rmtun"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->persist_config = true;
+ options->persist_mode = 0;
+ }
+ else if (streq (p[0], "mktun"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->persist_config = true;
+ options->persist_mode = 1;
+ }
+#endif
+ else
+ {
+ if (file)
+ msg (msglevel_fc, "Unrecognized option or missing parameter(s) in %s:%d: %s (%s)", file, line, p[0], PACKAGE_VERSION);
+ else
+ msg (msglevel_fc, "Unrecognized option or missing parameter(s): --%s (%s)", p[0], PACKAGE_VERSION);
+ }
+ err:
+ gc_free (&gc);
+}
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..dd49355
--- /dev/null
+++ b/options.h
@@ -0,0 +1,759 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 2004-01-28: Added Socks5 proxy support
+ * (Christof Meerwald, http://cmeerw.org)
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include "basic.h"
+#include "common.h"
+#include "mtu.h"
+#include "route.h"
+#include "tun.h"
+#include "socket.h"
+#include "plugin.h"
+#include "manage.h"
+#include "proxy.h"
+#include "lzo.h"
+#include "pushlist.h"
+
+/*
+ * Maximum number of parameters associated with an option,
+ * including the option name itself.
+ */
+#define MAX_PARMS 16
+
+/*
+ * Max size of options line and parameter.
+ */
+#define OPTION_PARM_SIZE 256
+#define OPTION_LINE_SIZE 256
+
+extern const char title_string[];
+
+#if P2MP
+
+/* certain options are saved before --pull modifications are applied */
+struct options_pre_pull
+{
+ bool tuntap_options_defined;
+ struct tuntap_options tuntap_options;
+
+ bool routes_defined;
+ struct route_option_list *routes;
+
+ int foreign_option_index;
+};
+
+#endif
+
+struct connection_entry
+{
+ int proto;
+ int local_port;
+ bool local_port_defined;
+ int remote_port;
+ bool port_option_used;
+ const char *local;
+ const char *remote;
+ bool remote_float;
+ bool bind_defined;
+ bool bind_local;
+ int connect_retry_seconds;
+ bool connect_retry_defined;
+ int connect_retry_max;
+ int connect_timeout;
+ bool connect_timeout_defined;
+#ifdef ENABLE_HTTP_PROXY
+ struct http_proxy_options *http_proxy_options;
+#endif
+#ifdef ENABLE_SOCKS
+ const char *socks_proxy_server;
+ int socks_proxy_port;
+ const char *socks_proxy_authfile;
+ bool socks_proxy_retry;
+#endif
+
+# define CE_DISABLED (1<<0)
+#if HTTP_PROXY_FALLBACK
+# define CE_HTTP_PROXY_FALLBACK (1<<1)
+ time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */
+#endif
+
+ unsigned int flags;
+};
+
+struct remote_entry
+{
+ const char *remote;
+ int remote_port;
+ int proto;
+};
+
+#ifdef ENABLE_CONNECTION
+
+#define CONNECTION_LIST_SIZE 64
+
+struct connection_list
+{
+ int len;
+ int current;
+ int n_cycles;
+ bool no_advance;
+ struct connection_entry *array[CONNECTION_LIST_SIZE];
+};
+
+struct remote_list
+{
+ int len;
+ struct remote_entry *array[CONNECTION_LIST_SIZE];
+};
+
+#endif
+
+#if HTTP_PROXY_FALLBACK
+struct hpo_store
+{
+ struct http_proxy_options hpo;
+ char server[80];
+};
+#endif
+
+/* Command line options */
+struct options
+{
+ struct gc_arena gc;
+ bool gc_owned;
+
+ /* first config file */
+ const char *config;
+
+ /* major mode */
+# define MODE_POINT_TO_POINT 0
+# define MODE_SERVER 1
+ int mode;
+
+ /* enable forward compatibility for post-2.1 features */
+ bool forward_compatible;
+
+ /* persist parms */
+ bool persist_config;
+ int persist_mode;
+
+#ifdef USE_CRYPTO
+ const char *key_pass_file;
+ bool show_ciphers;
+ bool show_digests;
+ bool show_engines;
+#ifdef USE_SSL
+ bool show_tls_ciphers;
+#endif
+ bool genkey;
+#endif
+
+ /* Networking parms */
+ struct connection_entry ce;
+
+#ifdef ENABLE_CONNECTION
+ char *remote_ip_hint;
+ struct connection_list *connection_list;
+ struct remote_list *remote_list;
+ bool force_connection_list;
+#endif
+
+#ifdef GENERAL_PROXY_SUPPORT
+ struct auto_proxy_info *auto_proxy_info;
+#endif
+
+#if HTTP_PROXY_FALLBACK
+ bool http_proxy_fallback;
+ struct http_proxy_options *http_proxy_override;
+ struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */
+#endif
+
+ bool remote_random;
+ const char *ipchange;
+ const char *dev;
+ const char *dev_type;
+ const char *dev_node;
+ const char *lladdr;
+ int topology; /* one of the TOP_x values from proto.h */
+ const char *ifconfig_local;
+ const char *ifconfig_remote_netmask;
+ bool ifconfig_noexec;
+ bool ifconfig_nowarn;
+#ifdef HAVE_GETTIMEOFDAY
+ int shaper;
+#endif
+ int tun_mtu; /* MTU of tun device */
+ int tun_mtu_extra;
+ bool tun_mtu_extra_defined;
+ int link_mtu; /* MTU of device over which tunnel packets pass via TCP/UDP */
+ bool tun_mtu_defined; /* true if user overriding parm with command line option */
+ bool link_mtu_defined; /* true if user overriding parm with command line option */
+
+ int proto_force;
+
+ /* Advanced MTU negotiation and datagram fragmentation options */
+ int mtu_discover_type; /* used if OS supports setting Path MTU discovery options on socket */
+
+#ifdef ENABLE_OCC
+ bool mtu_test;
+#endif
+
+ int fragment; /* internal fragmentation size */
+
+ bool mlock;
+
+ int keepalive_ping; /* a proxy for ping/ping-restart */
+ int keepalive_timeout;
+
+ int inactivity_timeout; /* --inactive */
+ int inactivity_minimum_bytes;
+
+ int ping_send_timeout; /* Send a TCP/UDP ping to remote every n seconds */
+ int ping_rec_timeout; /* Expect a TCP/UDP ping from remote at least once every n seconds */
+ bool ping_timer_remote; /* Run ping timer only if we have a remote address */
+ bool tun_ipv6; /* Build tun dev that supports IPv6 */
+
+# define PING_UNDEF 0
+# define PING_EXIT 1
+# define PING_RESTART 2
+ int ping_rec_timeout_action; /* What action to take on ping_rec_timeout (exit or restart)? */
+
+#ifdef ENABLE_OCC
+ int explicit_exit_notification; /* Explicitly tell peer when we are exiting via OCC_EXIT message */
+#endif
+
+ bool persist_tun; /* Don't close/reopen TUN/TAP dev on SIGUSR1 or PING_RESTART */
+ bool persist_local_ip; /* Don't re-resolve local address on SIGUSR1 or PING_RESTART */
+ bool persist_remote_ip; /* Don't re-resolve remote address on SIGUSR1 or PING_RESTART */
+ bool persist_key; /* Don't re-read key files on SIGUSR1 or PING_RESTART */
+
+ int mssfix; /* Upper bound on TCP MSS */
+ bool mssfix_default; /* true if --mssfix was supplied without a parameter */
+
+#if PASSTOS_CAPABILITY
+ bool passtos;
+#endif
+
+ int resolve_retry_seconds; /* If hostname resolve fails, retry for n seconds */
+
+ struct tuntap_options tuntap_options;
+
+ /* Misc parms */
+ const char *username;
+ const char *groupname;
+ const char *chroot_dir;
+ const char *cd_dir;
+#ifdef HAVE_SETCON
+ char *selinux_context;
+#endif
+ const char *writepid;
+ const char *up_script;
+ const char *down_script;
+ bool down_pre;
+ bool up_delay;
+ bool up_restart;
+ bool daemon;
+
+ int remap_sigusr1;
+
+ /* inetd modes defined in socket.h */
+ int inetd;
+
+ bool log;
+ bool suppress_timestamps;
+ int nice;
+ int verbosity;
+ int mute;
+
+#ifdef ENABLE_DEBUG
+ int gremlin;
+#endif
+
+ const char *status_file;
+ int status_file_version;
+ int status_file_update_freq;
+
+ /* optimize TUN/TAP/UDP writes */
+ bool fast_io;
+
+#ifdef USE_LZO
+ /* LZO_x flags from lzo.h */
+ unsigned int lzo;
+#endif
+
+ /* buffer sizes */
+ int rcvbuf;
+ int sndbuf;
+
+ /* socket flags */
+ unsigned int sockflags;
+
+ /* route management */
+ const char *route_script;
+ const char *route_default_gateway;
+ int route_default_metric;
+ bool route_noexec;
+ int route_delay;
+ int route_delay_window;
+ bool route_delay_defined;
+ int max_routes;
+ struct route_option_list *routes;
+ bool route_nopull;
+ bool route_gateway_via_dhcp;
+ bool allow_pull_fqdn; /* as a client, allow server to push a FQDN for certain parameters */
+
+#ifdef ENABLE_OCC
+ /* Enable options consistency check between peers */
+ bool occ;
+#endif
+
+#ifdef ENABLE_MANAGEMENT
+ const char *management_addr;
+ int management_port;
+ const char *management_user_pass;
+ int management_log_history_cache;
+ int management_echo_buffer_size;
+ int management_state_buffer_size;
+ const char *management_write_peer_info_file;
+
+ const char *management_client_user;
+ const char *management_client_group;
+
+ /* Mask of MF_ values of manage.h */
+ unsigned int management_flags;
+#endif
+
+#ifdef ENABLE_PLUGIN
+ struct plugin_option_list *plugin_list;
+#endif
+
+ const char *tmp_dir;
+
+#if P2MP
+
+#if P2MP_SERVER
+ bool server_defined;
+ in_addr_t server_network;
+ in_addr_t server_netmask;
+
+# define SF_NOPOOL (1<<0)
+# define SF_TCP_NODELAY_HELPER (1<<1)
+# define SF_NO_PUSH_ROUTE_GATEWAY (1<<2)
+ unsigned int server_flags;
+
+ bool server_bridge_proxy_dhcp;
+
+ bool server_bridge_defined;
+ in_addr_t server_bridge_ip;
+ in_addr_t server_bridge_netmask;
+ in_addr_t server_bridge_pool_start;
+ in_addr_t server_bridge_pool_end;
+
+ struct push_list push_list;
+ bool ifconfig_pool_defined;
+ in_addr_t ifconfig_pool_start;
+ in_addr_t ifconfig_pool_end;
+ in_addr_t ifconfig_pool_netmask;
+ const char *ifconfig_pool_persist_filename;
+ int ifconfig_pool_persist_refresh_freq;
+ int real_hash_size;
+ int virtual_hash_size;
+ const char *client_connect_script;
+ const char *client_disconnect_script;
+ const char *learn_address_script;
+ const char *client_config_dir;
+ bool ccd_exclusive;
+ bool disable;
+ int n_bcast_buf;
+ int tcp_queue_limit;
+ struct iroute *iroutes;
+ bool push_ifconfig_defined;
+ in_addr_t push_ifconfig_local;
+ in_addr_t push_ifconfig_remote_netmask;
+ bool push_ifconfig_constraint_defined;
+ in_addr_t push_ifconfig_constraint_network;
+ in_addr_t push_ifconfig_constraint_netmask;
+ bool enable_c2c;
+ bool duplicate_cn;
+ int cf_max;
+ int cf_per;
+ int max_clients;
+ int max_routes_per_client;
+
+ const char *auth_user_pass_verify_script;
+ bool auth_user_pass_verify_script_via_file;
+ unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
+#if PORT_SHARE
+ char *port_share_host;
+ int port_share_port;
+#endif
+#endif
+
+ bool client;
+ bool pull; /* client pull of config options from server */
+ int push_continuation;
+ const char *auth_user_pass_file;
+ struct options_pre_pull *pre_pull;
+
+ int server_poll_timeout;
+
+ int scheduled_exit_interval;
+
+#endif
+
+#ifdef USE_CRYPTO
+ /* Cipher parms */
+ const char *shared_secret_file;
+#if ENABLE_INLINE_FILES
+ const char *shared_secret_file_inline;
+#endif
+ int key_direction;
+ bool ciphername_defined;
+ const char *ciphername;
+ bool authname_defined;
+ const char *authname;
+ int keysize;
+ const char *prng_hash;
+ int prng_nonce_secret_len;
+ const char *engine;
+ bool replay;
+ bool mute_replay_warnings;
+ int replay_window;
+ int replay_time;
+ const char *packet_id_file;
+ bool use_iv;
+ bool test_crypto;
+
+#ifdef USE_SSL
+ /* TLS (control channel) parms */
+ bool tls_server;
+ bool tls_client;
+ const char *ca_file;
+ const char *ca_path;
+ const char *dh_file;
+ const char *cert_file;
+ const char *priv_key_file;
+ const char *pkcs12_file;
+ const char *cipher_list;
+ const char *tls_verify;
+ const char *tls_export_cert;
+ const char *tls_remote;
+ const char *crl_file;
+
+#if ENABLE_INLINE_FILES
+ const char *ca_file_inline;
+ const char *cert_file_inline;
+ char *priv_key_file_inline;
+ const char *dh_file_inline;
+ const char *pkcs12_file_inline; /* contains the base64 encoding of pkcs12 file */
+#endif
+
+ int ns_cert_type; /* set to 0, NS_SSL_SERVER, or NS_SSL_CLIENT */
+ unsigned remote_cert_ku[MAX_PARMS];
+ const char *remote_cert_eku;
+
+#ifdef ENABLE_PKCS11
+ const char *pkcs11_providers[MAX_PARMS];
+ unsigned pkcs11_private_mode[MAX_PARMS];
+ bool pkcs11_protected_authentication[MAX_PARMS];
+ bool pkcs11_cert_private[MAX_PARMS];
+ int pkcs11_pin_cache_period;
+ const char *pkcs11_id;
+ bool pkcs11_id_management;
+#endif
+
+#ifdef WIN32
+ const char *cryptoapi_cert;
+#endif
+
+ /* data channel key exchange method */
+ int key_method;
+
+ /* Per-packet timeout on control channel */
+ int tls_timeout;
+
+ /* Data channel key renegotiation parameters */
+ int renegotiate_bytes;
+ int renegotiate_packets;
+ int renegotiate_seconds;
+
+ /* Data channel key handshake must finalize
+ within n seconds of handshake initiation. */
+ int handshake_window;
+
+#ifdef ENABLE_X509ALTUSERNAME
+ /* Field used to be the username in X509 cert. */
+ char *x509_username_field;
+#endif
+
+ /* Old key allowed to live n seconds after new key goes active */
+ int transition_window;
+
+ /* Special authentication MAC for TLS control channel */
+ const char *tls_auth_file; /* shared secret */
+#if ENABLE_INLINE_FILES
+ const char *tls_auth_file_inline;
+#endif
+
+ /* Allow only one session */
+ bool single_session;
+
+#ifdef ENABLE_PUSH_PEER_INFO
+ bool push_peer_info;
+#endif
+
+ bool tls_exit;
+
+#endif /* USE_SSL */
+#endif /* USE_CRYPTO */
+
+ /* special state parms */
+ int foreign_option_index;
+
+#ifdef WIN32
+ const char *exit_event_name;
+ bool exit_event_initial_state;
+ bool show_net_up;
+ int route_method;
+#endif
+};
+
+#define streq(x, y) (!strcmp((x), (y)))
+
+/*
+ * Option classes.
+ */
+#define OPT_P_GENERAL (1<<0)
+#define OPT_P_UP (1<<1)
+#define OPT_P_ROUTE (1<<2)
+#define OPT_P_IPWIN32 (1<<3)
+#define OPT_P_SCRIPT (1<<4)
+#define OPT_P_SETENV (1<<5)
+#define OPT_P_SHAPER (1<<6)
+#define OPT_P_TIMER (1<<7)
+#define OPT_P_PERSIST (1<<8)
+#define OPT_P_PERSIST_IP (1<<9)
+#define OPT_P_COMP (1<<10) /* TODO */
+#define OPT_P_MESSAGES (1<<11)
+#define OPT_P_CRYPTO (1<<12) /* TODO */
+#define OPT_P_TLS_PARMS (1<<13) /* TODO */
+#define OPT_P_MTU (1<<14) /* TODO */
+#define OPT_P_NICE (1<<15)
+#define OPT_P_PUSH (1<<16)
+#define OPT_P_INSTANCE (1<<17)
+#define OPT_P_CONFIG (1<<18)
+#define OPT_P_EXPLICIT_NOTIFY (1<<19)
+#define OPT_P_ECHO (1<<20)
+#define OPT_P_INHERIT (1<<21)
+#define OPT_P_ROUTE_EXTRAS (1<<22)
+#define OPT_P_PULL_MODE (1<<23)
+#define OPT_P_PLUGIN (1<<24)
+#define OPT_P_SOCKBUF (1<<25)
+#define OPT_P_SOCKFLAGS (1<<26)
+#define OPT_P_CONNECTION (1<<27)
+
+#define OPT_P_DEFAULT (~(OPT_P_INSTANCE|OPT_P_PULL_MODE))
+
+#if P2MP
+#define PULL_DEFINED(opt) ((opt)->pull)
+#if P2MP_SERVER
+#define PUSH_DEFINED(opt) ((opt)->push_list)
+#endif
+#endif
+
+#ifndef PULL_DEFINED
+#define PULL_DEFINED(opt) (false)
+#endif
+
+#ifndef PUSH_DEFINED
+#define PUSH_DEFINED(opt) (false)
+#endif
+
+#ifdef WIN32
+#define ROUTE_OPTION_FLAGS(o) ((o)->route_method & ROUTE_METHOD_MASK)
+#else
+#define ROUTE_OPTION_FLAGS(o) (0)
+#endif
+
+#ifdef HAVE_GETTIMEOFDAY
+#define SHAPER_DEFINED(opt) ((opt)->shaper)
+#else
+#define SHAPER_DEFINED(opt) (false)
+#endif
+
+#ifdef ENABLE_PLUGIN
+#define PLUGIN_OPTION_LIST(opt) ((opt)->plugin_list)
+#else
+#define PLUGIN_OPTION_LIST(opt) (NULL)
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+#define MAN_CLIENT_AUTH_ENABLED(opt) ((opt)->management_flags & MF_CLIENT_AUTH)
+#else
+#define MAN_CLIENT_AUTH_ENABLED(opt) (false)
+#endif
+
+void parse_argv (struct options *options,
+ const int argc,
+ char *argv[],
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es);
+
+void notnull (const char *arg, const char *description);
+
+void usage_small (void);
+
+void init_options (struct options *o, const bool init_gc);
+void uninit_options (struct options *o);
+
+void setenv_settings (struct env_set *es, const struct options *o);
+void show_settings (const struct options *o);
+
+bool string_defined_equal (const char *s1, const char *s2);
+
+#ifdef ENABLE_OCC
+
+const char *options_string_version (const char* s, struct gc_arena *gc);
+
+char *options_string (const struct options *o,
+ const struct frame *frame,
+ struct tuntap *tt,
+ bool remote,
+ struct gc_arena *gc);
+
+bool options_cmp_equal_safe (char *actual, const char *expected, size_t actual_n);
+void options_warning_safe (char *actual, const char *expected, size_t actual_n);
+bool options_cmp_equal (char *actual, const char *expected);
+void options_warning (char *actual, const char *expected);
+
+#endif
+
+void options_postprocess (struct options *options);
+
+void pre_pull_save (struct options *o);
+void pre_pull_restore (struct options *o);
+
+bool apply_push_options (struct options *options,
+ struct buffer *buf,
+ unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es);
+
+bool is_persist_option (const struct options *o);
+bool is_stateful_restart (const struct options *o);
+
+void options_detach (struct options *o);
+
+void options_server_import (struct options *o,
+ const char *filename,
+ int msglevel,
+ unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es);
+
+void pre_pull_default (struct options *o);
+
+void rol_check_alloc (struct options *options);
+
+int parse_line (const char *line,
+ char *p[],
+ const int n,
+ const char *file,
+ const int line_num,
+ int msglevel,
+ struct gc_arena *gc);
+
+/*
+ * parse/print topology coding
+ */
+
+int parse_topology (const char *str, const int msglevel);
+const char *print_topology (const int topology);
+
+/*
+ * Manage auth-retry variable
+ */
+
+#if P2MP
+
+#define AR_NONE 0
+#define AR_INTERACT 1
+#define AR_NOINTERACT 2
+
+int auth_retry_get (void);
+bool auth_retry_set (const int msglevel, const char *option);
+const char *auth_retry_print (void);
+
+#endif
+
+void options_string_import (struct options *options,
+ const char *config,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es);
+
+/*
+ * inline functions
+ */
+static inline bool
+connection_list_defined (const struct options *o)
+{
+#ifdef ENABLE_CONNECTION
+ return o->connection_list != NULL;
+#else
+ return false;
+#endif
+}
+
+static inline void
+connection_list_set_no_advance (struct options *o)
+{
+#ifdef ENABLE_CONNECTION
+ if (o->connection_list)
+ o->connection_list->no_advance = true;
+#endif
+}
+
+#if HTTP_PROXY_FALLBACK
+
+struct http_proxy_options *
+parse_http_proxy_fallback (struct context *c,
+ const char *server,
+ const char *port,
+ const char *flags,
+ const int msglevel);
+
+#endif /* HTTP_PROXY_FALLBACK */
+
+#endif
diff --git a/otime.c b/otime.c
new file mode 100644
index 0000000..b295646
--- /dev/null
+++ b/otime.c
@@ -0,0 +1,273 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "otime.h"
+
+#include "memdbg.h"
+
+time_t now = 0; /* GLOBAL */
+
+#if TIME_BACKTRACK_PROTECTION && defined(HAVE_GETTIMEOFDAY)
+
+static time_t now_adj = 0; /* GLOBAL */
+time_t now_usec = 0; /* GLOBAL */
+
+/*
+ * Try to filter out time instability caused by the system
+ * clock backtracking or jumping forward.
+ */
+
+void
+update_now (const time_t system_time)
+{
+ const int forward_threshold = 86400; /* threshold at which to dampen forward jumps */
+ const int backward_trigger = 10; /* backward jump must be >= this many seconds before we adjust */
+ time_t real_time = system_time + now_adj;
+
+ if (real_time > now)
+ {
+ const time_t overshoot = real_time - now - 1;
+ if (overshoot > forward_threshold && now_adj >= overshoot)
+ {
+ now_adj -= overshoot;
+ real_time -= overshoot;
+ }
+ now = real_time;
+ }
+ else if (real_time < now - backward_trigger)
+ now_adj += (now - real_time);
+}
+
+void
+update_now_usec (struct timeval *tv)
+{
+ const time_t last = now;
+ update_now (tv->tv_sec);
+ if (now > last || (now == last && tv->tv_usec > now_usec))
+ now_usec = tv->tv_usec;
+}
+
+#endif /* TIME_BACKTRACK_PROTECTION && defined(HAVE_GETTIMEOFDAY) */
+
+/*
+ * Return a numerical string describing a struct timeval.
+ */
+const char *
+tv_string (const struct timeval *tv, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ buf_printf (&out, "[%d/%d]",
+ (int) tv->tv_sec,
+ (int )tv->tv_usec);
+ return BSTR (&out);
+}
+
+/*
+ * Return an ascii string describing an absolute
+ * date/time in a struct timeval.
+ *
+ */
+const char *
+tv_string_abs (const struct timeval *tv, struct gc_arena *gc)
+{
+ return time_string ((time_t) tv->tv_sec,
+ (int) tv->tv_usec,
+ true,
+ gc);
+}
+
+/* format a time_t as ascii, or use current time if 0 */
+
+const char *
+time_string (time_t t, int usec, bool show_usec, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ struct timeval tv;
+
+ if (t)
+ {
+ tv.tv_sec = t;
+ tv.tv_usec = usec;
+ }
+ else
+ {
+#ifdef HAVE_GETTIMEOFDAY
+ if (gettimeofday (&tv, NULL))
+#endif
+ {
+ tv.tv_sec = time (NULL);
+ tv.tv_usec = 0;
+ }
+ }
+
+ t = tv.tv_sec;
+ buf_printf (&out, "%s", ctime(&t));
+ buf_rmtail (&out, '\n');
+
+ if (show_usec && tv.tv_usec)
+ buf_printf (&out, " us=%d", (int)tv.tv_usec);
+
+ return BSTR (&out);
+}
+
+/*
+ * Limit the frequency of an event stream.
+ *
+ * Used to control maximum rate of new
+ * incoming connections.
+ */
+
+struct frequency_limit *
+frequency_limit_init (int max, int per)
+{
+ struct frequency_limit *f;
+
+ ASSERT (max >= 0 && per >= 0);
+
+ ALLOC_OBJ (f, struct frequency_limit);
+ f->max = max;
+ f->per = per;
+ f->n = 0;
+ f->reset = 0;
+ return f;
+}
+
+void
+frequency_limit_free (struct frequency_limit *f)
+{
+ free (f);
+}
+
+bool
+frequency_limit_event_allowed (struct frequency_limit *f)
+{
+ if (f->per)
+ {
+ bool ret;
+ if (now >= f->reset + f->per)
+ {
+ f->reset = now;
+ f->n = 0;
+ }
+ ret = (++f->n <= f->max);
+ return ret;
+ }
+ else
+ return true;
+}
+
+#ifdef WIN32
+
+static time_t gtc_base = 0;
+static DWORD gtc_last = 0;
+static time_t last_sec = 0;
+static unsigned int last_msec = 0;
+static bool bt_last = false;
+
+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();
+ bool bt = false;
+ time_t sec;
+ unsigned int msec;
+ const int backtrack_hold_seconds = 10;
+
+ /* 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 = true;
+ }
+ }
+ 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 = true;
+ }
+
+ tv->tv_sec = last_sec = sec;
+ tv->tv_usec = (last_msec = msec) * 1000;
+
+ if (bt && !bt_last)
+ gettimeofday_calibrate ();
+ bt_last = bt;
+
+ return 0;
+}
+
+#endif /* WIN32 */
+
+#ifdef TIME_TEST
+void
+time_test (void)
+{
+ struct timeval tv;
+ time_t t;
+ int i;
+ for (i = 0; i < 10000; ++i)
+ {
+ t = time(NULL);
+ gettimeofday (&tv, NULL);
+#if 1
+ msg (M_INFO, "t=%u s=%u us=%u",
+ (unsigned int)t,
+ (unsigned int)tv.tv_sec,
+ (unsigned int)tv.tv_usec);
+#endif
+ }
+}
+#endif
diff --git a/otime.h b/otime.h
new file mode 100644
index 0000000..fd73bbd
--- /dev/null
+++ b/otime.h
@@ -0,0 +1,271 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OTIME_H
+#define OTIME_H
+
+#include "common.h"
+#include "integer.h"
+#include "buffer.h"
+
+struct frequency_limit
+{
+ int max;
+ int per;
+ int n;
+ time_t reset;
+};
+
+struct frequency_limit *frequency_limit_init (int max, int per);
+void frequency_limit_free (struct frequency_limit *f);
+bool frequency_limit_event_allowed (struct frequency_limit *f);
+
+#ifdef WIN32
+int gettimeofday(struct timeval *tv, void *tz);
+#endif
+
+/* format a time_t as ascii, or use current time if 0 */
+const char* time_string (time_t t, int usec, bool show_usec, struct gc_arena *gc);
+
+/* struct timeval functions */
+
+const char *tv_string (const struct timeval *tv, struct gc_arena *gc);
+const char *tv_string_abs (const struct timeval *tv, struct gc_arena *gc);
+
+extern time_t now; /* updated frequently to time(NULL) */
+
+void time_test (void);
+
+#if TIME_BACKTRACK_PROTECTION && defined(HAVE_GETTIMEOFDAY)
+
+void update_now (const time_t system_time);
+
+extern time_t now_usec;
+void update_now_usec (struct timeval *tv);
+
+static inline int
+openvpn_gettimeofday (struct timeval *tv, void *tz)
+{
+ const int status = gettimeofday (tv, tz);
+ if (!status)
+ {
+ update_now_usec (tv);
+ tv->tv_sec = now;
+ tv->tv_usec = now_usec;
+ }
+ return status;
+}
+
+static inline void
+update_time (void)
+{
+#ifdef WIN32
+ /* on WIN32, gettimeofday is faster than time(NULL) */
+ struct timeval tv;
+ openvpn_gettimeofday (&tv, NULL);
+#else
+ update_now (time (NULL));
+#endif
+}
+
+#else /* !(TIME_BACKTRACK_PROTECTION && defined(HAVE_GETTIMEOFDAY)) */
+
+static inline void
+update_time (void)
+{
+#if defined(WIN32) && defined(HAVE_GETTIMEOFDAY)
+ struct timeval tv;
+ if (!gettimeofday (&tv, NULL))
+ {
+ if (tv.tv_sec != now)
+ now = tv.tv_sec;
+ }
+#else
+ const time_t real_time = time (NULL);
+ if (real_time != now)
+ now = real_time;
+#endif
+}
+
+#ifdef HAVE_GETTIMEOFDAY
+
+static inline int
+openvpn_gettimeofday (struct timeval *tv, void *tz)
+{
+ return gettimeofday (tv, tz);
+}
+
+#endif
+
+#endif /* TIME_BACKTRACK_PROTECTION && defined(HAVE_GETTIMEOFDAY) */
+
+static inline time_t
+openvpn_time (time_t *t)
+{
+ update_time ();
+ if (t)
+ *t = now;
+ return now;
+}
+
+static inline void
+tv_clear (struct timeval *tv)
+{
+ tv->tv_sec = 0;
+ tv->tv_usec = 0;
+}
+
+static inline bool
+tv_defined (const struct timeval *tv)
+{
+ return tv->tv_sec > 0 && tv->tv_usec > 0;
+}
+
+/* return tv1 - tv2 in usec, constrained by max_seconds */
+static inline int
+tv_subtract (const struct timeval *tv1, const struct timeval *tv2, const unsigned int max_seconds)
+{
+ const int max_usec = max_seconds * 1000000;
+ const int sec_diff = tv1->tv_sec - tv2->tv_sec;
+
+ if (sec_diff > ((int)max_seconds + 10))
+ return max_usec;
+ else if (sec_diff < -((int)max_seconds + 10))
+ return -max_usec;
+ return constrain_int (sec_diff * 1000000 + (tv1->tv_usec - tv2->tv_usec), -max_usec, max_usec);
+}
+
+static inline void
+tv_add (struct timeval *dest, const struct timeval *src)
+{
+ dest->tv_sec += src->tv_sec;
+ dest->tv_usec += src->tv_usec;
+ dest->tv_sec += (dest->tv_usec >> 20);
+ dest->tv_usec &= 0x000FFFFF;
+ if (dest->tv_usec >= 1000000)
+ {
+ dest->tv_usec -= 1000000;
+ dest->tv_sec += 1;
+ }
+}
+
+static inline bool
+tv_lt (const struct timeval *t1, const struct timeval *t2)
+{
+ if (t1->tv_sec < t2->tv_sec)
+ return true;
+ else if (t1->tv_sec > t2->tv_sec)
+ return false;
+ else
+ return t1->tv_usec < t2->tv_usec;
+}
+
+static inline bool
+tv_le (const struct timeval *t1, const struct timeval *t2)
+{
+ if (t1->tv_sec < t2->tv_sec)
+ return true;
+ else if (t1->tv_sec > t2->tv_sec)
+ return false;
+ else
+ return t1->tv_usec <= t2->tv_usec;
+}
+
+static inline bool
+tv_ge (const struct timeval *t1, const struct timeval *t2)
+{
+ if (t1->tv_sec > t2->tv_sec)
+ return true;
+ else if (t1->tv_sec < t2->tv_sec)
+ return false;
+ else
+ return t1->tv_usec >= t2->tv_usec;
+}
+
+static inline bool
+tv_gt (const struct timeval *t1, const struct timeval *t2)
+{
+ if (t1->tv_sec > t2->tv_sec)
+ return true;
+ else if (t1->tv_sec < t2->tv_sec)
+ return false;
+ else
+ return t1->tv_usec > t2->tv_usec;
+}
+
+static inline bool
+tv_eq (const struct timeval *t1, const struct timeval *t2)
+{
+ return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
+}
+
+static inline void
+tv_delta (struct timeval *dest, const struct timeval *t1, const struct timeval *t2)
+{
+ int sec = t2->tv_sec - t1->tv_sec;
+ int usec = t2->tv_usec - t1->tv_usec;
+
+ while (usec < 0)
+ {
+ usec += 1000000;
+ sec -= 1;
+ }
+
+ if (sec < 0)
+ usec = sec = 0;
+
+ dest->tv_sec = sec;
+ dest->tv_usec = usec;
+}
+
+#define TV_WITHIN_SIGMA_MAX_SEC 600
+#define TV_WITHIN_SIGMA_MAX_USEC (TV_WITHIN_SIGMA_MAX_SEC * 1000000)
+
+/*
+ * Is t1 and t2 within sigma microseconds of each other?
+ */
+static inline bool
+tv_within_sigma (const struct timeval *t1, const struct timeval *t2, unsigned int sigma)
+{
+ const int delta = tv_subtract (t1, t2, TV_WITHIN_SIGMA_MAX_SEC); /* sigma should be less than 10 minutes */
+ return -(int)sigma <= delta && delta <= (int)sigma;
+}
+
+/*
+ * Used to determine in how many seconds we should be
+ * called again.
+ */
+static inline void
+interval_earliest_wakeup (interval_t *wakeup, time_t at, time_t current) {
+ if (at > current)
+ {
+ const interval_t delta = (interval_t) (at - current);
+ if (delta < *wakeup)
+ *wakeup = delta;
+ if (*wakeup < 0)
+ *wakeup = 0;
+ }
+}
+
+#endif
diff --git a/packet_id.c b/packet_id.c
new file mode 100644
index 0000000..b11e71f
--- /dev/null
+++ b/packet_id.c
@@ -0,0 +1,486 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * These routines are designed to catch replay attacks,
+ * where a man-in-the-middle captures packets and then
+ * attempts to replay them back later.
+ *
+ * We use the "sliding-window" algorithm, similar
+ * to IPSec.
+ */
+
+#include "syshead.h"
+
+#ifdef USE_CRYPTO
+
+#include "packet_id.h"
+#include "misc.h"
+#include "integer.h"
+
+#include "memdbg.h"
+
+/*
+ * Special time_t value that indicates that
+ * sequence number has expired.
+ */
+#define SEQ_UNSEEN ((time_t)0)
+#define SEQ_EXPIRED ((time_t)1)
+
+void
+packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack)
+{
+ dmsg (D_PID_DEBUG_LOW, "PID packet_id_init seq_backtrack=%d time_backtrack=%d",
+ seq_backtrack,
+ time_backtrack);
+
+ ASSERT (p);
+ CLEAR (*p);
+
+ if (seq_backtrack)
+ {
+ ASSERT (MIN_SEQ_BACKTRACK <= seq_backtrack && seq_backtrack <= MAX_SEQ_BACKTRACK);
+ ASSERT (MIN_TIME_BACKTRACK <= time_backtrack && time_backtrack <= MAX_TIME_BACKTRACK);
+ CIRC_LIST_ALLOC (p->rec.seq_list, struct seq_list, seq_backtrack);
+ p->rec.seq_backtrack = seq_backtrack;
+ p->rec.time_backtrack = time_backtrack;
+ }
+ p->rec.initialized = true;
+}
+
+void
+packet_id_free (struct packet_id *p)
+{
+ if (p)
+ {
+ dmsg (D_PID_DEBUG_LOW, "PID packet_id_free");
+ if (p->rec.seq_list)
+ free (p->rec.seq_list);
+ CLEAR (*p);
+ }
+}
+
+void
+packet_id_add (struct packet_id_rec *p, const struct packet_id_net *pin)
+{
+ const time_t local_now = now;
+ if (p->seq_list)
+ {
+ packet_id_type diff;
+
+ /*
+ * If time value increases, start a new
+ * sequence number sequence.
+ */
+ if (!CIRC_LIST_SIZE (p->seq_list)
+ || pin->time > p->time
+ || (pin->id >= (packet_id_type)p->seq_backtrack
+ && pin->id - (packet_id_type)p->seq_backtrack > p->id))
+ {
+ p->time = pin->time;
+ p->id = 0;
+ if (pin->id > (packet_id_type)p->seq_backtrack)
+ p->id = pin->id - (packet_id_type)p->seq_backtrack;
+ CIRC_LIST_RESET (p->seq_list);
+ }
+
+ while (p->id < pin->id)
+ {
+ CIRC_LIST_PUSH (p->seq_list, SEQ_UNSEEN);
+ ++p->id;
+ }
+
+ diff = p->id - pin->id;
+ if (diff < (packet_id_type) CIRC_LIST_SIZE (p->seq_list)
+ && local_now > SEQ_EXPIRED)
+ CIRC_LIST_ITEM (p->seq_list, diff) = local_now;
+ }
+ else
+ {
+ p->time = pin->time;
+ p->id = pin->id;
+ }
+}
+
+/*
+ * Expire sequence numbers which can no longer
+ * be accepted because they would violate
+ * time_backtrack.
+ */
+void
+packet_id_reap (struct packet_id_rec *p)
+{
+ const time_t local_now = now;
+ if (p->time_backtrack)
+ {
+ int i;
+ bool expire = false;
+ for (i = 0; i < CIRC_LIST_SIZE (p->seq_list); ++i)
+ {
+ const time_t t = CIRC_LIST_ITEM (p->seq_list, i);
+ if (t == SEQ_EXPIRED)
+ break;
+ if (!expire && t && t + p->time_backtrack < local_now)
+ expire = true;
+ if (expire)
+ CIRC_LIST_ITEM (p->seq_list, i) = SEQ_EXPIRED;
+ }
+ }
+ p->last_reap = local_now;
+}
+
+/*
+ * Return true if packet id is ok, or false if
+ * it is a replay.
+ */
+bool
+packet_id_test (const struct packet_id_rec *p,
+ const struct packet_id_net *pin)
+{
+ static int max_backtrack_stat;
+ packet_id_type diff;
+
+ dmsg (D_PID_DEBUG,
+ "PID TEST " time_format ":" packet_id_format " " time_format ":" packet_id_format "",
+ (time_type)p->time, (packet_id_print_type)p->id, (time_type)pin->time,
+ (packet_id_print_type)pin->id);
+
+ ASSERT (p->initialized);
+
+ if (!pin->id)
+ return false;
+
+ if (p->seq_backtrack)
+ {
+ /*
+ * In backtrack mode, we allow packet reordering subject
+ * to the seq_backtrack and time_backtrack constraints.
+ *
+ * This mode is used with UDP.
+ */
+ if (pin->time == p->time)
+ {
+ /* is packet-id greater than any one we've seen yet? */
+ if (pin->id > p->id)
+ return true;
+
+ /* check packet-id sliding window for original/replay status */
+ diff = p->id - pin->id;
+
+ /* keep track of maximum backtrack seen for debugging purposes */
+ if ((int)diff > max_backtrack_stat)
+ {
+ max_backtrack_stat = (int)diff;
+ msg (D_BACKTRACK, "Replay-window backtrack occurred [%d]", max_backtrack_stat);
+ }
+
+ if (diff >= (packet_id_type) CIRC_LIST_SIZE (p->seq_list))
+ return false;
+
+ return CIRC_LIST_ITEM (p->seq_list, diff) == 0;
+ }
+ else if (pin->time < p->time) /* if time goes back, reject */
+ return false;
+ else /* time moved forward */
+ return true;
+ }
+ else
+ {
+ /*
+ * In non-backtrack mode, all sequence number series must
+ * begin at some number n > 0 and must increment linearly without gaps.
+ *
+ * This mode is used with TCP.
+ */
+ if (pin->time == p->time)
+ return !p->id || pin->id == p->id + 1;
+ else if (pin->time < p->time) /* if time goes back, reject */
+ return false;
+ else /* time moved forward */
+ return pin->id == 1;
+ }
+}
+
+/*
+ * Read/write a packet ID to/from the buffer. Short form is sequence number
+ * only. Long form is sequence number and timestamp.
+ */
+
+bool
+packet_id_read (struct packet_id_net *pin, struct buffer *buf, bool long_form)
+{
+ packet_id_type net_id;
+ net_time_t net_time;
+
+ pin->id = 0;
+ pin->time = 0;
+
+ if (!buf_read (buf, &net_id, sizeof (net_id)))
+ return false;
+ pin->id = ntohpid (net_id);
+ if (long_form)
+ {
+ if (!buf_read (buf, &net_time, sizeof (net_time)))
+ return false;
+ pin->time = ntohtime (net_time);
+ }
+ return true;
+}
+
+bool
+packet_id_write (const struct packet_id_net *pin, struct buffer *buf, bool long_form, bool prepend)
+{
+ packet_id_type net_id = htonpid (pin->id);
+ net_time_t net_time = htontime (pin->time);
+
+ if (prepend)
+ {
+ if (long_form)
+ {
+ if (!buf_write_prepend (buf, &net_time, sizeof (net_time)))
+ return false;
+ }
+ if (!buf_write_prepend (buf, &net_id, sizeof (net_id)))
+ return false;
+ }
+ else
+ {
+ if (!buf_write (buf, &net_id, sizeof (net_id)))
+ return false;
+ if (long_form)
+ {
+ if (!buf_write (buf, &net_time, sizeof (net_time)))
+ return false;
+ }
+ }
+ return true;
+}
+
+const char *
+packet_id_net_print (const struct packet_id_net *pin, bool print_timestamp, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+
+ buf_printf (&out, "[ #" packet_id_format, (packet_id_print_type)pin->id);
+ if (print_timestamp && pin->time)
+ buf_printf (&out, " / time = (" packet_id_format ") %s",
+ (packet_id_print_type)pin->time,
+ time_string (pin->time, 0, false, gc));
+
+ buf_printf (&out, " ]");
+ return BSTR (&out);
+}
+
+/* initialize the packet_id_persist structure in a disabled state */
+void
+packet_id_persist_init (struct packet_id_persist *p)
+{
+ p->filename = NULL;
+ p->fd = -1;
+ p->time = p->time_last_written = 0;
+ p->id = p->id_last_written = 0;
+}
+
+/* close the file descriptor if it is open, and switch to disabled state */
+void
+packet_id_persist_close (struct packet_id_persist *p)
+{
+ if (packet_id_persist_enabled (p))
+ {
+ if (close (p->fd))
+ msg (D_PID_PERSIST | M_ERRNO, "Close error on --replay-persist file %s", p->filename);
+ packet_id_persist_init (p);
+ }
+}
+
+/* load persisted rec packet_id (time and id) only once from file, and set state to enabled */
+void
+packet_id_persist_load (struct packet_id_persist *p, const char *filename)
+{
+ struct gc_arena gc = gc_new ();
+ if (!packet_id_persist_enabled (p))
+ {
+ /* open packet-id persist file for both read and write */
+ p->fd = open (filename,
+ O_CREAT | O_RDWR | O_BINARY,
+ S_IRUSR | S_IWUSR);
+ if (p->fd == -1)
+ {
+ msg (D_PID_PERSIST | M_ERRNO,
+ "Cannot open --replay-persist file %s for read/write",
+ filename);
+ }
+ else
+ {
+ struct packet_id_persist_file_image image;
+ ssize_t n;
+
+#if defined(HAVE_FLOCK) && defined(LOCK_EX) && defined(LOCK_NB)
+ if (flock (p->fd, LOCK_EX | LOCK_NB))
+ msg (M_ERR, "Cannot obtain exclusive lock on --replay-persist file %s", filename);
+#endif
+
+ p->filename = filename;
+ n = read (p->fd, &image, sizeof(image));
+ if (n == sizeof(image))
+ {
+ p->time = p->time_last_written = image.time;
+ p->id = p->id_last_written = image.id;
+ dmsg (D_PID_PERSIST_DEBUG, "PID Persist Read from %s: %s",
+ p->filename, packet_id_persist_print (p, &gc));
+ }
+ else if (n == -1)
+ {
+ msg (D_PID_PERSIST | M_ERRNO,
+ "Read error on --replay-persist file %s",
+ p->filename);
+ }
+ }
+ }
+ gc_free (&gc);
+}
+
+/* save persisted rec packet_id (time and id) to file (only if enabled state) */
+void
+packet_id_persist_save (struct packet_id_persist *p)
+{
+ if (packet_id_persist_enabled (p) && p->time && (p->time != p->time_last_written ||
+ p->id != p->id_last_written))
+ {
+ struct packet_id_persist_file_image image;
+ ssize_t n;
+ off_t seek_ret;
+ struct gc_arena gc = gc_new ();
+
+ image.time = p->time;
+ image.id = p->id;
+ seek_ret = lseek(p->fd, (off_t)0, SEEK_SET);
+ if (seek_ret == (off_t)0)
+ {
+ n = write(p->fd, &image, sizeof(image));
+ if (n == sizeof(image))
+ {
+ p->time_last_written = p->time;
+ p->id_last_written = p->id;
+ dmsg (D_PID_PERSIST_DEBUG, "PID Persist Write to %s: %s",
+ p->filename, packet_id_persist_print (p, &gc));
+ }
+ else
+ {
+ msg (D_PID_PERSIST | M_ERRNO,
+ "Cannot write to --replay-persist file %s",
+ p->filename);
+ }
+ }
+ else
+ {
+ msg (D_PID_PERSIST | M_ERRNO,
+ "Cannot seek to beginning of --replay-persist file %s",
+ p->filename);
+ }
+ gc_free (&gc);
+ }
+}
+
+/* transfer packet_id_persist -> packet_id */
+void
+packet_id_persist_load_obj (const struct packet_id_persist *p, struct packet_id *pid)
+{
+ if (p && pid && packet_id_persist_enabled (p) && p->time)
+ {
+ pid->rec.time = p->time;
+ pid->rec.id = p->id;
+ }
+}
+
+const char *
+packet_id_persist_print (const struct packet_id_persist *p, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+
+ buf_printf (&out, "[");
+
+ if (packet_id_persist_enabled (p))
+ {
+ buf_printf (&out, " #" packet_id_format, (packet_id_print_type)p->id);
+ if (p->time)
+ buf_printf (&out, " / time = (" packet_id_format ") %s",
+ (packet_id_print_type)p->time,
+ time_string (p->time, 0, false, gc));
+ }
+
+ buf_printf (&out, " ]");
+ return (char *)out.data;
+}
+
+#ifdef PID_TEST
+
+void
+packet_id_interactive_test ()
+{
+ struct packet_id pid;
+ struct packet_id_net pin;
+ bool long_form;
+ bool count = 0;
+ bool test;
+
+ const int seq_backtrack = 10;
+ const int time_backtrack = 10;
+
+ packet_id_init (&pid, seq_backtrack, time_backtrack);
+
+ while (true) {
+ char buf[80];
+ if (!fgets(buf, sizeof(buf), stdin))
+ break;
+ update_time ();
+ if (sscanf (buf, "%lu,%u", &pin.time, &pin.id) == 2)
+ {
+ packet_id_reap_test (&pid.rec);
+ test = packet_id_test (&pid.rec, &pin);
+ printf ("packet_id_test (" time_format ", " packet_id_format ") returned %d\n",
+ (time_type)pin.time,
+ (packet_id_print_type)pin.id,
+ test);
+ if (test)
+ packet_id_add (&pid.rec, &pin);
+ }
+ else
+ {
+ long_form = (count < 20);
+ packet_id_alloc_outgoing (&pid.send, &pin, long_form);
+ printf ("(" time_format "(" packet_id_format "), %d)\n",
+ (time_type)pin.time,
+ (packet_id_print_type)pin.id,
+ long_form);
+ if (pid.send.id == 10)
+ pid.send.id = 0xFFFFFFF8;
+ ++count;
+ }
+ }
+ packet_id_free (&pid);
+}
+#endif
+
+#endif /* USE_CRYPTO */
diff --git a/packet_id.h b/packet_id.h
new file mode 100644
index 0000000..12c1df3
--- /dev/null
+++ b/packet_id.h
@@ -0,0 +1,335 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * These routines are designed to catch replay attacks,
+ * where a man-in-the-middle captures packets and then
+ * attempts to replay them back later.
+ */
+
+#ifdef USE_CRYPTO
+
+#ifndef PACKET_ID_H
+#define PACKET_ID_H
+
+#include "circ_list.h"
+#include "buffer.h"
+#include "error.h"
+#include "otime.h"
+
+/*
+ * Enables OpenVPN to be compiled in special packet_id test mode.
+ */
+/*#define PID_TEST*/
+
+#if 1
+/*
+ * These are the types that members of
+ * a struct packet_id_net are converted
+ * to for network transmission.
+ */
+typedef uint32_t packet_id_type;
+typedef uint32_t net_time_t;
+
+/*
+ * In TLS mode, when a packet ID gets to this level,
+ * start thinking about triggering a new
+ * SSL/TLS handshake.
+ */
+#define PACKET_ID_WRAP_TRIGGER 0xFF000000
+
+/* convert a packet_id_type from host to network order */
+#define htonpid(x) htonl(x)
+
+/* convert a packet_id_type from network to host order */
+#define ntohpid(x) ntohl(x)
+
+/* convert a time_t in host order to a net_time_t in network order */
+#define htontime(x) htonl((net_time_t)x)
+
+/* convert a net_time_t in network order to a time_t in host order */
+#define ntohtime(x) ((time_t)ntohl(x))
+
+#else
+
+/*
+ * DEBUGGING ONLY.
+ * Make packet_id_type and net_time_t small
+ * to test wraparound logic and corner cases.
+ */
+
+typedef uint8_t packet_id_type;
+typedef uint16_t net_time_t;
+
+#define PACKET_ID_WRAP_TRIGGER 0x80
+
+#define htonpid(x) (x)
+#define ntohpid(x) (x)
+#define htontime(x) htons((net_time_t)x)
+#define ntohtime(x) ((time_t)ntohs(x))
+
+#endif
+
+/*
+ * Printf formats for special types
+ */
+#define packet_id_format "%u"
+typedef unsigned int packet_id_print_type;
+
+/*
+ * Maximum allowed backtrack in
+ * sequence number due to packets arriving
+ * out of order.
+ */
+#define MIN_SEQ_BACKTRACK 0
+#define MAX_SEQ_BACKTRACK 65536
+#define DEFAULT_SEQ_BACKTRACK 64
+
+/*
+ * Maximum allowed backtrack in
+ * seconds due to packets arriving
+ * out of order.
+ */
+#define MIN_TIME_BACKTRACK 0
+#define MAX_TIME_BACKTRACK 600
+#define DEFAULT_TIME_BACKTRACK 15
+
+/*
+ * Do a reap pass through the sequence number
+ * array once every n seconds in order to
+ * expire sequence numbers which can no longer
+ * be accepted because they would violate
+ * TIME_BACKTRACK.
+ */
+#define SEQ_REAP_INTERVAL 5
+
+CIRC_LIST (seq_list, time_t);
+
+/*
+ * This is the data structure we keep on the receiving side,
+ * to check that no packet-id (i.e. sequence number + optional timestamp)
+ * is accepted more than once.
+ */
+struct packet_id_rec
+{
+ time_t last_reap; /* last call of packet_id_reap */
+ time_t time; /* highest time stamp received */
+ packet_id_type id; /* highest sequence number received */
+ int seq_backtrack; /* set from --replay-window */
+ int time_backtrack; /* set from --replay-window */
+ bool initialized; /* true if packet_id_init was called */
+ struct seq_list *seq_list; /* packet-id "memory" */
+};
+
+/*
+ * file to facilitate cross-session persistence
+ * of time/id
+ */
+struct packet_id_persist
+{
+ const char *filename;
+ int fd;
+ time_t time; /* time stamp */
+ packet_id_type id; /* sequence number */
+ time_t time_last_written;
+ packet_id_type id_last_written;
+};
+
+struct packet_id_persist_file_image
+{
+ time_t time; /* time stamp */
+ packet_id_type id; /* sequence number */
+};
+
+/*
+ * Keep a record of our current packet-id state
+ * on the sending side.
+ */
+struct packet_id_send
+{
+ packet_id_type id;
+ time_t time;
+};
+
+/*
+ * Communicate packet-id over the wire.
+ * A short packet-id is just a 32 bit
+ * sequence number. A long packet-id
+ * includes a timestamp as well.
+ *
+ * Long packet-ids are used as IVs for
+ * CFB/OFB ciphers.
+ *
+ * This data structure is always sent
+ * over the net in network byte order,
+ * by calling htonpid, ntohpid,
+ * htontime, and ntohtime on the
+ * data elements to change them
+ * to and from standard sizes.
+ *
+ * In addition, time is converted to
+ * a net_time_t before sending,
+ * since openvpn always
+ * uses a 32-bit time_t but some
+ * 64 bit platforms use a
+ * 64 bit time_t.
+ */
+struct packet_id_net
+{
+ packet_id_type id;
+ time_t time; /* converted to net_time_t before transmission */
+};
+
+struct packet_id
+{
+ struct packet_id_send send;
+ struct packet_id_rec rec;
+};
+
+void packet_id_init (struct packet_id *p, int seq_backtrack, int time_backtrack);
+void packet_id_free (struct packet_id *p);
+
+/* should we accept an incoming packet id ? */
+bool packet_id_test (const struct packet_id_rec *p,
+ const struct packet_id_net *pin);
+
+/* change our current state to reflect an accepted packet id */
+void packet_id_add (struct packet_id_rec *p,
+ const struct packet_id_net *pin);
+
+/* expire TIME_BACKTRACK sequence numbers */
+void packet_id_reap (struct packet_id_rec *p);
+
+/*
+ * packet ID persistence
+ */
+
+/* initialize the packet_id_persist structure in a disabled state */
+void packet_id_persist_init (struct packet_id_persist *p);
+
+/* close the file descriptor if it is open, and switch to disabled state */
+void packet_id_persist_close (struct packet_id_persist *p);
+
+/* load persisted rec packet_id (time and id) only once from file, and set state to enabled */
+void packet_id_persist_load (struct packet_id_persist *p, const char *filename);
+
+/* save persisted rec packet_id (time and id) to file (only if enabled state) */
+void packet_id_persist_save (struct packet_id_persist *p);
+
+/* transfer packet_id_persist -> packet_id */
+void packet_id_persist_load_obj (const struct packet_id_persist *p, struct packet_id* pid);
+
+/* return an ascii string representing a packet_id_persist object */
+const char *packet_id_persist_print (const struct packet_id_persist *p, struct gc_arena *gc);
+
+/*
+ * Read/write a packet ID to/from the buffer. Short form is sequence number
+ * only. Long form is sequence number and timestamp.
+ */
+
+bool packet_id_read (struct packet_id_net *pin, struct buffer *buf, bool long_form);
+bool packet_id_write (const struct packet_id_net *pin, struct buffer *buf, bool long_form, bool prepend);
+
+/*
+ * Inline functions.
+ */
+
+/* are we in enabled state? */
+static inline bool
+packet_id_persist_enabled (const struct packet_id_persist *p)
+{
+ return p->fd >= 0;
+}
+
+/* transfer packet_id -> packet_id_persist */
+static inline void
+packet_id_persist_save_obj (struct packet_id_persist *p, const struct packet_id* pid)
+{
+ if (packet_id_persist_enabled (p) && pid->rec.time)
+ {
+ p->time = pid->rec.time;
+ p->id = pid->rec.id;
+ }
+}
+
+const char* packet_id_net_print(const struct packet_id_net *pin, bool print_timestamp, struct gc_arena *gc);
+
+#ifdef PID_TEST
+void packet_id_interactive_test();
+#endif
+
+static inline int
+packet_id_size (bool long_form)
+{
+ return sizeof (packet_id_type) + (long_form ? sizeof (net_time_t) : 0);
+}
+
+static inline bool
+packet_id_close_to_wrapping (const struct packet_id_send *p)
+{
+ return p->id >= PACKET_ID_WRAP_TRIGGER;
+}
+
+/*
+ * Allocate an outgoing packet id.
+ * Sequence number ranges from 1 to 2^32-1.
+ * In long_form, a time_t is added as well.
+ */
+static inline void
+packet_id_alloc_outgoing (struct packet_id_send *p, struct packet_id_net *pin, bool long_form)
+{
+ if (!p->time)
+ p->time = now;
+ pin->id = ++p->id;
+ if (!pin->id)
+ {
+ ASSERT (long_form);
+ p->time = now;
+ pin->id = p->id = 1;
+ }
+ pin->time = p->time;
+}
+
+static inline bool
+check_timestamp_delta (time_t remote, unsigned int max_delta)
+{
+ unsigned int abs;
+ const time_t local_now = now;
+
+ if (local_now >= remote)
+ abs = local_now - remote;
+ else
+ abs = remote - local_now;
+ return abs <= max_delta;
+}
+
+static inline void
+packet_id_reap_test (struct packet_id_rec *p)
+{
+ if (p->last_reap + SEQ_REAP_INTERVAL <= now)
+ packet_id_reap (p);
+}
+
+#endif /* PACKET_ID_H */
+#endif /* USE_CRYPTO */
diff --git a/perf.c b/perf.c
new file mode 100644
index 0000000..d9dbafc
--- /dev/null
+++ b/perf.c
@@ -0,0 +1,293 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "perf.h"
+
+#ifdef ENABLE_PERFORMANCE_METRICS
+
+#include "error.h"
+#include "otime.h"
+
+#include "memdbg.h"
+
+static const char *metric_names[] = {
+ "PERF_BIO_READ_PLAINTEXT",
+ "PERF_BIO_WRITE_PLAINTEXT",
+ "PERF_BIO_READ_CIPHERTEXT",
+ "PERF_BIO_WRITE_CIPHERTEXT",
+ "PERF_TLS_MULTI_PROCESS",
+ "PERF_IO_WAIT",
+ "PERF_EVENT_LOOP",
+ "PERF_MULTI_CREATE_INSTANCE",
+ "PERF_MULTI_CLOSE_INSTANCE",
+ "PERF_MULTI_SHOW_STATS",
+ "PERF_MULTI_BCAST",
+ "PERF_MULTI_MCAST",
+ "PERF_SCRIPT",
+ "PERF_READ_IN_LINK",
+ "PERF_PROC_IN_LINK",
+ "PERF_READ_IN_TUN",
+ "PERF_PROC_IN_TUN",
+ "PERF_PROC_OUT_LINK",
+ "PERF_PROC_OUT_TUN",
+ "PERF_PROC_OUT_TUN_MTCP"
+};
+
+struct perf
+{
+# define PS_INITIAL 0
+# define PS_METER_RUNNING 1
+# define PS_METER_INTERRUPTED 2
+ int state;
+
+ struct timeval start;
+ double sofar;
+ double sum;
+ double max;
+ double count;
+};
+
+struct perf_set
+{
+ int stack_len;
+ int stack[STACK_N];
+ struct perf perf[PERF_N];
+};
+
+static struct perf_set perf_set;
+
+static void perf_print_state (int lev);
+
+static inline int
+get_stack_index (int sdelta)
+{
+ const int sindex = perf_set.stack_len + sdelta;
+ if (sindex >= 0 && sindex < STACK_N)
+ return sindex;
+ else
+ return -1;
+}
+
+static int
+get_perf_index (int sdelta)
+{
+ const int sindex = get_stack_index (sdelta);
+ if (sindex >= 0)
+ {
+ const int pindex = perf_set.stack[sindex];
+ if (pindex >= 0 && pindex < PERF_N)
+ return pindex;
+ else
+ return -1;
+ }
+ else
+ return -1;
+}
+
+static struct perf *
+get_perf (int sdelta)
+{
+ const int pindex = get_perf_index (sdelta);
+ if (pindex >= 0)
+ return &perf_set.perf[pindex];
+ else
+ return NULL;
+}
+
+static void
+push_perf_index (int pindex)
+{
+ const int sindex = get_stack_index (0);
+ const int newlen = get_stack_index (1);
+ if (sindex >= 0 && newlen >= 0
+ && pindex >= 0 && pindex < PERF_N)
+ {
+ int i;
+ for (i = 0; i < sindex; ++i)
+ if (perf_set.stack[i] == pindex)
+ {
+ perf_print_state (M_INFO);
+ msg (M_FATAL, "PERF: push_perf_index %s failed",
+ metric_names [pindex]);
+ }
+
+ perf_set.stack[sindex] = pindex;
+ perf_set.stack_len = newlen;
+ }
+ else
+ msg (M_FATAL, "PERF: push_perf_index: stack push error");
+}
+
+static void
+pop_perf_index (void)
+{
+ const int newlen = get_stack_index (-1);
+ if (newlen >= 0)
+ {
+ perf_set.stack_len = newlen;
+ }
+ else
+ msg (M_FATAL, "PERF: pop_perf_index: stack pop error");
+}
+
+static void
+state_must_be (const struct perf *p, const int wanted)
+{
+ if (p->state != wanted)
+ msg (M_FATAL, "PERF: bad state actual=%d wanted=%d",
+ p->state,
+ wanted);
+}
+
+static void
+update_sofar (struct perf *p)
+{
+ struct timeval current;
+ ASSERT (!gettimeofday (&current, NULL));
+ p->sofar += (double) tv_subtract (&current, &p->start, 600) / 1000000.0;
+ tv_clear (&p->start);
+}
+
+static void
+perf_start (struct perf *p)
+{
+ state_must_be (p, PS_INITIAL);
+ ASSERT (!gettimeofday (&p->start, NULL));
+ p->sofar = 0.0;
+ p->state = PS_METER_RUNNING;
+}
+
+static void
+perf_stop (struct perf *p)
+{
+ state_must_be (p, PS_METER_RUNNING);
+ update_sofar (p);
+ p->sum += p->sofar;
+ if (p->sofar > p->max)
+ p->max = p->sofar;
+ p->count += 1.0;
+ p->sofar = 0.0;
+ p->state = PS_INITIAL;
+}
+
+static void
+perf_interrupt (struct perf *p)
+{
+ state_must_be (p, PS_METER_RUNNING);
+ update_sofar (p);
+ p->state = PS_METER_INTERRUPTED;
+}
+
+static void
+perf_resume (struct perf *p)
+{
+ state_must_be (p, PS_METER_INTERRUPTED);
+ ASSERT (!gettimeofday (&p->start, NULL));
+ p->state = PS_METER_RUNNING;
+}
+
+void
+perf_push (int type)
+{
+ struct perf *prev;
+ struct perf *cur;
+
+ ASSERT (SIZE(metric_names) == PERF_N);
+ push_perf_index (type);
+
+ prev = get_perf (-2);
+ cur = get_perf (-1);
+
+ ASSERT (cur);
+
+ if (prev)
+ perf_interrupt (prev);
+ perf_start (cur);
+}
+
+void
+perf_pop (void)
+{
+ struct perf *prev;
+ struct perf *cur;
+
+ prev = get_perf (-2);
+ cur = get_perf (-1);
+
+ ASSERT (cur);
+ perf_stop (cur);
+
+ if (prev)
+ perf_resume (prev);
+
+ pop_perf_index ();
+}
+
+void
+perf_output_results (void)
+{
+ int i;
+ msg (M_INFO, "LATENCY PROFILE (mean and max are in milliseconds)");
+ for (i = 0; i < PERF_N; ++i)
+ {
+ struct perf *p = &perf_set.perf[i];
+ if (p->count > 0.0)
+ {
+ const double mean = p->sum / p->count;
+ msg (M_INFO, "%s n=%.0f mean=%.3f max=%.3f", metric_names[i], p->count, mean*1000.0, p->max*1000.0);
+ }
+ }
+}
+
+static void
+perf_print_state (int lev)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ msg (lev, "PERF STATE");
+ msg (lev, "Stack:");
+ for (i = 0; i < perf_set.stack_len; ++i)
+ {
+ const int j = perf_set.stack[i];
+ const struct perf *p = &perf_set.perf[j];
+ msg (lev, "[%d] %s state=%d start=%s sofar=%f sum=%f max=%f count=%f",
+ i,
+ metric_names[j],
+ p->state,
+ tv_string (&p->start, &gc),
+ p->sofar,
+ p->sum,
+ p->max,
+ p->count);
+ }
+ gc_free (&gc);
+}
+
+#else
+#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
+static void dummy(void) {}
+#endif
+#endif
diff --git a/perf.h b/perf.h
new file mode 100644
index 0000000..c531d9c
--- /dev/null
+++ b/perf.h
@@ -0,0 +1,82 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * The interval_ routines are designed to optimize the calling of a routine
+ * (normally tls_multi_process()) which can be called less frequently
+ * between triggers.
+ */
+
+#ifndef PERF_H
+#define PERF_H
+
+/*#define ENABLE_PERFORMANCE_METRICS*/
+
+/*
+ * Metrics
+ */
+#define PERF_BIO_READ_PLAINTEXT 0
+#define PERF_BIO_WRITE_PLAINTEXT 1
+#define PERF_BIO_READ_CIPHERTEXT 2
+#define PERF_BIO_WRITE_CIPHERTEXT 3
+#define PERF_TLS_MULTI_PROCESS 4
+#define PERF_IO_WAIT 5
+#define PERF_EVENT_LOOP 6
+#define PERF_MULTI_CREATE_INSTANCE 7
+#define PERF_MULTI_CLOSE_INSTANCE 8
+#define PERF_MULTI_SHOW_STATS 9
+#define PERF_MULTI_BCAST 10
+#define PERF_MULTI_MCAST 11
+#define PERF_SCRIPT 12
+#define PERF_READ_IN_LINK 13
+#define PERF_PROC_IN_LINK 14
+#define PERF_READ_IN_TUN 15
+#define PERF_PROC_IN_TUN 16
+#define PERF_PROC_OUT_LINK 17
+#define PERF_PROC_OUT_TUN 18
+#define PERF_PROC_OUT_TUN_MTCP 19
+#define PERF_N 20
+
+#ifdef ENABLE_PERFORMANCE_METRICS
+
+#include "basic.h"
+
+/*
+ * Stack size
+ */
+#define STACK_N 64
+
+void perf_push (int type);
+void perf_pop (void);
+void perf_output_results (void);
+
+#else
+
+static inline void perf_push (int type) {}
+static inline void perf_pop (void) {}
+static inline void perf_output_results (void) {}
+
+#endif
+
+#endif
diff --git a/pf-inline.h b/pf-inline.h
new file mode 100644
index 0000000..6b5dcb2
--- /dev/null
+++ b/pf-inline.h
@@ -0,0 +1,59 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if defined(ENABLE_PF) && !defined(PF_INLINE_H)
+#define PF_INLINE_H
+
+/*
+ * Inline functions
+ */
+
+#define PCT_SRC 1
+#define PCT_DEST 2
+static inline bool
+pf_c2c_test (const struct context *src, const struct context *dest, const char *prefix)
+{
+ bool pf_cn_test (struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix);
+ return (!src->c2.pf.enabled || pf_cn_test (src->c2.pf.pfs, dest->c2.tls_multi, PCT_DEST, prefix))
+ && (!dest->c2.pf.enabled || pf_cn_test (dest->c2.pf.pfs, src->c2.tls_multi, PCT_SRC, prefix));
+}
+
+static inline bool
+pf_addr_test (const struct context *src, const struct mroute_addr *dest, const char *prefix)
+{
+ bool pf_addr_test_dowork (const struct context *src, const struct mroute_addr *dest, const char *prefix);
+
+ if (src->c2.pf.enabled)
+ return pf_addr_test_dowork (src, dest, prefix);
+ else
+ return true;
+}
+
+static inline bool
+pf_kill_test (const struct pf_set *pfs)
+{
+ return pfs->kill;
+}
+
+#endif
diff --git a/pf.c b/pf.c
new file mode 100644
index 0000000..6b4cba4
--- /dev/null
+++ b/pf.c
@@ -0,0 +1,711 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* packet filter functions */
+
+#include "syshead.h"
+
+#if defined(ENABLE_PF)
+
+#include "init.h"
+
+#include "memdbg.h"
+
+#include "pf-inline.h"
+
+static void
+pf_destroy (struct pf_set *pfs)
+{
+ if (pfs)
+ {
+ if (pfs->cns.hash_table)
+ hash_free (pfs->cns.hash_table);
+
+ {
+ struct pf_cn_elem *l = pfs->cns.list;
+ while (l)
+ {
+ struct pf_cn_elem *next = l->next;
+ free (l->rule.cn);
+ free (l);
+ l = next;
+ }
+ }
+ {
+ struct pf_subnet *l = pfs->sns.list;
+ while (l)
+ {
+ struct pf_subnet *next = l->next;
+ free (l);
+ l = next;
+ }
+ }
+ free (pfs);
+ }
+}
+
+static bool
+add_client (const char *line, const char *prefix, const int line_num, struct pf_cn_elem ***next, const bool exclude)
+{
+ struct pf_cn_elem *e;
+ ALLOC_OBJ_CLEAR (e, struct pf_cn_elem);
+ e->rule.exclude = exclude;
+ e->rule.cn = string_alloc (line, NULL);
+ **next = e;
+ *next = &e->next;
+ return true;
+}
+
+static bool
+add_subnet (const char *line, const char *prefix, const int line_num, struct pf_subnet ***next, const bool exclude)
+{
+ struct in_addr network;
+ in_addr_t netmask = 0;
+
+ if (strcmp (line, "unknown"))
+ {
+ int netbits = 32;
+ char *div = strchr (line, '/');
+
+ if (div)
+ {
+ *div++ = '\0';
+ if (sscanf (div, "%d", &netbits) != 1)
+ {
+ msg (D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: '%s'", prefix, line_num, div);
+ return false;
+ }
+ if (netbits < 0 || netbits > 32)
+ {
+ msg (D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", prefix, line_num, div);
+ return false;
+ }
+ }
+
+ if (openvpn_inet_aton (line, &network) != OIA_IP)
+ {
+ msg (D_PF_INFO, "PF: %s/%d: bad network address: '%s'", prefix, line_num, line);
+ return false;
+ }
+ netmask = netbits_to_netmask (netbits);
+ if ((network.s_addr & htonl (netmask)) != network.s_addr)
+ {
+ network.s_addr &= htonl (netmask);
+ msg (M_WARN, "WARNING: PF: %s/%d: incorrect subnet %s/%d changed to %s/%d", prefix, line_num, line, netbits, inet_ntoa (network), netbits);
+ }
+ }
+ else
+ {
+ /* match special "unknown" tag for addresses unrecognized by mroute */
+ network.s_addr = htonl(0);
+ netmask = ~0;
+ }
+
+ {
+ struct pf_subnet *e;
+ ALLOC_OBJ_CLEAR (e, struct pf_subnet);
+ e->rule.exclude = exclude;
+ e->rule.network = ntohl (network.s_addr);
+ e->rule.netmask = netmask;
+ **next = e;
+ *next = &e->next;
+ return true;
+ }
+}
+
+static uint32_t
+cn_hash_function (const void *key, uint32_t iv)
+{
+ return hash_func ((uint8_t *)key, strlen ((char *)key) + 1, iv);
+}
+
+static bool
+cn_compare_function (const void *key1, const void *key2)
+{
+ return !strcmp((const char *)key1, (const char *)key2);
+}
+
+static bool
+genhash (struct pf_cn_set *cns, const char *prefix, const int n_clients)
+{
+ struct pf_cn_elem *e;
+ bool status = true;
+ int n_buckets = n_clients;
+
+ if (n_buckets < 16)
+ n_buckets = 16;
+ cns->hash_table = hash_init (n_buckets, 0, cn_hash_function, cn_compare_function);
+ for (e = cns->list; e != NULL; e = e->next)
+ {
+ if (!hash_add (cns->hash_table, e->rule.cn, &e->rule, false))
+ {
+ msg (D_PF_INFO, "PF: %s: duplicate common name in [clients] section: '%s'", prefix, e->rule.cn);
+ status = false;
+ }
+ }
+
+ return status;
+}
+
+static struct pf_set *
+pf_init (const struct buffer_list *bl, const char *prefix, const bool allow_kill)
+{
+# define MODE_UNDEF 0
+# define MODE_CLIENTS 1
+# define MODE_SUBNETS 2
+ int mode = MODE_UNDEF;
+ int line_num = 0;
+ int n_clients = 0;
+ int n_subnets = 0;
+ int n_errors = 0;
+ struct pf_set *pfs = NULL;
+ char line[PF_MAX_LINE_LEN];
+
+ ALLOC_OBJ_CLEAR (pfs, struct pf_set);
+ if (bl)
+ {
+ struct pf_cn_elem **cl = &pfs->cns.list;
+ struct pf_subnet **sl = &pfs->sns.list;
+ struct buffer_entry *be;
+
+ for (be = bl->head; be != NULL; be = be->next)
+ {
+ ++line_num;
+ strncpynt (line, BSTR(&be->buf), sizeof(line));
+ rm_trailing_chars (line, "\r\n\t ");
+ if (line[0] == '\0' || line[0] == '#')
+ ;
+ else if (line[0] == '+' || line[0] == '-')
+ {
+ bool exclude = (line[0] == '-');
+
+ if (line[1] =='\0')
+ {
+ msg (D_PF_INFO, "PF: %s/%d: no data after +/-: '%s'", prefix, line_num, line);
+ ++n_errors;
+ }
+ else if (mode == MODE_CLIENTS)
+ {
+ if (add_client (&line[1], prefix, line_num, &cl, exclude))
+ ++n_clients;
+ else
+ ++n_errors;
+ }
+ else if (mode == MODE_SUBNETS)
+ {
+ if (add_subnet (&line[1], prefix, line_num, &sl, exclude))
+ ++n_subnets;
+ else
+ ++n_errors;
+ }
+ else if (mode == MODE_UNDEF)
+ ;
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ else if (line[0] == '[')
+ {
+ if (!strcasecmp (line, "[clients accept]"))
+ {
+ mode = MODE_CLIENTS;
+ pfs->cns.default_allow = true;
+ }
+ else if (!strcasecmp (line, "[clients drop]"))
+ {
+ mode = MODE_CLIENTS;
+ pfs->cns.default_allow = false;
+ }
+ else if (!strcasecmp (line, "[subnets accept]"))
+ {
+ mode = MODE_SUBNETS;
+ pfs->sns.default_allow = true;
+ }
+ else if (!strcasecmp (line, "[subnets drop]"))
+ {
+ mode = MODE_SUBNETS;
+ pfs->sns.default_allow = false;
+ }
+ else if (!strcasecmp (line, "[end]"))
+ goto done;
+ else if (allow_kill && !strcasecmp (line, "[kill]"))
+ goto kill;
+ else
+ {
+ mode = MODE_UNDEF;
+ msg (D_PF_INFO, "PF: %s/%d unknown tag: '%s'", prefix, line_num, line);
+ ++n_errors;
+ }
+ }
+ else
+ {
+ msg (D_PF_INFO, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", prefix, line_num, line);
+ ++n_errors;
+ }
+ }
+ ++n_errors;
+ msg (D_PF_INFO, "PF: %s: missing [end]", prefix);
+ }
+ else
+ {
+ msg (D_PF_INFO, "PF: %s: cannot open", prefix);
+ ++n_errors;
+ }
+
+ done:
+ if (bl)
+ {
+ if (!n_errors)
+ {
+ if (!genhash (&pfs->cns, prefix, n_clients))
+ ++n_errors;
+ }
+ if (n_errors)
+ msg (D_PF_INFO, "PF: %s rejected due to %d error(s)", prefix, n_errors);
+ }
+ if (n_errors)
+ {
+ pf_destroy (pfs);
+ pfs = NULL;
+ }
+ return pfs;
+
+ kill:
+ pf_destroy (pfs);
+ ALLOC_OBJ_CLEAR (pfs, struct pf_set);
+ pfs->kill = true;
+ return pfs;
+}
+
+#ifdef PLUGIN_PF
+static struct pf_set *
+pf_init_from_file (const char *fn)
+{
+ struct buffer_list *bl = buffer_list_file (fn, PF_MAX_LINE_LEN);
+ if (bl)
+ {
+ struct pf_set *pfs = pf_init (bl, fn, true);
+ buffer_list_free (bl);
+ return pfs;
+ }
+ else
+ {
+ msg (D_PF_INFO|M_ERRNO, "PF: %s: cannot open", fn);
+ return NULL;
+ }
+}
+#endif
+
+#ifdef ENABLE_DEBUG
+
+static const char *
+drop_accept (const bool accept)
+{
+ return accept ? "ACCEPT" : "DROP";
+}
+
+static const char *
+pct_name (const int type)
+{
+ switch (type)
+ {
+ case PCT_SRC:
+ return "SRC";
+ case PCT_DEST:
+ return "DEST";
+ default:
+ return "???";
+ }
+}
+
+static void
+pf_cn_test_print (const char *prefix,
+ const int type,
+ const char *prefix2,
+ const char *cn,
+ const bool allow,
+ const struct pf_cn *rule)
+{
+ if (rule)
+ {
+ dmsg (D_PF_DEBUG, "PF: %s/%s/%s %s %s rule=[%s %s]",
+ prefix, prefix2, pct_name (type),
+ cn, drop_accept (allow),
+ rule->cn, drop_accept (!rule->exclude));
+ }
+ else
+ {
+ dmsg (D_PF_DEBUG, "PF: %s/%s/%s %s %s",
+ prefix, prefix2, pct_name (type),
+ cn, drop_accept (allow));
+ }
+}
+
+static void
+pf_addr_test_print (const char *prefix,
+ const char *prefix2,
+ const struct context *src,
+ const struct mroute_addr *dest,
+ const bool allow,
+ const struct ipv4_subnet *rule)
+{
+ struct gc_arena gc = gc_new ();
+ if (rule)
+ {
+ dmsg (D_PF_DEBUG, "PF: %s/%s %s %s %s rule=[%s/%s %s]",
+ prefix,
+ prefix2,
+ tls_common_name (src->c2.tls_multi, false),
+ mroute_addr_print_ex (dest, MAPF_SHOW_ARP, &gc),
+ drop_accept (allow),
+ print_in_addr_t (rule->network, 0, &gc),
+ print_in_addr_t (rule->netmask, 0, &gc),
+ drop_accept (!rule->exclude));
+ }
+ else
+ {
+ dmsg (D_PF_DEBUG, "PF: %s/%s %s %s %s",
+ prefix,
+ prefix2,
+ tls_common_name (src->c2.tls_multi, false),
+ mroute_addr_print_ex (dest, MAPF_SHOW_ARP, &gc),
+ drop_accept (allow));
+ }
+ gc_free (&gc);
+}
+
+#endif
+
+static inline struct pf_cn *
+lookup_cn_rule (struct hash *h, const char *cn, const uint32_t cn_hash)
+{
+ struct hash_element *he = hash_lookup_fast (h, hash_bucket (h, cn_hash), cn, cn_hash);
+ if (he)
+ return (struct pf_cn *) he->value;
+ else
+ return NULL;
+}
+
+bool
+pf_cn_test (struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix)
+{
+ if (!pfs->kill)
+ {
+ const char *cn;
+ uint32_t cn_hash;
+ if (tls_common_name_hash (tm, &cn, &cn_hash))
+ {
+ const struct pf_cn *rule = lookup_cn_rule (pfs->cns.hash_table, cn, cn_hash);
+ if (rule)
+ {
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_cn_test_print ("PF_CN_MATCH", type, prefix, cn, !rule->exclude, rule);
+#endif
+ if (!rule->exclude)
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_cn_test_print ("PF_CN_DEFAULT", type, prefix, cn, pfs->cns.default_allow, NULL);
+#endif
+ if (pfs->cns.default_allow)
+ return true;
+ else
+ return false;
+ }
+ }
+ }
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_cn_test_print ("PF_CN_FAULT", type, prefix, tls_common_name (tm, false), false, NULL);
+#endif
+ return false;
+}
+
+bool
+pf_addr_test_dowork (const struct context *src, const struct mroute_addr *dest, const char *prefix)
+{
+ struct pf_set *pfs = src->c2.pf.pfs;
+ if (pfs && !pfs->kill)
+ {
+ const in_addr_t addr = in_addr_t_from_mroute_addr (dest);
+ const struct pf_subnet *se = pfs->sns.list;
+ while (se)
+ {
+ if ((addr & se->rule.netmask) == se->rule.network)
+ {
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_addr_test_print ("PF_ADDR_MATCH", prefix, src, dest, !se->rule.exclude, &se->rule);
+#endif
+ return !se->rule.exclude;
+ }
+ se = se->next;
+ }
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_addr_test_print ("PF_ADDR_DEFAULT", prefix, src, dest, pfs->sns.default_allow, NULL);
+#endif
+ return pfs->sns.default_allow;
+ }
+ else
+ {
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_addr_test_print ("PF_ADDR_FAULT", prefix, src, dest, false, NULL);
+#endif
+ return false;
+ }
+}
+
+#ifdef PLUGIN_PF
+void
+pf_check_reload (struct context *c)
+{
+ const int slow_wakeup = 15;
+ const int fast_wakeup = 1;
+ const int wakeup_transition = 60;
+ bool reloaded = false;
+
+ if (c->c2.pf.enabled
+ && c->c2.pf.filename
+ && event_timeout_trigger (&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT))
+ {
+ struct stat s;
+ if (!stat (c->c2.pf.filename, &s))
+ {
+ if (s.st_mtime > c->c2.pf.file_last_mod)
+ {
+ struct pf_set *pfs = pf_init_from_file (c->c2.pf.filename);
+ if (pfs)
+ {
+ if (c->c2.pf.pfs)
+ pf_destroy (c->c2.pf.pfs);
+ c->c2.pf.pfs = pfs;
+ reloaded = true;
+ if (pf_kill_test (pfs))
+ {
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "pf-kill";
+ }
+ }
+ c->c2.pf.file_last_mod = s.st_mtime;
+ }
+ }
+ {
+ int wakeup = slow_wakeup;
+ if (!c->c2.pf.pfs && c->c2.pf.n_check_reload < wakeup_transition)
+ wakeup = fast_wakeup;
+ event_timeout_init (&c->c2.pf.reload, wakeup, now);
+ reset_coarse_timers (c);
+ c->c2.pf.n_check_reload++;
+ }
+ }
+#ifdef ENABLE_DEBUG
+ if (reloaded && check_debug_level (D_PF_DEBUG))
+ pf_context_print (&c->c2.pf, "pf_check_reload", D_PF_DEBUG);
+#endif
+}
+#endif
+
+#ifdef MANAGEMENT_PF
+bool
+pf_load_from_buffer_list (struct context *c, const struct buffer_list *config)
+{
+ struct pf_set *pfs = pf_init (config, "[SERVER-PF]", false);
+ if (pfs)
+ {
+ if (c->c2.pf.pfs)
+ pf_destroy (c->c2.pf.pfs);
+ c->c2.pf.pfs = pfs;
+ return true;
+ }
+ else
+ return false;
+}
+#endif
+
+void
+pf_init_context (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+#ifdef PLUGIN_PF
+ if (plugin_defined (c->plugins, OPENVPN_PLUGIN_ENABLE_PF))
+ {
+ const char *pf_file = create_temp_file (c->options.tmp_dir, "pf", &gc);
+ if( pf_file ) {
+ setenv_str (c->c2.es, "pf_file", pf_file);
+
+ if (plugin_call (c->plugins, OPENVPN_PLUGIN_ENABLE_PF, NULL, NULL, c->c2.es) == OPENVPN_PLUGIN_FUNC_SUCCESS)
+ {
+ event_timeout_init (&c->c2.pf.reload, 1, now);
+ c->c2.pf.filename = string_alloc (pf_file, NULL);
+ c->c2.pf.enabled = true;
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_context_print (&c->c2.pf, "pf_init_context#1", D_PF_DEBUG);
+#endif
+ }
+ else
+ {
+ msg (M_WARN, "WARNING: OPENVPN_PLUGIN_ENABLE_PF disabled");
+ }
+ }
+ }
+#endif
+#ifdef MANAGEMENT_PF
+ if (!c->c2.pf.enabled && management_enable_pf (management))
+ {
+ c->c2.pf.enabled = true;
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_PF_DEBUG))
+ pf_context_print (&c->c2.pf, "pf_init_context#2", D_PF_DEBUG);
+#endif
+ }
+#endif
+ gc_free (&gc);
+}
+
+void
+pf_destroy_context (struct pf_context *pfc)
+{
+#ifdef PLUGIN_PF
+ if (pfc->filename)
+ {
+ delete_file (pfc->filename);
+ free (pfc->filename);
+ }
+#endif
+ if (pfc->pfs)
+ pf_destroy (pfc->pfs);
+}
+
+#ifdef ENABLE_DEBUG
+
+static void
+pf_subnet_set_print (const struct pf_subnet_set *s, const int lev)
+{
+ struct gc_arena gc = gc_new ();
+ if (s)
+ {
+ struct pf_subnet *e;
+
+ msg (lev, " ----- struct pf_subnet_set -----");
+ msg (lev, " default_allow=%s", drop_accept (s->default_allow));
+
+ for (e = s->list; e != NULL; e = e->next)
+ {
+ msg (lev, " %s/%s %s",
+ print_in_addr_t (e->rule.network, 0, &gc),
+ print_in_addr_t (e->rule.netmask, 0, &gc),
+ drop_accept (!e->rule.exclude));
+ }
+ }
+ gc_free (&gc);
+}
+
+static void
+pf_cn_set_print (const struct pf_cn_set *s, const int lev)
+{
+ if (s)
+ {
+ struct hash_iterator hi;
+ struct hash_element *he;
+
+ msg (lev, " ----- struct pf_cn_set -----");
+ msg (lev, " default_allow=%s", drop_accept (s->default_allow));
+
+ if (s->hash_table)
+ {
+ hash_iterator_init (s->hash_table, &hi);
+ while ((he = hash_iterator_next (&hi)))
+ {
+ struct pf_cn *e = (struct pf_cn *)he->value;
+ msg (lev, " %s %s",
+ e->cn,
+ drop_accept (!e->exclude));
+ }
+
+ msg (lev, " ----------");
+
+ {
+ struct pf_cn_elem *ce;
+ for (ce = s->list; ce != NULL; ce = ce->next)
+ {
+ struct pf_cn *e = lookup_cn_rule (s->hash_table, ce->rule.cn, cn_hash_function (ce->rule.cn, 0));
+ if (e)
+ {
+ msg (lev, " %s %s",
+ e->cn,
+ drop_accept (!e->exclude));
+ }
+ else
+ {
+ msg (lev, " %s LOOKUP FAILED", ce->rule.cn);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+pf_set_print (const struct pf_set *pfs, const int lev)
+{
+ if (pfs)
+ {
+ msg (lev, " ----- struct pf_set -----");
+ msg (lev, " kill=%d", pfs->kill);
+ pf_subnet_set_print (&pfs->sns, lev);
+ pf_cn_set_print (&pfs->cns, lev);
+ }
+}
+
+void
+pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev)
+{
+ msg (lev, "----- %s : struct pf_context -----", prefix);
+ if (pfc)
+ {
+ msg (lev, "enabled=%d", pfc->enabled);
+#ifdef PLUGIN_PF
+ msg (lev, "filename='%s'", np(pfc->filename));
+ msg (lev, "file_last_mod=%u", (unsigned int)pfc->file_last_mod);
+ msg (lev, "n_check_reload=%u", pfc->n_check_reload);
+ msg (lev, "reload=[%d,%u,%u]", pfc->reload.defined, pfc->reload.n, (unsigned int)pfc->reload.last);
+#endif
+ pf_set_print (pfc->pfs, lev);
+ }
+ msg (lev, "--------------------");
+}
+
+#endif
+
+#endif
diff --git a/pf.h b/pf.h
new file mode 100644
index 0000000..04adf0e
--- /dev/null
+++ b/pf.h
@@ -0,0 +1,102 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* packet filter functions */
+
+#if defined(ENABLE_PF) && !defined(OPENVPN_PF_H)
+#define OPENVPN_PF_H
+
+#include "list.h"
+#include "mroute.h"
+
+#define PF_MAX_LINE_LEN 256
+
+struct context;
+
+struct ipv4_subnet {
+ bool exclude;
+ in_addr_t network;
+ in_addr_t netmask;
+};
+
+struct pf_subnet {
+ struct pf_subnet *next;
+ struct ipv4_subnet rule;
+};
+
+struct pf_subnet_set {
+ bool default_allow;
+ struct pf_subnet *list;
+};
+
+struct pf_cn {
+ bool exclude;
+ char *cn;
+};
+
+struct pf_cn_elem {
+ struct pf_cn_elem *next;
+ struct pf_cn rule;
+};
+
+struct pf_cn_set {
+ bool default_allow;
+ struct pf_cn_elem *list;
+ struct hash *hash_table;
+};
+
+struct pf_set {
+ bool kill;
+ struct pf_subnet_set sns;
+ struct pf_cn_set cns;
+};
+
+struct pf_context {
+ bool enabled;
+ struct pf_set *pfs;
+#ifdef PLUGIN_PF
+ char *filename;
+ time_t file_last_mod;
+ unsigned int n_check_reload;
+ struct event_timeout reload;
+#endif
+};
+
+void pf_init_context (struct context *c);
+
+void pf_destroy_context (struct pf_context *pfc);
+
+#ifdef PLUGIN_PF
+void pf_check_reload (struct context *c);
+#endif
+
+#ifdef MANAGEMENT_PF
+bool pf_load_from_buffer_list (struct context *c, const struct buffer_list *config);
+#endif
+
+#ifdef ENABLE_DEBUG
+void pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev);
+#endif
+
+#endif
diff --git a/ping-inline.h b/ping-inline.h
new file mode 100644
index 0000000..c724970
--- /dev/null
+++ b/ping-inline.h
@@ -0,0 +1,59 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PING_INLINE_H
+#define PING_INLINE_H
+
+/*
+ * Should we exit or restart due to ping (or other authenticated packet)
+ * not received in n seconds?
+ */
+static inline void
+check_ping_restart (struct context *c)
+{
+ void check_ping_restart_dowork (struct context *c);
+ if (c->options.ping_rec_timeout
+ && event_timeout_trigger (&c->c2.ping_rec_interval,
+ &c->c2.timeval,
+ (!c->options.ping_timer_remote
+ || link_socket_actual_defined (&c->c1.link_socket_addr.actual))
+ ? ETT_DEFAULT : 15))
+ check_ping_restart_dowork (c);
+}
+
+/*
+ * Should we ping the remote?
+ */
+static inline void
+check_ping_send (struct context *c)
+{
+ void check_ping_send_dowork (struct context *c);
+ if (c->options.ping_send_timeout
+ && event_timeout_trigger (&c->c2.ping_send_interval,
+ &c->c2.timeval,
+ !TO_LINK_DEF(c) ? ETT_DEFAULT : 1))
+ check_ping_send_dowork (c);
+}
+
+#endif
diff --git a/ping.c b/ping.c
new file mode 100644
index 0000000..191ad74
--- /dev/null
+++ b/ping.c
@@ -0,0 +1,92 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "ping.h"
+
+#include "memdbg.h"
+
+#include "ping-inline.h"
+
+/*
+ * This random string identifies an OpenVPN ping packet.
+ * It should be of sufficient length and randomness
+ * so as not to collide with other tunnel data.
+ *
+ * PING_STRING_SIZE must be sizeof (ping_string)
+ */
+const uint8_t ping_string[] = {
+ 0x2a, 0x18, 0x7b, 0xf3, 0x64, 0x1e, 0xb4, 0xcb,
+ 0x07, 0xed, 0x2d, 0x0a, 0x98, 0x1f, 0xc7, 0x48
+};
+
+/*
+ * Should we exit or restart due to ping (or other authenticated packet)
+ * not received in n seconds?
+ */
+void
+check_ping_restart_dowork (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+ switch (c->options.ping_rec_timeout_action)
+ {
+ case PING_EXIT:
+ msg (M_INFO, "%sInactivity timeout (--ping-exit), exiting",
+ format_common_name (c, &gc));
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "ping-exit";
+ break;
+ case PING_RESTART:
+ msg (M_INFO, "%sInactivity timeout (--ping-restart), restarting",
+ format_common_name (c, &gc));
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Ping Restart */
+ c->sig->signal_text = "ping-restart";
+ break;
+ default:
+ ASSERT (0);
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Should we ping the remote?
+ */
+void
+check_ping_send_dowork (struct context *c)
+{
+ c->c2.buf = c->c2.buffers->aux_buf;
+ ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame)));
+ ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)));
+ ASSERT (buf_write (&c->c2.buf, ping_string, sizeof (ping_string)));
+
+ /*
+ * We will treat the ping like any other outgoing packet,
+ * encrypt, sign, etc.
+ */
+ encrypt_sign (c, true);
+ /* Set length to 0, so it won't be counted as activity */
+ c->c2.buf.len = 0;
+ dmsg (D_PING, "SENT PING");
+}
diff --git a/ping.h b/ping.h
new file mode 100644
index 0000000..88f5f3a
--- /dev/null
+++ b/ping.h
@@ -0,0 +1,47 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PING_H
+#define PING_H
+
+#include "init.h"
+#include "forward.h"
+
+/*
+ * Initial default --ping-restart before --pull
+ */
+#define PRE_PULL_INITIAL_PING_RESTART 120 /* in seconds */
+
+extern const uint8_t ping_string[];
+
+/* PING_STRING_SIZE must be sizeof (ping_string) */
+#define PING_STRING_SIZE 16
+
+static inline bool
+is_ping_msg (const struct buffer* buf)
+{
+ return buf_string_match (buf, ping_string, PING_STRING_SIZE);
+}
+
+#endif
diff --git a/pkcs11.c b/pkcs11.c
new file mode 100644
index 0000000..9cf18e5
--- /dev/null
+++ b/pkcs11.c
@@ -0,0 +1,990 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if defined(ENABLE_PKCS11)
+
+#include <pkcs11-helper-1.0/pkcs11h-certificate.h>
+#include <pkcs11-helper-1.0/pkcs11h-openssl.h>
+#include "basic.h"
+#include "error.h"
+#include "manage.h"
+#include "base64.h"
+#include "pkcs11.h"
+#include "misc.h"
+#include "otime.h"
+
+static
+time_t
+__mytime (void) {
+ return openvpn_time (NULL);
+}
+
+#if !defined(_WIN32)
+static
+int
+__mygettimeofday (struct timeval *tv) {
+ return gettimeofday (tv, NULL);
+}
+#endif
+
+static
+void
+__mysleep (const unsigned long usec) {
+#if defined(_WIN32)
+ Sleep (usec/1000);
+#else
+ usleep (usec);
+#endif
+}
+
+
+static pkcs11h_engine_system_t s_pkcs11h_sys_engine = {
+ malloc,
+ free,
+ __mytime,
+ __mysleep,
+#if defined(_WIN32)
+ NULL
+#else
+ __mygettimeofday
+#endif
+};
+
+static
+unsigned
+_pkcs11_msg_pkcs112openvpn (
+ const unsigned flags
+) {
+ unsigned openvpn_flags;
+
+ switch (flags) {
+ case PKCS11H_LOG_DEBUG2:
+ openvpn_flags = D_PKCS11_DEBUG;
+ break;
+ case PKCS11H_LOG_DEBUG1:
+ openvpn_flags = D_SHOW_PKCS11;
+ break;
+ case PKCS11H_LOG_INFO:
+ openvpn_flags = M_INFO;
+ break;
+ case PKCS11H_LOG_WARN:
+ openvpn_flags = M_WARN;
+ break;
+ case PKCS11H_LOG_ERROR:
+ openvpn_flags = M_FATAL;
+ break;
+ default:
+ openvpn_flags = M_FATAL;
+ break;
+ }
+
+#if defined(ENABLE_PKCS11_FORCE_DEBUG)
+ openvpn_flags=M_INFO;
+#endif
+
+ return openvpn_flags;
+}
+
+static
+unsigned
+_pkcs11_msg_openvpn2pkcs11 (
+ const unsigned flags
+) {
+ unsigned pkcs11_flags;
+
+ if ((flags & D_PKCS11_DEBUG) != 0) {
+ pkcs11_flags = PKCS11H_LOG_DEBUG2;
+ }
+ else if ((flags & D_SHOW_PKCS11) != 0) {
+ pkcs11_flags = PKCS11H_LOG_DEBUG1;
+ }
+ else if ((flags & M_INFO) != 0) {
+ pkcs11_flags = PKCS11H_LOG_INFO;
+ }
+ else if ((flags & M_WARN) != 0) {
+ pkcs11_flags = PKCS11H_LOG_WARN;
+ }
+ else if ((flags & M_FATAL) != 0) {
+ pkcs11_flags = PKCS11H_LOG_ERROR;
+ }
+ else {
+ pkcs11_flags = PKCS11H_LOG_ERROR;
+ }
+
+#if defined(ENABLE_PKCS11_FORCE_DEBUG)
+ pkcs11_flags = PKCS11H_LOG_DEBUG2;
+#endif
+
+ return pkcs11_flags;
+}
+
+static
+void
+_pkcs11_openvpn_log (
+ void * const global_data,
+ unsigned flags,
+ const char * const szFormat,
+ va_list args
+) {
+ char Buffer[10*1024];
+
+ (void)global_data;
+
+ vsnprintf (Buffer, sizeof (Buffer), szFormat, args);
+ Buffer[sizeof (Buffer)-1] = 0;
+
+ msg (_pkcs11_msg_pkcs112openvpn (flags), "%s", Buffer);
+}
+
+static
+PKCS11H_BOOL
+_pkcs11_openvpn_token_prompt (
+ void * const global_data,
+ void * const user_data,
+ const pkcs11h_token_id_t token,
+ const unsigned retry
+) {
+ struct user_pass token_resp;
+
+ (void)global_data;
+ (void)user_data;
+ (void)retry;
+
+ ASSERT (token!=NULL);
+
+ CLEAR (token_resp);
+ token_resp.defined = false;
+ token_resp.nocache = true;
+ openvpn_snprintf (
+ token_resp.username,
+ sizeof (token_resp.username),
+ "Please insert %s token",
+ token->label
+ );
+
+ if (
+ !get_user_pass (
+ &token_resp,
+ NULL,
+ "token-insertion-request",
+ GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_OK|GET_USER_PASS_NOFATAL
+ )
+ ) {
+ return false;
+ }
+ else {
+ return strcmp (token_resp.password, "ok") == 0;
+ }
+}
+
+static
+PKCS11H_BOOL
+_pkcs11_openvpn_pin_prompt (
+ void * const global_data,
+ void * const user_data,
+ const pkcs11h_token_id_t token,
+ const unsigned retry,
+ char * const pin,
+ const size_t pin_max
+) {
+ struct user_pass token_pass;
+ char prompt[1024];
+
+ (void)global_data;
+ (void)user_data;
+ (void)retry;
+
+ ASSERT (token!=NULL);
+
+ openvpn_snprintf (prompt, sizeof (prompt), "%s token", token->label);
+
+ token_pass.defined = false;
+ token_pass.nocache = true;
+
+ if (
+ !get_user_pass (
+ &token_pass,
+ NULL,
+ prompt,
+ GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY|GET_USER_PASS_NOFATAL
+ )
+ ) {
+ return false;
+ }
+ else {
+ strncpynt (pin, token_pass.password, pin_max);
+ purge_user_pass (&token_pass, true);
+
+ if (strlen (pin) == 0) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+}
+
+bool
+pkcs11_initialize (
+ const bool protected_auth,
+ const int nPINCachePeriod
+) {
+ CK_RV rv = CKR_FUNCTION_FAILED;
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_initialize - entered"
+ );
+
+ if ((rv = pkcs11h_engine_setSystem (&s_pkcs11h_sys_engine)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot initialize system engine %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_initialize ()) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setLogHook (_pkcs11_openvpn_log, NULL)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ pkcs11h_setLogLevel (_pkcs11_msg_openvpn2pkcs11 (get_debug_level ()));
+
+ if ((rv = pkcs11h_setForkMode (TRUE)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set fork mode %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setTokenPromptHook (_pkcs11_openvpn_token_prompt, NULL)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_pin_prompt, NULL)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setProtectedAuthentication (protected_auth)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set protected authentication mode %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setPINCachePeriod (nPINCachePeriod)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set Pcache period %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ rv = CKR_OK;
+
+cleanup:
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_initialize - return %ld-'%s'",
+ rv,
+ pkcs11h_getMessage (rv)
+ );
+
+ return rv == CKR_OK;
+}
+
+void
+pkcs11_terminate () {
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_terminate - entered"
+ );
+
+ pkcs11h_terminate ();
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_terminate - return"
+ );
+}
+
+void
+pkcs11_forkFixup () {
+ pkcs11h_forkFixup ();
+}
+
+bool
+pkcs11_addProvider (
+ const char * const provider,
+ const bool protected_auth,
+ const unsigned private_mode,
+ const bool cert_private
+) {
+ CK_RV rv = CKR_OK;
+
+ ASSERT (provider!=NULL);
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_addProvider - entered - provider='%s', private_mode=%08x",
+ provider,
+ private_mode
+ );
+
+ msg (
+ M_INFO,
+ "PKCS#11: Adding PKCS#11 provider '%s'",
+ provider
+ );
+
+ if (
+ (rv = pkcs11h_addProvider (
+ provider,
+ provider,
+ protected_auth,
+ private_mode,
+ PKCS11H_SLOTEVENT_METHOD_AUTO,
+ 0,
+ cert_private
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot initialize provider '%s' %ld-'%s'", provider, rv, pkcs11h_getMessage (rv));
+ }
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_addProvider - return rv=%ld-'%s'",
+ rv,
+ pkcs11h_getMessage (rv)
+ );
+
+ return rv == CKR_OK;
+}
+
+int
+pkcs11_logout() {
+ return pkcs11h_logout () == CKR_OK;
+}
+
+int
+pkcs11_management_id_count () {
+ pkcs11h_certificate_id_list_t id_list = NULL;
+ pkcs11h_certificate_id_list_t t = NULL;
+ CK_RV rv = CKR_OK;
+ int count = 0;
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_management_id_count - entered"
+ );
+
+ if (
+ (rv = pkcs11h_certificate_enumCertificateIds (
+ PKCS11H_ENUM_METHOD_CACHE_EXIST,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ NULL,
+ &id_list
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate list %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ for (count = 0, t = id_list; t != NULL; t = t->next) {
+ count++;
+ }
+
+cleanup:
+
+ if (id_list != NULL) {
+ pkcs11h_certificate_freeCertificateIdList (id_list);
+ id_list = NULL;
+ }
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_management_id_count - return count=%d",
+ count
+ );
+
+ return count;
+}
+
+bool
+pkcs11_management_id_get (
+ const int index,
+ char ** id,
+ char **base64
+) {
+ pkcs11h_certificate_id_list_t id_list = NULL;
+ pkcs11h_certificate_id_list_t entry = NULL;
+#if 0 /* certificate_id seems to be unused -- JY */
+ pkcs11h_certificate_id_t certificate_id = NULL;
+#endif
+ pkcs11h_certificate_t certificate = NULL;
+ CK_RV rv = CKR_OK;
+ unsigned char *certificate_blob = NULL;
+ size_t certificate_blob_size = 0;
+ size_t max;
+ char *internal_id = NULL;
+ char *internal_base64 = NULL;
+ int count = 0;
+ bool success = false;
+
+ ASSERT (id!=NULL);
+ ASSERT (base64!=NULL);
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_management_id_get - entered index=%d",
+ index
+ );
+
+ *id = NULL;
+ *base64 = NULL;
+
+ if (
+ (rv = pkcs11h_certificate_enumCertificateIds (
+ PKCS11H_ENUM_METHOD_CACHE_EXIST,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ NULL,
+ &id_list
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate list %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ entry = id_list;
+ count = 0;
+ while (entry != NULL && count != index) {
+ count++;
+ entry = entry->next;
+ }
+
+ if (entry == NULL) {
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_management_id_get - no certificate at index=%d",
+ index
+ );
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_serializeCertificateId (
+ NULL,
+ &max,
+ entry->certificate_id
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot serialize certificate id %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((internal_id = (char *)malloc (max)) == NULL) {
+ msg (M_FATAL, "PKCS#11: Cannot allocate memory");
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_serializeCertificateId (
+ internal_id,
+ &max,
+ entry->certificate_id
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot serialize certificate id %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_create (
+ entry->certificate_id,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ PKCS11H_PIN_CACHE_INFINITE,
+ &certificate
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_getCertificateBlob (
+ certificate,
+ NULL,
+ &certificate_blob_size
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate blob %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((certificate_blob = (unsigned char *)malloc (certificate_blob_size)) == NULL) {
+ msg (M_FATAL, "PKCS#11: Cannot allocate memory");
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_getCertificateBlob (
+ certificate,
+ certificate_blob,
+ &certificate_blob_size
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate blob %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if (base64_encode (certificate_blob, certificate_blob_size, &internal_base64) == -1) {
+ msg (M_WARN, "PKCS#11: Cannot encode certificate");
+ goto cleanup;
+ }
+
+ *id = internal_id;
+ internal_id = NULL;
+ *base64 = internal_base64;
+ internal_base64 = NULL;
+ success = true;
+
+cleanup:
+
+ if (id_list != NULL) {
+ pkcs11h_certificate_freeCertificateIdList (id_list);
+ id_list = NULL;
+ }
+
+ if (internal_id != NULL) {
+ free (internal_id);
+ internal_id = NULL;
+ }
+
+ if (internal_base64 != NULL) {
+ free (internal_base64);
+ internal_base64 = NULL;
+ }
+
+ if (certificate_blob != NULL) {
+ free (certificate_blob);
+ certificate_blob = NULL;
+ }
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: pkcs11_management_id_get - return success=%d, id='%s'",
+ success ? 1 : 0,
+ *id
+ );
+
+ return success;
+}
+
+int
+SSL_CTX_use_pkcs11 (
+ SSL_CTX * const ssl_ctx,
+ bool pkcs11_id_management,
+ const char * const pkcs11_id
+) {
+ X509 *x509 = NULL;
+ RSA *rsa = NULL;
+ pkcs11h_certificate_id_t certificate_id = NULL;
+ pkcs11h_certificate_t certificate = NULL;
+ pkcs11h_openssl_session_t openssl_session = NULL;
+ CK_RV rv = CKR_OK;
+
+ bool ok = false;
+
+ ASSERT (ssl_ctx!=NULL);
+ ASSERT (pkcs11_id_management || pkcs11_id!=NULL);
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: SSL_CTX_use_pkcs11 - entered - ssl_ctx=%p, pkcs11_id_management=%d, pkcs11_id='%s'",
+ (void *)ssl_ctx,
+ pkcs11_id_management ? 1 : 0,
+ pkcs11_id
+ );
+
+ if (pkcs11_id_management) {
+ struct user_pass id_resp;
+
+ CLEAR (id_resp);
+
+ id_resp.defined = false;
+ id_resp.nocache = true;
+ openvpn_snprintf (
+ id_resp.username,
+ sizeof (id_resp.username),
+ "Please specify PKCS#11 id to use"
+ );
+
+ if (
+ !get_user_pass (
+ &id_resp,
+ NULL,
+ "pkcs11-id-request",
+ GET_USER_PASS_MANAGEMENT|GET_USER_PASS_NEED_STR|GET_USER_PASS_NOFATAL
+ )
+ ) {
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_deserializeCertificateId (
+ &certificate_id,
+ id_resp.password
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot deserialize id %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+ }
+ else {
+ if (
+ (rv = pkcs11h_certificate_deserializeCertificateId (
+ &certificate_id,
+ pkcs11_id
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot deserialize id %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+ }
+
+ if (
+ (rv = pkcs11h_certificate_create (
+ certificate_id,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ PKCS11H_PIN_CACHE_INFINITE,
+ &certificate
+ )) != CKR_OK
+ ) {
+ msg (M_WARN, "PKCS#11: Cannot get certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((openssl_session = pkcs11h_openssl_createSession (certificate)) == NULL ) {
+ msg (M_WARN, "PKCS#11: Cannot initialize openssl session");
+ goto cleanup;
+ }
+
+ /*
+ * Will be released by openssl_session
+ */
+ certificate = NULL;
+
+ if ((rsa = pkcs11h_openssl_session_getRSA (openssl_session)) == NULL) {
+ msg (M_WARN, "PKCS#11: Unable get rsa object");
+ goto cleanup;
+ }
+
+ if ((x509 = pkcs11h_openssl_session_getX509 (openssl_session)) == NULL) {
+ msg (M_WARN, "PKCS#11: Unable get certificate object");
+ goto cleanup;
+ }
+
+ if (!SSL_CTX_use_RSAPrivateKey (ssl_ctx, rsa)) {
+ msg (M_WARN, "PKCS#11: Cannot set private key for openssl");
+ goto cleanup;
+ }
+
+ if (!SSL_CTX_use_certificate (ssl_ctx, x509)) {
+ msg (M_WARN, "PKCS#11: Cannot set certificate for openssl");
+ goto cleanup;
+ }
+
+ ok = true;
+
+cleanup:
+ /*
+ * openssl objects have reference
+ * count, so release them
+ */
+
+ if (x509 != NULL) {
+ X509_free (x509);
+ x509 = NULL;
+ }
+
+ if (rsa != NULL) {
+ RSA_free (rsa);
+ rsa = NULL;
+ }
+
+ if (certificate != NULL) {
+ pkcs11h_certificate_freeCertificate (certificate);
+ certificate = NULL;
+ }
+
+ if (certificate_id != NULL) {
+ pkcs11h_certificate_freeCertificateId (certificate_id);
+ certificate_id = NULL;
+ }
+
+ if (openssl_session != NULL) {
+ pkcs11h_openssl_freeSession (openssl_session);
+ openssl_session = NULL;
+ }
+
+ dmsg (
+ D_PKCS11_DEBUG,
+ "PKCS#11: SSL_CTX_use_pkcs11 - return ok=%d, rv=%ld",
+ ok ? 1 : 0,
+ rv
+ );
+
+ return ok ? 1 : 0;
+}
+
+static
+bool
+_pkcs11_openvpn_show_pkcs11_ids_pin_prompt (
+ void * const global_data,
+ void * const user_data,
+ const pkcs11h_token_id_t token,
+ const unsigned retry,
+ char * const pin,
+ const size_t pin_max
+) {
+ struct gc_arena gc = gc_new ();
+ struct buffer pass_prompt = alloc_buf_gc (128, &gc);
+
+ (void)global_data;
+ (void)user_data;
+ (void)retry;
+
+ ASSERT (token!=NULL);
+
+ buf_printf (&pass_prompt, "Please enter '%s' token PIN or 'cancel': ", token->display);
+
+ if (!get_console_input (BSTR (&pass_prompt), false, pin, pin_max)) {
+ msg (M_FATAL, "Cannot read password from stdin");
+ }
+
+ gc_free (&gc);
+
+ if (!strcmp (pin, "cancel")) {
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+void
+show_pkcs11_ids (
+ const char * const provider,
+ bool cert_private
+) {
+ pkcs11h_certificate_id_list_t user_certificates = NULL;
+ pkcs11h_certificate_id_list_t current = NULL;
+ CK_RV rv = CKR_FUNCTION_FAILED;
+
+ if ((rv = pkcs11h_initialize ()) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot initialize %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setLogHook (_pkcs11_openvpn_log, NULL)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set hooks %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ pkcs11h_setLogLevel (_pkcs11_msg_openvpn2pkcs11 (get_debug_level ()));
+
+ if ((rv = pkcs11h_setProtectedAuthentication (TRUE)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set protected authentication %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if ((rv = pkcs11h_setPINPromptHook (_pkcs11_openvpn_show_pkcs11_ids_pin_prompt, NULL)) != CKR_OK) {
+ msg (M_FATAL, "PKCS#11: Cannot set PIN hook %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_addProvider (
+ provider,
+ provider,
+ TRUE,
+ 0,
+ FALSE,
+ 0,
+ cert_private ? TRUE : FALSE
+ )) != CKR_OK
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot add provider '%s' %ld-'%s'", provider, rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_enumCertificateIds (
+ PKCS11H_ENUM_METHOD_CACHE_EXIST,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ NULL,
+ &user_certificates
+ )) != CKR_OK
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot enumerate certificates %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup;
+ }
+
+ msg (
+ M_INFO|M_NOPREFIX|M_NOLF,
+ (
+ "\n"
+ "The following objects are available for use.\n"
+ "Each object shown below may be used as parameter to\n"
+ "--pkcs11-id option please remember to use single quote mark.\n"
+ )
+ );
+ for (current = user_certificates;current != NULL; current = current->next) {
+ pkcs11h_certificate_t certificate = NULL;
+ X509 *x509 = NULL;
+ BIO *bio = NULL;
+ char dn[1024] = {0};
+ char serial[1024] = {0};
+ char *ser = NULL;
+ size_t ser_len = 0;
+ int n;
+
+ if (
+ (rv = pkcs11h_certificate_serializeCertificateId (
+ NULL,
+ &ser_len,
+ current->certificate_id
+ )) != CKR_OK
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot serialize certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup1;
+ }
+
+ if (
+ rv == CKR_OK &&
+ (ser = (char *)malloc (ser_len)) == NULL
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot allocate memory");
+ goto cleanup1;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_serializeCertificateId (
+ ser,
+ &ser_len,
+ current->certificate_id
+ )) != CKR_OK
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot serialize certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup1;
+ }
+
+ if (
+ (rv = pkcs11h_certificate_create (
+ current->certificate_id,
+ NULL,
+ PKCS11H_PROMPT_MASK_ALLOW_ALL,
+ PKCS11H_PIN_CACHE_INFINITE,
+ &certificate
+ ))
+ ) {
+ msg (M_FATAL, "PKCS#11: Cannot create certificate %ld-'%s'", rv, pkcs11h_getMessage (rv));
+ goto cleanup1;
+ }
+
+ if ((x509 = pkcs11h_openssl_getX509 (certificate)) == NULL) {
+ msg (M_FATAL, "PKCS#11: Cannot get X509");
+ goto cleanup1;
+ }
+
+ X509_NAME_oneline (
+ X509_get_subject_name (x509),
+ dn,
+ sizeof (dn)
+ );
+
+ if ((bio = BIO_new (BIO_s_mem ())) == NULL) {
+ msg (M_FATAL, "PKCS#11: Cannot create BIO");
+ goto cleanup1;
+ }
+
+ i2a_ASN1_INTEGER(bio, X509_get_serialNumber (x509));
+ n = BIO_read (bio, serial, sizeof (serial)-1);
+ if (n<0) {
+ serial[0] = '\x0';
+ }
+ else {
+ serial[n] = 0;
+ }
+
+ msg (
+ M_INFO|M_NOPREFIX|M_NOLF,
+ (
+ "\n"
+ "Certificate\n"
+ " DN: %s\n"
+ " Serial: %s\n"
+ " Serialized id: %s\n"
+ ),
+ dn,
+ serial,
+ ser
+ );
+
+ cleanup1:
+ if (x509 != NULL) {
+ X509_free (x509);
+ x509 = NULL;
+ }
+
+ if (certificate != NULL) {
+ pkcs11h_certificate_freeCertificate (certificate);
+ certificate = NULL;
+ }
+
+ if (ser != NULL) {
+ free (ser);
+ ser = NULL;
+ }
+ }
+
+cleanup:
+ if (user_certificates != NULL) {
+ pkcs11h_certificate_freeCertificateIdList (user_certificates);
+ user_certificates = NULL;
+ }
+
+ pkcs11h_terminate ();
+}
+
+#else
+#ifdef _MSC_VER /* Dummy function needed to avoid empty file compiler warning in Microsoft VC */
+static void dummy (void) {}
+#endif
+#endif /* ENABLE_PKCS11 */
diff --git a/pkcs11.h b/pkcs11.h
new file mode 100644
index 0000000..abe03b9
--- /dev/null
+++ b/pkcs11.h
@@ -0,0 +1,80 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OPENVPN_PKCS11_H
+#define OPENVPN_PKCS11_H
+
+#if defined(ENABLE_PKCS11)
+
+#include <openssl/ssl.h>
+
+bool
+pkcs11_initialize (
+ const bool fProtectedAuthentication,
+ const int nPINCachePeriod
+);
+
+void
+pkcs11_terminate ();
+
+void
+pkcs11_forkFixup ();
+
+bool
+pkcs11_addProvider (
+ const char * const provider,
+ const bool fProtectedAuthentication,
+ const unsigned private_mode,
+ const bool fCertIsPrivate
+);
+
+int
+pkcs11_logout();
+
+int
+pkcs11_management_id_count ();
+
+bool
+pkcs11_management_id_get (
+ const int index,
+ char ** id,
+ char **base64
+);
+
+int
+SSL_CTX_use_pkcs11 (
+ SSL_CTX * const ssl_ctx,
+ bool pkcs11_id_management,
+ const char * const pkcs11_id
+);
+
+void
+show_pkcs11_ids (
+ const char * const provider,
+ bool cert_private
+);
+
+#endif /* ENABLE_PKCS11 */
+
+#endif /* OPENVPN_PKCS11H_H */
diff --git a/plugin.c b/plugin.c
new file mode 100644
index 0000000..0d66611
--- /dev/null
+++ b/plugin.c
@@ -0,0 +1,747 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_PLUGIN
+
+#include "buffer.h"
+#include "error.h"
+#include "misc.h"
+#include "plugin.h"
+
+#include "memdbg.h"
+
+#define PLUGIN_SYMBOL_REQUIRED (1<<0)
+
+/* used only for program aborts */
+static struct plugin_common *static_plugin_common = NULL; /* GLOBAL */
+
+static void
+plugin_show_string_array (int msglevel, const char *name, const char *array[])
+{
+ int i;
+ for (i = 0; array[i]; ++i)
+ {
+ if (env_safe_to_print (array[i]))
+ msg (msglevel, "%s[%d] = '%s'", name, i, array[i]);
+ }
+}
+
+static void
+plugin_show_args_env (int msglevel, const char *argv[], const char *envp[])
+{
+ if (check_debug_level (msglevel))
+ {
+ plugin_show_string_array (msglevel, "ARGV", argv);
+ plugin_show_string_array (msglevel, "ENVP", envp);
+ }
+}
+
+static const char *
+plugin_type_name (const int type)
+{
+ switch (type)
+ {
+ case OPENVPN_PLUGIN_UP:
+ return "PLUGIN_UP";
+ case OPENVPN_PLUGIN_DOWN:
+ return "PLUGIN_DOWN";
+ case OPENVPN_PLUGIN_ROUTE_UP:
+ return "PLUGIN_ROUTE_UP";
+ case OPENVPN_PLUGIN_IPCHANGE:
+ return "PLUGIN_IPCHANGE";
+ case OPENVPN_PLUGIN_TLS_VERIFY:
+ return "PLUGIN_TLS_VERIFY";
+ case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
+ return "PLUGIN_AUTH_USER_PASS_VERIFY";
+ case OPENVPN_PLUGIN_CLIENT_CONNECT:
+ return "PLUGIN_CLIENT_CONNECT";
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+ return "PLUGIN_CLIENT_CONNECT";
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+ return "PLUGIN_CLIENT_DISCONNECT";
+ case OPENVPN_PLUGIN_LEARN_ADDRESS:
+ return "PLUGIN_LEARN_ADDRESS";
+ case OPENVPN_PLUGIN_TLS_FINAL:
+ return "PLUGIN_TLS_FINAL";
+ case OPENVPN_PLUGIN_ENABLE_PF:
+ return "OPENVPN_PLUGIN_ENABLE_PF";
+ default:
+ return "PLUGIN_???";
+ }
+}
+
+static const char *
+plugin_mask_string (const unsigned int type_mask, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ bool first = true;
+ int i;
+
+ for (i = 0; i < OPENVPN_PLUGIN_N; ++i)
+ {
+ if (OPENVPN_PLUGIN_MASK (i) & type_mask)
+ {
+ if (!first)
+ buf_printf (&out, "|");
+ buf_printf (&out, "%s", plugin_type_name (i));
+ first = false;
+ }
+ }
+ return BSTR (&out);
+}
+
+static inline unsigned int
+plugin_supported_types (void)
+{
+ return ((1<<OPENVPN_PLUGIN_N)-1);
+}
+
+struct plugin_option_list *
+plugin_option_list_new (struct gc_arena *gc)
+{
+ struct plugin_option_list *ret;
+ ALLOC_OBJ_CLEAR_GC (ret, struct plugin_option_list, gc);
+ return ret;
+}
+
+bool
+plugin_option_list_add (struct plugin_option_list *list, char **p, struct gc_arena *gc)
+{
+ if (list->n < MAX_PLUGINS)
+ {
+ struct plugin_option *o = &list->plugins[list->n++];
+ o->argv = make_extended_arg_array (p, gc);
+ if (o->argv[0])
+ o->so_pathname = o->argv[0];
+ return true;
+ }
+ else
+ return false;
+}
+
+#ifdef ENABLE_DEBUG
+void
+plugin_option_list_print (const struct plugin_option_list *list, int msglevel)
+{
+ int i;
+ struct gc_arena gc = gc_new ();
+
+ for (i = 0; i < list->n; ++i)
+ {
+ const struct plugin_option *o = &list->plugins[i];
+ msg (msglevel, " plugin[%d] %s '%s'", i, o->so_pathname, print_argv (o->argv, &gc, PA_BRACKET));
+ }
+
+ gc_free (&gc);
+}
+#endif
+
+#if defined(USE_LIBDL)
+
+static void
+libdl_resolve_symbol (void *handle, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
+{
+ *dest = dlsym (handle, symbol);
+ if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
+ msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin shared object %s: %s", symbol, plugin_name, dlerror());
+}
+
+#elif defined(USE_LOAD_LIBRARY)
+
+static void
+dll_resolve_symbol (HMODULE module, void **dest, const char *symbol, const char *plugin_name, const unsigned int flags)
+{
+ *dest = GetProcAddress (module, symbol);
+ if ((flags & PLUGIN_SYMBOL_REQUIRED) && !*dest)
+ msg (M_FATAL, "PLUGIN: could not find required symbol '%s' in plugin DLL %s", symbol, plugin_name);
+}
+
+#endif
+
+static void
+plugin_init_item (struct plugin *p, const struct plugin_option *o)
+{
+ struct gc_arena gc = gc_new ();
+ bool rel = false;
+
+ p->so_pathname = o->so_pathname;
+ p->plugin_type_mask = plugin_supported_types ();
+
+#if defined(USE_LIBDL)
+
+ p->handle = NULL;
+#if defined(PLUGIN_LIBDIR)
+ if (!absolute_pathname (p->so_pathname))
+ {
+ char full[PATH_MAX];
+
+ openvpn_snprintf (full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname);
+ p->handle = dlopen (full, RTLD_NOW);
+#if defined(ENABLE_PLUGIN_SEARCH)
+ if (!p->handle)
+ {
+ rel = true;
+ p->handle = dlopen (p->so_pathname, RTLD_NOW);
+ }
+#endif
+ }
+ else
+#endif
+ {
+ rel = !absolute_pathname (p->so_pathname);
+ p->handle = dlopen (p->so_pathname, RTLD_NOW);
+ }
+ if (!p->handle)
+ msg (M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror());
+
+# define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags)
+
+#elif defined(USE_LOAD_LIBRARY)
+
+ rel = !absolute_pathname (p->so_pathname);
+ p->module = LoadLibrary (p->so_pathname);
+ if (!p->module)
+ msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname);
+
+# define PLUGIN_SYM(var, name, flags) dll_resolve_symbol (p->module, (void*)&p->var, name, p->so_pathname, flags)
+
+#endif
+
+ PLUGIN_SYM (open1, "openvpn_plugin_open_v1", 0);
+ PLUGIN_SYM (open2, "openvpn_plugin_open_v2", 0);
+ PLUGIN_SYM (func1, "openvpn_plugin_func_v1", 0);
+ PLUGIN_SYM (func2, "openvpn_plugin_func_v2", 0);
+ PLUGIN_SYM (close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED);
+ PLUGIN_SYM (abort, "openvpn_plugin_abort_v1", 0);
+ PLUGIN_SYM (client_constructor, "openvpn_plugin_client_constructor_v1", 0);
+ PLUGIN_SYM (client_destructor, "openvpn_plugin_client_destructor_v1", 0);
+ PLUGIN_SYM (min_version_required, "openvpn_plugin_min_version_required_v1", 0);
+ PLUGIN_SYM (initialization_point, "openvpn_plugin_select_initialization_point_v1", 0);
+
+ if (!p->open1 && !p->open2)
+ msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname);
+
+ if (!p->func1 && !p->func2)
+ msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname);
+
+ /*
+ * Verify that we are sufficiently up-to-date to handle the plugin
+ */
+ if (p->min_version_required)
+ {
+ const int plugin_needs_version = (*p->min_version_required)();
+ if (plugin_needs_version > OPENVPN_PLUGIN_VERSION)
+ msg (M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s",
+ plugin_needs_version,
+ OPENVPN_PLUGIN_VERSION,
+ p->so_pathname);
+ }
+
+ if (p->initialization_point)
+ p->requested_initialization_point = (*p->initialization_point)();
+ else
+ p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON;
+
+ if (rel)
+ msg (M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname);
+
+ p->initialized = true;
+
+ gc_free (&gc);
+}
+
+static void
+plugin_open_item (struct plugin *p,
+ const struct plugin_option *o,
+ struct openvpn_plugin_string_list **retlist,
+ const char **envp,
+ const int init_point)
+{
+ ASSERT (p->initialized);
+
+ /* clear return list */
+ if (retlist)
+ *retlist = NULL;
+
+ if (!p->plugin_handle && init_point == p->requested_initialization_point)
+ {
+ struct gc_arena gc = gc_new ();
+
+ dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE");
+ plugin_show_args_env (D_PLUGIN_DEBUG, o->argv, envp);
+
+ /*
+ * Call the plugin initialization
+ */
+ if (p->open2)
+ p->plugin_handle = (*p->open2)(&p->plugin_type_mask, o->argv, envp, retlist);
+ else if (p->open1)
+ p->plugin_handle = (*p->open1)(&p->plugin_type_mask, o->argv, envp);
+ else
+ ASSERT (0);
+
+ msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s",
+ p->so_pathname,
+ print_argv (o->argv, &gc, PA_BRACKET),
+ plugin_mask_string (p->plugin_type_mask, &gc),
+ (retlist && *retlist) ? "[RETLIST]" : "");
+
+ if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types())
+ msg (M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]",
+ p->so_pathname,
+ p->plugin_type_mask,
+ plugin_supported_types());
+
+ if (p->plugin_handle == NULL)
+ msg (M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s",
+ p->so_pathname);
+
+ gc_free (&gc);
+ }
+}
+
+static int
+plugin_call_item (const struct plugin *p,
+ void *per_client_context,
+ const int type,
+ const struct argv *av,
+ struct openvpn_plugin_string_list **retlist,
+ const char **envp)
+{
+ int status = OPENVPN_PLUGIN_FUNC_SUCCESS;
+
+ /* clear return list */
+ if (retlist)
+ *retlist = NULL;
+
+ if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type)))
+ {
+ struct gc_arena gc = gc_new ();
+ struct argv a = argv_insert_head (av, p->so_pathname);
+
+ dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type));
+ plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp);
+
+ /*
+ * Call the plugin work function
+ */
+ if (p->func2)
+ status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist);
+ else if (p->func1)
+ status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp);
+ else
+ ASSERT (0);
+
+ msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d",
+ p->so_pathname,
+ plugin_type_name (type),
+ status);
+
+ if (status == OPENVPN_PLUGIN_FUNC_ERROR)
+ msg (M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s",
+ plugin_type_name (type),
+ status,
+ p->so_pathname);
+
+ argv_reset (&a);
+ gc_free (&gc);
+ }
+ return status;
+}
+
+static void
+plugin_close_item (struct plugin *p)
+{
+ if (p->initialized)
+ {
+ msg (D_PLUGIN, "PLUGIN_CLOSE: %s", p->so_pathname);
+
+ /*
+ * Call the plugin close function
+ */
+ if (p->plugin_handle)
+ (*p->close)(p->plugin_handle);
+
+#if defined(USE_LIBDL)
+ if (dlclose (p->handle))
+ msg (M_WARN, "PLUGIN_CLOSE: dlclose() failed on plugin: %s", p->so_pathname);
+#elif defined(USE_LOAD_LIBRARY)
+ if (!FreeLibrary (p->module))
+ msg (M_WARN, "PLUGIN_CLOSE: FreeLibrary() failed on plugin: %s", p->so_pathname);
+#endif
+
+ p->initialized = false;
+ }
+}
+
+static void
+plugin_abort_item (const struct plugin *p)
+{
+ /*
+ * Call the plugin abort function
+ */
+ if (p->abort)
+ (*p->abort)(p->plugin_handle);
+}
+
+static void
+plugin_per_client_init (const struct plugin_common *pc,
+ struct plugin_per_client *cli,
+ const int init_point)
+{
+ const int n = pc->n;
+ int i;
+
+ for (i = 0; i < n; ++i)
+ {
+ const struct plugin *p = &pc->plugins[i];
+ if (p->plugin_handle
+ && (init_point < 0 || init_point == p->requested_initialization_point)
+ && p->client_constructor)
+ cli->per_client_context[i] = (*p->client_constructor)(p->plugin_handle);
+ }
+}
+
+static void
+plugin_per_client_destroy (const struct plugin_common *pc, struct plugin_per_client *cli)
+{
+ const int n = pc->n;
+ int i;
+
+ for (i = 0; i < n; ++i)
+ {
+ const struct plugin *p = &pc->plugins[i];
+ void *cc = cli->per_client_context[i];
+
+ if (p->client_destructor && cc)
+ (*p->client_destructor)(p->plugin_handle, cc);
+ }
+ CLEAR (*cli);
+}
+
+struct plugin_list *
+plugin_list_inherit (const struct plugin_list *src)
+{
+ struct plugin_list *pl;
+ ALLOC_OBJ_CLEAR (pl, struct plugin_list);
+ pl->common = src->common;
+ ASSERT (pl->common);
+ plugin_per_client_init (pl->common, &pl->per_client, -1);
+ return pl;
+}
+
+static struct plugin_common *
+plugin_common_init (const struct plugin_option_list *list)
+{
+ int i;
+ struct plugin_common *pc;
+
+ ALLOC_OBJ_CLEAR (pc, struct plugin_common);
+
+ for (i = 0; i < list->n; ++i)
+ {
+ plugin_init_item (&pc->plugins[i],
+ &list->plugins[i]);
+ pc->n = i + 1;
+ }
+
+ static_plugin_common = pc;
+ return pc;
+}
+
+static void
+plugin_common_open (struct plugin_common *pc,
+ const struct plugin_option_list *list,
+ struct plugin_return *pr,
+ const struct env_set *es,
+ const int init_point)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ const char **envp;
+
+ envp = make_env_array (es, false, &gc);
+
+ if (pr)
+ plugin_return_init (pr);
+
+ for (i = 0; i < pc->n; ++i)
+ {
+ plugin_open_item (&pc->plugins[i],
+ &list->plugins[i],
+ pr ? &pr->list[i] : NULL,
+ envp,
+ init_point);
+ }
+
+ if (pr)
+ pr->n = i;
+
+ gc_free (&gc);
+}
+
+static void
+plugin_common_close (struct plugin_common *pc)
+{
+ static_plugin_common = NULL;
+ if (pc)
+ {
+ int i;
+
+ for (i = 0; i < pc->n; ++i)
+ plugin_close_item (&pc->plugins[i]);
+ free (pc);
+ }
+}
+
+struct plugin_list *
+plugin_list_init (const struct plugin_option_list *list)
+{
+ struct plugin_list *pl;
+ ALLOC_OBJ_CLEAR (pl, struct plugin_list);
+ pl->common = plugin_common_init (list);
+ pl->common_owned = true;
+ return pl;
+}
+
+void
+plugin_list_open (struct plugin_list *pl,
+ const struct plugin_option_list *list,
+ struct plugin_return *pr,
+ const struct env_set *es,
+ const int init_point)
+{
+ plugin_common_open (pl->common, list, pr, es, init_point);
+ plugin_per_client_init (pl->common, &pl->per_client, init_point);
+}
+
+int
+plugin_call (const struct plugin_list *pl,
+ const int type,
+ const struct argv *av,
+ struct plugin_return *pr,
+ struct env_set *es)
+{
+ if (pr)
+ plugin_return_init (pr);
+
+ if (plugin_defined (pl, type))
+ {
+ struct gc_arena gc = gc_new ();
+ int i;
+ const char **envp;
+ const int n = plugin_n (pl);
+ bool success = false;
+ bool error = false;
+ bool deferred = false;
+
+ setenv_del (es, "script_type");
+ envp = make_env_array (es, false, &gc);
+
+ for (i = 0; i < n; ++i)
+ {
+ const int status = plugin_call_item (&pl->common->plugins[i],
+ pl->per_client.per_client_context[i],
+ type,
+ av,
+ pr ? &pr->list[i] : NULL,
+ envp);
+ switch (status)
+ {
+ case OPENVPN_PLUGIN_FUNC_SUCCESS:
+ success = true;
+ break;
+ case OPENVPN_PLUGIN_FUNC_DEFERRED:
+ deferred = true;
+ break;
+ default:
+ error = true;
+ break;
+ }
+ }
+
+ if (pr)
+ pr->n = i;
+
+ gc_free (&gc);
+
+ if (type == OPENVPN_PLUGIN_ENABLE_PF && success)
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ else if (error)
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ else if (deferred)
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+void
+plugin_list_close (struct plugin_list *pl)
+{
+ if (pl)
+ {
+ if (pl->common)
+ {
+ plugin_per_client_destroy (pl->common, &pl->per_client);
+
+ if (pl->common_owned)
+ plugin_common_close (pl->common);
+ }
+
+ free (pl);
+ }
+}
+
+void
+plugin_abort (void)
+{
+ struct plugin_common *pc = static_plugin_common;
+ static_plugin_common = NULL;
+ if (pc)
+ {
+ int i;
+
+ for (i = 0; i < pc->n; ++i)
+ plugin_abort_item (&pc->plugins[i]);
+ }
+}
+
+bool
+plugin_defined (const struct plugin_list *pl, const int type)
+{
+ bool ret = false;
+
+ if (pl)
+ {
+ const struct plugin_common *pc = pl->common;
+
+ if (pc)
+ {
+ int i;
+ const unsigned int mask = OPENVPN_PLUGIN_MASK (type);
+ for (i = 0; i < pc->n; ++i)
+ {
+ if (pc->plugins[i].plugin_type_mask & mask)
+ {
+ ret = true;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Plugin return functions
+ */
+
+static void
+openvpn_plugin_string_list_item_free (struct openvpn_plugin_string_list *l)
+{
+ if (l)
+ {
+ free (l->name);
+ string_clear (l->value);
+ free (l->value);
+ free (l);
+ }
+}
+
+static void
+openvpn_plugin_string_list_free (struct openvpn_plugin_string_list *l)
+{
+ struct openvpn_plugin_string_list *next;
+ while (l)
+ {
+ next = l->next;
+ openvpn_plugin_string_list_item_free (l);
+ l = next;
+ }
+}
+
+static struct openvpn_plugin_string_list *
+openvpn_plugin_string_list_find (struct openvpn_plugin_string_list *l, const char *name)
+{
+ while (l)
+ {
+ if (!strcmp (l->name, name))
+ return l;
+ l = l->next;
+ }
+ return NULL;
+}
+
+void
+plugin_return_get_column (const struct plugin_return *src,
+ struct plugin_return *dest,
+ const char *colname)
+{
+ int i;
+
+ dest->n = 0;
+ for (i = 0; i < src->n; ++i)
+ dest->list[i] = openvpn_plugin_string_list_find (src->list[i], colname);
+ dest->n = i;
+}
+
+void
+plugin_return_free (struct plugin_return *pr)
+{
+ int i;
+ for (i = 0; i < pr->n; ++i)
+ openvpn_plugin_string_list_free (pr->list[i]);
+ pr->n = 0;
+}
+
+#ifdef ENABLE_DEBUG
+void
+plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr)
+{
+ int i;
+ msg (msglevel, "PLUGIN_RETURN_PRINT %s", prefix);
+ for (i = 0; i < pr->n; ++i)
+ {
+ struct openvpn_plugin_string_list *l = pr->list[i];
+ int count = 0;
+
+ msg (msglevel, "PLUGIN #%d (%s)", i, prefix);
+ while (l)
+ {
+ msg (msglevel, "[%d] '%s' -> '%s'\n",
+ ++count,
+ l->name,
+ l->value);
+ l = l->next;
+ }
+ }
+}
+#endif
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_PLUGIN */
diff --git a/plugin.h b/plugin.h
new file mode 100644
index 0000000..969d451
--- /dev/null
+++ b/plugin.h
@@ -0,0 +1,180 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * plug-in support, using dynamically loaded libraries
+ */
+
+#ifndef OPENVPN_PLUGIN_H
+#define OPENVPN_PLUGIN_H
+
+#include "openvpn-plugin.h"
+
+#ifdef ENABLE_PLUGIN
+
+#include "misc.h"
+
+#define MAX_PLUGINS 16
+
+struct plugin_option {
+ const char *so_pathname;
+ const char **argv;
+};
+
+struct plugin_option_list {
+ int n;
+ struct plugin_option plugins[MAX_PLUGINS];
+};
+
+struct plugin {
+ bool initialized;
+ const char *so_pathname;
+ unsigned int plugin_type_mask;
+ int requested_initialization_point;
+
+#if defined(USE_LIBDL)
+ void *handle;
+#elif defined(USE_LOAD_LIBRARY)
+ HMODULE module;
+#endif
+
+ openvpn_plugin_open_v1 open1;
+ openvpn_plugin_open_v2 open2;
+ openvpn_plugin_func_v1 func1;
+ openvpn_plugin_func_v2 func2;
+ openvpn_plugin_close_v1 close;
+ openvpn_plugin_abort_v1 abort;
+ openvpn_plugin_client_constructor_v1 client_constructor;
+ openvpn_plugin_client_destructor_v1 client_destructor;
+ openvpn_plugin_min_version_required_v1 min_version_required;
+ openvpn_plugin_select_initialization_point_v1 initialization_point;
+
+ openvpn_plugin_handle_t plugin_handle;
+};
+
+struct plugin_per_client
+{
+ void *per_client_context[MAX_PLUGINS];
+};
+
+struct plugin_common
+{
+ int n;
+ struct plugin plugins[MAX_PLUGINS];
+};
+
+struct plugin_list
+{
+ struct plugin_per_client per_client;
+ struct plugin_common *common;
+ bool common_owned;
+};
+
+struct plugin_return
+{
+ int n;
+ struct openvpn_plugin_string_list *list[MAX_PLUGINS];
+};
+
+struct plugin_option_list *plugin_option_list_new (struct gc_arena *gc);
+bool plugin_option_list_add (struct plugin_option_list *list, char **p, struct gc_arena *gc);
+
+#ifdef ENABLE_DEBUG
+void plugin_option_list_print (const struct plugin_option_list *list, int msglevel);
+#endif
+
+struct plugin_list *plugin_list_init (const struct plugin_option_list *list);
+
+void plugin_list_open (struct plugin_list *pl,
+ const struct plugin_option_list *list,
+ struct plugin_return *pr,
+ const struct env_set *es,
+ const int init_point);
+
+struct plugin_list *plugin_list_inherit (const struct plugin_list *src);
+
+int plugin_call (const struct plugin_list *pl,
+ const int type,
+ const struct argv *av,
+ struct plugin_return *pr,
+ struct env_set *es);
+
+void plugin_list_close (struct plugin_list *pl);
+bool plugin_defined (const struct plugin_list *pl, const int type);
+
+void plugin_return_get_column (const struct plugin_return *src,
+ struct plugin_return *dest,
+ const char *colname);
+
+void plugin_return_free (struct plugin_return *pr);
+
+#ifdef ENABLE_DEBUG
+void plugin_return_print (const int msglevel, const char *prefix, const struct plugin_return *pr);
+#endif
+
+static inline int
+plugin_n (const struct plugin_list *pl)
+{
+ if (pl && pl->common)
+ return pl->common->n;
+ else
+ return 0;
+}
+
+static inline bool
+plugin_return_defined (const struct plugin_return *pr)
+{
+ return pr->n >= 0;
+}
+
+static inline void
+plugin_return_init (struct plugin_return *pr)
+{
+ pr->n = 0;
+}
+
+#else
+
+struct plugin_list { int dummy; };
+struct plugin_return { int dummy; };
+
+static inline bool
+plugin_defined (const struct plugin_list *pl, const int type)
+{
+ return false;
+}
+
+static inline int
+plugin_call (const struct plugin_list *pl,
+ const int type,
+ const struct argv *av,
+ struct plugin_return *pr,
+ struct env_set *es)
+{
+ return 0;
+}
+
+#endif /* ENABLE_PLUGIN */
+
+#endif /* OPENVPN_PLUGIN_H */
diff --git a/plugin/README b/plugin/README
new file mode 100644
index 0000000..6e490c5
--- /dev/null
+++ b/plugin/README
@@ -0,0 +1,47 @@
+OpenVPN Plugins
+---------------
+
+Starting with OpenVPN 2.0-beta17, compiled plugin modules are
+supported on any *nix OS which includes libdl or on Windows.
+One or more modules may be loaded into OpenVPN using
+the --plugin directive, and each plugin module is capable of
+intercepting any of the script callbacks which OpenVPN supports:
+
+(1) up
+(2) down
+(3) route-up
+(4) ipchange
+(5) tls-verify
+(6) auth-user-pass-verify
+(7) client-connect
+(8) client-disconnect
+(9) learn-address
+
+See the openvpn-plugin.h file in the top-level directory of the
+OpenVPN source distribution for more detailed information
+on the plugin interface.
+
+Included Plugins
+----------------
+
+auth-pam -- Authenticate using PAM and a split privilege
+ execution model which functions even if
+ root privileges or the execution environment
+ have been altered with --user/--group/--chroot.
+ Tested on Linux only.
+
+down-root -- Enable the running of down scripts with root privileges
+ even if --user/--group/--chroot have been used
+ to drop root privileges or change the execution
+ environment. Not applicable on Windows.
+
+examples -- A simple example that demonstrates a portable
+ plugin, i.e. one which can be built for *nix
+ or Windows from the same source.
+
+Building Plugins
+----------------
+
+cd to the top-level directory of a plugin, and use the
+"make" command to build it. The examples plugin is
+built using a build script, not a makefile.
diff --git a/plugin/auth-pam/.svnignore b/plugin/auth-pam/.svnignore
new file mode 100644
index 0000000..140f8cf
--- /dev/null
+++ b/plugin/auth-pam/.svnignore
@@ -0,0 +1 @@
+*.so
diff --git a/plugin/auth-pam/Makefile b/plugin/auth-pam/Makefile
new file mode 100755
index 0000000..e69fe3f
--- /dev/null
+++ b/plugin/auth-pam/Makefile
@@ -0,0 +1,30 @@
+#
+# Build the OpenVPN auth-pam plugin module.
+#
+
+# If PAM modules are not linked against libpam.so, set DLOPEN_PAM to 1. This
+# must be done on SUSE 9.1, at least.
+DLOPEN_PAM=0
+
+ifeq ($(DLOPEN_PAM),1)
+ LIBPAM=-ldl
+else
+ LIBPAM=-lpam
+endif
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE=-I../..
+
+CC_FLAGS=-O2 -Wall -DDLOPEN_PAM=$(DLOPEN_PAM)
+
+openvpn-auth-pam.so : auth-pam.o pamdl.o
+ gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-auth-pam.so -o openvpn-auth-pam.so auth-pam.o pamdl.o -lc $(LIBPAM)
+
+auth-pam.o : auth-pam.c pamdl.h
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} auth-pam.c
+
+pamdl.o : pamdl.c pamdl.h
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} pamdl.c
+
+clean :
+ rm -f *.o *.so
diff --git a/plugin/auth-pam/README b/plugin/auth-pam/README
new file mode 100644
index 0000000..c957c02
--- /dev/null
+++ b/plugin/auth-pam/README
@@ -0,0 +1,74 @@
+openvpn-auth-pam
+
+SYNOPSIS
+
+The openvpn-auth-pam module implements username/password
+authentication via PAM, and essentially allows any authentication
+method supported by PAM (such as LDAP, RADIUS, or Linux Shadow
+passwords) to be used with OpenVPN. While PAM supports
+username/password authentication, this can be combined with X509
+certificates to provide two indepedent levels of authentication.
+
+This module uses a split privilege execution model which will
+function even if you drop openvpn daemon privileges using the user,
+group, or chroot directives.
+
+BUILD
+
+To build openvpn-auth-pam, you will need to have the pam-devel
+package installed.
+
+Build with the "make" command. The module will be named
+openvpn-auth-pam.so
+
+USAGE
+
+To use this plugin module, add to your OpenVPN config file:
+
+ plugin openvpn-auth-pam.so service-type
+
+The required service-type parameter corresponds to
+the PAM service definition file usually found
+in /etc/pam.d.
+
+This plugin also supports the usage of a list of name/value
+pairs to answer PAM module queries.
+
+For example:
+
+ plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD"
+
+tells auth-pam to (a) use the "login" PAM module, (b) answer a
+"login" query with the username given by the OpenVPN client, and
+(c) answer a "password" query with the password given by the
+OpenVPN client. This provides flexibility in dealing with the different
+types of query strings which different PAM modules might generate.
+For example, suppose you were using a PAM module called
+"test" which queried for "name" rather than "login":
+
+ plugin openvpn-auth-pam.so "test name USERNAME password PASSWORD"
+
+While "USERNAME" and "PASSWORD" are special strings which substitute
+to client-supplied values, it is also possible to name literal values
+to use as PAM module query responses. For example, suppose that the
+login module queried for a third parameter, "domain" which
+is to be answered with the constant value "mydomain.com":
+
+ plugin openvpn-auth-pam.so "login login USERNAME password PASSWORD domain mydomain.com"
+
+The following OpenVPN directives can also influence
+the operation of this plugin:
+
+ client-cert-not-required
+ username-as-common-name
+
+Run OpenVPN with --verb 7 or higher to get debugging output from
+this plugin, including the list of queries presented by the
+underlying PAM module. This is a useful debugging tool to figure
+out which queries a given PAM module is making, so that you can
+craft the appropriate plugin directive to answer it.
+
+CAVEATS
+
+This module will only work on *nix systems which support PAM,
+not Windows.
diff --git a/plugin/auth-pam/auth-pam.c b/plugin/auth-pam/auth-pam.c
new file mode 100644
index 0000000..0454ee1
--- /dev/null
+++ b/plugin/auth-pam/auth-pam.c
@@ -0,0 +1,797 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * OpenVPN plugin module to do PAM authentication using a split
+ * privilege model.
+ */
+
+#if DLOPEN_PAM
+#include <dlfcn.h>
+#include "pamdl.h"
+#else
+#include <security/pam_appl.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <syslog.h>
+
+#include "openvpn-plugin.h"
+
+#define DEBUG(verb) ((verb) >= 4)
+
+/* Command codes for foreground -> background communication */
+#define COMMAND_VERIFY 0
+#define COMMAND_EXIT 1
+
+/* Response codes for background -> foreground communication */
+#define RESPONSE_INIT_SUCCEEDED 10
+#define RESPONSE_INIT_FAILED 11
+#define RESPONSE_VERIFY_SUCCEEDED 12
+#define RESPONSE_VERIFY_FAILED 13
+
+/*
+ * Plugin state, used by foreground
+ */
+struct auth_pam_context
+{
+ /* Foreground's socket to background process */
+ int foreground_fd;
+
+ /* Process ID of background process */
+ pid_t background_pid;
+
+ /* Verbosity level of OpenVPN */
+ int verb;
+};
+
+/*
+ * Name/Value pairs for conversation function.
+ * Special Values:
+ *
+ * "USERNAME" -- substitute client-supplied username
+ * "PASSWORD" -- substitute client-specified password
+ */
+
+#define N_NAME_VALUE 16
+
+struct name_value {
+ const char *name;
+ const char *value;
+};
+
+struct name_value_list {
+ int len;
+ struct name_value data[N_NAME_VALUE];
+};
+
+/*
+ * Used to pass the username/password
+ * to the PAM conversation function.
+ */
+struct user_pass {
+ int verb;
+
+ char username[128];
+ char password[128];
+
+ const struct name_value_list *name_value_list;
+};
+
+/* Background process function */
+static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list);
+
+/* Read 'tosearch', replace all occurences of 'searchfor' with 'replacewith' and return
+ * a pointer to the NEW string. Does not modify the input strings. Will not enter an
+ * infinite loop with clever 'searchfor' and 'replacewith' strings.
+ * Daniel Johnson - Progman2000@usa.net / djohnson@progman.us
+ */
+static char *
+searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith)
+{
+ const char *searching=tosearch;
+ char *scratch;
+ char temp[strlen(tosearch)*10];
+ temp[0]=0;
+
+ if (!tosearch || !searchfor || !replacewith) return 0;
+ if (!strlen(tosearch) || !strlen(searchfor) || !strlen(replacewith)) return 0;
+
+ scratch = strstr(searching,searchfor);
+ if (!scratch) return strdup(tosearch);
+
+ while (scratch) {
+ strncat(temp,searching,scratch-searching);
+ strcat(temp,replacewith);
+
+ searching=scratch+strlen(searchfor);
+ scratch = strstr(searching,searchfor);
+ }
+ return strdup(temp);
+}
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Return the length of a string array
+ */
+static int
+string_array_len (const char *array[])
+{
+ int i = 0;
+ if (array)
+ {
+ while (array[i])
+ ++i;
+ }
+ return i;
+}
+
+/*
+ * Socket read/write functions.
+ */
+
+static int
+recv_control (int fd)
+{
+ unsigned char c;
+ const ssize_t size = read (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return c;
+ else
+ {
+ /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/
+ return -1;
+ }
+}
+
+static int
+send_control (int fd, int code)
+{
+ unsigned char c = (unsigned char) code;
+ const ssize_t size = write (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return (int) size;
+ else
+ return -1;
+}
+
+static int
+recv_string (int fd, char *buffer, int len)
+{
+ if (len > 0)
+ {
+ ssize_t size;
+ memset (buffer, 0, len);
+ size = read (fd, buffer, len);
+ buffer[len-1] = 0;
+ if (size >= 1)
+ return (int)size;
+ }
+ return -1;
+}
+
+static int
+send_string (int fd, const char *string)
+{
+ const int len = strlen (string) + 1;
+ const ssize_t size = write (fd, string, len);
+ if (size == len)
+ return (int) size;
+ else
+ return -1;
+}
+
+#ifdef DO_DAEMONIZE
+
+/*
+ * Daemonize if "daemon" env var is true.
+ * Preserve stderr across daemonization if
+ * "daemon_log_redirect" env var is true.
+ */
+static void
+daemonize (const char *envp[])
+{
+ const char *daemon_string = get_env ("daemon", envp);
+ if (daemon_string && daemon_string[0] == '1')
+ {
+ const char *log_redirect = get_env ("daemon_log_redirect", envp);
+ int fd = -1;
+ if (log_redirect && log_redirect[0] == '1')
+ fd = dup (2);
+ if (daemon (0, 0) < 0)
+ {
+ fprintf (stderr, "AUTH-PAM: daemonization failed\n");
+ }
+ else if (fd >= 3)
+ {
+ dup2 (fd, 2);
+ close (fd);
+ }
+ }
+}
+
+#endif
+
+/*
+ * Close most of parent's fds.
+ * Keep stdin/stdout/stderr, plus one
+ * other fd which is presumed to be
+ * our pipe back to parent.
+ * Admittedly, a bit of a kludge,
+ * but posix doesn't give us a kind
+ * of FD_CLOEXEC which will stop
+ * fds from crossing a fork().
+ */
+static void
+close_fds_except (int keep)
+{
+ int i;
+ closelog ();
+ for (i = 3; i <= 100; ++i)
+ {
+ if (i != keep)
+ close (i);
+ }
+}
+
+/*
+ * Usually we ignore signals, because our parent will
+ * deal with them.
+ */
+static void
+set_signals (void)
+{
+ signal (SIGTERM, SIG_DFL);
+
+ signal (SIGINT, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+}
+
+/*
+ * Return 1 if query matches match.
+ */
+static int
+name_value_match (const char *query, const char *match)
+{
+ while (!isalnum (*query))
+ {
+ if (*query == '\0')
+ return 0;
+ ++query;
+ }
+ return strncasecmp (match, query, strlen (match)) == 0;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ pid_t pid;
+ int fd[2];
+
+ struct auth_pam_context *context;
+ struct name_value_list name_value_list;
+
+ const int base_parms = 2;
+
+ /*
+ * Allocate our context
+ */
+ context = (struct auth_pam_context *) calloc (1, sizeof (struct auth_pam_context));
+ if (!context)
+ goto error;
+ context->foreground_fd = -1;
+
+ /*
+ * Intercept the --auth-user-pass-verify callback.
+ */
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY);
+
+ /*
+ * Make sure we have two string arguments: the first is the .so name,
+ * the second is the PAM service type.
+ */
+ if (string_array_len (argv) < base_parms)
+ {
+ fprintf (stderr, "AUTH-PAM: need PAM service parameter\n");
+ goto error;
+ }
+
+ /*
+ * See if we have optional name/value pairs to match against
+ * PAM module queried fields in the conversation function.
+ */
+ name_value_list.len = 0;
+ if (string_array_len (argv) > base_parms)
+ {
+ const int nv_len = string_array_len (argv) - base_parms;
+ int i;
+
+ if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE)
+ {
+ fprintf (stderr, "AUTH-PAM: bad name/value list length\n");
+ goto error;
+ }
+
+ name_value_list.len = nv_len / 2;
+ for (i = 0; i < name_value_list.len; ++i)
+ {
+ const int base = base_parms + i * 2;
+ name_value_list.data[i].name = argv[base];
+ name_value_list.data[i].value = argv[base+1];
+ }
+ }
+
+ /*
+ * Get verbosity level from environment
+ */
+ {
+ const char *verb_string = get_env ("verb", envp);
+ if (verb_string)
+ context->verb = atoi (verb_string);
+ }
+
+ /*
+ * Make a socket for foreground and background processes
+ * to communicate.
+ */
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: socketpair call failed\n");
+ goto error;
+ }
+
+ /*
+ * Fork off the privileged process. It will remain privileged
+ * even after the foreground process drops its privileges.
+ */
+ pid = fork ();
+
+ if (pid)
+ {
+ int status;
+
+ /*
+ * Foreground Process
+ */
+
+ context->background_pid = pid;
+
+ /* close our copy of child's socket */
+ close (fd[1]);
+
+ /* don't let future subprocesses inherit child socket */
+ if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0)
+ fprintf (stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n");
+
+ /* wait for background child process to initialize */
+ status = recv_control (fd[0]);
+ if (status == RESPONSE_INIT_SUCCEEDED)
+ {
+ context->foreground_fd = fd[0];
+ return (openvpn_plugin_handle_t) context;
+ }
+ }
+ else
+ {
+ /*
+ * Background Process
+ */
+
+ /* close all parent fds except our socket back to parent */
+ close_fds_except (fd[1]);
+
+ /* Ignore most signals (the parent will receive them) */
+ set_signals ();
+
+#ifdef DO_DAEMONIZE
+ /* Daemonize if --daemon option is set. */
+ daemonize (envp);
+#endif
+
+ /* execute the event loop */
+ pam_server (fd[1], argv[1], context->verb, &name_value_list);
+
+ close (fd[1]);
+
+ exit (0);
+ return 0; /* NOTREACHED */
+ }
+
+ error:
+ if (context)
+ free (context);
+ return NULL;
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+ struct auth_pam_context *context = (struct auth_pam_context *) handle;
+
+ if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0)
+ {
+ /* get username/password from envp string array */
+ const char *username = get_env ("username", envp);
+ const char *password = get_env ("password", envp);
+
+ if (username && strlen (username) > 0 && password)
+ {
+ if (send_control (context->foreground_fd, COMMAND_VERIFY) == -1
+ || send_string (context->foreground_fd, username) == -1
+ || send_string (context->foreground_fd, password) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: Error sending auth info to background process\n");
+ }
+ else
+ {
+ const int status = recv_control (context->foreground_fd);
+ if (status == RESPONSE_VERIFY_SUCCEEDED)
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ if (status == -1)
+ fprintf (stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n");
+ }
+ }
+ }
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct auth_pam_context *context = (struct auth_pam_context *) handle;
+
+ if (DEBUG (context->verb))
+ fprintf (stderr, "AUTH-PAM: close\n");
+
+ if (context->foreground_fd >= 0)
+ {
+ /* tell background process to exit */
+ if (send_control (context->foreground_fd, COMMAND_EXIT) == -1)
+ fprintf (stderr, "AUTH-PAM: Error signaling background process to exit\n");
+
+ /* wait for background process to exit */
+ if (context->background_pid > 0)
+ waitpid (context->background_pid, NULL, 0);
+
+ close (context->foreground_fd);
+ context->foreground_fd = -1;
+ }
+
+ free (context);
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle)
+{
+ struct auth_pam_context *context = (struct auth_pam_context *) handle;
+
+ /* tell background process to exit */
+ if (context && context->foreground_fd >= 0)
+ {
+ send_control (context->foreground_fd, COMMAND_EXIT);
+ close (context->foreground_fd);
+ context->foreground_fd = -1;
+ }
+}
+
+/*
+ * PAM conversation function
+ */
+static int
+my_conv (int n, const struct pam_message **msg_array,
+ struct pam_response **response_array, void *appdata_ptr)
+{
+ const struct user_pass *up = ( const struct user_pass *) appdata_ptr;
+ struct pam_response *aresp;
+ int i;
+ int ret = PAM_SUCCESS;
+
+ *response_array = NULL;
+
+ if (n <= 0 || n > PAM_MAX_NUM_MSG)
+ return (PAM_CONV_ERR);
+ if ((aresp = calloc (n, sizeof *aresp)) == NULL)
+ return (PAM_BUF_ERR);
+
+ /* loop through each PAM-module query */
+ for (i = 0; i < n; ++i)
+ {
+ const struct pam_message *msg = msg_array[i];
+ aresp[i].resp_retcode = 0;
+ aresp[i].resp = NULL;
+
+ if (DEBUG (up->verb))
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n",
+ i,
+ msg->msg ? msg->msg : "NULL",
+ msg->msg_style);
+ }
+
+ if (up->name_value_list && up->name_value_list->len > 0)
+ {
+ /* use name/value list match method */
+ const struct name_value_list *list = up->name_value_list;
+ int j;
+
+ /* loop through name/value pairs */
+ for (j = 0; j < list->len; ++j)
+ {
+ const char *match_name = list->data[j].name;
+ const char *match_value = list->data[j].value;
+
+ if (name_value_match (msg->msg, match_name))
+ {
+ /* found name/value match */
+ aresp[i].resp = NULL;
+
+ if (DEBUG (up->verb))
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n",
+ msg->msg,
+ match_name,
+ match_value);
+
+ if (strstr(match_value, "USERNAME"))
+ aresp[i].resp = searchandreplace(match_value, "USERNAME", up->username);
+ else if (strstr(match_value, "PASSWORD"))
+ aresp[i].resp = searchandreplace(match_value, "PASSWORD", up->password);
+ else
+ aresp[i].resp = strdup (match_value);
+
+ if (aresp[i].resp == NULL)
+ ret = PAM_CONV_ERR;
+ break;
+ }
+ }
+
+ if (j == list->len)
+ ret = PAM_CONV_ERR;
+ }
+ else
+ {
+ /* use PAM_PROMPT_ECHO_x hints */
+ switch (msg->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ aresp[i].resp = strdup (up->password);
+ if (aresp[i].resp == NULL)
+ ret = PAM_CONV_ERR;
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ aresp[i].resp = strdup (up->username);
+ if (aresp[i].resp == NULL)
+ ret = PAM_CONV_ERR;
+ break;
+
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ break;
+
+ default:
+ ret = PAM_CONV_ERR;
+ break;
+ }
+ }
+ }
+
+ if (ret == PAM_SUCCESS)
+ *response_array = aresp;
+ return ret;
+}
+
+/*
+ * Return 1 if authenticated and 0 if failed.
+ * Called once for every username/password
+ * to be authenticated.
+ */
+static int
+pam_auth (const char *service, const struct user_pass *up)
+{
+ struct pam_conv conv;
+ pam_handle_t *pamh = NULL;
+ int status = PAM_SUCCESS;
+ int ret = 0;
+ const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0);
+
+ /* Initialize PAM */
+ conv.conv = my_conv;
+ conv.appdata_ptr = (void *)up;
+ status = pam_start (service, name_value_list_provided ? NULL : up->username, &conv, &pamh);
+ if (status == PAM_SUCCESS)
+ {
+ /* Call PAM to verify username/password */
+ status = pam_authenticate(pamh, 0);
+ if (status == PAM_SUCCESS)
+ status = pam_acct_mgmt (pamh, 0);
+ if (status == PAM_SUCCESS)
+ ret = 1;
+
+ /* Output error message if failed */
+ if (!ret)
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n",
+ up->username,
+ pam_strerror (pamh, status));
+ }
+
+ /* Close PAM */
+ pam_end (pamh, status);
+ }
+
+ return ret;
+}
+
+/*
+ * Background process -- runs with privilege.
+ */
+static void
+pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list)
+{
+ struct user_pass up;
+ int command;
+#if DLOPEN_PAM
+ static const char pam_so[] = "libpam.so";
+#endif
+
+ /*
+ * Do initialization
+ */
+ if (DEBUG (verb))
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service);
+
+#if DLOPEN_PAM
+ /*
+ * Load PAM shared object
+ */
+ if (!dlopen_pam (pam_so))
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror());
+ send_control (fd, RESPONSE_INIT_FAILED);
+ goto done;
+ }
+#endif
+
+ /*
+ * Tell foreground that we initialized successfully
+ */
+ if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n");
+ goto done;
+ }
+
+ /*
+ * Event loop
+ */
+ while (1)
+ {
+ memset (&up, 0, sizeof (up));
+ up.verb = verb;
+ up.name_value_list = name_value_list;
+
+ /* get a command from foreground process */
+ command = recv_control (fd);
+
+ if (DEBUG (verb))
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command);
+
+ switch (command)
+ {
+ case COMMAND_VERIFY:
+ if (recv_string (fd, up.username, sizeof (up.username)) == -1
+ || recv_string (fd, up.password, sizeof (up.password)) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n",
+ command);
+ goto done;
+ }
+
+ if (DEBUG (verb))
+ {
+#if 0
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n",
+ up.username, up.password);
+#else
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: USER: %s\n", up.username);
+#endif
+ }
+
+ if (pam_auth (service, &up)) /* Succeeded */
+ {
+ if (send_control (fd, RESPONSE_VERIFY_SUCCEEDED) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n");
+ goto done;
+ }
+ }
+ else /* Failed */
+ {
+ if (send_control (fd, RESPONSE_VERIFY_FAILED) == -1)
+ {
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n");
+ goto done;
+ }
+ }
+ break;
+
+ case COMMAND_EXIT:
+ goto done;
+
+ case -1:
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n");
+ goto done;
+
+ default:
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n",
+ command);
+ goto done;
+ }
+ }
+ done:
+
+#if DLOPEN_PAM
+ dlclose_pam ();
+#endif
+ if (DEBUG (verb))
+ fprintf (stderr, "AUTH-PAM: BACKGROUND: EXIT\n");
+
+ return;
+}
diff --git a/plugin/auth-pam/pamdl.c b/plugin/auth-pam/pamdl.c
new file mode 100644
index 0000000..8636a8e
--- /dev/null
+++ b/plugin/auth-pam/pamdl.c
@@ -0,0 +1,180 @@
+#if DLOPEN_PAM
+/*
+ * If you want to dynamically load libpam using dlopen() or something,
+ * then dlopen( ' this shared object ' ); It takes care of exporting
+ * the right symbols to any modules loaded by libpam.
+ *
+ * Modified by JY for use with openvpn-pam-auth
+ */
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <security/pam_appl.h>
+
+#include "pamdl.h"
+
+static void *libpam_h = NULL;
+
+#define RESOLVE_PAM_FUNCTION(x, y, z, err) \
+ { \
+ union { const void *tpointer; y (*fn) z ; } fptr; \
+ fptr.tpointer = dlsym(libpam_h, #x); real_##x = fptr.fn; \
+ if (real_##x == NULL) { \
+ fprintf (stderr, "PAMDL: unable to resolve '%s': %s\n", #x, dlerror()); \
+ return err; \
+ } \
+ }
+
+int
+dlopen_pam (const char *so)
+{
+ if (libpam_h == NULL)
+ {
+ libpam_h = dlopen(so, RTLD_GLOBAL|RTLD_NOW);
+ }
+ return libpam_h != NULL;
+}
+
+void
+dlclose_pam (void)
+{
+ if (libpam_h != NULL)
+ {
+ dlclose(libpam_h);
+ libpam_h = NULL;
+ }
+}
+
+int pam_start(const char *service_name, const char *user,
+ const struct pam_conv *pam_conversation,
+ pam_handle_t **pamh)
+{
+ int (*real_pam_start)(const char *, const char *,
+ const struct pam_conv *,
+ pam_handle_t **);
+ RESOLVE_PAM_FUNCTION(pam_start, int, (const char *, const char *,
+ const struct pam_conv *,
+ pam_handle_t **), PAM_ABORT);
+ return real_pam_start(service_name, user, pam_conversation, pamh);
+}
+
+int pam_end(pam_handle_t *pamh, int pam_status)
+{
+ int (*real_pam_end)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_end, int, (pam_handle_t *, int), PAM_ABORT);
+ return real_pam_end(pamh, pam_status);
+}
+
+int pam_set_item(pam_handle_t *pamh, int item_type, const void *item)
+{
+ int (*real_pam_set_item)(pam_handle_t *, int, const void *);
+ RESOLVE_PAM_FUNCTION(pam_set_item, int,
+ (pam_handle_t *, int, const void *), PAM_ABORT);
+ return real_pam_set_item(pamh, item_type, item);
+}
+
+int pam_get_item(pam_handle_t *pamh, int item_type, const void **item)
+{
+ int (*real_pam_get_item)(const pam_handle_t *, int, const void **);
+ RESOLVE_PAM_FUNCTION(pam_get_item, int,
+ (const pam_handle_t *, int, const void **),
+ PAM_ABORT);
+ return real_pam_get_item(pamh, item_type, item);
+}
+
+int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay)
+{
+ int (*real_pam_fail_delay)(pam_handle_t *, unsigned int);
+ RESOLVE_PAM_FUNCTION(pam_fail_delay, int, (pam_handle_t *, unsigned int),
+ PAM_ABORT);
+ return real_pam_fail_delay(pamh, musec_delay);
+}
+
+typedef const char * const_char_pointer;
+
+const_char_pointer pam_strerror(pam_handle_t *pamh, int errnum)
+{
+ const_char_pointer (*real_pam_strerror)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_strerror, const_char_pointer,
+ (pam_handle_t *, int), NULL);
+ return real_pam_strerror(pamh, errnum);
+}
+
+int pam_putenv(pam_handle_t *pamh, const char *name_value)
+{
+ int (*real_pam_putenv)(pam_handle_t *, const char *);
+ RESOLVE_PAM_FUNCTION(pam_putenv, int, (pam_handle_t *, const char *),
+ PAM_ABORT);
+ return real_pam_putenv(pamh, name_value);
+}
+
+const_char_pointer pam_getenv(pam_handle_t *pamh, const char *name)
+{
+ const_char_pointer (*real_pam_getenv)(pam_handle_t *, const char *);
+ RESOLVE_PAM_FUNCTION(pam_getenv, const_char_pointer,
+ (pam_handle_t *, const char *), NULL);
+ return real_pam_getenv(pamh, name);
+}
+
+typedef char ** char_ppointer;
+char_ppointer pam_getenvlist(pam_handle_t *pamh)
+{
+ char_ppointer (*real_pam_getenvlist)(pam_handle_t *);
+ RESOLVE_PAM_FUNCTION(pam_getenvlist, char_ppointer, (pam_handle_t *),
+ NULL);
+ return real_pam_getenvlist(pamh);
+}
+
+/* Authentication management */
+
+int pam_authenticate(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_authenticate)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_authenticate, int, (pam_handle_t *, int),
+ PAM_ABORT);
+ return real_pam_authenticate(pamh, flags);
+}
+
+int pam_setcred(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_setcred)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_setcred, int, (pam_handle_t *, int), PAM_ABORT);
+ return real_pam_setcred(pamh, flags);
+}
+
+/* Account Management API's */
+
+int pam_acct_mgmt(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_acct_mgmt)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_acct_mgmt, int, (pam_handle_t *, int), PAM_ABORT);
+ return real_pam_acct_mgmt(pamh, flags);
+}
+
+/* Session Management API's */
+
+int pam_open_session(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_open_session)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_open_session, int, (pam_handle_t *, int),
+ PAM_ABORT);
+ return real_pam_open_session(pamh, flags);
+}
+
+int pam_close_session(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_close_session)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_close_session, int, (pam_handle_t *, int),
+ PAM_ABORT);
+ return real_pam_close_session(pamh, flags);
+}
+
+/* Password Management API's */
+
+int pam_chauthtok(pam_handle_t *pamh, int flags)
+{
+ int (*real_pam_chauthtok)(pam_handle_t *, int);
+ RESOLVE_PAM_FUNCTION(pam_chauthtok, int, (pam_handle_t *, int), PAM_ABORT);
+ return real_pam_chauthtok(pamh, flags);
+}
+#endif
diff --git a/plugin/auth-pam/pamdl.h b/plugin/auth-pam/pamdl.h
new file mode 100644
index 0000000..b10b035
--- /dev/null
+++ b/plugin/auth-pam/pamdl.h
@@ -0,0 +1,7 @@
+#if DLOPEN_PAM
+#include <security/pam_appl.h>
+
+/* Dynamically load and unload the PAM library */
+int dlopen_pam (const char *so);
+void dlclose_pam (void);
+#endif
diff --git a/plugin/defer/README b/plugin/defer/README
new file mode 100644
index 0000000..d8990f8
--- /dev/null
+++ b/plugin/defer/README
@@ -0,0 +1,16 @@
+OpenVPN plugin examples.
+
+Examples provided:
+
+simple.c -- using the --auth-user-pass-verify callback,
+ test deferred authentication.
+
+To build:
+
+ ./build simple (Linux/BSD/etc.)
+ ./winbuild simple (MinGW on Windows)
+
+To use in OpenVPN, add to config file:
+
+ plugin simple.so (Linux/BSD/etc.)
+ plugin simple.dll (MinGW on Windows)
diff --git a/plugin/defer/build b/plugin/defer/build
new file mode 100755
index 0000000..5907afa
--- /dev/null
+++ b/plugin/defer/build
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+#
+# Build an OpenVPN plugin module on *nix. The argument should
+# be the base name of the C source file (without the .c).
+#
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE="-I../.."
+
+CC_FLAGS="-O2 -Wall -g"
+
+gcc $CC_FLAGS -fPIC -c $INCLUDE $1.c && \
+gcc $CC_FLAGS -fPIC -shared -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/plugin/defer/simple.c b/plugin/defer/simple.c
new file mode 100644
index 0000000..6539865
--- /dev/null
+++ b/plugin/defer/simple.c
@@ -0,0 +1,305 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file implements a simple OpenVPN plugin module which
+ * will test deferred authentication and packet filtering.
+ *
+ * Will run on Windows or *nix.
+ *
+ * Sample usage:
+ *
+ * setenv test_deferred_auth 20
+ * setenv test_packet_filter 10
+ * plugin plugin/defer/simple.so
+ *
+ * This will enable deferred authentication to occur 20
+ * seconds after the normal TLS authentication process,
+ * and will cause a packet filter file to be generated 10
+ * seconds after the initial TLS negotiation, using
+ * {common-name}.pf as the source.
+ *
+ * Sample packet filter configuration:
+ *
+ * [CLIENTS DROP]
+ * +otherclient
+ * [SUBNETS DROP]
+ * +10.0.0.0/8
+ * -10.10.0.8
+ * [END]
+ *
+ * See the README file for build instructions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "openvpn-plugin.h"
+
+/* bool definitions */
+#define bool int
+#define true 1
+#define false 0
+
+/*
+ * Our context, where we keep our state.
+ */
+
+struct plugin_context {
+ int test_deferred_auth;
+ int test_packet_filter;
+};
+
+struct plugin_per_client_context {
+ int n_calls;
+ bool generated_pf_file;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* used for safe printf of possible NULL strings */
+static const char *
+np (const char *str)
+{
+ if (str)
+ return str;
+ else
+ return "[NULL]";
+}
+
+static int
+atoi_null0 (const char *str)
+{
+ if (str)
+ return atoi (str);
+ else
+ return 0;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context;
+
+ printf ("FUNC: openvpn_plugin_open_v1\n");
+
+ /*
+ * Allocate our context
+ */
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
+
+ context->test_deferred_auth = atoi_null0 (get_env ("test_deferred_auth", envp));
+ printf ("TEST_DEFERRED_AUTH %d\n", context->test_deferred_auth);
+
+ context->test_packet_filter = atoi_null0 (get_env ("test_packet_filter", envp));
+ printf ("TEST_PACKET_FILTER %d\n", context->test_packet_filter);
+
+ /*
+ * Which callbacks to intercept.
+ */
+ *type_mask =
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ENABLE_PF);
+
+ return (openvpn_plugin_handle_t) context;
+}
+
+static int
+auth_user_pass_verify (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
+{
+ if (context->test_deferred_auth)
+ {
+ /* get username/password from envp string array */
+ const char *username = get_env ("username", envp);
+ const char *password = get_env ("password", envp);
+
+ /* get auth_control_file filename from envp string array*/
+ const char *auth_control_file = get_env ("auth_control_file", envp);
+
+ printf ("DEFER u='%s' p='%s' acf='%s'\n",
+ np(username),
+ np(password),
+ np(auth_control_file));
+
+ /* Authenticate asynchronously in n seconds */
+ if (auth_control_file)
+ {
+ char buf[256];
+ int auth = 2;
+ sscanf (username, "%d", &auth);
+ snprintf (buf, sizeof(buf), "( sleep %d ; echo AUTH %s %d ; echo %d >%s ) &",
+ context->test_deferred_auth,
+ auth_control_file,
+ auth,
+ pcc->n_calls < auth,
+ auth_control_file);
+ printf ("%s\n", buf);
+ system (buf);
+ pcc->n_calls++;
+ return OPENVPN_PLUGIN_FUNC_DEFERRED;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+static int
+tls_final (struct plugin_context *context, struct plugin_per_client_context *pcc, const char *argv[], const char *envp[])
+{
+ if (context->test_packet_filter)
+ {
+ if (!pcc->generated_pf_file)
+ {
+ const char *pff = get_env ("pf_file", envp);
+ const char *cn = get_env ("username", envp);
+ if (pff && cn)
+ {
+ char buf[256];
+ snprintf (buf, sizeof(buf), "( sleep %d ; echo PF %s/%s ; cp \"%s.pf\" \"%s\" ) &",
+ context->test_packet_filter, cn, pff, cn, pff);
+ printf ("%s\n", buf);
+ system (buf);
+ pcc->generated_pf_file = true;
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT int
+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)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ struct plugin_per_client_context *pcc = (struct plugin_per_client_context *) per_client_context;
+ switch (type)
+ {
+ case OPENVPN_PLUGIN_UP:
+ printf ("OPENVPN_PLUGIN_UP\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_DOWN:
+ printf ("OPENVPN_PLUGIN_DOWN\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_ROUTE_UP:
+ printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_IPCHANGE:
+ printf ("OPENVPN_PLUGIN_IPCHANGE\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_TLS_VERIFY:
+ printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
+ printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
+ return auth_user_pass_verify (context, pcc, argv, envp);
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+ printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+ printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_LEARN_ADDRESS:
+ printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ case OPENVPN_PLUGIN_TLS_FINAL:
+ printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
+ return tls_final (context, pcc, argv, envp);
+ case OPENVPN_PLUGIN_ENABLE_PF:
+ printf ("OPENVPN_PLUGIN_ENABLE_PF\n");
+ if (context->test_packet_filter)
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ default:
+ printf ("OPENVPN_PLUGIN_?\n");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+}
+
+OPENVPN_EXPORT void *
+openvpn_plugin_client_constructor_v1 (openvpn_plugin_handle_t handle)
+{
+ printf ("FUNC: openvpn_plugin_client_constructor_v1\n");
+ return calloc (1, sizeof (struct plugin_per_client_context));
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_client_destructor_v1 (openvpn_plugin_handle_t handle, void *per_client_context)
+{
+ printf ("FUNC: openvpn_plugin_client_destructor_v1\n");
+ free (per_client_context);
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ printf ("FUNC: openvpn_plugin_close_v1\n");
+ free (context);
+}
diff --git a/plugin/defer/simple.def b/plugin/defer/simple.def
new file mode 100755
index 0000000..a87507d
--- /dev/null
+++ b/plugin/defer/simple.def
@@ -0,0 +1,6 @@
+LIBRARY OpenVPN_PLUGIN_SAMPLE
+DESCRIPTION "Sample OpenVPN plug-in module."
+EXPORTS
+ openvpn_plugin_open_v1 @1
+ openvpn_plugin_func_v1 @2
+ openvpn_plugin_close_v1 @3
diff --git a/plugin/defer/winbuild b/plugin/defer/winbuild
new file mode 100755
index 0000000..97e724a
--- /dev/null
+++ b/plugin/defer/winbuild
@@ -0,0 +1,18 @@
+#
+# Build an OpenVPN plugin module on Windows/MinGW.
+# The argument should be the base name of the C source file
+# (without the .c).
+#
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE="-I.."
+
+CC_FLAGS="-O2 -Wall"
+
+gcc -DBUILD_DLL $CC_FLAGS $INCLUDE -c $1.c
+gcc --disable-stdcall-fixup -mdll -DBUILD_DLL -o junk.tmp -Wl,--base-file,base.tmp $1.o
+rm junk.tmp
+dlltool --dllname $1.dll --base-file base.tmp --output-exp temp.exp --input-def $1.def
+rm base.tmp
+gcc --enable-stdcall-fixup -mdll -DBUILD_DLL -o $1.dll $1.o -Wl,temp.exp
+rm temp.exp
diff --git a/plugin/down-root/Makefile b/plugin/down-root/Makefile
new file mode 100755
index 0000000..5ce4ffb
--- /dev/null
+++ b/plugin/down-root/Makefile
@@ -0,0 +1,17 @@
+#
+# Build the OpenVPN down-root plugin module.
+#
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE=-I../..
+
+CC_FLAGS=-O2 -Wall
+
+down-root.so : down-root.o
+ gcc ${CC_FLAGS} -fPIC -shared -Wl,-soname,openvpn-down-root.so -o openvpn-down-root.so down-root.o -lc
+
+down-root.o : down-root.c
+ gcc ${CC_FLAGS} -fPIC -c ${INCLUDE} down-root.c
+
+clean :
+ rm -f *.o *.so
diff --git a/plugin/down-root/README b/plugin/down-root/README
new file mode 100644
index 0000000..d337ffe
--- /dev/null
+++ b/plugin/down-root/README
@@ -0,0 +1,29 @@
+down-root -- an OpenVPN Plugin Module
+
+SYNOPSIS
+
+The down-root module allows an OpenVPN configuration to
+call a down script with root privileges, even when privileges
+have been dropped using --user/--group/--chroot.
+
+This module uses a split privilege execution model which will
+fork() before OpenVPN drops root privileges, at the point where
+the --up script is usually called. The module will then remain
+in a wait state until it receives a message from OpenVPN via
+pipe to execute the down script. Thus, the down script will be
+run in the same execution environment as the up script.
+
+BUILD
+
+Build this module with the "make" command. The plugin
+module will be named openvpn-down-root.so
+
+USAGE
+
+To use this module, add to your OpenVPN config file:
+
+ plugin openvpn-down-root.so "command ..."
+
+CAVEATS
+
+This module will only work on *nix systems, not Windows.
diff --git a/plugin/down-root/down-root.c b/plugin/down-root/down-root.c
new file mode 100644
index 0000000..fced23b
--- /dev/null
+++ b/plugin/down-root/down-root.c
@@ -0,0 +1,553 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * OpenVPN plugin module to do privileged down-script execution.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <syslog.h>
+
+#include "openvpn-plugin.h"
+
+#define DEBUG(verb) ((verb) >= 7)
+
+/* Command codes for foreground -> background communication */
+#define COMMAND_RUN_SCRIPT 0
+#define COMMAND_EXIT 1
+
+/* Response codes for background -> foreground communication */
+#define RESPONSE_INIT_SUCCEEDED 10
+#define RESPONSE_INIT_FAILED 11
+#define RESPONSE_SCRIPT_SUCCEEDED 12
+#define RESPONSE_SCRIPT_FAILED 13
+
+/* Background process function */
+static void down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb);
+
+/*
+ * Plugin state, used by foreground
+ */
+struct down_root_context
+{
+ /* Foreground's socket to background process */
+ int foreground_fd;
+
+ /* Process ID of background process */
+ pid_t background_pid;
+
+ /* Verbosity level of OpenVPN */
+ int verb;
+
+ /* down command */
+ char *command;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Return the length of a string array
+ */
+static int
+string_array_len (const char *array[])
+{
+ int i = 0;
+ if (array)
+ {
+ while (array[i])
+ ++i;
+ }
+ return i;
+}
+
+/*
+ * Socket read/write functions.
+ */
+
+static int
+recv_control (int fd)
+{
+ unsigned char c;
+ const ssize_t size = read (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return c;
+ else
+ return -1;
+}
+
+static int
+send_control (int fd, int code)
+{
+ unsigned char c = (unsigned char) code;
+ const ssize_t size = write (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return (int) size;
+ else
+ return -1;
+}
+
+/*
+ * Daemonize if "daemon" env var is true.
+ * Preserve stderr across daemonization if
+ * "daemon_log_redirect" env var is true.
+ */
+static void
+daemonize (const char *envp[])
+{
+ const char *daemon_string = get_env ("daemon", envp);
+ if (daemon_string && daemon_string[0] == '1')
+ {
+ const char *log_redirect = get_env ("daemon_log_redirect", envp);
+ int fd = -1;
+ if (log_redirect && log_redirect[0] == '1')
+ fd = dup (2);
+ if (daemon (0, 0) < 0)
+ {
+ fprintf (stderr, "DOWN-ROOT: daemonization failed\n");
+ }
+ else if (fd >= 3)
+ {
+ dup2 (fd, 2);
+ close (fd);
+ }
+ }
+}
+
+/*
+ * Close most of parent's fds.
+ * Keep stdin/stdout/stderr, plus one
+ * other fd which is presumed to be
+ * our pipe back to parent.
+ * Admittedly, a bit of a kludge,
+ * but posix doesn't give us a kind
+ * of FD_CLOEXEC which will stop
+ * fds from crossing a fork().
+ */
+static void
+close_fds_except (int keep)
+{
+ int i;
+ closelog ();
+ for (i = 3; i <= 100; ++i)
+ {
+ if (i != keep)
+ close (i);
+ }
+}
+
+/*
+ * Usually we ignore signals, because our parent will
+ * deal with them.
+ */
+static void
+set_signals (void)
+{
+ signal (SIGTERM, SIG_DFL);
+
+ signal (SIGINT, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+}
+
+/*
+ * convert system() return into a success/failure value
+ */
+int
+system_ok (int stat)
+{
+#ifdef WIN32
+ return stat == 0;
+#else
+ return stat != -1 && WIFEXITED (stat) && WEXITSTATUS (stat) == 0;
+#endif
+}
+
+static char *
+build_command_line (const char *argv[])
+{
+ int size = 0;
+ int n = 0;
+ int i;
+ char *string;
+
+ /* precompute size */
+ if (argv)
+ {
+ for (i = 0; argv[i]; ++i)
+ {
+ size += (strlen (argv[i]) + 1); /* string length plus trailing space */
+ ++n;
+ }
+ }
+ ++size; /* for null terminator */
+
+ /* allocate memory */
+ string = (char *) malloc (size);
+ if (!string)
+ {
+ fprintf (stderr, "DOWN-ROOT: out of memory\n");
+ exit (1);
+ }
+ string[0] = '\0';
+
+ /* build string */
+ for (i = 0; i < n; ++i)
+ {
+ strcat (string, argv[i]);
+ if (i + 1 < n)
+ strcat (string, " ");
+ }
+ return string;
+}
+
+static void
+free_context (struct down_root_context *context)
+{
+ if (context)
+ {
+ if (context->command)
+ free (context->command);
+ free (context);
+ }
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ struct down_root_context *context;
+
+ /*
+ * Allocate our context
+ */
+ context = (struct down_root_context *) calloc (1, sizeof (struct down_root_context));
+ if (!context)
+ goto error;
+ context->foreground_fd = -1;
+
+ /*
+ * Intercept the --up and --down callbacks
+ */
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) | OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN);
+
+ /*
+ * Make sure we have two string arguments: the first is the .so name,
+ * the second is the script command.
+ */
+ if (string_array_len (argv) < 2)
+ {
+ fprintf (stderr, "DOWN-ROOT: need down script command\n");
+ goto error;
+ }
+
+ /*
+ * Save our argument in context
+ */
+ context->command = build_command_line (&argv[1]);
+
+ /*
+ * Get verbosity level from environment
+ */
+ {
+ const char *verb_string = get_env ("verb", envp);
+ if (verb_string)
+ context->verb = atoi (verb_string);
+ }
+
+ return (openvpn_plugin_handle_t) context;
+
+ error:
+ free_context (context);
+ return NULL;
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+ struct down_root_context *context = (struct down_root_context *) handle;
+
+ if (type == OPENVPN_PLUGIN_UP && context->foreground_fd == -1) /* fork off a process to hold onto root */
+ {
+ pid_t pid;
+ int fd[2];
+
+ /*
+ * Make a socket for foreground and background processes
+ * to communicate.
+ */
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
+ {
+ fprintf (stderr, "DOWN-ROOT: socketpair call failed\n");
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /*
+ * Fork off the privileged process. It will remain privileged
+ * even after the foreground process drops its privileges.
+ */
+ pid = fork ();
+
+ if (pid)
+ {
+ int status;
+
+ /*
+ * Foreground Process
+ */
+
+ context->background_pid = pid;
+
+ /* close our copy of child's socket */
+ close (fd[1]);
+
+ /* don't let future subprocesses inherit child socket */
+ if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0)
+ fprintf (stderr, "DOWN-ROOT: Set FD_CLOEXEC flag on socket file descriptor failed\n");
+
+ /* wait for background child process to initialize */
+ status = recv_control (fd[0]);
+ if (status == RESPONSE_INIT_SUCCEEDED)
+ {
+ context->foreground_fd = fd[0];
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ }
+ }
+ else
+ {
+ /*
+ * Background Process
+ */
+
+ /* close all parent fds except our socket back to parent */
+ close_fds_except (fd[1]);
+
+ /* Ignore most signals (the parent will receive them) */
+ set_signals ();
+
+ /* Daemonize if --daemon option is set. */
+ daemonize (envp);
+
+ /* execute the event loop */
+ down_root_server (fd[1], context->command, argv, envp, context->verb);
+
+ close (fd[1]);
+ exit (0);
+ return 0; /* NOTREACHED */
+ }
+ }
+ else if (type == OPENVPN_PLUGIN_DOWN && context->foreground_fd >= 0)
+ {
+ if (send_control (context->foreground_fd, COMMAND_RUN_SCRIPT) == -1)
+ {
+ fprintf (stderr, "DOWN-ROOT: Error sending script execution signal to background process\n");
+ }
+ else
+ {
+ const int status = recv_control (context->foreground_fd);
+ if (status == RESPONSE_SCRIPT_SUCCEEDED)
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ if (status == -1)
+ fprintf (stderr, "DOWN-ROOT: Error receiving script execution confirmation from background process\n");
+ }
+ }
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct down_root_context *context = (struct down_root_context *) handle;
+
+ if (DEBUG (context->verb))
+ fprintf (stderr, "DOWN-ROOT: close\n");
+
+ if (context->foreground_fd >= 0)
+ {
+ /* tell background process to exit */
+ if (send_control (context->foreground_fd, COMMAND_EXIT) == -1)
+ fprintf (stderr, "DOWN-ROOT: Error signaling background process to exit\n");
+
+ /* wait for background process to exit */
+ if (context->background_pid > 0)
+ waitpid (context->background_pid, NULL, 0);
+
+ close (context->foreground_fd);
+ context->foreground_fd = -1;
+ }
+
+ free_context (context);
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle)
+{
+ struct down_root_context *context = (struct down_root_context *) handle;
+
+ if (context && context->foreground_fd >= 0)
+ {
+ /* tell background process to exit */
+ send_control (context->foreground_fd, COMMAND_EXIT);
+ close (context->foreground_fd);
+ context->foreground_fd = -1;
+ }
+}
+
+/*
+ * Background process -- runs with privilege.
+ */
+static void
+down_root_server (const int fd, char *command, const char *argv[], const char *envp[], const int verb)
+{
+ const char *p[3];
+ char *command_line = NULL;
+ char *argv_cat = NULL;
+ int i;
+
+ /*
+ * Do initialization
+ */
+ if (DEBUG (verb))
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: INIT command='%s'\n", command);
+
+ /*
+ * Tell foreground that we initialized successfully
+ */
+ if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1)
+ {
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [1]\n");
+ goto done;
+ }
+
+ /*
+ * Build command line
+ */
+ if (string_array_len (argv) >= 2)
+ argv_cat = build_command_line (&argv[1]);
+ else
+ argv_cat = build_command_line (NULL);
+ p[0] = command;
+ p[1] = argv_cat;
+ p[2] = NULL;
+ command_line = build_command_line (p);
+
+ /*
+ * Save envp in environment
+ */
+ for (i = 0; envp[i]; ++i)
+ {
+ putenv ((char *)envp[i]);
+ }
+
+ /*
+ * Event loop
+ */
+ while (1)
+ {
+ int command_code;
+ int status;
+
+ /* get a command from foreground process */
+ command_code = recv_control (fd);
+
+ if (DEBUG (verb))
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: received command code: %d\n", command_code);
+
+ switch (command_code)
+ {
+ case COMMAND_RUN_SCRIPT:
+ status = system (command_line);
+ if (system_ok (status)) /* Succeeded */
+ {
+ if (send_control (fd, RESPONSE_SCRIPT_SUCCEEDED) == -1)
+ {
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [2]\n");
+ goto done;
+ }
+ }
+ else /* Failed */
+ {
+ if (send_control (fd, RESPONSE_SCRIPT_FAILED) == -1)
+ {
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: write error on response socket [3]\n");
+ goto done;
+ }
+ }
+ break;
+
+ case COMMAND_EXIT:
+ goto done;
+
+ case -1:
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: read error on command channel\n");
+ goto done;
+
+ default:
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: unknown command code: code=%d, exiting\n",
+ command_code);
+ goto done;
+ }
+ }
+
+ done:
+ if (argv_cat)
+ free (argv_cat);
+ if (command_line)
+ free (command_line);
+ if (DEBUG (verb))
+ fprintf (stderr, "DOWN-ROOT: BACKGROUND: EXIT\n");
+
+ return;
+}
diff --git a/plugin/examples/README b/plugin/examples/README
new file mode 100644
index 0000000..4400cd3
--- /dev/null
+++ b/plugin/examples/README
@@ -0,0 +1,16 @@
+OpenVPN plugin examples.
+
+Examples provided:
+
+simple.c -- using the --auth-user-pass-verify callback, verify
+ that the username/password is "foo"/"bar".
+
+To build:
+
+ ./build simple (Linux/BSD/etc.)
+ ./winbuild simple (MinGW on Windows)
+
+To use in OpenVPN, add to config file:
+
+ plugin simple.so (Linux/BSD/etc.)
+ plugin simple.dll (MinGW on Windows)
diff --git a/plugin/examples/build b/plugin/examples/build
new file mode 100755
index 0000000..5907afa
--- /dev/null
+++ b/plugin/examples/build
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+#
+# Build an OpenVPN plugin module on *nix. The argument should
+# be the base name of the C source file (without the .c).
+#
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE="-I../.."
+
+CC_FLAGS="-O2 -Wall -g"
+
+gcc $CC_FLAGS -fPIC -c $INCLUDE $1.c && \
+gcc $CC_FLAGS -fPIC -shared -Wl,-soname,$1.so -o $1.so $1.o -lc
diff --git a/plugin/examples/log.c b/plugin/examples/log.c
new file mode 100644
index 0000000..1cc4650
--- /dev/null
+++ b/plugin/examples/log.c
@@ -0,0 +1,184 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This plugin is similar to simple.c, except it also logs extra information
+ * to stdout for every plugin method called by OpenVPN.
+ *
+ * See the README file for build instructions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "openvpn-plugin.h"
+
+/*
+ * Our context, where we keep our state.
+ */
+struct plugin_context {
+ const char *username;
+ const char *password;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context;
+
+ /*
+ * Allocate our context
+ */
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
+
+ /*
+ * Set the username/password we will require.
+ */
+ context->username = "foo";
+ context->password = "bar";
+
+ /*
+ * Which callbacks to intercept.
+ */
+ *type_mask =
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_DOWN) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_ROUTE_UP) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_IPCHANGE) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_CONNECT_V2) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_CLIENT_DISCONNECT) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_LEARN_ADDRESS) |
+ OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_TLS_FINAL);
+
+ return (openvpn_plugin_handle_t) context;
+}
+
+void
+show (const int type, const char *argv[], const char *envp[])
+{
+ size_t i;
+ switch (type)
+ {
+ case OPENVPN_PLUGIN_UP:
+ printf ("OPENVPN_PLUGIN_UP\n");
+ break;
+ case OPENVPN_PLUGIN_DOWN:
+ printf ("OPENVPN_PLUGIN_DOWN\n");
+ break;
+ case OPENVPN_PLUGIN_ROUTE_UP:
+ printf ("OPENVPN_PLUGIN_ROUTE_UP\n");
+ break;
+ case OPENVPN_PLUGIN_IPCHANGE:
+ printf ("OPENVPN_PLUGIN_IPCHANGE\n");
+ break;
+ case OPENVPN_PLUGIN_TLS_VERIFY:
+ printf ("OPENVPN_PLUGIN_TLS_VERIFY\n");
+ break;
+ case OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY:
+ printf ("OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY\n");
+ break;
+ case OPENVPN_PLUGIN_CLIENT_CONNECT_V2:
+ printf ("OPENVPN_PLUGIN_CLIENT_CONNECT_V2\n");
+ break;
+ case OPENVPN_PLUGIN_CLIENT_DISCONNECT:
+ printf ("OPENVPN_PLUGIN_CLIENT_DISCONNECT\n");
+ break;
+ case OPENVPN_PLUGIN_LEARN_ADDRESS:
+ printf ("OPENVPN_PLUGIN_LEARN_ADDRESS\n");
+ break;
+ case OPENVPN_PLUGIN_TLS_FINAL:
+ printf ("OPENVPN_PLUGIN_TLS_FINAL\n");
+ break;
+ default:
+ printf ("OPENVPN_PLUGIN_?\n");
+ break;
+ }
+
+ printf ("ARGV\n");
+ for (i = 0; argv[i] != NULL; ++i)
+ printf ("%d '%s'\n", (int)i, argv[i]);
+
+ printf ("ENVP\n");
+ for (i = 0; envp[i] != NULL; ++i)
+ printf ("%d '%s'\n", (int)i, envp[i]);
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+
+ show (type, argv, envp);
+
+ /* check entered username/password against what we require */
+ if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
+ {
+ /* get username/password from envp string array */
+ const char *username = get_env ("username", envp);
+ const char *password = get_env ("password", envp);
+
+ if (username && !strcmp (username, context->username)
+ && password && !strcmp (password, context->password))
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+ else
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ free (context);
+}
diff --git a/plugin/examples/simple.c b/plugin/examples/simple.c
new file mode 100644
index 0000000..f26d89f
--- /dev/null
+++ b/plugin/examples/simple.c
@@ -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) 2002-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file implements a simple OpenVPN plugin module which
+ * will examine the username/password provided by a client,
+ * and make an accept/deny determination. Will run
+ * on Windows or *nix.
+ *
+ * See the README file for build instructions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "openvpn-plugin.h"
+
+/*
+ * Our context, where we keep our state.
+ */
+struct plugin_context {
+ const char *username;
+ const char *password;
+};
+
+/*
+ * Given an environmental variable name, search
+ * the envp array for its value, returning it
+ * if found or NULL otherwise.
+ */
+static const char *
+get_env (const char *name, const char *envp[])
+{
+ if (envp)
+ {
+ int i;
+ const int namelen = strlen (name);
+ for (i = 0; envp[i]; ++i)
+ {
+ if (!strncmp (envp[i], name, namelen))
+ {
+ const char *cp = envp[i] + namelen;
+ if (*cp == '=')
+ return cp + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+OPENVPN_EXPORT openvpn_plugin_handle_t
+openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context;
+
+ /*
+ * Allocate our context
+ */
+ context = (struct plugin_context *) calloc (1, sizeof (struct plugin_context));
+
+ /*
+ * Set the username/password we will require.
+ */
+ context->username = "foo";
+ context->password = "bar";
+
+ /*
+ * We are only interested in intercepting the
+ * --auth-user-pass-verify callback.
+ */
+ *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY);
+
+ return (openvpn_plugin_handle_t) context;
+}
+
+OPENVPN_EXPORT int
+openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+
+ /* get username/password from envp string array */
+ const char *username = get_env ("username", envp);
+ const char *password = get_env ("password", envp);
+
+ /* check entered username/password against what we require */
+ if (username && !strcmp (username, context->username)
+ && password && !strcmp (password, context->password))
+ return OPENVPN_PLUGIN_FUNC_SUCCESS;
+ else
+ return OPENVPN_PLUGIN_FUNC_ERROR;
+}
+
+OPENVPN_EXPORT void
+openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle)
+{
+ struct plugin_context *context = (struct plugin_context *) handle;
+ free (context);
+}
diff --git a/plugin/examples/simple.def b/plugin/examples/simple.def
new file mode 100755
index 0000000..a87507d
--- /dev/null
+++ b/plugin/examples/simple.def
@@ -0,0 +1,6 @@
+LIBRARY OpenVPN_PLUGIN_SAMPLE
+DESCRIPTION "Sample OpenVPN plug-in module."
+EXPORTS
+ openvpn_plugin_open_v1 @1
+ openvpn_plugin_func_v1 @2
+ openvpn_plugin_close_v1 @3
diff --git a/plugin/examples/winbuild b/plugin/examples/winbuild
new file mode 100755
index 0000000..97e724a
--- /dev/null
+++ b/plugin/examples/winbuild
@@ -0,0 +1,18 @@
+#
+# Build an OpenVPN plugin module on Windows/MinGW.
+# The argument should be the base name of the C source file
+# (without the .c).
+#
+
+# This directory is where we will look for openvpn-plugin.h
+INCLUDE="-I.."
+
+CC_FLAGS="-O2 -Wall"
+
+gcc -DBUILD_DLL $CC_FLAGS $INCLUDE -c $1.c
+gcc --disable-stdcall-fixup -mdll -DBUILD_DLL -o junk.tmp -Wl,--base-file,base.tmp $1.o
+rm junk.tmp
+dlltool --dllname $1.dll --base-file base.tmp --output-exp temp.exp --input-def $1.def
+rm base.tmp
+gcc --enable-stdcall-fixup -mdll -DBUILD_DLL -o $1.dll $1.o -Wl,temp.exp
+rm temp.exp
diff --git a/pool.c b/pool.c
new file mode 100644
index 0000000..84333df
--- /dev/null
+++ b/pool.c
@@ -0,0 +1,526 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "pool.h"
+#include "buffer.h"
+#include "error.h"
+#include "socket.h"
+#include "otime.h"
+
+#include "memdbg.h"
+
+#if P2MP
+
+static void
+ifconfig_pool_entry_free (struct ifconfig_pool_entry *ipe, bool hard)
+{
+ ipe->in_use = false;
+ if (hard && ipe->common_name)
+ {
+ free (ipe->common_name);
+ ipe->common_name = NULL;
+ }
+ if (hard)
+ ipe->last_release = 0;
+ else
+ ipe->last_release = now;
+}
+
+static int
+ifconfig_pool_find (struct ifconfig_pool *pool, const char *common_name)
+{
+ int i;
+ time_t earliest_release = 0;
+ int previous_usage = -1;
+ int new_usage = -1;
+
+ for (i = 0; i < pool->size; ++i)
+ {
+ struct ifconfig_pool_entry *ipe = &pool->list[i];
+ if (!ipe->in_use)
+ {
+ /*
+ * If duplicate_cn mode, take first available IP address
+ */
+ if (pool->duplicate_cn)
+ {
+ new_usage = i;
+ break;
+ }
+
+ /*
+ * Keep track of the unused IP address entry which
+ * was released earliest.
+ */
+ if ((new_usage == -1 || ipe->last_release < earliest_release) && !ipe->fixed)
+ {
+ earliest_release = ipe->last_release;
+ new_usage = i;
+ }
+
+ /*
+ * Keep track of a possible allocation to us
+ * from an earlier session.
+ */
+ if (previous_usage < 0
+ && common_name
+ && ipe->common_name
+ && !strcmp (common_name, ipe->common_name))
+ previous_usage = i;
+
+ }
+ }
+
+ if (previous_usage >= 0)
+ return previous_usage;
+
+ if (new_usage >= 0)
+ return new_usage;
+
+ return -1;
+}
+
+/*
+ * Verify start/end range
+ */
+bool
+ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = true;
+
+ if (start > end)
+ {
+ msg (msglevel, "--ifconfig-pool start IP [%s] is greater than end IP [%s]",
+ print_in_addr_t (start, 0, &gc),
+ print_in_addr_t (end, 0, &gc));
+ ret = false;
+ }
+ if (end - start >= IFCONFIG_POOL_MAX)
+ {
+ msg (msglevel, "--ifconfig-pool address range is too large [%s -> %s]. Current maximum is %d addresses, as defined by IFCONFIG_POOL_MAX variable.",
+ print_in_addr_t (start, 0, &gc),
+ print_in_addr_t (end, 0, &gc),
+ IFCONFIG_POOL_MAX);
+ ret = false;
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+struct ifconfig_pool *
+ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
+{
+ struct gc_arena gc = gc_new ();
+ struct ifconfig_pool *pool = NULL;
+
+ ASSERT (start <= end && end - start < IFCONFIG_POOL_MAX);
+ ALLOC_OBJ_CLEAR (pool, struct ifconfig_pool);
+
+ pool->type = type;
+ pool->duplicate_cn = duplicate_cn;
+
+ switch (type)
+ {
+ case IFCONFIG_POOL_30NET:
+ pool->base = start & ~3;
+ pool->size = (((end | 3) + 1) - pool->base) >> 2;
+ break;
+ case IFCONFIG_POOL_INDIV:
+ pool->base = start;
+ pool->size = end - start + 1;
+ break;
+ default:
+ ASSERT (0);
+ }
+
+ ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
+
+ msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
+ print_in_addr_t (pool->base, 0, &gc),
+ pool->size);
+
+ gc_free (&gc);
+ return pool;
+}
+
+void
+ifconfig_pool_free (struct ifconfig_pool *pool)
+{
+ if (pool)
+ {
+ int i;
+ for (i = 0; i < pool->size; ++i)
+ ifconfig_pool_entry_free (&pool->list[i], true);
+ free (pool->list);
+ free (pool);
+ }
+}
+
+ifconfig_pool_handle
+ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
+{
+ int i;
+
+ i = ifconfig_pool_find (pool, common_name);
+ if (i >= 0)
+ {
+ struct ifconfig_pool_entry *ipe = &pool->list[i];
+ ASSERT (!ipe->in_use);
+ ifconfig_pool_entry_free (ipe, true);
+ ipe->in_use = true;
+ if (common_name)
+ ipe->common_name = string_alloc (common_name, NULL);
+
+ switch (pool->type)
+ {
+ case IFCONFIG_POOL_30NET:
+ {
+ in_addr_t b = pool->base + (i << 2);
+ *local = b + 1;
+ *remote = b + 2;
+ break;
+ }
+ case IFCONFIG_POOL_INDIV:
+ {
+ in_addr_t b = pool->base + i;
+ *local = 0;
+ *remote = b;
+ break;
+ }
+ default:
+ ASSERT (0);
+ }
+ }
+ return i;
+}
+
+bool
+ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard)
+{
+ bool ret = false;
+ if (pool && hand >= 0 && hand < pool->size)
+ {
+ ifconfig_pool_entry_free (&pool->list[hand], hard);
+ ret = true;
+ }
+ return ret;
+}
+
+/*
+ * private access functions
+ */
+
+static ifconfig_pool_handle
+ifconfig_pool_ip_base_to_handle (const struct ifconfig_pool* pool, const in_addr_t addr)
+{
+ ifconfig_pool_handle ret = -1;
+
+ switch (pool->type)
+ {
+ case IFCONFIG_POOL_30NET:
+ {
+ ret = (addr - pool->base) >> 2;
+ break;
+ }
+ case IFCONFIG_POOL_INDIV:
+ {
+ ret = (addr - pool->base);
+ break;
+ }
+ default:
+ ASSERT (0);
+ }
+
+ if (ret < 0 || ret >= pool->size)
+ ret = -1;
+
+ return ret;
+}
+
+static in_addr_t
+ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
+{
+ in_addr_t ret = 0;
+
+ if (hand >= 0 && hand < pool->size)
+ {
+ switch (pool->type)
+ {
+ case IFCONFIG_POOL_30NET:
+ {
+ ret = pool->base + (hand << 2);;
+ break;
+ }
+ case IFCONFIG_POOL_INDIV:
+ {
+ ret = pool->base + hand;
+ break;
+ }
+ default:
+ ASSERT (0);
+ }
+ }
+
+ return ret;
+}
+
+static void
+ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
+{
+ ifconfig_pool_handle h = ifconfig_pool_ip_base_to_handle (pool, addr);
+ if (h >= 0)
+ {
+ struct ifconfig_pool_entry *e = &pool->list[h];
+ ifconfig_pool_entry_free (e, true);
+ e->in_use = false;
+ e->common_name = string_alloc (cn, NULL);
+ e->last_release = now;
+ e->fixed = fixed;
+ }
+}
+
+static void
+ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out)
+{
+ if (pool && out)
+ {
+ struct gc_arena gc = gc_new ();
+ int i;
+
+ for (i = 0; i < pool->size; ++i)
+ {
+ const struct ifconfig_pool_entry *e = &pool->list[i];
+ if (e->common_name)
+ {
+ const in_addr_t ip = ifconfig_pool_handle_to_ip_base (pool, i);
+ status_printf (out, "%s,%s",
+ e->common_name,
+ print_in_addr_t (ip, 0, &gc));
+ }
+ }
+ gc_free (&gc);
+ }
+}
+
+static void
+ifconfig_pool_msg (const struct ifconfig_pool* pool, int msglevel)
+{
+ struct status_output *so = status_open (NULL, 0, msglevel, NULL, 0);
+ ASSERT (so);
+ status_printf (so, "IFCONFIG POOL LIST");
+ ifconfig_pool_list (pool, so);
+ status_close (so);
+}
+
+/*
+ * Deal with reading/writing the ifconfig pool database to a file
+ */
+
+struct ifconfig_pool_persist *
+ifconfig_pool_persist_init (const char *filename, int refresh_freq)
+{
+ struct ifconfig_pool_persist *ret;
+
+ ASSERT (filename);
+
+ ALLOC_OBJ_CLEAR (ret, struct ifconfig_pool_persist);
+ if (refresh_freq > 0)
+ {
+ ret->fixed = false;
+ ret->file = status_open (filename, refresh_freq, -1, NULL, STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE);
+ }
+ else
+ {
+ ret->fixed = true;
+ ret->file = status_open (filename, 0, -1, NULL, STATUS_OUTPUT_READ);
+ }
+ return ret;
+}
+
+void
+ifconfig_pool_persist_close (struct ifconfig_pool_persist *persist)
+{
+ if (persist)
+ {
+ if (persist->file)
+ status_close (persist->file);
+ free (persist);
+ }
+}
+
+bool
+ifconfig_pool_write_trigger (struct ifconfig_pool_persist *persist)
+{
+ if (persist->file)
+ return status_trigger (persist->file);
+ else
+ return false;
+}
+
+void
+ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool *pool)
+{
+ const int buf_size = 128;
+
+ update_time ();
+ if (persist && persist->file && pool)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer in = alloc_buf_gc (256, &gc);
+ char *cn_buf;
+ char *ip_buf;
+ int line = 0;
+
+ ALLOC_ARRAY_CLEAR_GC (cn_buf, char, buf_size, &gc);
+ ALLOC_ARRAY_CLEAR_GC (ip_buf, char, buf_size, &gc);
+
+ while (true)
+ {
+ ASSERT (buf_init (&in, 0));
+ if (!status_read (persist->file, &in))
+ break;
+ ++line;
+ if (BLEN (&in))
+ {
+ int c = *BSTR(&in);
+ if (c == '#' || c == ';')
+ continue;
+ if (buf_parse (&in, ',', cn_buf, buf_size)
+ && buf_parse (&in, ',', ip_buf, buf_size))
+ {
+ bool succeeded;
+ const in_addr_t addr = getaddr (GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
+ if (succeeded)
+ {
+ ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
+ }
+ }
+ }
+ }
+
+ ifconfig_pool_msg (pool, D_IFCONFIG_POOL);
+
+ gc_free (&gc);
+ }
+}
+
+void
+ifconfig_pool_write (struct ifconfig_pool_persist *persist, const struct ifconfig_pool *pool)
+{
+ if (persist && persist->file && (status_rw_flags (persist->file) & STATUS_OUTPUT_WRITE) && pool)
+ {
+ status_reset (persist->file);
+ ifconfig_pool_list (pool, persist->file);
+ status_flush (persist->file);
+ }
+}
+
+/*
+ * TESTING ONLY
+ */
+
+#ifdef IFCONFIG_POOL_TEST
+
+#define DUP_CN
+
+void
+ifconfig_pool_test (in_addr_t start, in_addr_t end)
+{
+ struct gc_arena gc = gc_new ();
+ struct ifconfig_pool *p = ifconfig_pool_init (IFCONFIG_POOL_30NET, start, end);
+ /*struct ifconfig_pool *p = ifconfig_pool_init (IFCONFIG_POOL_INDIV, start, end);*/
+ ifconfig_pool_handle array[256];
+ int i;
+
+ CLEAR (array);
+
+ msg (M_INFO | M_NOPREFIX, "************ 1");
+ for (i = 0; i < (int) SIZE (array); ++i)
+ {
+ char *cn;
+ ifconfig_pool_handle h;
+ in_addr_t local, remote;
+ char buf[256];
+ openvpn_snprintf (buf, sizeof(buf), "common-name-%d", i);
+#ifdef DUP_CN
+ cn = NULL;
+#else
+ cn = buf;
+#endif
+ h = ifconfig_pool_acquire (p, &local, &remote, cn);
+ if (h < 0)
+ break;
+ msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote, 0, &gc),
+ cn);
+ array[i] = h;
+
+ }
+
+ msg (M_INFO | M_NOPREFIX, "************* 2");
+ for (i = (int) SIZE (array) / 16; i < (int) SIZE (array) / 8; ++i)
+ {
+ msg (M_INFO, "Attempt to release %d cn=%s", array[i], p->list[i].common_name);
+ if (!ifconfig_pool_release (p, array[i]))
+ break;
+ msg (M_INFO, "Succeeded");
+ }
+
+ CLEAR (array);
+
+ msg (M_INFO | M_NOPREFIX, "**************** 3");
+ for (i = 0; i < (int) SIZE (array); ++i)
+ {
+ char *cn;
+ ifconfig_pool_handle h;
+ in_addr_t local, remote;
+ char buf[256];
+ snprintf (buf, sizeof(buf), "common-name-%d", i+24);
+#ifdef DUP_CN
+ cn = NULL;
+#else
+ cn = buf;
+#endif
+ h = ifconfig_pool_acquire (p, &local, &remote, cn);
+ if (h < 0)
+ break;
+ msg (M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote, 0, &gc),
+ cn);
+ array[i] = h;
+
+ }
+
+ ifconfig_pool_free (p);
+ gc_free (&gc);
+}
+
+#endif
+
+#endif
diff --git a/pool.h b/pool.h
new file mode 100644
index 0000000..81264a9
--- /dev/null
+++ b/pool.h
@@ -0,0 +1,88 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef POOL_H
+#define POOL_H
+
+#if P2MP
+
+/*#define IFCONFIG_POOL_TEST*/
+
+#include "basic.h"
+#include "status.h"
+
+#define IFCONFIG_POOL_MAX 65536
+#define IFCONFIG_POOL_MIN_NETBITS 16
+
+#define IFCONFIG_POOL_30NET 0
+#define IFCONFIG_POOL_INDIV 1
+
+struct ifconfig_pool_entry
+{
+ bool in_use;
+ char *common_name;
+ time_t last_release;
+ bool fixed;
+};
+
+struct ifconfig_pool
+{
+ in_addr_t base;
+ int size;
+ int type;
+ bool duplicate_cn;
+ struct ifconfig_pool_entry *list;
+};
+
+struct ifconfig_pool_persist
+{
+ struct status_output *file;
+ bool fixed;
+};
+
+typedef int ifconfig_pool_handle;
+
+struct ifconfig_pool *ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn);
+
+void ifconfig_pool_free (struct ifconfig_pool *pool);
+
+bool ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end);
+
+ifconfig_pool_handle ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name);
+
+bool ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard);
+
+struct ifconfig_pool_persist *ifconfig_pool_persist_init (const char *filename, int refresh_freq);
+void ifconfig_pool_persist_close (struct ifconfig_pool_persist *persist);
+bool ifconfig_pool_write_trigger (struct ifconfig_pool_persist *persist);
+
+void ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool *pool);
+void ifconfig_pool_write (struct ifconfig_pool_persist *persist, const struct ifconfig_pool *pool);
+
+#ifdef IFCONFIG_POOL_TEST
+void ifconfig_pool_test (in_addr_t start, in_addr_t end);
+#endif
+
+#endif
+#endif
diff --git a/proto.c b/proto.c
new file mode 100644
index 0000000..65a6b67
--- /dev/null
+++ b/proto.c
@@ -0,0 +1,122 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "proto.h"
+#include "error.h"
+
+#include "memdbg.h"
+
+/*
+ * If raw tunnel packet is IPv4, return true and increment
+ * buffer offset to start of IP header.
+ */
+bool
+is_ipv4 (int tunnel_type, struct buffer *buf)
+{
+ int offset;
+ const struct openvpn_iphdr *ih;
+
+ verify_align_4 (buf);
+ if (tunnel_type == DEV_TYPE_TUN)
+ {
+ if (BLEN (buf) < (int) sizeof (struct openvpn_iphdr))
+ return false;
+ offset = 0;
+ }
+ else if (tunnel_type == DEV_TYPE_TAP)
+ {
+ const struct openvpn_ethhdr *eh;
+ if (BLEN (buf) < (int)(sizeof (struct openvpn_ethhdr)
+ + sizeof (struct openvpn_iphdr)))
+ return false;
+ eh = (const struct openvpn_ethhdr *) BPTR (buf);
+ if (ntohs (eh->proto) != OPENVPN_ETH_P_IPV4)
+ return false;
+ offset = sizeof (struct openvpn_ethhdr);
+ }
+ else
+ return false;
+
+ ih = (const struct openvpn_iphdr *) (BPTR (buf) + offset);
+
+ if (OPENVPN_IPH_GET_VER (ih->version_len) == 4)
+ return buf_advance (buf, offset);
+ else
+ return false;
+}
+
+#ifdef PACKET_TRUNCATION_CHECK
+
+void
+ipv4_packet_size_verify (const uint8_t *data,
+ const int size,
+ const int tunnel_type,
+ const char *prefix,
+ counter_type *errors)
+{
+ if (size > 0)
+ {
+ struct buffer buf;
+
+ buf_set_read (&buf, data, size);
+
+ if (is_ipv4 (tunnel_type, &buf))
+ {
+ const struct openvpn_iphdr *pip;
+ int hlen;
+ int totlen;
+ const char *msgstr = "PACKET SIZE INFO";
+ unsigned int msglevel = D_PACKET_TRUNC_DEBUG;
+
+ if (BLEN (&buf) < (int) sizeof (struct openvpn_iphdr))
+ return;
+
+ verify_align_4 (&buf);
+ pip = (struct openvpn_iphdr *) BPTR (&buf);
+
+ hlen = OPENVPN_IPH_GET_LEN (pip->version_len);
+ totlen = ntohs (pip->tot_len);
+
+ if (BLEN (&buf) != totlen)
+ {
+ msgstr = "PACKET TRUNCATION ERROR";
+ msglevel = D_PACKET_TRUNC_ERR;
+ if (errors)
+ ++(*errors);
+ }
+
+ msg (msglevel, "%s %s: size=%d totlen=%d hlen=%d errcount=" counter_format,
+ msgstr,
+ prefix,
+ BLEN (&buf),
+ totlen,
+ hlen,
+ errors ? *errors : (counter_type)0);
+ }
+ }
+}
+
+#endif
diff --git a/proto.h b/proto.h
new file mode 100644
index 0000000..55f0832
--- /dev/null
+++ b/proto.h
@@ -0,0 +1,202 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PROTO_H
+#define PROTO_H
+
+#include "common.h"
+#include "buffer.h"
+
+#pragma pack(1)
+
+/*
+ * Tunnel types
+ */
+#define DEV_TYPE_UNDEF 0
+#define DEV_TYPE_NULL 1
+#define DEV_TYPE_TUN 2 /* point-to-point IP tunnel */
+#define DEV_TYPE_TAP 3 /* ethernet (802.3) tunnel */
+
+/* TUN topologies */
+
+#define TOP_UNDEF 0
+#define TOP_NET30 1
+#define TOP_P2P 2
+#define TOP_SUBNET 3
+
+/*
+ * IP and Ethernet protocol structs. For portability,
+ * OpenVPN needs its own definitions of these structs, and
+ * names have been adjusted to avoid collisions with
+ * native structs.
+ */
+
+#define OPENVPN_ETH_ALEN 6 /* ethernet address length */
+struct openvpn_ethhdr
+{
+ uint8_t dest[OPENVPN_ETH_ALEN]; /* destination ethernet addr */
+ uint8_t source[OPENVPN_ETH_ALEN]; /* source ethernet addr */
+
+# define OPENVPN_ETH_P_IPV4 0x0800 /* IPv4 protocol */
+# define OPENVPN_ETH_P_IPV6 0x86DD /* IPv6 protocol */
+# define OPENVPN_ETH_P_ARP 0x0806 /* ARP protocol */
+ uint16_t proto; /* packet type ID field */
+};
+
+struct openvpn_arp {
+# define ARP_MAC_ADDR_TYPE 0x0001
+ uint16_t mac_addr_type; /* 0x0001 */
+
+ uint16_t proto_addr_type; /* 0x0800 */
+ uint8_t mac_addr_size; /* 0x06 */
+ uint8_t proto_addr_size; /* 0x04 */
+
+# define ARP_REQUEST 0x0001
+# define ARP_REPLY 0x0002
+ uint16_t arp_command; /* 0x0001 for ARP request, 0x0002 for ARP reply */
+
+ uint8_t mac_src[OPENVPN_ETH_ALEN];
+ in_addr_t ip_src;
+ uint8_t mac_dest[OPENVPN_ETH_ALEN];
+ in_addr_t ip_dest;
+};
+
+struct openvpn_iphdr {
+# define OPENVPN_IPH_GET_VER(v) (((v) >> 4) & 0x0F)
+# define OPENVPN_IPH_GET_LEN(v) (((v) & 0x0F) << 2)
+ uint8_t version_len;
+
+ uint8_t tos;
+ uint16_t tot_len;
+ uint16_t id;
+
+# define OPENVPN_IP_OFFMASK 0x1fff
+ uint16_t frag_off;
+
+ uint8_t ttl;
+
+# define OPENVPN_IPPROTO_IGMP 2 /* IGMP protocol */
+# define OPENVPN_IPPROTO_TCP 6 /* TCP protocol */
+# define OPENVPN_IPPROTO_UDP 17 /* UDP protocol */
+ uint8_t protocol;
+
+ uint16_t check;
+ uint32_t saddr;
+ uint32_t daddr;
+ /*The options start here. */
+};
+
+/*
+ * UDP header
+ */
+struct openvpn_udphdr {
+ uint16_t source;
+ uint16_t dest;
+ uint16_t len;
+ uint16_t check;
+};
+
+/*
+ * TCP header, per RFC 793.
+ */
+struct openvpn_tcphdr {
+ uint16_t source; /* source port */
+ uint16_t dest; /* destination port */
+ uint32_t seq; /* sequence number */
+ uint32_t ack_seq; /* acknowledgement number */
+
+# define OPENVPN_TCPH_GET_DOFF(d) (((d) & 0xF0) >> 2)
+ uint8_t doff_res;
+
+# define OPENVPN_TCPH_FIN_MASK (1<<0)
+# define OPENVPN_TCPH_SYN_MASK (1<<1)
+# define OPENVPN_TCPH_RST_MASK (1<<2)
+# define OPENVPN_TCPH_PSH_MASK (1<<3)
+# define OPENVPN_TCPH_ACK_MASK (1<<4)
+# define OPENVPN_TCPH_URG_MASK (1<<5)
+# define OPENVPN_TCPH_ECE_MASK (1<<6)
+# define OPENVPN_TCPH_CWR_MASK (1<<7)
+ uint8_t flags;
+
+ uint16_t window;
+ uint16_t check;
+ uint16_t urg_ptr;
+};
+
+#define OPENVPN_TCPOPT_EOL 0
+#define OPENVPN_TCPOPT_NOP 1
+#define OPENVPN_TCPOPT_MAXSEG 2
+#define OPENVPN_TCPOLEN_MAXSEG 4
+
+#pragma pack()
+
+/*
+ * The following macro is used to update an
+ * internet checksum. "acc" is a 32-bit
+ * accumulation of all the changes to the
+ * checksum (adding in old 16-bit words and
+ * subtracting out new words), and "cksum"
+ * is the checksum value to be updated.
+ */
+#define ADJUST_CHECKSUM(acc, cksum) { \
+ (acc) += (cksum); \
+ if ((acc) < 0) { \
+ (acc) = -(acc); \
+ (acc) = ((acc) >> 16) + ((acc) & 0xffff); \
+ (acc) += (acc) >> 16; \
+ (cksum) = (uint16_t) ~(acc); \
+ } else { \
+ (acc) = ((acc) >> 16) + ((acc) & 0xffff); \
+ (acc) += (acc) >> 16; \
+ (cksum) = (uint16_t) (acc); \
+ } \
+}
+
+/*
+ * We are in a "liberal" position with respect to MSS,
+ * i.e. we assume that MSS can be calculated from MTU
+ * by subtracting out only the IP and TCP header sizes
+ * without options.
+ *
+ * (RFC 879, section 7).
+ */
+#define MTU_TO_MSS(mtu) (mtu - sizeof(struct openvpn_iphdr) \
+ - sizeof(struct openvpn_tcphdr))
+
+/*
+ * If raw tunnel packet is IPv4, return true and increment
+ * buffer offset to start of IP header.
+ */
+bool is_ipv4 (int tunnel_type, struct buffer *buf);
+
+#ifdef PACKET_TRUNCATION_CHECK
+void ipv4_packet_size_verify (const uint8_t *data,
+ const int size,
+ const int tunnel_type,
+ const char
+ *prefix,
+ counter_type *errors);
+#endif
+
+#endif
diff --git a/proxy.c b/proxy.c
new file mode 100644
index 0000000..fce64a1
--- /dev/null
+++ b/proxy.c
@@ -0,0 +1,1129 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "common.h"
+#include "misc.h"
+#include "crypto.h"
+#include "win32.h"
+#include "socket.h"
+#include "fdmisc.h"
+#include "proxy.h"
+#include "base64.h"
+#include "httpdigest.h"
+#include "ntlm.h"
+
+#ifdef WIN32
+#include "ieproxy.h"
+#endif
+
+#include "memdbg.h"
+
+#ifdef ENABLE_HTTP_PROXY
+
+#define UP_TYPE_PROXY "HTTP Proxy"
+
+/* cached proxy username/password */
+static struct user_pass static_proxy_user_pass;
+
+static bool
+recv_line (socket_descriptor_t sd,
+ char *buf,
+ int len,
+ const int timeout_sec,
+ const bool verbose,
+ struct buffer *lookahead,
+ volatile int *signal_received)
+{
+ struct buffer la;
+ int lastc = 0;
+
+ CLEAR (la);
+ if (lookahead)
+ la = *lookahead;
+
+ while (true)
+ {
+ int status;
+ ssize_t size;
+ fd_set reads;
+ struct timeval tv;
+ uint8_t c;
+
+ if (buf_defined (&la))
+ {
+ ASSERT (buf_init (&la, 0));
+ }
+
+ FD_ZERO (&reads);
+ FD_SET (sd, &reads);
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, &reads, NULL, NULL, &tv);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ goto error;
+
+ /* timeout? */
+ if (status == 0)
+ {
+ if (verbose)
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read timeout expired");
+ goto error;
+ }
+
+ /* error */
+ if (status < 0)
+ {
+ if (verbose)
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read failed on select()");
+ goto error;
+ }
+
+ /* read single char */
+ size = recv (sd, &c, 1, MSG_NOSIGNAL);
+
+ /* error? */
+ if (size != 1)
+ {
+ if (verbose)
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: TCP port read failed on recv()");
+ goto error;
+ }
+
+#if 0
+ if (isprint(c))
+ msg (M_INFO, "PROXY: read '%c' (%d)", c, (int)c);
+ else
+ msg (M_INFO, "PROXY: read (%d)", (int)c);
+#endif
+
+ /* store char in buffer */
+ if (len > 1)
+ {
+ *buf++ = c;
+ --len;
+ }
+
+ /* also store char in lookahead buffer */
+ if (buf_defined (&la))
+ {
+ buf_write_u8 (&la, c);
+ if (!isprint(c) && !isspace(c)) /* not ascii? */
+ {
+ if (verbose)
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_line: Non-ASCII character (%d) read on recv()", (int)c);
+ *lookahead = la;
+ return false;
+ }
+ }
+
+ /* end of line? */
+ if (lastc == '\r' && c == '\n')
+ break;
+
+ lastc = c;
+ }
+
+ /* append trailing null */
+ if (len > 0)
+ *buf++ = '\0';
+
+ return true;
+
+ error:
+ return false;
+}
+
+static bool
+send_line (socket_descriptor_t sd,
+ const char *buf)
+{
+ const ssize_t size = send (sd, buf, strlen (buf), MSG_NOSIGNAL);
+ if (size != (ssize_t) strlen (buf))
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "send_line: TCP port write failed on send()");
+ return false;
+ }
+ return true;
+}
+
+static bool
+send_line_crlf (socket_descriptor_t sd,
+ const char *src)
+{
+ bool ret;
+
+ struct buffer buf = alloc_buf (strlen (src) + 3);
+ ASSERT (buf_write (&buf, src, strlen (src)));
+ ASSERT (buf_write (&buf, "\r\n", 3));
+ ret = send_line (sd, BSTR (&buf));
+ free_buf (&buf);
+ return ret;
+}
+
+static bool
+send_crlf (socket_descriptor_t sd)
+{
+ return send_line_crlf (sd, "");
+}
+
+uint8_t *
+make_base64_string2 (const uint8_t *str, int src_len, struct gc_arena *gc)
+{
+ uint8_t *ret = NULL;
+ char *b64out = NULL;
+ ASSERT (base64_encode ((const void *)str, src_len, &b64out) >= 0);
+ ret = (uint8_t *) string_alloc (b64out, gc);
+ free (b64out);
+ return ret;
+}
+
+uint8_t *
+make_base64_string (const uint8_t *str, struct gc_arena *gc)
+{
+ return make_base64_string2 (str, strlen ((const char *)str), gc);
+}
+
+static const char *
+username_password_as_base64 (const struct http_proxy_info *p,
+ struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (strlen (p->up.username) + strlen (p->up.password) + 2, gc);
+ ASSERT (strlen (p->up.username) > 0);
+ buf_printf (&out, "%s:%s", p->up.username, p->up.password);
+ return (const char *)make_base64_string ((const uint8_t*)BSTR (&out), gc);
+}
+
+static void
+get_user_pass_http (struct http_proxy_info *p, const bool force)
+{
+ if (!static_proxy_user_pass.defined || force)
+ {
+ unsigned int flags = GET_USER_PASS_MANAGEMENT;
+ if (p->queried_creds)
+ flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED;
+ get_user_pass (&static_proxy_user_pass,
+ p->options.auth_file,
+ UP_TYPE_PROXY,
+ flags);
+ p->queried_creds = true;
+ p->up = static_proxy_user_pass;
+ }
+}
+static void
+clear_user_pass_http (void)
+{
+ purge_user_pass (&static_proxy_user_pass, true);
+}
+
+static void
+dump_residual (socket_descriptor_t sd,
+ int timeout,
+ volatile int *signal_received)
+{
+ char buf[256];
+ while (true)
+ {
+ if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received))
+ return;
+ chomp (buf);
+ msg (D_PROXY, "PROXY HEADER: '%s'", buf);
+ }
+}
+
+/*
+ * Extract the Proxy-Authenticate header from the stream.
+ * Consumes all headers.
+ */
+static int
+get_proxy_authenticate (socket_descriptor_t sd,
+ int timeout,
+ char **data,
+ struct gc_arena *gc,
+ volatile int *signal_received)
+{
+ char buf[256];
+ int ret = HTTP_AUTH_NONE;
+ while (true)
+ {
+ if (!recv_line (sd, buf, sizeof (buf), timeout, true, NULL, signal_received))
+ {
+ *data = NULL;
+ return HTTP_AUTH_NONE;
+ }
+ chomp (buf);
+ if (!strlen(buf))
+ return ret;
+ if (ret == HTTP_AUTH_NONE && !strncmp(buf, "Proxy-Authenticate: ", 20))
+ {
+ if (!strncmp(buf+20, "Basic ", 6))
+ {
+ msg (D_PROXY, "PROXY AUTH BASIC: '%s'", buf);
+ *data = string_alloc(buf+26, gc);
+ ret = HTTP_AUTH_BASIC;
+ }
+#if PROXY_DIGEST_AUTH
+ else if (!strncmp(buf+20, "Digest ", 7))
+ {
+ msg (D_PROXY, "PROXY AUTH DIGEST: '%s'", buf);
+ *data = string_alloc(buf+27, gc);
+ ret = HTTP_AUTH_DIGEST;
+ }
+#endif
+#if NTLM
+ else if (!strncmp(buf+20, "NTLM", 4))
+ {
+ msg (D_PROXY, "PROXY AUTH HTLM: '%s'", buf);
+ *data = NULL;
+ ret = HTTP_AUTH_NTLM;
+ }
+#endif
+ }
+ }
+}
+
+static void
+store_proxy_authenticate (struct http_proxy_info *p, char *data)
+{
+ if (p->proxy_authenticate)
+ free (p->proxy_authenticate);
+ p->proxy_authenticate = data;
+}
+
+/*
+ * Parse out key/value pairs from Proxy-Authenticate string.
+ * Return true on success, or false on parse failure.
+ */
+static bool
+get_key_value(const char *str, /* source string */
+ char *key, /* key stored here */
+ char *value, /* value stored here */
+ int max_key_len,
+ int max_value_len,
+ const char **endptr) /* next search position */
+{
+ int c;
+ bool starts_with_quote = false;
+ bool escape = false;
+
+ for (c = max_key_len-1; (*str && (*str != '=') && c--); )
+ *key++ = *str++;
+ *key = '\0';
+
+ if('=' != *str++)
+ /* no key/value found */
+ return false;
+
+ if('\"' == *str)
+ {
+ /* quoted string */
+ str++;
+ starts_with_quote = true;
+ }
+
+ for (c = max_value_len-1; *str && c--; str++)
+ {
+ switch (*str)
+ {
+ case '\\':
+ if (!escape)
+ {
+ /* possibly the start of an escaped quote */
+ escape = true;
+ *value++ = '\\'; /* even though this is an escape character, we still
+ store it as-is in the target buffer */
+ continue;
+ }
+ break;
+ case ',':
+ if (!starts_with_quote)
+ {
+ /* this signals the end of the value if we didn't get a starting quote
+ and then we do "sloppy" parsing */
+ c=0; /* the end */
+ continue;
+ }
+ break;
+ case '\r':
+ case '\n':
+ /* end of string */
+ c=0;
+ continue;
+ case '\"':
+ if (!escape && starts_with_quote)
+ {
+ /* end of string */
+ c=0;
+ continue;
+ }
+ break;
+ }
+ escape = false;
+ *value++ = *str;
+ }
+ *value = '\0';
+
+ *endptr = str;
+
+ return true; /* success */
+}
+
+static char *
+get_pa_var (const char *key, const char *pa, struct gc_arena *gc)
+{
+ char k[64];
+ char v[256];
+ const char *content = pa;
+
+ while (true)
+ {
+ const int status = get_key_value(content, k, v, sizeof(k), sizeof(v), &content);
+ if (status)
+ {
+ if (!strcmp(key, k))
+ return string_alloc(v, gc);
+ }
+ else
+ return NULL;
+
+ /* advance to start of next key */
+ if (*content == ',')
+ ++content;
+ while (*content && isspace(*content))
+ ++content;
+ }
+}
+
+struct http_proxy_info *
+http_proxy_new (const struct http_proxy_options *o,
+ struct auto_proxy_info *auto_proxy_info)
+{
+ struct http_proxy_info *p;
+ struct http_proxy_options opt;
+
+ if (auto_proxy_info)
+ {
+ if (o && o->server)
+ {
+ /* if --http-proxy explicitly given, disable auto-proxy */
+ auto_proxy_info = NULL;
+ }
+ else
+ {
+ /* if no --http-proxy explicitly given and no auto settings, fail */
+ if (!auto_proxy_info->http.server)
+ return NULL;
+
+ if (o)
+ {
+ opt = *o;
+ }
+ else
+ {
+ CLEAR (opt);
+
+ /* These settings are only used for --auto-proxy */
+ opt.timeout = 5;
+ opt.http_version = "1.0";
+ }
+
+ opt.server = auto_proxy_info->http.server;
+ opt.port = auto_proxy_info->http.port;
+ if (!opt.auth_retry)
+ opt.auth_retry = PAR_ALL;
+
+ o = &opt;
+ }
+ }
+
+ if (!o || !o->server)
+ msg (M_FATAL, "HTTP_PROXY: server not specified");
+
+ ASSERT (legal_ipv4_port (o->port));
+
+ ALLOC_OBJ_CLEAR (p, struct http_proxy_info);
+ p->options = *o;
+
+ /* parse authentication method */
+ p->auth_method = HTTP_AUTH_NONE;
+ if (o->auth_method_string)
+ {
+ if (!strcmp (o->auth_method_string, "none"))
+ p->auth_method = HTTP_AUTH_NONE;
+ else if (!strcmp (o->auth_method_string, "basic"))
+ p->auth_method = HTTP_AUTH_BASIC;
+#if NTLM
+ else if (!strcmp (o->auth_method_string, "ntlm"))
+ p->auth_method = HTTP_AUTH_NTLM;
+ else if (!strcmp (o->auth_method_string, "ntlm2"))
+ p->auth_method = HTTP_AUTH_NTLM2;
+#endif
+ else
+ msg (M_FATAL, "ERROR: unknown HTTP authentication method: '%s'",
+ o->auth_method_string);
+ }
+
+ /* only basic and NTLM/NTLMv2 authentication supported so far */
+ if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
+ {
+ get_user_pass_http (p, true);
+ }
+
+#if !NTLM
+ if (p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2)
+ msg (M_FATAL, "Sorry, this version of " PACKAGE_NAME " was built without NTLM Proxy support.");
+#endif
+
+ p->defined = true;
+ return p;
+}
+
+void
+http_proxy_close (struct http_proxy_info *hp)
+{
+ free (hp);
+}
+
+bool
+establish_http_proxy_passthru (struct http_proxy_info *p,
+ socket_descriptor_t sd, /* already open to proxy */
+ const char *host, /* openvpn server remote */
+ const int port, /* openvpn server port */
+ struct buffer *lookahead,
+ volatile int *signal_received)
+{
+ struct gc_arena gc = gc_new ();
+ char buf[512];
+ char buf2[128];
+ char get[80];
+ int status;
+ int nparms;
+ bool ret = false;
+ bool processed = false;
+
+ /* get user/pass if not previously given or if --auto-proxy is being used */
+ if (p->auth_method == HTTP_AUTH_BASIC
+ || p->auth_method == HTTP_AUTH_DIGEST
+ || p->auth_method == HTTP_AUTH_NTLM)
+ get_user_pass_http (p, false);
+
+ /* are we being called again after getting the digest server nonce in the previous transaction? */
+ if (p->auth_method == HTTP_AUTH_DIGEST && p->proxy_authenticate)
+ {
+ nparms = 1;
+ status = 407;
+ }
+ else
+ {
+ /* format HTTP CONNECT message */
+ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s",
+ host,
+ port,
+ p->options.http_version);
+
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+
+ /* send HTTP CONNECT message to proxy */
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ openvpn_snprintf(buf, sizeof(buf), "Host: %s", host);
+ if (!send_line_crlf(sd, buf))
+ goto error;
+
+ /* send User-Agent string if provided */
+ if (p->options.user_agent)
+ {
+ openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s",
+ p->options.user_agent);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+ }
+
+ /* auth specified? */
+ switch (p->auth_method)
+ {
+ case HTTP_AUTH_NONE:
+ break;
+
+ case HTTP_AUTH_BASIC:
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s",
+ username_password_as_base64 (p, &gc));
+ msg (D_PROXY, "Attempting Basic Proxy-Authorization");
+ dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+ break;
+
+#if NTLM
+ case HTTP_AUTH_NTLM:
+ case HTTP_AUTH_NTLM2:
+ /* keep-alive connection */
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s",
+ ntlm_phase_1 (p, &gc));
+ msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1");
+ dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+ break;
+#endif
+
+ default:
+ ASSERT (0);
+ }
+
+ /* send empty CR, LF */
+ if (!send_crlf (sd))
+ goto error;
+
+ /* receive reply from proxy */
+ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
+ goto error;
+
+ /* remove trailing CR, LF */
+ chomp (buf);
+
+ msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
+
+ /* parse return string */
+ nparms = sscanf (buf, "%*s %d", &status);
+
+ }
+
+ /* check for a "407 Proxy Authentication Required" response */
+ while (nparms >= 1 && status == 407)
+ {
+ msg (D_PROXY, "Proxy requires authentication");
+
+ if (p->auth_method == HTTP_AUTH_BASIC && !processed)
+ {
+ processed = true;
+ }
+ else if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) /* check for NTLM */
+ {
+#if NTLM
+ /* look for the phase 2 response */
+
+ while (true)
+ {
+ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
+ goto error;
+ chomp (buf);
+ msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
+
+ openvpn_snprintf (get, sizeof get, "%%*s NTLM %%%ds", (int) sizeof (buf2) - 1);
+ nparms = sscanf (buf, get, buf2);
+ buf2[127] = 0; /* we only need the beginning - ensure it's null terminated. */
+
+ /* check for "Proxy-Authenticate: NTLM TlRM..." */
+ if (nparms == 1)
+ {
+ /* parse buf2 */
+ msg (D_PROXY, "auth string: '%s'", buf2);
+ break;
+ }
+ }
+ /* if we are here then auth string was got */
+ msg (D_PROXY, "Received NTLM Proxy-Authorization phase 2 response");
+
+ /* receive and discard everything else */
+ while (recv_line (sd, NULL, 0, 2, true, NULL, signal_received))
+ ;
+
+ /* now send the phase 3 reply */
+
+ /* format HTTP CONNECT message */
+ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s",
+ host,
+ port,
+ p->options.http_version);
+
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+
+ /* send HTTP CONNECT message to proxy */
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ /* keep-alive connection */
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive");
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+
+ /* send HOST etc, */
+ openvpn_snprintf (buf, sizeof(buf), "Host: %s", host);
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3");
+ {
+ const char *np3 = ntlm_phase_3 (p, buf2, &gc);
+ if (!np3)
+ {
+ msg (D_PROXY, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server");
+ goto error;
+ }
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", np3);
+ }
+
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+ /* ok so far... */
+ /* send empty CR, LF */
+ if (!send_crlf (sd))
+ goto error;
+
+ /* receive reply from proxy */
+ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
+ goto error;
+
+ /* remove trailing CR, LF */
+ chomp (buf);
+
+ msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
+
+ /* parse return string */
+ nparms = sscanf (buf, "%*s %d", &status);
+ processed = true;
+#endif
+ }
+#if PROXY_DIGEST_AUTH
+ else if (p->auth_method == HTTP_AUTH_DIGEST && !processed)
+ {
+ char *pa = p->proxy_authenticate;
+ const int method = p->auth_method;
+ ASSERT(pa);
+
+ if (method == HTTP_AUTH_DIGEST)
+ {
+ const char *http_method = "CONNECT";
+ const char *nonce_count = "00000001";
+ const char *qop = "auth";
+ const char *username = p->up.username;
+ const char *password = p->up.password;
+ char *opaque_kv = "";
+ char uri[128];
+ uint8_t cnonce_raw[8];
+ uint8_t *cnonce;
+ HASHHEX session_key;
+ HASHHEX response;
+
+ const char *realm = get_pa_var("realm", pa, &gc);
+ const char *nonce = get_pa_var("nonce", pa, &gc);
+ const char *algor = get_pa_var("algorithm", pa, &gc);
+ const char *opaque = get_pa_var("opaque", pa, &gc);
+
+ /* generate a client nonce */
+ ASSERT(RAND_bytes(cnonce_raw, sizeof(cnonce_raw)));
+ cnonce = make_base64_string2(cnonce_raw, sizeof(cnonce_raw), &gc);
+
+
+ /* build the digest response */
+ openvpn_snprintf (uri, sizeof(uri), "%s:%d",
+ host,
+ port);
+
+ if (opaque)
+ {
+ const int len = strlen(opaque)+16;
+ opaque_kv = gc_malloc(len, false, &gc);
+ openvpn_snprintf (opaque_kv, len, ", opaque=\"%s\"", opaque);
+ }
+
+ DigestCalcHA1(algor,
+ username,
+ realm,
+ password,
+ nonce,
+ (char *)cnonce,
+ session_key);
+ DigestCalcResponse(session_key,
+ nonce,
+ nonce_count,
+ (char *)cnonce,
+ qop,
+ http_method,
+ uri,
+ NULL,
+ response);
+
+ /* format HTTP CONNECT message */
+ openvpn_snprintf (buf, sizeof(buf), "%s %s HTTP/%s",
+ http_method,
+ uri,
+ p->options.http_version);
+
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+
+ /* send HTTP CONNECT message to proxy */
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ /* send HOST etc, */
+ openvpn_snprintf (buf, sizeof(buf), "Host: %s", host);
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+
+ /* send digest response */
+ openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=%s, nc=%s, cnonce=\"%s\", response=\"%s\"%s",
+ username,
+ realm,
+ nonce,
+ uri,
+ qop,
+ nonce_count,
+ cnonce,
+ response,
+ opaque_kv
+ );
+ msg (D_PROXY, "Send to HTTP proxy: '%s'", buf);
+ if (!send_line_crlf (sd, buf))
+ goto error;
+ if (!send_crlf (sd))
+ goto error;
+
+ /* receive reply from proxy */
+ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received))
+ goto error;
+
+ /* remove trailing CR, LF */
+ chomp (buf);
+
+ msg (D_PROXY, "HTTP proxy returned: '%s'", buf);
+
+ /* parse return string */
+ nparms = sscanf (buf, "%*s %d", &status);
+ processed = true;
+ }
+ else
+ {
+ msg (D_PROXY, "HTTP proxy: digest method not supported");
+ goto error;
+ }
+ }
+#endif
+ else if (p->options.auth_retry)
+ {
+ /* figure out what kind of authentication the proxy needs */
+ char *pa = NULL;
+ const int method = get_proxy_authenticate(sd,
+ p->options.timeout,
+ &pa,
+ NULL,
+ signal_received);
+ if (method != HTTP_AUTH_NONE)
+ {
+ if (pa)
+ msg (D_PROXY, "HTTP proxy authenticate '%s'", pa);
+ if (p->options.auth_retry == PAR_NCT && method == HTTP_AUTH_BASIC)
+ {
+ msg (D_PROXY, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled");
+ goto error;
+ }
+ p->auth_method = method;
+ store_proxy_authenticate(p, pa);
+ ret = true;
+ goto done;
+ }
+ else
+ {
+ msg (D_PROXY, "HTTP proxy: do not recognize the authentication method required by proxy");
+ free (pa);
+ goto error;
+ }
+ }
+ else
+ {
+ if (!processed)
+ msg (D_PROXY, "HTTP proxy: no support for proxy authentication method");
+ goto error;
+ }
+
+ /* clear state */
+ if (p->options.auth_retry)
+ clear_user_pass_http();
+ store_proxy_authenticate(p, NULL);
+ }
+
+ /* check return code, success = 200 */
+ if (nparms < 1 || status != 200)
+ {
+ msg (D_LINK_ERRORS, "HTTP proxy returned bad status");
+#if 0
+ /* DEBUGGING -- show a multi-line HTTP error response */
+ dump_residual(sd, p->options.timeout, signal_received);
+#endif
+ goto error;
+ }
+
+ /* SUCCESS */
+
+ /* receive line from proxy and discard */
+ if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received))
+ goto error;
+
+ /*
+ * Toss out any extraneous chars, but don't throw away the
+ * start of the OpenVPN data stream (put it in lookahead).
+ */
+ while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received))
+ ;
+
+ /* reset queried_creds so that we don't think that the next creds request is due to an auth error */
+ p->queried_creds = false;
+
+#if 0
+ if (lookahead && BLEN (lookahead))
+ msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0));
+#endif
+
+ done:
+ gc_free (&gc);
+ return ret;
+
+ error:
+ /* on error, should we exit or restart? */
+ if (!*signal_received)
+ *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */
+ gc_free (&gc);
+ return ret;
+}
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_HTTP_PROXY */
+
+#ifdef GENERAL_PROXY_SUPPORT
+
+#ifdef WIN32
+
+#if 0
+char *
+get_windows_internet_string (const DWORD dwOption, struct gc_arena *gc)
+{
+ DWORD size = 0;
+ char *ret = NULL;
+
+ /* Initially, get size of return buffer */
+ InternetQueryOption (NULL, dwOption, NULL, &size);
+ if (size)
+ {
+ /* Now get actual info */
+ ret = (INTERNET_PROXY_INFO *) gc_malloc (size, false, gc);
+ if (!InternetQueryOption (NULL, dwOption, (LPVOID) ret, &size))
+ ret = NULL;
+ }
+ return ret;
+}
+#endif
+
+static INTERNET_PROXY_INFO *
+get_windows_proxy_settings (struct gc_arena *gc)
+{
+ DWORD size = 0;
+ INTERNET_PROXY_INFO *ret = NULL;
+
+ /* Initially, get size of return buffer */
+ InternetQueryOption (NULL, INTERNET_OPTION_PROXY, NULL, &size);
+ if (size)
+ {
+ /* Now get actual info */
+ ret = (INTERNET_PROXY_INFO *) gc_malloc (size, false, gc);
+ if (!InternetQueryOption (NULL, INTERNET_OPTION_PROXY, (LPVOID) ret, &size))
+ ret = NULL;
+ }
+ return ret;
+}
+
+static const char *
+parse_windows_proxy_setting (const char *str, struct auto_proxy_info_entry *e, struct gc_arena *gc)
+{
+ char buf[128];
+ const char *ret = NULL;
+ struct buffer in;
+
+ CLEAR (*e);
+
+ buf_set_read (&in, (const uint8_t *)str, strlen (str));
+
+ if (strchr (str, '=') != NULL)
+ {
+ if (buf_parse (&in, '=', buf, sizeof (buf)))
+ ret = string_alloc (buf, gc);
+ }
+
+ if (buf_parse (&in, ':', buf, sizeof (buf)))
+ e->server = string_alloc (buf, gc);
+
+ if (e->server && buf_parse (&in, '\0', buf, sizeof (buf)))
+ e->port = atoi (buf);
+
+ return ret;
+}
+
+static void
+parse_windows_proxy_setting_list (const char *str, const char *type, struct auto_proxy_info_entry *e, struct gc_arena *gc)
+{
+ struct gc_arena gc_local = gc_new ();
+ struct auto_proxy_info_entry el;
+
+ CLEAR (*e);
+ if (type)
+ {
+ char buf[128];
+ struct buffer in;
+
+ buf_set_read (&in, (const uint8_t *)str, strlen (str));
+ if (strchr (str, '=') != NULL)
+ {
+ while (buf_parse (&in, ' ', buf, sizeof (buf)))
+ {
+ const char *t = parse_windows_proxy_setting (buf, &el, &gc_local);
+ if (t && !strcmp (t, type))
+ goto found;
+ }
+ }
+ }
+ else
+ {
+ if (!parse_windows_proxy_setting (str, &el, &gc_local))
+ goto found;
+ }
+ goto done;
+
+ found:
+ if (el.server && el.port > 0)
+ {
+ e->server = string_alloc (el.server, gc);
+ e->port = el.port;
+ }
+
+ done:
+ gc_free (&gc_local);
+}
+
+static const char *
+win_proxy_access_type (const DWORD dwAccessType)
+{
+ switch (dwAccessType)
+ {
+ case INTERNET_OPEN_TYPE_DIRECT:
+ return "INTERNET_OPEN_TYPE_DIRECT";
+ case INTERNET_OPEN_TYPE_PROXY:
+ return "INTERNET_OPEN_TYPE_PROXY";
+ default:
+ return "[UNKNOWN]";
+ }
+}
+
+void
+show_win_proxy_settings (const int msglevel)
+{
+ INTERNET_PROXY_INFO *info;
+ struct gc_arena gc = gc_new ();
+
+ info = get_windows_proxy_settings (&gc);
+ msg (msglevel, "PROXY INFO: %s %s",
+ win_proxy_access_type (info->dwAccessType),
+ info->lpszProxy ? info->lpszProxy : "[NULL]");
+
+ gc_free (&gc);
+}
+
+struct auto_proxy_info *
+get_proxy_settings (char **err, struct gc_arena *gc)
+{
+ struct gc_arena gc_local = gc_new ();
+ INTERNET_PROXY_INFO *info;
+ struct auto_proxy_info *pi;
+
+ ALLOC_OBJ_CLEAR_GC (pi, struct auto_proxy_info, gc);
+
+ if (err)
+ *err = NULL;
+
+ info = get_windows_proxy_settings (&gc_local);
+
+ if (!info)
+ {
+ if (err)
+ *err = "PROXY: failed to obtain windows proxy info";
+ goto done;
+ }
+
+ switch (info->dwAccessType)
+ {
+ case INTERNET_OPEN_TYPE_DIRECT:
+ break;
+ case INTERNET_OPEN_TYPE_PROXY:
+ if (!info->lpszProxy)
+ break;
+ parse_windows_proxy_setting_list (info->lpszProxy, NULL, &pi->http, gc);
+ if (!pi->http.server)
+ parse_windows_proxy_setting_list (info->lpszProxy, "http", &pi->http, gc);
+ parse_windows_proxy_setting_list (info->lpszProxy, "socks", &pi->socks, gc);
+ break;
+ default:
+ if (err)
+ *err = "PROXY: unknown proxy type";
+ break;
+ }
+
+ done:
+ gc_free (&gc_local);
+ return pi;
+}
+
+#else
+
+struct auto_proxy_info *
+get_proxy_settings (char **err, struct gc_arena *gc)
+{
+#if 1
+ if (err)
+ *err = string_alloc ("PROXY: automatic detection not supported on this OS", gc);
+ return NULL;
+#else /* test --auto-proxy feature */
+ struct auto_proxy_info *pi;
+ ALLOC_OBJ_CLEAR_GC (pi, struct auto_proxy_info, gc);
+ pi->http.server = "10.10.0.2";
+ pi->http.port = 4000;
+ return pi;
+#endif
+}
+
+#endif
+
+#endif /* GENERAL_PROXY_SUPPORT */
diff --git a/proxy.h b/proxy.h
new file mode 100644
index 0000000..d89aa4a
--- /dev/null
+++ b/proxy.h
@@ -0,0 +1,114 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PROXY_H
+#define PROXY_H
+
+#include "buffer.h"
+#include "misc.h"
+
+#ifdef GENERAL_PROXY_SUPPORT
+
+/*
+ * Return value for get_proxy_settings to automatically
+ * determine proxy information.
+ */
+struct auto_proxy_info_entry {
+ char *server;
+ int port;
+};
+
+struct auto_proxy_info {
+ struct auto_proxy_info_entry http;
+ struct auto_proxy_info_entry socks;
+};
+
+struct auto_proxy_info *get_proxy_settings (char **err, struct gc_arena *gc);
+
+#ifdef WIN32
+void show_win_proxy_settings (const int msglevel);
+#endif /* WIN32 */
+
+#endif /* GENERAL_PROXY_SUPPORT */
+
+#ifdef ENABLE_HTTP_PROXY
+
+/* HTTP CONNECT authentication methods */
+#define HTTP_AUTH_NONE 0
+#define HTTP_AUTH_BASIC 1
+#define HTTP_AUTH_DIGEST 2
+#define HTTP_AUTH_NTLM 3
+#define HTTP_AUTH_NTLM2 4
+#define HTTP_AUTH_N 5 /* number of HTTP_AUTH methods */
+
+struct http_proxy_options {
+ const char *server;
+ int port;
+ bool retry;
+ int timeout;
+
+# define PAR_NO 0 /* don't support any auth retries */
+# define PAR_ALL 1 /* allow all proxy auth protocols */
+# define PAR_NCT 2 /* disable cleartext proxy auth protocols */
+ int auth_retry;
+
+ const char *auth_method_string;
+ const char *auth_file;
+ const char *http_version;
+ const char *user_agent;
+};
+
+struct http_proxy_options_simple {
+ const char *server;
+ int port;
+ int auth_retry;
+};
+
+struct http_proxy_info {
+ bool defined;
+ int auth_method;
+ struct http_proxy_options options;
+ struct user_pass up;
+ char *proxy_authenticate;
+ bool queried_creds;
+};
+
+struct http_proxy_info *http_proxy_new (const struct http_proxy_options *o,
+ struct auto_proxy_info *auto_proxy_info);
+
+void http_proxy_close (struct http_proxy_info *hp);
+
+bool establish_http_proxy_passthru (struct http_proxy_info *p,
+ socket_descriptor_t sd, /* already open to proxy */
+ const char *host, /* openvpn server remote */
+ const int port, /* openvpn server port */
+ struct buffer *lookahead,
+ volatile int *signal_received);
+
+uint8_t *make_base64_string2 (const uint8_t *str, int str_len, struct gc_arena *gc);
+uint8_t *make_base64_string (const uint8_t *str, struct gc_arena *gc);
+
+#endif /* ENABLE_HTTP_PROXY */
+
+#endif /* PROXY_H */
diff --git a/ps.c b/ps.c
new file mode 100644
index 0000000..5533341
--- /dev/null
+++ b/ps.c
@@ -0,0 +1,880 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if PORT_SHARE
+
+#include "event.h"
+#include "socket.h"
+#include "fdmisc.h"
+#include "crypto.h"
+#include "ps.h"
+
+#include "memdbg.h"
+
+struct port_share *port_share = NULL; /* GLOBAL */
+
+/* size of i/o buffers */
+#define PROXY_CONNECTION_BUFFER_SIZE 1500
+
+/* Command codes for foreground -> background communication */
+#define COMMAND_REDIRECT 10
+#define COMMAND_EXIT 11
+
+/* Response codes for background -> foreground communication */
+#define RESPONSE_INIT_SUCCEEDED 20
+#define RESPONSE_INIT_FAILED 21
+
+/*
+ * Return values for proxy_connection_io functions
+ */
+
+#define IOSTAT_EAGAIN_ON_READ 0 /* recv returned EAGAIN */
+#define IOSTAT_EAGAIN_ON_WRITE 1 /* send returned EAGAIN */
+#define IOSTAT_READ_ERROR 2 /* the other end of our read socket (pc) was closed */
+#define IOSTAT_WRITE_ERROR 3 /* the other end of our write socket (pc->counterpart) was closed */
+#define IOSTAT_GOOD 4 /* nothing to report */
+
+/*
+ * A foreign (non-OpenVPN) connection we are proxying,
+ * usually HTTPS
+ */
+struct proxy_connection {
+ bool defined;
+ struct proxy_connection *next;
+ struct proxy_connection *counterpart;
+ struct buffer buf;
+ bool buffer_initial;
+ int rwflags;
+ int sd;
+};
+
+#if 0
+static const char *
+headc (const struct buffer *buf)
+{
+ static char foo[16];
+ strncpy (foo, BSTR(buf), 15);
+ foo[15] = 0;
+ return foo;
+}
+#endif
+
+static inline void
+close_socket_if_defined (const socket_descriptor_t sd)
+{
+ if (socket_defined (sd))
+ openvpn_close_socket (sd);
+}
+
+/*
+ * Close most of parent's fds.
+ * Keep stdin/stdout/stderr, plus one
+ * other fd which is presumed to be
+ * our pipe back to parent.
+ * Admittedly, a bit of a kludge,
+ * but posix doesn't give us a kind
+ * of FD_CLOEXEC which will stop
+ * fds from crossing a fork().
+ */
+static void
+close_fds_except (int keep)
+{
+ socket_descriptor_t i;
+ closelog ();
+ for (i = 3; i <= 100; ++i)
+ {
+ if (i != keep)
+ openvpn_close_socket (i);
+ }
+}
+
+/*
+ * Usually we ignore signals, because our parent will
+ * deal with them.
+ */
+static void
+set_signals (void)
+{
+ signal (SIGTERM, SIG_DFL);
+
+ signal (SIGINT, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+}
+
+/*
+ * Socket read/write functions.
+ */
+
+static int
+recv_control (const socket_descriptor_t fd)
+{
+ unsigned char c;
+ const ssize_t size = read (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return c;
+ else
+ {
+ return -1;
+ }
+}
+
+static int
+send_control (const socket_descriptor_t fd, int code)
+{
+ unsigned char c = (unsigned char) code;
+ const ssize_t size = write (fd, &c, sizeof (c));
+ if (size == sizeof (c))
+ return (int) size;
+ else
+ return -1;
+}
+
+static int
+cmsg_size ()
+{
+ return CMSG_SPACE(sizeof(socket_descriptor_t));
+}
+
+/*
+ * Send a command (char), data (head), and a file descriptor (sd_send) to a local process
+ * over unix socket sd. Unfortunately, there's no portable way to send file descriptors
+ * to other processes, so this code, as well as its analog (control_message_from_parent below),
+ * is Linux-specific. This function runs in the context of the main process and is used to
+ * send commands, data, and file descriptors to the background process.
+ */
+static void
+port_share_sendmsg (const socket_descriptor_t sd,
+ const char command,
+ const struct buffer *head,
+ const socket_descriptor_t sd_send)
+{
+ if (socket_defined (sd))
+ {
+ struct msghdr mesg;
+ struct cmsghdr* h;
+ struct iovec iov[2];
+ socket_descriptor_t sd_null[2] = { SOCKET_UNDEFINED, SOCKET_UNDEFINED };
+ char cmd;
+ ssize_t status;
+
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: sendmsg sd=%d len=%d",
+ (int)sd_send,
+ head ? BLEN(head) : -1);
+
+ CLEAR (mesg);
+
+ cmd = command;
+
+ iov[0].iov_base = &cmd;
+ iov[0].iov_len = sizeof (cmd);
+ mesg.msg_iovlen = 1;
+
+ if (head)
+ {
+ iov[1].iov_base = BPTR (head);
+ iov[1].iov_len = BLEN (head);
+ mesg.msg_iovlen = 2;
+ }
+
+ mesg.msg_iov = iov;
+
+ mesg.msg_controllen = cmsg_size ();
+ mesg.msg_control = (char *) malloc (mesg.msg_controllen);
+ check_malloc_return (mesg.msg_control);
+ mesg.msg_flags = 0;
+
+ h = CMSG_FIRSTHDR(&mesg);
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_RIGHTS;
+ h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
+
+ if (socket_defined (sd_send))
+ {
+ *((socket_descriptor_t*)CMSG_DATA(h)) = sd_send;
+ }
+ else
+ {
+ socketpair (PF_UNIX, SOCK_DGRAM, 0, sd_null);
+ *((socket_descriptor_t*)CMSG_DATA(h)) = sd_null[0];
+ }
+
+ status = sendmsg (sd, &mesg, MSG_NOSIGNAL);
+ if (status == -1)
+ msg (M_WARN, "PORT SHARE: sendmsg failed (unable to communicate with background process)");
+
+ close_socket_if_defined (sd_null[0]);
+ close_socket_if_defined (sd_null[1]);
+ free (mesg.msg_control);
+ }
+}
+
+static void
+proxy_entry_close_sd (struct proxy_connection *pc, struct event_set *es)
+{
+ if (pc->defined && socket_defined (pc->sd))
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: delete sd=%d", (int)pc->sd);
+ if (es)
+ event_del (es, pc->sd);
+ openvpn_close_socket (pc->sd);
+ pc->sd = SOCKET_UNDEFINED;
+ }
+}
+
+/*
+ * Mark a proxy entry and its counterpart for close.
+ */
+static void
+proxy_entry_mark_for_close (struct proxy_connection *pc, struct event_set *es)
+{
+ if (pc->defined)
+ {
+ struct proxy_connection *cp = pc->counterpart;
+ proxy_entry_close_sd (pc, es);
+ free_buf (&pc->buf);
+ pc->buffer_initial = false;
+ pc->rwflags = 0;
+ pc->defined = false;
+ if (cp && cp->defined && cp->counterpart == pc)
+ proxy_entry_mark_for_close (cp, es);
+ }
+}
+
+/*
+ * Run through the proxy entry list and delete all entries marked
+ * for close.
+ */
+static void
+proxy_list_housekeeping (struct proxy_connection **list)
+{
+ if (list)
+ {
+ struct proxy_connection *prev = NULL;
+ struct proxy_connection *pc = *list;
+
+ while (pc)
+ {
+ struct proxy_connection *next = pc->next;
+ if (!pc->defined)
+ {
+ free (pc);
+ if (prev)
+ prev->next = next;
+ else
+ *list = next;
+ }
+ else
+ prev = pc;
+ pc = next;
+ }
+ }
+}
+
+/*
+ * Cleanup function, on proxy process exit.
+ */
+static void
+proxy_list_close (struct proxy_connection **list)
+{
+ if (list)
+ {
+ struct proxy_connection *pc = *list;
+ while (pc)
+ {
+ proxy_entry_mark_for_close (pc, NULL);
+ pc = pc->next;
+ }
+ proxy_list_housekeeping (list);
+ }
+}
+
+static void
+sock_addr_set (struct openvpn_sockaddr *osaddr,
+ const in_addr_t addr,
+ const int port)
+{
+ CLEAR (*osaddr);
+ osaddr->sa.sin_family = AF_INET;
+ osaddr->sa.sin_addr.s_addr = htonl (addr);
+ osaddr->sa.sin_port = htons (port);
+}
+
+static inline void
+proxy_connection_io_requeue (struct proxy_connection *pc, const int rwflags_new, struct event_set *es)
+{
+ if (socket_defined (pc->sd) && pc->rwflags != rwflags_new)
+ {
+ /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: requeue[%d] rwflags=%d", (int)pc->sd, rwflags_new);*/
+ event_ctl (es, pc->sd, rwflags_new, (void*)pc);
+ pc->rwflags = rwflags_new;
+ }
+}
+
+/*
+ * Create a new pair of proxy_connection entries, one for each
+ * socket file descriptor involved in the proxy. We are given
+ * the client fd, and we should derive our own server fd by connecting
+ * to the server given by server_addr/server_port. Return true
+ * on success and false on failure to connect to server.
+ */
+static bool
+proxy_entry_new (struct proxy_connection **list,
+ struct event_set *es,
+ const in_addr_t server_addr,
+ const int server_port,
+ const socket_descriptor_t sd_client,
+ struct buffer *initial_data)
+{
+ struct openvpn_sockaddr osaddr;
+ socket_descriptor_t sd_server;
+ int status;
+ struct proxy_connection *pc;
+ struct proxy_connection *cp;
+
+ /* connect to port share server */
+ sock_addr_set (&osaddr, server_addr, server_port);
+ sd_server = create_socket_tcp ();
+ status = openvpn_connect (sd_server, &osaddr, 5, NULL);
+ if (status)
+ {
+ msg (M_WARN, "PORT SHARE PROXY: connect to port-share server failed");
+ openvpn_close_socket (sd_server);
+ return false;
+ }
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: connect to port-share server succeeded");
+
+ set_nonblock (sd_client);
+ set_nonblock (sd_server);
+
+ /* allocate 2 new proxy_connection objects */
+ ALLOC_OBJ_CLEAR (pc, struct proxy_connection);
+ ALLOC_OBJ_CLEAR (cp, struct proxy_connection);
+
+ /* client object */
+ pc->defined = true;
+ pc->next = cp;
+ pc->counterpart = cp;
+ pc->buf = *initial_data;
+ pc->buffer_initial = true;
+ pc->rwflags = EVENT_UNDEF;
+ pc->sd = sd_client;
+
+ /* server object */
+ cp->defined = true;
+ cp->next = *list;
+ cp->counterpart = pc;
+ cp->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ cp->buffer_initial = false;
+ cp->rwflags = EVENT_UNDEF;
+ cp->sd = sd_server;
+
+ /* add to list */
+ *list = pc;
+
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: NEW CONNECTION [c=%d s=%d]", (int)sd_client, (int)sd_server);
+
+ /* set initial i/o states */
+ proxy_connection_io_requeue (pc, EVENT_READ, es);
+ proxy_connection_io_requeue (cp, EVENT_READ|EVENT_WRITE, es);
+
+ return true;
+}
+
+/*
+ * This function runs in the context of the background proxy process.
+ * Receive a control message from the parent (sent by the port_share_sendmsg
+ * function above) and act on it. Return false if the proxy process should
+ * exit, true otherwise.
+ */
+static bool
+control_message_from_parent (const socket_descriptor_t sd_control,
+ struct proxy_connection **list,
+ struct event_set *es,
+ const in_addr_t server_addr,
+ const int server_port)
+{
+ struct buffer buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ struct msghdr mesg;
+ struct cmsghdr* h;
+ struct iovec iov[2];
+ char command = 0;
+ ssize_t status;
+ int ret = true;
+
+ CLEAR (mesg);
+
+ iov[0].iov_base = &command;
+ iov[0].iov_len = sizeof (command);
+ iov[1].iov_base = BPTR (&buf);
+ iov[1].iov_len = BCAP (&buf);
+ mesg.msg_iov = iov;
+ mesg.msg_iovlen = 2;
+
+ mesg.msg_controllen = cmsg_size ();
+ mesg.msg_control = (char *) malloc (mesg.msg_controllen);
+ check_malloc_return (mesg.msg_control);
+ mesg.msg_flags = 0;
+
+ h = CMSG_FIRSTHDR(&mesg);
+ h->cmsg_len = CMSG_LEN(sizeof(socket_descriptor_t));
+ h->cmsg_level = SOL_SOCKET;
+ h->cmsg_type = SCM_RIGHTS;
+ *((socket_descriptor_t*)CMSG_DATA(h)) = SOCKET_UNDEFINED;
+
+ status = recvmsg (sd_control, &mesg, MSG_NOSIGNAL);
+ if (status != -1)
+ {
+ if ( h == NULL
+ || h->cmsg_len != CMSG_LEN(sizeof(socket_descriptor_t))
+ || h->cmsg_level != SOL_SOCKET
+ || h->cmsg_type != SCM_RIGHTS )
+ {
+ ret = false;
+ }
+ else
+ {
+ const socket_descriptor_t received_fd = *((socket_descriptor_t*)CMSG_DATA(h));
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED sd=%d", (int)received_fd);
+
+ if (status >= 2 && command == COMMAND_REDIRECT)
+ {
+ buf.len = status - 1;
+ if (proxy_entry_new (list,
+ es,
+ server_addr,
+ server_port,
+ received_fd,
+ &buf))
+ {
+ CLEAR (buf); /* we gave the buffer to proxy_entry_new */
+ }
+ else
+ {
+ openvpn_close_socket (received_fd);
+ }
+ }
+ else if (status >= 1 && command == COMMAND_EXIT)
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: RECEIVED COMMAND_EXIT");
+ openvpn_close_socket (received_fd); /* null socket */
+ ret = false;
+ }
+ }
+ }
+ free (mesg.msg_control);
+ free_buf (&buf);
+ return ret;
+}
+
+static int
+proxy_connection_io_recv (struct proxy_connection *pc)
+{
+ /* recv data from socket */
+ const int status = recv (pc->sd, BPTR(&pc->buf), BCAP(&pc->buf), MSG_NOSIGNAL);
+ if (status < 0)
+ {
+ return (errno == EAGAIN) ? IOSTAT_EAGAIN_ON_READ : IOSTAT_READ_ERROR;
+ }
+ else
+ {
+ if (!status)
+ return IOSTAT_READ_ERROR;
+ pc->buf.len = status;
+ }
+ return IOSTAT_GOOD;
+}
+
+static int
+proxy_connection_io_send (struct proxy_connection *pc, int *bytes_sent)
+{
+ const socket_descriptor_t sd = pc->counterpart->sd;
+ const int status = send (sd, BPTR(&pc->buf), BLEN(&pc->buf), MSG_NOSIGNAL);
+
+ if (status < 0)
+ {
+ const int e = errno;
+ return (e == EAGAIN) ? IOSTAT_EAGAIN_ON_WRITE : IOSTAT_WRITE_ERROR;
+ }
+ else
+ {
+ *bytes_sent += status;
+ if (status != pc->buf.len)
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: partial write[%d], tried=%d got=%d", (int)sd, pc->buf.len, status);
+ buf_advance (&pc->buf, status);
+ return IOSTAT_EAGAIN_ON_WRITE;
+ }
+ else
+ {
+ /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: wrote[%d] %d", (int)sd, status);*/
+ pc->buf.len = 0;
+ pc->buf.offset = 0;
+ }
+ }
+
+ /* realloc send buffer after initial send */
+ if (pc->buffer_initial)
+ {
+ free_buf (&pc->buf);
+ pc->buf = alloc_buf (PROXY_CONNECTION_BUFFER_SIZE);
+ pc->buffer_initial = false;
+ }
+ return IOSTAT_GOOD;
+}
+
+/*
+ * Forward data from pc to pc->counterpart.
+ */
+
+static int
+proxy_connection_io_xfer (struct proxy_connection *pc, const int max_transfer)
+{
+ int transferred = 0;
+ while (transferred < max_transfer)
+ {
+ if (!BLEN (&pc->buf))
+ {
+ const int status = proxy_connection_io_recv (pc);
+ if (status != IOSTAT_GOOD)
+ return status;
+ }
+
+ if (BLEN (&pc->buf))
+ {
+ const int status = proxy_connection_io_send (pc, &transferred);
+ if (status != IOSTAT_GOOD)
+ return status;
+ }
+ }
+ return IOSTAT_EAGAIN_ON_READ;
+}
+
+/*
+ * Decide how the receipt of an EAGAIN status should affect our next IO queueing.
+ */
+static bool
+proxy_connection_io_status (const int status, int *rwflags_pc, int *rwflags_cp)
+{
+ switch (status)
+ {
+ case IOSTAT_EAGAIN_ON_READ:
+ *rwflags_pc |= EVENT_READ;
+ *rwflags_cp &= ~EVENT_WRITE;
+ return true;
+ case IOSTAT_EAGAIN_ON_WRITE:
+ *rwflags_pc &= ~EVENT_READ;
+ *rwflags_cp |= EVENT_WRITE;
+ return true;
+ case IOSTAT_READ_ERROR:
+ return false;
+ case IOSTAT_WRITE_ERROR:
+ return false;
+ default:
+ msg (M_FATAL, "PORT SHARE PROXY: unexpected status=%d", status);
+ }
+ return false; /* NOTREACHED */
+}
+
+/*
+ * Dispatch function for forwarding data between the two socket fds involved
+ * in the proxied connection.
+ */
+static int
+proxy_connection_io_dispatch (struct proxy_connection *pc,
+ const int rwflags,
+ struct event_set *es)
+{
+ const int max_transfer_per_iteration = 10000;
+ struct proxy_connection *cp = pc->counterpart;
+ int rwflags_pc = pc->rwflags;
+ int rwflags_cp = cp->rwflags;
+
+ if (rwflags & EVENT_READ)
+ {
+ const int status = proxy_connection_io_xfer (pc, max_transfer_per_iteration);
+ if (!proxy_connection_io_status (status, &rwflags_pc, &rwflags_cp))
+ goto bad;
+ }
+ if (rwflags & EVENT_WRITE)
+ {
+ const int status = proxy_connection_io_xfer (cp, max_transfer_per_iteration);
+ if (!proxy_connection_io_status (status, &rwflags_cp, &rwflags_pc))
+ goto bad;
+ }
+ proxy_connection_io_requeue (pc, rwflags_pc, es);
+ proxy_connection_io_requeue (cp, rwflags_cp, es);
+
+ return true;
+
+ bad:
+ proxy_entry_mark_for_close (pc, es);
+ return false;
+}
+
+/*
+ * This is the main function for the port share proxy background process.
+ */
+static void
+port_share_proxy (const in_addr_t hostaddr, const int port, const socket_descriptor_t sd_control)
+{
+ if (send_control (sd_control, RESPONSE_INIT_SUCCEEDED) >= 0)
+ {
+ void *sd_control_marker = (void *)1;
+ int maxevents = 256;
+ struct event_set *es;
+ struct event_set_return esr[64];
+ struct proxy_connection *list = NULL;
+ time_t last_housekeeping = 0;
+
+ msg (D_PS_PROXY, "PORT SHARE PROXY: proxy starting");
+
+ es = event_set_init (&maxevents, 0);
+ event_ctl (es, sd_control, EVENT_READ, sd_control_marker);
+ while (true)
+ {
+ int n_events;
+ struct timeval tv;
+ time_t current;
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ n_events = event_wait (es, &tv, esr, SIZE(esr));
+ /*dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait returned %d", n_events);*/
+ current = time(NULL);
+ if (n_events > 0)
+ {
+ int i;
+ for (i = 0; i < n_events; ++i)
+ {
+ const struct event_set_return *e = &esr[i];
+ if (e->arg == sd_control_marker)
+ {
+ if (!control_message_from_parent (sd_control, &list, es, hostaddr, port))
+ goto done;
+ }
+ else
+ {
+ struct proxy_connection *pc = (struct proxy_connection *)e->arg;
+ if (pc->defined)
+ proxy_connection_io_dispatch (pc, e->rwflags, es);
+ }
+ }
+ }
+ else if (n_events < 0)
+ {
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE PROXY: event_wait failed");
+ }
+ if (current > last_housekeeping)
+ {
+ proxy_list_housekeeping (&list);
+ last_housekeeping = current;
+ }
+ }
+
+ done:
+ proxy_list_close (&list);
+ event_free (es);
+ }
+ msg (D_PS_PROXY, "PORT SHARE PROXY: proxy exiting");
+}
+
+/*
+ * Called from the main OpenVPN process to enable the port
+ * share proxy.
+ */
+struct port_share *
+port_share_open (const char *host, const int port)
+{
+ pid_t pid;
+ socket_descriptor_t fd[2];
+ in_addr_t hostaddr;
+ struct port_share *ps;
+
+ ALLOC_OBJ_CLEAR (ps, struct port_share);
+
+ /*
+ * Get host's IP address
+ */
+ hostaddr = getaddr (GETADDR_RESOLVE|GETADDR_HOST_ORDER|GETADDR_FATAL, host, 0, NULL, NULL);
+
+ /*
+ * Make a socket for foreground and background processes
+ * to communicate.
+ */
+ if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
+ {
+ msg (M_WARN, "PORT SHARE: socketpair call failed");
+ goto error;
+ }
+
+ /*
+ * Fork off background proxy process.
+ */
+ pid = fork ();
+
+ if (pid)
+ {
+ int status;
+
+ /*
+ * Foreground Process
+ */
+
+ ps->background_pid = pid;
+
+ /* close our copy of child's socket */
+ openvpn_close_socket (fd[1]);
+
+ /* don't let future subprocesses inherit child socket */
+ set_cloexec (fd[0]);
+
+ /* wait for background child process to initialize */
+ status = recv_control (fd[0]);
+ if (status == RESPONSE_INIT_SUCCEEDED)
+ {
+ ps->foreground_fd = fd[0];
+ return ps;
+ }
+ }
+ else
+ {
+ /*
+ * Background Process
+ */
+
+ /* Ignore most signals (the parent will receive them) */
+ set_signals ();
+
+ /* Let msg know that we forked */
+ msg_forked ();
+
+#ifdef ENABLE_MANAGEMENT
+ /* Don't interact with management interface */
+ management = NULL;
+#endif
+
+ /* close all parent fds except our socket back to parent */
+ close_fds_except (fd[1]);
+
+ /* no blocking on control channel back to parent */
+ set_nonblock (fd[1]);
+
+ /* initialize prng */
+ prng_init (NULL, 0);
+
+ /* execute the event loop */
+ port_share_proxy (hostaddr, port, fd[1]);
+
+ openvpn_close_socket (fd[1]);
+
+ exit (0);
+ return 0; /* NOTREACHED */
+ }
+
+ error:
+ port_share_close (ps);
+ return NULL;
+}
+
+void
+port_share_close (struct port_share *ps)
+{
+ if (ps)
+ {
+ if (ps->foreground_fd >= 0)
+ {
+ /* tell background process to exit */
+ port_share_sendmsg (ps->foreground_fd, COMMAND_EXIT, NULL, SOCKET_UNDEFINED);
+
+ /* wait for background process to exit */
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: waiting for background process to exit");
+ if (ps->background_pid > 0)
+ waitpid (ps->background_pid, NULL, 0);
+ dmsg (D_PS_PROXY_DEBUG, "PORT SHARE: background process exited");
+
+ openvpn_close_socket (ps->foreground_fd);
+ ps->foreground_fd = -1;
+ }
+
+ free (ps);
+ }
+}
+
+void
+port_share_abort (struct port_share *ps)
+{
+ if (ps)
+ {
+ /* tell background process to exit */
+ if (ps->foreground_fd >= 0)
+ {
+ send_control (ps->foreground_fd, COMMAND_EXIT);
+ openvpn_close_socket (ps->foreground_fd);
+ ps->foreground_fd = -1;
+ }
+ }
+}
+
+/*
+ * Given either the first 2 or 3 bytes of an initial client -> server
+ * data payload, return true if the protocol is that of an OpenVPN
+ * client attempting to connect with an OpenVPN server.
+ */
+bool
+is_openvpn_protocol (const struct buffer *buf)
+{
+ const unsigned char *p = (const unsigned char *) BSTR (buf);
+ const int len = BLEN (buf);
+ if (len >= 3)
+ {
+ return p[0] == 0
+ && p[1] >= 14
+ && p[2] == (P_CONTROL_HARD_RESET_CLIENT_V2<<P_OPCODE_SHIFT);
+ }
+ else if (len >= 2)
+ {
+ return p[0] == 0 && p[1] >= 14;
+ }
+ else
+ return true;
+}
+
+/*
+ * Called from the foreground process. Send a message to the background process that it
+ * should proxy the TCP client on sd to the host/port defined in the initial port_share_open
+ * call.
+ */
+void
+port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd)
+{
+ if (ps)
+ port_share_sendmsg (ps->foreground_fd, COMMAND_REDIRECT, head, sd);
+}
+
+#endif
diff --git a/ps.h b/ps.h
new file mode 100644
index 0000000..d488a72
--- /dev/null
+++ b/ps.h
@@ -0,0 +1,57 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PS_H
+#define PS_H
+
+#if PORT_SHARE
+
+#include "basic.h"
+#include "buffer.h"
+#include "ssl.h"
+
+typedef void (*post_fork_cleanup_func_t)(void *arg);
+
+struct port_share {
+ /* Foreground's socket to background process */
+ socket_descriptor_t foreground_fd;
+
+ /* Process ID of background process */
+ pid_t background_pid;
+};
+
+extern struct port_share *port_share;
+
+struct port_share *port_share_open (const char *host,
+ const int port);
+
+void port_share_close (struct port_share *ps);
+void port_share_abort (struct port_share *ps);
+
+bool is_openvpn_protocol (const struct buffer *buf);
+
+void port_share_redirect (struct port_share *ps, const struct buffer *head, socket_descriptor_t sd);
+
+#endif
+#endif
diff --git a/push.c b/push.c
new file mode 100644
index 0000000..08c7f99
--- /dev/null
+++ b/push.c
@@ -0,0 +1,458 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "push.h"
+#include "options.h"
+#include "ssl.h"
+#include "manage.h"
+
+#include "memdbg.h"
+
+#if P2MP
+
+/*
+ * Auth username/password
+ *
+ * Client received an authentication failed message from server.
+ * Runs on client.
+ */
+void
+receive_auth_failed (struct context *c, const struct buffer *buffer)
+{
+ msg (M_VERB0, "AUTH: Received AUTH_FAILED control message");
+ connection_list_set_no_advance(&c->options);
+ if (c->options.pull)
+ {
+ switch (auth_retry_get ())
+ {
+ case AR_NONE:
+ c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */
+ break;
+ case AR_INTERACT:
+ ssl_purge_auth ();
+ case AR_NOINTERACT:
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */
+ break;
+ default:
+ ASSERT (0);
+ }
+ c->sig->signal_text = "auth-failure";
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ const char *reason = NULL;
+ struct buffer buf = *buffer;
+ if (buf_string_compare_advance (&buf, "AUTH_FAILED,") && BLEN (&buf))
+ reason = BSTR (&buf);
+ management_auth_failure (management, UP_TYPE_AUTH, reason);
+ } else
+#endif
+ {
+#ifdef ENABLE_CLIENT_CR
+ struct buffer buf = *buffer;
+ if (buf_string_match_head_str (&buf, "AUTH_FAILED,CRV1:") && BLEN (&buf))
+ {
+ buf_advance (&buf, 12); /* Length of "AUTH_FAILED," substring */
+ ssl_put_auth_challenge (BSTR (&buf));
+ }
+#endif
+ }
+ }
+}
+
+/*
+ * Act on received restart message from server
+ */
+void
+server_pushed_restart (struct context *c, const struct buffer *buffer)
+{
+ if (c->options.pull)
+ {
+ msg (D_STREAM_ERRORS, "Connection reset command was pushed by server");
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */
+ c->sig->signal_text = "server-pushed-connection-reset";
+ }
+}
+
+#if P2MP_SERVER
+
+/*
+ * Send auth failed message from server to client.
+ */
+void
+send_auth_failed (struct context *c, const char *client_reason)
+{
+ struct gc_arena gc = gc_new ();
+ static const char auth_failed[] = "AUTH_FAILED";
+ size_t len;
+
+ schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
+
+ len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
+ if (len > PUSH_BUNDLE_SIZE)
+ len = PUSH_BUNDLE_SIZE;
+
+ {
+ struct buffer buf = alloc_buf_gc (len, &gc);
+ buf_printf (&buf, auth_failed);
+ if (client_reason)
+ buf_printf (&buf, ",%s", client_reason);
+ send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ }
+
+ gc_free (&gc);
+}
+
+/*
+ * Send restart message from server to client.
+ */
+void
+send_restart (struct context *c)
+{
+ schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
+ send_control_channel_string (c, "RESTART", D_PUSH);
+}
+
+#endif
+
+/*
+ * Push/Pull
+ */
+
+void
+incoming_push_message (struct context *c, const struct buffer *buffer)
+{
+ struct gc_arena gc = gc_new ();
+ unsigned int option_types_found = 0;
+ int status;
+
+ msg (D_PUSH, "PUSH: Received control message: '%s'", BSTR (buffer));
+
+ status = process_incoming_push_msg (c,
+ buffer,
+ c->options.pull,
+ pull_permission_mask (c),
+ &option_types_found);
+
+ if (status == PUSH_MSG_ERROR)
+ msg (D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", BSTR (buffer));
+ else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION)
+ {
+ if (status == PUSH_MSG_REPLY)
+ do_up (c, true, option_types_found); /* delay bringing tun/tap up until --push parms received from remote */
+ event_timeout_clear (&c->c2.push_request_interval);
+ }
+
+ gc_free (&gc);
+}
+
+bool
+send_push_request (struct context *c)
+{
+ return send_control_channel_string (c, "PUSH_REQUEST", D_PUSH);
+}
+
+#if P2MP_SERVER
+
+bool
+send_push_reply (struct context *c)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
+ struct push_entry *e = c->options.push_list.head;
+ bool multi_push = false;
+ static char cmd[] = "PUSH_REPLY";
+ const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */
+ const int safe_cap = BCAP (&buf) - extra;
+ bool push_sent = false;
+
+ buf_printf (&buf, "%s", cmd);
+
+ while (e)
+ {
+ if (e->enable)
+ {
+ const int l = strlen (e->option);
+ if (BLEN (&buf) + l >= safe_cap)
+ {
+ buf_printf (&buf, ",push-continuation 2");
+ {
+ const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ if (!status)
+ goto fail;
+ push_sent = true;
+ multi_push = true;
+ buf_reset_len (&buf);
+ buf_printf (&buf, "%s", cmd);
+ }
+ }
+ if (BLEN (&buf) + l >= safe_cap)
+ {
+ msg (M_WARN, "--push option is too long");
+ goto fail;
+ }
+ buf_printf (&buf, ",%s", e->option);
+ }
+ e = e->next;
+ }
+
+ if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask)
+ buf_printf (&buf, ",ifconfig %s %s",
+ print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc),
+ print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc));
+ if (multi_push)
+ buf_printf (&buf, ",push-continuation 1");
+
+ if (BLEN (&buf) > sizeof(cmd)-1)
+ {
+ const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH);
+ if (!status)
+ goto fail;
+ push_sent = true;
+ }
+
+ /* If nothing have been pushed, send an empty push,
+ * as the client is expecting a response
+ */
+ if (!push_sent)
+ {
+ bool status = false;
+
+ buf_reset_len (&buf);
+ buf_printf (&buf, "%s", cmd);
+ status = send_control_channel_string (c, BSTR(&buf), D_PUSH);
+ if (!status)
+ goto fail;
+ }
+
+ gc_free (&gc);
+ return true;
+
+ fail:
+ gc_free (&gc);
+ return false;
+}
+
+static void
+push_option_ex (struct options *o, const char *opt, bool enable, int msglevel)
+{
+ if (!string_class (opt, CC_ANY, CC_COMMA))
+ {
+ msg (msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt);
+ }
+ else
+ {
+ struct push_entry *e;
+ ALLOC_OBJ_CLEAR_GC (e, struct push_entry, &o->gc);
+ e->enable = true;
+ e->option = opt;
+ if (o->push_list.head)
+ {
+ ASSERT(o->push_list.tail);
+ o->push_list.tail->next = e;
+ o->push_list.tail = e;
+ }
+ else
+ {
+ ASSERT(!o->push_list.tail);
+ o->push_list.head = e;
+ o->push_list.tail = e;
+ }
+ }
+}
+
+void
+push_option (struct options *o, const char *opt, int msglevel)
+{
+ push_option_ex (o, opt, true, msglevel);
+}
+
+void
+clone_push_list (struct options *o)
+{
+ if (o->push_list.head)
+ {
+ const struct push_entry *e = o->push_list.head;
+ push_reset (o);
+ while (e)
+ {
+ push_option_ex (o, string_alloc (e->option, &o->gc), true, M_FATAL);
+ e = e->next;
+ }
+ }
+}
+
+void
+push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc)
+{
+ const char **argv = make_extended_arg_array (p, gc);
+ char *opt = print_argv (argv, gc, 0);
+ push_option (o, opt, msglevel);
+}
+
+void
+push_reset (struct options *o)
+{
+ CLEAR (o->push_list);
+}
+#endif
+
+int
+process_incoming_push_msg (struct context *c,
+ const struct buffer *buffer,
+ bool honor_received_options,
+ unsigned int permission_mask,
+ unsigned int *option_types_found)
+{
+ int ret = PUSH_MSG_ERROR;
+ struct buffer buf = *buffer;
+
+#if P2MP_SERVER
+ if (buf_string_compare_advance (&buf, "PUSH_REQUEST"))
+ {
+ if (tls_authentication_status (c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
+ {
+ const char *client_reason = tls_client_reason (c->c2.tls_multi);
+ send_auth_failed (c, client_reason);
+ ret = PUSH_MSG_AUTH_FAILURE;
+ }
+ else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
+ {
+ if (send_push_reply (c))
+ ret = PUSH_MSG_REQUEST;
+ }
+ else
+ {
+ ret = PUSH_MSG_REQUEST_DEFERRED;
+ }
+ }
+ else
+#endif
+
+ if (honor_received_options && buf_string_compare_advance (&buf, "PUSH_REPLY"))
+ {
+ const uint8_t ch = buf_read_u8 (&buf);
+ if (ch == ',')
+ {
+ struct buffer buf_orig = buf;
+ if (!c->c2.did_pre_pull_restore)
+ {
+ pre_pull_restore (&c->options);
+ md5_state_init (&c->c2.pulled_options_state);
+ c->c2.did_pre_pull_restore = true;
+ }
+ if (apply_push_options (&c->options,
+ &buf,
+ permission_mask,
+ option_types_found,
+ c->c2.es))
+ switch (c->options.push_continuation)
+ {
+ case 0:
+ case 1:
+ md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
+ md5_state_final (&c->c2.pulled_options_state, &c->c2.pulled_options_digest);
+ ret = PUSH_MSG_REPLY;
+ break;
+ case 2:
+ md5_state_update (&c->c2.pulled_options_state, BPTR(&buf_orig), BLEN(&buf_orig));
+ ret = PUSH_MSG_CONTINUATION;
+ break;
+ }
+ }
+ else if (ch == '\0')
+ {
+ ret = PUSH_MSG_REPLY;
+ }
+ /* show_settings (&c->options); */
+ }
+ return ret;
+}
+
+#if P2MP_SERVER
+
+/*
+ * Remove iroutes from the push_list.
+ */
+void
+remove_iroutes_from_push_route_list (struct options *o)
+{
+ if (o && o->push_list.head && o->iroutes)
+ {
+ struct gc_arena gc = gc_new ();
+ struct push_entry *e = o->push_list.head;
+
+ /* cycle through the push list */
+ while (e)
+ {
+ char *p[MAX_PARMS];
+ bool enable = true;
+
+ /* parse the push item */
+ CLEAR (p);
+ if (parse_line (e->option, p, SIZE (p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc))
+ {
+ /* is the push item a route directive? */
+ if (p[0] && !strcmp (p[0], "route") && !p[3])
+ {
+ /* get route parameters */
+ bool status1, status2;
+ const in_addr_t network = getaddr (GETADDR_HOST_ORDER, p[1], 0, &status1, NULL);
+ const in_addr_t netmask = getaddr (GETADDR_HOST_ORDER, p[2] ? p[2] : "255.255.255.255", 0, &status2, NULL);
+
+ /* did route parameters parse correctly? */
+ if (status1 && status2)
+ {
+ const struct iroute *ir;
+
+ /* does route match an iroute? */
+ for (ir = o->iroutes; ir != NULL; ir = ir->next)
+ {
+ if (network == ir->network && netmask == netbits_to_netmask (ir->netbits >= 0 ? ir->netbits : 32))
+ {
+ enable = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* should we copy the push item? */
+ e->enable = enable;
+ if (!enable)
+ msg (D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option);
+
+ e = e->next;
+ }
+
+ gc_free (&gc);
+ }
+}
+
+#endif
+
+#endif
diff --git a/push.h b/push.h
new file mode 100644
index 0000000..089cf45
--- /dev/null
+++ b/push.h
@@ -0,0 +1,73 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PUSH_H
+#define PUSH_H
+
+#if P2MP
+
+#include "forward.h"
+
+#define PUSH_MSG_ERROR 0
+#define PUSH_MSG_REQUEST 1
+#define PUSH_MSG_REPLY 2
+#define PUSH_MSG_REQUEST_DEFERRED 3
+#define PUSH_MSG_AUTH_FAILURE 4
+#define PUSH_MSG_CONTINUATION 5
+
+void incoming_push_message (struct context *c,
+ const struct buffer *buffer);
+
+int process_incoming_push_msg (struct context *c,
+ const struct buffer *buffer,
+ bool honor_received_options,
+ unsigned int permission_mask,
+ unsigned int *option_types_found);
+
+bool send_push_request (struct context *c);
+
+void receive_auth_failed (struct context *c, const struct buffer *buffer);
+
+void server_pushed_restart (struct context *c, const struct buffer *buffer);
+
+#if P2MP_SERVER
+
+void clone_push_list (struct options *o);
+
+void push_option (struct options *o, const char *opt, int msglevel);
+void push_options (struct options *o, char **p, int msglevel, struct gc_arena *gc);
+
+void push_reset (struct options *o);
+
+bool send_push_reply (struct context *c);
+
+void remove_iroutes_from_push_route_list (struct options *o);
+
+void send_auth_failed (struct context *c, const char *client_reason);
+
+void send_restart (struct context *c);
+
+#endif
+#endif
+#endif
diff --git a/pushlist.h b/pushlist.h
new file mode 100644
index 0000000..b252676
--- /dev/null
+++ b/pushlist.h
@@ -0,0 +1,42 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(PUSHLIST_H) && P2MP && P2MP_SERVER
+#define PUSHLIST_H
+
+/* parameters to be pushed to peer */
+
+struct push_entry {
+ struct push_entry *next;
+ bool enable;
+ const char *option;
+};
+
+struct push_list {
+ struct push_entry *head;
+ struct push_entry *tail;
+};
+
+
+#endif
diff --git a/reliable.c b/reliable.c
new file mode 100644
index 0000000..1f238cc
--- /dev/null
+++ b/reliable.c
@@ -0,0 +1,751 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * These routines implement a reliability layer on top of UDP,
+ * so that SSL/TLS can be run over UDP.
+ */
+
+#include "syshead.h"
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#include "buffer.h"
+#include "error.h"
+#include "common.h"
+#include "reliable.h"
+
+#include "memdbg.h"
+
+/*
+ * verify that test - base < extent while allowing for base or test wraparound
+ */
+static inline bool
+reliable_pid_in_range1 (const packet_id_type test,
+ const packet_id_type base,
+ const unsigned int extent)
+{
+ if (test >= base)
+ {
+ if (test - base < extent)
+ return true;
+ }
+ else
+ {
+ if ((test+0x80000000u) - (base+0x80000000u) < extent)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * verify that test < base + extent while allowing for base or test wraparound
+ */
+static inline bool
+reliable_pid_in_range2 (const packet_id_type test,
+ const packet_id_type base,
+ const unsigned int extent)
+{
+ if (base + extent >= base)
+ {
+ if (test < base + extent)
+ return true;
+ }
+ else
+ {
+ if ((test+0x80000000u) < (base+0x80000000u) + extent)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * verify that p1 < p2 while allowing for p1 or p2 wraparound
+ */
+static inline bool
+reliable_pid_min (const packet_id_type p1,
+ const packet_id_type p2)
+{
+ return !reliable_pid_in_range1 (p1, p2, 0x80000000u);
+}
+
+/* check if a particular packet_id is present in ack */
+static inline bool
+reliable_ack_packet_id_present (struct reliable_ack *ack, packet_id_type pid)
+{
+ int i;
+ for (i = 0; i < ack->len; ++i)
+ if (ack->packet_id[i] == pid)
+ return true;
+ return false;
+}
+
+/* get a packet_id from buf */
+bool
+reliable_ack_read_packet_id (struct buffer *buf, packet_id_type *pid)
+{
+ packet_id_type net_pid;
+
+ if (buf_read (buf, &net_pid, sizeof (net_pid)))
+ {
+ *pid = ntohpid (net_pid);
+ dmsg (D_REL_DEBUG, "ACK read ID " packet_id_format " (buf->len=%d)",
+ (packet_id_print_type)*pid, buf->len);
+ return true;
+ }
+
+ dmsg (D_REL_LOW, "ACK read ID FAILED (buf->len=%d)", buf->len);
+ return false;
+}
+
+/* acknowledge a packet_id by adding it to a struct reliable_ack */
+bool
+reliable_ack_acknowledge_packet_id (struct reliable_ack *ack, packet_id_type pid)
+{
+ if (!reliable_ack_packet_id_present (ack, pid) && ack->len < RELIABLE_ACK_SIZE)
+ {
+ ack->packet_id[ack->len++] = pid;
+ dmsg (D_REL_DEBUG, "ACK acknowledge ID " packet_id_format " (ack->len=%d)",
+ (packet_id_print_type)pid, ack->len);
+ return true;
+ }
+
+ dmsg (D_REL_LOW, "ACK acknowledge ID " packet_id_format " FAILED (ack->len=%d)",
+ (packet_id_print_type)pid, ack->len);
+ return false;
+}
+
+/* read a packet ID acknowledgement record from buf into ack */
+bool
+reliable_ack_read (struct reliable_ack * ack,
+ struct buffer * buf, const struct session_id * sid)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ uint8_t count;
+ packet_id_type net_pid;
+ packet_id_type pid;
+ struct session_id session_id_remote;
+
+ if (!buf_read (buf, &count, sizeof (count)))
+ goto error;
+ for (i = 0; i < count; ++i)
+ {
+ if (!buf_read (buf, &net_pid, sizeof (net_pid)))
+ goto error;
+ if (ack->len >= RELIABLE_ACK_SIZE)
+ goto error;
+ pid = ntohpid (net_pid);
+ ack->packet_id[ack->len++] = pid;
+ }
+ if (count)
+ {
+ if (!session_id_read (&session_id_remote, buf))
+ goto error;
+ if (!session_id_defined (&session_id_remote) ||
+ !session_id_equal (&session_id_remote, sid))
+ {
+ dmsg (D_REL_LOW,
+ "ACK read BAD SESSION-ID FROM REMOTE, local=%s, remote=%s",
+ session_id_print (sid, &gc), session_id_print (&session_id_remote, &gc));
+ goto error;
+ }
+ }
+ gc_free (&gc);
+ return true;
+
+error:
+ gc_free (&gc);
+ return false;
+}
+
+#define ACK_SIZE(n) (sizeof (uint8_t) + ((n) ? SID_SIZE : 0) + sizeof (packet_id_type) * (n))
+
+/* write a packet ID acknowledgement record to buf, */
+/* removing all acknowledged entries from ack */
+bool
+reliable_ack_write (struct reliable_ack * ack,
+ struct buffer * buf,
+ const struct session_id * sid, int max, bool prepend)
+{
+ int i, j;
+ uint8_t n;
+ struct buffer sub;
+
+ n = ack->len;
+ if (n > max)
+ n = max;
+ sub = buf_sub (buf, ACK_SIZE(n), prepend);
+ if (!BDEF (&sub))
+ goto error;
+ ASSERT (buf_write (&sub, &n, sizeof (n)));
+ for (i = 0; i < n; ++i)
+ {
+ packet_id_type pid = ack->packet_id[i];
+ packet_id_type net_pid = htonpid (pid);
+ ASSERT (buf_write (&sub, &net_pid, sizeof (net_pid)));
+ dmsg (D_REL_DEBUG, "ACK write ID " packet_id_format " (ack->len=%d, n=%d)", (packet_id_print_type)pid, ack->len, n);
+ }
+ if (n)
+ {
+ ASSERT (session_id_defined (sid));
+ ASSERT (session_id_write (sid, &sub));
+ for (i = 0, j = n; j < ack->len;)
+ ack->packet_id[i++] = ack->packet_id[j++];
+ ack->len = i;
+ }
+
+ return true;
+
+error:
+ return false;
+}
+
+/* add to extra_frame the maximum number of bytes we will need for reliable_ack_write */
+void
+reliable_ack_adjust_frame_parameters (struct frame* frame, int max)
+{
+ frame_add_to_extra_frame (frame, ACK_SIZE (max));
+}
+
+/* print a reliable ACK record coming off the wire */
+const char *
+reliable_ack_print (struct buffer *buf, bool verbose, struct gc_arena *gc)
+{
+ int i;
+ uint8_t n_ack;
+ struct session_id sid_ack;
+ packet_id_type pid;
+ struct buffer out = alloc_buf_gc (256, gc);
+
+ buf_printf (&out, "[");
+ if (!buf_read (buf, &n_ack, sizeof (n_ack)))
+ goto done;
+ for (i = 0; i < n_ack; ++i)
+ {
+ if (!buf_read (buf, &pid, sizeof (pid)))
+ goto done;
+ pid = ntohpid (pid);
+ buf_printf (&out, " " packet_id_format, (packet_id_print_type)pid);
+ }
+ if (n_ack)
+ {
+ if (!session_id_read (&sid_ack, buf))
+ goto done;
+ if (verbose)
+ buf_printf (&out, " sid=%s", session_id_print (&sid_ack, gc));
+ }
+
+ done:
+ buf_printf (&out, " ]");
+ return BSTR (&out);
+}
+
+/*
+ * struct reliable member functions.
+ */
+
+void
+reliable_init (struct reliable *rel, int buf_size, int offset, int array_size, bool hold)
+{
+ int i;
+
+ CLEAR (*rel);
+ ASSERT (array_size > 0 && array_size <= RELIABLE_CAPACITY);
+ rel->hold = hold;
+ rel->size = array_size;
+ rel->offset = offset;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ e->buf = alloc_buf (buf_size);
+ ASSERT (buf_init (&e->buf, offset));
+ }
+}
+
+void
+reliable_free (struct reliable *rel)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ free_buf (&e->buf);
+ }
+}
+
+/* no active buffers? */
+bool
+reliable_empty (const struct reliable *rel)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ return false;
+ }
+ return true;
+}
+
+/* del acknowledged items from send buf */
+void
+reliable_send_purge (struct reliable *rel, struct reliable_ack *ack)
+{
+ int i, j;
+ for (i = 0; i < ack->len; ++i)
+ {
+ packet_id_type pid = ack->packet_id[i];
+ for (j = 0; j < rel->size; ++j)
+ {
+ struct reliable_entry *e = &rel->array[j];
+ if (e->active && e->packet_id == pid)
+ {
+ dmsg (D_REL_DEBUG,
+ "ACK received for pid " packet_id_format ", deleting from send buffer",
+ (packet_id_print_type)pid);
+#if 0
+ /* DEBUGGING -- how close were we timing out on ACK failure and resending? */
+ {
+ if (e->next_try)
+ {
+ const interval_t wake = e->next_try - now;
+ msg (M_INFO, "ACK " packet_id_format ", wake=%d", pid, wake);
+ }
+ }
+#endif
+ e->active = false;
+ break;
+ }
+ }
+ }
+}
+
+/* print the current sequence of active packet IDs */
+static const char *
+reliable_print_ids (const struct reliable *rel, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ int i;
+
+ buf_printf (&out, "[" packet_id_format "]", (packet_id_print_type)rel->packet_id);
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ buf_printf (&out, " " packet_id_format, (packet_id_print_type)e->packet_id);
+ }
+ return BSTR (&out);
+}
+
+/* true if at least one free buffer available */
+bool
+reliable_can_get (const struct reliable *rel)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (!e->active)
+ return true;
+ }
+ dmsg (D_REL_LOW, "ACK no free receive buffer available: %s", reliable_print_ids (rel, &gc));
+ gc_free (&gc);
+ return false;
+}
+
+/* make sure that incoming packet ID isn't a replay */
+bool
+reliable_not_replay (const struct reliable *rel, packet_id_type id)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ if (reliable_pid_min (id, rel->packet_id))
+ goto bad;
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active && e->packet_id == id)
+ goto bad;
+ }
+ gc_free (&gc);
+ return true;
+
+ bad:
+ dmsg (D_REL_DEBUG, "ACK " packet_id_format " is a replay: %s", (packet_id_print_type)id, reliable_print_ids (rel, &gc));
+ gc_free (&gc);
+ return false;
+}
+
+/* make sure that incoming packet ID won't deadlock the receive buffer */
+bool
+reliable_wont_break_sequentiality (const struct reliable *rel, packet_id_type id)
+{
+ struct gc_arena gc = gc_new ();
+
+ const int ret = reliable_pid_in_range2 (id, rel->packet_id, rel->size);
+
+ if (!ret)
+ {
+ dmsg (D_REL_LOW, "ACK " packet_id_format " breaks sequentiality: %s",
+ (packet_id_print_type)id, reliable_print_ids (rel, &gc));
+ }
+
+ dmsg (D_REL_DEBUG, "ACK RWBS rel->size=%d rel->packet_id=%08x id=%08x ret=%d\n", rel->size, rel->packet_id, id, ret);
+
+ gc_free (&gc);
+ return ret;
+}
+
+/* grab a free buffer */
+struct buffer *
+reliable_get_buf (struct reliable *rel)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (!e->active)
+ {
+ ASSERT (buf_init (&e->buf, rel->offset));
+ return &e->buf;
+ }
+ }
+ return NULL;
+}
+
+/* grab a free buffer, fail if buffer clogged by unacknowledged low packet IDs */
+struct buffer *
+reliable_get_buf_output_sequenced (struct reliable *rel)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ packet_id_type min_id = 0;
+ bool min_id_defined = false;
+ struct buffer *ret = NULL;
+
+ /* find minimum active packet_id */
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ {
+ if (!min_id_defined || reliable_pid_min (e->packet_id, min_id))
+ {
+ min_id_defined = true;
+ min_id = e->packet_id;
+ }
+ }
+ }
+
+ if (!min_id_defined || reliable_pid_in_range1 (rel->packet_id, min_id, rel->size))
+ {
+ ret = reliable_get_buf (rel);
+ }
+ else
+ {
+ dmsg (D_REL_LOW, "ACK output sequence broken: %s", reliable_print_ids (rel, &gc));
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+/* get active buffer for next sequentially increasing key ID */
+struct buffer *
+reliable_get_buf_sequenced (struct reliable *rel)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (e->active && e->packet_id == rel->packet_id)
+ {
+ return &e->buf;
+ }
+ }
+ return NULL;
+}
+
+/* return true if reliable_send would return a non-NULL result */
+bool
+reliable_can_send (const struct reliable *rel)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ int n_active = 0, n_current = 0;
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ {
+ ++n_active;
+ if (now >= e->next_try)
+ ++n_current;
+ }
+ }
+ dmsg (D_REL_DEBUG, "ACK reliable_can_send active=%d current=%d : %s",
+ n_active,
+ n_current,
+ reliable_print_ids (rel, &gc));
+
+ gc_free (&gc);
+ return n_current > 0 && !rel->hold;
+}
+
+#ifdef EXPONENTIAL_BACKOFF
+/* return a unique point-in-time to trigger retry */
+static time_t
+reliable_unique_retry (struct reliable *rel, time_t retry)
+{
+ int i;
+ while (true)
+ {
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (e->active && e->next_try == retry)
+ goto again;
+ }
+ break;
+ again:
+ ++retry;
+ }
+ return retry;
+}
+#endif
+
+/* return next buffer to send to remote */
+struct buffer *
+reliable_send (struct reliable *rel, int *opcode)
+{
+ int i;
+ struct reliable_entry *best = NULL;
+ const time_t local_now = now;
+
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (e->active && local_now >= e->next_try)
+ {
+ if (!best || reliable_pid_min (e->packet_id, best->packet_id))
+ best = e;
+ }
+ }
+ if (best)
+ {
+#ifdef EXPONENTIAL_BACKOFF
+ /* exponential backoff */
+ best->next_try = reliable_unique_retry (rel, local_now + best->timeout);
+ best->timeout *= 2;
+#else
+ /* constant timeout, no backoff */
+ best->next_try = local_now + best->timeout;
+#endif
+ *opcode = best->opcode;
+ dmsg (D_REL_DEBUG, "ACK reliable_send ID " packet_id_format " (size=%d to=%d)",
+ (packet_id_print_type)best->packet_id, best->buf.len,
+ (int)(best->next_try - local_now));
+ return &best->buf;
+ }
+ return NULL;
+}
+
+/* schedule all pending packets for immediate retransmit */
+void
+reliable_schedule_now (struct reliable *rel)
+{
+ int i;
+ dmsg (D_REL_DEBUG, "ACK reliable_schedule_now");
+ rel->hold = false;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ {
+ e->next_try = now;
+ e->timeout = rel->initial_timeout;
+ }
+ }
+}
+
+/* in how many seconds should we wake up to check for timeout */
+/* if we return BIG_TIMEOUT, nothing to wait for */
+interval_t
+reliable_send_timeout (const struct reliable *rel)
+{
+ struct gc_arena gc = gc_new ();
+ interval_t ret = BIG_TIMEOUT;
+ int i;
+ const time_t local_now = now;
+
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ {
+ if (e->next_try <= local_now)
+ {
+ ret = 0;
+ break;
+ }
+ else
+ {
+ ret = min_int (ret, e->next_try - local_now);
+ }
+ }
+ }
+
+ dmsg (D_REL_DEBUG, "ACK reliable_send_timeout %d %s",
+ (int) ret,
+ reliable_print_ids (rel, &gc));
+
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Enable an incoming buffer previously returned by a get function as active.
+ */
+
+void
+reliable_mark_active_incoming (struct reliable *rel, struct buffer *buf,
+ packet_id_type pid, int opcode)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (buf == &e->buf)
+ {
+ e->active = true;
+
+ /* packets may not arrive in sequential order */
+ e->packet_id = pid;
+
+ /* check for replay */
+ ASSERT (!reliable_pid_min (pid, rel->packet_id));
+
+ e->opcode = opcode;
+ e->next_try = 0;
+ e->timeout = 0;
+ dmsg (D_REL_DEBUG, "ACK mark active incoming ID " packet_id_format, (packet_id_print_type)e->packet_id);
+ return;
+ }
+ }
+ ASSERT (0); /* buf not found in rel */
+}
+
+/*
+ * Enable an outgoing buffer previously returned by a get function as active.
+ */
+
+void
+reliable_mark_active_outgoing (struct reliable *rel, struct buffer *buf, int opcode)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (buf == &e->buf)
+ {
+ /* Write mode, increment packet_id (i.e. sequence number)
+ linearly and prepend id to packet */
+ packet_id_type net_pid;
+ e->packet_id = rel->packet_id++;
+ net_pid = htonpid (e->packet_id);
+ ASSERT (buf_write_prepend (buf, &net_pid, sizeof (net_pid)));
+ e->active = true;
+ e->opcode = opcode;
+ e->next_try = 0;
+ e->timeout = rel->initial_timeout;
+ dmsg (D_REL_DEBUG, "ACK mark active outgoing ID " packet_id_format, (packet_id_print_type)e->packet_id);
+ return;
+ }
+ }
+ ASSERT (0); /* buf not found in rel */
+}
+
+/* delete a buffer previously activated by reliable_mark_active() */
+void
+reliable_mark_deleted (struct reliable *rel, struct buffer *buf, bool inc_pid)
+{
+ int i;
+ for (i = 0; i < rel->size; ++i)
+ {
+ struct reliable_entry *e = &rel->array[i];
+ if (buf == &e->buf)
+ {
+ e->active = false;
+ if (inc_pid)
+ rel->packet_id = e->packet_id + 1;
+ return;
+ }
+ }
+ ASSERT (0);
+}
+
+#if 0
+
+void
+reliable_ack_debug_print (const struct reliable_ack *ack, char *desc)
+{
+ int i;
+
+ printf ("********* struct reliable_ack %s\n", desc);
+ for (i = 0; i < ack->len; ++i)
+ {
+ printf (" %d: " packet_id_format "\n", i, (packet_id_print_type) ack->packet_id[i]);
+ }
+}
+
+void
+reliable_debug_print (const struct reliable *rel, char *desc)
+{
+ int i;
+ update_time ();
+
+ printf ("********* struct reliable %s\n", desc);
+ printf (" initial_timeout=%d\n", (int)rel->initial_timeout);
+ printf (" packet_id=" packet_id_format "\n", rel->packet_id);
+ printf (" now=" time_format "\n", now);
+ for (i = 0; i < rel->size; ++i)
+ {
+ const struct reliable_entry *e = &rel->array[i];
+ if (e->active)
+ {
+ printf (" %d: packet_id=" packet_id_format " len=%d", i, e->packet_id, e->buf.len);
+ printf (" next_try=" time_format, e->next_try);
+ printf ("\n");
+ }
+ }
+}
+
+#endif
+
+#else
+static void dummy(void) {}
+#endif /* USE_CRYPTO && USE_SSL*/
diff --git a/reliable.h b/reliable.h
new file mode 100644
index 0000000..9cf8eec
--- /dev/null
+++ b/reliable.h
@@ -0,0 +1,163 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This routines implement a reliability layer on top of UDP,
+ * so that TLS can be run over UDP.
+ */
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#ifndef RELIABLE_H
+#define RELIABLE_H
+
+#include "basic.h"
+#include "buffer.h"
+#include "packet_id.h"
+#include "session_id.h"
+#include "mtu.h"
+
+#define EXPONENTIAL_BACKOFF
+
+#define RELIABLE_ACK_SIZE 8
+
+struct reliable_ack
+{
+ int len;
+ packet_id_type packet_id[RELIABLE_ACK_SIZE];
+};
+
+/* no active buffers? */
+static inline bool
+reliable_ack_empty (struct reliable_ack *ack)
+{
+ return !ack->len;
+}
+
+/* get a packet_id from buf */
+bool reliable_ack_read_packet_id (struct buffer *buf, packet_id_type *pid);
+
+/* acknowledge a packet_id by adding it to a struct reliable_ack */
+bool reliable_ack_acknowledge_packet_id (struct reliable_ack *ack, packet_id_type pid);
+
+/* read a packet ID acknowledgement record from buf */
+bool reliable_ack_read (struct reliable_ack *ack,
+ struct buffer *buf, const struct session_id *sid);
+
+/* write a packet ID acknowledgement record to buf */
+bool reliable_ack_write (struct reliable_ack *ack,
+ struct buffer *buf,
+ const struct session_id *sid, int max, bool prepend);
+
+/* print a reliable ACK record coming off the wire */
+const char *reliable_ack_print (struct buffer *buf, bool verbose, struct gc_arena *gc);
+
+/* add to extra_frame the maximum number of bytes we will need for reliable_ack_write */
+void reliable_ack_adjust_frame_parameters (struct frame* frame, int max);
+
+void reliable_ack_debug_print (const struct reliable_ack *ack, char *desc);
+
+#define RELIABLE_CAPACITY 8
+
+struct reliable_entry
+{
+ bool active;
+ interval_t timeout;
+ time_t next_try;
+ packet_id_type packet_id;
+ int opcode;
+ struct buffer buf;
+};
+
+struct reliable
+{
+ int size;
+ interval_t initial_timeout;
+ packet_id_type packet_id;
+ int offset;
+ bool hold; /* don't xmit until reliable_schedule_now is called */
+ struct reliable_entry array[RELIABLE_CAPACITY];
+};
+
+void reliable_debug_print (const struct reliable *rel, char *desc);
+
+/* set sending timeout (after this time we send again until ACK) */
+static inline void
+reliable_set_timeout (struct reliable *rel, interval_t timeout)
+{
+ rel->initial_timeout = timeout;
+}
+
+void reliable_init (struct reliable *rel, int buf_size, int offset, int array_size, bool hold);
+
+void reliable_free (struct reliable *rel);
+
+/* no active buffers? */
+bool reliable_empty (const struct reliable *rel);
+
+/* in how many seconds should we wake up to check for timeout */
+interval_t reliable_send_timeout (const struct reliable *rel);
+
+/* del acknowledged items from send buf */
+void reliable_send_purge (struct reliable *rel, struct reliable_ack *ack);
+
+/* true if at least one free buffer available */
+bool reliable_can_get (const struct reliable *rel);
+
+/* make sure that incoming packet ID isn't a replay */
+bool reliable_not_replay (const struct reliable *rel, packet_id_type id);
+
+/* make sure that incoming packet ID won't deadlock the receive buffer */
+bool reliable_wont_break_sequentiality (const struct reliable *rel, packet_id_type id);
+
+/* grab a free buffer */
+struct buffer *reliable_get_buf (struct reliable *rel);
+
+/* grab a free buffer, fail if buffer clogged by unacknowledged low packet IDs */
+struct buffer *reliable_get_buf_output_sequenced (struct reliable *rel);
+
+/* get active buffer for next sequentially increasing key ID */
+struct buffer *reliable_get_buf_sequenced (struct reliable *rel);
+
+/* return true if reliable_send would return a non-NULL result */
+bool reliable_can_send (const struct reliable *rel);
+
+/* return next buffer to send to remote */
+struct buffer *reliable_send (struct reliable *rel, int *opcode);
+
+/* schedule all pending packets for immediate retransmit */
+void reliable_schedule_now (struct reliable *rel);
+
+/* enable an incoming buffer previously returned by a get function as active */
+void reliable_mark_active_incoming (struct reliable *rel, struct buffer *buf,
+ packet_id_type pid, int opcode);
+
+/* enable an outgoing buffer previously returned by a get function as active. */
+void reliable_mark_active_outgoing (struct reliable *rel, struct buffer *buf, int opcode);
+
+/* delete a buffer previously activated by reliable_mark_active() */
+void reliable_mark_deleted (struct reliable *rel, struct buffer *buf, bool inc_pid);
+
+#endif /* RELIABLE_H */
+#endif /* USE_CRYPTO && USE_SSL */
diff --git a/route.c b/route.c
new file mode 100644
index 0000000..b5092fe
--- /dev/null
+++ b/route.c
@@ -0,0 +1,2404 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Support routines for adding/deleting network routes.
+ */
+
+#include "syshead.h"
+
+#include "common.h"
+#include "error.h"
+#include "route.h"
+#include "misc.h"
+#include "socket.h"
+#include "manage.h"
+#include "win32.h"
+
+#include "memdbg.h"
+
+static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
+
+#ifdef ENABLE_DEBUG
+
+static void
+print_bypass_addresses (const struct route_bypass *rb)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ for (i = 0; i < rb->n_bypass; ++i)
+ {
+ msg (D_ROUTE, "ROUTE: bypass_host_route[%d]=%s",
+ i,
+ print_in_addr_t (rb->bypass[i], 0, &gc));
+ }
+ gc_free (&gc);
+}
+
+#endif
+
+struct route_option_list *
+new_route_option_list (const int max_routes, struct gc_arena *a)
+{
+ struct route_option_list *ret;
+ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_option_list, struct route_option, max_routes, a);
+ ret->capacity = max_routes;
+ return ret;
+}
+
+struct route_option_list *
+clone_route_option_list (const struct route_option_list *src, struct gc_arena *a)
+{
+ const size_t rl_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list));
+ struct route_option_list *ret = gc_malloc (rl_size, false, a);
+ memcpy (ret, src, rl_size);
+ return ret;
+}
+
+void
+copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src)
+{
+ const size_t src_size = array_mult_safe (sizeof(struct route_option), src->capacity, sizeof(struct route_option_list));
+ if (src->n > dest->capacity)
+ msg (M_FATAL, PACKAGE_NAME " ROUTE: (copy) number of route options in src (%d) is greater than route list capacity in dest (%d)", src->n, dest->capacity);
+ memcpy (dest, src, src_size);
+}
+
+struct route_list *
+new_route_list (const int max_routes, struct gc_arena *a)
+{
+ struct route_list *ret;
+ ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_list, struct route, max_routes, a);
+ ret->capacity = max_routes;
+ return ret;
+}
+
+static const char *
+route_string (const struct route *r, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ buf_printf (&out, "ROUTE network %s netmask %s gateway %s",
+ print_in_addr_t (r->network, 0, gc),
+ print_in_addr_t (r->netmask, 0, gc),
+ print_in_addr_t (r->gateway, 0, gc)
+ );
+ if (r->metric_defined)
+ buf_printf (&out, " metric %d", r->metric);
+ return BSTR (&out);
+}
+
+static bool
+is_route_parm_defined (const char *parm)
+{
+ if (!parm)
+ return false;
+ if (!strcmp (parm, "default"))
+ return false;
+ return true;
+}
+
+static void
+setenv_route_addr (struct env_set *es, const char *key, const in_addr_t addr, int i)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer name = alloc_buf_gc (256, &gc);
+ if (i >= 0)
+ buf_printf (&name, "route_%s_%d", key, i);
+ else
+ buf_printf (&name, "route_%s", key);
+ setenv_str (es, BSTR (&name), print_in_addr_t (addr, 0, &gc));
+ gc_free (&gc);
+}
+
+static bool
+get_special_addr (const struct route_special_addr *spec,
+ const char *string,
+ in_addr_t *out,
+ bool *status)
+{
+ if (status)
+ *status = true;
+ if (!strcmp (string, "vpn_gateway"))
+ {
+ if (spec)
+ {
+ if (spec->remote_endpoint_defined)
+ *out = spec->remote_endpoint;
+ else
+ {
+ msg (M_INFO, PACKAGE_NAME " ROUTE: vpn_gateway undefined");
+ if (status)
+ *status = false;
+ }
+ }
+ return true;
+ }
+ else if (!strcmp (string, "net_gateway"))
+ {
+ if (spec)
+ {
+ if (spec->net_gateway_defined)
+ *out = spec->net_gateway;
+ else
+ {
+ msg (M_INFO, PACKAGE_NAME " ROUTE: net_gateway undefined -- unable to get default gateway from system");
+ if (status)
+ *status = false;
+ }
+ }
+ return true;
+ }
+ else if (!strcmp (string, "remote_host"))
+ {
+ if (spec)
+ {
+ if (spec->remote_host_defined)
+ *out = spec->remote_host;
+ else
+ {
+ msg (M_INFO, PACKAGE_NAME " ROUTE: remote_host undefined");
+ if (status)
+ *status = false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+is_special_addr (const char *addr_str)
+{
+ if (addr_str)
+ return get_special_addr (NULL, addr_str, NULL, NULL);
+ else
+ return false;
+}
+
+static bool
+init_route (struct route *r,
+ struct resolve_list *network_list,
+ const struct route_option *ro,
+ const struct route_special_addr *spec)
+{
+ const in_addr_t default_netmask = ~0;
+ bool status;
+
+ r->option = ro;
+ r->defined = false;
+
+ /* network */
+
+ if (!is_route_parm_defined (ro->network))
+ {
+ goto fail;
+ }
+
+ if (!get_special_addr (spec, ro->network, &r->network, &status))
+ {
+ r->network = getaddr_multi (
+ GETADDR_RESOLVE
+ | GETADDR_HOST_ORDER
+ | GETADDR_WARN_ON_SIGNAL,
+ ro->network,
+ 0,
+ &status,
+ NULL,
+ network_list);
+ }
+
+ if (!status)
+ goto fail;
+
+ /* netmask */
+
+ if (is_route_parm_defined (ro->netmask))
+ {
+ r->netmask = getaddr (
+ GETADDR_HOST_ORDER
+ | GETADDR_WARN_ON_SIGNAL,
+ ro->netmask,
+ 0,
+ &status,
+ NULL);
+ if (!status)
+ goto fail;
+ }
+ else
+ r->netmask = default_netmask;
+
+ /* gateway */
+
+ if (is_route_parm_defined (ro->gateway))
+ {
+ if (!get_special_addr (spec, ro->gateway, &r->gateway, &status))
+ {
+ r->gateway = getaddr (
+ GETADDR_RESOLVE
+ | GETADDR_HOST_ORDER
+ | GETADDR_WARN_ON_SIGNAL,
+ ro->gateway,
+ 0,
+ &status,
+ NULL);
+ }
+ if (!status)
+ goto fail;
+ }
+ else
+ {
+ if (spec->remote_endpoint_defined)
+ r->gateway = spec->remote_endpoint;
+ else
+ {
+ msg (M_WARN, PACKAGE_NAME " ROUTE: " PACKAGE_NAME " needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options");
+ goto fail;
+ }
+ }
+
+ /* metric */
+
+ r->metric_defined = false;
+ r->metric = 0;
+ if (is_route_parm_defined (ro->metric))
+ {
+ r->metric = atoi (ro->metric);
+ if (r->metric < 0)
+ {
+ msg (M_WARN, PACKAGE_NAME " ROUTE: route metric for network %s (%s) must be >= 0",
+ ro->network,
+ ro->metric);
+ goto fail;
+ }
+ r->metric_defined = true;
+ }
+ else if (spec->default_metric_defined)
+ {
+ r->metric = spec->default_metric;
+ r->metric_defined = true;
+ }
+
+ r->defined = true;
+
+ return true;
+
+ fail:
+ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
+ ro->network);
+ r->defined = false;
+ return false;
+}
+
+void
+add_route_to_option_list (struct route_option_list *l,
+ const char *network,
+ const char *netmask,
+ const char *gateway,
+ const char *metric)
+{
+ struct route_option *ro;
+ if (l->n >= l->capacity)
+ msg (M_FATAL, PACKAGE_NAME " ROUTE: cannot add more than %d routes -- please increase the max-routes option in the client configuration file",
+ l->capacity);
+ ro = &l->routes[l->n];
+ ro->network = network;
+ ro->netmask = netmask;
+ ro->gateway = gateway;
+ ro->metric = metric;
+ ++l->n;
+}
+
+void
+clear_route_list (struct route_list *rl)
+{
+ const int capacity = rl->capacity;
+ const size_t rl_size = array_mult_safe (sizeof(struct route), capacity, sizeof(struct route_list));
+ memset(rl, 0, rl_size);
+ rl->capacity = capacity;
+}
+
+void
+route_list_add_default_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
+{
+ rl->spec.remote_endpoint = addr;
+ rl->spec.remote_endpoint_defined = true;
+ setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
+}
+
+bool
+init_route_list (struct route_list *rl,
+ const struct route_option_list *opt,
+ const char *remote_endpoint,
+ int default_metric,
+ in_addr_t remote_host,
+ struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = true;
+
+ clear_route_list (rl);
+
+ rl->flags = opt->flags;
+
+ if (remote_host)
+ {
+ rl->spec.remote_host = remote_host;
+ rl->spec.remote_host_defined = true;
+ }
+
+ if (default_metric)
+ {
+ rl->spec.default_metric = default_metric;
+ rl->spec.default_metric_defined = true;
+ }
+
+ rl->spec.net_gateway_defined = get_default_gateway (&rl->spec.net_gateway, NULL);
+ if (rl->spec.net_gateway_defined)
+ {
+ setenv_route_addr (es, "net_gateway", rl->spec.net_gateway, -1);
+ dmsg (D_ROUTE, "ROUTE default_gateway=%s", print_in_addr_t (rl->spec.net_gateway, 0, &gc));
+ }
+ else
+ {
+ dmsg (D_ROUTE, "ROUTE: default_gateway=UNDEF");
+ }
+
+ if (rl->flags & RG_ENABLE)
+ {
+ get_bypass_addresses (&rl->spec.bypass, rl->flags);
+#ifdef ENABLE_DEBUG
+ print_bypass_addresses (&rl->spec.bypass);
+#endif
+ }
+
+ if (is_route_parm_defined (remote_endpoint))
+ {
+ rl->spec.remote_endpoint = getaddr (
+ GETADDR_RESOLVE
+ | GETADDR_HOST_ORDER
+ | GETADDR_WARN_ON_SIGNAL,
+ remote_endpoint,
+ 0,
+ &rl->spec.remote_endpoint_defined,
+ NULL);
+
+ if (rl->spec.remote_endpoint_defined)
+ {
+ setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
+ }
+ else
+ {
+ msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve default gateway: %s",
+ remote_endpoint);
+ ret = false;
+ }
+ }
+ else
+ rl->spec.remote_endpoint_defined = false;
+
+ /* parse the routes from opt to rl */
+ {
+ int i, j = 0;
+ bool warned = false;
+ for (i = 0; i < opt->n; ++i)
+ {
+ struct resolve_list netlist;
+ struct route r;
+ int k;
+
+ CLEAR(netlist); /* init_route() will not always init this */
+
+ if (!init_route (&r,
+ &netlist,
+ &opt->routes[i],
+ &rl->spec))
+ ret = false;
+ else
+ {
+ if (!netlist.len)
+ {
+ netlist.data[0] = r.network;
+ netlist.len = 1;
+ }
+ for (k = 0; k < netlist.len; ++k)
+ {
+ if (j < rl->capacity)
+ {
+ r.network = netlist.data[k];
+ rl->routes[j++] = r;
+ }
+ else
+ {
+ if (!warned)
+ {
+ msg (M_WARN, PACKAGE_NAME " ROUTE: routes dropped because number of expanded routes is greater than route list capacity (%d)", rl->capacity);
+ warned = true;
+ }
+ }
+ }
+ }
+ }
+ rl->n = j;
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+static void
+add_route3 (in_addr_t network,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es)
+{
+ struct route r;
+ CLEAR (r);
+ r.defined = true;
+ r.network = network;
+ r.netmask = netmask;
+ r.gateway = gateway;
+ add_route (&r, tt, flags, es);
+}
+
+static void
+del_route3 (in_addr_t network,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es)
+{
+ struct route r;
+ CLEAR (r);
+ r.defined = true;
+ r.network = network;
+ r.netmask = netmask;
+ r.gateway = gateway;
+ delete_route (&r, tt, flags, es);
+}
+
+static void
+add_bypass_routes (struct route_bypass *rb,
+ in_addr_t gateway,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es)
+{
+ int i;
+ for (i = 0; i < rb->n_bypass; ++i)
+ {
+ if (rb->bypass[i] != gateway)
+ add_route3 (rb->bypass[i],
+ ~0,
+ gateway,
+ tt,
+ flags,
+ es);
+ }
+}
+
+static void
+del_bypass_routes (struct route_bypass *rb,
+ in_addr_t gateway,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es)
+{
+ int i;
+ for (i = 0; i < rb->n_bypass; ++i)
+ {
+ if (rb->bypass[i] != gateway)
+ del_route3 (rb->bypass[i],
+ ~0,
+ gateway,
+ tt,
+ flags,
+ es);
+ }
+}
+
+static void
+redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ const char err[] = "NOTE: unable to redirect default gateway --";
+
+ if (rl->flags & RG_ENABLE)
+ {
+ if (!rl->spec.remote_endpoint_defined)
+ {
+ msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err);
+ }
+ else if (!rl->spec.net_gateway_defined)
+ {
+ msg (M_WARN, "%s Cannot read current default gateway from system", err);
+ }
+ else if (!rl->spec.remote_host_defined)
+ {
+ msg (M_WARN, "%s Cannot obtain current remote host address", err);
+ }
+ else
+ {
+ bool local = BOOL_CAST(rl->flags & RG_LOCAL);
+ if (rl->flags & RG_AUTO_LOCAL) {
+ const int tla = test_local_addr (rl->spec.remote_host);
+ if (tla == TLA_NONLOCAL)
+ {
+ dmsg (D_ROUTE, "ROUTE remote_host is NOT LOCAL");
+ local = false;
+ }
+ else if (tla == TLA_LOCAL)
+ {
+ dmsg (D_ROUTE, "ROUTE remote_host is LOCAL");
+ local = true;
+ }
+ }
+ if (!local)
+ {
+ /* route remote host to original default gateway */
+ add_route3 (rl->spec.remote_host,
+ ~0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+ rl->did_local = true;
+ }
+
+ /* route DHCP/DNS server traffic through original default gateway */
+ add_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+
+ if (rl->flags & RG_REROUTE_GW)
+ {
+ if (rl->flags & RG_DEF1)
+ {
+ /* add new default route (1st component) */
+ add_route3 (0x00000000,
+ 0x80000000,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+
+ /* add new default route (2nd component) */
+ add_route3 (0x80000000,
+ 0x80000000,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+ }
+ else
+ {
+ /* delete default route */
+ del_route3 (0,
+ 0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+
+ /* add new default route */
+ add_route3 (0,
+ 0,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+ }
+ }
+
+ /* set a flag so we can undo later */
+ rl->did_redirect_default_gateway = true;
+ }
+ }
+}
+
+static void
+undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ if (rl->did_redirect_default_gateway)
+ {
+ /* delete remote host route */
+ if (rl->did_local)
+ {
+ del_route3 (rl->spec.remote_host,
+ ~0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+ rl->did_local = false;
+ }
+
+ /* delete special DHCP/DNS bypass route */
+ del_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+
+ if (rl->flags & RG_REROUTE_GW)
+ {
+ if (rl->flags & RG_DEF1)
+ {
+ /* delete default route (1st component) */
+ del_route3 (0x00000000,
+ 0x80000000,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+
+ /* delete default route (2nd component) */
+ del_route3 (0x80000000,
+ 0x80000000,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+ }
+ else
+ {
+ /* delete default route */
+ del_route3 (0,
+ 0,
+ rl->spec.remote_endpoint,
+ tt,
+ flags,
+ es);
+
+ /* restore original default route */
+ add_route3 (0,
+ 0,
+ rl->spec.net_gateway,
+ tt,
+ flags,
+ es);
+ }
+ }
+
+ rl->did_redirect_default_gateway = false;
+ }
+}
+
+void
+add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ redirect_default_route_to_vpn (rl, tt, flags, es);
+ if (!rl->routes_added)
+ {
+ int i;
+
+#ifdef ENABLE_MANAGEMENT
+ if (management && rl->n)
+ {
+ management_set_state (management,
+ OPENVPN_STATE_ADD_ROUTES,
+ NULL,
+ 0,
+ 0);
+ }
+#endif
+
+ for (i = 0; i < rl->n; ++i)
+ {
+ struct route *r = &rl->routes[i];
+ check_subnet_conflict (r->network, r->netmask, "route");
+ if (flags & ROUTE_DELETE_FIRST)
+ delete_route (r, tt, flags, es);
+ add_route (r, tt, flags, es);
+ }
+ rl->routes_added = true;
+ }
+}
+
+void
+delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ if (rl->routes_added)
+ {
+ int i;
+ for (i = rl->n - 1; i >= 0; --i)
+ {
+ const struct route *r = &rl->routes[i];
+ delete_route (r, tt, flags, es);
+ }
+ rl->routes_added = false;
+ }
+ undo_redirect_default_route_to_vpn (rl, tt, flags, es);
+
+ clear_route_list (rl);
+}
+
+#ifdef ENABLE_DEBUG
+
+static const char *
+show_opt (const char *option)
+{
+ if (!option)
+ return "nil";
+ else
+ return option;
+}
+
+static void
+print_route_option (const struct route_option *ro, int level)
+{
+ msg (level, " route %s/%s/%s/%s",
+ show_opt (ro->network),
+ show_opt (ro->netmask),
+ show_opt (ro->gateway),
+ show_opt (ro->metric));
+}
+
+void
+print_route_options (const struct route_option_list *rol,
+ int level)
+{
+ int i;
+ if (rol->flags & RG_ENABLE)
+ msg (level, " [redirect_default_gateway local=%d]",
+ (rol->flags & RG_LOCAL) != 0);
+ for (i = 0; i < rol->n; ++i)
+ print_route_option (&rol->routes[i], level);
+}
+
+#endif
+
+static void
+print_route (const struct route *r, int level)
+{
+ struct gc_arena gc = gc_new ();
+ if (r->defined)
+ msg (level, "%s", route_string (r, &gc));
+ gc_free (&gc);
+}
+
+void
+print_routes (const struct route_list *rl, int level)
+{
+ int i;
+ for (i = 0; i < rl->n; ++i)
+ print_route (&rl->routes[i], level);
+}
+
+static void
+setenv_route (struct env_set *es, const struct route *r, int i)
+{
+ struct gc_arena gc = gc_new ();
+ if (r->defined)
+ {
+ setenv_route_addr (es, "network", r->network, i);
+ setenv_route_addr (es, "netmask", r->netmask, i);
+ setenv_route_addr (es, "gateway", r->gateway, i);
+
+ if (r->metric_defined)
+ {
+ struct buffer name = alloc_buf_gc (256, &gc);
+ buf_printf (&name, "route_metric_%d", i);
+ setenv_int (es, BSTR (&name), r->metric);
+ }
+ }
+ gc_free (&gc);
+}
+
+void
+setenv_routes (struct env_set *es, const struct route_list *rl)
+{
+ int i;
+ for (i = 0; i < rl->n; ++i)
+ setenv_route (es, &rl->routes[i], i + 1);
+}
+
+void
+add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ struct gc_arena gc;
+ struct argv argv;
+ const char *network;
+ const char *netmask;
+ const char *gateway;
+ bool status = false;
+
+ if (!r->defined)
+ return;
+
+ gc_init (&gc);
+ argv_init (&argv);
+
+ network = print_in_addr_t (r->network, 0, &gc);
+ netmask = print_in_addr_t (r->netmask, 0, &gc);
+ gateway = print_in_addr_t (r->gateway, 0, &gc);
+
+ /*
+ * Filter out routes which are essentially no-ops
+ */
+ if (r->network == r->gateway && r->netmask == 0xFFFFFFFF)
+ {
+ msg (M_INFO, PACKAGE_NAME " ROUTE: omitted no-op route: %s/%s -> %s",
+ network, netmask, gateway);
+ goto done;
+ }
+
+#if defined(TARGET_LINUX)
+#ifdef CONFIG_FEATURE_IPROUTE
+ argv_printf (&argv, "%s route add %s/%d via %s",
+ iproute_path,
+ network,
+ count_netmask_bits(netmask),
+ gateway);
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "metric %d", r->metric);
+
+#else
+ argv_printf (&argv, "%s add -net %s netmask %s gw %s",
+ ROUTE_PATH,
+ network,
+ netmask,
+ gateway);
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "metric %d", r->metric);
+#endif /*CONFIG_FEATURE_IPROUTE*/
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
+
+#elif defined (WIN32)
+
+ argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "METRIC %d", r->metric);
+
+ argv_msg (D_ROUTE, &argv);
+
+ if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
+ {
+ status = add_route_ipapi (r, tt);
+ msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
+ {
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
+ netcmd_semaphore_release ();
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
+ {
+ status = add_route_ipapi (r, tt);
+ msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
+ if (!status)
+ {
+ msg (D_ROUTE, "Route addition fallback to route.exe");
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
+ netcmd_semaphore_release ();
+ }
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+#elif defined (TARGET_SOLARIS)
+
+ /* example: route add 192.0.2.32 -netmask 255.255.255.224 somegateway */
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+
+ argv_printf_cat (&argv, "%s -netmask %s %s",
+ network,
+ netmask,
+ gateway);
+
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "%d", r->metric);
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
+
+#elif defined(TARGET_FREEBSD)
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+
+#if 0
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
+#endif
+
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
+
+#elif defined(TARGET_DRAGONFLY)
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+
+#if 0
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
+#endif
+
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
+
+#elif defined(TARGET_DARWIN)
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+
+#if 0
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
+#endif
+
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
+
+#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
+
+ argv_printf (&argv, "%s add",
+ ROUTE_PATH);
+
+#if 0
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
+#endif
+
+ argv_printf_cat (&argv, "-net %s %s -netmask %s",
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
+
+#else
+ msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
+#endif
+
+ done:
+ r->defined = status;
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
+static void
+delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+{
+ struct gc_arena gc;
+ struct argv argv;
+ const char *network;
+ const char *netmask;
+ const char *gateway;
+
+ if (!r->defined)
+ return;
+
+ gc_init (&gc);
+ argv_init (&argv);
+
+ network = print_in_addr_t (r->network, 0, &gc);
+ netmask = print_in_addr_t (r->netmask, 0, &gc);
+ gateway = print_in_addr_t (r->gateway, 0, &gc);
+
+#if defined(TARGET_LINUX)
+#ifdef CONFIG_FEATURE_IPROUTE
+ argv_printf (&argv, "%s route del %s/%d",
+ iproute_path,
+ network,
+ count_netmask_bits(netmask));
+#else
+
+ argv_printf (&argv, "%s del -net %s netmask %s",
+ ROUTE_PATH,
+ network,
+ netmask);
+#endif /*CONFIG_FEATURE_IPROUTE*/
+ if (r->metric_defined)
+ argv_printf_cat (&argv, "metric %d", r->metric);
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
+
+#elif defined (WIN32)
+
+ argv_printf (&argv, "%s%sc DELETE %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
+
+ argv_msg (D_ROUTE, &argv);
+
+ if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
+ {
+ const bool status = del_route_ipapi (r, tt);
+ msg (D_ROUTE, "Route deletion via IPAPI %s", status ? "succeeded" : "failed");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
+ {
+ netcmd_semaphore_lock ();
+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed");
+ netcmd_semaphore_release ();
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
+ {
+ const bool status = del_route_ipapi (r, tt);
+ msg (D_ROUTE, "Route deletion via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
+ if (!status)
+ {
+ msg (D_ROUTE, "Route deletion fallback to route.exe");
+ netcmd_semaphore_lock ();
+ openvpn_execve_check (&argv, es, 0, "ERROR: Windows route delete command failed [adaptive]");
+ netcmd_semaphore_release ();
+ }
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+#elif defined (TARGET_SOLARIS)
+
+ argv_printf (&argv, "%s delete %s -netmask %s %s",
+ ROUTE_PATH,
+ network,
+ netmask,
+ gateway);
+
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route delete command failed");
+
+#elif defined(TARGET_FREEBSD)
+
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route delete command failed");
+
+#elif defined(TARGET_DRAGONFLY)
+
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route delete command failed");
+
+#elif defined(TARGET_DARWIN)
+
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
+
+#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
+
+ argv_printf (&argv, "%s delete -net %s %s -netmask %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+
+ argv_msg (D_ROUTE, &argv);
+ openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route delete command failed");
+
+#else
+ msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
+#endif
+
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
+/*
+ * The --redirect-gateway option requires OS-specific code below
+ * to get the current default gateway.
+ */
+
+#if defined(WIN32)
+
+static const MIB_IPFORWARDTABLE *
+get_windows_routing_table (struct gc_arena *gc)
+{
+ ULONG size = 0;
+ PMIB_IPFORWARDTABLE rt = NULL;
+ DWORD status;
+
+ status = GetIpForwardTable (NULL, &size, TRUE);
+ if (status == ERROR_INSUFFICIENT_BUFFER)
+ {
+ rt = (PMIB_IPFORWARDTABLE) gc_malloc (size, false, gc);
+ status = GetIpForwardTable (rt, &size, TRUE);
+ if (status != NO_ERROR)
+ {
+ msg (D_ROUTE, "NOTE: GetIpForwardTable returned error: %s (code=%u)",
+ strerror_win32 (status, gc),
+ (unsigned int)status);
+ rt = NULL;
+ }
+ }
+ return rt;
+}
+
+static int
+test_route (const IP_ADAPTER_INFO *adapters,
+ const in_addr_t gateway,
+ DWORD *index)
+{
+ int count = 0;
+ DWORD i = adapter_index_of_ip (adapters, gateway, &count, NULL);
+ if (index)
+ *index = i;
+ return count;
+}
+
+static void
+test_route_helper (bool *ret,
+ int *count,
+ int *good,
+ int *ambig,
+ const IP_ADAPTER_INFO *adapters,
+ const in_addr_t gateway)
+{
+ int c;
+
+ ++*count;
+ c = test_route (adapters, gateway, NULL);
+ if (c == 0)
+ *ret = false;
+ else
+ ++*good;
+ if (c > 1)
+ ++*ambig;
+}
+
+/*
+ * If we tried to add routes now, would we succeed?
+ */
+bool
+test_routes (const struct route_list *rl, const struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+ const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
+ bool ret = false;
+ int count = 0;
+ int good = 0;
+ int ambig = 0;
+ bool adapter_up = false;
+
+ if (is_adapter_up (tt, adapters))
+ {
+ ret = true;
+ adapter_up = true;
+
+ if (rl)
+ {
+ int i;
+ for (i = 0; i < rl->n; ++i)
+ test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway);
+
+ if ((rl->flags & RG_ENABLE) && rl->spec.remote_endpoint_defined)
+ test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint);
+ }
+ }
+
+ msg (D_ROUTE, "TEST ROUTES: %d/%d succeeded len=%d ret=%d a=%d u/d=%s",
+ good,
+ count,
+ rl ? rl->n : -1,
+ (int)ret,
+ ambig,
+ adapter_up ? "up" : "down");
+
+ gc_free (&gc);
+ return ret;
+}
+
+static const MIB_IPFORWARDROW *
+get_default_gateway_row (const MIB_IPFORWARDTABLE *routes)
+{
+ struct gc_arena gc = gc_new ();
+ DWORD lowest_metric = ~0;
+ const MIB_IPFORWARDROW *ret = NULL;
+ int i;
+ int best = -1;
+
+ if (routes)
+ {
+ for (i = 0; i < routes->dwNumEntries; ++i)
+ {
+ const MIB_IPFORWARDROW *row = &routes->table[i];
+ const in_addr_t net = ntohl (row->dwForwardDest);
+ const in_addr_t mask = ntohl (row->dwForwardMask);
+ const DWORD index = row->dwForwardIfIndex;
+ const DWORD metric = row->dwForwardMetric1;
+
+ dmsg (D_ROUTE_DEBUG, "GDGR: route[%d] %s/%s i=%d m=%d",
+ i,
+ print_in_addr_t ((in_addr_t) net, 0, &gc),
+ print_in_addr_t ((in_addr_t) mask, 0, &gc),
+ (int)index,
+ (int)metric);
+
+ if (!net && !mask && metric < lowest_metric)
+ {
+ ret = row;
+ lowest_metric = metric;
+ best = i;
+ }
+ }
+ }
+
+ dmsg (D_ROUTE_DEBUG, "GDGR: best=%d lm=%u", best, (unsigned int)lowest_metric);
+
+ gc_free (&gc);
+ return ret;
+}
+
+bool
+get_default_gateway (in_addr_t *gw, in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret_bool = false;
+
+ const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
+ const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);
+ const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);
+
+ if (row)
+ {
+ *gw = ntohl (row->dwForwardNextHop);
+ if (netmask)
+ {
+ if (adapter_index_of_ip (adapters, *gw, NULL, netmask) == ~0)
+ *netmask = ~0;
+ }
+ ret_bool = true;
+ }
+
+ gc_free (&gc);
+ return ret_bool;
+}
+
+static DWORD
+windows_route_find_if_index (const struct route *r, const struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+ DWORD ret = ~0;
+ int count = 0;
+ const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
+ const IP_ADAPTER_INFO *tun_adapter = get_tun_adapter (tt, adapters);
+ bool on_tun = false;
+
+ /* first test on tun interface */
+ if (is_ip_in_adapter_subnet (tun_adapter, r->gateway, NULL))
+ {
+ ret = tun_adapter->Index;
+ count = 1;
+ on_tun = true;
+ }
+ else /* test on other interfaces */
+ {
+ count = test_route (adapters, r->gateway, &ret);
+ }
+
+ if (count == 0)
+ {
+ msg (M_WARN, "Warning: route gateway is not reachable on any active network adapters: %s",
+ print_in_addr_t (r->gateway, 0, &gc));
+ ret = ~0;
+ }
+ else if (count > 1)
+ {
+ msg (M_WARN, "Warning: route gateway is ambiguous: %s (%d matches)",
+ print_in_addr_t (r->gateway, 0, &gc),
+ count);
+ ret = ~0;
+ }
+
+ dmsg (D_ROUTE_DEBUG, "DEBUG: route find if: on_tun=%d count=%d index=%d",
+ on_tun,
+ count,
+ (int)ret);
+
+ gc_free (&gc);
+ return ret;
+}
+
+bool
+add_route_ipapi (const struct route *r, const struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+ DWORD status;
+ const DWORD if_index = windows_route_find_if_index (r, tt);
+
+ if (if_index != ~0)
+ {
+ MIB_IPFORWARDROW fr;
+ CLEAR (fr);
+ fr.dwForwardDest = htonl (r->network);
+ fr.dwForwardMask = htonl (r->netmask);
+ fr.dwForwardPolicy = 0;
+ fr.dwForwardNextHop = htonl (r->gateway);
+ fr.dwForwardIfIndex = if_index;
+ fr.dwForwardType = 4; /* the next hop is not the final dest */
+ fr.dwForwardProto = 3; /* PROTO_IP_NETMGMT */
+ fr.dwForwardAge = 0;
+ fr.dwForwardNextHopAS = 0;
+ fr.dwForwardMetric1 = r->metric_defined ? r->metric : 1;
+ fr.dwForwardMetric2 = ~0;
+ fr.dwForwardMetric3 = ~0;
+ fr.dwForwardMetric4 = ~0;
+ fr.dwForwardMetric5 = ~0;
+
+ if ((r->network & r->netmask) != r->network)
+ msg (M_WARN, "Warning: address %s is not a network address in relation to netmask %s",
+ print_in_addr_t (r->network, 0, &gc),
+ print_in_addr_t (r->netmask, 0, &gc));
+
+ status = CreateIpForwardEntry (&fr);
+
+ if (status == NO_ERROR)
+ ret = true;
+ else
+ {
+ /* failed, try increasing the metric to work around Vista issue */
+ const unsigned int forward_metric_limit = 2048; /* iteratively retry higher metrics up to this limit */
+
+ for ( ; fr.dwForwardMetric1 <= forward_metric_limit; ++fr.dwForwardMetric1)
+ {
+ /* try a different forward type=3 ("the next hop is the final dest") in addition to 4.
+ --redirect-gateway over RRAS seems to need this. */
+ for (fr.dwForwardType = 4; fr.dwForwardType >= 3; --fr.dwForwardType)
+ {
+ status = CreateIpForwardEntry (&fr);
+ if (status == NO_ERROR)
+ {
+ msg (D_ROUTE, "ROUTE: CreateIpForwardEntry succeeded with dwForwardMetric1=%u and dwForwardType=%u",
+ (unsigned int)fr.dwForwardMetric1,
+ (unsigned int)fr.dwForwardType);
+ ret = true;
+ goto doublebreak;
+ }
+ else if (status != ERROR_BAD_ARGUMENTS)
+ goto doublebreak;
+ }
+ }
+
+ doublebreak:
+ if (status != NO_ERROR)
+ msg (M_WARN, "ROUTE: route addition failed using CreateIpForwardEntry: %s [status=%u if_index=%u]",
+ strerror_win32 (status, &gc),
+ (unsigned int)status,
+ (unsigned int)if_index);
+ }
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+bool
+del_route_ipapi (const struct route *r, const struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+ DWORD status;
+ const DWORD if_index = windows_route_find_if_index (r, tt);
+
+ if (if_index != ~0)
+ {
+ MIB_IPFORWARDROW fr;
+ CLEAR (fr);
+
+ fr.dwForwardDest = htonl (r->network);
+ fr.dwForwardMask = htonl (r->netmask);
+ fr.dwForwardPolicy = 0;
+ fr.dwForwardNextHop = htonl (r->gateway);
+ fr.dwForwardIfIndex = if_index;
+
+ status = DeleteIpForwardEntry (&fr);
+
+ if (status == NO_ERROR)
+ ret = true;
+ else
+ msg (M_WARN, "ROUTE: route deletion failed using DeleteIpForwardEntry: %s",
+ strerror_win32 (status, &gc));
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+static const char *
+format_route_entry (const MIB_IPFORWARDROW *r, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ buf_printf (&out, "%s %s %s p=%d i=%d t=%d pr=%d a=%d h=%d m=%d/%d/%d/%d/%d",
+ print_in_addr_t (r->dwForwardDest, IA_NET_ORDER, gc),
+ print_in_addr_t (r->dwForwardMask, IA_NET_ORDER, gc),
+ print_in_addr_t (r->dwForwardNextHop, IA_NET_ORDER, gc),
+ (int)r->dwForwardPolicy,
+ (int)r->dwForwardIfIndex,
+ (int)r->dwForwardType,
+ (int)r->dwForwardProto,
+ (int)r->dwForwardAge,
+ (int)r->dwForwardNextHopAS,
+ (int)r->dwForwardMetric1,
+ (int)r->dwForwardMetric2,
+ (int)r->dwForwardMetric3,
+ (int)r->dwForwardMetric4,
+ (int)r->dwForwardMetric5);
+ return BSTR (&out);
+}
+
+/*
+ * Show current routing table
+ */
+void
+show_routes (int msglev)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+
+ const MIB_IPFORWARDTABLE *rt = get_windows_routing_table (&gc);
+
+ msg (msglev, "SYSTEM ROUTING TABLE");
+ if (rt)
+ {
+ for (i = 0; i < rt->dwNumEntries; ++i)
+ {
+ msg (msglev, "%s", format_route_entry (&rt->table[i], &gc));
+ }
+ }
+ gc_free (&gc);
+}
+
+#elif defined(TARGET_LINUX)
+
+bool
+get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+ FILE *fp = fopen ("/proc/net/route", "r");
+ if (fp)
+ {
+ char line[256];
+ int count = 0;
+ int best_count = 0;
+ unsigned int lowest_metric = ~0;
+ in_addr_t best_gw = 0;
+ while (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if (count)
+ {
+ unsigned int net_x = 0;
+ unsigned int mask_x = 0;
+ unsigned int gw_x = 0;
+ unsigned int metric = 0;
+ const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
+ &net_x,
+ &gw_x,
+ &metric,
+ &mask_x);
+ if (np == 4)
+ {
+ const in_addr_t net = ntohl (net_x);
+ const in_addr_t mask = ntohl (mask_x);
+ const in_addr_t gw = ntohl (gw_x);
+
+ dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
+ count,
+ print_in_addr_t ((in_addr_t) net, 0, &gc),
+ print_in_addr_t ((in_addr_t) mask, 0, &gc),
+ print_in_addr_t ((in_addr_t) gw, 0, &gc),
+ metric);
+
+ if (!net && !mask && metric < lowest_metric)
+ {
+ best_gw = gw;
+ lowest_metric = metric;
+ best_count = count;
+ }
+ }
+ }
+ ++count;
+ }
+ fclose (fp);
+
+ if (best_gw)
+ {
+ *gateway = best_gw;
+ if (netmask)
+ {
+ *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
+ }
+ ret = true;
+ }
+
+ dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
+ print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
+ best_count,
+ (unsigned int)lowest_metric);
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* all of this is taken from <net/route.h> in FreeBSD */
+#define RTA_DST 0x1
+#define RTA_GATEWAY 0x2
+#define RTA_NETMASK 0x4
+
+#define RTM_GET 0x4
+#define RTM_VERSION 5
+
+#define RTF_UP 0x1
+#define RTF_GATEWAY 0x2
+
+/*
+ * These numbers are used by reliable protocols for determining
+ * retransmission behavior and are included in the routing structure.
+ */
+struct rt_metrics {
+ u_long rmx_locks; /* Kernel must leave these values alone */
+ u_long rmx_mtu; /* MTU for this path */
+ u_long rmx_hopcount; /* max hops expected */
+ u_long rmx_expire; /* lifetime for route, e.g. redirect */
+ u_long rmx_recvpipe; /* inbound delay-bandwidth product */
+ u_long rmx_sendpipe; /* outbound delay-bandwidth product */
+ u_long rmx_ssthresh; /* outbound gateway buffer limit */
+ u_long rmx_rtt; /* estimated round trip time */
+ u_long rmx_rttvar; /* estimated rtt variance */
+ u_long rmx_pksent; /* packets sent using this route */
+ u_long rmx_filler[4]; /* will be used for T/TCP later */
+};
+
+/*
+ * Structures for routing messages.
+ */
+struct rt_msghdr {
+ u_short rtm_msglen; /* to skip over non-understood messages */
+ u_char rtm_version; /* future binary compatibility */
+ u_char rtm_type; /* message type */
+ u_short rtm_index; /* index for associated ifp */
+ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
+ int rtm_addrs; /* bitmask identifying sockaddrs in msg */
+ pid_t rtm_pid; /* identify sender */
+ int rtm_seq; /* for sender to identify action */
+ int rtm_errno; /* why failed */
+ int rtm_use; /* from rtentry */
+ u_long rtm_inits; /* which metrics we are initializing */
+ struct rt_metrics rtm_rmx; /* metrics themselves */
+};
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+bool
+get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ int s, seq, l, pid, rtm_addrs, i;
+ struct sockaddr so_dst, so_mask;
+ char *cp = m_rtmsg.m_space;
+ struct sockaddr *gate = NULL, *sa;
+ struct rt_msghdr *rtm_aux;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
+ }
+
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+#define rtm m_rtmsg.m_rtm
+
+ pid = getpid();
+ seq = 0;
+ rtm_addrs = RTA_DST | RTA_NETMASK;
+
+ bzero(&so_dst, sizeof(so_dst));
+ bzero(&so_mask, sizeof(so_mask));
+ bzero(&rtm, sizeof(struct rt_msghdr));
+
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+
+ so_dst.sa_family = AF_INET;
+ so_dst.sa_len = sizeof(struct sockaddr_in);
+ so_mask.sa_family = AF_INET;
+ so_mask.sa_len = sizeof(struct sockaddr_in);
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+ if (write(s, (char *)&m_rtmsg, l) < 0)
+ {
+ warn("writing to routing socket");
+ gc_free (&gc);
+ close(s);
+ return false;
+ }
+
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ close(s);
+
+ rtm_aux = &rtm;
+
+ cp = ((char *)(rtm_aux + 1));
+ if (rtm_aux->rtm_addrs) {
+ for (i = 1; i; i <<= 1)
+ if (i & rtm_aux->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+
+
+ if (gate != NULL )
+ {
+ *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+#if 0
+ msg (M_INFO, "gw %s",
+ print_in_addr_t ((in_addr_t) *ret, 0, &gc));
+#endif
+
+ if (netmask)
+ {
+ *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ }
+
+ gc_free (&gc);
+ return true;
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+}
+
+#elif defined(TARGET_DARWIN)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* all of this is taken from <net/route.h> in Darwin */
+#define RTA_DST 0x1
+#define RTA_GATEWAY 0x2
+#define RTA_NETMASK 0x4
+
+#define RTM_GET 0x4
+#define RTM_VERSION 5
+
+#define RTF_UP 0x1
+#define RTF_GATEWAY 0x2
+
+/*
+ * These numbers are used by reliable protocols for determining
+ * retransmission behavior and are included in the routing structure.
+ */
+struct rt_metrics {
+ u_long rmx_locks; /* Kernel must leave these values alone */
+ u_long rmx_mtu; /* MTU for this path */
+ u_long rmx_hopcount; /* max hops expected */
+ u_long rmx_expire; /* lifetime for route, e.g. redirect */
+ u_long rmx_recvpipe; /* inbound delay-bandwidth product */
+ u_long rmx_sendpipe; /* outbound delay-bandwidth product */
+ u_long rmx_ssthresh; /* outbound gateway buffer limit */
+ u_long rmx_rtt; /* estimated round trip time */
+ u_long rmx_rttvar; /* estimated rtt variance */
+ u_long rmx_pksent; /* packets sent using this route */
+ u_long rmx_filler[4]; /* will be used for T/TCP later */
+};
+
+/*
+ * Structures for routing messages.
+ */
+struct rt_msghdr {
+ u_short rtm_msglen; /* to skip over non-understood messages */
+ u_char rtm_version; /* future binary compatibility */
+ u_char rtm_type; /* message type */
+ u_short rtm_index; /* index for associated ifp */
+ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
+ int rtm_addrs; /* bitmask identifying sockaddrs in msg */
+ pid_t rtm_pid; /* identify sender */
+ int rtm_seq; /* for sender to identify action */
+ int rtm_errno; /* why failed */
+ int rtm_use; /* from rtentry */
+ u_long rtm_inits; /* which metrics we are initializing */
+ struct rt_metrics rtm_rmx; /* metrics themselves */
+};
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+bool
+get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ int s, seq, l, pid, rtm_addrs, i;
+ struct sockaddr so_dst, so_mask;
+ char *cp = m_rtmsg.m_space;
+ struct sockaddr *gate = NULL, *sa;
+ struct rt_msghdr *rtm_aux;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
+ }
+
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+#define rtm m_rtmsg.m_rtm
+
+ pid = getpid();
+ seq = 0;
+ rtm_addrs = RTA_DST | RTA_NETMASK;
+
+ bzero(&so_dst, sizeof(so_dst));
+ bzero(&so_mask, sizeof(so_mask));
+ bzero(&rtm, sizeof(struct rt_msghdr));
+
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+
+ so_dst.sa_family = AF_INET;
+ so_dst.sa_len = sizeof(struct sockaddr_in);
+ so_mask.sa_family = AF_INET;
+ so_mask.sa_len = sizeof(struct sockaddr_in);
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+ if (write(s, (char *)&m_rtmsg, l) < 0)
+ {
+ msg (M_WARN, "ROUTE: problem writing to routing socket");
+ gc_free (&gc);
+ close(s);
+ return false;
+ }
+
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ close(s);
+
+ rtm_aux = &rtm;
+
+ cp = ((char *)(rtm_aux + 1));
+ if (rtm_aux->rtm_addrs) {
+ for (i = 1; i; i <<= 1)
+ if (i & rtm_aux->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+
+
+ if (gate != NULL )
+ {
+ *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+#if 0
+ msg (M_INFO, "gw %s",
+ print_in_addr_t ((in_addr_t) *ret, 0, &gc));
+#endif
+
+ if (netmask)
+ {
+ *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ }
+
+ gc_free (&gc);
+ return true;
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+}
+
+#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/* all of this is taken from <net/route.h> in OpenBSD 3.6 */
+#define RTA_DST 0x1 /* destination sockaddr present */
+#define RTA_GATEWAY 0x2 /* gateway sockaddr present */
+#define RTA_NETMASK 0x4 /* netmask sockaddr present */
+
+#define RTM_GET 0x4 /* Report Metrics */
+
+#define RTM_VERSION 3 /* Up the ante and ignore older versions */
+
+#define RTF_UP 0x1 /* route usable */
+#define RTF_GATEWAY 0x2 /* destination is a gateway */
+
+/*
+ * Huge version for userland compatibility.
+ */
+struct rt_metrics {
+ u_long rmx_locks; /* Kernel must leave these values alone */
+ u_long rmx_mtu; /* MTU for this path */
+ u_long rmx_hopcount; /* max hops expected */
+ u_long rmx_expire; /* lifetime for route, e.g. redirect */
+ u_long rmx_recvpipe; /* inbound delay-bandwidth product */
+ u_long rmx_sendpipe; /* outbound delay-bandwidth product */
+ u_long rmx_ssthresh; /* outbound gateway buffer limit */
+ u_long rmx_rtt; /* estimated round trip time */
+ u_long rmx_rttvar; /* estimated rtt variance */
+ u_long rmx_pksent; /* packets sent using this route */
+};
+
+/*
+ * Structures for routing messages.
+ */
+struct rt_msghdr {
+ u_short rtm_msglen; /* to skip over non-understood messages */
+ u_char rtm_version; /* future binary compatibility */
+ u_char rtm_type; /* message type */
+ u_short rtm_index; /* index for associated ifp */
+ int rtm_flags; /* flags, incl. kern & message, e.g. DONE */
+ int rtm_addrs; /* bitmask identifying sockaddrs in msg */
+ pid_t rtm_pid; /* identify sender */
+ int rtm_seq; /* for sender to identify action */
+ int rtm_errno; /* why failed */
+ int rtm_use; /* from rtentry */
+ u_long rtm_inits; /* which metrics we are initializing */
+ struct rt_metrics rtm_rmx; /* metrics themselves */
+};
+
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+bool
+get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ int s, seq, l, rtm_addrs, i;
+ pid_t pid;
+ struct sockaddr so_dst, so_mask;
+ char *cp = m_rtmsg.m_space;
+ struct sockaddr *gate = NULL, *sa;
+ struct rt_msghdr *rtm_aux;
+
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
+ }
+
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+#define rtm m_rtmsg.m_rtm
+
+ pid = getpid();
+ seq = 0;
+ rtm_addrs = RTA_DST | RTA_NETMASK;
+
+ bzero(&so_dst, sizeof(so_dst));
+ bzero(&so_mask, sizeof(so_mask));
+ bzero(&rtm, sizeof(struct rt_msghdr));
+
+ rtm.rtm_type = RTM_GET;
+ rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_seq = ++seq;
+ rtm.rtm_addrs = rtm_addrs;
+
+ so_dst.sa_family = AF_INET;
+ so_dst.sa_len = sizeof(struct sockaddr_in);
+ so_mask.sa_family = AF_INET;
+ so_mask.sa_len = sizeof(struct sockaddr_in);
+
+ NEXTADDR(RTA_DST, so_dst);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
+
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+
+ if (write(s, (char *)&m_rtmsg, l) < 0)
+ {
+ warn("writing to routing socket");
+ gc_free (&gc);
+ close(s);
+ return false;
+ }
+
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
+
+ close(s);
+
+ rtm_aux = &rtm;
+
+ cp = ((char *)(rtm_aux + 1));
+ if (rtm_aux->rtm_addrs) {
+ for (i = 1; i; i <<= 1)
+ if (i & rtm_aux->rtm_addrs) {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+
+
+ if (gate != NULL )
+ {
+ *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+#if 0
+ msg (M_INFO, "gw %s",
+ print_in_addr_t ((in_addr_t) *ret, 0, &gc));
+#endif
+
+ if (netmask)
+ {
+ *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ }
+
+ gc_free (&gc);
+ return true;
+ }
+ else
+ {
+ gc_free (&gc);
+ return false;
+ }
+}
+
+#else
+
+bool
+get_default_gateway (in_addr_t *ret, in_addr_t *netmask) /* PLATFORM-SPECIFIC */
+{
+ return false;
+}
+
+#endif
+
+bool
+netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbits)
+{
+ int i;
+ const int addrlen = sizeof (in_addr_t) * 8;
+
+ if ((network & netmask) == network)
+ {
+ for (i = 0; i <= addrlen; ++i)
+ {
+ in_addr_t mask = netbits_to_netmask (i);
+ if (mask == netmask)
+ {
+ if (i == addrlen)
+ *netbits = -1;
+ else
+ *netbits = i;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * get_bypass_addresses() is used by the redirect-gateway bypass-x
+ * functions to build a route bypass to selected DHCP/DNS servers,
+ * so that outgoing packets to these servers don't end up in the tunnel.
+ */
+
+#if defined(WIN32)
+
+static void
+add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr)
+{
+ if (test_local_addr(addr) == TLA_NONLOCAL && addr != 0 && addr != ~0) {
+ int i;
+ for (i = 0; i < rb->n_bypass; ++i)
+ {
+ if (addr == rb->bypass[i]) /* avoid duplicates */
+ return;
+ }
+ if (rb->n_bypass < N_ROUTE_BYPASS)
+ {
+ rb->bypass[rb->n_bypass++] = addr;
+ }
+ }
+}
+
+static void
+add_host_route_array (struct route_bypass *rb, const IP_ADDR_STRING *iplist)
+{
+ while (iplist)
+ {
+ bool succeed = false;
+ const in_addr_t ip = getaddr (GETADDR_HOST_ORDER, iplist->IpAddress.String, 0, &succeed, NULL);
+ if (succeed)
+ {
+ add_host_route_if_nonlocal (rb, ip);
+ }
+ iplist = iplist->Next;
+ }
+}
+
+static void
+get_bypass_addresses (struct route_bypass *rb, const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ /*bool ret_bool = false;*/
+
+ /* get full routing table */
+ const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);
+
+ /* get the route which represents the default gateway */
+ const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);
+
+ if (row)
+ {
+ /* get the adapter which the default gateway is associated with */
+ const IP_ADAPTER_INFO *dgi = get_adapter_info (row->dwForwardIfIndex, &gc);
+
+ /* get extra adapter info, such as DNS addresses */
+ const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (row->dwForwardIfIndex, &gc);
+
+ /* Bypass DHCP server address */
+ if ((flags & RG_BYPASS_DHCP) && dgi && dgi->DhcpEnabled)
+ add_host_route_array (rb, &dgi->DhcpServer);
+
+ /* Bypass DNS server addresses */
+ if ((flags & RG_BYPASS_DNS) && pai)
+ add_host_route_array (rb, &pai->DnsServerList);
+ }
+
+ gc_free (&gc);
+}
+
+#else
+
+static void
+get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLATFORM-SPECIFIC */
+{
+}
+
+#endif
+
+#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
+
+#if defined(TARGET_LINUX)
+
+bool
+get_default_gateway_mac_addr (unsigned char *macaddr)
+{
+ struct ifreq *ifr, *ifend;
+ in_addr_t ina, mask;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[20]; // Maximum number of interfaces to scan
+ int sd = -1;
+ in_addr_t gwip = 0;
+ bool ret = false;
+
+ if (!get_default_gateway (&gwip, NULL))
+ {
+ msg (M_WARN, "GDGMA: get_default_gateway failed");
+ goto err;
+ }
+
+ if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ msg (M_WARN, "GDGMA: socket() failed");
+ goto err;
+ }
+
+ ifc.ifc_len = sizeof (ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
+ {
+ msg (M_WARN, "GDGMA: ioctl(SIOCGIFCONF) failed");
+ goto err;
+ }
+
+ /* scan through interface list */
+ ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
+ {
+ if (ifr->ifr_addr.sa_family == AF_INET)
+ {
+ ina = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
+ strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+
+ dmsg (D_AUTO_USERID, "GDGMA: %s", ifreq.ifr_name);
+
+ /* check that the interface is up, and not point-to-point or loopback */
+ if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
+ {
+ dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFFLAGS(%s) failed", ifreq.ifr_name);
+ continue;
+ }
+
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_LOOPBACK)) != IFF_UP)
+ {
+ dmsg (D_AUTO_USERID, "GDGMA: interface %s is down or loopback", ifreq.ifr_name);
+ continue;
+ }
+
+ /* get interface netmask and check for correct subnet */
+ if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
+ {
+ dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFNETMASK(%s) failed", ifreq.ifr_name);
+ continue;
+ }
+
+ mask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+ if (((gwip ^ ina) & mask) != 0)
+ {
+ dmsg (D_AUTO_USERID, "GDGMA: gwip=0x%08x ina=0x%08x mask=0x%08x",
+ (unsigned int)gwip,
+ (unsigned int)ina,
+ (unsigned int)mask);
+ continue;
+ }
+ break;
+ }
+ }
+ if (ifr >= ifend)
+ {
+ msg (M_WARN, "GDGMA: couldn't find gw interface");
+ goto err;
+ }
+
+ /* now get the hardware address. */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
+ {
+ msg (M_WARN, "GDGMA: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
+ goto err;
+ }
+
+ memcpy (macaddr, &ifreq.ifr_hwaddr.sa_data, 6);
+ ret = true;
+
+ err:
+ if (sd >= 0)
+ close (sd);
+ return ret;
+}
+
+#elif defined(WIN32)
+
+bool
+get_default_gateway_mac_addr (unsigned char *macaddr)
+{
+ struct gc_arena gc = gc_new ();
+ const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
+ in_addr_t gwip = 0;
+ DWORD a_index;
+ const IP_ADAPTER_INFO *ai;
+
+ if (!get_default_gateway (&gwip, NULL))
+ {
+ msg (M_WARN, "GDGMA: get_default_gateway failed");
+ goto err;
+ }
+
+ a_index = adapter_index_of_ip (adapters, gwip, NULL, NULL);
+ ai = get_adapter (adapters, a_index);
+
+ if (!ai)
+ {
+ msg (M_WARN, "GDGMA: couldn't find gw interface");
+ goto err;
+ }
+
+ memcpy (macaddr, ai->Address, 6);
+
+ gc_free (&gc);
+ return true;
+
+ err:
+ gc_free (&gc);
+ return false;
+}
+
+#else
+
+bool
+get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
+{
+ return false;
+}
+
+#endif
+#endif /* AUTO_USERID */
+
+/*
+ * Test if addr is reachable via a local interface (return ILA_LOCAL),
+ * or if it needs to be routed via the default gateway (return
+ * ILA_NONLOCAL). If the target platform doesn't implement this
+ * function, return ILA_NOT_IMPLEMENTED.
+ *
+ * Used by redirect-gateway autolocal feature
+ */
+
+#if defined(WIN32)
+
+int
+test_local_addr (const in_addr_t addr)
+{
+ struct gc_arena gc = gc_new ();
+ const in_addr_t nonlocal_netmask = 0x80000000L; /* routes with netmask <= to this are considered non-local */
+ bool ret = TLA_NONLOCAL;
+
+ /* get full routing table */
+ const MIB_IPFORWARDTABLE *rt = get_windows_routing_table (&gc);
+ if (rt)
+ {
+ int i;
+ for (i = 0; i < rt->dwNumEntries; ++i)
+ {
+ const MIB_IPFORWARDROW *row = &rt->table[i];
+ const in_addr_t net = ntohl (row->dwForwardDest);
+ const in_addr_t mask = ntohl (row->dwForwardMask);
+ if (mask > nonlocal_netmask && (addr & mask) == net)
+ {
+ ret = TLA_LOCAL;
+ break;
+ }
+ }
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+#else
+
+
+int
+test_local_addr (const in_addr_t addr) /* PLATFORM-SPECIFIC */
+{
+ return TLA_NOT_IMPLEMENTED;
+}
+
+#endif
diff --git a/route.h b/route.h
new file mode 100644
index 0000000..c5cbb7c
--- /dev/null
+++ b/route.h
@@ -0,0 +1,220 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Support routines for adding/deleting network routes.
+ */
+
+#ifndef ROUTE_H
+#define ROUTE_H
+
+#include "tun.h"
+#include "misc.h"
+
+#define MAX_ROUTES_DEFAULT 100
+
+#ifdef WIN32
+/*
+ * Windows route methods
+ */
+#define ROUTE_METHOD_ADAPTIVE 0 /* try IP helper first then route.exe */
+#define ROUTE_METHOD_IPAPI 1 /* use IP helper API */
+#define ROUTE_METHOD_EXE 2 /* use route.exe */
+#define ROUTE_METHOD_MASK 3
+#endif
+
+/*
+ * Route add flags (must stay clear of ROUTE_METHOD bits)
+ */
+#define ROUTE_DELETE_FIRST 4
+
+struct route_bypass
+{
+# define N_ROUTE_BYPASS 8
+ int n_bypass;
+ in_addr_t bypass[N_ROUTE_BYPASS];
+};
+
+struct route_special_addr
+{
+ in_addr_t remote_endpoint;
+ bool remote_endpoint_defined;
+ in_addr_t net_gateway;
+ bool net_gateway_defined;
+ in_addr_t remote_host;
+ bool remote_host_defined;
+ struct route_bypass bypass;
+ int default_metric;
+ bool default_metric_defined;
+};
+
+struct route_option {
+ const char *network;
+ const char *netmask;
+ const char *gateway;
+ const char *metric;
+};
+
+/* redirect-gateway flags */
+#define RG_ENABLE (1<<0)
+#define RG_LOCAL (1<<1)
+#define RG_DEF1 (1<<2)
+#define RG_BYPASS_DHCP (1<<3)
+#define RG_BYPASS_DNS (1<<4)
+#define RG_REROUTE_GW (1<<5)
+#define RG_AUTO_LOCAL (1<<6)
+
+struct route_option_list {
+ unsigned int flags;
+ int capacity;
+ int n;
+ struct route_option routes[EMPTY_ARRAY_SIZE];
+};
+
+struct route {
+ bool defined;
+ const struct route_option *option;
+ in_addr_t network;
+ in_addr_t netmask;
+ in_addr_t gateway;
+ bool metric_defined;
+ int metric;
+};
+
+struct route_list {
+ bool routes_added;
+ struct route_special_addr spec;
+ unsigned int flags;
+ bool did_redirect_default_gateway;
+ bool did_local;
+ int capacity;
+ int n;
+ struct route routes[EMPTY_ARRAY_SIZE];
+};
+
+#if P2MP
+/* internal OpenVPN route */
+struct iroute {
+ in_addr_t network;
+ int netbits;
+ struct iroute *next;
+};
+#endif
+
+struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
+struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
+void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
+
+struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
+
+void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+
+void add_route_to_option_list (struct route_option_list *l,
+ const char *network,
+ const char *netmask,
+ const char *gateway,
+ const char *metric);
+
+bool init_route_list (struct route_list *rl,
+ const struct route_option_list *opt,
+ const char *remote_endpoint,
+ int default_metric,
+ in_addr_t remote_host,
+ struct env_set *es);
+
+void route_list_add_default_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr);
+
+void add_routes (struct route_list *rl,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es);
+
+void delete_routes (struct route_list *rl,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct env_set *es);
+
+void setenv_routes (struct env_set *es, const struct route_list *rl);
+
+bool is_special_addr (const char *addr_str);
+
+bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask);
+
+/*
+ * Test if addr is reachable via a local interface (return ILA_LOCAL),
+ * or if it needs to be routed via the default gateway (return
+ * ILA_NONLOCAL). If the current platform doesn't implement this
+ * function, return ILA_NOT_IMPLEMENTED.
+ */
+#define TLA_NOT_IMPLEMENTED 0
+#define TLA_NONLOCAL 1
+#define TLA_LOCAL 2
+int test_local_addr (const in_addr_t addr);
+
+#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
+bool get_default_gateway_mac_addr (unsigned char *macaddr);
+#endif
+
+#ifdef ENABLE_DEBUG
+void print_route_options (const struct route_option_list *rol,
+ int level);
+#endif
+
+void print_routes (const struct route_list *rl, int level);
+
+#ifdef WIN32
+
+void show_routes (int msglev);
+bool test_routes (const struct route_list *rl, const struct tuntap *tt);
+bool add_route_ipapi (const struct route *r, const struct tuntap *tt);
+bool del_route_ipapi (const struct route *r, const struct tuntap *tt);
+
+#else
+static inline bool test_routes (const struct route_list *rl, const struct tuntap *tt) { return true; }
+#endif
+
+bool netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbits);
+
+static inline in_addr_t
+netbits_to_netmask (const int netbits)
+{
+ const int addrlen = sizeof (in_addr_t) * 8;
+ in_addr_t mask = 0;
+ if (netbits > 0 && netbits <= addrlen)
+ mask = ~0 << (addrlen-netbits);
+ return mask;
+}
+
+static inline bool
+route_list_default_gateway_needed (const struct route_list *rl)
+{
+ if (!rl)
+ return false;
+ else
+ return !rl->spec.remote_endpoint_defined;
+}
+
+#endif
diff --git a/sample-config-files/README b/sample-config-files/README
new file mode 100644
index 0000000..d53ac79
--- /dev/null
+++ b/sample-config-files/README
@@ -0,0 +1,6 @@
+Sample OpenVPN Configuration Files.
+
+These files are part of the OpenVPN HOWTO
+which is located at:
+
+http://openvpn.net/howto.html
diff --git a/sample-config-files/client.conf b/sample-config-files/client.conf
new file mode 100644
index 0000000..58b2038
--- /dev/null
+++ b/sample-config-files/client.conf
@@ -0,0 +1,123 @@
+##############################################
+# Sample client-side OpenVPN 2.0 config file #
+# for connecting to multi-client server. #
+# #
+# This configuration can be used by multiple #
+# clients, however each client should have #
+# its own cert and key files. #
+# #
+# On Windows, you might want to rename this #
+# file so it has a .ovpn extension #
+##############################################
+
+# Specify that we are a client and that we
+# will be pulling certain config file directives
+# from the server.
+client
+
+# Use the same setting as you are using on
+# the server.
+# On most systems, the VPN will not function
+# unless you partially or fully disable
+# the firewall for the TUN/TAP interface.
+;dev tap
+dev tun
+
+# Windows needs the TAP-Win32 adapter name
+# from the Network Connections panel
+# if you have more than one. On XP SP2,
+# you may need to disable the firewall
+# for the TAP adapter.
+;dev-node MyTap
+
+# Are we connecting to a TCP or
+# UDP server? Use the same setting as
+# on the server.
+;proto tcp
+proto udp
+
+# The hostname/IP and port of the server.
+# You can have multiple remote entries
+# to load balance between the servers.
+remote my-server-1 1194
+;remote my-server-2 1194
+
+# Choose a random host from the remote
+# list for load-balancing. Otherwise
+# try hosts in the order specified.
+;remote-random
+
+# Keep trying indefinitely to resolve the
+# host name of the OpenVPN server. Very useful
+# on machines which are not permanently connected
+# to the internet such as laptops.
+resolv-retry infinite
+
+# Most clients don't need to bind to
+# a specific local port number.
+nobind
+
+# Downgrade privileges after initialization (non-Windows only)
+;user nobody
+;group nobody
+
+# Try to preserve some state across restarts.
+persist-key
+persist-tun
+
+# If you are connecting through an
+# HTTP proxy to reach the actual OpenVPN
+# server, put the proxy server/IP and
+# port number here. See the man page
+# if your proxy server requires
+# authentication.
+;http-proxy-retry # retry on connection failures
+;http-proxy [proxy server] [proxy port #]
+
+# Wireless networks often produce a lot
+# of duplicate packets. Set this flag
+# to silence duplicate packet warnings.
+;mute-replay-warnings
+
+# SSL/TLS parms.
+# See the server config file for more
+# description. It's best to use
+# a separate .crt/.key file pair
+# for each client. A single ca
+# file can be used for all clients.
+ca ca.crt
+cert client.crt
+key client.key
+
+# Verify server certificate by checking
+# that the certicate has the nsCertType
+# field set to "server". This is an
+# important precaution to protect against
+# a potential attack discussed here:
+# http://openvpn.net/howto.html#mitm
+#
+# To use this feature, you will need to generate
+# your server certificates with the nsCertType
+# field set to "server". The build-key-server
+# script in the easy-rsa folder will do this.
+ns-cert-type server
+
+# If a tls-auth key is used on the server
+# then every client must also have the key.
+;tls-auth ta.key 1
+
+# Select a cryptographic cipher.
+# If the cipher option is used on the server
+# then you must also specify it here.
+;cipher x
+
+# Enable compression on the VPN link.
+# Don't enable this unless it is also
+# enabled in the server config file.
+comp-lzo
+
+# Set log file verbosity.
+verb 3
+
+# Silence repeating messages
+;mute 20
diff --git a/sample-config-files/firewall.sh b/sample-config-files/firewall.sh
new file mode 100755
index 0000000..19d75ee
--- /dev/null
+++ b/sample-config-files/firewall.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# A Sample OpenVPN-aware firewall.
+
+# eth0 is connected to the internet.
+# eth1 is connected to a private subnet.
+
+# Change this subnet to correspond to your private
+# ethernet subnet. Home will use HOME_NET/24 and
+# Office will use OFFICE_NET/24.
+PRIVATE=10.0.0.0/24
+
+# Loopback address
+LOOP=127.0.0.1
+
+# Delete old iptables rules
+# and temporarily block all traffic.
+iptables -P OUTPUT DROP
+iptables -P INPUT DROP
+iptables -P FORWARD DROP
+iptables -F
+
+# Set default policies
+iptables -P OUTPUT ACCEPT
+iptables -P INPUT DROP
+iptables -P FORWARD DROP
+
+# Prevent external packets from using loopback addr
+iptables -A INPUT -i eth0 -s $LOOP -j DROP
+iptables -A FORWARD -i eth0 -s $LOOP -j DROP
+iptables -A INPUT -i eth0 -d $LOOP -j DROP
+iptables -A FORWARD -i eth0 -d $LOOP -j DROP
+
+# Anything coming from the Internet should have a real Internet address
+iptables -A FORWARD -i eth0 -s 192.168.0.0/16 -j DROP
+iptables -A FORWARD -i eth0 -s 172.16.0.0/12 -j DROP
+iptables -A FORWARD -i eth0 -s 10.0.0.0/8 -j DROP
+iptables -A INPUT -i eth0 -s 192.168.0.0/16 -j DROP
+iptables -A INPUT -i eth0 -s 172.16.0.0/12 -j DROP
+iptables -A INPUT -i eth0 -s 10.0.0.0/8 -j DROP
+
+# Block outgoing NetBios (if you have windows machines running
+# on the private subnet). This will not affect any NetBios
+# traffic that flows over the VPN tunnel, but it will stop
+# local windows machines from broadcasting themselves to
+# the internet.
+iptables -A FORWARD -p tcp --sport 137:139 -o eth0 -j DROP
+iptables -A FORWARD -p udp --sport 137:139 -o eth0 -j DROP
+iptables -A OUTPUT -p tcp --sport 137:139 -o eth0 -j DROP
+iptables -A OUTPUT -p udp --sport 137:139 -o eth0 -j DROP
+
+# Check source address validity on packets going out to internet
+iptables -A FORWARD -s ! $PRIVATE -i eth1 -j DROP
+
+# Allow local loopback
+iptables -A INPUT -s $LOOP -j ACCEPT
+iptables -A INPUT -d $LOOP -j ACCEPT
+
+# Allow incoming pings (can be disabled)
+iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
+
+# Allow services such as www and ssh (can be disabled)
+iptables -A INPUT -p tcp --dport http -j ACCEPT
+iptables -A INPUT -p tcp --dport ssh -j ACCEPT
+
+# Allow incoming OpenVPN packets
+# Duplicate the line below for each
+# OpenVPN tunnel, changing --dport n
+# to match the OpenVPN UDP port.
+#
+# In OpenVPN, the port number is
+# controlled by the --port n option.
+# If you put this option in the config
+# file, you can remove the leading '--'
+#
+# If you taking the stateful firewall
+# approach (see the OpenVPN HOWTO),
+# then comment out the line below.
+
+iptables -A INPUT -p udp --dport 1194 -j ACCEPT
+
+# Allow packets from TUN/TAP devices.
+# When OpenVPN is run in a secure mode,
+# it will authenticate packets prior
+# to their arriving on a tun or tap
+# interface. Therefore, it is not
+# necessary to add any filters here,
+# unless you want to restrict the
+# type of packets which can flow over
+# the tunnel.
+
+iptables -A INPUT -i tun+ -j ACCEPT
+iptables -A FORWARD -i tun+ -j ACCEPT
+iptables -A INPUT -i tap+ -j ACCEPT
+iptables -A FORWARD -i tap+ -j ACCEPT
+
+# Allow packets from private subnets
+iptables -A INPUT -i eth1 -j ACCEPT
+iptables -A FORWARD -i eth1 -j ACCEPT
+
+# Keep state of connections from local machine and private subnets
+iptables -A OUTPUT -m state --state NEW -o eth0 -j ACCEPT
+iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+iptables -A FORWARD -m state --state NEW -o eth0 -j ACCEPT
+iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
+
+# Masquerade local subnet
+iptables -t nat -A POSTROUTING -s $PRIVATE -o eth0 -j MASQUERADE
diff --git a/sample-config-files/home.up b/sample-config-files/home.up
new file mode 100755
index 0000000..9c347cc
--- /dev/null
+++ b/sample-config-files/home.up
@@ -0,0 +1,2 @@
+#!/bin/sh
+route add -net 10.0.0.0 netmask 255.255.255.0 gw $5
diff --git a/sample-config-files/loopback-client b/sample-config-files/loopback-client
new file mode 100644
index 0000000..d7f59e6
--- /dev/null
+++ b/sample-config-files/loopback-client
@@ -0,0 +1,25 @@
+# Perform a TLS loopback test -- client side.
+#
+# This test performs a TLS negotiation once every 10 seconds,
+# and will terminate after 2 minutes.
+#
+# From the root directory of the OpenVPN distribution,
+# after openvpn has been built, run:
+#
+# ./openvpn --config sample-config-files/loopback-client (In one window)
+# ./openvpn --config sample-config-files/loopback-server (Simultaneously in another window)
+
+rport 16000
+lport 16001
+remote localhost
+local localhost
+dev null
+verb 3
+reneg-sec 10
+tls-client
+ca sample-keys/ca.crt
+key sample-keys/client.key
+cert sample-keys/client.crt
+cipher DES-EDE3-CBC
+ping 1
+inactive 120 10000000
diff --git a/sample-config-files/loopback-server b/sample-config-files/loopback-server
new file mode 100644
index 0000000..9d21bce
--- /dev/null
+++ b/sample-config-files/loopback-server
@@ -0,0 +1,26 @@
+# Perform a TLS loopback test -- server side.
+#
+# This test performs a TLS negotiation once every 10 seconds,
+# and will terminate after 2 minutes.
+#
+# From the root directory of the OpenVPN distribution,
+# after openvpn has been built, run:
+#
+# ./openvpn --config sample-config-files/loopback-client (In one window)
+# ./openvpn --config sample-config-files/loopback-server (Simultaneously in another window)
+
+rport 16001
+lport 16000
+remote localhost
+local localhost
+dev null
+verb 3
+reneg-sec 10
+tls-server
+dh sample-keys/dh1024.pem
+ca sample-keys/ca.crt
+key sample-keys/server.key
+cert sample-keys/server.crt
+cipher DES-EDE3-CBC
+ping 1
+inactive 120 10000000
diff --git a/sample-config-files/office.up b/sample-config-files/office.up
new file mode 100755
index 0000000..74a71a3
--- /dev/null
+++ b/sample-config-files/office.up
@@ -0,0 +1,2 @@
+#!/bin/sh
+route add -net 10.0.1.0 netmask 255.255.255.0 gw $5
diff --git a/sample-config-files/openvpn-shutdown.sh b/sample-config-files/openvpn-shutdown.sh
new file mode 100755
index 0000000..8ed2d1d
--- /dev/null
+++ b/sample-config-files/openvpn-shutdown.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# stop all openvpn processes
+
+killall -TERM openvpn
diff --git a/sample-config-files/openvpn-startup.sh b/sample-config-files/openvpn-startup.sh
new file mode 100755
index 0000000..0ee006b
--- /dev/null
+++ b/sample-config-files/openvpn-startup.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# A sample OpenVPN startup script
+# for Linux.
+
+# openvpn config file directory
+dir=/etc/openvpn
+
+# load the firewall
+$dir/firewall.sh
+
+# load TUN/TAP kernel module
+modprobe tun
+
+# enable IP forwarding
+echo 1 > /proc/sys/net/ipv4/ip_forward
+
+# Invoke openvpn for each VPN tunnel
+# in daemon mode. Alternatively,
+# you could remove "--daemon" from
+# the command line and add "daemon"
+# to the config file.
+#
+# Each tunnel should run on a separate
+# UDP port. Use the "port" option
+# to control this. Like all of
+# OpenVPN's options, you can
+# specify "--port 8000" on the command
+# line or "port 8000" in the config
+# file.
+
+openvpn --cd $dir --daemon --config vpn1.conf
+openvpn --cd $dir --daemon --config vpn2.conf
+openvpn --cd $dir --daemon --config vpn2.conf
diff --git a/sample-config-files/server.conf b/sample-config-files/server.conf
new file mode 100644
index 0000000..f483b6b
--- /dev/null
+++ b/sample-config-files/server.conf
@@ -0,0 +1,299 @@
+#################################################
+# Sample OpenVPN 2.0 config file for #
+# multi-client server. #
+# #
+# This file is for the server side #
+# of a many-clients <-> one-server #
+# OpenVPN configuration. #
+# #
+# OpenVPN also supports #
+# single-machine <-> single-machine #
+# configurations (See the Examples page #
+# on the web site for more info). #
+# #
+# This config should work on Windows #
+# or Linux/BSD systems. Remember on #
+# Windows to quote pathnames and use #
+# double backslashes, e.g.: #
+# "C:\\Program Files\\OpenVPN\\config\\foo.key" #
+# #
+# Comments are preceded with '#' or ';' #
+#################################################
+
+# Which local IP address should OpenVPN
+# listen on? (optional)
+;local a.b.c.d
+
+# Which TCP/UDP port should OpenVPN listen on?
+# If you want to run multiple OpenVPN instances
+# on the same machine, use a different port
+# number for each one. You will need to
+# open up this port on your firewall.
+port 1194
+
+# TCP or UDP server?
+;proto tcp
+proto udp
+
+# "dev tun" will create a routed IP tunnel,
+# "dev tap" will create an ethernet tunnel.
+# Use "dev tap0" if you are ethernet bridging
+# and have precreated a tap0 virtual interface
+# and bridged it with your ethernet interface.
+# If you want to control access policies
+# over the VPN, you must create firewall
+# rules for the the TUN/TAP interface.
+# On non-Windows systems, you can give
+# an explicit unit number, such as tun0.
+# On Windows, use "dev-node" for this.
+# On most systems, the VPN will not function
+# unless you partially or fully disable
+# the firewall for the TUN/TAP interface.
+;dev tap
+dev tun
+
+# Windows needs the TAP-Win32 adapter name
+# from the Network Connections panel if you
+# have more than one. On XP SP2 or higher,
+# you may need to selectively disable the
+# Windows firewall for the TAP adapter.
+# Non-Windows systems usually don't need this.
+;dev-node MyTap
+
+# SSL/TLS root certificate (ca), certificate
+# (cert), and private key (key). Each client
+# and the server must have their own cert and
+# key file. The server and all clients will
+# use the same ca file.
+#
+# See the "easy-rsa" directory for a series
+# of scripts for generating RSA certificates
+# and private keys. Remember to use
+# a unique Common Name for the server
+# and each of the client certificates.
+#
+# Any X509 key management system can be used.
+# OpenVPN can also use a PKCS #12 formatted key file
+# (see "pkcs12" directive in man page).
+ca ca.crt
+cert server.crt
+key server.key # This file should be kept secret
+
+# Diffie hellman parameters.
+# Generate your own with:
+# openssl dhparam -out dh1024.pem 1024
+# Substitute 2048 for 1024 if you are using
+# 2048 bit keys.
+dh dh1024.pem
+
+# Configure server mode and supply a VPN subnet
+# for OpenVPN to draw client addresses from.
+# The server will take 10.8.0.1 for itself,
+# the rest will be made available to clients.
+# Each client will be able to reach the server
+# on 10.8.0.1. Comment this line out if you are
+# ethernet bridging. See the man page for more info.
+server 10.8.0.0 255.255.255.0
+
+# Maintain a record of client <-> virtual IP address
+# associations in this file. If OpenVPN goes down or
+# is restarted, reconnecting clients can be assigned
+# the same virtual IP address from the pool that was
+# previously assigned.
+ifconfig-pool-persist ipp.txt
+
+# Configure server mode for ethernet bridging.
+# You must first use your OS's bridging capability
+# to bridge the TAP interface with the ethernet
+# NIC interface. Then you must manually set the
+# IP/netmask on the bridge interface, here we
+# assume 10.8.0.4/255.255.255.0. Finally we
+# must set aside an IP range in this subnet
+# (start=10.8.0.50 end=10.8.0.100) to allocate
+# to connecting clients. Leave this line commented
+# out unless you are ethernet bridging.
+;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100
+
+# Configure server mode for ethernet bridging
+# using a DHCP-proxy, where clients talk
+# to the OpenVPN server-side DHCP server
+# to receive their IP address allocation
+# and DNS server addresses. You must first use
+# your OS's bridging capability to bridge the TAP
+# interface with the ethernet NIC interface.
+# Note: this mode only works on clients (such as
+# Windows), where the client-side TAP adapter is
+# bound to a DHCP client.
+;server-bridge
+
+# Push routes to the client to allow it
+# to reach other private subnets behind
+# the server. Remember that these
+# private subnets will also need
+# to know to route the OpenVPN client
+# address pool (10.8.0.0/255.255.255.0)
+# back to the OpenVPN server.
+;push "route 192.168.10.0 255.255.255.0"
+;push "route 192.168.20.0 255.255.255.0"
+
+# To assign specific IP addresses to specific
+# clients or if a connecting client has a private
+# subnet behind it that should also have VPN access,
+# use the subdirectory "ccd" for client-specific
+# configuration files (see man page for more info).
+
+# EXAMPLE: Suppose the client
+# having the certificate common name "Thelonious"
+# also has a small subnet behind his connecting
+# machine, such as 192.168.40.128/255.255.255.248.
+# First, uncomment out these lines:
+;client-config-dir ccd
+;route 192.168.40.128 255.255.255.248
+# Then create a file ccd/Thelonious with this line:
+# iroute 192.168.40.128 255.255.255.248
+# This will allow Thelonious' private subnet to
+# access the VPN. This example will only work
+# if you are routing, not bridging, i.e. you are
+# using "dev tun" and "server" directives.
+
+# EXAMPLE: Suppose you want to give
+# Thelonious a fixed VPN IP address of 10.9.0.1.
+# First uncomment out these lines:
+;client-config-dir ccd
+;route 10.9.0.0 255.255.255.252
+# Then add this line to ccd/Thelonious:
+# ifconfig-push 10.9.0.1 10.9.0.2
+
+# Suppose that you want to enable different
+# firewall access policies for different groups
+# of clients. There are two methods:
+# (1) Run multiple OpenVPN daemons, one for each
+# group, and firewall the TUN/TAP interface
+# for each group/daemon appropriately.
+# (2) (Advanced) Create a script to dynamically
+# modify the firewall in response to access
+# from different clients. See man
+# page for more info on learn-address script.
+;learn-address ./script
+
+# If enabled, this directive will configure
+# all clients to redirect their default
+# network gateway through the VPN, causing
+# all IP traffic such as web browsing and
+# and DNS lookups to go through the VPN
+# (The OpenVPN server machine may need to NAT
+# or bridge the TUN/TAP interface to the internet
+# in order for this to work properly).
+;push "redirect-gateway def1 bypass-dhcp"
+
+# Certain Windows-specific network settings
+# can be pushed to clients, such as DNS
+# or WINS server addresses. CAVEAT:
+# http://openvpn.net/faq.html#dhcpcaveats
+# The addresses below refer to the public
+# DNS servers provided by opendns.com.
+;push "dhcp-option DNS 208.67.222.222"
+;push "dhcp-option DNS 208.67.220.220"
+
+# Uncomment this directive to allow different
+# clients to be able to "see" each other.
+# By default, clients will only see the server.
+# To force clients to only see the server, you
+# will also need to appropriately firewall the
+# server's TUN/TAP interface.
+;client-to-client
+
+# Uncomment this directive if multiple clients
+# might connect with the same certificate/key
+# files or common names. This is recommended
+# only for testing purposes. For production use,
+# each client should have its own certificate/key
+# pair.
+#
+# IF YOU HAVE NOT GENERATED INDIVIDUAL
+# CERTIFICATE/KEY PAIRS FOR EACH CLIENT,
+# EACH HAVING ITS OWN UNIQUE "COMMON NAME",
+# UNCOMMENT THIS LINE OUT.
+;duplicate-cn
+
+# The keepalive directive causes ping-like
+# messages to be sent back and forth over
+# the link so that each side knows when
+# the other side has gone down.
+# Ping every 10 seconds, assume that remote
+# peer is down if no ping received during
+# a 120 second time period.
+keepalive 10 120
+
+# For extra security beyond that provided
+# by SSL/TLS, create an "HMAC firewall"
+# to help block DoS attacks and UDP port flooding.
+#
+# Generate with:
+# openvpn --genkey --secret ta.key
+#
+# The server and each client must have
+# a copy of this key.
+# The second parameter should be '0'
+# on the server and '1' on the clients.
+;tls-auth ta.key 0 # This file is secret
+
+# Select a cryptographic cipher.
+# This config item must be copied to
+# the client config file as well.
+;cipher BF-CBC # Blowfish (default)
+;cipher AES-128-CBC # AES
+;cipher DES-EDE3-CBC # Triple-DES
+
+# Enable compression on the VPN link.
+# If you enable it here, you must also
+# enable it in the client config file.
+comp-lzo
+
+# The maximum number of concurrently connected
+# clients we want to allow.
+;max-clients 100
+
+# It's a good idea to reduce the OpenVPN
+# daemon's privileges after initialization.
+#
+# You can uncomment this out on
+# non-Windows systems.
+;user nobody
+;group nobody
+
+# The persist options will try to avoid
+# accessing certain resources on restart
+# that may no longer be accessible because
+# of the privilege downgrade.
+persist-key
+persist-tun
+
+# Output a short status file showing
+# current connections, truncated
+# and rewritten every minute.
+status openvpn-status.log
+
+# By default, log messages will go to the syslog (or
+# on Windows, if running as a service, they will go to
+# the "\Program Files\OpenVPN\log" directory).
+# Use log or log-append to override this default.
+# "log" will truncate the log file on OpenVPN startup,
+# while "log-append" will append to it. Use one
+# or the other (but not both).
+;log openvpn.log
+;log-append openvpn.log
+
+# Set the appropriate level of log
+# file verbosity.
+#
+# 0 is silent, except for fatal errors
+# 4 is reasonable for general usage
+# 5 and 6 can help to debug connection problems
+# 9 is extremely verbose
+verb 3
+
+# Silence repeating messages. At most 20
+# sequential messages of the same message
+# category will be output to the log.
+;mute 20
diff --git a/sample-config-files/static-home.conf b/sample-config-files/static-home.conf
new file mode 100644
index 0000000..c966687
--- /dev/null
+++ b/sample-config-files/static-home.conf
@@ -0,0 +1,72 @@
+#
+# Sample OpenVPN configuration file for
+# home using a pre-shared static key.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+# For Linux 2.2 or non-Linux OSes,
+# you may want to use an explicit
+# unit number such as "tun1".
+# OpenVPN also supports virtual
+# ethernet "tap" devices.
+dev tun
+
+# Our OpenVPN peer is the office gateway.
+remote 1.2.3.4
+
+# 10.1.0.2 is our local VPN endpoint (home).
+# 10.1.0.1 is our remote VPN endpoint (office).
+ifconfig 10.1.0.2 10.1.0.1
+
+# Our up script will establish routes
+# once the VPN is alive.
+up ./home.up
+
+# Our pre-shared static key
+secret static.key
+
+# OpenVPN 2.0 uses UDP port 1194 by default
+# (official port assignment by iana.org 11/04).
+# OpenVPN 1.x uses UDP port 5000 by default.
+# Each OpenVPN tunnel must use
+# a different port number.
+# lport or rport can be used
+# to denote different ports
+# for local and remote.
+; port 1194
+
+# Downgrade UID and GID to
+# "nobody" after initialization
+# for extra security.
+; user nobody
+; group nobody
+
+# If you built OpenVPN with
+# LZO compression, uncomment
+# out the following line.
+; comp-lzo
+
+# Send a UDP ping to remote once
+# every 15 seconds to keep
+# stateful firewall connection
+# alive. Uncomment this
+# out if you are using a stateful
+# firewall.
+; ping 15
+
+# Uncomment this section for a more reliable detection when a system
+# loses its connection. For example, dial-ups or laptops that
+# travel to other locations.
+; ping 15
+; ping-restart 45
+; ping-timer-rem
+; persist-tun
+; persist-key
+
+# Verbosity level.
+# 0 -- quiet except for fatal errors.
+# 1 -- mostly quiet, but display non-fatal network errors.
+# 3 -- medium output, good for normal operation.
+# 9 -- verbose, good for troubleshooting
+verb 3
diff --git a/sample-config-files/static-office.conf b/sample-config-files/static-office.conf
new file mode 100644
index 0000000..68030cc
--- /dev/null
+++ b/sample-config-files/static-office.conf
@@ -0,0 +1,69 @@
+#
+# Sample OpenVPN configuration file for
+# office using a pre-shared static key.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+# For Linux 2.2 or non-Linux OSes,
+# you may want to use an explicit
+# unit number such as "tun1".
+# OpenVPN also supports virtual
+# ethernet "tap" devices.
+dev tun
+
+# 10.1.0.1 is our local VPN endpoint (office).
+# 10.1.0.2 is our remote VPN endpoint (home).
+ifconfig 10.1.0.1 10.1.0.2
+
+# Our up script will establish routes
+# once the VPN is alive.
+up ./office.up
+
+# Our pre-shared static key
+secret static.key
+
+# OpenVPN 2.0 uses UDP port 1194 by default
+# (official port assignment by iana.org 11/04).
+# OpenVPN 1.x uses UDP port 5000 by default.
+# Each OpenVPN tunnel must use
+# a different port number.
+# lport or rport can be used
+# to denote different ports
+# for local and remote.
+; port 1194
+
+# Downgrade UID and GID to
+# "nobody" after initialization
+# for extra security.
+; user nobody
+; group nobody
+
+# If you built OpenVPN with
+# LZO compression, uncomment
+# out the following line.
+; comp-lzo
+
+# Send a UDP ping to remote once
+# every 15 seconds to keep
+# stateful firewall connection
+# alive. Uncomment this
+# out if you are using a stateful
+# firewall.
+; ping 15
+
+# Uncomment this section for a more reliable detection when a system
+# loses its connection. For example, dial-ups or laptops that
+# travel to other locations.
+; ping 15
+; ping-restart 45
+; ping-timer-rem
+; persist-tun
+; persist-key
+
+# Verbosity level.
+# 0 -- quiet except for fatal errors.
+# 1 -- mostly quiet, but display non-fatal network errors.
+# 3 -- medium output, good for normal operation.
+# 9 -- verbose, good for troubleshooting
+verb 3
diff --git a/sample-config-files/tls-home.conf b/sample-config-files/tls-home.conf
new file mode 100644
index 0000000..daa4ea1
--- /dev/null
+++ b/sample-config-files/tls-home.conf
@@ -0,0 +1,83 @@
+#
+# Sample OpenVPN configuration file for
+# home using SSL/TLS mode and RSA certificates/keys.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+# For Linux 2.2 or non-Linux OSes,
+# you may want to use an explicit
+# unit number such as "tun1".
+# OpenVPN also supports virtual
+# ethernet "tap" devices.
+dev tun
+
+# Our OpenVPN peer is the office gateway.
+remote 1.2.3.4
+
+# 10.1.0.2 is our local VPN endpoint (home).
+# 10.1.0.1 is our remote VPN endpoint (office).
+ifconfig 10.1.0.2 10.1.0.1
+
+# Our up script will establish routes
+# once the VPN is alive.
+up ./home.up
+
+# In SSL/TLS key exchange, Office will
+# assume server role and Home
+# will assume client role.
+tls-client
+
+# Certificate Authority file
+ca my-ca.crt
+
+# Our certificate/public key
+cert home.crt
+
+# Our private key
+key home.key
+
+# OpenVPN 2.0 uses UDP port 1194 by default
+# (official port assignment by iana.org 11/04).
+# OpenVPN 1.x uses UDP port 5000 by default.
+# Each OpenVPN tunnel must use
+# a different port number.
+# lport or rport can be used
+# to denote different ports
+# for local and remote.
+; port 1194
+
+# Downgrade UID and GID to
+# "nobody" after initialization
+# for extra security.
+; user nobody
+; group nobody
+
+# If you built OpenVPN with
+# LZO compression, uncomment
+# out the following line.
+; comp-lzo
+
+# Send a UDP ping to remote once
+# every 15 seconds to keep
+# stateful firewall connection
+# alive. Uncomment this
+# out if you are using a stateful
+# firewall.
+; ping 15
+
+# Uncomment this section for a more reliable detection when a system
+# loses its connection. For example, dial-ups or laptops that
+# travel to other locations.
+; ping 15
+; ping-restart 45
+; ping-timer-rem
+; persist-tun
+; persist-key
+
+# Verbosity level.
+# 0 -- quiet except for fatal errors.
+# 1 -- mostly quiet, but display non-fatal network errors.
+# 3 -- medium output, good for normal operation.
+# 9 -- verbose, good for troubleshooting
+verb 3
diff --git a/sample-config-files/tls-office.conf b/sample-config-files/tls-office.conf
new file mode 100644
index 0000000..f790f46
--- /dev/null
+++ b/sample-config-files/tls-office.conf
@@ -0,0 +1,83 @@
+#
+# Sample OpenVPN configuration file for
+# office using SSL/TLS mode and RSA certificates/keys.
+#
+# '#' or ';' may be used to delimit comments.
+
+# Use a dynamic tun device.
+# For Linux 2.2 or non-Linux OSes,
+# you may want to use an explicit
+# unit number such as "tun1".
+# OpenVPN also supports virtual
+# ethernet "tap" devices.
+dev tun
+
+# 10.1.0.1 is our local VPN endpoint (office).
+# 10.1.0.2 is our remote VPN endpoint (home).
+ifconfig 10.1.0.1 10.1.0.2
+
+# Our up script will establish routes
+# once the VPN is alive.
+up ./office.up
+
+# In SSL/TLS key exchange, Office will
+# assume server role and Home
+# will assume client role.
+tls-server
+
+# Diffie-Hellman Parameters (tls-server only)
+dh dh1024.pem
+
+# Certificate Authority file
+ca my-ca.crt
+
+# Our certificate/public key
+cert office.crt
+
+# Our private key
+key office.key
+
+# OpenVPN 2.0 uses UDP port 1194 by default
+# (official port assignment by iana.org 11/04).
+# OpenVPN 1.x uses UDP port 5000 by default.
+# Each OpenVPN tunnel must use
+# a different port number.
+# lport or rport can be used
+# to denote different ports
+# for local and remote.
+; port 1194
+
+# Downgrade UID and GID to
+# "nobody" after initialization
+# for extra security.
+; user nobody
+; group nobody
+
+# If you built OpenVPN with
+# LZO compression, uncomment
+# out the following line.
+; comp-lzo
+
+# Send a UDP ping to remote once
+# every 15 seconds to keep
+# stateful firewall connection
+# alive. Uncomment this
+# out if you are using a stateful
+# firewall.
+; ping 15
+
+# Uncomment this section for a more reliable detection when a system
+# loses its connection. For example, dial-ups or laptops that
+# travel to other locations.
+; ping 15
+; ping-restart 45
+; ping-timer-rem
+; persist-tun
+; persist-key
+
+# Verbosity level.
+# 0 -- quiet except for fatal errors.
+# 1 -- mostly quiet, but display non-fatal network errors.
+# 3 -- medium output, good for normal operation.
+# 9 -- verbose, good for troubleshooting
+verb 3
diff --git a/sample-config-files/xinetd-client-config b/sample-config-files/xinetd-client-config
new file mode 100644
index 0000000..03c5c1f
--- /dev/null
+++ b/sample-config-files/xinetd-client-config
@@ -0,0 +1,11 @@
+# This OpenVPN config file
+# is the client side counterpart
+# of xinetd-server-config
+
+dev tun
+ifconfig 10.4.0.1 10.4.0.2
+remote my-server
+port 1194
+user nobody
+secret /root/openvpn/key
+inactive 600
diff --git a/sample-config-files/xinetd-server-config b/sample-config-files/xinetd-server-config
new file mode 100644
index 0000000..803a6f8
--- /dev/null
+++ b/sample-config-files/xinetd-server-config
@@ -0,0 +1,25 @@
+# An xinetd configuration file for OpenVPN.
+#
+# This file should be renamed to openvpn or something suitably
+# descriptive and copied to the /etc/xinetd.d directory.
+# xinetd can then be made aware of this file by restarting
+# it or sending it a SIGHUP signal.
+#
+# For each potential incoming client, create a separate version
+# of this configuration file on a unique port number. Also note
+# that the key file and ifconfig endpoints should be unique for
+# each client. This configuration assumes that the OpenVPN
+# executable and key live in /root/openvpn. Change this to fit
+# your environment.
+
+service openvpn_1
+{
+ type = UNLISTED
+ port = 1194
+ socket_type = dgram
+ protocol = udp
+ wait = yes
+ user = root
+ server = /root/openvpn/openvpn
+ server_args = --inetd --dev tun --ifconfig 10.4.0.2 10.4.0.1 --secret /root/openvpn/key --inactive 600 --user nobody
+}
diff --git a/sample-keys/README b/sample-keys/README
new file mode 100644
index 0000000..1cd473a
--- /dev/null
+++ b/sample-keys/README
@@ -0,0 +1,14 @@
+Sample RSA keys.
+
+See the examples section of the man page
+for usage examples.
+
+NOTE: THESE KEYS ARE FOR TESTING PURPOSES ONLY.
+ DON'T USE THEM FOR ANY REAL WORK BECAUSE
+ THEY ARE TOTALLY INSECURE!
+
+ca.{crt,key} -- sample CA key/cert
+client.{crt,key} -- sample client key/cert
+server.{crt,key} -- sample server key/cert (nsCertType=server)
+pass.{crt,key} -- sample client key/cert with password-encrypted key
+ password = "password"
diff --git a/sample-keys/ca.crt b/sample-keys/ca.crt
new file mode 100644
index 0000000..e063ccc
--- /dev/null
+++ b/sample-keys/ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBjCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy
+NTE0NDA1NVoXDTE0MTEyMzE0NDA1NVowZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf
+BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAqPjWJnesPu6bR/iec4FMz3opVaPdBHxg+ORKNmrnVZPh0t8/
+ZT34KXkYoI9B82scurp8UlZVXG8JdUsz+yai8ti9+g7vcuyKUtcCIjn0HLgmdPu5
+gFX25lB0pXw+XIU031dOfPvtROdG5YZN5yCErgCy7TE7zntLnkEDuRmyU6cCAwEA
+AaOBwzCBwDAdBgNVHQ4EFgQUiaZg47rqPq/8ZH9MvYzSSI3gzEYwgZAGA1UdIwSB
+iDCBhYAUiaZg47rqPq/8ZH9MvYzSSI3gzEahaqRoMGYxCzAJBgNVBAYTAktHMQsw
+CQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQTi1U
+RVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CAQAwDAYDVR0T
+BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBfJoiWYrYdjM0mKPEzUQk0nLYTovBP
+I0es/2rfGrin1zbcFY+4dhVBd1E/StebnG+CP8r7QeEIwu7x8gYDdOLLsZn+2vBL
+e4jNU1ClI6Q0L7jrzhhunQ5mAaZztVyYwFB15odYcdN2iO0tP7jtEsvrRqxICNy3
+8itzViPTf5W4sA==
+-----END CERTIFICATE-----
diff --git a/sample-keys/ca.key b/sample-keys/ca.key
new file mode 100644
index 0000000..b4bf792
--- /dev/null
+++ b/sample-keys/ca.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCo+NYmd6w+7ptH+J5zgUzPeilVo90EfGD45Eo2audVk+HS3z9l
+PfgpeRigj0Hzaxy6unxSVlVcbwl1SzP7JqLy2L36Du9y7IpS1wIiOfQcuCZ0+7mA
+VfbmUHSlfD5chTTfV058++1E50blhk3nIISuALLtMTvOe0ueQQO5GbJTpwIDAQAB
+AoGAQuVREyWp4bhhbZr2UFBOco2ws6EOLWp4kdD/uI+WSoEjlHKiDJj+GJ1CrL5K
+o+4yD5MpCQf4/4FOQ0ukprfjJpDwDinTG6vzuWSLTHNiTgvksW3vy7IsNMJx97hT
+4D2QOOl9HhA50Qqg70teMPYXOgLRMVsdCIV7p7zDNy4nM+ECQQDX8m5ZcQmPtUDA
+38dPTfpL4U7kMB94FItJYH/Lk5kMW1/J33xymNhL+BHaG064ol9n2ubGW4XEO5t2
+qE1IOsVpAkEAyE/x/OBVSI1s75aYGlEwMd87p3qaDdtXT7WzujjRY7r8Y1ynkMU6
+GtMeneBX/lk4BY/6I+5bhAzce+hqhaXejwJBAL5Wg+c4GApf41xdogqHm7doNyYw
+OHyZ9w9NDDc+uGbI30xLPSCxEe0cEXgiG6foDpm2uzRZFTWaqHPU8pFYpAkCQGNX
+cpWM0/7VVK9Fqk1y8knpgfY/UWOJ4jU/0dCLGR0ywLSuYNPlXDmtdkOp3TnhGW14
+x/9F2NEWZ8pzq1B4wHUCQQC5ztD4m/rpiIpinoewUJODoeBJXYBKqx1+mdrALCq6
+ESvK1WRiusMaY3xmsdv4J2TB5iUPryELbn3jU12WGcQc
+-----END RSA PRIVATE KEY-----
diff --git a/sample-keys/client.crt b/sample-keys/client.crt
new file mode 100644
index 0000000..c047446
--- /dev/null
+++ b/sample-keys/client.crt
@@ -0,0 +1,65 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Nov 25 14:46:49 2004 GMT
+ Not After : Nov 23 14:46:49 2014 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:d2:12:5c:c6:4d:13:34:ae:cf:fa:ab:fe:cb:de:
+ 8c:f1:4b:4a:95:28:60:87:82:2c:b8:c1:e5:8e:c6:
+ 5d:11:58:61:a4:a5:f1:42:d7:86:74:6c:9d:9c:7a:
+ f0:3a:5c:29:e6:53:3b:5e:6d:d8:f0:45:06:2c:23:
+ ee:09:bc:02:8f:0e:b8:d5:33:1f:c3:4a:11:02:48:
+ 0b:cc:4b:ad:6e:74:e0:a2:53:b1:d6:cc:89:b9:e2:
+ 6f:db:15:b3:19:1e:57:04:79:48:3a:da:76:31:fc:
+ bf:d3:34:21:e7:32:d8:9e:06:4e:be:f3:e3:79:b0:
+ 54:fd:d1:42:32:aa:3e:7a:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 17:B7:3F:C7:62:A0:A9:FD:A4:31:0E:58:D7:D9:94:7B:4B:3F:CB:56
+ X509v3 Authority Key Identifier:
+ keyid:89:A6:60:E3:BA:EA:3E:AF:FC:64:7F:4C:BD:8C:D2:48:8D:E0:CC:46
+ DirName:/C=KG/ST=NA/L=BISHKEK/O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ serial:00
+
+ Signature Algorithm: md5WithRSAEncryption
+ 61:c6:d1:fa:24:0f:c7:be:09:3b:d8:04:17:63:31:17:07:f9:
+ 56:99:af:4c:67:fa:db:cb:94:cf:55:a5:7b:16:20:8b:42:64:
+ 13:23:62:45:28:93:5e:36:f7:db:02:95:a1:e9:fd:e3:0f:8d:
+ 73:a1:7b:0e:55:78:4d:a5:c4:b7:22:12:a0:ee:55:e0:b8:0e:
+ c9:9b:12:e3:b0:ef:9b:68:93:57:6e:6c:ad:16:68:8e:8d:30:
+ 33:fe:2a:1b:c3:03:8f:b6:0a:2d:0c:b1:3c:bb:f9:58:3f:8c:
+ 81:59:6b:14:dd:62:b5:c2:93:ed:5d:c6:19:0f:9b:4b:52:b3:
+ 7c:78
+-----BEGIN CERTIFICATE-----
+MIIDNTCCAp6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy
+NTE0NDY0OVoXDTE0MTEyMzE0NDY0OVowajELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxFDASBgNVBAMTC1Rlc3QtQ2xpZW50
+MSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBANISXMZNEzSuz/qr/svejPFLSpUoYIeCLLjB5Y7GXRFY
+YaSl8ULXhnRsnZx68DpcKeZTO15t2PBFBiwj7gm8Ao8OuNUzH8NKEQJIC8xLrW50
+4KJTsdbMibnib9sVsxkeVwR5SDradjH8v9M0Iecy2J4GTr7z43mwVP3RQjKqPnrB
+AgMBAAGjge4wgeswCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBH
+ZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFBe3P8dioKn9pDEOWNfZlHtL
+P8tWMIGQBgNVHSMEgYgwgYWAFImmYOO66j6v/GR/TL2M0kiN4MxGoWqkaDBmMQsw
+CQYDVQQGEwJLRzELMAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNV
+BAoTDE9wZW5WUE4tVEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9t
+YWluggEAMA0GCSqGSIb3DQEBBAUAA4GBAGHG0fokD8e+CTvYBBdjMRcH+VaZr0xn
++tvLlM9VpXsWIItCZBMjYkUok14299sClaHp/eMPjXOhew5VeE2lxLciEqDuVeC4
+DsmbEuOw75tok1dubK0WaI6NMDP+KhvDA4+2Ci0MsTy7+Vg/jIFZaxTdYrXCk+1d
+xhkPm0tSs3x4
+-----END CERTIFICATE-----
diff --git a/sample-keys/client.key b/sample-keys/client.key
new file mode 100644
index 0000000..17b9509
--- /dev/null
+++ b/sample-keys/client.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDSElzGTRM0rs/6q/7L3ozxS0qVKGCHgiy4weWOxl0RWGGkpfFC
+14Z0bJ2cevA6XCnmUztebdjwRQYsI+4JvAKPDrjVMx/DShECSAvMS61udOCiU7HW
+zIm54m/bFbMZHlcEeUg62nYx/L/TNCHnMtieBk6+8+N5sFT90UIyqj56wQIDAQAB
+AoGBAK8RoIGekCfym99DYYfTg9A/t/tQeAnWYaDj7oSrKbqf1lgZ91OGPEZgkoVr
+KzLnxf9uU+bhUs8CJx+4HdO8/L9rAJA+oD9QNuMp0elN4AKuEGE1Eq3a0e3cmgPI
++VIoXM6WVAGgK9I03Zu/UerYQ/DdXWGOIsKhFe8qyQoG9pKxAkEA9ld6O9MHQt3d
+JAjJkgCNn4psozxjrfLWy2huXd3H3CRqGMjLITDGzdkVSgXjHokBYroi0+TZTu4M
+ulJSJaWwBQJBANpO2DAexH2zRHw5Z6QyeEVxz7B3/FzU4GgJx9BH+FSBh+F0G5Ln
+ir5Vst8vZ/LGcgpYjHQLNAvZVgUjiQ4Y6I0CQGvwMJL+CHR4GmmroAblTyjU0n1D
+/Lk/anZ+L73Za7U+D28ErFzCrpmLwRRKOBYtGfpUbOZDpCQ9kj4hy/TLALECQCcL
+9ysUNbzt9Y/qjJkX1d9F7gn4TBEmmkTBixW76bTjvjQbGlt6Qpyso2O8DPGlgPxM
+vkJ7RoHgC7y7kGYPGnkCQBVxSNGIjLx4NQBgN4HD0y4+fars1PTUGnckBcS4npb9
+onLNyerBlWdBwbARyBS7WPIbyyf5VCrn3yIqWxaARO0=
+-----END RSA PRIVATE KEY-----
diff --git a/sample-keys/dh1024.pem b/sample-keys/dh1024.pem
new file mode 100644
index 0000000..7ce05f0
--- /dev/null
+++ b/sample-keys/dh1024.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh
+1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32
+9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC
+-----END DH PARAMETERS-----
diff --git a/sample-keys/pass.crt b/sample-keys/pass.crt
new file mode 100644
index 0000000..8bb7b17
--- /dev/null
+++ b/sample-keys/pass.crt
@@ -0,0 +1,65 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 3 (0x3)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Nov 25 14:48:55 2004 GMT
+ Not After : Nov 23 14:48:55 2014 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client-Password/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:ca:b4:05:67:7b:51:c1:d2:fe:21:57:b1:a5:57:
+ 5c:c0:86:38:05:a8:91:cf:e7:a4:bd:7a:76:d8:3b:
+ cf:fe:f3:78:65:24:d6:72:7d:1b:6d:b6:da:04:f2:
+ a8:f6:b4:04:78:d2:24:a7:21:2f:ca:29:46:96:0f:
+ 0b:91:31:66:1e:4d:22:9a:5d:05:17:99:9c:a0:7e:
+ e0:2a:be:78:0c:a1:b9:d4:04:c4:ec:f8:61:79:62:
+ b5:52:2d:f5:41:af:db:9f:8c:ab:08:1b:b7:95:b8:
+ c1:f0:29:d3:da:fb:00:3f:8e:5c:27:e3:8d:fa:ee:
+ dc:b4:3b:0b:8b:e0:ab:c1:c1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 40:57:F1:8C:9C:86:B2:DA:E0:3F:A7:B8:D7:85:43:45:07:8A:40:73
+ X509v3 Authority Key Identifier:
+ keyid:89:A6:60:E3:BA:EA:3E:AF:FC:64:7F:4C:BD:8C:D2:48:8D:E0:CC:46
+ DirName:/C=KG/ST=NA/L=BISHKEK/O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ serial:00
+
+ Signature Algorithm: md5WithRSAEncryption
+ a5:79:72:7f:a2:08:28:8e:66:da:e1:d0:be:bb:97:3d:65:9f:
+ ab:1e:19:ac:f1:66:44:14:8f:4e:7c:eb:ea:1e:2f:57:ea:44:
+ 46:4c:b9:56:5b:c0:0c:58:d2:45:87:26:6d:82:de:8c:64:b8:
+ 8b:22:61:61:c6:68:36:08:9d:5a:fd:2f:e5:21:e1:a2:0c:7f:
+ 3e:ca:e1:06:ea:9f:81:62:3d:a0:ce:f1:1e:0d:ab:86:89:ed:
+ 9a:89:34:32:c9:e9:6d:7d:f5:11:c3:5d:7e:a5:f7:f1:a6:83:
+ 77:1b:94:67:d9:0f:5c:ac:0e:08:4a:88:98:65:49:eb:66:9e:
+ 2d:28
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAqegAwIBAgIBAzANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy
+NTE0NDg1NVoXDTE0MTEyMzE0NDg1NVowczELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxHTAbBgNVBAMTFFRlc3QtQ2xpZW50
+LVBhc3N3b3JkMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMq0BWd7UcHS/iFXsaVXXMCGOAWokc/n
+pL16dtg7z/7zeGUk1nJ9G2222gTyqPa0BHjSJKchL8opRpYPC5ExZh5NIppdBReZ
+nKB+4Cq+eAyhudQExOz4YXlitVIt9UGv25+Mqwgbt5W4wfAp09r7AD+OXCfjjfru
+3LQ7C4vgq8HBAgMBAAGjge4wgeswCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYd
+T3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFEBX8YychrLa
+4D+nuNeFQ0UHikBzMIGQBgNVHSMEgYgwgYWAFImmYOO66j6v/GR/TL2M0kiN4MxG
+oWqkaDBmMQswCQYDVQQGEwJLRzELMAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hL
+RUsxFTATBgNVBAoTDE9wZW5WUE4tVEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlo
+b3N0Lm15ZG9tYWluggEAMA0GCSqGSIb3DQEBBAUAA4GBAKV5cn+iCCiOZtrh0L67
+lz1ln6seGazxZkQUj0586+oeL1fqREZMuVZbwAxY0kWHJm2C3oxkuIsiYWHGaDYI
+nVr9L+Uh4aIMfz7K4Qbqn4FiPaDO8R4Nq4aJ7ZqJNDLJ6W199RHDXX6l9/Gmg3cb
+lGfZD1ysDghKiJhlSetmni0o
+-----END CERTIFICATE-----
diff --git a/sample-keys/pass.key b/sample-keys/pass.key
new file mode 100644
index 0000000..4916364
--- /dev/null
+++ b/sample-keys/pass.key
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,959F7365DBBFDB77
+
+nGm57l+rR/8dAZOHL/1x/6dt11zUca7rphjsgw6XRnSf3M/CWmHvHVjApWcNLEs5
+SWNMp1xfUogtGzsKoMBbnlZLDA7RVHUYD6dVMyCpc64UjzT08LmdZhtQYLAKmlUC
+PT1VXS4Ae+SrqCPUqJkw1xP3kr0F1EVCXNu0nhOBAuuTGOS7PPEyW2N+k4nRHtsR
+IaPp8GCuIeoR6CdymTFTq6d/GeCiEcyrUM4BNrG4GtRRrURxxOrzQFEOS5sjBPSg
+Km1lwa6zBQFRLg9dKjRBL4teKuPY5Z2Nmpcml/aN4CkdkVEso4lW6/UHLE/joOMQ
+0MdpdYtu8wnt1WI/Z4immQfl3MF+QcPMkqXXzCEhGG/5SbAo89KC46UXvu1Z5OhS
+8XFHhvYBivOYWgZ3XUQqyZ0ulF60mFX7aE1Ph/eEbhWBHmU39hGjxzop1UoPwqLx
+ahvtfvCkR3ZeqlWO9SHzCA3MlrKwQ1p1UL6nG6AJhNN9jSevH6by+8wr07NBZOqX
+fJx+J/8EdVsUCFG2UJxPwM83ZSwAsvKRqph6CuWEl9ndUb7rw6khmRIoY0Iz3LbU
+1MlcDoJNcJas6lYDr1UeFSk86g0SiGCHXZIqsjyUgq6HIy4YrAYiQUthnlF8tp2Q
+nNQBPLo1GsHf0dC2MqKfDFASu7ST+Bl+yajHcIiUXvUJPxWbjkWYG9Q2p2ZBLzZD
+uqeRr66OKxTzUS4go/QbHDNsAulXl61gQIEOdZw5uy/Jl11kyAI6EQbzmehagKdH
+EshTgKp8ks62y0bBHgy3FMKyidJ5Hm58ZDhBxrwN0w+vhRoTGOepTA==
+-----END RSA PRIVATE KEY-----
diff --git a/sample-keys/pkcs12.p12 b/sample-keys/pkcs12.p12
new file mode 100644
index 0000000..253d408
--- /dev/null
+++ b/sample-keys/pkcs12.p12
Binary files differ
diff --git a/sample-keys/server.crt b/sample-keys/server.crt
new file mode 100644
index 0000000..28bb4d9
--- /dev/null
+++ b/sample-keys/server.crt
@@ -0,0 +1,67 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: md5WithRSAEncryption
+ Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ Validity
+ Not Before: Nov 25 14:42:22 2004 GMT
+ Not After : Nov 23 14:42:22 2014 GMT
+ Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Server/emailAddress=me@myhost.mydomain
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:cb:4e:ac:f9:83:57:f6:69:d2:32:29:b4:bc:ad:
+ e6:f7:26:21:89:33:30:43:40:a3:35:d9:de:26:01:
+ d6:b4:f0:bc:0a:19:55:99:3b:f1:4c:91:60:b6:fd:
+ 74:34:8d:5a:c7:62:ec:ce:f2:d6:02:ce:57:32:f4:
+ 35:8c:71:a0:6d:65:2a:e7:80:ae:29:59:cf:36:73:
+ f8:7c:4a:73:90:fc:30:28:d5:46:7d:35:a4:4e:c9:
+ 9f:90:7b:e2:09:21:36:c5:a8:ec:85:82:9a:32:b4:
+ 91:3b:c1:d6:4f:9f:d1:f8:6f:68:f4:1d:d2:06:91:
+ 32:cc:9a:48:fd:cd:98:7f:2f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Cert Type:
+ SSL Server
+ Netscape Comment:
+ OpenSSL Generated Server Certificate
+ X509v3 Subject Key Identifier:
+ 69:11:FE:E7:9F:89:7B:71:34:69:C0:DC:82:F8:D0:5D:4D:FB:78:DF
+ X509v3 Authority Key Identifier:
+ keyid:89:A6:60:E3:BA:EA:3E:AF:FC:64:7F:4C:BD:8C:D2:48:8D:E0:CC:46
+ DirName:/C=KG/ST=NA/L=BISHKEK/O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
+ serial:00
+
+ Signature Algorithm: md5WithRSAEncryption
+ 35:5c:75:da:57:ef:b5:79:f2:a2:db:36:e4:75:e8:c7:bc:73:
+ 26:cf:30:36:4b:2e:51:46:37:60:2f:4e:2b:f6:71:a2:23:db:
+ 8e:d8:5c:d5:af:2e:22:28:dd:30:a8:89:66:3a:cc:5b:3c:0f:
+ 96:12:20:de:5e:41:52:74:35:ed:4c:26:40:19:ca:73:df:54:
+ b1:30:96:9c:a5:14:d0:38:28:3f:ab:30:07:d7:de:98:d2:7f:
+ 7f:90:b2:52:1d:e5:95:88:ed:ba:8a:6a:14:85:66:76:ec:75:
+ 30:e8:ae:94:f4:e1:76:fa:4b:0e:f1:53:d7:95:be:fb:69:fa:
+ 3d:32
+-----BEGIN CERTIFICATE-----
+MIIDUTCCArqgAwIBAgIBATANBgkqhkiG9w0BAQQFADBmMQswCQYDVQQGEwJLRzEL
+MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
+VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTA0MTEy
+NTE0NDIyMloXDTE0MTEyMzE0NDIyMlowajELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxFDASBgNVBAMTC1Rlc3QtU2VydmVy
+MSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBAMtOrPmDV/Zp0jIptLyt5vcmIYkzMENAozXZ3iYB1rTw
+vAoZVZk78UyRYLb9dDSNWsdi7M7y1gLOVzL0NYxxoG1lKueArilZzzZz+HxKc5D8
+MCjVRn01pE7Jn5B74gkhNsWo7IWCmjK0kTvB1k+f0fhvaPQd0gaRMsyaSP3NmH8v
+AgMBAAGjggEJMIIBBTAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAzBglg
+hkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRl
+MB0GA1UdDgQWBBRpEf7nn4l7cTRpwNyC+NBdTft43zCBkAYDVR0jBIGIMIGFgBSJ
+pmDjuuo+r/xkf0y9jNJIjeDMRqFqpGgwZjELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
+Ak5BMRAwDgYDVQQHEwdCSVNIS0VLMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxITAf
+BgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpboIBADANBgkqhkiG9w0BAQQF
+AAOBgQA1XHXaV++1efKi2zbkdejHvHMmzzA2Sy5RRjdgL04r9nGiI9uO2FzVry4i
+KN0wqIlmOsxbPA+WEiDeXkFSdDXtTCZAGcpz31SxMJacpRTQOCg/qzAH196Y0n9/
+kLJSHeWViO26imoUhWZ27HUw6K6U9OF2+ksO8VPXlb77afo9Mg==
+-----END CERTIFICATE-----
diff --git a/sample-keys/server.key b/sample-keys/server.key
new file mode 100644
index 0000000..976acab
--- /dev/null
+++ b/sample-keys/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDLTqz5g1f2adIyKbS8reb3JiGJMzBDQKM12d4mAda08LwKGVWZ
+O/FMkWC2/XQ0jVrHYuzO8tYCzlcy9DWMcaBtZSrngK4pWc82c/h8SnOQ/DAo1UZ9
+NaROyZ+Qe+IJITbFqOyFgpoytJE7wdZPn9H4b2j0HdIGkTLMmkj9zZh/LwIDAQAB
+AoGBAKP1ljA/iY/zNY447kZ/5NWKzd7tBk4mcbl7M9no/7O6tZtbZRoIKoi6cYoC
+C1ZabUyBbkNTud5XdCFmq0zRUjOWvoFMZ9VZfd2kRPvl4TGczBtJAq65b+EYMGui
+q6T9p61xPdtzu0vM+Ecj127pAMk5XcJyxu8XQK7lZWmG5UoJAkEA8CxXNZN+A3qD
+bMBPI3VdwKCNSjNVEQEnygMbNgw7VLdxPpspzZziqJEGdzsM4dsnOBwKxIWFLN2h
+lbGBOquAswJBANi0atGWM8VUxDjvqqHCTS9RUXWgnvYhee4/xraJBQPBSivjC9P0
+vKT7PjBHU6djtKSLKGaHn1vHqmyY7PCMjZUCQQCNVSqExqSzG1dXmdt4PErNXi2G
+6qo2dX2arTVIGu6XLdQgSWLSMm5XT/CEHWW5SyPLKwVTHFeATXQXCPvJML9tAkEA
+k0yXax0g1ZoXwufN4SQUmPw6Va03P/BjU/nP1ZVvbiz9gLVU/d7WN4J7tA9XomkY
+idv5OzAmtxkSE70jGSNAvQJAWhCf9+iHkzOHRyKKOYlh1DHUwDfSEp+hlZYg9H03
+P2sraQzUxgWDY/DIY63KvW78ny863baFz7onz21MYGgJXg==
+-----END RSA PRIVATE KEY-----
diff --git a/sample-keys/ta.key b/sample-keys/ta.key
new file mode 100644
index 0000000..4d6656b
--- /dev/null
+++ b/sample-keys/ta.key
@@ -0,0 +1,21 @@
+#
+# 2048 bit OpenVPN static key
+#
+-----BEGIN OpenVPN Static key V1-----
+0ced1f4037e2f0313eac5002536e69fd
+50cd49736a392ce423df01e9692e34f0
+fa2b1e9bcc0a3ec17bfcabf660cd3c44
+7bab4b01a0f0af075262d513657ed9b3
+f5fa96e93b00a96fb3177569d4e1f432
+d3f1c72da118496a723329ab1d3b2dee
+c9d9180bc07ff10b0537da11fcd2632a
+e67adc78c1feda90db8eb7858320d5bb
+e3edf64e0e8ee951b9a1bc6e0606d7c8
+90b6481d2d7d4508282fa16b9402eeaf
+7d32c3bf61ccf98f1f7ac3de55da0351
+a4f23623f3a9e38d49e6a8aec4173cb2
+3687d72d42f910b1dd7c688e4e509d81
+024c1d82c22f1277cd6c8ce71cf4bb60
+953c45659cdc7dbb88f45fcd90a60f61
+753a63ed08cc974a57d36f743a30b0a3
+-----END OpenVPN Static key V1-----
diff --git a/sample-scripts/auth-pam.pl b/sample-scripts/auth-pam.pl
new file mode 100755
index 0000000..5333bad
--- /dev/null
+++ b/sample-scripts/auth-pam.pl
@@ -0,0 +1,97 @@
+#!/usr/bin/perl -t
+
+# OpenVPN PAM AUTHENTICATON
+# This script can be used to add PAM-based authentication
+# to OpenVPN 2.0. The OpenVPN client must provide
+# a username/password, using the --auth-user-pass directive.
+# The OpenVPN server should specify --auth-user-pass-verify
+# with this script as the argument and the 'via-file' method
+# specified. The server can also optionally specify
+# --client-cert-not-required and/or --username-as-common-name.
+
+# SCRIPT OPERATION
+# Return success or failure status based on whether or not a
+# given username/password authenticates using PAM.
+# Caller should write username/password as two lines in a file
+# which is passed to this script as a command line argument.
+
+# CAVEATS
+# * Requires Authen::PAM module, which may also
+# require the pam-devel package.
+# * May need to be run as root in order to
+# access username/password file.
+
+# NOTES
+# * This script is provided mostly as a demonstration of the
+# --auth-user-pass-verify script capability in OpenVPN.
+# For real world usage, see the auth-pam module in the plugin
+# folder.
+
+use Authen::PAM;
+use POSIX;
+
+# This "conversation function" will pass
+# $password to PAM when it asks for it.
+
+sub my_conv_func {
+ my @res;
+ while ( @_ ) {
+ my $code = shift;
+ my $msg = shift;
+ my $ans = "";
+
+ $ans = $password if $msg =~ /[Pp]assword/;
+
+ push @res, (PAM_SUCCESS(),$ans);
+ }
+ push @res, PAM_SUCCESS();
+ return @res;
+}
+
+# Identify service type to PAM
+$service = "login";
+
+# Get username/password from file
+
+if ($ARG = shift @ARGV) {
+ if (!open (UPFILE, "<$ARG")) {
+ print "Could not open username/password file: $ARG\n";
+ exit 1;
+ }
+} else {
+ print "No username/password file specified on command line\n";
+ exit 1;
+}
+
+$username = <UPFILE>;
+$password = <UPFILE>;
+
+if (!$username || !$password) {
+ print "Username/password not found in file: $ARG\n";
+ exit 1;
+}
+
+chomp $username;
+chomp $password;
+
+close (UPFILE);
+
+# Initialize PAM object
+
+if (!ref($pamh = new Authen::PAM($service, $username, \&my_conv_func))) {
+ print "Authen::PAM init failed\n";
+ exit 1;
+}
+
+# Authenticate with PAM
+
+$res = $pamh->pam_authenticate;
+
+# Return success or failure
+
+if ($res == PAM_SUCCESS()) {
+ exit 0;
+} else {
+ print "Auth '$username' failed, PAM said: ", $pamh->pam_strerror($res), "\n";
+ exit 1;
+}
diff --git a/sample-scripts/bridge-start b/sample-scripts/bridge-start
new file mode 100755
index 0000000..d20a260
--- /dev/null
+++ b/sample-scripts/bridge-start
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#################################
+# Set up Ethernet bridge on Linux
+# Requires: bridge-utils
+#################################
+
+# Define Bridge Interface
+br="br0"
+
+# Define list of TAP interfaces to be bridged,
+# for example tap="tap0 tap1 tap2".
+tap="tap0"
+
+# Define physical ethernet interface to be bridged
+# with TAP interface(s) above.
+eth="eth0"
+eth_ip="192.168.8.4"
+eth_netmask="255.255.255.0"
+eth_broadcast="192.168.8.255"
+
+for t in $tap; do
+ openvpn --mktun --dev $t
+done
+
+brctl addbr $br
+brctl addif $br $eth
+
+for t in $tap; do
+ brctl addif $br $t
+done
+
+for t in $tap; do
+ ifconfig $t 0.0.0.0 promisc up
+done
+
+ifconfig $eth 0.0.0.0 promisc up
+
+ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast
diff --git a/sample-scripts/bridge-stop b/sample-scripts/bridge-stop
new file mode 100755
index 0000000..8192779
--- /dev/null
+++ b/sample-scripts/bridge-stop
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+####################################
+# Tear Down Ethernet bridge on Linux
+####################################
+
+# Define Bridge Interface
+br="br0"
+
+# Define list of TAP interfaces to be bridged together
+tap="tap0"
+
+ifconfig $br down
+brctl delbr $br
+
+for t in $tap; do
+ openvpn --rmtun --dev $t
+done
diff --git a/sample-scripts/openvpn.init b/sample-scripts/openvpn.init
new file mode 100755
index 0000000..821abd5
--- /dev/null
+++ b/sample-scripts/openvpn.init
@@ -0,0 +1,244 @@
+#!/bin/sh
+#
+# openvpn This shell script takes care of starting and stopping
+# openvpn on RedHat or other chkconfig-based system.
+#
+# chkconfig: 345 24 76
+#
+# description: OpenVPN is a robust and highly flexible tunneling application \
+# that uses all of the encryption, authentication, and \
+# certification features of the OpenSSL library to securely \
+# tunnel IP networks over a single UDP port.
+#
+
+# Contributed to the OpenVPN project by
+# Douglas Keller <doug@voidstar.dyndns.org>
+# 2002.05.15
+
+# To install:
+# copy this file to /etc/rc.d/init.d/openvpn
+# shell> chkconfig --add openvpn
+# shell> mkdir /etc/openvpn
+# make .conf or .sh files in /etc/openvpn (see below)
+
+# To uninstall:
+# run: chkconfig --del openvpn
+
+# Author's Notes:
+#
+# I have created an /etc/init.d init script and enhanced openvpn.spec to
+# automatically register the init script. Once the RPM is installed you
+# can start and stop OpenVPN with "service openvpn start" and "service
+# openvpn stop".
+#
+# The init script does the following:
+#
+# - Starts an openvpn process for each .conf file it finds in
+# /etc/openvpn.
+#
+# - If /etc/openvpn/xxx.sh exists for a xxx.conf file then it executes
+# it before starting openvpn (useful for doing openvpn --mktun...).
+#
+# - In addition to start/stop you can do:
+#
+# service openvpn reload - SIGHUP
+# service openvpn reopen - SIGUSR1
+# service openvpn status - SIGUSR2
+#
+# Modifications:
+#
+# 2003.05.02
+# * Changed == to = for sh compliance (Bishop Clark).
+# * If condrestart|reload|reopen|status, check that we were
+# actually started (James Yonan).
+# * Added lock, piddir, and work variables (James Yonan).
+# * If start is attempted twice, without an intervening stop, or
+# if start is attempted when previous start was not properly
+# shut down, then kill any previously started processes, before
+# commencing new start operation (James Yonan).
+# * Do a better job of flagging errors on start, and properly
+# returning success or failure status to caller (James Yonan).
+#
+# 2005.04.04
+# * Added openvpn-startup and openvpn-shutdown script calls
+# (James Yonan).
+#
+
+# Location of openvpn binary
+openvpn=""
+openvpn_locations="/usr/sbin/openvpn /usr/local/sbin/openvpn"
+for location in $openvpn_locations
+do
+ if [ -f "$location" ]
+ then
+ openvpn=$location
+ fi
+done
+
+# Lockfile
+lock="/var/lock/subsys/openvpn"
+
+# PID directory
+piddir="/var/run/openvpn"
+
+# Our working directory
+work=/etc/openvpn
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+if [ ${NETWORKING} = "no" ]
+then
+ echo "Networking is down"
+ exit 0
+fi
+
+# Check that binary exists
+if ! [ -f $openvpn ]
+then
+ echo "openvpn binary not found"
+ exit 0
+fi
+
+# See how we were called.
+case "$1" in
+ start)
+ echo -n $"Starting openvpn: "
+
+ /sbin/modprobe tun >/dev/null 2>&1
+
+ # From a security perspective, I think it makes
+ # sense to remove this, and have users who need
+ # it explictly enable in their --up scripts or
+ # firewall setups.
+
+ #echo 1 > /proc/sys/net/ipv4/ip_forward
+
+ # Run startup script, if defined
+ if [ -f $work/openvpn-startup ]; then
+ $work/openvpn-startup
+ fi
+
+ if [ ! -d $piddir ]; then
+ mkdir $piddir
+ fi
+
+ if [ -f $lock ]; then
+ # we were not shut down correctly
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+ rm -f $pidf
+ done
+ rm -f $lock
+ sleep 2
+ fi
+
+ rm -f $piddir/*.pid
+ cd $work
+
+ # Start every .conf in $work and run .sh if exists
+ errors=0
+ successes=0
+ for c in `/bin/ls *.conf 2>/dev/null`; do
+ bn=${c%%.conf}
+ if [ -f "$bn.sh" ]; then
+ . $bn.sh
+ fi
+ rm -f $piddir/$bn.pid
+ $openvpn --daemon --writepid $piddir/$bn.pid --config $c --cd $work
+ if [ $? = 0 ]; then
+ successes=1
+ else
+ errors=1
+ fi
+ done
+
+ if [ $errors = 1 ]; then
+ failure; echo
+ else
+ success; echo
+ fi
+
+ if [ $successes = 1 ]; then
+ touch $lock
+ fi
+ ;;
+ stop)
+ echo -n $"Shutting down openvpn: "
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+ rm -f $pidf
+ done
+
+ # Run shutdown script, if defined
+ if [ -f $work/openvpn-shutdown ]; then
+ $work/openvpn-shutdown
+ fi
+
+ success; echo
+ rm -f $lock
+ ;;
+ restart)
+ $0 stop
+ sleep 2
+ $0 start
+ ;;
+ reload)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -HUP `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+ ;;
+ reopen)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR1 `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+ ;;
+ condrestart)
+ if [ -f $lock ]; then
+ $0 stop
+ # avoid race
+ sleep 2
+ $0 start
+ fi
+ ;;
+ status)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR2 `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ echo "Status written to /var/log/messages"
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+ ;;
+ *)
+ echo "Usage: openvpn {start|stop|restart|condrestart|reload|reopen|status}"
+ exit 1
+ ;;
+esac
+exit 0
diff --git a/sample-scripts/ucn.pl b/sample-scripts/ucn.pl
new file mode 100755
index 0000000..6d708f8
--- /dev/null
+++ b/sample-scripts/ucn.pl
@@ -0,0 +1,11 @@
+#!/usr/bin/perl -t
+
+# OpenVPN --auth-user-pass-verify script.
+# Only authenticate if username equals common_name.
+# In OpenVPN config file:
+# auth-user-pass-verify ./ucn.pl via-env
+
+$username = $ENV{'username'};
+$common_name = $ENV{'common_name'};
+
+exit !(length($username) > 0 && length($common_name) > 0 && $username eq $common_name);
diff --git a/sample-scripts/verify-cn b/sample-scripts/verify-cn
new file mode 100755
index 0000000..f9fea0f
--- /dev/null
+++ b/sample-scripts/verify-cn
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+# verify-cn -- a sample OpenVPN tls-verify script
+#
+# Return 0 if cn matches the common name component of
+# X509_NAME_oneline, 1 otherwise.
+#
+# For example in OpenVPN, you could use the directive:
+#
+# tls-verify "./verify-cn /etc/openvpn/allowed_clients"
+#
+# This would cause the connection to be dropped unless
+# the client common name is listed on a line in the
+# allowed_clients file.
+
+die "usage: verify-cn cnfile certificate_depth X509_NAME_oneline" if (@ARGV != 3);
+
+# Parse out arguments:
+# cnfile -- The file containing the list of common names, one per
+# line, which the client is required to have,
+# taken from the argument to the tls-verify directive
+# in the OpenVPN config file.
+# The file can have blank lines and comment lines that begin
+# with the # character.
+# depth -- The current certificate chain depth. In a typical
+# bi-level chain, the root certificate will be at level
+# 1 and the client certificate will be at level 0.
+# This script will be called separately for each level.
+# x509 -- the X509 subject string as extracted by OpenVPN from
+# the client's provided certificate.
+($cnfile, $depth, $x509) = @ARGV;
+
+if ($depth == 0) {
+ # If depth is zero, we know that this is the final
+ # certificate in the chain (i.e. the client certificate),
+ # and the one we are interested in examining.
+ # If so, parse out the common name substring in
+ # the X509 subject string.
+
+ if ($x509 =~ /\/CN=([^\/]+)/) {
+ $cn = $1;
+ # Accept the connection if the X509 common name
+ # string matches the passed cn argument.
+ open(FH, '<', $cnfile) or exit 1; # can't open, nobody authenticates!
+ while (defined($line = <FH>)) {
+ if ($line !~ /^[[:space:]]*(#|$)/o) {
+ chop($line);
+ if ($line eq $cn) {
+ exit 0;
+ }
+ }
+ }
+ close(FH);
+ }
+
+ # Authentication failed -- Either we could not parse
+ # the X509 subject string, or the common name in the
+ # subject string didn't match the passed cn argument.
+ exit 1;
+}
+
+# If depth is nonzero, tell OpenVPN to continue processing
+# the certificate chain.
+exit 0;
diff --git a/schedule.c b/schedule.c
new file mode 100644
index 0000000..f0482ab
--- /dev/null
+++ b/schedule.c
@@ -0,0 +1,653 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#if P2MP_SERVER
+
+#include "buffer.h"
+#include "misc.h"
+#include "crypto.h"
+#include "schedule.h"
+
+#include "memdbg.h"
+
+#ifdef SCHEDULE_TEST
+
+struct status
+{
+ int sru;
+ int ins;
+ int coll;
+ int lsteps;
+};
+
+static struct status z;
+
+#endif
+
+#ifdef ENABLE_DEBUG
+static void
+schedule_entry_debug_info (const char *caller, const struct schedule_entry *e)
+{
+ struct gc_arena gc = gc_new ();
+ if (e)
+ {
+ dmsg (D_SCHEDULER, "SCHEDULE: %s wakeup=[%s] pri=%u",
+ caller,
+ tv_string_abs (&e->tv, &gc),
+ e->pri);
+ }
+ else
+ {
+ dmsg (D_SCHEDULER, "SCHEDULE: %s NULL",
+ caller);
+ }
+ gc_free (&gc);
+}
+#endif
+
+static inline void
+schedule_set_pri (struct schedule_entry *e)
+{
+ e->pri = random ();
+ if (e->pri < 1)
+ e->pri = 1;
+}
+
+/* This is the master key comparison routine. A key is
+ * simply a struct timeval containing the absolute time for
+ * an event. The unique treap priority (pri) is used to ensure
+ * that keys do not collide.
+ */
+static inline int
+schedule_entry_compare (const struct schedule_entry *e1,
+ const struct schedule_entry *e2)
+{
+ if (e1->tv.tv_sec < e2->tv.tv_sec)
+ return -1;
+ else if (e1->tv.tv_sec > e2->tv.tv_sec)
+ return 1;
+ else
+ {
+ if (e1->tv.tv_usec < e2->tv.tv_usec)
+ return -1;
+ else if (e1->tv.tv_usec > e2->tv.tv_usec)
+ return 1;
+ else
+ {
+ if (e1->pri < e2->pri)
+ return -1;
+ else if (e1->pri > e2->pri)
+ return 1;
+ else
+ return 0;
+ }
+ }
+}
+
+/*
+ * Detach a btree node from its parent
+ */
+static inline void
+schedule_detach_parent (struct schedule *s, struct schedule_entry *e)
+{
+ if (e)
+ {
+ if (e->parent)
+ {
+ if (e->parent->lt == e)
+ e->parent->lt = NULL;
+ else if (e->parent->gt == e)
+ e->parent->gt = NULL;
+ else
+ {
+ /* parent <-> child linkage is corrupted */
+ ASSERT (0);
+ }
+ e->parent = NULL;
+ }
+ else
+ {
+ if (s->root == e) /* last element deleted, tree is empty */
+ s->root = NULL;
+ }
+ }
+}
+
+/*
+ *
+ * Given a binary search tree, move a node toward the root
+ * while still maintaining the correct ordering relationships
+ * within the tree. This function is the workhorse
+ * of the tree balancer.
+ *
+ * This code will break on key collisions, which shouldn't
+ * happen because the treap priority is considered part of the key
+ * and is guaranteed to be unique.
+ */
+static void
+schedule_rotate_up (struct schedule *s, struct schedule_entry *e)
+{
+ if (e && e->parent)
+ {
+ struct schedule_entry *lt = e->lt;
+ struct schedule_entry *gt = e->gt;
+ struct schedule_entry *p = e->parent;
+ struct schedule_entry *gp = p->parent;
+
+ if (gp) /* if grandparent exists, modify its child link */
+ {
+ if (gp->gt == p)
+ gp->gt = e;
+ else if (gp->lt == p)
+ gp->lt = e;
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ else /* no grandparent, now we are the root */
+ {
+ s->root = e;
+ }
+
+ /* grandparent is now our parent */
+ e->parent = gp;
+
+ /* parent is now our child */
+ p->parent = e;
+
+ /* reorient former parent's links
+ to reflect new position in the tree */
+ if (p->gt == e)
+ {
+ e->lt = p;
+ p->gt = lt;
+ if (lt)
+ lt->parent = p;
+ }
+ else if (p->lt == e)
+ {
+ e->gt = p;
+ p->lt = gt;
+ if (gt)
+ gt->parent = p;
+ }
+ else
+ {
+ /* parent <-> child linkage is corrupted */
+ ASSERT (0);
+ }
+
+#ifdef SCHEDULE_TEST
+ ++z.sru;
+#endif
+ }
+}
+
+/*
+ * This is the treap deletion algorithm:
+ *
+ * Rotate lesser-priority children up in the tree
+ * until we are childless. Then delete.
+ */
+void
+schedule_remove_node (struct schedule *s, struct schedule_entry *e)
+{
+ while (e->lt || e->gt)
+ {
+ if (e->lt)
+ {
+ if (e->gt)
+ {
+ if (e->lt->pri < e->gt->pri)
+ schedule_rotate_up (s, e->lt);
+ else
+ schedule_rotate_up (s, e->gt);
+ }
+ else
+ schedule_rotate_up (s, e->lt);
+ }
+ else if (e->gt)
+ schedule_rotate_up (s, e->gt);
+ }
+
+ schedule_detach_parent (s, e);
+ e->pri = 0;
+}
+
+/*
+ * Trivially add a node to a binary search tree without
+ * regard for balance.
+ */
+static void
+schedule_insert (struct schedule *s, struct schedule_entry *e)
+{
+ struct schedule_entry *c = s->root;
+ while (true)
+ {
+ const int comp = schedule_entry_compare (e, c);
+
+#ifdef SCHEDULE_TEST
+ ++z.ins;
+#endif
+
+ if (comp == -1)
+ {
+ if (c->lt)
+ {
+ c = c->lt;
+ continue;
+ }
+ else
+ {
+ c->lt = e;
+ e->parent = c;
+ break;
+ }
+ }
+ else if (comp == 1)
+ {
+ if (c->gt)
+ {
+ c = c->gt;
+ continue;
+ }
+ else
+ {
+ c->gt = e;
+ e->parent = c;
+ break;
+ }
+ }
+ else
+ {
+ /* rare key/priority collision -- no big deal,
+ just choose another priority and retry */
+#ifdef SCHEDULE_TEST
+ ++z.coll;
+#endif
+ schedule_set_pri (e);
+ /* msg (M_INFO, "PRI COLLISION pri=%u", e->pri); */
+ c = s->root;
+ continue;
+ }
+ }
+}
+
+/*
+ * Given an element, remove it from the btree if it's already
+ * there and re-insert it based on its current key.
+ */
+void
+schedule_add_modify (struct schedule *s, struct schedule_entry *e)
+{
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_SCHEDULER))
+ schedule_entry_debug_info ("schedule_add_modify", e);
+#endif
+
+ /* already in tree, remove */
+ if (IN_TREE (e))
+ schedule_remove_node (s, e);
+
+ /* set random priority */
+ schedule_set_pri (e);
+
+ if (s->root)
+ schedule_insert (s, e); /* trivial insert into tree */
+ else
+ s->root = e; /* tree was empty, we are the first element */
+
+ /* This is the magic of the randomized treap algorithm which
+ keeps the tree balanced. Move the node up the tree until
+ its own priority is greater than that of its parent */
+ while (e->parent && e->parent->pri > e->pri)
+ schedule_rotate_up (s, e);
+}
+
+/*
+ * Find the earliest event to be scheduled
+ */
+struct schedule_entry *
+schedule_find_least (struct schedule_entry *e)
+{
+ if (e)
+ {
+ while (e->lt)
+ {
+#ifdef SCHEDULE_TEST
+ ++z.lsteps;
+#endif
+ e = e->lt;
+ }
+ }
+
+#ifdef ENABLE_DEBUG
+ if (check_debug_level (D_SCHEDULER))
+ schedule_entry_debug_info ("schedule_find_least", e);
+#endif
+
+ return e;
+}
+
+/*
+ * Public functions below this point
+ */
+
+struct schedule *
+schedule_init (void)
+{
+ struct schedule *s;
+
+ ALLOC_OBJ_CLEAR (s, struct schedule);
+ return s;
+}
+
+void
+schedule_free (struct schedule *s)
+{
+ free (s);
+}
+
+void
+schedule_remove_entry (struct schedule *s, struct schedule_entry *e)
+{
+ s->earliest_wakeup = NULL; /* invalidate cache */
+ schedule_remove_node (s, e);
+}
+
+/*
+ * Debug functions below this point
+ */
+
+#ifdef SCHEDULE_TEST
+
+static inline struct schedule_entry *
+schedule_find_earliest_wakeup (struct schedule *s)
+{
+ return schedule_find_least (s->root);
+}
+
+/*
+ * Recursively check that the treap (btree) is
+ * internally consistent.
+ */
+int
+schedule_debug_entry (const struct schedule_entry* e,
+ int depth,
+ int *count,
+ struct timeval *least,
+ const struct timeval *min,
+ const struct timeval *max)
+{
+ struct gc_arena gc = gc_new ();
+ int maxdepth = depth;
+ if (e)
+ {
+ int d;
+
+ ASSERT (e != e->lt);
+ ASSERT (e != e->gt);
+ ASSERT (e != e->parent);
+ ASSERT (!e->parent || e->parent != e->lt);
+ ASSERT (!e->parent || e->parent != e->gt);
+ ASSERT (!e->lt || e->lt != e->gt);
+
+ if (e->lt)
+ {
+ ASSERT (e->lt->parent == e);
+ ASSERT (schedule_entry_compare (e->lt, e) == -1);
+ ASSERT (e->lt->pri >= e->pri);
+ }
+
+ if (e->gt)
+ {
+ ASSERT (e->gt->parent == e);
+ ASSERT (schedule_entry_compare (e->gt, e));
+ ASSERT (e->gt->pri >= e->pri);
+ }
+
+ ASSERT (tv_le (min, &e->tv));
+ ASSERT (tv_le (&e->tv, max));
+
+ if (count)
+ ++(*count);
+
+ if (least && tv_lt (&e->tv, least))
+ *least = e->tv;
+
+ d = schedule_debug_entry (e->lt, depth+1, count, least, min, &e->tv);
+ if (d > maxdepth)
+ maxdepth = d;
+
+ d = schedule_debug_entry (e->gt, depth+1, count, least, &e->tv, max);
+ if (d > maxdepth)
+ maxdepth = d;
+ }
+ gc_free (&gc);
+ return maxdepth;
+}
+
+int
+schedule_debug (struct schedule *s, int *count, struct timeval *least)
+{
+ struct timeval min;
+ struct timeval max;
+
+ min.tv_sec = 0;
+ min.tv_usec = 0;
+ max.tv_sec = 0x7FFFFFFF;
+ max.tv_usec = 0x7FFFFFFF;
+
+ if (s->root)
+ {
+ ASSERT (s->root->parent == NULL);
+ }
+ return schedule_debug_entry (s->root, 0, count, least, &min, &max);
+}
+
+#if 1
+
+void
+tv_randomize (struct timeval *tv)
+{
+ tv->tv_sec += random() % 100;
+ tv->tv_usec = random () % 100;
+}
+
+#else
+
+void
+tv_randomize (struct timeval *tv)
+{
+ struct gc_arena gc = gc_new ();
+ long int choice = get_random ();
+ if ((choice & 0xFF) == 0)
+ tv->tv_usec += ((choice >> 8) & 0xFF);
+ else
+ prng_bytes ((uint8_t *)tv, sizeof (struct timeval));
+ gc_free (&gc);
+}
+
+#endif
+
+void
+schedule_verify (struct schedule *s)
+{
+ struct gc_arena gc = gc_new ();
+ struct timeval least;
+ int count;
+ int maxlev;
+ struct schedule_entry* e;
+ const struct status zz = z;
+
+ least.tv_sec = least.tv_usec = 0x7FFFFFFF;
+
+ count = 0;
+
+ maxlev = schedule_debug (s, &count, &least);
+
+ e = schedule_find_earliest_wakeup (s);
+
+ if (e)
+ {
+ printf ("Verification Phase count=%d maxlev=%d sru=%d ins=%d coll=%d ls=%d l=%s",
+ count,
+ maxlev,
+ zz.sru,
+ zz.ins,
+ zz.coll,
+ zz.lsteps,
+ tv_string (&e->tv, &gc));
+
+ if (!tv_eq (&least, &e->tv))
+ printf (" [COMPUTED DIFFERENT MIN VALUES!]");
+
+ printf ("\n");
+ }
+
+ CLEAR (z);
+ gc_free (&gc);
+}
+
+void
+schedule_randomize_array (struct schedule_entry **array, int size)
+{
+ int i;
+ for (i = 0; i < size; ++i)
+ {
+ const int src = get_random () % size;
+ struct schedule_entry *tmp = array [i];
+ if (i != src)
+ {
+ array [i] = array [src];
+ array [src] = tmp;
+ }
+ }
+}
+
+void
+schedule_print_work (struct schedule_entry *e, int indent)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ for (i = 0; i < indent; ++i)
+ printf (" ");
+ if (e)
+ {
+ printf ("%s [%u] e=" ptr_format ", p=" ptr_format " lt=" ptr_format " gt=" ptr_format "\n",
+ tv_string (&e->tv, &gc),
+ e->pri,
+ (ptr_type)e,
+ (ptr_type)e->parent,
+ (ptr_type)e->lt,
+ (ptr_type)e->gt);
+ schedule_print_work (e->lt, indent+1);
+ schedule_print_work (e->gt, indent+1);
+ }
+ else
+ printf ("NULL\n");
+ gc_free (&gc);
+}
+
+void
+schedule_print (struct schedule *s)
+{
+ printf ("*************************\n");
+ schedule_print_work (s->root, 0);
+}
+
+void
+schedule_test (void)
+{
+ struct gc_arena gc = gc_new ();
+ int n = 1000;
+ int n_mod = 25;
+
+ int i, j;
+ struct schedule_entry **array;
+ struct schedule *s = schedule_init ();
+ struct schedule_entry* e;
+
+ CLEAR (z);
+ ALLOC_ARRAY (array, struct schedule_entry *, n);
+
+ printf ("Creation/Insertion Phase\n");
+
+ for (i = 0; i < n; ++i)
+ {
+ ALLOC_OBJ_CLEAR (array[i], struct schedule_entry);
+ tv_randomize (&array[i]->tv);
+ /*schedule_print (s);*/
+ /*schedule_verify (s);*/
+ schedule_add_modify (s, array[i]);
+ }
+
+ schedule_randomize_array (array, n);
+
+ /*schedule_print (s);*/
+ schedule_verify (s);
+
+ for (j = 1; j <= n_mod; ++j)
+ {
+ printf ("Modification Phase Pass %d\n", j);
+
+ for (i = 0; i < n; ++i)
+ {
+ e = schedule_find_earliest_wakeup (s);
+ /*printf ("BEFORE %s\n", tv_string (&e->tv, &gc));*/
+ tv_randomize (&e->tv);
+ /*printf ("AFTER %s\n", tv_string (&e->tv, &gc));*/
+ schedule_add_modify (s, e);
+ /*schedule_verify (s);*/
+ /*schedule_print (s);*/
+ }
+ schedule_verify (s);
+ /*schedule_print (s);*/
+ }
+
+ /*printf ("INS=%d\n", z.ins);*/
+
+ while ((e = schedule_find_earliest_wakeup (s)))
+ {
+ schedule_remove_node (s, e);
+ /*schedule_verify (s);*/
+ }
+ schedule_verify (s);
+
+ printf ("S->ROOT is %s\n", s->root ? "NOT NULL" : "NULL");
+
+ for (i = 0; i < n; ++i)
+ {
+ free (array[i]);
+ }
+ free (array);
+ free (s);
+ gc_free (&gc);
+}
+
+#endif
+#endif
diff --git a/schedule.h b/schedule.h
new file mode 100644
index 0000000..71c6d8c
--- /dev/null
+++ b/schedule.h
@@ -0,0 +1,132 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SCHEDULE_H
+#define SCHEDULE_H
+
+/*
+ * This code implements an efficient scheduler using
+ * a random treap binary tree.
+ *
+ * The scheduler is used by the server executive to
+ * keep track of which instances need service at a
+ * known time in the future. Instances need to
+ * schedule events for things such as sending
+ * a ping or scheduling a TLS renegotiation.
+ */
+
+#if P2MP_SERVER
+
+/* define to enable a special test mode */
+/*#define SCHEDULE_TEST*/
+
+#include "otime.h"
+#include "error.h"
+
+struct schedule_entry
+{
+ struct timeval tv; /* wakeup time */
+ unsigned int pri; /* random treap priority */
+ struct schedule_entry *parent; /* treap (btree) links */
+ struct schedule_entry *lt;
+ struct schedule_entry *gt;
+};
+
+struct schedule
+{
+ struct schedule_entry *earliest_wakeup; /* cached earliest wakeup */
+ struct schedule_entry *root; /* the root of the treap (btree) */
+};
+
+/* Public functions */
+
+struct schedule *schedule_init (void);
+void schedule_free (struct schedule *s);
+void schedule_remove_entry (struct schedule *s, struct schedule_entry *e);
+
+#ifdef SCHEDULE_TEST
+void schedule_test (void);
+#endif
+
+/* Private Functions */
+
+/* is node already in tree? */
+#define IN_TREE(e) ((e)->pri)
+
+struct schedule_entry *schedule_find_least (struct schedule_entry *e);
+void schedule_add_modify (struct schedule *s, struct schedule_entry *e);
+void schedule_remove_node (struct schedule *s, struct schedule_entry *e);
+
+/* Public inline functions */
+
+/*
+ * Add a struct schedule_entry (whose storage is managed by
+ * caller) to the btree. tv signifies the wakeup time for
+ * a future event. sigma is a time interval measured
+ * in microseconds -- the event window being represented
+ * starts at (tv - sigma) and ends at (tv + sigma).
+ * Event signaling can occur anywere within this interval.
+ * Making the interval larger makes the scheduler more efficient,
+ * while making it smaller results in more precise scheduling.
+ * The caller should treat the passed struct schedule_entry as
+ * an opaque object.
+ */
+static inline void
+schedule_add_entry (struct schedule *s,
+ struct schedule_entry *e,
+ const struct timeval *tv,
+ unsigned int sigma)
+{
+ if (!IN_TREE (e) || !sigma || !tv_within_sigma (tv, &e->tv, sigma))
+ {
+ e->tv = *tv;
+ schedule_add_modify (s, e);
+ s->earliest_wakeup = NULL; /* invalidate cache */
+ }
+}
+
+/*
+ * Return the node with the earliest wakeup time. If two
+ * nodes have the exact same wakeup time, select based on
+ * the random priority assigned to each node (the priority
+ * is randomized every time an entry is re-added).
+ */
+static inline struct schedule_entry *
+schedule_get_earliest_wakeup (struct schedule *s,
+ struct timeval *wakeup)
+{
+ struct schedule_entry *ret;
+
+ /* cache result */
+ if (!s->earliest_wakeup)
+ s->earliest_wakeup = schedule_find_least (s->root);
+ ret = s->earliest_wakeup;
+ if (ret)
+ *wakeup = ret->tv;
+
+ return ret;
+}
+
+#endif
+#endif
diff --git a/service-win32/Makefile.am b/service-win32/Makefile.am
new file mode 100644
index 0000000..4e4f55e
--- /dev/null
+++ b/service-win32/Makefile.am
@@ -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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+if WIN32
+
+sbin_PROGRAMS = openvpnserv
+
+openvpnserv_SOURCES = \
+ openvpnserv.c \
+ service.h service.c
+
+else
+
+dist_noinst_DATA = \
+ openvpnserv.c \
+ service.h service.c
+
+endif
diff --git a/service-win32/Makefile.in b/service-win32/Makefile.in
new file mode 100644
index 0000000..88ef15b
--- /dev/null
+++ b/service-win32/Makefile.in
@@ -0,0 +1,509 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+# distribution); if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@WIN32_TRUE@sbin_PROGRAMS = openvpnserv$(EXEEXT)
+subdir = service-win32
+DIST_COMMON = $(am__dist_noinst_DATA_DIST) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__openvpnserv_SOURCES_DIST = openvpnserv.c service.h service.c
+@WIN32_TRUE@am_openvpnserv_OBJECTS = openvpnserv.$(OBJEXT) \
+@WIN32_TRUE@ service.$(OBJEXT)
+openvpnserv_OBJECTS = $(am_openvpnserv_OBJECTS)
+openvpnserv_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(openvpnserv_SOURCES)
+DIST_SOURCES = $(am__openvpnserv_SOURCES_DIST)
+am__dist_noinst_DATA_DIST = openvpnserv.c service.h service.c
+DATA = $(dist_noinst_DATA)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+IFCONFIG = @IFCONFIG@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+IPROUTE = @IPROUTE@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MAN2HTML = @MAN2HTML@
+MKDIR_P = @MKDIR_P@
+NETSTAT = @NETSTAT@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+ROUTE = @ROUTE@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAP_ID = @TAP_ID@
+TAP_WIN32_MIN_MAJOR = @TAP_WIN32_MIN_MAJOR@
+TAP_WIN32_MIN_MINOR = @TAP_WIN32_MIN_MINOR@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+win32datadir = @win32datadir@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+@WIN32_TRUE@openvpnserv_SOURCES = \
+@WIN32_TRUE@ openvpnserv.c \
+@WIN32_TRUE@ service.h service.c
+
+@WIN32_FALSE@dist_noinst_DATA = \
+@WIN32_FALSE@ openvpnserv.c \
+@WIN32_FALSE@ service.h service.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu service-win32/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu service-win32/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+openvpnserv$(EXEEXT): $(openvpnserv_OBJECTS) $(openvpnserv_DEPENDENCIES)
+ @rm -f openvpnserv$(EXEEXT)
+ $(LINK) $(openvpnserv_OBJECTS) $(openvpnserv_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openvpnserv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-sbinPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-sbinPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-sbinPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/service-win32/msvc.mak b/service-win32/msvc.mak
new file mode 100644
index 0000000..ba4bab7
--- /dev/null
+++ b/service-win32/msvc.mak
@@ -0,0 +1,30 @@
+# This makefile builds the OpenVPN service wrapper for Windows in the
+# Visual Studio 2008 environment.
+
+# Some of these libs may not be needed
+LIBS = ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib
+EXE = openvpnserv.exe
+
+CPP=cl.exe
+CPP_ARG_COMMON=/nologo /W3 -DWIN32 -DWIN32_LEAN_AND_MEAN -D_CONSOLE -D_MBCS -D_CRT_SECURE_NO_DEPRECATE /FD /c -I".."
+CPP_PROJ=$(CPP_ARG_COMMON) /O2 /MD -DNDEBUG
+
+LINK32=link.exe
+LINK32_FLAGS=/nologo /subsystem:console /incremental:no
+
+OBJS = \
+ openvpnserv.obj \
+ service.obj
+
+openvpnserv : $(OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) "/out:$(EXE)" $(LIBS) $(OBJS)
+<<
+
+clean :
+ del /Q $(OBJS) $(EXE) *.idb *.pdb
+
+.c.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
diff --git a/service-win32/openvpnserv.c b/service-win32/openvpnserv.c
new file mode 100755
index 0000000..5b0eb6e
--- /dev/null
+++ b/service-win32/openvpnserv.c
@@ -0,0 +1,530 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This program allows one or more OpenVPN processes to be started
+ * as a service. To build, you must get the service sample from the
+ * Platform SDK and replace Simple.c with this file.
+ *
+ * You should also apply service.patch to
+ * service.c and service.h from the Platform SDK service sample.
+ *
+ * This code is designed to be built with the mingw compiler.
+ */
+
+#include "config.h"
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <process.h>
+#include "service.h"
+
+/* bool definitions */
+#define bool int
+#define true 1
+#define false 0
+
+/* These are new for 2000/XP, so they aren't in the mingw headers yet */
+#ifndef BELOW_NORMAL_PRIORITY_CLASS
+#define BELOW_NORMAL_PRIORITY_CLASS 0x00004000
+#endif
+#ifndef ABOVE_NORMAL_PRIORITY_CLASS
+#define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
+#endif
+
+struct security_attributes
+{
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR sd;
+};
+
+/*
+ * This event is initially created in the non-signaled
+ * state. It will transition to the signaled state when
+ * we have received a terminate signal from the Service
+ * Control Manager which will cause an asynchronous call
+ * of ServiceStop below.
+ */
+#define EXIT_EVENT_NAME PACKAGE "_exit_1"
+
+/*
+ * Which registry key in HKLM should
+ * we get config info from?
+ */
+#define REG_KEY "SOFTWARE\\" PACKAGE_NAME
+
+static HANDLE exit_event = NULL;
+
+/* clear an object */
+#define CLEAR(x) memset(&(x), 0, sizeof(x))
+
+/*
+ * Message handling
+ */
+#define M_INFO (0) // informational
+#define M_SYSERR (MSG_FLAGS_ERROR|MSG_FLAGS_SYS_CODE) // error + system code
+#define M_ERR (MSG_FLAGS_ERROR) // error
+
+/* write error to event log */
+#define MSG(flags, ...) \
+ { \
+ char x_msg[256]; \
+ openvpn_snprintf (x_msg, sizeof(x_msg), __VA_ARGS__); \
+ AddToMessageLog ((flags), x_msg); \
+ }
+
+/* get a registry string */
+#define QUERY_REG_STRING(name, data) \
+ { \
+ len = sizeof (data); \
+ status = RegQueryValueEx(openvpn_key, name, NULL, &type, data, &len); \
+ if (status != ERROR_SUCCESS || type != REG_SZ) \
+ { \
+ SetLastError (status); \
+ MSG (M_SYSERR, error_format_str, name); \
+ RegCloseKey (openvpn_key); \
+ goto finish; \
+ } \
+ }
+
+/* get a registry string */
+#define QUERY_REG_DWORD(name, data) \
+ { \
+ len = sizeof (DWORD); \
+ status = RegQueryValueEx(openvpn_key, name, NULL, &type, (LPBYTE)&data, &len); \
+ if (status != ERROR_SUCCESS || type != REG_DWORD || len != sizeof (DWORD)) \
+ { \
+ SetLastError (status); \
+ MSG (M_SYSERR, error_format_dword, name); \
+ RegCloseKey (openvpn_key); \
+ goto finish; \
+ } \
+ }
+
+/*
+ * This is necessary due to certain buggy implementations of snprintf,
+ * that don't guarantee null termination for size > 0.
+ * (copied from ../buffer.c, line 217)
+ * (git: 100644 blob e2f8caab0a5b2a870092c6cd508a1a50c21c3ba3 buffer.c)
+ */
+
+int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+{
+ va_list arglist;
+ int ret = 0;
+ if (size > 0)
+ {
+ va_start (arglist, format);
+ ret = vsnprintf (str, size, format, arglist);
+ va_end (arglist);
+ str[size - 1] = 0;
+ }
+ return ret;
+}
+
+
+bool
+init_security_attributes_allow_all (struct security_attributes *obj)
+{
+ CLEAR (*obj);
+
+ obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ obj->sa.lpSecurityDescriptor = &obj->sd;
+ obj->sa.bInheritHandle = TRUE;
+ if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
+ return false;
+ if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
+ return false;
+ return true;
+}
+
+HANDLE
+create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset)
+{
+ if (allow_all)
+ {
+ struct security_attributes sa;
+ if (!init_security_attributes_allow_all (&sa))
+ return NULL;
+ return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name);
+ }
+ else
+ return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name);
+}
+
+void
+close_if_open (HANDLE h)
+{
+ if (h != NULL)
+ CloseHandle (h);
+}
+
+static bool
+match (const WIN32_FIND_DATA *find, const char *ext)
+{
+ int i;
+
+ if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ return false;
+
+ if (!strlen (ext))
+ return true;
+
+ i = strlen (find->cFileName) - strlen (ext) - 1;
+ if (i < 1)
+ return false;
+
+ return find->cFileName[i] == '.' && !strcasecmp (find->cFileName + i + 1, ext);
+}
+
+/*
+ * Modify the extension on a filename.
+ */
+static bool
+modext (char *dest, int size, const char *src, const char *newext)
+{
+ int i;
+
+ if (size > 0 && (strlen (src) + 1) <= size)
+ {
+ strcpy (dest, src);
+ dest [size - 1] = '\0';
+ i = strlen (dest);
+ while (--i >= 0)
+ {
+ if (dest[i] == '\\')
+ break;
+ if (dest[i] == '.')
+ {
+ dest[i] = '\0';
+ break;
+ }
+ }
+ if (strlen (dest) + strlen(newext) + 2 <= size)
+ {
+ strcat (dest, ".");
+ strcat (dest, newext);
+ return true;
+ }
+ dest [0] = '\0';
+ }
+ return false;
+}
+
+VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv)
+{
+ char exe_path[MAX_PATH];
+ char config_dir[MAX_PATH];
+ char ext_string[16];
+ char log_dir[MAX_PATH];
+ char priority_string[64];
+ char append_string[2];
+
+ DWORD priority;
+ bool append;
+
+ ResetError ();
+
+ if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
+ {
+ MSG (M_ERR, "ReportStatusToSCMgr #1 failed");
+ goto finish;
+ }
+
+ /*
+ * Create our exit event
+ */
+ exit_event = create_event (EXIT_EVENT_NAME, false, false, true);
+ if (!exit_event)
+ {
+ MSG (M_ERR, "CreateEvent failed");
+ goto finish;
+ }
+
+ /*
+ * If exit event is already signaled, it means we were not
+ * shut down properly.
+ */
+ if (WaitForSingleObject (exit_event, 0) != WAIT_TIMEOUT)
+ {
+ MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly");
+ goto finish;
+ }
+
+ if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
+ {
+ MSG (M_ERR, "ReportStatusToSCMgr #2 failed");
+ goto finish;
+ }
+
+ /*
+ * Read info from registry in key HKLM\SOFTWARE\OpenVPN
+ */
+ {
+ HKEY openvpn_key;
+ LONG status;
+ DWORD len;
+ DWORD type;
+
+ static const char error_format_str[] =
+ "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s";
+
+ static const char error_format_dword[] =
+ "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s";
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ REG_KEY,
+ 0,
+ KEY_READ,
+ &openvpn_key);
+
+ if (status != ERROR_SUCCESS)
+ {
+ SetLastError (status);
+ MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found");
+ goto finish;
+ }
+
+ /* get path to openvpn.exe */
+ QUERY_REG_STRING ("exe_path", exe_path);
+
+ /* get path to configuration directory */
+ QUERY_REG_STRING ("config_dir", config_dir);
+
+ /* get extension on configuration files */
+ QUERY_REG_STRING ("config_ext", ext_string);
+
+ /* get path to log directory */
+ QUERY_REG_STRING ("log_dir", log_dir);
+
+ /* get priority for spawned OpenVPN subprocesses */
+ QUERY_REG_STRING ("priority", priority_string);
+
+ /* should we truncate or append to logfile? */
+ QUERY_REG_STRING ("log_append", append_string);
+
+ RegCloseKey (openvpn_key);
+ }
+
+ /* set process priority */
+ priority = NORMAL_PRIORITY_CLASS;
+ if (!strcasecmp (priority_string, "IDLE_PRIORITY_CLASS"))
+ priority = IDLE_PRIORITY_CLASS;
+ else if (!strcasecmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS"))
+ priority = BELOW_NORMAL_PRIORITY_CLASS;
+ else if (!strcasecmp (priority_string, "NORMAL_PRIORITY_CLASS"))
+ priority = NORMAL_PRIORITY_CLASS;
+ else if (!strcasecmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS"))
+ priority = ABOVE_NORMAL_PRIORITY_CLASS;
+ else if (!strcasecmp (priority_string, "HIGH_PRIORITY_CLASS"))
+ priority = HIGH_PRIORITY_CLASS;
+ else
+ {
+ MSG (M_ERR, "Unknown priority name: %s", priority_string);
+ goto finish;
+ }
+
+ /* set log file append/truncate flag */
+ append = false;
+ if (append_string[0] == '0')
+ append = false;
+ else if (append_string[0] == '1')
+ append = true;
+ else
+ {
+ MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string);
+ goto finish;
+ }
+
+ /*
+ * Instantiate an OpenVPN process for each configuration
+ * file found.
+ */
+ {
+ WIN32_FIND_DATA find_obj;
+ HANDLE find_handle;
+ BOOL more_files;
+ char find_string[MAX_PATH];
+
+ openvpn_snprintf (find_string, MAX_PATH, "%s\\*", config_dir);
+
+ find_handle = FindFirstFile (find_string, &find_obj);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ {
+ MSG (M_ERR, "Cannot get configuration file list using: %s", find_string);
+ goto finish;
+ }
+
+ /*
+ * Loop over each config file
+ */
+ do {
+ HANDLE log_handle = NULL;
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+ struct security_attributes sa;
+ char log_file[MAX_PATH];
+ char log_path[MAX_PATH];
+ char command_line[256];
+
+ CLEAR (start_info);
+ CLEAR (proc_info);
+ CLEAR (sa);
+
+ if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
+ {
+ MSG (M_ERR, "ReportStatusToSCMgr #3 failed");
+ FindClose (find_handle);
+ goto finish;
+ }
+
+ /* does file have the correct type and extension? */
+ if (match (&find_obj, ext_string))
+ {
+ /* get log file pathname */
+ if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log"))
+ {
+ MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName);
+ FindClose (find_handle);
+ goto finish;
+ }
+ openvpn_snprintf (log_path, sizeof(log_path),
+ "%s\\%s", log_dir, log_file);
+
+ /* construct command line */
+ openvpn_snprintf (command_line, sizeof(command_line), PACKAGE " --service %s 1 --config \"%s\"",
+ EXIT_EVENT_NAME,
+ find_obj.cFileName);
+
+ /* Make security attributes struct for logfile handle so it can
+ be inherited. */
+ if (!init_security_attributes_allow_all (&sa))
+ {
+ MSG (M_SYSERR, "InitializeSecurityDescriptor start_" PACKAGE " failed");
+ goto finish;
+ }
+
+ /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */
+ log_handle = CreateFile (log_path,
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ &sa.sa,
+ append ? OPEN_ALWAYS : CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (log_handle == INVALID_HANDLE_VALUE)
+ {
+ MSG (M_SYSERR, "Cannot open logfile: %s", log_path);
+ FindClose (find_handle);
+ goto finish;
+ }
+
+ /* append to logfile? */
+ if (append)
+ {
+ if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ MSG (M_SYSERR, "Cannot seek to end of logfile: %s", log_path);
+ FindClose (find_handle);
+ goto finish;
+ }
+ }
+
+ /* fill in STARTUPINFO struct */
+ GetStartupInfo(&start_info);
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+ start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start_info.hStdOutput = start_info.hStdError = log_handle;
+
+ /* create an OpenVPN process for one config file */
+ if (!CreateProcess(exe_path,
+ command_line,
+ NULL,
+ NULL,
+ TRUE,
+ priority | CREATE_NEW_CONSOLE,
+ NULL,
+ config_dir,
+ &start_info,
+ &proc_info))
+ {
+ MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'",
+ exe_path,
+ command_line,
+ config_dir);
+
+ FindClose (find_handle);
+ CloseHandle (log_handle);
+ goto finish;
+ }
+
+ /* close unneeded handles */
+ Sleep (1000); /* try to prevent race if we close logfile
+ handle before child process DUPs it */
+ if (!CloseHandle (proc_info.hProcess)
+ || !CloseHandle (proc_info.hThread)
+ || !CloseHandle (log_handle))
+ {
+ MSG (M_SYSERR, "CloseHandle failed");
+ goto finish;
+ }
+ }
+
+ /* more files to process? */
+ more_files = FindNextFile (find_handle, &find_obj);
+
+ } while (more_files);
+
+ FindClose (find_handle);
+ }
+
+ /* we are now fully started */
+ if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
+ {
+ MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed");
+ goto finish;
+ }
+
+ /* wait for our shutdown signal */
+ if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0)
+ {
+ MSG (M_ERR, "wait for shutdown signal failed");
+ }
+
+ finish:
+ ServiceStop ();
+ if (exit_event)
+ CloseHandle (exit_event);
+}
+
+VOID ServiceStop()
+{
+ if (exit_event)
+ SetEvent(exit_event);
+}
diff --git a/service-win32/service.c b/service-win32/service.c
new file mode 100644
index 0000000..91b5821
--- /dev/null
+++ b/service-win32/service.c
@@ -0,0 +1,695 @@
+/*---------------------------------------------------------------------------
+THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+PARTICULAR PURPOSE.
+
+Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
+
+MODULE: service.c
+
+PURPOSE: Implements functions required by all Windows NT services
+
+FUNCTIONS:
+ main(int argc, char **argv);
+ service_ctrl(DWORD dwCtrlCode);
+ service_main(DWORD dwArgc, LPTSTR *lpszArgv);
+ CmdInstallService();
+ CmdRemoveService();
+ CmdStartService();
+ CmdDebugService(int argc, char **argv);
+ ControlHandler ( DWORD dwCtrlType );
+ GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
+
+---------------------------------------------------------------------------*/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <tchar.h>
+
+#include "service.h"
+
+// internal variables
+SERVICE_STATUS ssStatus; // current status of the service
+SERVICE_STATUS_HANDLE sshStatusHandle;
+DWORD dwErr = 0;
+BOOL bDebug = FALSE;
+TCHAR szErr[256];
+
+// internal function prototypes
+VOID WINAPI service_ctrl(DWORD dwCtrlCode);
+VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
+int CmdInstallService();
+int CmdRemoveService();
+int CmdStartService();
+VOID CmdDebugService(int argc, char **argv);
+BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
+LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
+
+//
+// FUNCTION: main
+//
+// PURPOSE: entrypoint for service
+//
+// PARAMETERS:
+// argc - number of command line arguments
+// argv - array of command line arguments
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+// main() either performs the command line task, or
+// call StartServiceCtrlDispatcher to register the
+// main service thread. When the this call returns,
+// the service has stopped, so exit.
+//
+int __cdecl main(int argc, char **argv)
+{
+ SERVICE_TABLE_ENTRY dispatchTable[] =
+ {
+ { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
+ { NULL, NULL}
+ };
+
+ if ( (argc > 1) &&
+ ((*argv[1] == '-') || (*argv[1] == '/')) )
+ {
+ if ( _stricmp( "install", argv[1]+1 ) == 0 )
+ {
+ return CmdInstallService();
+ }
+ else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
+ {
+ return CmdRemoveService();
+ }
+ else if ( _stricmp( "start", argv[1]+1 ) == 0)
+ {
+ return CmdStartService();
+ }
+ else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
+ {
+ bDebug = TRUE;
+ CmdDebugService(argc, argv);
+ }
+ else
+ {
+ goto dispatch;
+ }
+ return 0;
+ }
+
+ // if it doesn't match any of the above parameters
+ // the service control manager may be starting the service
+ // so we must call StartServiceCtrlDispatcher
+ dispatch:
+ // this is just to be friendly
+ printf( "%s -install to install the service\n", SZAPPNAME );
+ printf( "%s -start to start the service\n", SZAPPNAME );
+ printf( "%s -remove to remove the service\n", SZAPPNAME );
+ printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
+ printf( "\nStartServiceCtrlDispatcher being called.\n" );
+ printf( "This may take several seconds. Please wait.\n" );
+
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ AddToMessageLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed."));
+
+ return 0;
+}
+
+
+
+//
+// FUNCTION: service_main
+//
+// PURPOSE: To perform actual initialization of the service
+//
+// PARAMETERS:
+// dwArgc - number of command line arguments
+// lpszArgv - array of command line arguments
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+// This routine performs the service initialization and then calls
+// the user defined ServiceStart() routine to perform majority
+// of the work.
+//
+void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
+{
+
+ // register our service control handler:
+ //
+ sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
+
+ if (!sshStatusHandle)
+ goto cleanup;
+
+ // SERVICE_STATUS members that don't change in example
+ //
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwServiceSpecificExitCode = 0;
+
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto cleanup;
+
+
+ ServiceStart( dwArgc, lpszArgv );
+
+ cleanup:
+
+ // try to report the stopped status to the service control manager.
+ //
+ if (sshStatusHandle)
+ (VOID)ReportStatusToSCMgr(
+ SERVICE_STOPPED,
+ dwErr,
+ 0);
+
+ return;
+}
+
+
+
+//
+// FUNCTION: service_ctrl
+//
+// PURPOSE: This function is called by the SCM whenever
+// ControlService() is called on this service.
+//
+// PARAMETERS:
+// dwCtrlCode - type of control requested
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+//
+VOID WINAPI service_ctrl(DWORD dwCtrlCode)
+{
+ // Handle the requested control code.
+ //
+ switch (dwCtrlCode)
+ {
+ // Stop the service.
+ //
+ // SERVICE_STOP_PENDING should be reported before
+ // setting the Stop Event - hServerStopEvent - in
+ // ServiceStop(). This avoids a race condition
+ // which may result in a 1053 - The Service did not respond...
+ // error.
+ case SERVICE_CONTROL_STOP:
+ ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
+ ServiceStop();
+ return;
+
+ // Update the service status.
+ //
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ // invalid control code
+ //
+ default:
+ break;
+
+ }
+
+ ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
+}
+
+
+
+//
+// FUNCTION: ReportStatusToSCMgr()
+//
+// PURPOSE: Sets the current status of the service and
+// reports it to the Service Control Manager
+//
+// PARAMETERS:
+// dwCurrentState - the state of the service
+// dwWin32ExitCode - error code to report
+// dwWaitHint - worst case estimate to next checkpoint
+//
+// RETURN VALUE:
+// TRUE - success
+// FALSE - failure
+//
+// COMMENTS:
+//
+BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
+ DWORD dwWin32ExitCode,
+ DWORD dwWaitHint)
+{
+ static DWORD dwCheckPoint = 1;
+ BOOL fResult = TRUE;
+
+
+ if ( !bDebug ) // when debugging we don't report to the SCM
+ {
+ if (dwCurrentState == SERVICE_START_PENDING)
+ ssStatus.dwControlsAccepted = 0;
+ else
+ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ ssStatus.dwCurrentState = dwCurrentState;
+ ssStatus.dwWin32ExitCode = dwWin32ExitCode;
+ ssStatus.dwWaitHint = dwWaitHint;
+
+ if ( ( dwCurrentState == SERVICE_RUNNING ) ||
+ ( dwCurrentState == SERVICE_STOPPED ) )
+ ssStatus.dwCheckPoint = 0;
+ else
+ ssStatus.dwCheckPoint = dwCheckPoint++;
+
+
+ // Report the status of the service to the service control manager.
+ //
+ if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus)))
+ {
+ AddToMessageLog(MSG_FLAGS_ERROR, TEXT("SetServiceStatus"));
+ }
+ }
+ return fResult;
+}
+
+
+
+//
+// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
+//
+// PURPOSE: Allows any thread to log an error message
+//
+// PARAMETERS:
+// lpszMsg - text for message
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+//
+void AddToMessageLog(DWORD flags, LPTSTR lpszMsg)
+{
+ TCHAR szMsg [(sizeof(SZSERVICENAME) / sizeof(TCHAR)) + 100 ];
+ HANDLE hEventSource;
+ LPCSTR lpszStrings[2];
+
+ if ( !bDebug )
+ {
+ if (flags & MSG_FLAGS_SYS_CODE)
+ dwErr = GetLastError();
+ else
+ dwErr = 0;
+
+ // Use event logging to log the error.
+ //
+ hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
+
+ _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), (int)dwErr);
+ lpszStrings[0] = szMsg;
+ lpszStrings[1] = lpszMsg;
+
+ if (hEventSource != NULL)
+ {
+ ReportEvent(hEventSource, // handle of event source
+ // event type
+ (flags & MSG_FLAGS_ERROR)
+ ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
+ 0, // event category
+ 0, // event ID
+ NULL, // current user's SID
+ 2, // strings in lpszStrings
+ 0, // no bytes of raw data
+ lpszStrings, // array of error strings
+ NULL); // no raw data
+
+ (VOID) DeregisterEventSource(hEventSource);
+ }
+ }
+}
+
+void ResetError (void)
+{
+ dwErr = 0;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// The following code handles service installation and removal
+//
+
+
+//
+// FUNCTION: CmdInstallService()
+//
+// PURPOSE: Installs the service
+//
+// PARAMETERS:
+// none
+//
+// RETURN VALUE:
+// 0 if success
+//
+// COMMENTS:
+//
+int CmdInstallService()
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ TCHAR szPath[512];
+
+ int ret = 0;
+
+ if ( GetModuleFileName( NULL, szPath+1, 510 ) == 0 )
+ {
+ _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
+ return 1;
+ }
+ szPath[0] = '\"';
+ strcat(szPath, "\"");
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
+ );
+ if ( schSCManager )
+ {
+ schService = CreateService(
+ schSCManager, // SCManager database
+ TEXT(SZSERVICENAME), // name of service
+ TEXT(SZSERVICEDISPLAYNAME), // name to display
+ SERVICE_QUERY_STATUS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START
+ SERVICE_ERROR_NORMAL, // error control type
+ szPath, // service's binary
+ NULL, // no load ordering group
+ NULL, // no tag identifier
+ TEXT(SZDEPENDENCIES), // dependencies
+ NULL, // LocalSystem account
+ NULL); // no password
+
+ if ( schService )
+ {
+ _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ CloseServiceHandle(schService);
+ }
+ else
+ {
+ _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
+ ret = 1;
+ }
+
+ CloseServiceHandle(schSCManager);
+ }
+ else
+ {
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+ return ret;
+}
+
+//
+// FUNCTION: CmdStartService()
+//
+// PURPOSE: Start the service
+//
+// PARAMETERS:
+// none
+//
+// RETURN VALUE:
+// 0 if success
+//
+// COMMENTS:
+
+int CmdStartService()
+{
+ int ret = 0;
+
+ SC_HANDLE schSCManager;
+ SC_HANDLE schService;
+
+
+ // Open a handle to the SC Manager database.
+ schSCManager = OpenSCManager(
+ NULL, // local machine
+ NULL, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS); // full access rights
+
+ if (NULL == schSCManager) {
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+
+ schService = OpenService(
+ schSCManager, // SCM database
+ SZSERVICENAME, // service name
+ SERVICE_ALL_ACCESS);
+
+ if (schService == NULL) {
+ _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+
+ if (!StartService(
+ schService, // handle to service
+ 0, // number of arguments
+ NULL) ) // no arguments
+ {
+ _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+ else
+ {
+ _tprintf(TEXT("Service Started\n"));
+ ret = 0;
+ }
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+ return ret;
+}
+
+//
+// FUNCTION: CmdRemoveService()
+//
+// PURPOSE: Stops and removes the service
+//
+// PARAMETERS:
+// none
+//
+// RETURN VALUE:
+// 0 if success
+//
+// COMMENTS:
+//
+int CmdRemoveService()
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ int ret = 0;
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_CONNECT // access required
+ );
+ if ( schSCManager )
+ {
+ schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
+
+ if (schService)
+ {
+ // try to stop the service
+ if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
+ {
+ _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
+ Sleep( 1000 );
+
+ while ( QueryServiceStatus( schService, &ssStatus ) )
+ {
+ if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
+ {
+ _tprintf(TEXT("."));
+ Sleep( 1000 );
+ }
+ else
+ break;
+ }
+
+ if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
+ _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ {
+ _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ ret = 1;
+ }
+
+ }
+
+ // now remove the service
+ if ( DeleteService(schService) )
+ _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ {
+ _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+
+
+ CloseServiceHandle(schService);
+ }
+ else
+ {
+ _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+
+ CloseServiceHandle(schSCManager);
+ }
+ else
+ {
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+ ret = 1;
+ }
+ return ret;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////
+//
+// The following code is for running the service as a console app
+//
+
+
+//
+// FUNCTION: CmdDebugService(int argc, char ** argv)
+//
+// PURPOSE: Runs the service as a console application
+//
+// PARAMETERS:
+// argc - number of command line arguments
+// argv - array of command line arguments
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+//
+void CmdDebugService(int argc, char ** argv)
+{
+ DWORD dwArgc;
+ LPTSTR *lpszArgv;
+
+#ifdef UNICODE
+ lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
+ if (NULL == lpszArgv)
+ {
+ // CommandLineToArvW failed!!
+ _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
+ return;
+ }
+#else
+ dwArgc = (DWORD) argc;
+ lpszArgv = argv;
+#endif
+
+ _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+
+ SetConsoleCtrlHandler( ControlHandler, TRUE );
+
+ ServiceStart( dwArgc, lpszArgv );
+
+#ifdef UNICODE
+// Must free memory allocated for arguments
+
+ GlobalFree(lpszArgv);
+#endif // UNICODE
+
+}
+
+
+//
+// FUNCTION: ControlHandler ( DWORD dwCtrlType )
+//
+// PURPOSE: Handled console control events
+//
+// PARAMETERS:
+// dwCtrlType - type of control event
+//
+// RETURN VALUE:
+// True - handled
+// False - unhandled
+//
+// COMMENTS:
+//
+BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
+{
+ switch ( dwCtrlType )
+ {
+ case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
+ case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
+ _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+ ServiceStop();
+ return TRUE;
+ break;
+
+ }
+ return FALSE;
+}
+
+//
+// FUNCTION: GetLastErrorText
+//
+// PURPOSE: copies error message text to string
+//
+// PARAMETERS:
+// lpszBuf - destination buffer
+// dwSize - size of buffer
+//
+// RETURN VALUE:
+// destination buffer
+//
+// COMMENTS:
+//
+LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
+{
+ DWORD dwRet;
+ LPTSTR lpszTemp = NULL;
+
+ dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL,
+ GetLastError(),
+ LANG_NEUTRAL,
+ (LPTSTR)&lpszTemp,
+ 0,
+ NULL );
+
+ // supplied buffer is not long enough
+ if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
+ lpszBuf[0] = TEXT('\0');
+ else
+ {
+ lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
+ _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() );
+ }
+
+ if ( lpszTemp )
+ LocalFree((HLOCAL) lpszTemp );
+
+ return lpszBuf;
+}
diff --git a/service-win32/service.h b/service-win32/service.h
new file mode 100644
index 0000000..028d075
--- /dev/null
+++ b/service-win32/service.h
@@ -0,0 +1,141 @@
+/*---------------------------------------------------------------------------
+THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+PARTICULAR PURPOSE.
+
+Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
+
+ MODULE: service.h
+
+ Comments: The use of this header file and the accompanying service.c
+ file simplifies the process of writting a service. You as a developer
+ simply need to follow the TODO's outlined in this header file, and
+ implement the ServiceStart() and ServiceStop() functions.
+
+ There is no need to modify the code in service.c. Just add service.c
+ to your project and link with the following libraries...
+
+ libcmt.lib kernel32.lib advapi.lib shell32.lib
+
+ This code also supports unicode. Be sure to compile both service.c and
+ and code #include "service.h" with the same Unicode setting.
+
+ Upon completion, your code will have the following command line interface
+
+ <service exe> -? to display this list
+ <service exe> -install to install the service
+ <service exe> -remove to remove the service
+ <service exe> -debug <params> to run as a console app for debugging
+
+ Note: This code also implements Ctrl+C and Ctrl+Break handlers
+ when using the debug option. These console events cause
+ your ServiceStop routine to be called
+
+ Also, this code only handles the OWN_SERVICE service type
+ running in the LOCAL_SYSTEM security context.
+
+ To control your service ( start, stop, etc ) you may use the
+ Services control panel applet or the NET.EXE program.
+
+ To aid in writing/debugging service, the
+ SDK contains a utility (MSTOOLS\BIN\SC.EXE) that
+ can be used to control, configure, or obtain service status.
+ SC displays complete status for any service/driver
+ in the service database, and allows any of the configuration
+ parameters to be easily changed at the command line.
+ For more information on SC.EXE, type SC at the command line.
+
+
+------------------------------------------------------------------------------*/
+
+#ifndef _SERVICE_H
+#define _SERVICE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "config.h"
+
+//////////////////////////////////////////////////////////////////////////////
+//// todo: change to desired strings
+////
+// name of the executable
+#define SZAPPNAME PACKAGE "serv"
+// internal name of the service
+#define SZSERVICENAME PACKAGE_NAME "Service"
+// displayed name of the service
+#define SZSERVICEDISPLAYNAME PACKAGE_NAME " Service"
+// list of service dependencies - "dep1\0dep2\0\0"
+#define SZDEPENDENCIES TAP_ID "\0Dhcp\0\0"
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//// todo: ServiceStart()must be defined by in your code.
+//// The service should use ReportStatusToSCMgr to indicate
+//// progress. This routine must also be used by StartService()
+//// to report to the SCM when the service is running.
+////
+//// If a ServiceStop procedure is going to take longer than
+//// 3 seconds to execute, it should spawn a thread to
+//// execute the stop code, and return. Otherwise, the
+//// ServiceControlManager will believe that the service has
+//// stopped responding
+////
+ VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
+ VOID ServiceStop();
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//// The following are procedures which
+//// may be useful to call within the above procedures,
+//// but require no implementation by the user.
+//// They are implemented in service.c
+
+//
+// FUNCTION: ReportStatusToSCMgr()
+//
+// PURPOSE: Sets the current status of the service and
+// reports it to the Service Control Manager
+//
+// PARAMETERS:
+// dwCurrentState - the state of the service
+// dwWin32ExitCode - error code to report
+// dwWaitHint - worst case estimate to next checkpoint
+//
+// RETURN VALUE:
+// TRUE - success
+// FALSE - failure
+//
+ BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
+
+
+//
+// FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
+//
+// PURPOSE: Allows any thread to log an error message
+//
+// PARAMETERS:
+// lpszMsg - text for message
+//
+// RETURN VALUE:
+// none
+//
+# define MSG_FLAGS_ERROR (1<<0)
+# define MSG_FLAGS_SYS_CODE (1<<1)
+ void AddToMessageLog(DWORD flags, LPTSTR lpszMsg);
+ void ResetError (void);
+//////////////////////////////////////////////////////////////////////////////
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/session_id.c b/session_id.c
new file mode 100644
index 0000000..95fa5f7
--- /dev/null
+++ b/session_id.c
@@ -0,0 +1,61 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Each session is identified by a random 8-byte session identifier.
+ *
+ * For efficiency, the session id is only transmitted over the control
+ * channel (which only sees traffic occasionally when keys are being
+ * negotiated). The data channel sees a smaller version of the session-id --
+ * it is called the key_id and is currently 2 bits long.
+ */
+
+#include "syshead.h"
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#include "error.h"
+#include "common.h"
+#include "crypto.h"
+#include "session_id.h"
+
+#include "memdbg.h"
+
+const struct session_id x_session_id_zero;
+
+void
+session_id_random (struct session_id *sid)
+{
+ prng_bytes (sid->id, SID_SIZE);
+}
+
+const char *
+session_id_print (const struct session_id *sid, struct gc_arena *gc)
+{
+ return format_hex (sid->id, SID_SIZE, 0, gc);
+}
+
+#else
+static void dummy(void) {}
+#endif /* USE_CRYPTO && USE_SSL*/
diff --git a/session_id.h b/session_id.h
new file mode 100644
index 0000000..10f30ed
--- /dev/null
+++ b/session_id.h
@@ -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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Each session is identified by a random 8-byte session identifier.
+ *
+ * For efficiency, the session id is only transmitted over the control
+ * channel (which only sees traffic occasionally when keys are being
+ * negotiated).
+ */
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#ifndef SESSION_ID_H
+#define SESSION_ID_H
+
+#include "basic.h"
+#include "buffer.h"
+
+struct session_id
+{
+ uint8_t id[8];
+};
+
+extern const struct session_id x_session_id_zero;
+
+#define SID_SIZE (sizeof (x_session_id_zero.id))
+
+static inline bool
+session_id_equal (const struct session_id *sid1,
+ const struct session_id *sid2)
+{
+ return !memcmp (sid1->id, sid2->id, SID_SIZE);
+}
+
+static inline bool
+session_id_defined (const struct session_id *sid1)
+{
+ return memcmp (sid1->id, &x_session_id_zero.id, SID_SIZE) != 0;
+}
+
+static inline bool
+session_id_read (struct session_id *sid, struct buffer *buf)
+{
+ return buf_read (buf, sid->id, SID_SIZE);
+}
+
+static inline bool
+session_id_write_prepend (const struct session_id *sid, struct buffer *buf)
+{
+ return buf_write_prepend (buf, sid->id, SID_SIZE);
+}
+
+static inline bool
+session_id_write (const struct session_id *sid, struct buffer *buf)
+{
+ return buf_write (buf, sid->id, SID_SIZE);
+}
+
+void session_id_random (struct session_id *sid);
+
+const char *session_id_print (const struct session_id *sid, struct gc_arena *gc);
+
+#endif /* SESSION_ID_H */
+#endif /* USE_CRYPTO && USE_SSL */
diff --git a/shaper.c b/shaper.c
new file mode 100644
index 0000000..1a89fc2
--- /dev/null
+++ b/shaper.c
@@ -0,0 +1,95 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+#include "shaper.h"
+#include "memdbg.h"
+
+#ifdef HAVE_GETTIMEOFDAY
+
+/*
+ * We want to wake up in delay microseconds. If timeval is larger
+ * than delay, set timeval to delay.
+ */
+bool
+shaper_soonest_event (struct timeval *tv, int delay)
+{
+ bool ret = false;
+ if (delay < 1000000)
+ {
+ if (tv->tv_sec)
+ {
+ tv->tv_sec = 0;
+ tv->tv_usec = delay;
+ ret = true;
+ }
+ else if (delay < tv->tv_usec)
+ {
+ tv->tv_usec = delay;
+ ret = true;
+ }
+ }
+ else
+ {
+ const int sec = delay / 1000000;
+ const int usec = delay % 1000000;
+
+ if (sec < tv->tv_sec)
+ {
+ tv->tv_sec = sec;
+ tv->tv_usec = usec;
+ ret = true;
+ }
+ else if (sec == tv->tv_sec)
+ {
+ if (usec < tv->tv_usec)
+ {
+ tv->tv_usec = usec;
+ ret = true;
+ }
+ }
+ }
+#ifdef SHAPER_DEBUG
+ dmsg (D_SHAPER_DEBUG, "SHAPER shaper_soonest_event sec=%d usec=%d ret=%d",
+ (int)tv->tv_sec, (int)tv->tv_usec, (int)ret);
+#endif
+ return ret;
+}
+
+void
+shaper_reset_wakeup (struct shaper *s)
+{
+ CLEAR (s->wakeup);
+}
+
+void
+shaper_msg (struct shaper *s)
+{
+ msg (M_INFO, "Output Traffic Shaping initialized at %d bytes per second",
+ s->bytes_per_second);
+}
+
+#else
+static void dummy(void) {}
+#endif /* HAVE_GETTIMEOFDAY */
diff --git a/shaper.h b/shaper.h
new file mode 100644
index 0000000..4825011
--- /dev/null
+++ b/shaper.h
@@ -0,0 +1,178 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SHAPER_H
+#define SHAPER_H
+
+/*#define SHAPER_DEBUG*/
+
+#ifdef HAVE_GETTIMEOFDAY
+
+#include "basic.h"
+#include "integer.h"
+#include "misc.h"
+#include "error.h"
+#include "interval.h"
+
+/*
+ * A simple traffic shaper for
+ * the output direction.
+ */
+
+#define SHAPER_MIN 100 /* bytes per second */
+#define SHAPER_MAX 100000000
+
+#define SHAPER_MAX_TIMEOUT 10 /* seconds */
+
+#define SHAPER_USE_FP
+
+struct shaper
+{
+ int bytes_per_second;
+ struct timeval wakeup;
+
+#ifdef SHAPER_USE_FP
+ double factor;
+#else
+ int factor;
+#endif
+};
+
+void shaper_msg (struct shaper *s);
+void shaper_reset_wakeup (struct shaper *s);
+
+/*
+ * We want to wake up in delay microseconds. If timeval is larger
+ * than delay, set timeval to delay.
+ */
+bool shaper_soonest_event (struct timeval *tv, int delay);
+
+/*
+ * inline functions
+ */
+
+static inline void
+shaper_reset (struct shaper *s, int bytes_per_second)
+{
+ s->bytes_per_second = bytes_per_second ? constrain_int (bytes_per_second, SHAPER_MIN, SHAPER_MAX) : 0;
+
+#ifdef SHAPER_USE_FP
+ s->factor = 1000000.0 / (double)s->bytes_per_second;
+#else
+ s->factor = 1000000 / s->bytes_per_second;
+#endif
+}
+
+static inline void
+shaper_init (struct shaper *s, int bytes_per_second)
+{
+ shaper_reset (s, bytes_per_second);
+ shaper_reset_wakeup (s);
+}
+
+static inline int
+shaper_current_bandwidth (struct shaper *s)
+{
+ return s->bytes_per_second;
+}
+
+/*
+ * Returns traffic shaping delay in microseconds relative to current
+ * time, or 0 if no delay.
+ */
+static inline int
+shaper_delay (struct shaper* s)
+{
+ struct timeval tv;
+ int delay = 0;
+
+ if (tv_defined (&s->wakeup))
+ {
+ ASSERT (!openvpn_gettimeofday (&tv, NULL));
+ delay = tv_subtract (&s->wakeup, &tv, SHAPER_MAX_TIMEOUT);
+#ifdef SHAPER_DEBUG
+ dmsg (D_SHAPER_DEBUG, "SHAPER shaper_delay delay=%d", delay);
+#endif
+ }
+
+ return delay > 0 ? delay : 0;
+}
+
+
+/*
+ * We are about to send a datagram of nbytes bytes.
+ *
+ * Compute when we can send another datagram,
+ * based on target throughput (s->bytes_per_second).
+ */
+static inline void
+shaper_wrote_bytes (struct shaper* s, int nbytes)
+{
+ struct timeval tv;
+
+ /* compute delay in microseconds */
+ tv.tv_sec = 0;
+#ifdef SHAPER_USE_FP
+ tv.tv_usec = min_int ((int)((double)max_int (nbytes, 100) * s->factor), (SHAPER_MAX_TIMEOUT*1000000));
+#else
+ tv.tv_usec = s->bytes_per_second
+ ? min_int (max_int (nbytes, 100) * s->factor, (SHAPER_MAX_TIMEOUT*1000000))
+ : 0;
+#endif
+
+ if (tv.tv_usec)
+ {
+ ASSERT (!openvpn_gettimeofday (&s->wakeup, NULL));
+ tv_add (&s->wakeup, &tv);
+
+#ifdef SHAPER_DEBUG
+ dmsg (D_SHAPER_DEBUG, "SHAPER shaper_wrote_bytes bytes=%d delay=%d sec=%d usec=%d",
+ nbytes,
+ (int)tv.tv_usec,
+ (int)s->wakeup.tv_sec,
+ (int)s->wakeup.tv_usec);
+#endif
+ }
+}
+
+#if 0
+/*
+ * Increase/Decrease bandwidth by a percentage.
+ *
+ * Return true if bandwidth changed.
+ */
+static inline bool
+shaper_change_pct (struct shaper *s, int pct)
+{
+ const int orig_bandwidth = s->bytes_per_second;
+ const int new_bandwidth = orig_bandwidth + (orig_bandwidth * pct / 100);
+ ASSERT (s->bytes_per_second);
+ shaper_reset (s, new_bandwidth);
+ return s->bytes_per_second != orig_bandwidth;
+}
+#endif
+
+#endif /* HAVE_GETTIMEOFDAY */
+
+#endif
diff --git a/sig.c b/sig.c
new file mode 100644
index 0000000..631a3a8
--- /dev/null
+++ b/sig.c
@@ -0,0 +1,368 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "buffer.h"
+#include "error.h"
+#include "win32.h"
+#include "init.h"
+#include "status.h"
+#include "sig.h"
+#include "occ.h"
+#include "manage.h"
+#include "openvpn.h"
+
+#include "memdbg.h"
+
+/* Handle signals */
+
+struct signal_info siginfo_static; /* GLOBAL */
+
+struct signame {
+ int value;
+ const char *upper;
+ const char *lower;
+};
+
+static const struct signame signames[] = {
+ { SIGINT, "SIGINT", "sigint"},
+ { SIGTERM, "SIGTERM", "sigterm" },
+ { SIGHUP, "SIGHUP", "sighup" },
+ { SIGUSR1, "SIGUSR1", "sigusr1" },
+ { SIGUSR2, "SIGUSR2", "sigusr2" }
+};
+
+int
+parse_signal (const char *signame)
+{
+ int i;
+ for (i = 0; i < (int)SIZE (signames); ++i)
+ {
+ if (!strcmp (signame, signames[i].upper))
+ return signames[i].value;
+ }
+ return -1;
+}
+
+const char *
+signal_name (const int sig, const bool upper)
+{
+ int i;
+ for (i = 0; i < (int)SIZE (signames); ++i)
+ {
+ if (sig == signames[i].value)
+ return upper ? signames[i].upper : signames[i].lower;
+ }
+ return "UNKNOWN";
+}
+
+const char *
+signal_description (const int signum, const char *sigtext)
+{
+ if (sigtext)
+ return sigtext;
+ else
+ return signal_name (signum, false);
+}
+
+void
+throw_signal (const int signum)
+{
+ siginfo_static.signal_received = signum;
+ siginfo_static.hard = true;
+}
+
+void
+throw_signal_soft (const int signum, const char *signal_text)
+{
+ siginfo_static.signal_received = signum;
+ siginfo_static.hard = false;
+ siginfo_static.signal_text = signal_text;
+}
+
+static void
+signal_reset (struct signal_info *si)
+{
+ if (si)
+ {
+ si->signal_received = 0;
+ si->signal_text = NULL;
+ si->hard = false;
+ }
+}
+
+void
+print_signal (const struct signal_info *si, const char *title, int msglevel)
+{
+ if (si)
+ {
+ const char *hs = (si->hard ? "hard" : "soft");
+ const char *type = (si->signal_text ? si->signal_text : "");
+ const char *t = (title ? title : "process");
+
+ switch (si->signal_received)
+ {
+ case SIGINT:
+ case SIGTERM:
+ msg (msglevel, "%s[%s,%s] received, %s exiting",
+ signal_name (si->signal_received, true), hs, type, t);
+ break;
+ case SIGHUP:
+ case SIGUSR1:
+ msg (msglevel, "%s[%s,%s] received, %s restarting",
+ signal_name (si->signal_received, true), hs, type, t);
+ break;
+ default:
+ msg (msglevel, "Unknown signal %d [%s,%s] received by %s", si->signal_received, hs, type, t);
+ break;
+ }
+ }
+ else
+ msg (msglevel, "Unknown signal received");
+}
+
+/*
+ * Call management interface with restart info
+ */
+void
+signal_restart_status (const struct signal_info *si)
+{
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ int state = -1;
+ switch (si->signal_received)
+ {
+ case SIGINT:
+ case SIGTERM:
+ state = OPENVPN_STATE_EXITING;
+ break;
+ case SIGHUP:
+ case SIGUSR1:
+ state = OPENVPN_STATE_RECONNECTING;
+ break;
+ }
+
+ if (state >= 0)
+ management_set_state (management,
+ state,
+ si->signal_text ? si->signal_text : signal_name (si->signal_received, true),
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+#endif
+}
+
+#ifdef HAVE_SIGNAL_H
+
+/* normal signal handler, when we are in event loop */
+static void
+signal_handler (const int signum)
+{
+ throw_signal (signum);
+ signal (signum, signal_handler);
+}
+
+#endif
+
+/* set handlers for unix signals */
+
+#ifdef HAVE_SIGNAL_H
+#define SM_UNDEF 0
+#define SM_PRE_INIT 1
+#define SM_POST_INIT 2
+static int signal_mode; /* GLOBAL */
+#endif
+
+void
+pre_init_signal_catch (void)
+{
+#ifdef HAVE_SIGNAL_H
+ signal_mode = SM_PRE_INIT;
+ signal (SIGINT, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGUSR1, SIG_IGN);
+ signal (SIGUSR2, SIG_IGN);
+ signal (SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGNAL_H */
+}
+
+void
+post_init_signal_catch (void)
+{
+#ifdef HAVE_SIGNAL_H
+ signal_mode = SM_POST_INIT;
+ signal (SIGINT, signal_handler);
+ signal (SIGTERM, signal_handler);
+ signal (SIGHUP, signal_handler);
+ signal (SIGUSR1, signal_handler);
+ signal (SIGUSR2, signal_handler);
+ signal (SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGNAL_H */
+}
+
+/* called after daemonization to retain signal settings */
+void
+restore_signal_state (void)
+{
+#ifdef HAVE_SIGNAL_H
+ if (signal_mode == SM_PRE_INIT)
+ pre_init_signal_catch ();
+ else if (signal_mode == SM_POST_INIT)
+ post_init_signal_catch ();
+#endif
+}
+
+/*
+ * Print statistics.
+ *
+ * Triggered by SIGUSR2 or F2 on Windows.
+ */
+void
+print_status (const struct context *c, struct status_output *so)
+{
+ struct gc_arena gc = gc_new ();
+
+ status_reset (so);
+
+ status_printf (so, "OpenVPN STATISTICS");
+ status_printf (so, "Updated,%s", time_string (0, 0, false, &gc));
+ status_printf (so, "TUN/TAP read bytes," counter_format, c->c2.tun_read_bytes);
+ status_printf (so, "TUN/TAP write bytes," counter_format, c->c2.tun_write_bytes);
+ status_printf (so, "TCP/UDP read bytes," counter_format, c->c2.link_read_bytes);
+ status_printf (so, "TCP/UDP write bytes," counter_format, c->c2.link_write_bytes);
+ status_printf (so, "Auth read bytes," counter_format, c->c2.link_read_bytes_auth);
+#ifdef USE_LZO
+ if (lzo_defined (&c->c2.lzo_compwork))
+ lzo_print_stats (&c->c2.lzo_compwork, so);
+#endif
+#ifdef PACKET_TRUNCATION_CHECK
+ status_printf (so, "TUN read truncations," counter_format, c->c2.n_trunc_tun_read);
+ status_printf (so, "TUN write truncations," counter_format, c->c2.n_trunc_tun_write);
+ status_printf (so, "Pre-encrypt truncations," counter_format, c->c2.n_trunc_pre_encrypt);
+ status_printf (so, "Post-decrypt truncations," counter_format, c->c2.n_trunc_post_decrypt);
+#endif
+#ifdef WIN32
+ if (tuntap_defined (c->c1.tuntap))
+ status_printf (so, "TAP-WIN32 driver status,\"%s\"",
+ tap_win32_getinfo (c->c1.tuntap, &gc));
+#endif
+
+ status_printf (so, "END");
+ status_flush (so);
+ gc_free (&gc);
+}
+
+#ifdef ENABLE_OCC
+/*
+ * Handle the triggering and time-wait of explicit
+ * exit notification.
+ */
+
+static void
+process_explicit_exit_notification_init (struct context *c)
+{
+ msg (M_INFO, "SIGTERM received, sending exit notification to peer");
+ event_timeout_init (&c->c2.explicit_exit_notification_interval, 1, 0);
+ reset_coarse_timers (c);
+ signal_reset (c->sig);
+ halt_non_edge_triggered_signals ();
+ c->c2.explicit_exit_notification_time_wait = now;
+}
+
+void
+process_explicit_exit_notification_timer_wakeup (struct context *c)
+{
+ if (event_timeout_trigger (&c->c2.explicit_exit_notification_interval,
+ &c->c2.timeval,
+ ETT_DEFAULT))
+ {
+ ASSERT (c->c2.explicit_exit_notification_time_wait && c->options.explicit_exit_notification);
+ if (now >= c->c2.explicit_exit_notification_time_wait + c->options.explicit_exit_notification)
+ {
+ event_timeout_clear (&c->c2.explicit_exit_notification_interval);
+ c->sig->signal_received = SIGTERM;
+ c->sig->signal_text = "exit-with-notification";
+ }
+ else
+ {
+ c->c2.occ_op = OCC_EXIT;
+ }
+ }
+}
+#endif
+
+/*
+ * Process signals
+ */
+
+void
+remap_signal (struct context *c)
+{
+ if (c->sig->signal_received == SIGUSR1 && c->options.remap_sigusr1)
+ c->sig->signal_received = c->options.remap_sigusr1;
+}
+
+static void
+process_sigusr2 (const struct context *c)
+{
+ struct status_output *so = status_open (NULL, 0, M_INFO, NULL, 0);
+ print_status (c, so);
+ status_close (so);
+ signal_reset (c->sig);
+}
+
+static bool
+process_sigterm (struct context *c)
+{
+ bool ret = true;
+#ifdef ENABLE_OCC
+ if (c->options.explicit_exit_notification
+ && !c->c2.explicit_exit_notification_time_wait)
+ {
+ process_explicit_exit_notification_init (c);
+ ret = false;
+ }
+#endif
+ return ret;
+}
+
+bool
+process_signal (struct context *c)
+{
+ bool ret = true;
+
+ if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT)
+ {
+ ret = process_sigterm (c);
+ }
+ else if (c->sig->signal_received == SIGUSR2)
+ {
+ process_sigusr2 (c);
+ ret = false;
+ }
+ return ret;
+}
diff --git a/sig.h b/sig.h
new file mode 100644
index 0000000..be87fe4
--- /dev/null
+++ b/sig.h
@@ -0,0 +1,102 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SIG_H
+#define SIG_H
+
+#include "status.h"
+#include "win32.h"
+
+/*
+ * Signal information, including signal code
+ * and descriptive text.
+ */
+struct signal_info
+{
+ volatile int signal_received;
+ volatile bool hard;
+ const char *signal_text;
+};
+
+#define IS_SIG(c) ((c)->sig->signal_received)
+
+struct context;
+
+extern struct signal_info siginfo_static;
+
+int parse_signal (const char *signame);
+const char *signal_name (const int sig, const bool upper);
+const char *signal_description (const int signum, const char *sigtext);
+void throw_signal (const int signum);
+void throw_signal_soft (const int signum, const char *signal_text);
+
+void pre_init_signal_catch (void);
+void post_init_signal_catch (void);
+void restore_signal_state (void);
+
+void print_signal (const struct signal_info *si, const char *title, int msglevel);
+void print_status (const struct context *c, struct status_output *so);
+
+void remap_signal (struct context *c);
+
+void signal_restart_status (const struct signal_info *si);
+
+bool process_signal (struct context *c);
+
+#ifdef ENABLE_OCC
+void process_explicit_exit_notification_timer_wakeup (struct context *c);
+#endif
+
+#ifdef WIN32
+
+static inline void
+get_signal (volatile int *sig)
+{
+ *sig = win32_signal_get (&win32_signal);
+}
+
+static inline void
+halt_non_edge_triggered_signals (void)
+{
+ win32_signal_close (&win32_signal);
+}
+
+#else
+
+static inline void
+get_signal (volatile int *sig)
+{
+ const int i = siginfo_static.signal_received;
+ if (i)
+ *sig = i;
+}
+
+static inline void
+halt_non_edge_triggered_signals (void)
+{
+}
+
+#endif
+
+#endif
diff --git a/socket.c b/socket.c
new file mode 100644
index 0000000..4720398
--- /dev/null
+++ b/socket.c
@@ -0,0 +1,2808 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "socket.h"
+#include "fdmisc.h"
+#include "misc.h"
+#include "gremlin.h"
+#include "plugin.h"
+#include "ps.h"
+#include "manage.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+const int proto_overhead[] = { /* indexed by PROTO_x */
+ IPv4_UDP_HEADER_SIZE,
+ IPv4_TCP_HEADER_SIZE,
+ IPv4_TCP_HEADER_SIZE,
+ IPv4_TCP_HEADER_SIZE
+};
+
+/*
+ * Convert sockflags/getaddr_flags into getaddr_flags
+ */
+static unsigned int
+sf2gaf(const unsigned int getaddr_flags,
+ const unsigned int sockflags)
+{
+ if (sockflags & SF_HOST_RANDOMIZE)
+ return getaddr_flags | GETADDR_RANDOMIZE;
+ else
+ return getaddr_flags;
+}
+
+/*
+ * Functions related to the translation of DNS names to IP addresses.
+ */
+
+static const char*
+h_errno_msg(int h_errno_err)
+{
+ switch (h_errno_err)
+ {
+ case HOST_NOT_FOUND:
+ return "[HOST_NOT_FOUND] The specified host is unknown.";
+ case NO_DATA:
+ return "[NO_DATA] The requested name is valid but does not have an IP address.";
+ case NO_RECOVERY:
+ return "[NO_RECOVERY] A non-recoverable name server error occurred.";
+ case TRY_AGAIN:
+ return "[TRY_AGAIN] A temporary error occurred on an authoritative name server.";
+ }
+ return "[unknown h_errno value]";
+}
+
+/*
+ * Translate IP addr or hostname to in_addr_t.
+ * If resolve error, try again for
+ * resolve_retry_seconds seconds.
+ */
+in_addr_t
+getaddr (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ bool *succeeded,
+ volatile int *signal_received)
+{
+ return getaddr_multi (flags, hostname, resolve_retry_seconds, succeeded, signal_received, NULL);
+}
+
+in_addr_t
+getaddr_multi (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ bool *succeeded,
+ volatile int *signal_received,
+ struct resolve_list *reslist)
+{
+ struct in_addr ia;
+ int status;
+ int sigrec = 0;
+ int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
+ struct gc_arena gc = gc_new ();
+
+ if (reslist)
+ reslist->len = 0;
+
+ if (flags & GETADDR_RANDOMIZE)
+ hostname = hostname_randomize(hostname, &gc);
+
+ if (flags & GETADDR_MSG_VIRT_OUT)
+ msglevel |= M_MSG_VIRT_OUT;
+
+ CLEAR (ia);
+ if (succeeded)
+ *succeeded = false;
+
+ if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
+ && !signal_received)
+ signal_received = &sigrec;
+
+ status = openvpn_inet_aton (hostname, &ia); /* parse ascii IP address */
+
+ if (status != OIA_IP) /* parse as IP address failed? */
+ {
+ const int fail_wait_interval = 5; /* seconds */
+ int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 : (resolve_retry_seconds / fail_wait_interval);
+ struct hostent *h;
+ const char *fmt;
+ int level = 0;
+
+ CLEAR (ia);
+
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s";
+ if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
+ && !resolve_retry_seconds)
+ fmt = "RESOLVE: Cannot resolve host address: %s: %s (I would have retried this name query if you had specified the --resolv-retry option.)";
+
+ if (!(flags & GETADDR_RESOLVE) || status == OIA_ERROR)
+ {
+ msg (msglevel, "RESOLVE: Cannot parse IP address: %s", hostname);
+ goto done;
+ }
+
+#ifdef ENABLE_MANAGEMENT
+ if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
+ {
+ if (management)
+ management_set_state (management,
+ OPENVPN_STATE_RESOLVE,
+ NULL,
+ (in_addr_t)0,
+ (in_addr_t)0);
+ }
+#endif
+
+ /*
+ * Resolve hostname
+ */
+ while (true)
+ {
+ /* try hostname lookup */
+#if defined(HAVE_RES_INIT)
+ res_init ();
+#endif
+ h = gethostbyname (hostname);
+
+ if (signal_received)
+ {
+ get_signal (signal_received);
+ if (*signal_received) /* were we interrupted by a signal? */
+ {
+ h = NULL;
+ if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
+ {
+ msg (level, "RESOLVE: Ignored SIGUSR1 signal received during DNS resolution attempt");
+ *signal_received = 0;
+ }
+ else
+ goto done;
+ }
+ }
+
+ /* success? */
+ if (h)
+ break;
+
+ /* resolve lookup failed, should we
+ continue or fail? */
+
+ level = msglevel;
+ if (resolve_retries > 0)
+ level = D_RESOLVE_ERRORS;
+
+ msg (level,
+ fmt,
+ hostname,
+ h_errno_msg (h_errno));
+
+ if (--resolve_retries <= 0)
+ goto done;
+
+ openvpn_sleep (fail_wait_interval);
+ }
+
+ if (h->h_addrtype != AF_INET || h->h_length != 4)
+ {
+ msg (msglevel, "RESOLVE: Sorry, but we only accept IPv4 DNS names: %s", hostname);
+ goto done;
+ }
+
+ ia.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+
+ if (ia.s_addr)
+ {
+ if (h->h_addr_list[1]) /* more than one address returned */
+ {
+ int n = 0;
+
+ /* count address list */
+ while (h->h_addr_list[n])
+ ++n;
+ ASSERT (n >= 2);
+
+ msg (D_RESOLVE_ERRORS, "RESOLVE: NOTE: %s resolves to %d addresses",
+ hostname,
+ n);
+
+ /* choose address randomly, for basic load-balancing capability */
+ /*ia.s_addr = *(in_addr_t *) (h->h_addr_list[get_random () % n]);*/
+
+ /* choose first address */
+ ia.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+
+ if (reslist)
+ {
+ int i;
+ for (i = 0; i < n && i < SIZE(reslist->data); ++i)
+ {
+ in_addr_t a = *(in_addr_t *) (h->h_addr_list[i]);
+ if (flags & GETADDR_HOST_ORDER)
+ a = ntohl(a);
+ reslist->data[i] = a;
+ }
+ reslist->len = i;
+ }
+ }
+ }
+
+ /* hostname resolve succeeded */
+ if (succeeded)
+ *succeeded = true;
+ }
+ else
+ {
+ /* IP address parse succeeded */
+ if (succeeded)
+ *succeeded = true;
+ }
+
+ done:
+ if (signal_received && *signal_received)
+ {
+ int level = 0;
+ if (flags & GETADDR_FATAL_ON_SIGNAL)
+ level = M_FATAL;
+ else if (flags & GETADDR_WARN_ON_SIGNAL)
+ level = M_WARN;
+ msg (level, "RESOLVE: signal received during DNS resolution attempt");
+ }
+
+ gc_free (&gc);
+ return (flags & GETADDR_HOST_ORDER) ? ntohl (ia.s_addr) : ia.s_addr;
+}
+
+/*
+ * We do our own inet_aton because the glibc function
+ * isn't very good about error checking.
+ */
+int
+openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr)
+{
+ unsigned int a, b, c, d;
+
+ CLEAR (*addr);
+ if (sscanf (dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) == 4)
+ {
+ if (a < 256 && b < 256 && c < 256 && d < 256)
+ {
+ addr->s_addr = htonl (a<<24 | b<<16 | c<<8 | d);
+ return OIA_IP; /* good dotted quad */
+ }
+ }
+ if (string_class (dotted_quad, CC_DIGIT|CC_DOT, 0))
+ return OIA_ERROR; /* probably a badly formatted dotted quad */
+ else
+ return OIA_HOSTNAME; /* probably a hostname */
+}
+
+bool
+ip_addr_dotted_quad_safe (const char *dotted_quad)
+{
+ /* verify non-NULL */
+ if (!dotted_quad)
+ return false;
+
+ /* verify length is within limits */
+ if (strlen (dotted_quad) > 15)
+ return false;
+
+ /* verify that all chars are either numeric or '.' and that no numeric
+ substring is greater than 3 chars */
+ {
+ int nnum = 0;
+ const char *p = dotted_quad;
+ int c;
+
+ while ((c = *p++))
+ {
+ if (c >= '0' && c <= '9')
+ {
+ ++nnum;
+ if (nnum > 3)
+ return false;
+ }
+ else if (c == '.')
+ {
+ nnum = 0;
+ }
+ else
+ return false;
+ }
+ }
+
+ /* verify that string will convert to IP address */
+ {
+ struct in_addr a;
+ return openvpn_inet_aton (dotted_quad, &a) == OIA_IP;
+ }
+}
+
+static bool
+dns_addr_safe (const char *addr)
+{
+ if (addr)
+ {
+ const size_t len = strlen (addr);
+ return len > 0 && len <= 255 && string_class (addr, CC_ALNUM|CC_DASH|CC_DOT, 0);
+ }
+ else
+ return false;
+}
+
+bool
+ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn)
+{
+ if (ip_addr_dotted_quad_safe (addr))
+ return true;
+ else if (allow_fqdn)
+ return dns_addr_safe (addr);
+ else
+ return false;
+}
+
+bool
+mac_addr_safe (const char *mac_addr)
+{
+ /* verify non-NULL */
+ if (!mac_addr)
+ return false;
+
+ /* verify length is within limits */
+ if (strlen (mac_addr) > 17)
+ return false;
+
+ /* verify that all chars are either alphanumeric or ':' and that no
+ alphanumeric substring is greater than 2 chars */
+ {
+ int nnum = 0;
+ const char *p = mac_addr;
+ int c;
+
+ while ((c = *p++))
+ {
+ if ( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )
+ {
+ ++nnum;
+ if (nnum > 2)
+ return false;
+ }
+ else if (c == ':')
+ {
+ nnum = 0;
+ }
+ else
+ return false;
+ }
+ }
+
+ /* error-checking is left to script invoked in lladdr.c */
+ return true;
+}
+
+static void
+update_remote (const char* host,
+ struct openvpn_sockaddr *addr,
+ bool *changed,
+ const unsigned int sockflags)
+{
+ if (host && addr)
+ {
+ const in_addr_t new_addr = getaddr (
+ sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sockflags),
+ host,
+ 1,
+ NULL,
+ NULL);
+ if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
+ {
+ addr->sa.sin_addr.s_addr = new_addr;
+ *changed = true;
+ }
+ }
+}
+
+static int
+socket_get_sndbuf (int sd)
+{
+#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
+ int val;
+ socklen_t len;
+
+ len = sizeof (val);
+ if (getsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &val, &len) == 0
+ && len == sizeof (val))
+ return val;
+#endif
+ return 0;
+}
+
+static void
+socket_set_sndbuf (int sd, int size)
+{
+#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_SNDBUF)
+ if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX)
+ {
+ if (setsockopt (sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof (size)) != 0)
+ {
+ msg (M_WARN, "NOTE: setsockopt SO_SNDBUF=%d failed", size);
+ }
+ }
+#endif
+}
+
+static int
+socket_get_rcvbuf (int sd)
+{
+#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
+ int val;
+ socklen_t len;
+
+ len = sizeof (val);
+ if (getsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &val, &len) == 0
+ && len == sizeof (val))
+ return val;
+#endif
+ return 0;
+}
+
+static bool
+socket_set_rcvbuf (int sd, int size)
+{
+#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_RCVBUF)
+ if (size > 0 && size < SOCKET_SND_RCV_BUF_MAX)
+ {
+ if (setsockopt (sd, SOL_SOCKET, SO_RCVBUF, (void *) &size, sizeof (size)) != 0)
+ {
+ msg (M_WARN, "NOTE: setsockopt SO_RCVBUF=%d failed", size);
+ return false;
+ }
+ }
+ return true;
+#endif
+}
+
+static void
+socket_set_buffers (int fd, const struct socket_buffer_size *sbs)
+{
+ if (sbs)
+ {
+ const int sndbuf_old = socket_get_sndbuf (fd);
+ const int rcvbuf_old = socket_get_rcvbuf (fd);
+
+ if (sbs->sndbuf)
+ socket_set_sndbuf (fd, sbs->sndbuf);
+
+ if (sbs->rcvbuf)
+ socket_set_rcvbuf (fd, sbs->rcvbuf);
+
+ msg (D_OSBUF, "Socket Buffers: R=[%d->%d] S=[%d->%d]",
+ rcvbuf_old,
+ socket_get_rcvbuf (fd),
+ sndbuf_old,
+ socket_get_sndbuf (fd));
+ }
+}
+
+/*
+ * Set other socket options
+ */
+
+static bool
+socket_set_tcp_nodelay (int sd, int state)
+{
+#if defined(WIN32) || (defined(HAVE_SETSOCKOPT) && defined(IPPROTO_TCP) && defined(TCP_NODELAY))
+ if (setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (void *) &state, sizeof (state)) != 0)
+ {
+ msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed", state);
+ return false;
+ }
+ else
+ {
+ dmsg (D_OSBUF, "Socket flags: TCP_NODELAY=%d succeeded", state);
+ return true;
+ }
+#else
+ msg (M_WARN, "NOTE: setsockopt TCP_NODELAY=%d failed (No kernel support)", state);
+ return false;
+#endif
+}
+
+static bool
+socket_set_flags (int sd, unsigned int sockflags)
+{
+ if (sockflags & SF_TCP_NODELAY)
+ return socket_set_tcp_nodelay (sd, 1);
+ else
+ return true;
+}
+
+bool
+link_socket_update_flags (struct link_socket *ls, unsigned int sockflags)
+{
+ if (ls && socket_defined (ls->sd))
+ return socket_set_flags (ls->sd, ls->sockflags = sockflags);
+ else
+ return false;
+}
+
+void
+link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf)
+{
+ if (ls && socket_defined (ls->sd))
+ {
+ ls->socket_buffer_sizes.sndbuf = sndbuf;
+ ls->socket_buffer_sizes.rcvbuf = rcvbuf;
+ socket_set_buffers (ls->sd, &ls->socket_buffer_sizes);
+ }
+}
+
+/*
+ * SOCKET INITALIZATION CODE.
+ * Create a TCP/UDP socket
+ */
+
+socket_descriptor_t
+create_socket_tcp (void)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+ msg (M_SOCKERR, "Cannot create TCP socket");
+
+#ifndef WIN32 /* using SO_REUSEADDR on Windows will cause bind to succeed on port conflicts! */
+ /* set SO_REUSEADDR on socket */
+ {
+ int on = 1;
+ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR,
+ (void *) &on, sizeof (on)) < 0)
+ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP socket");
+ }
+#endif
+
+#if 0
+ /* set socket linger options */
+ {
+ struct linger linger;
+ linger.l_onoff = 1;
+ linger.l_linger = 2;
+ if (setsockopt (sd, SOL_SOCKET, SO_LINGER,
+ (void *) &linger, sizeof (linger)) < 0)
+ msg (M_SOCKERR, "TCP: Cannot setsockopt SO_LINGER on TCP socket");
+ }
+#endif
+
+ return sd;
+}
+
+static socket_descriptor_t
+create_socket_udp (const unsigned int flags)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ msg (M_SOCKERR, "UDP: Cannot create UDP socket");
+#if ENABLE_IP_PKTINFO
+ else if (flags & SF_USE_IP_PKTINFO)
+ {
+ int pad = 1;
+ setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
+ }
+#endif
+ return sd;
+}
+
+static void
+create_socket (struct link_socket *sock)
+{
+ /* create socket */
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ sock->sd = create_socket_udp (sock->sockflags);
+
+#ifdef ENABLE_SOCKS
+ if (sock->socks_proxy)
+ sock->ctrl_sd = create_socket_tcp ();
+#endif
+ }
+ else if (sock->info.proto == PROTO_TCPv4_SERVER
+ || sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+ sock->sd = create_socket_tcp ();
+ }
+ else
+ {
+ ASSERT (0);
+ }
+}
+
+/*
+ * Functions used for establishing a TCP stream connection.
+ */
+
+static void
+socket_do_listen (socket_descriptor_t sd,
+ const struct openvpn_sockaddr *local,
+ bool do_listen,
+ bool do_set_nonblock)
+{
+ struct gc_arena gc = gc_new ();
+ if (do_listen)
+ {
+ msg (M_INFO, "Listening for incoming TCP connection on %s",
+ print_sockaddr (local, &gc));
+ if (listen (sd, 1))
+ msg (M_SOCKERR, "TCP: listen() failed");
+ }
+
+ /* set socket to non-blocking mode */
+ if (do_set_nonblock)
+ set_nonblock (sd);
+
+ gc_free (&gc);
+}
+
+socket_descriptor_t
+socket_do_accept (socket_descriptor_t sd,
+ struct link_socket_actual *act,
+ const bool nowait)
+{
+ socklen_t remote_len = sizeof (act->dest.sa);
+ socket_descriptor_t new_sd = SOCKET_UNDEFINED;
+
+ CLEAR (*act);
+
+#ifdef HAVE_GETPEERNAME
+ if (nowait)
+ {
+ new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+
+ if (!socket_defined (new_sd))
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed");
+ else
+ new_sd = sd;
+ }
+#else
+ if (nowait)
+ msg (M_WARN, "TCP: this OS does not provide the getpeername() function");
+#endif
+ else
+ {
+ new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ }
+
+#if 0 /* For debugging only, test the effect of accept() failures */
+ {
+ static int foo = 0;
+ ++foo;
+ if (foo & 1)
+ new_sd = -1;
+ }
+#endif
+
+ if (!socket_defined (new_sd))
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd);
+ }
+ else if (remote_len != sizeof (act->dest.sa))
+ {
+ msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
+ openvpn_close_socket (new_sd);
+ new_sd = SOCKET_UNDEFINED;
+ }
+ return new_sd;
+}
+
+static void
+tcp_connection_established (const struct link_socket_actual *act)
+{
+ struct gc_arena gc = gc_new ();
+ msg (M_INFO, "TCP connection established with %s",
+ print_link_socket_actual (act, &gc));
+ gc_free (&gc);
+}
+
+static int
+socket_listen_accept (socket_descriptor_t sd,
+ struct link_socket_actual *act,
+ const char *remote_dynamic,
+ bool *remote_changed,
+ const struct openvpn_sockaddr *local,
+ bool do_listen,
+ bool nowait,
+ volatile int *signal_received)
+{
+ struct gc_arena gc = gc_new ();
+ /* struct openvpn_sockaddr *remote = &act->dest; */
+ struct openvpn_sockaddr remote_verify = act->dest;
+ int new_sd = SOCKET_UNDEFINED;
+
+ CLEAR (*act);
+ socket_do_listen (sd, local, do_listen, true);
+
+ while (true)
+ {
+ int status;
+ fd_set reads;
+ struct timeval tv;
+
+ FD_ZERO (&reads);
+ FD_SET (sd, &reads);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, &reads, NULL, NULL, &tv);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ {
+ gc_free (&gc);
+ return sd;
+ }
+
+ if (status < 0)
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: select() failed");
+
+ if (status <= 0)
+ {
+ openvpn_sleep (1);
+ continue;
+ }
+
+ new_sd = socket_do_accept (sd, act, nowait);
+
+ if (socket_defined (new_sd))
+ {
+ update_remote (remote_dynamic, &remote_verify, remote_changed, 0);
+ if (addr_defined (&remote_verify)
+ && !addr_match (&remote_verify, &act->dest))
+ {
+ msg (M_WARN,
+ "TCP NOTE: Rejected connection attempt from %s due to --remote setting",
+ print_link_socket_actual (act, &gc));
+ if (openvpn_close_socket (new_sd))
+ msg (M_SOCKERR, "TCP: close socket failed (new_sd)");
+ }
+ else
+ break;
+ }
+ openvpn_sleep (1);
+ }
+
+ if (!nowait && openvpn_close_socket (sd))
+ msg (M_SOCKERR, "TCP: close socket failed (sd)");
+
+ tcp_connection_established (act);
+
+ gc_free (&gc);
+ return new_sd;
+}
+
+void
+socket_bind (socket_descriptor_t sd,
+ struct openvpn_sockaddr *local,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (bind (sd, (struct sockaddr *) &local->sa, sizeof (local->sa)))
+ {
+ const int errnum = openvpn_errno_socket ();
+ msg (M_FATAL, "%s: Socket bind failed on local address %s: %s",
+ prefix,
+ print_sockaddr (local, &gc),
+ strerror_ts (errnum, &gc));
+ }
+ gc_free (&gc);
+}
+
+int
+openvpn_connect (socket_descriptor_t sd,
+ struct openvpn_sockaddr *remote,
+ int connect_timeout,
+ volatile int *signal_received)
+{
+ int status = 0;
+
+#ifdef CONNECT_NONBLOCK
+ set_nonblock (sd);
+ status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ if (status)
+ status = openvpn_errno_socket ();
+ if (status == EINPROGRESS)
+ {
+ while (true)
+ {
+ fd_set writes;
+ struct timeval tv;
+
+ FD_ZERO (&writes);
+ FD_SET (sd, &writes);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, NULL, &writes, NULL, &tv);
+
+ if (signal_received)
+ {
+ get_signal (signal_received);
+ if (*signal_received)
+ {
+ status = 0;
+ break;
+ }
+ }
+ if (status < 0)
+ {
+ status = openvpn_errno_socket ();
+ break;
+ }
+ if (status <= 0)
+ {
+ if (--connect_timeout < 0)
+ {
+ status = ETIMEDOUT;
+ break;
+ }
+ openvpn_sleep (1);
+ continue;
+ }
+
+ /* got it */
+ {
+ int val = 0;
+ socklen_t len;
+
+ len = sizeof (val);
+ if (getsockopt (sd, SOL_SOCKET, SO_ERROR, (void *) &val, &len) == 0
+ && len == sizeof (val))
+ status = val;
+ else
+ status = openvpn_errno_socket ();
+ break;
+ }
+ }
+ }
+#else
+ status = connect (sd, (struct sockaddr *) &remote->sa, sizeof (remote->sa));
+ if (status)
+ status = openvpn_errno_socket ();
+#endif
+
+ return status;
+}
+
+void
+socket_connect (socket_descriptor_t *sd,
+ struct openvpn_sockaddr *local,
+ bool bind_local,
+ struct openvpn_sockaddr *remote,
+ const bool connection_profiles_defined,
+ const char *remote_dynamic,
+ bool *remote_changed,
+ const int connect_retry_seconds,
+ const int connect_timeout,
+ const int connect_retry_max,
+ const unsigned int sockflags,
+ volatile int *signal_received)
+{
+ struct gc_arena gc = gc_new ();
+ int retry = 0;
+
+#ifdef CONNECT_NONBLOCK
+ msg (M_INFO, "Attempting to establish TCP connection with %s [nonblock]",
+ print_sockaddr (remote, &gc));
+#else
+ msg (M_INFO, "Attempting to establish TCP connection with %s",
+ print_sockaddr (remote, &gc));
+#endif
+
+ while (true)
+ {
+ int status;
+
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ management_set_state (management,
+ OPENVPN_STATE_TCP_CONNECT,
+ NULL,
+ (in_addr_t)0,
+ (in_addr_t)0);
+#endif
+
+ status = openvpn_connect (*sd, remote, connect_timeout, signal_received);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ goto done;
+
+ if (!status)
+ break;
+
+ msg (D_LINK_ERRORS,
+ "TCP: connect to %s failed, will try again in %d seconds: %s",
+ print_sockaddr (remote, &gc),
+ connect_retry_seconds,
+ strerror_ts (status, &gc));
+
+ gc_reset (&gc);
+
+ openvpn_close_socket (*sd);
+ *sd = SOCKET_UNDEFINED;
+
+ if ((connect_retry_max > 0 && ++retry >= connect_retry_max) || connection_profiles_defined)
+ {
+ *signal_received = SIGUSR1;
+ goto done;
+ }
+
+ openvpn_sleep (connect_retry_seconds);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ goto done;
+
+ *sd = create_socket_tcp ();
+ if (bind_local)
+ socket_bind (*sd, local, "TCP Client");
+ update_remote (remote_dynamic, remote, remote_changed, sockflags);
+ }
+
+ msg (M_INFO, "TCP connection established with %s",
+ print_sockaddr (remote, &gc));
+
+ done:
+ gc_free (&gc);
+}
+
+/* For stream protocols, allocate a buffer to build up packet.
+ Called after frame has been finalized. */
+
+static void
+socket_frame_init (const struct frame *frame, struct link_socket *sock)
+{
+#ifdef WIN32
+ overlapped_io_init (&sock->reads, frame, FALSE, false);
+ overlapped_io_init (&sock->writes, frame, TRUE, false);
+ sock->rw_handle.read = sock->reads.overlapped.hEvent;
+ sock->rw_handle.write = sock->writes.overlapped.hEvent;
+#endif
+
+ if (link_socket_connection_oriented (sock))
+ {
+#ifdef WIN32
+ stream_buf_init (&sock->stream_buf,
+ &sock->reads.buf_init,
+ sock->sockflags,
+ sock->info.proto);
+#else
+ alloc_buf_sock_tun (&sock->stream_buf_data,
+ frame,
+ false,
+ FRAME_HEADROOM_MARKER_READ_STREAM);
+
+ stream_buf_init (&sock->stream_buf,
+ &sock->stream_buf_data,
+ sock->sockflags,
+ sock->info.proto);
+#endif
+ }
+}
+
+/*
+ * Adjust frame structure based on a Path MTU value given
+ * to us by the OS.
+ */
+void
+frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto)
+{
+ frame_set_mtu_dynamic (frame, pmtu - datagram_overhead (proto), SET_MTU_UPPER_BOUND);
+}
+
+static void
+resolve_bind_local (struct link_socket *sock)
+{
+ struct gc_arena gc = gc_new ();
+
+ /* resolve local address if undefined */
+ if (!addr_defined (&sock->info.lsa->local))
+ {
+ sock->info.lsa->local.sa.sin_family = AF_INET;
+ sock->info.lsa->local.sa.sin_addr.s_addr =
+ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+ sock->local_host,
+ 0,
+ NULL,
+ NULL)
+ : htonl (INADDR_ANY));
+ sock->info.lsa->local.sa.sin_port = htons (sock->local_port);
+ }
+
+ /* bind to local address/port */
+ if (sock->bind_local)
+ {
+#ifdef ENABLE_SOCKS
+ if (sock->socks_proxy && sock->info.proto == PROTO_UDPv4)
+ socket_bind (sock->ctrl_sd, &sock->info.lsa->local, "SOCKS");
+ else
+#endif
+ socket_bind (sock->sd, &sock->info.lsa->local, "TCP/UDP");
+ }
+ gc_free (&gc);
+}
+
+static void
+resolve_remote (struct link_socket *sock,
+ int phase,
+ const char **remote_dynamic,
+ volatile int *signal_received)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (!sock->did_resolve_remote)
+ {
+ /* resolve remote address if undefined */
+ if (!addr_defined (&sock->info.lsa->remote))
+ {
+ sock->info.lsa->remote.sa.sin_family = AF_INET;
+ sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+
+ if (sock->remote_host)
+ {
+ unsigned int flags = sf2gaf(GETADDR_RESOLVE|GETADDR_UPDATE_MANAGEMENT_STATE, sock->sockflags);
+ int retry = 0;
+ bool status = false;
+
+ if (sock->connection_profiles_defined && sock->resolve_retry_seconds == RESOLV_RETRY_INFINITE)
+ {
+ if (phase == 2)
+ flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL);
+ retry = 0;
+ }
+ else if (phase == 1)
+ {
+ if (sock->resolve_retry_seconds)
+ {
+ retry = 0;
+ }
+ else
+ {
+ flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY);
+ retry = 0;
+ }
+ }
+ else if (phase == 2)
+ {
+ if (sock->resolve_retry_seconds)
+ {
+ flags |= GETADDR_FATAL;
+ retry = sock->resolve_retry_seconds;
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
+ flags,
+ sock->remote_host,
+ retry,
+ &status,
+ signal_received);
+
+ dmsg (D_SOCKET_DEBUG, "RESOLVE_REMOTE flags=0x%04x phase=%d rrs=%d sig=%d status=%d",
+ flags,
+ phase,
+ retry,
+ signal_received ? *signal_received : -1,
+ status);
+
+ if (signal_received)
+ {
+ if (*signal_received)
+ goto done;
+ }
+ if (!status)
+ {
+ if (signal_received)
+ *signal_received = SIGUSR1;
+ goto done;
+ }
+ }
+
+ sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
+ }
+
+ /* should we re-use previous active remote address? */
+ if (link_socket_actual_defined (&sock->info.lsa->actual))
+ {
+ msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s",
+ print_link_socket_actual (&sock->info.lsa->actual, &gc));
+ if (remote_dynamic)
+ *remote_dynamic = NULL;
+ }
+ else
+ {
+ CLEAR (sock->info.lsa->actual);
+ sock->info.lsa->actual.dest = sock->info.lsa->remote;
+ }
+
+ /* remember that we finished */
+ sock->did_resolve_remote = true;
+ }
+
+ done:
+ gc_free (&gc);
+}
+
+struct link_socket *
+link_socket_new (void)
+{
+ struct link_socket *sock;
+
+ ALLOC_OBJ_CLEAR (sock, struct link_socket);
+ sock->sd = SOCKET_UNDEFINED;
+#ifdef ENABLE_SOCKS
+ sock->ctrl_sd = SOCKET_UNDEFINED;
+#endif
+ return sock;
+}
+
+/* bind socket if necessary */
+void
+link_socket_init_phase1 (struct link_socket *sock,
+ const bool connection_profiles_defined,
+ const char *local_host,
+ int local_port,
+ const char *remote_host,
+ int remote_port,
+ int proto,
+ int mode,
+ const struct link_socket *accept_from,
+#ifdef ENABLE_HTTP_PROXY
+ struct http_proxy_info *http_proxy,
+#endif
+#ifdef ENABLE_SOCKS
+ struct socks_proxy_info *socks_proxy,
+#endif
+#ifdef ENABLE_DEBUG
+ int gremlin,
+#endif
+ bool bind_local,
+ bool remote_float,
+ int inetd,
+ struct link_socket_addr *lsa,
+ const char *ipchange_command,
+ const struct plugin_list *plugins,
+ int resolve_retry_seconds,
+ int connect_retry_seconds,
+ int connect_timeout,
+ int connect_retry_max,
+ int mtu_discover_type,
+ int rcvbuf,
+ int sndbuf,
+ unsigned int sockflags)
+{
+ ASSERT (sock);
+
+ sock->connection_profiles_defined = connection_profiles_defined;
+
+ sock->local_host = local_host;
+ sock->local_port = local_port;
+ sock->remote_host = remote_host;
+ sock->remote_port = remote_port;
+
+#ifdef ENABLE_HTTP_PROXY
+ sock->http_proxy = http_proxy;
+#endif
+
+#ifdef ENABLE_SOCKS
+ sock->socks_proxy = socks_proxy;
+#endif
+
+ sock->bind_local = bind_local;
+ sock->inetd = inetd;
+ sock->resolve_retry_seconds = resolve_retry_seconds;
+ sock->connect_retry_seconds = connect_retry_seconds;
+ sock->connect_timeout = connect_timeout;
+ sock->connect_retry_max = connect_retry_max;
+ sock->mtu_discover_type = mtu_discover_type;
+
+#ifdef ENABLE_DEBUG
+ sock->gremlin = gremlin;
+#endif
+
+ sock->socket_buffer_sizes.rcvbuf = rcvbuf;
+ sock->socket_buffer_sizes.sndbuf = sndbuf;
+
+ sock->sockflags = sockflags;
+
+ sock->info.proto = proto;
+ sock->info.remote_float = remote_float;
+ sock->info.lsa = lsa;
+ sock->info.ipchange_command = ipchange_command;
+ sock->info.plugins = plugins;
+
+ sock->mode = mode;
+ if (mode == LS_MODE_TCP_ACCEPT_FROM)
+ {
+ ASSERT (accept_from);
+ ASSERT (sock->info.proto == PROTO_TCPv4_SERVER);
+ ASSERT (!sock->inetd);
+ sock->sd = accept_from->sd;
+ }
+
+ if (false)
+ ;
+#ifdef ENABLE_HTTP_PROXY
+ /* are we running in HTTP proxy mode? */
+ else if (sock->http_proxy)
+ {
+ ASSERT (sock->info.proto == PROTO_TCPv4_CLIENT);
+ ASSERT (!sock->inetd);
+
+ /* the proxy server */
+ sock->remote_host = http_proxy->options.server;
+ sock->remote_port = http_proxy->options.port;
+
+ /* the OpenVPN server we will use the proxy to connect to */
+ sock->proxy_dest_host = remote_host;
+ sock->proxy_dest_port = remote_port;
+ }
+#endif
+#ifdef ENABLE_SOCKS
+ /* or in Socks proxy mode? */
+ else if (sock->socks_proxy)
+ {
+ ASSERT (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_UDPv4);
+ ASSERT (!sock->inetd);
+
+ /* the proxy server */
+ sock->remote_host = socks_proxy->server;
+ sock->remote_port = socks_proxy->port;
+
+ /* the OpenVPN server we will use the proxy to connect to */
+ sock->proxy_dest_host = remote_host;
+ sock->proxy_dest_port = remote_port;
+ }
+#endif
+ else
+ {
+ sock->remote_host = remote_host;
+ sock->remote_port = remote_port;
+ }
+
+ /* bind behavior for TCP server vs. client */
+ if (sock->info.proto == PROTO_TCPv4_SERVER)
+ {
+ if (sock->mode == LS_MODE_TCP_ACCEPT_FROM)
+ sock->bind_local = false;
+ else
+ sock->bind_local = true;
+ }
+
+ /* were we started by inetd or xinetd? */
+ if (sock->inetd)
+ {
+ ASSERT (sock->info.proto != PROTO_TCPv4_CLIENT);
+ ASSERT (socket_defined (inetd_socket_descriptor));
+ sock->sd = inetd_socket_descriptor;
+ }
+ else if (mode != LS_MODE_TCP_ACCEPT_FROM)
+ {
+ create_socket (sock);
+
+ /* set socket buffers based on --sndbuf and --rcvbuf options */
+ socket_set_buffers (sock->sd, &sock->socket_buffer_sizes);
+
+ resolve_bind_local (sock);
+ resolve_remote (sock, 1, NULL, NULL);
+ }
+}
+
+/* finalize socket initialization */
+void
+link_socket_init_phase2 (struct link_socket *sock,
+ const struct frame *frame,
+ volatile int *signal_received)
+{
+ struct gc_arena gc = gc_new ();
+ const char *remote_dynamic = NULL;
+ bool remote_changed = false;
+ int sig_save = 0;
+
+ ASSERT (sock);
+
+ if (signal_received && *signal_received)
+ {
+ sig_save = *signal_received;
+ *signal_received = 0;
+ }
+
+ /* initialize buffers */
+ socket_frame_init (frame, sock);
+
+ /*
+ * Pass a remote name to connect/accept so that
+ * they can test for dynamic IP address changes
+ * and throw a SIGUSR1 if appropriate.
+ */
+ if (sock->resolve_retry_seconds)
+ remote_dynamic = sock->remote_host;
+
+ /* were we started by inetd or xinetd? */
+ if (sock->inetd)
+ {
+ if (sock->info.proto == PROTO_TCPv4_SERVER)
+ sock->sd =
+ socket_listen_accept (sock->sd,
+ &sock->info.lsa->actual,
+ remote_dynamic,
+ &remote_changed,
+ &sock->info.lsa->local,
+ false,
+ sock->inetd == INETD_NOWAIT,
+ signal_received);
+ ASSERT (!remote_changed);
+ if (*signal_received)
+ goto done;
+ }
+ else
+ {
+ resolve_remote (sock, 2, &remote_dynamic, signal_received);
+
+ if (*signal_received)
+ goto done;
+
+ /* TCP client/server */
+ if (sock->info.proto == PROTO_TCPv4_SERVER)
+ {
+ switch (sock->mode)
+ {
+ case LS_MODE_DEFAULT:
+ sock->sd = socket_listen_accept (sock->sd,
+ &sock->info.lsa->actual,
+ remote_dynamic,
+ &remote_changed,
+ &sock->info.lsa->local,
+ true,
+ false,
+ signal_received);
+ break;
+ case LS_MODE_TCP_LISTEN:
+ socket_do_listen (sock->sd,
+ &sock->info.lsa->local,
+ true,
+ false);
+ break;
+ case LS_MODE_TCP_ACCEPT_FROM:
+ sock->sd = socket_do_accept (sock->sd,
+ &sock->info.lsa->actual,
+ false);
+ if (!socket_defined (sock->sd))
+ {
+ *signal_received = SIGTERM;
+ goto done;
+ }
+ tcp_connection_established (&sock->info.lsa->actual);
+ break;
+ default:
+ ASSERT (0);
+ }
+ }
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+
+#ifdef GENERAL_PROXY_SUPPORT
+ bool proxy_retry = false;
+#else
+ const bool proxy_retry = false;
+#endif
+ do {
+ socket_connect (&sock->sd,
+ &sock->info.lsa->local,
+ sock->bind_local,
+ &sock->info.lsa->actual.dest,
+ sock->connection_profiles_defined,
+ remote_dynamic,
+ &remote_changed,
+ sock->connect_retry_seconds,
+ sock->connect_timeout,
+ sock->connect_retry_max,
+ sock->sockflags,
+ signal_received);
+
+ if (*signal_received)
+ goto done;
+
+ if (false)
+ ;
+#ifdef ENABLE_HTTP_PROXY
+ else if (sock->http_proxy)
+ {
+ proxy_retry = establish_http_proxy_passthru (sock->http_proxy,
+ sock->sd,
+ sock->proxy_dest_host,
+ sock->proxy_dest_port,
+ &sock->stream_buf.residual,
+ signal_received);
+ }
+#endif
+#ifdef ENABLE_SOCKS
+ else if (sock->socks_proxy)
+ {
+ establish_socks_proxy_passthru (sock->socks_proxy,
+ sock->sd,
+ sock->proxy_dest_host,
+ sock->proxy_dest_port,
+ signal_received);
+ }
+#endif
+ if (proxy_retry)
+ {
+ openvpn_close_socket (sock->sd);
+ sock->sd = create_socket_tcp ();
+ }
+ } while (proxy_retry);
+ }
+#ifdef ENABLE_SOCKS
+ else if (sock->info.proto == PROTO_UDPv4 && sock->socks_proxy)
+ {
+ socket_connect (&sock->ctrl_sd,
+ &sock->info.lsa->local,
+ sock->bind_local,
+ &sock->info.lsa->actual.dest,
+ sock->connection_profiles_defined,
+ remote_dynamic,
+ &remote_changed,
+ sock->connect_retry_seconds,
+ sock->connect_timeout,
+ sock->connect_retry_max,
+ sock->sockflags,
+ signal_received);
+
+ if (*signal_received)
+ goto done;
+
+ establish_socks_proxy_udpassoc (sock->socks_proxy,
+ sock->ctrl_sd,
+ sock->sd,
+ &sock->socks_relay.dest,
+ signal_received);
+
+ if (*signal_received)
+ goto done;
+
+ sock->remote_host = sock->proxy_dest_host;
+ sock->remote_port = sock->proxy_dest_port;
+ sock->did_resolve_remote = false;
+
+ sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0;
+ sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+
+ resolve_remote (sock, 1, NULL, signal_received);
+
+ if (*signal_received)
+ goto done;
+ }
+#endif
+
+ if (*signal_received)
+ goto done;
+
+ if (remote_changed)
+ {
+ msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment");
+ sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr;
+ }
+ }
+
+ /* set misc socket parameters */
+ socket_set_flags (sock->sd, sock->sockflags);
+
+ /* set socket to non-blocking mode */
+ set_nonblock (sock->sd);
+
+ /* set socket file descriptor to not pass across execs, so that
+ scripts don't have access to it */
+ set_cloexec (sock->sd);
+
+#ifdef ENABLE_SOCKS
+ if (socket_defined (sock->ctrl_sd))
+ set_cloexec (sock->ctrl_sd);
+#endif
+
+ /* set Path MTU discovery options on the socket */
+ set_mtu_discover_type (sock->sd, sock->mtu_discover_type);
+
+#if EXTENDED_SOCKET_ERROR_CAPABILITY
+ /* if the OS supports it, enable extended error passing on the socket */
+ set_sock_extended_error_passing (sock->sd);
+#endif
+
+ /* print local address */
+ if (sock->inetd)
+ msg (M_INFO, "%s link local: [inetd]", proto2ascii (sock->info.proto, true));
+ else
+ msg (M_INFO, "%s link local%s: %s",
+ proto2ascii (sock->info.proto, true),
+ (sock->bind_local ? " (bound)" : ""),
+ print_sockaddr_ex (&sock->info.lsa->local, ":", sock->bind_local ? PS_SHOW_PORT : 0, &gc));
+
+ /* print active remote address */
+ msg (M_INFO, "%s link remote: %s",
+ proto2ascii (sock->info.proto, true),
+ print_link_socket_actual_ex (&sock->info.lsa->actual,
+ ":",
+ PS_SHOW_PORT_IF_DEFINED,
+ &gc));
+
+ done:
+ if (sig_save && signal_received)
+ {
+ if (!*signal_received)
+ *signal_received = sig_save;
+ }
+ gc_free (&gc);
+}
+
+void
+link_socket_close (struct link_socket *sock)
+{
+ if (sock)
+ {
+#ifdef ENABLE_DEBUG
+ const int gremlin = GREMLIN_CONNECTION_FLOOD_LEVEL (sock->gremlin);
+#else
+ const int gremlin = 0;
+#endif
+
+ if (socket_defined (sock->sd))
+ {
+#ifdef WIN32
+ close_net_event_win32 (&sock->listen_handle, sock->sd, 0);
+#endif
+ if (!gremlin)
+ {
+ msg (D_CLOSE, "TCP/UDP: Closing socket");
+ if (openvpn_close_socket (sock->sd))
+ msg (M_WARN | M_ERRNO_SOCK, "TCP/UDP: Close Socket failed");
+ }
+ sock->sd = SOCKET_UNDEFINED;
+#ifdef WIN32
+ if (!gremlin)
+ {
+ overlapped_io_close (&sock->reads);
+ overlapped_io_close (&sock->writes);
+ }
+#endif
+ }
+
+#ifdef ENABLE_SOCKS
+ if (socket_defined (sock->ctrl_sd))
+ {
+ if (openvpn_close_socket (sock->ctrl_sd))
+ msg (M_WARN | M_ERRNO_SOCK, "TCP/UDP: Close Socket (ctrl_sd) failed");
+ sock->ctrl_sd = SOCKET_UNDEFINED;
+ }
+#endif
+
+ stream_buf_close (&sock->stream_buf);
+ free_buf (&sock->stream_buf_data);
+ if (!gremlin)
+ free (sock);
+ }
+}
+
+/* for stream protocols, allow for packet length prefix */
+void
+socket_adjust_frame_parameters (struct frame *frame, int proto)
+{
+ if (link_socket_proto_connection_oriented (proto))
+ frame_add_to_extra_frame (frame, sizeof (packet_size_type));
+}
+
+void
+setenv_trusted (struct env_set *es, const struct link_socket_info *info)
+{
+ setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
+}
+
+static void
+ipchange_fmt (const bool include_cmd, struct argv *argv, const struct link_socket_info *info, struct gc_arena *gc)
+{
+ const char *ip = print_sockaddr_ex (&info->lsa->actual.dest, NULL, 0, gc);
+ const char *port = print_sockaddr_ex (&info->lsa->actual.dest, NULL, PS_DONT_SHOW_ADDR|PS_SHOW_PORT, gc);
+ if (include_cmd)
+ argv_printf (argv, "%sc %s %s",
+ info->ipchange_command,
+ ip,
+ port);
+ else
+ argv_printf (argv, "%s %s",
+ ip,
+ port);
+}
+
+void
+link_socket_connection_initiated (const struct buffer *buf,
+ struct link_socket_info *info,
+ const struct link_socket_actual *act,
+ const char *common_name,
+ struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+
+ info->lsa->actual = *act; /* Note: skip this line for --force-dest */
+ setenv_trusted (es, info);
+ info->connection_established = true;
+
+ /* Print connection initiated message, with common name if available */
+ {
+ struct buffer out = alloc_buf_gc (256, &gc);
+ if (common_name)
+ buf_printf (&out, "[%s] ", common_name);
+ buf_printf (&out, "Peer Connection Initiated with %s", print_link_socket_actual (&info->lsa->actual, &gc));
+ msg (M_INFO, "%s", BSTR (&out));
+ }
+
+ /* set environmental vars */
+ setenv_str (es, "common_name", common_name);
+
+ /* Process --ipchange plugin */
+ if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
+ {
+ struct argv argv = argv_new ();
+ ipchange_fmt (false, &argv, info, &gc);
+ if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, &argv, NULL, es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ msg (M_WARN, "WARNING: ipchange plugin call failed");
+ argv_reset (&argv);
+ }
+
+ /* Process --ipchange option */
+ if (info->ipchange_command)
+ {
+ struct argv argv = argv_new ();
+ setenv_str (es, "script_type", "ipchange");
+ ipchange_fmt (true, &argv, info, &gc);
+ openvpn_run_script (&argv, es, 0, "--ipchange");
+ argv_reset (&argv);
+ }
+
+ gc_free (&gc);
+}
+
+void
+link_socket_bad_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+ const struct link_socket_actual *from_addr)
+{
+ struct gc_arena gc = gc_new ();
+
+ msg (D_LINK_ERRORS,
+ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
+ print_link_socket_actual (from_addr, &gc),
+ (int)from_addr->dest.sa.sin_family,
+ print_sockaddr (&info->lsa->remote, &gc));
+ buf->len = 0;
+
+ gc_free (&gc);
+}
+
+void
+link_socket_bad_outgoing_addr (void)
+{
+ dmsg (D_READ_WRITE, "TCP/UDP: No outgoing address to send packet");
+}
+
+in_addr_t
+link_socket_current_remote (const struct link_socket_info *info)
+{
+ const struct link_socket_addr *lsa = info->lsa;
+
+ if (link_socket_actual_defined (&lsa->actual))
+ return ntohl (lsa->actual.dest.sa.sin_addr.s_addr);
+ else if (addr_defined (&lsa->remote))
+ return ntohl (lsa->remote.sa.sin_addr.s_addr);
+ else
+ return 0;
+}
+
+/*
+ * Return a status string describing socket state.
+ */
+const char *
+socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ if (s)
+ {
+ if (rwflags & EVENT_READ)
+ {
+ buf_printf (&out, "S%s",
+ (s->rwflags_debug & EVENT_READ) ? "R" : "r");
+#ifdef WIN32
+ buf_printf (&out, "%s",
+ overlapped_io_state_ascii (&s->reads));
+#endif
+ }
+ if (rwflags & EVENT_WRITE)
+ {
+ buf_printf (&out, "S%s",
+ (s->rwflags_debug & EVENT_WRITE) ? "W" : "w");
+#ifdef WIN32
+ buf_printf (&out, "%s",
+ overlapped_io_state_ascii (&s->writes));
+#endif
+ }
+ }
+ else
+ {
+ buf_printf (&out, "S?");
+ }
+ return BSTR (&out);
+}
+
+/*
+ * Stream buffer functions, used to packetize a TCP
+ * stream connection.
+ */
+
+static inline void
+stream_buf_reset (struct stream_buf *sb)
+{
+ dmsg (D_STREAM_DEBUG, "STREAM: RESET");
+ sb->residual_fully_formed = false;
+ sb->buf = sb->buf_init;
+ buf_reset (&sb->next);
+ sb->len = -1;
+}
+
+void
+stream_buf_init (struct stream_buf *sb,
+ struct buffer *buf,
+ const unsigned int sockflags,
+ const int proto)
+{
+ sb->buf_init = *buf;
+ sb->maxlen = sb->buf_init.len;
+ sb->buf_init.len = 0;
+ sb->residual = alloc_buf (sb->maxlen);
+ sb->error = false;
+#if PORT_SHARE
+ sb->port_share_state = ((sockflags & SF_PORT_SHARE) && (proto == PROTO_TCPv4_SERVER))
+ ? PS_ENABLED
+ : PS_DISABLED;
+#endif
+ stream_buf_reset (sb);
+
+ dmsg (D_STREAM_DEBUG, "STREAM: INIT maxlen=%d", sb->maxlen);
+}
+
+static inline void
+stream_buf_set_next (struct stream_buf *sb)
+{
+ /* set up 'next' for next i/o read */
+ sb->next = sb->buf;
+ sb->next.offset = sb->buf.offset + sb->buf.len;
+ sb->next.len = (sb->len >= 0 ? sb->len : sb->maxlen) - sb->buf.len;
+ dmsg (D_STREAM_DEBUG, "STREAM: SET NEXT, buf=[%d,%d] next=[%d,%d] len=%d maxlen=%d",
+ sb->buf.offset, sb->buf.len,
+ sb->next.offset, sb->next.len,
+ sb->len, sb->maxlen);
+ ASSERT (sb->next.len > 0);
+ ASSERT (buf_safe (&sb->buf, sb->next.len));
+}
+
+static inline void
+stream_buf_get_final (struct stream_buf *sb, struct buffer *buf)
+{
+ dmsg (D_STREAM_DEBUG, "STREAM: GET FINAL len=%d",
+ buf_defined (&sb->buf) ? sb->buf.len : -1);
+ ASSERT (buf_defined (&sb->buf));
+ *buf = sb->buf;
+}
+
+static inline void
+stream_buf_get_next (struct stream_buf *sb, struct buffer *buf)
+{
+ dmsg (D_STREAM_DEBUG, "STREAM: GET NEXT len=%d",
+ buf_defined (&sb->next) ? sb->next.len : -1);
+ ASSERT (buf_defined (&sb->next));
+ *buf = sb->next;
+}
+
+bool
+stream_buf_read_setup_dowork (struct link_socket* sock)
+{
+ if (sock->stream_buf.residual.len && !sock->stream_buf.residual_fully_formed)
+ {
+ ASSERT (buf_copy (&sock->stream_buf.buf, &sock->stream_buf.residual));
+ ASSERT (buf_init (&sock->stream_buf.residual, 0));
+ sock->stream_buf.residual_fully_formed = stream_buf_added (&sock->stream_buf, 0);
+ dmsg (D_STREAM_DEBUG, "STREAM: RESIDUAL FULLY FORMED [%s], len=%d",
+ sock->stream_buf.residual_fully_formed ? "YES" : "NO",
+ sock->stream_buf.residual.len);
+ }
+
+ if (!sock->stream_buf.residual_fully_formed)
+ stream_buf_set_next (&sock->stream_buf);
+ return !sock->stream_buf.residual_fully_formed;
+}
+
+bool
+stream_buf_added (struct stream_buf *sb,
+ int length_added)
+{
+ dmsg (D_STREAM_DEBUG, "STREAM: ADD length_added=%d", length_added);
+ if (length_added > 0)
+ sb->buf.len += length_added;
+
+ /* if length unknown, see if we can get the length prefix from
+ the head of the buffer */
+ if (sb->len < 0 && sb->buf.len >= (int) sizeof (packet_size_type))
+ {
+ packet_size_type net_size;
+
+#if PORT_SHARE
+ if (sb->port_share_state == PS_ENABLED)
+ {
+ if (!is_openvpn_protocol (&sb->buf))
+ {
+ msg (D_STREAM_ERRORS, "Non-OpenVPN client protocol detected");
+ sb->port_share_state = PS_FOREIGN;
+ sb->error = true;
+ return false;
+ }
+ else
+ sb->port_share_state = PS_DISABLED;
+ }
+#endif
+
+ ASSERT (buf_read (&sb->buf, &net_size, sizeof (net_size)));
+ sb->len = ntohps (net_size);
+
+ if (sb->len < 1 || sb->len > sb->maxlen)
+ {
+ msg (M_WARN, "WARNING: Bad encapsulated packet length from peer (%d), which must be > 0 and <= %d -- please ensure that --tun-mtu or --link-mtu is equal on both peers -- this condition could also indicate a possible active attack on the TCP link -- [Attempting restart...]", sb->len, sb->maxlen);
+ stream_buf_reset (sb);
+ sb->error = true;
+ return false;
+ }
+ }
+
+ /* is our incoming packet fully read? */
+ if (sb->len > 0 && sb->buf.len >= sb->len)
+ {
+ /* save any residual data that's part of the next packet */
+ ASSERT (buf_init (&sb->residual, 0));
+ if (sb->buf.len > sb->len)
+ ASSERT (buf_copy_excess (&sb->residual, &sb->buf, sb->len));
+ dmsg (D_STREAM_DEBUG, "STREAM: ADD returned TRUE, buf_len=%d, residual_len=%d",
+ BLEN (&sb->buf),
+ BLEN (&sb->residual));
+ return true;
+ }
+ else
+ {
+ dmsg (D_STREAM_DEBUG, "STREAM: ADD returned FALSE (have=%d need=%d)", sb->buf.len, sb->len);
+ stream_buf_set_next (sb);
+ return false;
+ }
+}
+
+void
+stream_buf_close (struct stream_buf* sb)
+{
+ free_buf (&sb->residual);
+}
+
+/*
+ * The listen event is a special event whose sole purpose is
+ * to tell us that there's a new incoming connection on a
+ * TCP socket, for use in server mode.
+ */
+event_t
+socket_listen_event_handle (struct link_socket *s)
+{
+#ifdef WIN32
+ if (!defined_net_event_win32 (&s->listen_handle))
+ init_net_event_win32 (&s->listen_handle, FD_ACCEPT, s->sd, 0);
+ return &s->listen_handle;
+#else
+ return s->sd;
+#endif
+}
+
+/*
+ * Format IP addresses in ascii
+ */
+
+const char *
+print_sockaddr (const struct openvpn_sockaddr *addr, struct gc_arena *gc)
+{
+ return print_sockaddr_ex (addr, ":", PS_SHOW_PORT, gc);
+}
+
+const char *
+print_sockaddr_ex (const struct openvpn_sockaddr *addr,
+ const char* separator,
+ const unsigned int flags,
+ struct gc_arena *gc)
+{
+ if (addr)
+ {
+ struct buffer out = alloc_buf_gc (64, gc);
+ const int port = ntohs (addr->sa.sin_port);
+
+ if (!(flags & PS_DONT_SHOW_ADDR))
+ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
+
+ if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
+ && port)
+ {
+ if (separator)
+ buf_printf (&out, "%s", separator);
+
+ buf_printf (&out, "%d", port);
+ }
+ return BSTR (&out);
+ }
+ else
+ return "[NULL]";
+}
+
+const char *
+print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena *gc)
+{
+ return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
+}
+
+const char *
+print_link_socket_actual_ex (const struct link_socket_actual *act,
+ const char *separator,
+ const unsigned int flags,
+ struct gc_arena *gc)
+{
+ if (act)
+ {
+ struct buffer out = alloc_buf_gc (128, gc);
+ buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc));
+#if ENABLE_IP_PKTINFO
+ if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr)
+ {
+ struct openvpn_sockaddr sa;
+ CLEAR (sa);
+ sa.sa.sin_addr = act->pi.ipi_spec_dst;
+ buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
+ }
+#endif
+ return BSTR (&out);
+ }
+ else
+ return "[NULL]";
+}
+
+/*
+ * Convert an in_addr_t in host byte order
+ * to an ascii dotted quad.
+ */
+const char *
+print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc)
+{
+ struct in_addr ia;
+ struct buffer out = alloc_buf_gc (64, gc);
+
+ if (addr || !(flags & IA_EMPTY_IF_UNDEF))
+ {
+ CLEAR (ia);
+ ia.s_addr = (flags & IA_NET_ORDER) ? addr : htonl (addr);
+
+ buf_printf (&out, "%s", inet_ntoa (ia));
+ }
+ return BSTR (&out);
+}
+
+/* set environmental variables for ip/port in *addr */
+void
+setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
+{
+ char name_buf[256];
+
+ if (flags & SA_IP_PORT)
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_ip", name_prefix);
+ else
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
+
+ setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
+
+ if ((flags & SA_IP_PORT) && addr->sa.sin_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+ setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+ }
+}
+
+void
+setenv_in_addr_t (struct env_set *es, const char *name_prefix, in_addr_t addr, const bool flags)
+{
+ if (addr || !(flags & SA_SET_IF_NONZERO))
+ {
+ struct openvpn_sockaddr si;
+ CLEAR (si);
+ si.sa.sin_addr.s_addr = htonl (addr);
+ setenv_sockaddr (es, name_prefix, &si, flags);
+ }
+}
+
+void
+setenv_link_socket_actual (struct env_set *es,
+ const char *name_prefix,
+ const struct link_socket_actual *act,
+ const bool flags)
+{
+ setenv_sockaddr (es, name_prefix, &act->dest, flags);
+}
+
+/*
+ * Convert protocol names between index and ascii form.
+ */
+
+struct proto_names {
+ const char *short_form;
+ const char *display_form;
+};
+
+/* Indexed by PROTO_x */
+static const struct proto_names proto_names[] = {
+ {"udp", "UDPv4"},
+ {"tcp-server", "TCPv4_SERVER"},
+ {"tcp-client", "TCPv4_CLIENT"},
+ {"tcp", "TCPv4"}
+};
+
+int
+ascii2proto (const char* proto_name)
+{
+ int i;
+ ASSERT (PROTO_N == SIZE (proto_names));
+ for (i = 0; i < PROTO_N; ++i)
+ if (!strcmp (proto_name, proto_names[i].short_form))
+ return i;
+ return -1;
+}
+
+const char *
+proto2ascii (int proto, bool display_form)
+{
+ ASSERT (PROTO_N == SIZE (proto_names));
+ if (proto < 0 || proto >= PROTO_N)
+ return "[unknown protocol]";
+ else if (display_form)
+ return proto_names[proto].display_form;
+ else
+ return proto_names[proto].short_form;
+}
+
+const char *
+proto2ascii_all (struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ int i;
+
+ ASSERT (PROTO_N == SIZE (proto_names));
+ for (i = 0; i < PROTO_N; ++i)
+ {
+ if (i)
+ buf_printf(&out, " ");
+ buf_printf(&out, "[%s]", proto2ascii(i, false));
+ }
+ return BSTR (&out);
+}
+
+/*
+ * Given a local proto, return local proto
+ * if !remote, or compatible remote proto
+ * if remote.
+ *
+ * This is used for options compatibility
+ * checking.
+ */
+int
+proto_remote (int proto, bool remote)
+{
+ ASSERT (proto >= 0 && proto < PROTO_N);
+ if (remote)
+ {
+ if (proto == PROTO_TCPv4_SERVER)
+ return PROTO_TCPv4_CLIENT;
+ if (proto == PROTO_TCPv4_CLIENT)
+ return PROTO_TCPv4_SERVER;
+ }
+ return proto;
+}
+
+/*
+ * Bad incoming address lengths that differ from what
+ * we expect are considered to be fatal errors.
+ */
+void
+bad_address_length (int actual, int expected)
+{
+ msg (M_FATAL, "ERROR: received strange incoming packet with an address length of %d -- we only accept address lengths of %d.",
+ actual,
+ expected);
+}
+
+/*
+ * Socket Read Routines
+ */
+
+int
+link_socket_read_tcp (struct link_socket *sock,
+ struct buffer *buf)
+{
+ int len = 0;
+
+ if (!sock->stream_buf.residual_fully_formed)
+ {
+#ifdef WIN32
+ len = socket_finalize (sock->sd, &sock->reads, buf, NULL);
+#else
+ struct buffer frag;
+ stream_buf_get_next (&sock->stream_buf, &frag);
+ len = recv (sock->sd, BPTR (&frag), BLEN (&frag), MSG_NOSIGNAL);
+#endif
+
+ if (!len)
+ sock->stream_reset = true;
+ if (len <= 0)
+ return buf->len = len;
+ }
+
+ if (sock->stream_buf.residual_fully_formed
+ || stream_buf_added (&sock->stream_buf, len)) /* packet complete? */
+ {
+ stream_buf_get_final (&sock->stream_buf, buf);
+ stream_buf_reset (&sock->stream_buf);
+ return buf->len;
+ }
+ else
+ return buf->len = 0; /* no error, but packet is still incomplete */
+}
+
+#ifndef WIN32
+
+#if ENABLE_IP_PKTINFO
+
+#pragma pack(1) /* needed to keep structure size consistent for 32 vs. 64-bit architectures */
+struct openvpn_pktinfo
+{
+ struct cmsghdr cmsghdr;
+ struct in_pktinfo in_pktinfo;
+};
+#pragma pack()
+
+static socklen_t
+link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+ struct link_socket_actual *from)
+{
+ struct iovec iov;
+ struct openvpn_pktinfo opi;
+ struct msghdr mesg;
+ socklen_t fromlen = sizeof (from->dest.sa);
+
+ iov.iov_base = BPTR (buf);
+ iov.iov_len = maxsize;
+ mesg.msg_iov = &iov;
+ mesg.msg_iovlen = 1;
+ mesg.msg_name = &from->dest.sa;
+ mesg.msg_namelen = fromlen;
+ mesg.msg_control = &opi;
+ mesg.msg_controllen = sizeof (opi);
+ buf->len = recvmsg (sock->sd, &mesg, 0);
+ if (buf->len >= 0)
+ {
+ struct cmsghdr *cmsg;
+ fromlen = mesg.msg_namelen;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ if (cmsg != NULL
+ && CMSG_NXTHDR (&mesg, cmsg) == NULL
+ && cmsg->cmsg_level == SOL_IP
+ && cmsg->cmsg_type == IP_PKTINFO
+ && cmsg->cmsg_len >= sizeof (opi))
+ {
+ struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+ from->pi.ipi_ifindex = pkti->ipi_ifindex;
+ from->pi.ipi_spec_dst = pkti->ipi_spec_dst;
+ }
+ }
+ return fromlen;
+}
+#endif
+
+int
+link_socket_read_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+ struct link_socket_actual *from)
+{
+ socklen_t fromlen = sizeof (from->dest.sa);
+ from->dest.sa.sin_addr.s_addr = 0;
+ ASSERT (buf_safe (buf, maxsize));
+#if ENABLE_IP_PKTINFO
+ if (sock->sockflags & SF_USE_IP_PKTINFO)
+ fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from);
+ else
+#endif
+ buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
+ (struct sockaddr *) &from->dest.sa, &fromlen);
+ if (fromlen != sizeof (from->dest.sa))
+ bad_address_length (fromlen, sizeof (from->dest.sa));
+ return buf->len;
+}
+
+#endif
+
+/*
+ * Socket Write Routines
+ */
+
+int
+link_socket_write_tcp (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+ packet_size_type len = BLEN (buf);
+ dmsg (D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset);
+ ASSERT (len <= sock->stream_buf.maxlen);
+ len = htonps (len);
+ ASSERT (buf_write_prepend (buf, &len, sizeof (len)));
+#ifdef WIN32
+ return link_socket_write_win32 (sock, buf, to);
+#else
+ return link_socket_write_tcp_posix (sock, buf, to);
+#endif
+}
+
+#if ENABLE_IP_PKTINFO
+
+int
+link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+ struct iovec iov;
+ struct msghdr mesg;
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pkti;
+ struct openvpn_pktinfo opi;
+
+ iov.iov_base = BPTR (buf);
+ iov.iov_len = BLEN (buf);
+ mesg.msg_iov = &iov;
+ mesg.msg_iovlen = 1;
+ mesg.msg_name = &to->dest.sa;
+ mesg.msg_namelen = sizeof (to->dest.sa);
+ mesg.msg_control = &opi;
+ mesg.msg_controllen = sizeof (opi);
+ mesg.msg_flags = 0;
+ cmsg = CMSG_FIRSTHDR (&mesg);
+ cmsg->cmsg_len = sizeof (opi);
+ cmsg->cmsg_level = SOL_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+ pkti->ipi_ifindex = to->pi.ipi_ifindex;
+ pkti->ipi_spec_dst = to->pi.ipi_spec_dst;
+ pkti->ipi_addr.s_addr = 0;
+ return sendmsg (sock->sd, &mesg, 0);
+}
+
+#endif
+
+/*
+ * Win32 overlapped socket I/O functions.
+ */
+
+#ifdef WIN32
+
+int
+socket_recv_queue (struct link_socket *sock, int maxsize)
+{
+ if (sock->reads.iostate == IOSTATE_INITIAL)
+ {
+ WSABUF wsabuf[1];
+ int status;
+
+ /* reset buf to its initial state */
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ sock->reads.buf = sock->reads.buf_init;
+ }
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ {
+ stream_buf_get_next (&sock->stream_buf, &sock->reads.buf);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ /* Win32 docs say it's okay to allocate the wsabuf on the stack */
+ wsabuf[0].buf = BPTR (&sock->reads.buf);
+ wsabuf[0].len = maxsize ? maxsize : BLEN (&sock->reads.buf);
+
+ /* check for buffer overflow */
+ ASSERT (wsabuf[0].len <= BLEN (&sock->reads.buf));
+
+ /* the overlapped read will signal this event on I/O completion */
+ ASSERT (ResetEvent (sock->reads.overlapped.hEvent));
+ sock->reads.flags = 0;
+
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ sock->reads.addr_defined = true;
+ sock->reads.addrlen = sizeof (sock->reads.addr);
+ status = WSARecvFrom(
+ sock->sd,
+ wsabuf,
+ 1,
+ &sock->reads.size,
+ &sock->reads.flags,
+ (struct sockaddr *) &sock->reads.addr,
+ &sock->reads.addrlen,
+ &sock->reads.overlapped,
+ NULL);
+ }
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ {
+ sock->reads.addr_defined = false;
+ status = WSARecv(
+ sock->sd,
+ wsabuf,
+ 1,
+ &sock->reads.size,
+ &sock->reads.flags,
+ &sock->reads.overlapped,
+ NULL);
+ }
+ else
+ {
+ status = 0;
+ ASSERT (0);
+ }
+
+ if (!status) /* operation completed immediately? */
+ {
+ if (sock->reads.addr_defined && sock->reads.addrlen != sizeof (sock->reads.addr))
+ bad_address_length (sock->reads.addrlen, sizeof (sock->reads.addr));
+
+ sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
+
+ /* since we got an immediate return, we must signal the event object ourselves */
+ ASSERT (SetEvent (sock->reads.overlapped.hEvent));
+ sock->reads.status = 0;
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive immediate return [%d,%d]",
+ (int) wsabuf[0].len,
+ (int) sock->reads.size);
+ }
+ else
+ {
+ status = WSAGetLastError ();
+ if (status == WSA_IO_PENDING) /* operation queued? */
+ {
+ sock->reads.iostate = IOSTATE_QUEUED;
+ sock->reads.status = status;
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive queued [%d]",
+ (int) wsabuf[0].len);
+ }
+ else /* error occurred */
+ {
+ struct gc_arena gc = gc_new ();
+ ASSERT (SetEvent (sock->reads.overlapped.hEvent));
+ sock->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
+ sock->reads.status = status;
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Receive error [%d]: %s",
+ (int) wsabuf[0].len,
+ strerror_win32 (status, &gc));
+ gc_free (&gc);
+ }
+ }
+ }
+ return sock->reads.iostate;
+}
+
+int
+socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct link_socket_actual *to)
+{
+ if (sock->writes.iostate == IOSTATE_INITIAL)
+ {
+ WSABUF wsabuf[1];
+ int status;
+
+ /* make a private copy of buf */
+ sock->writes.buf = sock->writes.buf_init;
+ sock->writes.buf.len = 0;
+ ASSERT (buf_copy (&sock->writes.buf, buf));
+
+ /* Win32 docs say it's okay to allocate the wsabuf on the stack */
+ wsabuf[0].buf = BPTR (&sock->writes.buf);
+ wsabuf[0].len = BLEN (&sock->writes.buf);
+
+ /* the overlapped write will signal this event on I/O completion */
+ ASSERT (ResetEvent (sock->writes.overlapped.hEvent));
+ sock->writes.flags = 0;
+
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ /* set destination address for UDP writes */
+ sock->writes.addr_defined = true;
+ sock->writes.addr = to->dest.sa;
+ sock->writes.addrlen = sizeof (sock->writes.addr);
+
+ status = WSASendTo(
+ sock->sd,
+ wsabuf,
+ 1,
+ &sock->writes.size,
+ sock->writes.flags,
+ (struct sockaddr *) &sock->writes.addr,
+ sock->writes.addrlen,
+ &sock->writes.overlapped,
+ NULL);
+ }
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT || sock->info.proto == PROTO_TCPv4_SERVER)
+ {
+ /* destination address for TCP writes was established on connection initiation */
+ sock->writes.addr_defined = false;
+
+ status = WSASend(
+ sock->sd,
+ wsabuf,
+ 1,
+ &sock->writes.size,
+ sock->writes.flags,
+ &sock->writes.overlapped,
+ NULL);
+ }
+ else
+ {
+ status = 0;
+ ASSERT (0);
+ }
+
+ if (!status) /* operation completed immediately? */
+ {
+ sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
+
+ /* since we got an immediate return, we must signal the event object ourselves */
+ ASSERT (SetEvent (sock->writes.overlapped.hEvent));
+
+ sock->writes.status = 0;
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send immediate return [%d,%d]",
+ (int) wsabuf[0].len,
+ (int) sock->writes.size);
+ }
+ else
+ {
+ status = WSAGetLastError ();
+ if (status == WSA_IO_PENDING) /* operation queued? */
+ {
+ sock->writes.iostate = IOSTATE_QUEUED;
+ sock->writes.status = status;
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send queued [%d]",
+ (int) wsabuf[0].len);
+ }
+ else /* error occurred */
+ {
+ struct gc_arena gc = gc_new ();
+ ASSERT (SetEvent (sock->writes.overlapped.hEvent));
+ sock->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
+ sock->writes.status = status;
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Send error [%d]: %s",
+ (int) wsabuf[0].len,
+ strerror_win32 (status, &gc));
+
+ gc_free (&gc);
+ }
+ }
+ }
+ return sock->writes.iostate;
+}
+
+int
+socket_finalize (SOCKET s,
+ struct overlapped_io *io,
+ struct buffer *buf,
+ struct link_socket_actual *from)
+{
+ int ret = -1;
+ BOOL status;
+
+ switch (io->iostate)
+ {
+ case IOSTATE_QUEUED:
+ status = WSAGetOverlappedResult(
+ s,
+ &io->overlapped,
+ &io->size,
+ FALSE,
+ &io->flags
+ );
+ if (status)
+ {
+ /* successful return for a queued operation */
+ if (buf)
+ *buf = io->buf;
+ ret = io->size;
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion success [%d]", ret);
+ }
+ else
+ {
+ /* error during a queued operation */
+ ret = -1;
+ if (WSAGetLastError() != WSA_IO_INCOMPLETE)
+ {
+ /* if no error (i.e. just not finished yet), then DON'T execute this code */
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+ msg (D_WIN32_IO | M_ERRNO_SOCK, "WIN32 I/O: Socket Completion error");
+ }
+ }
+ break;
+
+ case IOSTATE_IMMEDIATE_RETURN:
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+ if (io->status)
+ {
+ /* error return for a non-queued operation */
+ WSASetLastError (io->status);
+ ret = -1;
+ msg (D_WIN32_IO | M_ERRNO_SOCK, "WIN32 I/O: Socket Completion non-queued error");
+ }
+ else
+ {
+ /* successful return for a non-queued operation */
+ if (buf)
+ *buf = io->buf;
+ ret = io->size;
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion non-queued success [%d]", ret);
+ }
+ break;
+
+ case IOSTATE_INITIAL: /* were we called without proper queueing? */
+ WSASetLastError (WSAEINVAL);
+ ret = -1;
+ dmsg (D_WIN32_IO, "WIN32 I/O: Socket Completion BAD STATE");
+ break;
+
+ default:
+ ASSERT (0);
+ }
+
+ /* return from address if requested */
+ if (from)
+ {
+ if (ret >= 0 && io->addr_defined)
+ {
+ if (io->addrlen != sizeof (io->addr))
+ bad_address_length (io->addrlen, sizeof (io->addr));
+ from->dest.sa = io->addr;
+ }
+ else
+ CLEAR (from->dest.sa);
+ }
+
+ if (buf)
+ buf->len = ret;
+ return ret;
+}
+
+#endif /* WIN32 */
+
+/*
+ * Socket event notification
+ */
+
+unsigned int
+socket_set (struct link_socket *s,
+ struct event_set *es,
+ unsigned int rwflags,
+ void *arg,
+ unsigned int *persistent)
+{
+ if (s)
+ {
+ if ((rwflags & EVENT_READ) && !stream_buf_read_setup (s))
+ {
+ ASSERT (!persistent);
+ rwflags &= ~EVENT_READ;
+ }
+
+#ifdef WIN32
+ if (rwflags & EVENT_READ)
+ socket_recv_queue (s, 0);
+#endif
+
+ /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
+ if (!persistent || *persistent != rwflags)
+ {
+ event_ctl (es, socket_event_handle (s), rwflags, arg);
+ if (persistent)
+ *persistent = rwflags;
+ }
+
+ s->rwflags_debug = rwflags;
+ }
+ return rwflags;
+}
+
+void
+sd_close (socket_descriptor_t *sd)
+{
+ if (sd && socket_defined (*sd))
+ {
+ openvpn_close_socket (*sd);
+ *sd = SOCKET_UNDEFINED;
+ }
+}
+
+#if UNIX_SOCK_SUPPORT
+
+/*
+ * code for unix domain sockets
+ */
+
+const char *
+sockaddr_unix_name (const struct sockaddr_un *local, const char *null)
+{
+ if (local && local->sun_family == PF_UNIX)
+ return local->sun_path;
+ else
+ return null;
+}
+
+socket_descriptor_t
+create_socket_unix (void)
+{
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_UNIX, SOCK_STREAM, 0)) < 0)
+ msg (M_SOCKERR, "Cannot create unix domain socket");
+ return sd;
+}
+
+void
+socket_bind_unix (socket_descriptor_t sd,
+ struct sockaddr_un *local,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+#ifdef HAVE_UMASK
+ const mode_t orig_umask = umask (0);
+#endif
+
+ if (bind (sd, (struct sockaddr *) local, sizeof (struct sockaddr_un)))
+ {
+ const int errnum = openvpn_errno_socket ();
+ msg (M_FATAL, "%s: Socket bind[%d] failed on unix domain socket %s: %s",
+ prefix,
+ (int)sd,
+ sockaddr_unix_name (local, "NULL"),
+ strerror_ts (errnum, &gc));
+ }
+
+#ifdef HAVE_UMASK
+ umask (orig_umask);
+#endif
+
+ gc_free (&gc);
+}
+
+socket_descriptor_t
+socket_accept_unix (socket_descriptor_t sd,
+ struct sockaddr_un *remote)
+{
+ socklen_t remote_len = sizeof (struct sockaddr_un);
+ socket_descriptor_t ret;
+
+ CLEAR (*remote);
+ ret = accept (sd, (struct sockaddr *) remote, &remote_len);
+ return ret;
+}
+
+int
+socket_connect_unix (socket_descriptor_t sd,
+ struct sockaddr_un *remote)
+{
+ int status = connect (sd, (struct sockaddr *) remote, sizeof (struct sockaddr_un));
+ if (status)
+ status = openvpn_errno_socket ();
+ return status;
+}
+
+void
+sockaddr_unix_init (struct sockaddr_un *local, const char *path)
+{
+ local->sun_family = PF_UNIX;
+ strncpynt (local->sun_path, path, sizeof (local->sun_path));
+}
+
+void
+socket_delete_unix (const struct sockaddr_un *local)
+{
+ const char *name = sockaddr_unix_name (local, NULL);
+#ifdef HAVE_UNLINK
+ if (name && strlen (name))
+ unlink (name);
+#endif
+}
+
+bool
+unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int *gid)
+{
+#ifdef HAVE_GETPEEREID
+ uid_t u;
+ gid_t g;
+ if (getpeereid (sd, &u, &g) == -1)
+ return false;
+ if (uid)
+ *uid = u;
+ if (gid)
+ *gid = g;
+ return true;
+#elif defined(SO_PEERCRED)
+ struct ucred peercred;
+ socklen_t so_len = sizeof(peercred);
+ if (getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) == -1)
+ return false;
+ if (uid)
+ *uid = peercred.uid;
+ if (gid)
+ *gid = peercred.gid;
+ return true;
+#else
+ return false;
+#endif
+}
+
+#endif
diff --git a/socket.h b/socket.h
new file mode 100644
index 0000000..eef98d1
--- /dev/null
+++ b/socket.h
@@ -0,0 +1,944 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include "buffer.h"
+#include "common.h"
+#include "error.h"
+#include "proto.h"
+#include "mtu.h"
+#include "win32.h"
+#include "event.h"
+#include "proxy.h"
+#include "socks.h"
+#include "misc.h"
+
+/*
+ * OpenVPN's default port number as assigned by IANA.
+ */
+#define OPENVPN_PORT 1194
+
+/*
+ * Maximum size passed passed to setsockopt SNDBUF/RCVBUF
+ */
+#define SOCKET_SND_RCV_BUF_MAX 1000000
+
+/*
+ * Number of seconds that "resolv-retry infinite"
+ * represents.
+ */
+#define RESOLV_RETRY_INFINITE 1000000000
+
+/*
+ * packet_size_type is used to communicate packet size
+ * over the wire when stream oriented protocols are
+ * being used
+ */
+
+typedef uint16_t packet_size_type;
+
+/* convert a packet_size_type from host to network order */
+#define htonps(x) htons(x)
+
+/* convert a packet_size_type from network to host order */
+#define ntohps(x) ntohs(x)
+
+/* OpenVPN sockaddr struct */
+struct openvpn_sockaddr
+{
+ /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */
+ struct sockaddr_in sa;
+};
+
+/* actual address of remote, based on source address of received packets */
+struct link_socket_actual
+{
+ /*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */
+ struct openvpn_sockaddr dest;
+#if ENABLE_IP_PKTINFO
+ struct in_pktinfo pi;
+#endif
+};
+
+/* IP addresses which are persistant across SIGUSR1s */
+struct link_socket_addr
+{
+ struct openvpn_sockaddr local;
+ struct openvpn_sockaddr remote; /* initial remote */
+ struct link_socket_actual actual; /* reply to this address */
+};
+
+struct link_socket_info
+{
+ struct link_socket_addr *lsa;
+ bool connection_established;
+ const char *ipchange_command;
+ const struct plugin_list *plugins;
+ bool remote_float;
+ int proto; /* Protocol (PROTO_x defined below) */
+ int mtu_changed; /* Set to true when mtu value is changed */
+};
+
+/*
+ * Used to extract packets encapsulated in streams into a buffer,
+ * in this case IP packets embedded in a TCP stream.
+ */
+struct stream_buf
+{
+ struct buffer buf_init;
+ struct buffer residual;
+ int maxlen;
+ bool residual_fully_formed;
+
+ struct buffer buf;
+ struct buffer next;
+ int len; /* -1 if not yet known */
+
+ bool error; /* if true, fatal TCP error has occurred,
+ requiring that connection be restarted */
+#if PORT_SHARE
+# define PS_DISABLED 0
+# define PS_ENABLED 1
+# define PS_FOREIGN 2
+ int port_share_state;
+#endif
+};
+
+/*
+ * Used to set socket buffer sizes
+ */
+struct socket_buffer_size
+{
+ int rcvbuf;
+ int sndbuf;
+};
+
+/*
+ * This is the main socket structure used by OpenVPN. The SOCKET_
+ * defines try to abstract away our implementation differences between
+ * using sockets on Posix vs. Win32.
+ */
+struct link_socket
+{
+ struct link_socket_info info;
+
+ socket_descriptor_t sd;
+
+#ifdef ENABLE_SOCKS
+ socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */
+#endif
+
+#ifdef WIN32
+ struct overlapped_io reads;
+ struct overlapped_io writes;
+ struct rw_handle rw_handle;
+ struct rw_handle listen_handle; /* For listening on TCP socket in server mode */
+#endif
+
+ /* used for printing status info only */
+ unsigned int rwflags_debug;
+
+ /* used for long-term queueing of pre-accepted socket listen */
+ bool listen_persistent_queued;
+
+ /* Does config file contain any <connection> ... </connection> blocks? */
+ bool connection_profiles_defined;
+
+ const char *remote_host;
+ int remote_port;
+ const char *local_host;
+ int local_port;
+ bool bind_local;
+
+# define INETD_NONE 0
+# define INETD_WAIT 1
+# define INETD_NOWAIT 2
+ int inetd;
+
+# define LS_MODE_DEFAULT 0
+# define LS_MODE_TCP_LISTEN 1
+# define LS_MODE_TCP_ACCEPT_FROM 2
+ int mode;
+
+ int resolve_retry_seconds;
+ int connect_retry_seconds;
+ int connect_timeout;
+ int connect_retry_max;
+ int mtu_discover_type;
+
+ struct socket_buffer_size socket_buffer_sizes;
+
+ int mtu; /* OS discovered MTU, or 0 if unknown */
+
+ bool did_resolve_remote;
+
+# define SF_USE_IP_PKTINFO (1<<0)
+# define SF_TCP_NODELAY (1<<1)
+# define SF_PORT_SHARE (1<<2)
+# define SF_HOST_RANDOMIZE (1<<3)
+ unsigned int sockflags;
+
+ /* for stream sockets */
+ struct stream_buf stream_buf;
+ struct buffer stream_buf_data;
+ bool stream_reset;
+
+#ifdef ENABLE_HTTP_PROXY
+ /* HTTP proxy */
+ struct http_proxy_info *http_proxy;
+#endif
+
+#ifdef ENABLE_SOCKS
+ /* Socks proxy */
+ struct socks_proxy_info *socks_proxy;
+ struct link_socket_actual socks_relay; /* Socks UDP relay address */
+#endif
+
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS)
+ /* The OpenVPN server we will use the proxy to connect to */
+ const char *proxy_dest_host;
+ int proxy_dest_port;
+#endif
+
+#if PASSTOS_CAPABILITY
+ /* used to get/set TOS. */
+ uint8_t ptos;
+ bool ptos_defined;
+#endif
+
+#ifdef ENABLE_DEBUG
+ int gremlin; /* --gremlin bits */
+#endif
+};
+
+/*
+ * Some Posix/Win32 differences.
+ */
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#ifdef WIN32
+
+#define openvpn_close_socket(s) closesocket(s)
+
+int socket_recv_queue (struct link_socket *sock, int maxsize);
+
+int socket_send_queue (struct link_socket *sock,
+ struct buffer *buf,
+ const struct link_socket_actual *to);
+
+int socket_finalize (
+ SOCKET s,
+ struct overlapped_io *io,
+ struct buffer *buf,
+ struct link_socket_actual *from);
+
+#else
+
+#define openvpn_close_socket(s) close(s)
+
+#endif
+
+struct link_socket *link_socket_new (void);
+
+void socket_bind (socket_descriptor_t sd,
+ struct openvpn_sockaddr *local,
+ const char *prefix);
+
+int openvpn_connect (socket_descriptor_t sd,
+ struct openvpn_sockaddr *remote,
+ int connect_timeout,
+ volatile int *signal_received);
+
+/*
+ * Initialize link_socket object.
+ */
+
+void
+link_socket_init_phase1 (struct link_socket *sock,
+ const bool connection_profiles_defined,
+ const char *local_host,
+ int local_port,
+ const char *remote_host,
+ int remote_port,
+ int proto,
+ int mode,
+ const struct link_socket *accept_from,
+#ifdef ENABLE_HTTP_PROXY
+ struct http_proxy_info *http_proxy,
+#endif
+#ifdef ENABLE_SOCKS
+ struct socks_proxy_info *socks_proxy,
+#endif
+#ifdef ENABLE_DEBUG
+ int gremlin,
+#endif
+ bool bind_local,
+ bool remote_float,
+ int inetd,
+ struct link_socket_addr *lsa,
+ const char *ipchange_command,
+ const struct plugin_list *plugins,
+ int resolve_retry_seconds,
+ int connect_retry_seconds,
+ int connect_timeout,
+ int connect_retry_max,
+ int mtu_discover_type,
+ int rcvbuf,
+ int sndbuf,
+ unsigned int sockflags);
+
+void link_socket_init_phase2 (struct link_socket *sock,
+ const struct frame *frame,
+ volatile int *signal_received);
+
+void socket_adjust_frame_parameters (struct frame *frame, int proto);
+
+void frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto);
+
+void link_socket_close (struct link_socket *sock);
+
+void sd_close (socket_descriptor_t *sd);
+
+#define PS_SHOW_PORT_IF_DEFINED (1<<0)
+#define PS_SHOW_PORT (1<<1)
+#define PS_SHOW_PKTINFO (1<<2)
+#define PS_DONT_SHOW_ADDR (1<<3)
+
+const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
+ const char* separator,
+ const unsigned int flags,
+ struct gc_arena *gc);
+
+
+const char *print_sockaddr (const struct openvpn_sockaddr *addr,
+ struct gc_arena *gc);
+
+const char *print_link_socket_actual_ex (const struct link_socket_actual *act,
+ const char* separator,
+ const unsigned int flags,
+ struct gc_arena *gc);
+
+const char *print_link_socket_actual (const struct link_socket_actual *act,
+ struct gc_arena *gc);
+
+
+#define IA_EMPTY_IF_UNDEF (1<<0)
+#define IA_NET_ORDER (1<<1)
+const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
+
+#define SA_IP_PORT (1<<0)
+#define SA_SET_IF_NONZERO (1<<1)
+void setenv_sockaddr (struct env_set *es,
+ const char *name_prefix,
+ const struct openvpn_sockaddr *addr,
+ const bool flags);
+
+void setenv_in_addr_t (struct env_set *es,
+ const char *name_prefix,
+ in_addr_t addr,
+ const bool flags);
+
+void setenv_link_socket_actual (struct env_set *es,
+ const char *name_prefix,
+ const struct link_socket_actual *act,
+ const bool flags);
+
+void bad_address_length (int actual, int expected);
+
+in_addr_t link_socket_current_remote (const struct link_socket_info *info);
+
+void link_socket_connection_initiated (const struct buffer *buf,
+ struct link_socket_info *info,
+ const struct link_socket_actual *addr,
+ const char *common_name,
+ struct env_set *es);
+
+void link_socket_bad_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+ const struct link_socket_actual *from_addr);
+
+void link_socket_bad_outgoing_addr (void);
+
+void setenv_trusted (struct env_set *es, const struct link_socket_info *info);
+
+bool link_socket_update_flags (struct link_socket *ls, unsigned int sockflags);
+void link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf);
+
+/*
+ * Low-level functions
+ */
+
+/* return values of openvpn_inet_aton */
+#define OIA_HOSTNAME 0
+#define OIA_IP 1
+#define OIA_ERROR -1
+int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr);
+
+/* integrity validation on pulled options */
+bool ip_addr_dotted_quad_safe (const char *dotted_quad);
+bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn);
+bool mac_addr_safe (const char *mac_addr);
+
+socket_descriptor_t create_socket_tcp (void);
+
+socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
+ struct link_socket_actual *act,
+ const bool nowait);
+
+#if UNIX_SOCK_SUPPORT
+
+socket_descriptor_t create_socket_unix (void);
+
+void socket_bind_unix (socket_descriptor_t sd,
+ struct sockaddr_un *local,
+ const char *prefix);
+
+socket_descriptor_t socket_accept_unix (socket_descriptor_t sd,
+ struct sockaddr_un *remote);
+
+int socket_connect_unix (socket_descriptor_t sd,
+ struct sockaddr_un *remote);
+
+void sockaddr_unix_init (struct sockaddr_un *local, const char *path);
+
+const char *sockaddr_unix_name (const struct sockaddr_un *local, const char *null);
+
+void socket_delete_unix (const struct sockaddr_un *local);
+
+bool unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int *gid);
+
+#endif
+
+/*
+ * DNS resolution
+ */
+
+struct resolve_list {
+ int len;
+ in_addr_t data[16];
+};
+
+#define GETADDR_RESOLVE (1<<0)
+#define GETADDR_FATAL (1<<1)
+#define GETADDR_HOST_ORDER (1<<2)
+#define GETADDR_MENTION_RESOLVE_RETRY (1<<3)
+#define GETADDR_FATAL_ON_SIGNAL (1<<4)
+#define GETADDR_WARN_ON_SIGNAL (1<<5)
+#define GETADDR_MSG_VIRT_OUT (1<<6)
+#define GETADDR_TRY_ONCE (1<<7)
+#define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8)
+#define GETADDR_RANDOMIZE (1<<9)
+
+in_addr_t getaddr (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ bool *succeeded,
+ volatile int *signal_received);
+
+in_addr_t getaddr_multi (unsigned int flags,
+ const char *hostname,
+ int resolve_retry_seconds,
+ bool *succeeded,
+ volatile int *signal_received,
+ struct resolve_list *reslist);
+
+/*
+ * Transport protocol naming and other details.
+ */
+
+#define PROTO_UDPv4 0
+#define PROTO_TCPv4_SERVER 1
+#define PROTO_TCPv4_CLIENT 2
+#define PROTO_TCPv4 3
+#define PROTO_N 4
+
+int ascii2proto (const char* proto_name);
+const char *proto2ascii (int proto, bool display_form);
+const char *proto2ascii_all (struct gc_arena *gc);
+int proto_remote (int proto, bool remote);
+
+/*
+ * Overhead added to packets by various protocols.
+ */
+#define IPv4_UDP_HEADER_SIZE 28
+#define IPv4_TCP_HEADER_SIZE 40
+#define IPv6_UDP_HEADER_SIZE 40
+
+extern const int proto_overhead[];
+
+static inline int
+datagram_overhead (int proto)
+{
+ ASSERT (proto >= 0 && proto < PROTO_N);
+ return proto_overhead [proto];
+}
+
+/*
+ * Misc inline functions
+ */
+
+static inline bool
+legal_ipv4_port (int port)
+{
+ return port > 0 && port < 65536;
+}
+
+static inline int
+is_proto_tcp(const int p)
+{
+ return p > 0; /* depends on the definition of PROTO_x */
+}
+
+static inline bool
+link_socket_proto_connection_oriented (int proto)
+{
+ return proto == PROTO_TCPv4_SERVER || proto == PROTO_TCPv4_CLIENT;
+}
+
+static inline bool
+link_socket_connection_oriented (const struct link_socket *sock)
+{
+ if (sock)
+ return link_socket_proto_connection_oriented (sock->info.proto);
+ else
+ return false;
+}
+
+static inline bool
+addr_defined (const struct openvpn_sockaddr *addr)
+{
+ return addr->sa.sin_addr.s_addr != 0;
+}
+
+static inline bool
+link_socket_actual_defined (const struct link_socket_actual *act)
+{
+ return act && addr_defined (&act->dest);
+}
+
+static inline bool
+addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
+{
+ return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr;
+}
+
+static inline in_addr_t
+addr_host (const struct openvpn_sockaddr *s)
+{
+ return ntohl (s->sa.sin_addr.s_addr);
+}
+
+static inline bool
+addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
+{
+ return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr
+ && a1->sa.sin_port == a2->sa.sin_port;
+}
+
+static inline bool
+addr_match_proto (const struct openvpn_sockaddr *a1,
+ const struct openvpn_sockaddr *a2,
+ const int proto)
+{
+ return link_socket_proto_connection_oriented (proto)
+ ? addr_match (a1, a2)
+ : addr_port_match (a1, a2);
+}
+
+static inline bool
+link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2)
+{
+ return addr_port_match (&a1->dest, &a2->dest);
+}
+
+#if PORT_SHARE
+
+static inline bool
+socket_foreign_protocol_detected (const struct link_socket *sock)
+{
+ return link_socket_connection_oriented (sock)
+ && sock->stream_buf.port_share_state == PS_FOREIGN;
+}
+
+static inline const struct buffer *
+socket_foreign_protocol_head (const struct link_socket *sock)
+{
+ return &sock->stream_buf.buf;
+}
+
+static inline int
+socket_foreign_protocol_sd (const struct link_socket *sock)
+{
+ return sock->sd;
+}
+
+#endif
+
+static inline bool
+socket_connection_reset (const struct link_socket *sock, int status)
+{
+ if (link_socket_connection_oriented (sock))
+ {
+ if (sock->stream_reset || sock->stream_buf.error)
+ return true;
+ else if (status < 0)
+ {
+ const int err = openvpn_errno_socket ();
+#ifdef WIN32
+ return err == WSAECONNRESET || err == WSAECONNABORTED;
+#else
+ return err == ECONNRESET;
+#endif
+ }
+ }
+ return false;
+}
+
+static inline bool
+link_socket_verify_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+ const struct link_socket_actual *from_addr)
+{
+ if (buf->len > 0)
+ {
+ if (from_addr->dest.sa.sin_family != AF_INET)
+ return false;
+ if (!link_socket_actual_defined (from_addr))
+ return false;
+ if (info->remote_float || !addr_defined (&info->lsa->remote))
+ return true;
+ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
+ return true;
+ }
+ return false;
+}
+
+static inline void
+link_socket_get_outgoing_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+ struct link_socket_actual **act)
+{
+ if (buf->len > 0)
+ {
+ struct link_socket_addr *lsa = info->lsa;
+ if (link_socket_actual_defined (&lsa->actual))
+ *act = &lsa->actual;
+ else
+ {
+ link_socket_bad_outgoing_addr ();
+ buf->len = 0;
+ *act = NULL;
+ }
+ }
+}
+
+static inline void
+link_socket_set_outgoing_addr (const struct buffer *buf,
+ struct link_socket_info *info,
+ const struct link_socket_actual *act,
+ const char *common_name,
+ struct env_set *es)
+{
+ if (!buf || buf->len > 0)
+ {
+ struct link_socket_addr *lsa = info->lsa;
+ if (
+ /* new or changed address? */
+ (!info->connection_established
+ || !addr_match_proto (&act->dest, &lsa->actual.dest, info->proto))
+ /* address undef or address == remote or --float */
+ && (info->remote_float
+ || !addr_defined (&lsa->remote)
+ || addr_match_proto (&act->dest, &lsa->remote, info->proto))
+ )
+ {
+ link_socket_connection_initiated (buf, info, act, common_name, es);
+ }
+ }
+}
+
+/*
+ * Stream buffer handling -- stream_buf is a helper class
+ * to assist in the packetization of stream transport protocols
+ * such as TCP.
+ */
+
+void stream_buf_init (struct stream_buf *sb,
+ struct buffer *buf,
+ const unsigned int sockflags,
+ const int proto);
+
+void stream_buf_close (struct stream_buf* sb);
+bool stream_buf_added (struct stream_buf *sb, int length_added);
+
+static inline bool
+stream_buf_read_setup (struct link_socket* sock)
+{
+ bool stream_buf_read_setup_dowork (struct link_socket* sock);
+ if (link_socket_connection_oriented (sock))
+ return stream_buf_read_setup_dowork (sock);
+ else
+ return true;
+}
+
+/*
+ * Socket Read Routines
+ */
+
+int link_socket_read_tcp (struct link_socket *sock,
+ struct buffer *buf);
+
+#ifdef WIN32
+
+static inline int
+link_socket_read_udp_win32 (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *from)
+{
+ return socket_finalize (sock->sd, &sock->reads, buf, from);
+}
+
+#else
+
+int link_socket_read_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+ struct link_socket_actual *from);
+
+#endif
+
+/* read a TCP or UDP packet from link */
+static inline int
+link_socket_read (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+ struct link_socket_actual *from)
+{
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ int res;
+
+#ifdef WIN32
+ res = link_socket_read_udp_win32 (sock, buf, from);
+#else
+ res = link_socket_read_udp_posix (sock, buf, maxsize, from);
+#endif
+ return res;
+ }
+ else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+ /* from address was returned by accept */
+ from->dest.sa = sock->info.lsa->actual.dest.sa;
+ return link_socket_read_tcp (sock, buf);
+ }
+ else
+ {
+ ASSERT (0);
+ return -1; /* NOTREACHED */
+ }
+}
+
+/*
+ * Socket Write routines
+ */
+
+int link_socket_write_tcp (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to);
+
+#ifdef WIN32
+
+static inline int
+link_socket_write_win32 (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+ int err = 0;
+ int status = 0;
+ if (overlapped_io_active (&sock->writes))
+ {
+ status = socket_finalize (sock->sd, &sock->writes, NULL, NULL);
+ if (status < 0)
+ err = WSAGetLastError ();
+ }
+ socket_send_queue (sock, buf, to);
+ if (status < 0)
+ {
+ WSASetLastError (err);
+ return status;
+ }
+ else
+ return BLEN (buf);
+}
+
+#else
+
+static inline int
+link_socket_write_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+#if ENABLE_IP_PKTINFO
+ int link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to);
+
+ if (sock->sockflags & SF_USE_IP_PKTINFO)
+ return link_socket_write_udp_posix_sendmsg (sock, buf, to);
+ else
+#endif
+ return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
+ (struct sockaddr *) &to->dest.sa,
+ (socklen_t) sizeof (to->dest.sa));
+}
+
+static inline int
+link_socket_write_tcp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+ return send (sock->sd, BPTR (buf), BLEN (buf), MSG_NOSIGNAL);
+}
+
+#endif
+
+static inline int
+link_socket_write_udp (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+#ifdef WIN32
+ return link_socket_write_win32 (sock, buf, to);
+#else
+ return link_socket_write_udp_posix (sock, buf, to);
+#endif
+}
+
+/* write a TCP or UDP packet to link */
+static inline int
+link_socket_write (struct link_socket *sock,
+ struct buffer *buf,
+ struct link_socket_actual *to)
+{
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+ return link_socket_write_udp (sock, buf, to);
+ }
+ else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+ return link_socket_write_tcp (sock, buf, to);
+ }
+ else
+ {
+ ASSERT (0);
+ return -1; /* NOTREACHED */
+ }
+}
+
+#if PASSTOS_CAPABILITY
+
+/*
+ * Extract TOS bits. Assumes that ipbuf is a valid IPv4 packet.
+ */
+static inline void
+link_socket_extract_tos (struct link_socket *ls, const struct buffer *ipbuf)
+{
+ if (ls && ipbuf)
+ {
+ struct openvpn_iphdr *iph = (struct openvpn_iphdr *) BPTR (ipbuf);
+ ls->ptos = iph->tos;
+ ls->ptos_defined = true;
+ }
+}
+
+/*
+ * Set socket properties to reflect TOS bits which were extracted
+ * from tunnel packet.
+ */
+static inline void
+link_socket_set_tos (struct link_socket *ls)
+{
+ if (ls && ls->ptos_defined)
+ setsockopt (ls->sd, IPPROTO_IP, IP_TOS, &ls->ptos, sizeof (ls->ptos));
+}
+
+#endif
+
+/*
+ * Socket I/O wait functions
+ */
+
+static inline bool
+socket_read_residual (const struct link_socket *s)
+{
+ return s && s->stream_buf.residual_fully_formed;
+}
+
+static inline event_t
+socket_event_handle (const struct link_socket *s)
+{
+#ifdef WIN32
+ return &s->rw_handle;
+#else
+ return s->sd;
+#endif
+}
+
+event_t socket_listen_event_handle (struct link_socket *s);
+
+unsigned int
+socket_set (struct link_socket *s,
+ struct event_set *es,
+ unsigned int rwflags,
+ void *arg,
+ unsigned int *persistent);
+
+static inline void
+socket_set_listen_persistent (struct link_socket *s,
+ struct event_set *es,
+ void *arg)
+{
+ if (s && !s->listen_persistent_queued)
+ {
+ event_ctl (es, socket_listen_event_handle (s), EVENT_READ, arg);
+ s->listen_persistent_queued = true;
+ }
+}
+
+static inline void
+socket_reset_listen_persistent (struct link_socket *s)
+{
+#ifdef WIN32
+ reset_net_event_win32 (&s->listen_handle, s->sd);
+#endif
+}
+
+const char *socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc);
+
+#endif /* SOCKET_H */
diff --git a/socks.c b/socks.c
new file mode 100644
index 0000000..8287274
--- /dev/null
+++ b/socks.c
@@ -0,0 +1,551 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 2004-01-30: Added Socks5 proxy support, see RFC 1928
+ * (Christof Meerwald, http://cmeerw.org)
+ *
+ * 2010-10-10: Added Socks5 plain text authentication support (RFC 1929)
+ * (Pierre Bourdon <delroth@gmail.com>)
+ */
+
+#include "syshead.h"
+
+#ifdef ENABLE_SOCKS
+
+#include "common.h"
+#include "misc.h"
+#include "win32.h"
+#include "socket.h"
+#include "fdmisc.h"
+#include "misc.h"
+#include "proxy.h"
+
+#include "memdbg.h"
+
+#define UP_TYPE_SOCKS "SOCKS Proxy"
+
+void
+socks_adjust_frame_parameters (struct frame *frame, int proto)
+{
+ if (proto == PROTO_UDPv4)
+ frame_add_to_extra_link (frame, 10);
+}
+
+struct socks_proxy_info *
+socks_proxy_new (const char *server,
+ int port,
+ const char *authfile,
+ bool retry,
+ struct auto_proxy_info *auto_proxy_info)
+{
+ struct socks_proxy_info *p;
+
+ if (auto_proxy_info)
+ {
+ if (!server)
+ {
+ if (!auto_proxy_info->socks.server)
+ return NULL;
+
+ server = auto_proxy_info->socks.server;
+ port = auto_proxy_info->socks.port;
+ }
+ }
+
+ ALLOC_OBJ_CLEAR (p, struct socks_proxy_info);
+
+ ASSERT (server);
+ ASSERT (legal_ipv4_port (port));
+
+ strncpynt (p->server, server, sizeof (p->server));
+ p->port = port;
+
+ if (authfile)
+ strncpynt (p->authfile, authfile, sizeof (p->authfile));
+ else
+ p->authfile[0] = 0;
+
+ p->retry = retry;
+ p->defined = true;
+
+ return p;
+}
+
+void
+socks_proxy_close (struct socks_proxy_info *sp)
+{
+ free (sp);
+}
+
+static bool
+socks_username_password_auth (struct socks_proxy_info *p,
+ socket_descriptor_t sd,
+ volatile int *signal_received)
+{
+ char to_send[516];
+ char buf[2];
+ int len = 0;
+ const int timeout_sec = 5;
+ struct user_pass creds;
+ ssize_t size;
+
+ creds.defined = 0;
+ get_user_pass (&creds, p->authfile, UP_TYPE_SOCKS, GET_USER_PASS_MANAGEMENT);
+
+ if( !creds.username || (strlen(creds.username) > 255)
+ || !creds.password || (strlen(creds.password) > 255) ) {
+ msg (M_NONFATAL,
+ "SOCKS username and/or password exceeds 255 characters. "
+ "Authentication not possible.");
+ return false;
+ }
+ openvpn_snprintf (to_send, sizeof (to_send), "\x01%c%s%c%s", (int) strlen(creds.username),
+ creds.username, (int) strlen(creds.password), creds.password);
+ size = send (sd, to_send, strlen(to_send), MSG_NOSIGNAL);
+
+ if (size != strlen (to_send))
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port write failed on send()");
+ return false;
+ }
+
+ while (len < 2)
+ {
+ int status;
+ ssize_t size;
+ fd_set reads;
+ struct timeval tv;
+ char c;
+
+ FD_ZERO (&reads);
+ FD_SET (sd, &reads);
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, &reads, NULL, NULL, &tv);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ return false;
+
+ /* timeout? */
+ if (status == 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read timeout expired");
+ return false;
+ }
+
+ /* error */
+ if (status < 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read failed on select()");
+ return false;
+ }
+
+ /* read single char */
+ size = recv(sd, &c, 1, MSG_NOSIGNAL);
+
+ /* error? */
+ if (size != 1)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_username_password_auth: TCP port read failed on recv()");
+ return false;
+ }
+
+ /* store char in buffer */
+ buf[len++] = c;
+ }
+
+ /* VER = 5, SUCCESS = 0 --> auth success */
+ if (buf[0] != 5 && buf[1] != 0)
+ {
+ msg (D_LINK_ERRORS, "socks_username_password_auth: server refused the authentication");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+socks_handshake (struct socks_proxy_info *p,
+ socket_descriptor_t sd,
+ volatile int *signal_received)
+{
+ char buf[2];
+ int len = 0;
+ const int timeout_sec = 5;
+
+ /* VER = 5, NMETHODS = 2, METHODS = [0 (no auth), 2 (plain login)] */
+ const ssize_t size = send (sd, "\x05\x02\x00\x02", 4, MSG_NOSIGNAL);
+ if (size != 4)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_handshake: TCP port write failed on send()");
+ return false;
+ }
+
+ while (len < 2)
+ {
+ int status;
+ ssize_t size;
+ fd_set reads;
+ struct timeval tv;
+ char c;
+
+ FD_ZERO (&reads);
+ FD_SET (sd, &reads);
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, &reads, NULL, NULL, &tv);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ return false;
+
+ /* timeout? */
+ if (status == 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_handshake: TCP port read timeout expired");
+ return false;
+ }
+
+ /* error */
+ if (status < 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_handshake: TCP port read failed on select()");
+ return false;
+ }
+
+ /* read single char */
+ size = recv(sd, &c, 1, MSG_NOSIGNAL);
+
+ /* error? */
+ if (size != 1)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "socks_handshake: TCP port read failed on recv()");
+ return false;
+ }
+
+ /* store char in buffer */
+ buf[len++] = c;
+ }
+
+ /* VER == 5 */
+ if (buf[0] != '\x05')
+ {
+ msg (D_LINK_ERRORS, "socks_handshake: Socks proxy returned bad status");
+ return false;
+ }
+
+ /* select the appropriate authentication method */
+ switch (buf[1])
+ {
+ case 0: /* no authentication */
+ break;
+
+ case 2: /* login/password */
+ if (!p->authfile[0])
+ {
+ msg(D_LINK_ERRORS, "socks_handshake: server asked for username/login auth but we were "
+ "not provided any credentials");
+ return false;
+ }
+
+ if (!socks_username_password_auth(p, sd, signal_received))
+ return false;
+
+ break;
+
+ default: /* unknown auth method */
+ msg(D_LINK_ERRORS, "socks_handshake: unknown SOCKS auth method");
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+recv_socks_reply (socket_descriptor_t sd,
+ struct openvpn_sockaddr *addr,
+ volatile int *signal_received)
+{
+ char atyp = '\0';
+ int alen = 0;
+ int len = 0;
+ char buf[22];
+ const int timeout_sec = 5;
+
+ if (addr != NULL)
+ {
+ addr->sa.sin_family = AF_INET;
+ addr->sa.sin_addr.s_addr = htonl (INADDR_ANY);
+ addr->sa.sin_port = htons (0);
+ }
+
+ while (len < 4 + alen + 2)
+ {
+ int status;
+ ssize_t size;
+ fd_set reads;
+ struct timeval tv;
+ char c;
+
+ FD_ZERO (&reads);
+ FD_SET (sd, &reads);
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+
+ status = select (sd + 1, &reads, NULL, NULL, &tv);
+
+ get_signal (signal_received);
+ if (*signal_received)
+ return false;
+
+ /* timeout? */
+ if (status == 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_socks_reply: TCP port read timeout expired");
+ return false;
+ }
+
+ /* error */
+ if (status < 0)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_socks_reply: TCP port read failed on select()");
+ return false;
+ }
+
+ /* read single char */
+ size = recv(sd, &c, 1, MSG_NOSIGNAL);
+
+ /* error? */
+ if (size != 1)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "recv_socks_reply: TCP port read failed on recv()");
+ return false;
+ }
+
+ if (len == 3)
+ atyp = c;
+
+ if (len == 4)
+ {
+ switch (atyp)
+ {
+ case '\x01': /* IP V4 */
+ alen = 4;
+ break;
+
+ case '\x03': /* DOMAINNAME */
+ alen = (unsigned char) c;
+ break;
+
+ case '\x04': /* IP V6 */
+ alen = 16;
+ break;
+
+ default:
+ msg (D_LINK_ERRORS, "recv_socks_reply: Socks proxy returned bad address type");
+ return false;
+ }
+ }
+
+ /* store char in buffer */
+ if (len < (int)sizeof(buf))
+ buf[len] = c;
+ ++len;
+ }
+
+ /* VER == 5 && REP == 0 (succeeded) */
+ if (buf[0] != '\x05' || buf[1] != '\x00')
+ {
+ msg (D_LINK_ERRORS, "recv_socks_reply: Socks proxy returned bad reply");
+ return false;
+ }
+
+ /* ATYP == 1 (IP V4 address) */
+ if (atyp == '\x01' && addr != NULL)
+ {
+ memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr));
+ memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port));
+ }
+
+
+ return true;
+}
+
+void
+establish_socks_proxy_passthru (struct socks_proxy_info *p,
+ socket_descriptor_t sd, /* already open to proxy */
+ const char *host, /* openvpn server remote */
+ const int port, /* openvpn server port */
+ volatile int *signal_received)
+{
+ char buf[128];
+ size_t len;
+
+ if (!socks_handshake (p, sd, signal_received))
+ goto error;
+
+ /* format Socks CONNECT message */
+ buf[0] = '\x05'; /* VER = 5 */
+ buf[1] = '\x01'; /* CMD = 1 (CONNECT) */
+ buf[2] = '\x00'; /* RSV */
+ buf[3] = '\x03'; /* ATYP = 3 (DOMAINNAME) */
+
+ len = strlen(host);
+ len = (5 + len + 2 > sizeof(buf)) ? (sizeof(buf) - 5 - 2) : len;
+
+ buf[4] = (char) len;
+ memcpy(buf + 5, host, len);
+
+ buf[5 + len] = (char) (port >> 8);
+ buf[5 + len + 1] = (char) (port & 0xff);
+
+ {
+ const ssize_t size = send (sd, buf, 5 + len + 2, MSG_NOSIGNAL);
+ if ((int)size != 5 + (int)len + 2)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "establish_socks_proxy_passthru: TCP port write failed on send()");
+ goto error;
+ }
+ }
+
+ /* receive reply from Socks proxy and discard */
+ if (!recv_socks_reply (sd, NULL, signal_received))
+ goto error;
+
+ return;
+
+ error:
+ /* on error, should we exit or restart? */
+ if (!*signal_received)
+ *signal_received = (p->retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- socks error */
+ return;
+}
+
+void
+establish_socks_proxy_udpassoc (struct socks_proxy_info *p,
+ socket_descriptor_t ctrl_sd, /* already open to proxy */
+ socket_descriptor_t udp_sd,
+ struct openvpn_sockaddr *relay_addr,
+ volatile int *signal_received)
+{
+ if (!socks_handshake (p, ctrl_sd, signal_received))
+ goto error;
+
+ {
+ /* send Socks UDP ASSOCIATE message */
+ /* VER = 5, CMD = 3 (UDP ASSOCIATE), RSV = 0, ATYP = 1 (IP V4),
+ BND.ADDR = 0, BND.PORT = 0 */
+ const ssize_t size = send (ctrl_sd,
+ "\x05\x03\x00\x01\x00\x00\x00\x00\x00\x00",
+ 10, MSG_NOSIGNAL);
+ if (size != 10)
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "establish_socks_proxy_passthru: TCP port write failed on send()");
+ goto error;
+ }
+ }
+
+ /* receive reply from Socks proxy */
+ CLEAR (*relay_addr);
+ if (!recv_socks_reply (ctrl_sd, relay_addr, signal_received))
+ goto error;
+
+ return;
+
+ error:
+ /* on error, should we exit or restart? */
+ if (!*signal_received)
+ *signal_received = (p->retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- socks error */
+ return;
+}
+
+/*
+ * Remove the 10 byte socks5 header from an incoming
+ * UDP packet, setting *from to the source address.
+ *
+ * Run after UDP read.
+ */
+void
+socks_process_incoming_udp (struct buffer *buf,
+ struct link_socket_actual *from)
+{
+ int atyp;
+
+ if (BLEN (buf) < 10)
+ goto error;
+
+ buf_read_u16 (buf);
+ if (buf_read_u8 (buf) != 0)
+ goto error;
+
+ atyp = buf_read_u8 (buf);
+ if (atyp != 1) /* ATYP == 1 (IP V4) */
+ goto error;
+
+ buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr));
+ buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port));
+
+ return;
+
+ error:
+ buf->len = 0;
+}
+
+/*
+ * Add a 10 byte socks header prior to UDP write.
+ * *to is the destination address.
+ *
+ * Run before UDP write.
+ * Returns the size of the header.
+ */
+int
+socks_process_outgoing_udp (struct buffer *buf,
+ const struct link_socket_actual *to)
+{
+ /*
+ * Get a 10 byte subset buffer prepended to buf --
+ * we expect these bytes will be here because
+ * we allocated frame space in socks_adjust_frame_parameters.
+ */
+ struct buffer head = buf_sub (buf, 10, true);
+
+ /* crash if not enough headroom in buf */
+ ASSERT (buf_defined (&head));
+
+ buf_write_u16 (&head, 0); /* RSV = 0 */
+ buf_write_u8 (&head, 0); /* FRAG = 0 */
+ buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */
+ buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr));
+ buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port));
+
+ return 10;
+}
+
+#else
+static void dummy(void) {}
+#endif /* ENABLE_SOCKS */
diff --git a/socks.h b/socks.h
new file mode 100644
index 0000000..b748bb3
--- /dev/null
+++ b/socks.h
@@ -0,0 +1,78 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 2004-01-30: Added Socks5 proxy support
+ * (Christof Meerwald, http://cmeerw.org)
+ */
+
+#ifndef SOCKS_H
+#define SOCKS_H
+
+#ifdef ENABLE_SOCKS
+
+#include "buffer.h"
+
+struct openvpn_sockaddr;
+struct link_socket_actual;
+
+struct socks_proxy_info {
+ bool defined;
+ bool retry;
+
+ char server[128];
+ int port;
+ char authfile[256];
+};
+
+void socks_adjust_frame_parameters (struct frame *frame, int proto);
+
+struct socks_proxy_info *socks_proxy_new (const char *server,
+ int port,
+ const char *authfile,
+ bool retry,
+ struct auto_proxy_info *auto_proxy_info);
+
+void socks_proxy_close (struct socks_proxy_info *sp);
+
+void establish_socks_proxy_passthru (struct socks_proxy_info *p,
+ socket_descriptor_t sd, /* already open to proxy */
+ const char *host, /* openvpn server remote */
+ const int port, /* openvpn server port */
+ volatile int *signal_received);
+
+void establish_socks_proxy_udpassoc (struct socks_proxy_info *p,
+ socket_descriptor_t ctrl_sd, /* already open to proxy */
+ socket_descriptor_t udp_sd,
+ struct openvpn_sockaddr *relay_addr,
+ volatile int *signal_received);
+
+void socks_process_incoming_udp (struct buffer *buf,
+ struct link_socket_actual *from);
+
+int socks_process_outgoing_udp (struct buffer *buf,
+ const struct link_socket_actual *to);
+
+#endif
+#endif
diff --git a/ssl.c b/ssl.c
new file mode 100644
index 0000000..6d9a9fd
--- /dev/null
+++ b/ssl.c
@@ -0,0 +1,5385 @@
+/*
+ * 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-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * Additions for eurephia plugin done by:
+ * David Sommerseth <dazo@users.sourceforge.net> Copyright (C) 2008-2009
+ *
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * The routines in this file deal with dynamically negotiating
+ * the data channel HMAC and cipher keys through a TLS session.
+ *
+ * Both the TLS session and the data channel are multiplexed
+ * over the same TCP/UDP port.
+ */
+
+#include "syshead.h"
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#include "ssl.h"
+#include "error.h"
+#include "common.h"
+#include "integer.h"
+#include "socket.h"
+#include "misc.h"
+#include "fdmisc.h"
+#include "interval.h"
+#include "perf.h"
+#include "status.h"
+#include "gremlin.h"
+#include "pkcs11.h"
+#include "list.h"
+
+#ifdef WIN32
+#include "cryptoapi.h"
+#endif
+
+#include "memdbg.h"
+
+#ifndef ENABLE_OCC
+static const char ssl_default_options_string[] = "V0 UNDEF";
+#endif
+
+static inline const char *
+local_options_string (const struct tls_session *session)
+{
+#ifdef ENABLE_OCC
+ return session->opt->local_options;
+#else
+ return ssl_default_options_string;
+#endif
+}
+
+#ifdef MEASURE_TLS_HANDSHAKE_STATS
+
+static int tls_handshake_success; /* GLOBAL */
+static int tls_handshake_error; /* GLOBAL */
+static int tls_packets_generated; /* GLOBAL */
+static int tls_packets_sent; /* GLOBAL */
+
+#define INCR_SENT ++tls_packets_sent
+#define INCR_GENERATED ++tls_packets_generated
+#define INCR_SUCCESS ++tls_handshake_success
+#define INCR_ERROR ++tls_handshake_error
+
+void
+show_tls_performance_stats(void)
+{
+ msg (D_TLS_DEBUG_LOW, "TLS Handshakes, success=%f%% (good=%d, bad=%d), retransmits=%f%%",
+ (double) tls_handshake_success / (tls_handshake_success + tls_handshake_error) * 100.0,
+ tls_handshake_success, tls_handshake_error,
+ (double) (tls_packets_sent - tls_packets_generated) / tls_packets_generated * 100.0);
+}
+#else
+
+#define INCR_SENT
+#define INCR_GENERATED
+#define INCR_SUCCESS
+#define INCR_ERROR
+
+#endif
+
+#ifdef BIO_DEBUG
+
+#warning BIO_DEBUG defined
+
+static FILE *biofp; /* GLOBAL */
+static bool biofp_toggle; /* GLOBAL */
+static time_t biofp_last_open; /* GLOBAL */
+static const int biofp_reopen_interval = 600; /* GLOBAL */
+
+static void
+close_biofp()
+{
+ if (biofp)
+ {
+ ASSERT (!fclose (biofp));
+ biofp = NULL;
+ }
+}
+
+static void
+open_biofp()
+{
+ const time_t current = time (NULL);
+ const pid_t pid = getpid ();
+
+ if (biofp_last_open + biofp_reopen_interval < current)
+ close_biofp();
+ if (!biofp)
+ {
+ char fn[256];
+ openvpn_snprintf(fn, sizeof(fn), "bio/%d-%d.log", pid, biofp_toggle);
+ biofp = fopen (fn, "w");
+ ASSERT (biofp);
+ biofp_last_open = time (NULL);
+ biofp_toggle ^= 1;
+ }
+}
+
+static void
+bio_debug_data (const char *mode, BIO *bio, const uint8_t *buf, int len, const char *desc)
+{
+ struct gc_arena gc = gc_new ();
+ if (len > 0)
+ {
+ open_biofp();
+ fprintf(biofp, "BIO_%s %s time=" time_format " bio=" ptr_format " len=%d data=%s\n",
+ mode, desc, time (NULL), (ptr_type)bio, len, format_hex (buf, len, 0, &gc));
+ fflush (biofp);
+ }
+ gc_free (&gc);
+}
+
+static void
+bio_debug_oc (const char *mode, BIO *bio)
+{
+ open_biofp();
+ fprintf(biofp, "BIO %s time=" time_format " bio=" ptr_format "\n",
+ mode, time (NULL), (ptr_type)bio);
+ fflush (biofp);
+}
+
+#endif
+
+/*
+ * Max number of bytes we will add
+ * for data structures common to both
+ * data and control channel packets.
+ * (opcode only).
+ */
+void
+tls_adjust_frame_parameters(struct frame *frame)
+{
+ frame_add_to_extra_frame (frame, 1); /* space for opcode */
+}
+
+/*
+ * Max number of bytes we will add
+ * to control channel packet.
+ */
+static void
+tls_init_control_channel_frame_parameters(const struct frame *data_channel_frame,
+ struct frame *frame)
+{
+ /*
+ * frame->extra_frame is already initialized with tls_auth buffer requirements,
+ * if --tls-auth is enabled.
+ */
+
+ /* inherit link MTU and extra_link from data channel */
+ frame->link_mtu = data_channel_frame->link_mtu;
+ frame->extra_link = data_channel_frame->extra_link;
+
+ /* set extra_frame */
+ tls_adjust_frame_parameters (frame);
+ reliable_ack_adjust_frame_parameters (frame, CONTROL_SEND_ACK_MAX);
+ frame_add_to_extra_frame (frame, SID_SIZE + sizeof (packet_id_type));
+
+ /* set dynamic link MTU to minimum value */
+ frame_set_mtu_dynamic (frame, 0, SET_MTU_TUN);
+}
+
+/*
+ * Allocate space in SSL objects
+ * in which to store a struct tls_session
+ * pointer back to parent.
+ */
+
+static int mydata_index; /* GLOBAL */
+
+static void
+ssl_set_mydata_index ()
+{
+ mydata_index = SSL_get_ex_new_index (0, "struct session *", NULL, NULL, NULL);
+ ASSERT (mydata_index >= 0);
+}
+
+void
+init_ssl_lib ()
+{
+ SSL_library_init ();
+ SSL_load_error_strings ();
+ OpenSSL_add_all_algorithms ();
+
+ init_crypto_lib();
+
+ /*
+ * If you build the OpenSSL library and OpenVPN with
+ * CRYPTO_MDEBUG, you will get a listing of OpenSSL
+ * memory leaks on program termination.
+ */
+#ifdef CRYPTO_MDEBUG
+ CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
+#endif
+
+ ssl_set_mydata_index ();
+}
+
+void
+free_ssl_lib ()
+{
+#ifdef CRYPTO_MDEBUG
+ FILE* fp = fopen ("sdlog", "w");
+ ASSERT (fp);
+ CRYPTO_mem_leaks_fp (fp);
+ fclose (fp);
+#endif
+
+ uninit_crypto_lib ();
+ EVP_cleanup ();
+ ERR_free_strings ();
+}
+
+/*
+ * OpenSSL library calls pem_password_callback if the
+ * private key is protected by a password.
+ */
+
+static struct user_pass passbuf; /* GLOBAL */
+
+void
+pem_password_setup (const char *auth_file)
+{
+ if (!strlen (passbuf.password))
+ get_user_pass (&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_PASSWORD_ONLY);
+}
+
+int
+pem_password_callback (char *buf, int size, int rwflag, void *u)
+{
+ if (buf)
+ {
+ /* prompt for password even if --askpass wasn't specified */
+ pem_password_setup (NULL);
+ strncpynt (buf, passbuf.password, size);
+ purge_user_pass (&passbuf, false);
+
+ return strlen (buf);
+ }
+ return 0;
+}
+
+/*
+ * Auth username/password handling
+ */
+
+static bool auth_user_pass_enabled; /* GLOBAL */
+static struct user_pass auth_user_pass; /* GLOBAL */
+
+#ifdef ENABLE_CLIENT_CR
+static char *auth_challenge; /* GLOBAL */
+#endif
+
+void
+auth_user_pass_setup (const char *auth_file)
+{
+ auth_user_pass_enabled = true;
+ if (!auth_user_pass.defined)
+ {
+#if AUTO_USERID
+ get_user_pass_auto_userid (&auth_user_pass, auth_file);
+#elif defined(ENABLE_CLIENT_CR)
+ get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
+#else
+ get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
+#endif
+ }
+}
+
+/*
+ * Disable password caching
+ */
+void
+ssl_set_auth_nocache (void)
+{
+ passbuf.nocache = true;
+ auth_user_pass.nocache = true;
+}
+
+/*
+ * Forget private key password AND auth-user-pass username/password.
+ */
+void
+ssl_purge_auth (void)
+{
+#ifdef USE_PKCS11
+ pkcs11_logout ();
+#endif
+ purge_user_pass (&passbuf, true);
+ purge_user_pass (&auth_user_pass, true);
+#ifdef ENABLE_CLIENT_CR
+ ssl_purge_auth_challenge();
+#endif
+}
+
+#ifdef ENABLE_CLIENT_CR
+
+void
+ssl_purge_auth_challenge (void)
+{
+ free (auth_challenge);
+ auth_challenge = NULL;
+}
+
+void
+ssl_put_auth_challenge (const char *cr_str)
+{
+ ssl_purge_auth_challenge();
+ auth_challenge = string_alloc(cr_str, NULL);
+}
+
+#endif
+
+/*
+ * OpenSSL callback to get a temporary RSA key, mostly
+ * used for export ciphers.
+ */
+static RSA *
+tmp_rsa_cb (SSL * s, int is_export, int keylength)
+{
+ static RSA *rsa_tmp = NULL;
+ if (rsa_tmp == NULL)
+ {
+ msg (D_HANDSHAKE, "Generating temp (%d bit) RSA key", keylength);
+ rsa_tmp = RSA_generate_key (keylength, RSA_F4, NULL, NULL);
+ }
+ return (rsa_tmp);
+}
+
+/*
+ * Cert hash functions
+ */
+static void
+cert_hash_remember (struct tls_session *session, const int error_depth, const unsigned char *sha1_hash)
+{
+ if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
+ {
+ if (!session->cert_hash_set)
+ ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
+ if (!session->cert_hash_set->ch[error_depth])
+ ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
+ {
+ struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
+ memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH);
+ }
+ }
+}
+
+#if 0
+static void
+cert_hash_print (const struct cert_hash_set *chs, int msglevel)
+{
+ struct gc_arena gc = gc_new ();
+ msg (msglevel, "CERT_HASH");
+ if (chs)
+ {
+ int i;
+ for (i = 0; i < MAX_CERT_DEPTH; ++i)
+ {
+ const struct cert_hash *ch = chs->ch[i];
+ if (ch)
+ msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
+ }
+ }
+ gc_free (&gc);
+}
+#endif
+
+static void
+cert_hash_free (struct cert_hash_set *chs)
+{
+ if (chs)
+ {
+ int i;
+ for (i = 0; i < MAX_CERT_DEPTH; ++i)
+ free (chs->ch[i]);
+ free (chs);
+ }
+}
+
+static bool
+cert_hash_compare (const struct cert_hash_set *chs1, const struct cert_hash_set *chs2)
+{
+ if (chs1 && chs2)
+ {
+ int i;
+ for (i = 0; i < MAX_CERT_DEPTH; ++i)
+ {
+ const struct cert_hash *ch1 = chs1->ch[i];
+ const struct cert_hash *ch2 = chs2->ch[i];
+
+ if (!ch1 && !ch2)
+ continue;
+ else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, SHA_DIGEST_LENGTH))
+ continue;
+ else
+ return false;
+ }
+ return true;
+ }
+ else if (!chs1 && !chs2)
+ return true;
+ else
+ return false;
+}
+
+static struct cert_hash_set *
+cert_hash_copy (const struct cert_hash_set *chs)
+{
+ struct cert_hash_set *dest = NULL;
+ if (chs)
+ {
+ int i;
+ ALLOC_OBJ_CLEAR (dest, struct cert_hash_set);
+ for (i = 0; i < MAX_CERT_DEPTH; ++i)
+ {
+ const struct cert_hash *ch = chs->ch[i];
+ if (ch)
+ {
+ ALLOC_OBJ (dest->ch[i], struct cert_hash);
+ memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH);
+ }
+ }
+ }
+ return dest;
+}
+
+/*
+ * Extract a field from an X509 subject name.
+ *
+ * Example:
+ *
+ * /C=US/ST=CO/L=Denver/O=ORG/CN=First-CN/CN=Test-CA/Email=jim@yonan.net
+ *
+ * The common name is 'Test-CA'
+ *
+ * Return true on success, false on error (insufficient buffer size in 'out'
+ * to contain result is grounds for error).
+ */
+static bool
+extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int size)
+{
+ int lastpos = -1;
+ int tmp = -1;
+ X509_NAME_ENTRY *x509ne = 0;
+ ASN1_STRING *asn1 = 0;
+ unsigned char *buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
+ int nid = OBJ_txt2nid((char *)field_name);
+
+ ASSERT (size > 0);
+ *out = '\0';
+ do {
+ lastpos = tmp;
+ tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos);
+ } while (tmp > -1);
+
+ /* Nothing found */
+ if (lastpos == -1)
+ return false;
+
+ x509ne = X509_NAME_get_entry(x509, lastpos);
+ if (!x509ne)
+ return false;
+
+ asn1 = X509_NAME_ENTRY_get_data(x509ne);
+ if (!asn1)
+ return false;
+ tmp = ASN1_STRING_to_UTF8(&buf, asn1);
+ if (tmp <= 0)
+ return false;
+
+ strncpynt(out, (char *)buf, size);
+
+ {
+ const bool ret = (strlen ((char *)buf) < size);
+ OPENSSL_free (buf);
+ return ret;
+ }
+}
+
+/*
+ * Save X509 fields to environment, using the naming convention:
+ *
+ * X509_{cert_depth}_{name}={value}
+ */
+static void
+setenv_x509 (struct env_set *es, const int error_depth, X509_NAME *x509)
+{
+ int i, n;
+ int fn_nid;
+ ASN1_OBJECT *fn;
+ ASN1_STRING *val;
+ X509_NAME_ENTRY *ent;
+ const char *objbuf;
+ unsigned char *buf;
+ char *name_expand;
+ size_t name_expand_size;
+
+ n = X509_NAME_entry_count (x509);
+ for (i = 0; i < n; ++i)
+ {
+ ent = X509_NAME_get_entry (x509, i);
+ if (!ent)
+ continue;
+ fn = X509_NAME_ENTRY_get_object (ent);
+ if (!fn)
+ continue;
+ val = X509_NAME_ENTRY_get_data (ent);
+ if (!val)
+ continue;
+ fn_nid = OBJ_obj2nid (fn);
+ if (fn_nid == NID_undef)
+ continue;
+ objbuf = OBJ_nid2sn (fn_nid);
+ if (!objbuf)
+ continue;
+ buf = (unsigned char *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */
+ if (ASN1_STRING_to_UTF8 (&buf, val) <= 0)
+ continue;
+ name_expand_size = 64 + strlen (objbuf);
+ name_expand = (char *) malloc (name_expand_size);
+ check_malloc_return (name_expand);
+ openvpn_snprintf (name_expand, name_expand_size, "X509_%d_%s", error_depth, objbuf);
+ string_mod (name_expand, CC_PRINT, CC_CRLF, '_');
+ string_mod ((char*)buf, CC_PRINT, CC_CRLF, '_');
+ setenv_str (es, name_expand, (char*)buf);
+ free (name_expand);
+ OPENSSL_free (buf);
+ }
+}
+
+static void
+setenv_untrusted (struct tls_session *session)
+{
+ setenv_link_socket_actual (session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT);
+}
+
+static void
+set_common_name (struct tls_session *session, const char *common_name)
+{
+ if (session->common_name)
+ {
+ free (session->common_name);
+ session->common_name = NULL;
+#ifdef ENABLE_PF
+ session->common_name_hashval = 0;
+#endif
+ }
+ if (common_name)
+ {
+ session->common_name = string_alloc (common_name, NULL);
+#ifdef ENABLE_PF
+ {
+ const uint32_t len = (uint32_t) strlen (common_name);
+ if (len)
+ session->common_name_hashval = hash_func ((const uint8_t*)common_name, len+1, 0);
+ else
+ session->common_name_hashval = 0;
+ }
+#endif
+ }
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+bool verify_cert_eku (X509 *x509, const char * const expected_oid) {
+
+ EXTENDED_KEY_USAGE *eku = NULL;
+ bool fFound = false;
+
+ if ((eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i (x509, NID_ext_key_usage, NULL, NULL)) == NULL) {
+ msg (D_HANDSHAKE, "Certificate does not have extended key usage extension");
+ }
+ else {
+ int i;
+
+ msg (D_HANDSHAKE, "Validating certificate extended key usage");
+ for(i = 0; !fFound && i < sk_ASN1_OBJECT_num (eku); i++) {
+ ASN1_OBJECT *oid = sk_ASN1_OBJECT_value (eku, i);
+ char szOid[1024];
+
+ if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 0) != -1) {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (str) %s, expects %s", szOid, expected_oid);
+ if (!strcmp (expected_oid, szOid)) {
+ fFound = true;
+ }
+ }
+ if (!fFound && OBJ_obj2txt (szOid, sizeof (szOid), oid, 1) != -1) {
+ msg (D_HANDSHAKE, "++ Certificate has EKU (oid) %s, expects %s", szOid, expected_oid);
+ if (!strcmp (expected_oid, szOid)) {
+ fFound = true;
+ }
+ }
+ }
+ }
+
+ if (eku != NULL) {
+ sk_ASN1_OBJECT_pop_free (eku, ASN1_OBJECT_free);
+ }
+
+ return fFound;
+}
+
+bool verify_cert_ku (X509 *x509, const unsigned * const expected_ku, int expected_len) {
+
+ ASN1_BIT_STRING *ku = NULL;
+ bool fFound = false;
+
+ if ((ku = (ASN1_BIT_STRING *)X509_get_ext_d2i (x509, NID_key_usage, NULL, NULL)) == NULL) {
+ msg (D_HANDSHAKE, "Certificate does not have key usage extension");
+ }
+ else {
+ unsigned nku = 0;
+ int i;
+ for (i=0;i<8;i++) {
+ if (ASN1_BIT_STRING_get_bit (ku, i)) {
+ nku |= 1<<(7-i);
+ }
+ }
+
+ /*
+ * Fixup if no LSB bits
+ */
+ if ((nku & 0xff) == 0) {
+ nku >>= 8;
+ }
+
+ msg (D_HANDSHAKE, "Validating certificate key usage");
+ for (i=0;!fFound && i<expected_len;i++) {
+ if (expected_ku[i] != 0) {
+ msg (D_HANDSHAKE, "++ Certificate has key usage %04x, expects %04x", nku, expected_ku[i]);
+
+ if (nku == expected_ku[i]) {
+ fFound = true;
+ }
+ }
+ }
+ }
+
+ if (ku != NULL) {
+ ASN1_BIT_STRING_free (ku);
+ }
+
+ return fFound;
+}
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
+/*
+ * nsCertType checking
+ */
+
+#define verify_nsCertType(x, usage) (((x)->ex_flags & EXFLAG_NSCERT) && ((x)->ex_nscert & (usage)))
+
+static const char *
+print_nsCertType (int type)
+{
+ switch (type)
+ {
+ case NS_SSL_SERVER:
+ return "SERVER";
+ case NS_SSL_CLIENT:
+ return "CLIENT";
+ default:
+ return "?";
+ }
+}
+
+static void
+string_mod_sslname (char *str, const unsigned int restrictive_flags, const unsigned int ssl_flags)
+{
+ if (ssl_flags & SSLF_NO_NAME_REMAPPING)
+ string_mod (str, CC_PRINT, CC_CRLF, '_');
+ else
+ string_mod (str, restrictive_flags, 0, '_');
+}
+
+/* Get peer cert and store it in pem format in a temporary file
+ * in tmp_dir
+ */
+
+const char *
+get_peer_cert(X509_STORE_CTX *ctx, const char *tmp_dir, struct gc_arena *gc)
+{
+ X509 *peercert;
+ FILE *peercert_file;
+ const char *peercert_filename="";
+
+ if(!tmp_dir)
+ return NULL;
+
+ /* get peer cert */
+ peercert = X509_STORE_CTX_get_current_cert(ctx);
+ if(!peercert)
+ {
+ msg (M_ERR, "Unable to get peer certificate from current context");
+ return NULL;
+ }
+
+ /* create tmp file to store peer cert */
+ peercert_filename = create_temp_file (tmp_dir, "pcf", gc);
+
+ /* write peer-cert in tmp-file */
+ peercert_file = fopen(peercert_filename, "w+");
+ if(!peercert_file)
+ {
+ msg (M_ERR, "Failed to open temporary file : %s", peercert_filename);
+ return NULL;
+ }
+ if(PEM_write_X509(peercert_file,peercert)<0)
+ {
+ msg (M_ERR, "Failed to write peer certificate in PEM format");
+ fclose(peercert_file);
+ return NULL;
+ }
+
+ fclose(peercert_file);
+ return peercert_filename;
+}
+
+char * x509_username_field; /* GLOBAL */
+
+/*
+ * Our verify callback function -- check
+ * that an incoming peer certificate is good.
+ */
+
+static int
+verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
+{
+ char *subject = NULL;
+ char envname[64];
+ char common_name[TLS_USERNAME_LEN];
+ SSL *ssl;
+ struct tls_session *session;
+ const struct tls_options *opt;
+ const int max_depth = MAX_CERT_DEPTH;
+ struct argv argv = argv_new ();
+
+ /* get the tls_session pointer */
+ ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ ASSERT (ssl);
+ session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index);
+ ASSERT (session);
+ opt = session->opt;
+ ASSERT (opt);
+
+ session->verified = false;
+
+ /* get the X509 name */
+ subject = X509_NAME_oneline (X509_get_subject_name (ctx->current_cert), NULL, 0);
+ if (!subject)
+ {
+ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, could not extract X509 subject string from certificate", ctx->error_depth);
+ goto err;
+ }
+
+ /* Save X509 fields in environment */
+ setenv_x509 (opt->es, ctx->error_depth, X509_get_subject_name (ctx->current_cert));
+
+ /* enforce character class restrictions in X509 name */
+ string_mod_sslname (subject, X509_NAME_CHAR_CLASS, opt->ssl_flags);
+ string_replace_leading (subject, '-', '_');
+
+ /* extract the username (default is CN) */
+ if (!extract_x509_field_ssl (X509_get_subject_name (ctx->current_cert), x509_username_field, common_name, sizeof(common_name)))
+ {
+ if (!ctx->error_depth)
+ {
+ msg (D_TLS_ERRORS, "VERIFY ERROR: could not extract %s from X509 subject string ('%s') -- note that the username length is limited to %d characters",
+ x509_username_field,
+ subject,
+ TLS_USERNAME_LEN);
+ goto err;
+ }
+ }
+
+
+ string_mod_sslname (common_name, COMMON_NAME_CHAR_CLASS, opt->ssl_flags);
+
+ cert_hash_remember (session, ctx->error_depth, ctx->current_cert->sha1_hash);
+
+#if 0 /* print some debugging info */
+ {
+ struct gc_arena gc = gc_new ();
+ msg (M_INFO, "LOCAL OPT[%d]: %s", ctx->error_depth, opt->local_options);
+ msg (M_INFO, "X509[%d]: %s", ctx->error_depth, subject);
+ msg (M_INFO, "SHA1[%d]: %s", ctx->error_depth, format_hex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, &gc));
+ gc_free (&gc);
+ }
+#endif
+
+ /* did peer present cert which was signed our root cert? */
+ if (!preverify_ok)
+ {
+ /* Remote site specified a certificate, but it's not correct */
+ msg (D_TLS_ERRORS, "VERIFY ERROR: depth=%d, error=%s: %s",
+ ctx->error_depth, X509_verify_cert_error_string (ctx->error), subject);
+ goto err; /* Reject connection */
+ }
+
+ /* warn if cert chain is too deep */
+ if (ctx->error_depth >= max_depth)
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Convoluted certificate chain detected with depth [%d] greater than %d", ctx->error_depth, max_depth);
+ goto err; /* Reject connection */
+ }
+
+ /* save common name in session object */
+ if (ctx->error_depth == 0)
+ set_common_name (session, common_name);
+
+ /* export subject name string as environmental variable */
+ session->verify_maxlevel = max_int (session->verify_maxlevel, ctx->error_depth);
+ openvpn_snprintf (envname, sizeof(envname), "tls_id_%d", ctx->error_depth);
+ setenv_str (opt->es, envname, subject);
+
+#ifdef ENABLE_EUREPHIA
+ /* export X509 cert SHA1 fingerprint */
+ {
+ struct gc_arena gc = gc_new ();
+ openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", ctx->error_depth);
+ setenv_str (opt->es, envname,
+ format_hex_ex(ctx->current_cert->sha1_hash, SHA_DIGEST_LENGTH, 0, 1, ":", &gc));
+ gc_free(&gc);
+ }
+#endif
+#if 0
+ /* export common name string as environmental variable */
+ openvpn_snprintf (envname, sizeof(envname), "tls_common_name_%d", ctx->error_depth);
+ setenv_str (opt->es, envname, common_name);
+#endif
+
+ /* export serial number as environmental variable */
+ {
+ BIO *bio = NULL;
+ char serial[100];
+ int n1, n2;
+
+ CLEAR (serial);
+ if ((bio = BIO_new (BIO_s_mem ())) == NULL)
+ {
+ msg (M_WARN, "CALLBACK: Cannot create BIO (for tls_serial_%d)", ctx->error_depth);
+ }
+ else
+ {
+ /* "prints" the serial number onto the BIO and read it back */
+ if ( ! ( ( (n1 = i2a_ASN1_INTEGER(bio, X509_get_serialNumber (ctx->current_cert))) >= 0 ) &&
+ ( (n2 = BIO_read (bio, serial, sizeof (serial)-1)) >= 0 ) &&
+ ( n1 == n2 ) ) )
+ {
+ msg (M_WARN, "CALLBACK: Error reading/writing BIO (for tls_serial_%d)", ctx->error_depth);
+ CLEAR (serial); /* empty string */
+ }
+
+ openvpn_snprintf (envname, sizeof(envname), "tls_serial_%d", ctx->error_depth);
+ setenv_str (opt->es, envname, serial);
+ BIO_free(bio);
+ }
+ }
+
+ /* export current untrusted IP */
+ setenv_untrusted (session);
+
+ /* verify certificate nsCertType */
+ if (opt->ns_cert_type && ctx->error_depth == 0)
+ {
+ if (verify_nsCertType (ctx->current_cert, opt->ns_cert_type))
+ {
+ msg (D_HANDSHAKE, "VERIFY OK: nsCertType=%s",
+ print_nsCertType (opt->ns_cert_type));
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY nsCertType ERROR: %s, require nsCertType=%s",
+ subject, print_nsCertType (opt->ns_cert_type));
+ goto err; /* Reject connection */
+ }
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+
+ /* verify certificate ku */
+ if (opt->remote_cert_ku[0] != 0 && ctx->error_depth == 0)
+ {
+ if (verify_cert_ku (ctx->current_cert, opt->remote_cert_ku, MAX_PARMS))
+ {
+ msg (D_HANDSHAKE, "VERIFY KU OK");
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY KU ERROR");
+ goto err; /* Reject connection */
+ }
+ }
+
+ /* verify certificate eku */
+ if (opt->remote_cert_eku != NULL && ctx->error_depth == 0)
+ {
+ if (verify_cert_eku (ctx->current_cert, opt->remote_cert_eku))
+ {
+ msg (D_HANDSHAKE, "VERIFY EKU OK");
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY EKU ERROR");
+ goto err; /* Reject connection */
+ }
+ }
+
+#endif /* OPENSSL_VERSION_NUMBER */
+
+ /* verify X509 name or common name against --tls-remote */
+ if (opt->verify_x509name && strlen (opt->verify_x509name) > 0 && ctx->error_depth == 0)
+ {
+ if (strcmp (opt->verify_x509name, subject) == 0
+ || strncmp (opt->verify_x509name, common_name, strlen (opt->verify_x509name)) == 0)
+ msg (D_HANDSHAKE, "VERIFY X509NAME OK: %s", subject);
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY X509NAME ERROR: %s, must be %s",
+ subject, opt->verify_x509name);
+ goto err; /* Reject connection */
+ }
+ }
+
+ /* call --tls-verify plug-in(s) */
+ if (plugin_defined (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY))
+ {
+ int ret;
+
+ argv_printf (&argv, "%d %s",
+ ctx->error_depth,
+ subject);
+
+ ret = plugin_call (opt->plugins, OPENVPN_PLUGIN_TLS_VERIFY, &argv, NULL, opt->es);
+
+ if (ret == OPENVPN_PLUGIN_FUNC_SUCCESS)
+ {
+ msg (D_HANDSHAKE, "VERIFY PLUGIN OK: depth=%d, %s",
+ ctx->error_depth, subject);
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY PLUGIN ERROR: depth=%d, %s",
+ ctx->error_depth, subject);
+ goto err; /* Reject connection */
+ }
+ }
+
+ /* run --tls-verify script */
+ if (opt->verify_command)
+ {
+ const char *tmp_file = NULL;
+ struct gc_arena gc;
+ int ret;
+
+ setenv_str (opt->es, "script_type", "tls-verify");
+
+ if (opt->verify_export_cert)
+ {
+ gc = gc_new();
+ if ((tmp_file=get_peer_cert(ctx, opt->verify_export_cert,&gc)))
+ {
+ setenv_str(opt->es, "peer_cert", tmp_file);
+ }
+ }
+
+ argv_printf (&argv, "%sc %d %s",
+ opt->verify_command,
+ ctx->error_depth,
+ subject);
+ argv_msg_prefix (D_TLS_DEBUG, &argv, "TLS: executing verify command");
+ ret = openvpn_run_script (&argv, opt->es, 0, "--tls-verify script");
+
+ if (opt->verify_export_cert)
+ {
+ if (tmp_file)
+ delete_file(tmp_file);
+ gc_free(&gc);
+ }
+
+ if (ret)
+ {
+ msg (D_HANDSHAKE, "VERIFY SCRIPT OK: depth=%d, %s",
+ ctx->error_depth, subject);
+ }
+ else
+ {
+ msg (D_HANDSHAKE, "VERIFY SCRIPT ERROR: depth=%d, %s",
+ ctx->error_depth, subject);
+ goto err; /* Reject connection */
+ }
+ }
+
+ /* check peer cert against CRL */
+ if (opt->crl_file)
+ {
+ X509_CRL *crl=NULL;
+ X509_REVOKED *revoked;
+ BIO *in=NULL;
+ int n,i,retval = 0;
+
+ in=BIO_new(BIO_s_file());
+
+ if (in == NULL) {
+ msg (M_ERR, "CRL: BIO err");
+ goto end;
+ }
+ if (BIO_read_filename(in, opt->crl_file) <= 0) {
+ msg (M_ERR, "CRL: cannot read: %s", opt->crl_file);
+ goto end;
+ }
+ crl=PEM_read_bio_X509_CRL(in,NULL,NULL,NULL);
+ if (crl == NULL) {
+ msg (M_ERR, "CRL: cannot read CRL from file %s", opt->crl_file);
+ goto end;
+ }
+
+ if (X509_NAME_cmp(X509_CRL_get_issuer(crl), X509_get_issuer_name(ctx->current_cert)) != 0) {
+ msg (M_WARN, "CRL: CRL %s is from a different issuer than the issuer of certificate %s", opt->crl_file, subject);
+ retval = 1;
+ goto end;
+ }
+
+ n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
+
+ for (i = 0; i < n; i++) {
+ revoked = (X509_REVOKED *)sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
+ if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(ctx->current_cert)) == 0) {
+ msg (D_HANDSHAKE, "CRL CHECK FAILED: %s is REVOKED",subject);
+ goto end;
+ }
+ }
+
+ retval = 1;
+ msg (D_HANDSHAKE, "CRL CHECK OK: %s",subject);
+
+ end:
+
+ BIO_free(in);
+ if (crl)
+ X509_CRL_free (crl);
+ if (!retval)
+ goto err;
+ }
+
+ msg (D_HANDSHAKE, "VERIFY OK: depth=%d, %s", ctx->error_depth, subject);
+
+ session->verified = true;
+ OPENSSL_free (subject);
+ argv_reset (&argv);
+ return 1; /* Accept connection */
+
+ err:
+ ERR_clear_error ();
+ OPENSSL_free (subject);
+ argv_reset (&argv);
+ return 0; /* Reject connection */
+}
+
+void
+tls_set_common_name (struct tls_multi *multi, const char *common_name)
+{
+ if (multi)
+ set_common_name (&multi->session[TM_ACTIVE], common_name);
+}
+
+const char *
+tls_common_name (const struct tls_multi *multi, const bool null)
+{
+ const char *ret = NULL;
+ if (multi)
+ ret = multi->session[TM_ACTIVE].common_name;
+ if (ret && strlen (ret))
+ return ret;
+ else if (null)
+ return NULL;
+ else
+ return "UNDEF";
+}
+
+void
+tls_lock_common_name (struct tls_multi *multi)
+{
+ const char *cn = multi->session[TM_ACTIVE].common_name;
+ if (cn && !multi->locked_cn)
+ multi->locked_cn = string_alloc (cn, NULL);
+}
+
+void
+tls_lock_cert_hash_set (struct tls_multi *multi)
+{
+ const struct cert_hash_set *chs = multi->session[TM_ACTIVE].cert_hash_set;
+ if (chs && !multi->locked_cert_hash_set)
+ multi->locked_cert_hash_set = cert_hash_copy (chs);
+}
+
+static bool
+tls_lock_username (struct tls_multi *multi, const char *username)
+{
+ if (multi->locked_username)
+ {
+ if (!username || strcmp (username, multi->locked_username))
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: username attempted to change from '%s' to '%s' -- tunnel disabled",
+ multi->locked_username,
+ np(username));
+
+ /* disable the tunnel */
+ tls_deauthenticate (multi);
+ return false;
+ }
+ }
+ else
+ {
+ if (username)
+ multi->locked_username = string_alloc (username, NULL);
+ }
+ return true;
+}
+
+#ifdef ENABLE_DEF_AUTH
+/* key_state_test_auth_control_file return values,
+ NOTE: acf_merge indexing depends on these values */
+#define ACF_UNDEFINED 0
+#define ACF_SUCCEEDED 1
+#define ACF_DISABLED 2
+#define ACF_FAILED 3
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+static void
+man_def_auth_set_client_reason (struct tls_multi *multi, const char *client_reason)
+{
+ if (multi->client_reason)
+ {
+ free (multi->client_reason);
+ multi->client_reason = NULL;
+ }
+ if (client_reason && strlen (client_reason))
+ multi->client_reason = string_alloc (client_reason, NULL);
+}
+
+static inline unsigned int
+man_def_auth_test (const struct key_state *ks)
+{
+ if (management_enable_def_auth (management))
+ return ks->mda_status;
+ else
+ return ACF_DISABLED;
+}
+#endif
+
+#ifdef PLUGIN_DEF_AUTH
+
+/*
+ * auth_control_file functions
+ */
+
+static void
+key_state_rm_auth_control_file (struct key_state *ks)
+{
+ if (ks && ks->auth_control_file)
+ {
+ delete_file (ks->auth_control_file);
+ free (ks->auth_control_file);
+ ks->auth_control_file = NULL;
+ }
+}
+
+static void
+key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options *opt)
+{
+ struct gc_arena gc = gc_new ();
+ const char *acf;
+
+ key_state_rm_auth_control_file (ks);
+ acf = create_temp_file (opt->tmp_dir, "acf", &gc);
+ if( acf ) {
+ ks->auth_control_file = string_alloc (acf, NULL);
+ setenv_str (opt->es, "auth_control_file", ks->auth_control_file);
+ } /* FIXME: Should have better error handling? */
+ gc_free (&gc);
+}
+
+static unsigned int
+key_state_test_auth_control_file (struct key_state *ks)
+{
+ if (ks && ks->auth_control_file)
+ {
+ unsigned int ret = ks->auth_control_status;
+ if (ret == ACF_UNDEFINED)
+ {
+ FILE *fp = fopen (ks->auth_control_file, "r");
+ if (fp)
+ {
+ const int c = fgetc (fp);
+ if (c == '1')
+ ret = ACF_SUCCEEDED;
+ else if (c == '0')
+ ret = ACF_FAILED;
+ fclose (fp);
+ ks->auth_control_status = ret;
+ }
+ }
+ return ret;
+ }
+ return ACF_DISABLED;
+}
+
+#endif
+
+/*
+ * Return current session authentication state. Return
+ * value is TLS_AUTHENTICATION_x.
+ */
+
+int
+tls_authentication_status (struct tls_multi *multi, const int latency)
+{
+ bool deferred = false;
+ bool success = false;
+ bool active = false;
+
+#ifdef ENABLE_DEF_AUTH
+ static const unsigned char acf_merge[] =
+ {
+ ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_UNDEFINED */
+ ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_SUCCEEDED */
+ ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_DISABLED */
+ ACF_FAILED, /* s1=ACF_UNDEFINED s2=ACF_FAILED */
+ ACF_UNDEFINED, /* s1=ACF_SUCCEEDED s2=ACF_UNDEFINED */
+ ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_SUCCEEDED */
+ ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_DISABLED */
+ ACF_FAILED, /* s1=ACF_SUCCEEDED s2=ACF_FAILED */
+ ACF_UNDEFINED, /* s1=ACF_DISABLED s2=ACF_UNDEFINED */
+ ACF_SUCCEEDED, /* s1=ACF_DISABLED s2=ACF_SUCCEEDED */
+ ACF_DISABLED, /* s1=ACF_DISABLED s2=ACF_DISABLED */
+ ACF_FAILED, /* s1=ACF_DISABLED s2=ACF_FAILED */
+ ACF_FAILED, /* s1=ACF_FAILED s2=ACF_UNDEFINED */
+ ACF_FAILED, /* s1=ACF_FAILED s2=ACF_SUCCEEDED */
+ ACF_FAILED, /* s1=ACF_FAILED s2=ACF_DISABLED */
+ ACF_FAILED /* s1=ACF_FAILED s2=ACF_FAILED */
+ };
+#endif
+
+ if (multi)
+ {
+ int i;
+
+#ifdef ENABLE_DEF_AUTH
+ if (latency && multi->tas_last && multi->tas_last + latency >= now)
+ return TLS_AUTHENTICATION_UNDEFINED;
+ multi->tas_last = now;
+#endif
+
+ for (i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = multi->key_scan[i];
+ if (DECRYPT_KEY_ENABLED (multi, ks))
+ {
+ active = true;
+ if (ks->authenticated)
+ {
+#ifdef ENABLE_DEF_AUTH
+ unsigned int s1 = ACF_DISABLED;
+ unsigned int s2 = ACF_DISABLED;
+#ifdef PLUGIN_DEF_AUTH
+ s1 = key_state_test_auth_control_file (ks);
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+ s2 = man_def_auth_test (ks);
+#endif
+ ASSERT (s1 < 4 && s2 < 4);
+ switch (acf_merge[(s1<<2) + s2])
+ {
+ case ACF_SUCCEEDED:
+ case ACF_DISABLED:
+ success = true;
+ ks->auth_deferred = false;
+ break;
+ case ACF_UNDEFINED:
+ if (now < ks->auth_deferred_expire)
+ deferred = true;
+ break;
+ case ACF_FAILED:
+ ks->authenticated = false;
+ break;
+ default:
+ ASSERT (0);
+ }
+#else
+ success = true;
+#endif
+ }
+ }
+ }
+ }
+
+#if 0
+ dmsg (D_TLS_ERRORS, "TAS: a=%d s=%d d=%d", active, success, deferred);
+#endif
+
+ if (success)
+ return TLS_AUTHENTICATION_SUCCEEDED;
+ else if (!active || deferred)
+ return TLS_AUTHENTICATION_DEFERRED;
+ else
+ return TLS_AUTHENTICATION_FAILED;
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+/*
+ * For deferred auth, this is where the management interface calls (on server)
+ * to indicate auth failure/success.
+ */
+bool
+tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason)
+{
+ bool ret = false;
+ if (multi)
+ {
+ int i;
+ man_def_auth_set_client_reason (multi, client_reason);
+ for (i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = multi->key_scan[i];
+ if (ks->mda_key_id == mda_key_id)
+ {
+ ks->mda_status = auth ? ACF_SUCCEEDED : ACF_FAILED;
+ ret = true;
+ }
+ }
+ }
+ return ret;
+}
+#endif
+
+void
+tls_deauthenticate (struct tls_multi *multi)
+{
+ if (multi)
+ {
+ int i, j;
+ for (i = 0; i < TM_SIZE; ++i)
+ for (j = 0; j < KS_SIZE; ++j)
+ multi->session[i].key[j].authenticated = false;
+ }
+}
+
+/*
+ * Print debugging information on SSL/TLS session negotiation.
+ */
+static void
+info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
+{
+ if (where & SSL_CB_LOOP)
+ {
+ dmsg (D_HANDSHAKE_VERBOSE, "SSL state (%s): %s",
+ where & SSL_ST_CONNECT ? "connect" :
+ where & SSL_ST_ACCEPT ? "accept" :
+ "undefined", SSL_state_string_long (s));
+ }
+ else if (where & SSL_CB_ALERT)
+ {
+ dmsg (D_HANDSHAKE_VERBOSE, "SSL alert (%s): %s: %s",
+ where & SSL_CB_READ ? "read" : "write",
+ SSL_alert_type_string_long (ret),
+ SSL_alert_desc_string_long (ret));
+ }
+}
+
+#if ENABLE_INLINE_FILES
+
+static int
+use_inline_load_verify_locations (SSL_CTX *ctx, const char *ca_string)
+{
+ X509_STORE *store = NULL;
+ X509* cert = NULL;
+ BIO *in = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)ca_string, -1);
+ if (!in)
+ goto err;
+
+ for (;;)
+ {
+ if (!PEM_read_bio_X509 (in, &cert, 0, NULL))
+ {
+ ret = 1;
+ break;
+ }
+ if (!cert)
+ break;
+
+ store = SSL_CTX_get_cert_store (ctx);
+ if (!store)
+ break;
+
+ if (!X509_STORE_add_cert (store, cert))
+ break;
+
+ if (cert)
+ {
+ X509_free (cert);
+ cert = NULL;
+ }
+ }
+
+ err:
+ if (cert)
+ X509_free (cert);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+static int
+xname_cmp(const X509_NAME * const *a, const X509_NAME * const *b)
+{
+ return(X509_NAME_cmp(*a,*b));
+}
+
+static STACK_OF(X509_NAME) *
+use_inline_load_client_CA_file (SSL_CTX *ctx, const char *ca_string)
+{
+ BIO *in = NULL;
+ X509 *x = NULL;
+ X509_NAME *xn = NULL;
+ STACK_OF(X509_NAME) *ret = NULL, *sk;
+
+ sk=sk_X509_NAME_new(xname_cmp);
+
+ in = BIO_new_mem_buf ((char *)ca_string, -1);
+ if (!in)
+ goto err;
+
+ if ((sk == NULL) || (in == NULL))
+ goto err;
+
+ for (;;)
+ {
+ if (PEM_read_bio_X509(in,&x,NULL,NULL) == NULL)
+ break;
+ if (ret == NULL)
+ {
+ ret = sk_X509_NAME_new_null();
+ if (ret == NULL)
+ goto err;
+ }
+ if ((xn=X509_get_subject_name(x)) == NULL) goto err;
+ /* check for duplicates */
+ xn=X509_NAME_dup(xn);
+ if (xn == NULL) goto err;
+ if (sk_X509_NAME_find(sk,xn) >= 0)
+ X509_NAME_free(xn);
+ else
+ {
+ sk_X509_NAME_push(sk,xn);
+ sk_X509_NAME_push(ret,xn);
+ }
+ }
+
+ if (0)
+ {
+ err:
+ if (ret != NULL) sk_X509_NAME_pop_free(ret,X509_NAME_free);
+ ret=NULL;
+ }
+ if (sk != NULL) sk_X509_NAME_free(sk);
+ if (in != NULL) BIO_free(in);
+ if (x != NULL) X509_free(x);
+ if (ret != NULL)
+ ERR_clear_error();
+ return(ret);
+}
+
+static int
+use_inline_certificate_file (SSL_CTX *ctx, const char *cert_string)
+{
+ BIO *in = NULL;
+ X509 *x = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)cert_string, -1);
+ if (!in)
+ goto end;
+
+ x = PEM_read_bio_X509 (in,
+ NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata);
+ if (!x)
+ goto end;
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ end:
+ if (x)
+ X509_free (x);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+static int
+use_inline_PrivateKey_file (SSL_CTX *ctx, const char *key_string)
+{
+ BIO *in = NULL;
+ EVP_PKEY *pkey = NULL;
+ int ret = 0;
+
+ in = BIO_new_mem_buf ((char *)key_string, -1);
+ if (!in)
+ goto end;
+
+ pkey = PEM_read_bio_PrivateKey (in,
+ NULL,
+ ctx->default_passwd_callback,
+ ctx->default_passwd_callback_userdata);
+ if (!pkey)
+ goto end;
+
+ ret = SSL_CTX_use_PrivateKey (ctx, pkey);
+
+ end:
+ if (pkey)
+ EVP_PKEY_free (pkey);
+ if (in)
+ BIO_free (in);
+ return ret;
+}
+
+#endif
+
+/*
+ * Initialize SSL context.
+ * All files are in PEM format.
+ */
+SSL_CTX *
+init_ssl (const struct options *options)
+{
+ SSL_CTX *ctx = NULL;
+ DH *dh;
+ BIO *bio;
+ bool using_cert_file = false;
+
+ ERR_clear_error ();
+
+ if (options->tls_server)
+ {
+ ctx = SSL_CTX_new (TLSv1_server_method ());
+ if (ctx == NULL)
+ msg (M_SSLERR, "SSL_CTX_new TLSv1_server_method");
+
+ SSL_CTX_set_tmp_rsa_callback (ctx, tmp_rsa_cb);
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->dh_file, INLINE_FILE_TAG) && options->dh_file_inline)
+ {
+ if (!(bio = BIO_new_mem_buf ((char *)options->dh_file_inline, -1)))
+ msg (M_SSLERR, "Cannot open memory BIO for inline DH parameters");
+ }
+ else
+#endif
+ {
+ /* Get Diffie Hellman Parameters */
+ if (!(bio = BIO_new_file (options->dh_file, "r")))
+ msg (M_SSLERR, "Cannot open %s for DH parameters", options->dh_file);
+ }
+
+ dh = PEM_read_bio_DHparams (bio, NULL, NULL, NULL);
+ BIO_free (bio);
+ if (!dh)
+ msg (M_SSLERR, "Cannot load DH parameters from %s", options->dh_file);
+ if (!SSL_CTX_set_tmp_dh (ctx, dh))
+ msg (M_SSLERR, "SSL_CTX_set_tmp_dh");
+ msg (D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with %d bit key",
+ 8 * DH_size (dh));
+ DH_free (dh);
+ }
+ else /* if client */
+ {
+ ctx = SSL_CTX_new (TLSv1_client_method ());
+ if (ctx == NULL)
+ msg (M_SSLERR, "SSL_CTX_new TLSv1_client_method");
+ }
+
+ /* Set SSL options */
+ SSL_CTX_set_session_cache_mode (ctx, SSL_SESS_CACHE_OFF);
+ SSL_CTX_set_options (ctx, SSL_OP_SINGLE_DH_USE);
+
+ /* Set callback for getting password from user to decrypt private key */
+ SSL_CTX_set_default_passwd_cb (ctx, pem_password_callback);
+
+ if (options->pkcs12_file)
+ {
+ /* Use PKCS #12 file for key, cert and CA certs */
+
+ FILE *fp;
+ EVP_PKEY *pkey;
+ X509 *cert;
+ STACK_OF(X509) *ca = NULL;
+ PKCS12 *p12=NULL;
+ int i;
+ char password[256];
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->pkcs12_file, INLINE_FILE_TAG) && options->pkcs12_file_inline)
+ {
+ BIO *b64 = BIO_new (BIO_f_base64());
+ BIO *bio = BIO_new_mem_buf ((void *)options->pkcs12_file_inline, (int)strlen(options->pkcs12_file_inline));
+ ASSERT(b64 && bio);
+ BIO_push (b64, bio);
+ p12 = d2i_PKCS12_bio(b64, NULL);
+ if (!p12)
+ msg (M_SSLERR, "Error reading inline PKCS#12 file");
+ BIO_free (b64);
+ BIO_free (bio);
+ }
+ else
+#endif
+ {
+ /* Load the PKCS #12 file */
+ if (!(fp = fopen(options->pkcs12_file, "rb")))
+ msg (M_SSLERR, "Error opening file %s", options->pkcs12_file);
+ p12 = d2i_PKCS12_fp(fp, NULL);
+ fclose (fp);
+ if (!p12)
+ msg (M_SSLERR, "Error reading PKCS#12 file %s", options->pkcs12_file);
+ }
+
+ /* Parse the PKCS #12 file */
+ if (!PKCS12_parse(p12, "", &pkey, &cert, &ca))
+ {
+ pem_password_callback (password, sizeof(password) - 1, 0, NULL);
+ /* Reparse the PKCS #12 file with password */
+ ca = NULL;
+ if (!PKCS12_parse(p12, password, &pkey, &cert, &ca))
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (management && (ERR_GET_REASON (ERR_peek_error()) == PKCS12_R_MAC_VERIFY_FAILURE))
+ management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
+#endif
+ PKCS12_free(p12);
+ msg (M_INFO, "OpenSSL ERROR code: %d", (ERR_GET_REASON (ERR_peek_error()))); // fixme
+ goto err;
+ }
+ }
+ PKCS12_free(p12);
+
+ /* Load Certificate */
+ if (!SSL_CTX_use_certificate (ctx, cert))
+ msg (M_SSLERR, "Cannot use certificate");
+
+ /* Load Private Key */
+ if (!SSL_CTX_use_PrivateKey (ctx, pkey))
+ msg (M_SSLERR, "Cannot use private key");
+ warn_if_group_others_accessible (options->pkcs12_file);
+
+ /* Check Private Key */
+ if (!SSL_CTX_check_private_key (ctx))
+ msg (M_SSLERR, "Private key does not match the certificate");
+
+ /* Set Certificate Verification chain */
+ if (!options->ca_file)
+ {
+ if (ca && sk_X509_num(ca))
+ {
+ for (i = 0; i < sk_X509_num(ca); i++)
+ {
+ if (!X509_STORE_add_cert(ctx->cert_store,sk_X509_value(ca, i)))
+ msg (M_SSLERR, "Cannot add certificate to certificate chain (X509_STORE_add_cert)");
+ if (!SSL_CTX_add_client_CA(ctx, sk_X509_value(ca, i)))
+ msg (M_SSLERR, "Cannot add certificate to client CA list (SSL_CTX_add_client_CA)");
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Use seperate PEM files for key, cert and CA certs */
+
+#ifdef ENABLE_PKCS11
+ if (options->pkcs11_providers[0])
+ {
+ /* Load Certificate and Private Key */
+ if (!SSL_CTX_use_pkcs11 (ctx, options->pkcs11_id_management, options->pkcs11_id))
+ {
+ msg (M_WARN, "Cannot load certificate \"%s\" using PKCS#11 interface", options->pkcs11_id);
+ goto err;
+ }
+ }
+ else
+#endif
+
+#ifdef WIN32
+ if (options->cryptoapi_cert)
+ {
+ /* Load Certificate and Private Key */
+ if (!SSL_CTX_use_CryptoAPI_certificate (ctx, options->cryptoapi_cert))
+ msg (M_SSLERR, "Cannot load certificate \"%s\" from Microsoft Certificate Store",
+ options->cryptoapi_cert);
+ }
+ else
+#endif
+ {
+ /* Load Certificate */
+ if (options->cert_file)
+ {
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->cert_file, INLINE_FILE_TAG) && options->cert_file_inline)
+ {
+ if (!use_inline_certificate_file (ctx, options->cert_file_inline))
+ msg (M_SSLERR, "Cannot load inline certificate file");
+ }
+ else
+#endif
+ {
+ if (!SSL_CTX_use_certificate_file (ctx, options->cert_file, SSL_FILETYPE_PEM))
+ msg (M_SSLERR, "Cannot load certificate file %s", options->cert_file);
+ using_cert_file = true;
+ }
+ }
+
+ /* Load Private Key */
+ if (options->priv_key_file)
+ {
+ int status;
+
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->priv_key_file, INLINE_FILE_TAG) && options->priv_key_file_inline)
+ {
+ status = use_inline_PrivateKey_file (ctx, options->priv_key_file_inline);
+ }
+ else
+#endif
+ {
+ status = SSL_CTX_use_PrivateKey_file (ctx, options->priv_key_file, SSL_FILETYPE_PEM);
+ }
+ if (!status)
+ {
+#ifdef ENABLE_MANAGEMENT
+ if (management && (ERR_GET_REASON (ERR_peek_error()) == EVP_R_BAD_DECRYPT))
+ management_auth_failure (management, UP_TYPE_PRIVATE_KEY, NULL);
+#endif
+ msg (M_WARN|M_SSL, "Cannot load private key file %s", options->priv_key_file);
+ goto err;
+ }
+ warn_if_group_others_accessible (options->priv_key_file);
+
+ /* Check Private Key */
+ if (!SSL_CTX_check_private_key (ctx))
+ msg (M_SSLERR, "Private key does not match the certificate");
+ }
+ }
+ }
+
+ if (options->ca_file || options->ca_path)
+ {
+ int status;
+
+#if ENABLE_INLINE_FILES
+ if (options->ca_file && !strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
+ {
+ status = use_inline_load_verify_locations (ctx, options->ca_file_inline);
+ }
+ else
+#endif
+ {
+ /* Load CA file for verifying peer supplied certificate */
+ status = SSL_CTX_load_verify_locations (ctx, options->ca_file, options->ca_path);
+ }
+
+ if (!status)
+ msg (M_SSLERR, "Cannot load CA certificate file %s path %s (SSL_CTX_load_verify_locations)", options->ca_file, options->ca_path);
+
+ /* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */
+ if (options->ca_path) {
+ X509_STORE *store = SSL_CTX_get_cert_store(ctx);
+
+ if (store)
+ {
+ X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if (!X509_LOOKUP_add_dir(lookup, options->ca_path, X509_FILETYPE_PEM))
+ X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
+ else
+ msg(M_WARN, "WARNING: experimental option --capath %s", options->ca_path);
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+#else
+ msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath");
+#endif
+ }
+ else
+ msg(M_SSLERR, "Cannot get certificate store (SSL_CTX_get_cert_store)");
+ }
+
+ /* Load names of CAs from file and use it as a client CA list */
+ if (options->ca_file) {
+ STACK_OF(X509_NAME) *cert_names = NULL;
+#if ENABLE_INLINE_FILES
+ if (!strcmp (options->ca_file, INLINE_FILE_TAG) && options->ca_file_inline)
+ {
+ cert_names = use_inline_load_client_CA_file (ctx, options->ca_file_inline);
+ }
+ else
+#endif
+ {
+ cert_names = SSL_load_client_CA_file (options->ca_file);
+ }
+ if (!cert_names)
+ msg (M_SSLERR, "Cannot load CA certificate file %s (SSL_load_client_CA_file)", options->ca_file);
+ SSL_CTX_set_client_CA_list (ctx, cert_names);
+ }
+ }
+
+ /* Enable the use of certificate chains */
+ if (using_cert_file)
+ {
+ if (!SSL_CTX_use_certificate_chain_file (ctx, options->cert_file))
+ msg (M_SSLERR, "Cannot load certificate chain file %s (SSL_use_certificate_chain_file)", options->cert_file);
+ }
+
+ /* Require peer certificate verification */
+#if P2MP_SERVER
+ if (options->ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)
+ {
+ msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION --client-cert-not-required may accept clients which do not present a certificate");
+ }
+ else
+#endif
+ {
+#ifdef ENABLE_X509ALTUSERNAME
+ x509_username_field = (char *) options->x509_username_field;
+#else
+ x509_username_field = X509_USERNAME_FIELD_DEFAULT;
+#endif
+ SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ verify_callback);
+ }
+
+ /* Connection information callback */
+ SSL_CTX_set_info_callback (ctx, info_callback);
+
+ /* Allowable ciphers */
+ if (options->cipher_list)
+ {
+ if (!SSL_CTX_set_cipher_list (ctx, options->cipher_list))
+ msg (M_SSLERR, "Problem with cipher list: %s", options->cipher_list);
+ }
+
+ ERR_clear_error ();
+
+ return ctx;
+
+ err:
+ ERR_clear_error ();
+ if (ctx)
+ SSL_CTX_free (ctx);
+ return NULL;
+}
+
+/*
+ * Print a one line summary of SSL/TLS session handshake.
+ */
+static void
+print_details (SSL * c_ssl, const char *prefix)
+{
+ const SSL_CIPHER *ciph;
+ X509 *cert;
+ char s1[256];
+ char s2[256];
+
+ s1[0] = s2[0] = 0;
+ ciph = SSL_get_current_cipher (c_ssl);
+ openvpn_snprintf (s1, sizeof (s1), "%s %s, cipher %s %s",
+ prefix,
+ SSL_get_version (c_ssl),
+ SSL_CIPHER_get_version (ciph),
+ SSL_CIPHER_get_name (ciph));
+ cert = SSL_get_peer_certificate (c_ssl);
+ if (cert != NULL)
+ {
+ EVP_PKEY *pkey = X509_get_pubkey (cert);
+ if (pkey != NULL)
+ {
+ if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != NULL
+ && pkey->pkey.rsa->n != NULL)
+ {
+ openvpn_snprintf (s2, sizeof (s2), ", %d bit RSA",
+ BN_num_bits (pkey->pkey.rsa->n));
+ }
+ else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != NULL
+ && pkey->pkey.dsa->p != NULL)
+ {
+ openvpn_snprintf (s2, sizeof (s2), ", %d bit DSA",
+ BN_num_bits (pkey->pkey.dsa->p));
+ }
+ EVP_PKEY_free (pkey);
+ }
+ X509_free (cert);
+ }
+ /* The SSL API does not allow us to look at temporary RSA/DH keys,
+ * otherwise we should print their lengths too */
+ msg (D_HANDSHAKE, "%s%s", s1, s2);
+}
+
+/*
+ * Show the TLS ciphers that are available for us to use
+ * in the OpenSSL library.
+ */
+void
+show_available_tls_ciphers ()
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+ const char *cipher_name;
+ int priority = 0;
+
+ ctx = SSL_CTX_new (TLSv1_method ());
+ if (!ctx)
+ msg (M_SSLERR, "Cannot create SSL_CTX object");
+ ssl = SSL_new (ctx);
+ if (!ssl)
+ msg (M_SSLERR, "Cannot create SSL object");
+
+ printf ("Available TLS Ciphers,\n");
+ printf ("listed in order of preference:\n\n");
+ while ((cipher_name = SSL_get_cipher_list (ssl, priority++)))
+ printf ("%s\n", cipher_name);
+ printf ("\n");
+
+ SSL_free (ssl);
+ SSL_CTX_free (ctx);
+}
+
+/*
+ * The OpenSSL library has a notion of preference in TLS
+ * ciphers. Higher preference == more secure.
+ * Return the highest preference cipher.
+ */
+void
+get_highest_preference_tls_cipher (char *buf, int size)
+{
+ SSL_CTX *ctx;
+ SSL *ssl;
+ const char *cipher_name;
+
+ ctx = SSL_CTX_new (TLSv1_method ());
+ if (!ctx)
+ msg (M_SSLERR, "Cannot create SSL_CTX object");
+ ssl = SSL_new (ctx);
+ if (!ssl)
+ msg (M_SSLERR, "Cannot create SSL object");
+
+ cipher_name = SSL_get_cipher_list (ssl, 0);
+ strncpynt (buf, cipher_name, size);
+
+ SSL_free (ssl);
+ SSL_CTX_free (ctx);
+}
+
+/*
+ * Map internal constants to ascii names.
+ */
+static const char *
+state_name (int state)
+{
+ switch (state)
+ {
+ case S_UNDEF:
+ return "S_UNDEF";
+ case S_INITIAL:
+ return "S_INITIAL";
+ case S_PRE_START:
+ return "S_PRE_START";
+ case S_START:
+ return "S_START";
+ case S_SENT_KEY:
+ return "S_SENT_KEY";
+ case S_GOT_KEY:
+ return "S_GOT_KEY";
+ case S_ACTIVE:
+ return "S_ACTIVE";
+ case S_NORMAL_OP:
+ return "S_NORMAL_OP";
+ case S_ERROR:
+ return "S_ERROR";
+ default:
+ return "S_???";
+ }
+}
+
+static const char *
+packet_opcode_name (int op)
+{
+ switch (op)
+ {
+ case P_CONTROL_HARD_RESET_CLIENT_V1:
+ return "P_CONTROL_HARD_RESET_CLIENT_V1";
+ case P_CONTROL_HARD_RESET_SERVER_V1:
+ return "P_CONTROL_HARD_RESET_SERVER_V1";
+ case P_CONTROL_HARD_RESET_CLIENT_V2:
+ return "P_CONTROL_HARD_RESET_CLIENT_V2";
+ case P_CONTROL_HARD_RESET_SERVER_V2:
+ return "P_CONTROL_HARD_RESET_SERVER_V2";
+ case P_CONTROL_SOFT_RESET_V1:
+ return "P_CONTROL_SOFT_RESET_V1";
+ case P_CONTROL_V1:
+ return "P_CONTROL_V1";
+ case P_ACK_V1:
+ return "P_ACK_V1";
+ case P_DATA_V1:
+ return "P_DATA_V1";
+ default:
+ return "P_???";
+ }
+}
+
+static const char *
+session_index_name (int index)
+{
+ switch (index)
+ {
+ case TM_ACTIVE:
+ return "TM_ACTIVE";
+ case TM_UNTRUSTED:
+ return "TM_UNTRUSTED";
+ case TM_LAME_DUCK:
+ return "TM_LAME_DUCK";
+ default:
+ return "TM_???";
+ }
+}
+
+/*
+ * For debugging.
+ */
+static const char *
+print_key_id (struct tls_multi *multi, struct gc_arena *gc)
+{
+ int i;
+ struct buffer out = alloc_buf_gc (256, gc);
+
+ for (i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = multi->key_scan[i];
+ buf_printf (&out, " [key#%d state=%s id=%d sid=%s]", i,
+ state_name (ks->state), ks->key_id,
+ session_id_print (&ks->session_id_remote, gc));
+ }
+
+ return BSTR (&out);
+}
+
+/*
+ * Given a key_method, return true if op
+ * represents the required form of hard_reset.
+ *
+ * If key_method = 0, return true if any
+ * form of hard reset is used.
+ */
+static bool
+is_hard_reset (int op, int key_method)
+{
+ if (!key_method || key_method == 1)
+ if (op == P_CONTROL_HARD_RESET_CLIENT_V1 || op == P_CONTROL_HARD_RESET_SERVER_V1)
+ return true;
+
+ if (!key_method || key_method >= 2)
+ if (op == P_CONTROL_HARD_RESET_CLIENT_V2 || op == P_CONTROL_HARD_RESET_SERVER_V2)
+ return true;
+
+ return false;
+}
+
+/*
+ * OpenVPN's interface to SSL/TLS authentication,
+ * encryption, and decryption is exclusively
+ * through "memory BIOs".
+ */
+static BIO *
+getbio (BIO_METHOD * type, const char *desc)
+{
+ BIO *ret;
+ ret = BIO_new (type);
+ if (!ret)
+ msg (M_SSLERR, "Error creating %s BIO", desc);
+ return ret;
+}
+
+/*
+ * Write to an OpenSSL BIO in non-blocking mode.
+ */
+static int
+bio_write (struct tls_multi* multi, BIO *bio, const uint8_t *data, int size, const char *desc)
+{
+ int i;
+ int ret = 0;
+ ASSERT (size >= 0);
+ if (size)
+ {
+ /*
+ * Free the L_TLS lock prior to calling BIO routines
+ * so that foreground thread can still call
+ * tls_pre_decrypt or tls_pre_encrypt,
+ * allowing tunnel packet forwarding to continue.
+ */
+#ifdef BIO_DEBUG
+ bio_debug_data ("write", bio, data, size, desc);
+#endif
+ i = BIO_write (bio, data, size);
+
+ if (i < 0)
+ {
+ if (BIO_should_retry (bio))
+ {
+ ;
+ }
+ else
+ {
+ msg (D_TLS_ERRORS | M_SSL, "TLS ERROR: BIO write %s error",
+ desc);
+ ret = -1;
+ ERR_clear_error ();
+ }
+ }
+ else if (i != size)
+ {
+ msg (D_TLS_ERRORS | M_SSL,
+ "TLS ERROR: BIO write %s incomplete %d/%d", desc, i, size);
+ ret = -1;
+ ERR_clear_error ();
+ }
+ else
+ { /* successful write */
+ dmsg (D_HANDSHAKE_VERBOSE, "BIO write %s %d bytes", desc, i);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Read from an OpenSSL BIO in non-blocking mode.
+ */
+static int
+bio_read (struct tls_multi* multi, BIO *bio, struct buffer *buf, int maxlen, const char *desc)
+{
+ int i;
+ int ret = 0;
+ ASSERT (buf->len >= 0);
+ if (buf->len)
+ {
+ ;
+ }
+ else
+ {
+ int len = buf_forward_capacity (buf);
+ if (maxlen < len)
+ len = maxlen;
+
+ /*
+ * BIO_read brackets most of the serious RSA
+ * key negotiation number crunching.
+ */
+ i = BIO_read (bio, BPTR (buf), len);
+
+ VALGRIND_MAKE_READABLE ((void *) &i, sizeof (i));
+
+#ifdef BIO_DEBUG
+ bio_debug_data ("read", bio, BPTR (buf), i, desc);
+#endif
+ if (i < 0)
+ {
+ if (BIO_should_retry (bio))
+ {
+ ;
+ }
+ else
+ {
+ msg (D_TLS_ERRORS | M_SSL, "TLS_ERROR: BIO read %s error",
+ desc);
+ buf->len = 0;
+ ret = -1;
+ ERR_clear_error ();
+ }
+ }
+ else if (!i)
+ {
+ buf->len = 0;
+ }
+ else
+ { /* successful read */
+ dmsg (D_HANDSHAKE_VERBOSE, "BIO read %s %d bytes", desc, i);
+ buf->len = i;
+ ret = 1;
+ VALGRIND_MAKE_READABLE ((void *) BPTR (buf), BLEN (buf));
+ }
+ }
+ return ret;
+}
+
+/*
+ * Inline functions for reading from and writing
+ * to BIOs.
+ */
+
+static void
+bio_write_post (const int status, struct buffer *buf)
+{
+ if (status == 1) /* success status return from bio_write? */
+ {
+ memset (BPTR (buf), 0, BLEN (buf)); /* erase data just written */
+ buf->len = 0;
+ }
+}
+
+static int
+key_state_write_plaintext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf)
+{
+ int ret;
+ perf_push (PERF_BIO_WRITE_PLAINTEXT);
+ ret = bio_write (multi, ks->ssl_bio, BPTR(buf), BLEN(buf), "tls_write_plaintext");
+ bio_write_post (ret, buf);
+ perf_pop ();
+ return ret;
+}
+
+static int
+key_state_write_plaintext_const (struct tls_multi *multi, struct key_state *ks, const uint8_t *data, int len)
+{
+ int ret;
+ perf_push (PERF_BIO_WRITE_PLAINTEXT);
+ ret = bio_write (multi, ks->ssl_bio, data, len, "tls_write_plaintext_const");
+ perf_pop ();
+ return ret;
+}
+
+static int
+key_state_write_ciphertext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf)
+{
+ int ret;
+ perf_push (PERF_BIO_WRITE_CIPHERTEXT);
+ ret = bio_write (multi, ks->ct_in, BPTR(buf), BLEN(buf), "tls_write_ciphertext");
+ bio_write_post (ret, buf);
+ perf_pop ();
+ return ret;
+}
+
+static int
+key_state_read_plaintext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf,
+ int maxlen)
+{
+ int ret;
+ perf_push (PERF_BIO_READ_PLAINTEXT);
+ ret = bio_read (multi, ks->ssl_bio, buf, maxlen, "tls_read_plaintext");
+ perf_pop ();
+ return ret;
+}
+
+static int
+key_state_read_ciphertext (struct tls_multi *multi, struct key_state *ks, struct buffer *buf,
+ int maxlen)
+{
+ int ret;
+ perf_push (PERF_BIO_READ_CIPHERTEXT);
+ ret = bio_read (multi, ks->ct_out, buf, maxlen, "tls_read_ciphertext");
+ perf_pop ();
+ return ret;
+}
+
+/*
+ * Initialize a key_state. Each key_state corresponds to
+ * a specific SSL/TLS session.
+ */
+static void
+key_state_init (struct tls_session *session, struct key_state *ks)
+{
+ update_time ();
+
+ /*
+ * Build TLS object that reads/writes ciphertext
+ * to/from memory BIOs.
+ */
+ CLEAR (*ks);
+
+ ks->ssl = SSL_new (session->opt->ssl_ctx);
+ if (!ks->ssl)
+ msg (M_SSLERR, "SSL_new failed");
+
+ /* put session * in ssl object so we can access it
+ from verify callback*/
+ SSL_set_ex_data (ks->ssl, mydata_index, session);
+
+ ks->ssl_bio = getbio (BIO_f_ssl (), "ssl_bio");
+ ks->ct_in = getbio (BIO_s_mem (), "ct_in");
+ ks->ct_out = getbio (BIO_s_mem (), "ct_out");
+
+#ifdef BIO_DEBUG
+ bio_debug_oc ("open ssl_bio", ks->ssl_bio);
+ bio_debug_oc ("open ct_in", ks->ct_in);
+ bio_debug_oc ("open ct_out", ks->ct_out);
+#endif
+
+ if (session->opt->server)
+ SSL_set_accept_state (ks->ssl);
+ else
+ SSL_set_connect_state (ks->ssl);
+
+ SSL_set_bio (ks->ssl, ks->ct_in, ks->ct_out);
+ BIO_set_ssl (ks->ssl_bio, ks->ssl, BIO_NOCLOSE);
+
+ /* Set control-channel initiation mode */
+ ks->initial_opcode = session->initial_opcode;
+ session->initial_opcode = P_CONTROL_SOFT_RESET_V1;
+ ks->state = S_INITIAL;
+ ks->key_id = session->key_id;
+
+ /*
+ * key_id increments to KEY_ID_MASK then recycles back to 1.
+ * This way you know that if key_id is 0, it is the first key.
+ */
+ ++session->key_id;
+ session->key_id &= P_KEY_ID_MASK;
+ if (!session->key_id)
+ session->key_id = 1;
+
+ /* allocate key source material object */
+ ALLOC_OBJ_CLEAR (ks->key_src, struct key_source2);
+
+ /* allocate reliability objects */
+ ALLOC_OBJ_CLEAR (ks->send_reliable, struct reliable);
+ ALLOC_OBJ_CLEAR (ks->rec_reliable, struct reliable);
+ ALLOC_OBJ_CLEAR (ks->rec_ack, struct reliable_ack);
+
+ /* allocate buffers */
+ ks->plaintext_read_buf = alloc_buf (TLS_CHANNEL_BUF_SIZE);
+ ks->plaintext_write_buf = alloc_buf (TLS_CHANNEL_BUF_SIZE);
+ ks->ack_write_buf = alloc_buf (BUF_SIZE (&session->opt->frame));
+ reliable_init (ks->send_reliable, BUF_SIZE (&session->opt->frame),
+ FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_SEND_BUFFERS,
+ ks->key_id ? false : session->opt->xmit_hold);
+ reliable_init (ks->rec_reliable, BUF_SIZE (&session->opt->frame),
+ FRAME_HEADROOM (&session->opt->frame), TLS_RELIABLE_N_REC_BUFFERS,
+ false);
+ reliable_set_timeout (ks->send_reliable, session->opt->packet_timeout);
+
+ /* init packet ID tracker */
+ packet_id_init (&ks->packet_id,
+ session->opt->replay_window,
+ session->opt->replay_time);
+
+#ifdef MANAGEMENT_DEF_AUTH
+ ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++;
+#endif
+}
+
+static void
+key_state_free (struct key_state *ks, bool clear)
+{
+ ks->state = S_UNDEF;
+
+ if (ks->ssl) {
+#ifdef BIO_DEBUG
+ bio_debug_oc ("close ssl_bio", ks->ssl_bio);
+ bio_debug_oc ("close ct_in", ks->ct_in);
+ bio_debug_oc ("close ct_out", ks->ct_out);
+#endif
+ BIO_free_all(ks->ssl_bio);
+ SSL_free (ks->ssl);
+ }
+
+ free_key_ctx_bi (&ks->key);
+ free_buf (&ks->plaintext_read_buf);
+ free_buf (&ks->plaintext_write_buf);
+ free_buf (&ks->ack_write_buf);
+ buffer_list_free(ks->paybuf);
+
+ if (ks->send_reliable)
+ {
+ reliable_free (ks->send_reliable);
+ free (ks->send_reliable);
+ }
+
+ if (ks->rec_reliable)
+ {
+ reliable_free (ks->rec_reliable);
+ free (ks->rec_reliable);
+ }
+
+ if (ks->rec_ack)
+ free (ks->rec_ack);
+
+ if (ks->key_src)
+ free (ks->key_src);
+
+ packet_id_free (&ks->packet_id);
+
+#ifdef PLUGIN_DEF_AUTH
+ key_state_rm_auth_control_file (ks);
+#endif
+
+ if (clear)
+ CLEAR (*ks);
+}
+
+/*
+ * Must be called if we move a tls_session in memory.
+ */
+static inline void tls_session_set_self_referential_pointers (struct tls_session* session) {
+ session->tls_auth.packet_id = &session->tls_auth_pid;
+}
+
+/*
+ * Initialize a TLS session. A TLS session normally has 2 key_state objects,
+ * one for the current key, and one for the lame duck (i.e. retiring) key.
+ */
+static void
+tls_session_init (struct tls_multi *multi, struct tls_session *session)
+{
+ struct gc_arena gc = gc_new ();
+
+ dmsg (D_TLS_DEBUG, "TLS: tls_session_init: entry");
+
+ CLEAR (*session);
+
+ /* Set options data to point to parent's option structure */
+ session->opt = &multi->opt;
+
+ /* Randomize session # if it is 0 */
+ while (!session_id_defined(&session->session_id))
+ session_id_random (&session->session_id);
+
+ /* Are we a TLS server or client? */
+ ASSERT (session->opt->key_method >= 1);
+ if (session->opt->key_method == 1)
+ {
+ session->initial_opcode = session->opt->server ?
+ P_CONTROL_HARD_RESET_SERVER_V1 : P_CONTROL_HARD_RESET_CLIENT_V1;
+ }
+ else /* session->opt->key_method >= 2 */
+ {
+ session->initial_opcode = session->opt->server ?
+ P_CONTROL_HARD_RESET_SERVER_V2 : P_CONTROL_HARD_RESET_CLIENT_V2;
+ }
+
+ /* Initialize control channel authentication parameters */
+ session->tls_auth = session->opt->tls_auth;
+
+ /* Set session internal pointers (also called if session object is moved in memory) */
+ tls_session_set_self_referential_pointers (session);
+
+ /* initialize packet ID replay window for --tls-auth */
+ packet_id_init (session->tls_auth.packet_id,
+ session->opt->replay_window,
+ session->opt->replay_time);
+
+ /* load most recent packet-id to replay protect on --tls-auth */
+ packet_id_persist_load_obj (session->tls_auth.pid_persist, session->tls_auth.packet_id);
+
+ key_state_init (session, &session->key[KS_PRIMARY]);
+
+ dmsg (D_TLS_DEBUG, "TLS: tls_session_init: new session object, sid=%s",
+ session_id_print (&session->session_id, &gc));
+
+ gc_free (&gc);
+}
+
+static void
+tls_session_free (struct tls_session *session, bool clear)
+{
+ int i;
+
+ if (session->tls_auth.packet_id)
+ packet_id_free (session->tls_auth.packet_id);
+
+ for (i = 0; i < KS_SIZE; ++i)
+ key_state_free (&session->key[i], false);
+
+ if (session->common_name)
+ free (session->common_name);
+
+ cert_hash_free (session->cert_hash_set);
+
+ if (clear)
+ CLEAR (*session);
+}
+
+static void
+move_session (struct tls_multi* multi, int dest, int src, bool reinit_src)
+{
+ msg (D_TLS_DEBUG_LOW, "TLS: move_session: dest=%s src=%s reinit_src=%d",
+ session_index_name(dest),
+ session_index_name(src),
+ reinit_src);
+ ASSERT (src != dest);
+ ASSERT (src >= 0 && src < TM_SIZE);
+ ASSERT (dest >= 0 && dest < TM_SIZE);
+ tls_session_free (&multi->session[dest], false);
+ multi->session[dest] = multi->session[src];
+ tls_session_set_self_referential_pointers (&multi->session[dest]);
+
+ if (reinit_src)
+ tls_session_init (multi, &multi->session[src]);
+ else
+ CLEAR (multi->session[src]);
+
+ dmsg (D_TLS_DEBUG, "TLS: move_session: exit");
+}
+
+static void
+reset_session (struct tls_multi *multi, struct tls_session *session)
+{
+ tls_session_free (session, false);
+ tls_session_init (multi, session);
+}
+
+#if 0
+/*
+ * Transmit a TLS reset on our untrusted channel.
+ */
+static void
+initiate_untrusted_session (struct tls_multi *multi, struct sockaddr_in *to)
+{
+ struct tls_session *session = &multi->session[TM_UNTRUSTED];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+
+ reset_session (multi, session);
+ ks->remote_addr = *to;
+ msg (D_TLS_DEBUG_LOW, "TLS: initiate_untrusted_session: addr=%s", print_sockaddr (to));
+}
+#endif
+
+/*
+ * Used to determine in how many seconds we should be
+ * called again.
+ */
+static inline void
+compute_earliest_wakeup (interval_t *earliest, interval_t seconds_from_now) {
+ if (seconds_from_now < *earliest)
+ *earliest = seconds_from_now;
+ if (*earliest < 0)
+ *earliest = 0;
+}
+
+/*
+ * Return true if "lame duck" or retiring key has expired and can
+ * no longer be used.
+ */
+static inline bool
+lame_duck_must_die (const struct tls_session* session, interval_t *wakeup)
+{
+ const struct key_state* lame = &session->key[KS_LAME_DUCK];
+ if (lame->state >= S_INITIAL)
+ {
+ const time_t local_now = now;
+ ASSERT (lame->must_die); /* a lame duck key must always have an expiration */
+ if (local_now < lame->must_die)
+ {
+ compute_earliest_wakeup (wakeup, lame->must_die - local_now);
+ return false;
+ }
+ else
+ return true;
+ }
+ else if (lame->state == S_ERROR)
+ return true;
+ else
+ return false;
+}
+
+/*
+ * A tls_multi object fully encapsulates OpenVPN's TLS state.
+ * See ssl.h for more comments.
+ */
+struct tls_multi *
+tls_multi_init (struct tls_options *tls_options)
+{
+ struct tls_multi *ret;
+
+ ALLOC_OBJ_CLEAR (ret, struct tls_multi);
+
+ /* get command line derived options */
+ ret->opt = *tls_options;
+
+ /* set up pointer to HMAC object for TLS packet authentication */
+ ret->opt.tls_auth.key_ctx_bi = &ret->opt.tls_auth_key;
+
+ /* set up list of keys to be scanned by data channel encrypt and decrypt routines */
+ ASSERT (SIZE (ret->key_scan) == 3);
+ ret->key_scan[0] = &ret->session[TM_ACTIVE].key[KS_PRIMARY];
+ ret->key_scan[1] = &ret->session[TM_ACTIVE].key[KS_LAME_DUCK];
+ ret->key_scan[2] = &ret->session[TM_LAME_DUCK].key[KS_LAME_DUCK];
+
+ return ret;
+}
+
+/*
+ * Finalize our computation of frame sizes.
+ */
+void
+tls_multi_init_finalize (struct tls_multi* multi, const struct frame* frame)
+{
+ tls_init_control_channel_frame_parameters (frame, &multi->opt.frame);
+
+ /* initialize the active and untrusted sessions */
+
+ tls_session_init (multi, &multi->session[TM_ACTIVE]);
+
+ if (!multi->opt.single_session)
+ tls_session_init (multi, &multi->session[TM_UNTRUSTED]);
+}
+
+/*
+ * Initialize and finalize a standalone tls-auth verification object.
+ */
+
+struct tls_auth_standalone *
+tls_auth_standalone_init (struct tls_options *tls_options,
+ struct gc_arena *gc)
+{
+ struct tls_auth_standalone *tas;
+
+ ALLOC_OBJ_CLEAR_GC (tas, struct tls_auth_standalone, gc);
+
+ /* set up pointer to HMAC object for TLS packet authentication */
+ tas->tls_auth_key = tls_options->tls_auth_key;
+ tas->tls_auth_options.key_ctx_bi = &tas->tls_auth_key;
+ tas->tls_auth_options.flags |= CO_PACKET_ID_LONG_FORM;
+
+ /* get initial frame parms, still need to finalize */
+ tas->frame = tls_options->frame;
+
+ return tas;
+}
+
+void
+tls_auth_standalone_finalize (struct tls_auth_standalone *tas,
+ const struct frame *frame)
+{
+ tls_init_control_channel_frame_parameters (frame, &tas->frame);
+}
+
+/*
+ * Set local and remote option compatibility strings.
+ * Used to verify compatibility of local and remote option
+ * sets.
+ */
+void
+tls_multi_init_set_options (struct tls_multi* multi,
+ const char *local,
+ const char *remote)
+{
+#ifdef ENABLE_OCC
+ /* initialize options string */
+ multi->opt.local_options = local;
+ multi->opt.remote_options = remote;
+#endif
+}
+
+void
+tls_multi_free (struct tls_multi *multi, bool clear)
+{
+ int i;
+
+ ASSERT (multi);
+
+#ifdef MANAGEMENT_DEF_AUTH
+ man_def_auth_set_client_reason(multi, NULL);
+
+ free (multi->peer_info);
+#endif
+
+ if (multi->locked_cn)
+ free (multi->locked_cn);
+
+ if (multi->locked_username)
+ free (multi->locked_username);
+
+ cert_hash_free (multi->locked_cert_hash_set);
+
+ for (i = 0; i < TM_SIZE; ++i)
+ tls_session_free (&multi->session[i], false);
+
+ if (clear)
+ CLEAR (*multi);
+
+ free(multi);
+}
+
+/*
+ * Move a packet authentication HMAC + related fields to or from the front
+ * of the buffer so it can be processed by encrypt/decrypt.
+ */
+
+/*
+ * Dependent on hmac size, opcode size, and session_id size.
+ * Will assert if too small.
+ */
+#define SWAP_BUF_SIZE 256
+
+static bool
+swap_hmac (struct buffer *buf, const struct crypto_options *co, bool incoming)
+{
+ struct key_ctx *ctx;
+
+ ASSERT (co);
+
+ ctx = (incoming ? &co->key_ctx_bi->decrypt : &co->key_ctx_bi->encrypt);
+ ASSERT (ctx->hmac);
+
+ {
+ /* hmac + packet_id (8 bytes) */
+ const int hmac_size = HMAC_size (ctx->hmac) + packet_id_size (true);
+
+ /* opcode + session_id */
+ const int osid_size = 1 + SID_SIZE;
+
+ int e1, e2;
+ uint8_t *b = BPTR (buf);
+ uint8_t buf1[SWAP_BUF_SIZE];
+ uint8_t buf2[SWAP_BUF_SIZE];
+
+ if (incoming)
+ {
+ e1 = osid_size;
+ e2 = hmac_size;
+ }
+ else
+ {
+ e1 = hmac_size;
+ e2 = osid_size;
+ }
+
+ ASSERT (e1 <= SWAP_BUF_SIZE && e2 <= SWAP_BUF_SIZE);
+
+ if (buf->len >= e1 + e2)
+ {
+ memcpy (buf1, b, e1);
+ memcpy (buf2, b + e1, e2);
+ memcpy (b, buf2, e2);
+ memcpy (b + e2, buf1, e1);
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+#undef SWAP_BUF_SIZE
+
+/*
+ * Write a control channel authentication record.
+ */
+static void
+write_control_auth (struct tls_session *session,
+ struct key_state *ks,
+ struct buffer *buf,
+ struct link_socket_actual **to_link_addr,
+ int opcode,
+ int max_ack,
+ bool prepend_ack)
+{
+ uint8_t *header;
+ struct buffer null = clear_buf ();
+
+ ASSERT (link_socket_actual_defined (&ks->remote_addr));
+ ASSERT (reliable_ack_write
+ (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack));
+ ASSERT (session_id_write_prepend (&session->session_id, buf));
+ ASSERT (header = buf_prepend (buf, 1));
+ *header = ks->key_id | (opcode << P_OPCODE_SHIFT);
+ if (session->tls_auth.key_ctx_bi->encrypt.hmac)
+ {
+ /* no encryption, only write hmac */
+ openvpn_encrypt (buf, null, &session->tls_auth, NULL);
+ ASSERT (swap_hmac (buf, &session->tls_auth, false));
+ }
+ *to_link_addr = &ks->remote_addr;
+}
+
+/*
+ * Read a control channel authentication record.
+ */
+static bool
+read_control_auth (struct buffer *buf,
+ const struct crypto_options *co,
+ const struct link_socket_actual *from)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (co->key_ctx_bi->decrypt.hmac)
+ {
+ struct buffer null = clear_buf ();
+
+ /* move the hmac record to the front of the packet */
+ if (!swap_hmac (buf, co, true))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: cannot locate HMAC in incoming packet from %s",
+ print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return false;
+ }
+
+ /* authenticate only (no decrypt) and remove the hmac record
+ from the head of the buffer */
+ openvpn_decrypt (buf, null, co, NULL);
+ if (!buf->len)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: incoming packet authentication failed from %s",
+ print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return false;
+ }
+
+ }
+
+ /* advance buffer pointer past opcode & session_id since our caller
+ already read it */
+ buf_advance (buf, SID_SIZE + 1);
+
+ gc_free (&gc);
+ return true;
+}
+
+/*
+ * For debugging, print contents of key_source2 structure.
+ */
+
+static void
+key_source_print (const struct key_source *k,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+
+ VALGRIND_MAKE_READABLE ((void *)k->pre_master, sizeof (k->pre_master));
+ VALGRIND_MAKE_READABLE ((void *)k->random1, sizeof (k->random1));
+ VALGRIND_MAKE_READABLE ((void *)k->random2, sizeof (k->random2));
+
+ dmsg (D_SHOW_KEY_SOURCE,
+ "%s pre_master: %s",
+ prefix,
+ format_hex (k->pre_master, sizeof (k->pre_master), 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE,
+ "%s random1: %s",
+ prefix,
+ format_hex (k->random1, sizeof (k->random1), 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE,
+ "%s random2: %s",
+ prefix,
+ format_hex (k->random2, sizeof (k->random2), 0, &gc));
+
+ gc_free (&gc);
+}
+
+static void
+key_source2_print (const struct key_source2 *k)
+{
+ key_source_print (&k->client, "Client");
+ key_source_print (&k->server, "Server");
+}
+
+/*
+ * Use the TLS PRF function for generating data channel keys.
+ * This code is taken from the OpenSSL library.
+ *
+ * TLS generates keys as such:
+ *
+ * master_secret[48] = PRF(pre_master_secret[48], "master secret",
+ * ClientHello.random[32] + ServerHello.random[32])
+ *
+ * key_block[] = PRF(SecurityParameters.master_secret[48],
+ * "key expansion",
+ * SecurityParameters.server_random[32] +
+ * SecurityParameters.client_random[32]);
+ *
+ * Notes:
+ *
+ * (1) key_block contains a full set of 4 keys.
+ * (2) The pre-master secret is generated by the client.
+ */
+
+static void
+tls1_P_hash(const EVP_MD *md,
+ const uint8_t *sec,
+ int sec_len,
+ const uint8_t *seed,
+ int seed_len,
+ uint8_t *out,
+ int olen)
+{
+ struct gc_arena gc = gc_new ();
+ int chunk,n;
+ unsigned int j;
+ HMAC_CTX ctx;
+ HMAC_CTX ctx_tmp;
+ uint8_t A1[EVP_MAX_MD_SIZE];
+ unsigned int A1_len;
+
+#ifdef ENABLE_DEBUG
+ const int olen_orig = olen;
+ const uint8_t *out_orig = out;
+#endif
+
+ dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex (sec, sec_len, 0, &gc));
+ dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex (seed, seed_len, 0, &gc));
+
+ chunk=EVP_MD_size(md);
+
+ HMAC_CTX_init(&ctx);
+ HMAC_CTX_init(&ctx_tmp);
+ HMAC_Init_ex(&ctx,sec,sec_len,md, NULL);
+ HMAC_Init_ex(&ctx_tmp,sec,sec_len,md, NULL);
+ HMAC_Update(&ctx,seed,seed_len);
+ HMAC_Final(&ctx,A1,&A1_len);
+
+ n=0;
+ for (;;)
+ {
+ HMAC_Init_ex(&ctx,NULL,0,NULL,NULL); /* re-init */
+ HMAC_Init_ex(&ctx_tmp,NULL,0,NULL,NULL); /* re-init */
+ HMAC_Update(&ctx,A1,A1_len);
+ HMAC_Update(&ctx_tmp,A1,A1_len);
+ HMAC_Update(&ctx,seed,seed_len);
+
+ if (olen > chunk)
+ {
+ HMAC_Final(&ctx,out,&j);
+ out+=j;
+ olen-=j;
+ HMAC_Final(&ctx_tmp,A1,&A1_len); /* calc the next A1 value */
+ }
+ else /* last one */
+ {
+ HMAC_Final(&ctx,A1,&A1_len);
+ memcpy(out,A1,olen);
+ break;
+ }
+ }
+ HMAC_CTX_cleanup(&ctx);
+ HMAC_CTX_cleanup(&ctx_tmp);
+ CLEAR (A1);
+
+ dmsg (D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex (out_orig, olen_orig, 0, &gc));
+ gc_free (&gc);
+}
+
+static void
+tls1_PRF(uint8_t *label,
+ int label_len,
+ const uint8_t *sec,
+ int slen,
+ uint8_t *out1,
+ int olen)
+{
+ struct gc_arena gc = gc_new ();
+ const EVP_MD *md5 = EVP_md5();
+ const EVP_MD *sha1 = EVP_sha1();
+ int len,i;
+ const uint8_t *S1,*S2;
+ uint8_t *out2;
+
+ out2 = (uint8_t *) gc_malloc (olen, false, &gc);
+
+ len=slen/2;
+ S1=sec;
+ S2= &(sec[len]);
+ len+=(slen&1); /* add for odd, make longer */
+
+
+ tls1_P_hash(md5 ,S1,len,label,label_len,out1,olen);
+ tls1_P_hash(sha1,S2,len,label,label_len,out2,olen);
+
+ for (i=0; i<olen; i++)
+ out1[i]^=out2[i];
+
+ memset (out2, 0, olen);
+
+ dmsg (D_SHOW_KEY_SOURCE, "tls1_PRF out[%d]: %s", olen, format_hex (out1, olen, 0, &gc));
+
+ gc_free (&gc);
+}
+
+static void
+openvpn_PRF (const uint8_t *secret,
+ int secret_len,
+ const char *label,
+ const uint8_t *client_seed,
+ int client_seed_len,
+ const uint8_t *server_seed,
+ int server_seed_len,
+ const struct session_id *client_sid,
+ const struct session_id *server_sid,
+ uint8_t *output,
+ int output_len)
+{
+ /* concatenate seed components */
+
+ struct buffer seed = alloc_buf (strlen (label)
+ + client_seed_len
+ + server_seed_len
+ + SID_SIZE * 2);
+
+ ASSERT (buf_write (&seed, label, strlen (label)));
+ ASSERT (buf_write (&seed, client_seed, client_seed_len));
+ ASSERT (buf_write (&seed, server_seed, server_seed_len));
+
+ if (client_sid)
+ ASSERT (buf_write (&seed, client_sid->id, SID_SIZE));
+ if (server_sid)
+ ASSERT (buf_write (&seed, server_sid->id, SID_SIZE));
+
+ /* compute PRF */
+ tls1_PRF (BPTR(&seed), BLEN(&seed), secret, secret_len, output, output_len);
+
+ buf_clear (&seed);
+ free_buf (&seed);
+
+ VALGRIND_MAKE_READABLE ((void *)output, output_len);
+}
+
+/*
+ * Using source entropy from local and remote hosts, mix into
+ * master key.
+ */
+static bool
+generate_key_expansion (struct key_ctx_bi *key,
+ const struct key_type *key_type,
+ const struct key_source2 *key_src,
+ const struct session_id *client_sid,
+ const struct session_id *server_sid,
+ bool server)
+{
+ uint8_t master[48];
+ struct key2 key2;
+ bool ret = false;
+ int i;
+
+ CLEAR (master);
+ CLEAR (key2);
+
+ /* debugging print of source key material */
+ key_source2_print (key_src);
+
+ /* compute master secret */
+ openvpn_PRF (key_src->client.pre_master,
+ sizeof(key_src->client.pre_master),
+ KEY_EXPANSION_ID " master secret",
+ key_src->client.random1,
+ sizeof(key_src->client.random1),
+ key_src->server.random1,
+ sizeof(key_src->server.random1),
+ NULL,
+ NULL,
+ master,
+ sizeof(master));
+
+ /* compute key expansion */
+ openvpn_PRF (master,
+ sizeof(master),
+ KEY_EXPANSION_ID " key expansion",
+ key_src->client.random2,
+ sizeof(key_src->client.random2),
+ key_src->server.random2,
+ sizeof(key_src->server.random2),
+ client_sid,
+ server_sid,
+ (uint8_t*)key2.keys,
+ sizeof(key2.keys));
+
+ key2.n = 2;
+
+ key2_print (&key2, key_type, "Master Encrypt", "Master Decrypt");
+
+ /* check for weak keys */
+ for (i = 0; i < 2; ++i)
+ {
+ fixup_key (&key2.keys[i], key_type);
+ if (!check_key (&key2.keys[i], key_type))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Bad dynamic key generated");
+ goto exit;
+ }
+ }
+
+ /* Initialize OpenSSL key contexts */
+
+ ASSERT (server == true || server == false);
+
+ init_key_ctx (&key->encrypt,
+ &key2.keys[(int)server],
+ key_type,
+ DO_ENCRYPT,
+ "Data Channel Encrypt");
+
+ init_key_ctx (&key->decrypt,
+ &key2.keys[1-(int)server],
+ key_type,
+ DO_DECRYPT,
+ "Data Channel Decrypt");
+
+ ret = true;
+
+ exit:
+ CLEAR (master);
+ CLEAR (key2);
+
+ return ret;
+}
+
+static bool
+random_bytes_to_buf (struct buffer *buf,
+ uint8_t *out,
+ int outlen)
+{
+ if (!RAND_bytes (out, outlen))
+ msg (M_FATAL, "ERROR: Random number generator cannot obtain entropy for key generation [SSL]");
+ if (!buf_write (buf, out, outlen))
+ return false;
+ return true;
+}
+
+static bool
+key_source2_randomize_write (struct key_source2 *k2,
+ struct buffer *buf,
+ bool server)
+{
+ struct key_source *k = &k2->client;
+ if (server)
+ k = &k2->server;
+
+ CLEAR (*k);
+
+ if (!server)
+ {
+ if (!random_bytes_to_buf (buf, k->pre_master, sizeof (k->pre_master)))
+ return false;
+ }
+
+ if (!random_bytes_to_buf (buf, k->random1, sizeof (k->random1)))
+ return false;
+ if (!random_bytes_to_buf (buf, k->random2, sizeof (k->random2)))
+ return false;
+
+ return true;
+}
+
+static int
+key_source2_read (struct key_source2 *k2,
+ struct buffer *buf,
+ bool server)
+{
+ struct key_source *k = &k2->client;
+
+ if (!server)
+ k = &k2->server;
+
+ CLEAR (*k);
+
+ if (server)
+ {
+ if (!buf_read (buf, k->pre_master, sizeof (k->pre_master)))
+ return 0;
+ }
+
+ if (!buf_read (buf, k->random1, sizeof (k->random1)))
+ return 0;
+ if (!buf_read (buf, k->random2, sizeof (k->random2)))
+ return 0;
+
+ return 1;
+}
+
+static void
+flush_payload_buffer (struct tls_multi *multi, struct key_state *ks)
+{
+ struct buffer *b;
+ while ((b = buffer_list_peek (ks->paybuf)))
+ {
+ key_state_write_plaintext_const (multi, ks, b->data, b->len);
+ buffer_list_pop (ks->paybuf);
+ }
+}
+
+/*
+ * Macros for key_state_soft_reset & tls_process
+ */
+#define ks (&session->key[KS_PRIMARY]) /* primary key */
+#define ks_lame (&session->key[KS_LAME_DUCK]) /* retiring key */
+
+/* true if no in/out acknowledgements pending */
+#define FULL_SYNC \
+ (reliable_empty(ks->send_reliable) && reliable_ack_empty (ks->rec_ack))
+
+/*
+ * Move the active key to the lame duck key and reinitialize the
+ * active key.
+ */
+static void
+key_state_soft_reset (struct tls_session *session)
+{
+ ks->must_die = now + session->opt->transition_window; /* remaining lifetime of old key */
+ key_state_free (ks_lame, false);
+ *ks_lame = *ks;
+
+ key_state_init (session, ks);
+ ks->session_id_remote = ks_lame->session_id_remote;
+ ks->remote_addr = ks_lame->remote_addr;
+}
+
+/*
+ * Read/write strings from/to a struct buffer with a u16 length prefix.
+ */
+
+static bool
+write_string (struct buffer *buf, const char *str, const int maxlen)
+{
+ const int len = strlen (str) + 1;
+ if (len < 1 || (maxlen >= 0 && len > maxlen))
+ return false;
+ if (!buf_write_u16 (buf, len))
+ return false;
+ if (!buf_write (buf, str, len))
+ return false;
+ return true;
+}
+
+static bool
+write_empty_string (struct buffer *buf)
+{
+ if (!buf_write_u16 (buf, 0))
+ return false;
+ return true;
+}
+
+static bool
+read_string (struct buffer *buf, char *str, const unsigned int capacity)
+{
+ const int len = buf_read_u16 (buf);
+ if (len < 1 || len > (int)capacity)
+ return false;
+ if (!buf_read (buf, str, len))
+ return false;
+ str[len-1] = '\0';
+ return true;
+}
+
+static char *
+read_string_alloc (struct buffer *buf)
+{
+ const int len = buf_read_u16 (buf);
+ char *str;
+
+ if (len < 1)
+ return NULL;
+ str = (char *) malloc(len);
+ check_malloc_return(str);
+ if (!buf_read (buf, str, len))
+ {
+ free (str);
+ return NULL;
+ }
+ str[len-1] = '\0';
+ return str;
+}
+
+void
+read_string_discard (struct buffer *buf)
+{
+ char *data = read_string_alloc(buf);
+ if (data)
+ free (data);
+}
+
+/*
+ * Authenticate a client using username/password.
+ * Runs on server.
+ *
+ * If you want to add new authentication methods,
+ * this is the place to start.
+ */
+
+static bool
+verify_user_pass_script (struct tls_session *session, const struct user_pass *up)
+{
+ struct gc_arena gc = gc_new ();
+ struct argv argv = argv_new ();
+ const char *tmp_file = "";
+ bool ret = false;
+
+ /* Is username defined? */
+ if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
+ {
+ /* Set environmental variables prior to calling script */
+ setenv_str (session->opt->es, "script_type", "user-pass-verify");
+
+ if (session->opt->auth_user_pass_verify_script_via_file)
+ {
+ struct status_output *so;
+
+ tmp_file = create_temp_file (session->opt->tmp_dir, "up", &gc);
+ if( tmp_file ) {
+ so = status_open (tmp_file, 0, -1, NULL, STATUS_OUTPUT_WRITE);
+ status_printf (so, "%s", up->username);
+ status_printf (so, "%s", up->password);
+ if (!status_close (so))
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: could not write username/password to file: %s",
+ tmp_file);
+ goto done;
+ }
+ } else {
+ msg (D_TLS_ERRORS, "TLS Auth Error: could not create write "
+ "username/password to temp file");
+ }
+ }
+ else
+ {
+ setenv_str (session->opt->es, "username", up->username);
+ setenv_str (session->opt->es, "password", up->password);
+ }
+
+ /* setenv incoming cert common name for script */
+ setenv_str (session->opt->es, "common_name", session->common_name);
+
+ /* setenv client real IP address */
+ setenv_untrusted (session);
+
+ /* format command line */
+ argv_printf (&argv, "%sc %s", session->opt->auth_user_pass_verify_script, tmp_file);
+
+ /* call command */
+ ret = openvpn_run_script (&argv, session->opt->es, 0,
+ "--auth-user-pass-verify");
+
+ if (!session->opt->auth_user_pass_verify_script_via_file)
+ setenv_del (session->opt->es, "password");
+ }
+ else
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
+ }
+
+ done:
+ if (tmp_file && strlen (tmp_file) > 0)
+ delete_file (tmp_file);
+
+ argv_reset (&argv);
+ gc_free (&gc);
+ return ret;
+}
+
+static int
+verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up, const char *raw_username)
+{
+ int retval = OPENVPN_PLUGIN_FUNC_ERROR;
+
+ /* Is username defined? */
+ if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
+ {
+ /* set username/password in private env space */
+ setenv_str (session->opt->es, "username", raw_username);
+ setenv_str (session->opt->es, "password", up->password);
+
+ /* setenv incoming cert common name for script */
+ setenv_str (session->opt->es, "common_name", session->common_name);
+
+ /* setenv client real IP address */
+ setenv_untrusted (session);
+
+#ifdef PLUGIN_DEF_AUTH
+ /* generate filename for deferred auth control file */
+ key_state_gen_auth_control_file (ks, session->opt);
+#endif
+
+ /* call command */
+ retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
+
+#ifdef PLUGIN_DEF_AUTH
+ /* purge auth control filename (and file itself) for non-deferred returns */
+ if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
+ key_state_rm_auth_control_file (ks);
+#endif
+
+ setenv_del (session->opt->es, "password");
+ setenv_str (session->opt->es, "username", up->username);
+ }
+ else
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
+ }
+
+ return retval;
+}
+
+/*
+ * MANAGEMENT_DEF_AUTH internal ssl.c status codes
+ */
+#define KMDA_ERROR 0
+#define KMDA_SUCCESS 1
+#define KMDA_UNDEF 2
+#define KMDA_DEF 3
+
+#ifdef MANAGEMENT_DEF_AUTH
+static int
+verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
+{
+ int retval = KMDA_ERROR;
+
+ /* Is username defined? */
+ if ((session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL) || strlen (up->username))
+ {
+ /* set username/password in private env space */
+ setenv_str (session->opt->es, "username", raw_username);
+ setenv_str (session->opt->es, "password", up->password);
+
+ /* setenv incoming cert common name for script */
+ setenv_str (session->opt->es, "common_name", session->common_name);
+
+ /* setenv client real IP address */
+ setenv_untrusted (session);
+
+ if (management)
+ management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
+
+ setenv_del (session->opt->es, "password");
+ setenv_str (session->opt->es, "username", up->username);
+
+ retval = KMDA_SUCCESS;
+ }
+ else
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
+ }
+
+ return retval;
+}
+#endif
+
+/*
+ * Handle the reading and writing of key data to and from
+ * the TLS control channel (cleartext).
+ */
+
+static bool
+key_method_1_write (struct buffer *buf, struct tls_session *session)
+{
+ struct key key;
+
+ ASSERT (session->opt->key_method == 1);
+ ASSERT (buf_init (buf, 0));
+
+ generate_key_random (&key, &session->opt->key_type);
+ if (!check_key (&key, &session->opt->key_type))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Bad encrypting key generated");
+ return false;
+ }
+
+ if (!write_key (&key, &session->opt->key_type, buf))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: write_key failed");
+ return false;
+ }
+
+ init_key_ctx (&ks->key.encrypt, &key, &session->opt->key_type,
+ DO_ENCRYPT, "Data Channel Encrypt");
+ CLEAR (key);
+
+ /* send local options string */
+ {
+ const char *local_options = local_options_string (session);
+ const int optlen = strlen (local_options) + 1;
+ if (!buf_write (buf, local_options, optlen))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: KM1 write options failed");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool
+push_peer_info(struct buffer *buf, struct tls_session *session)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+#ifdef ENABLE_PUSH_PEER_INFO
+ if (session->opt->push_peer_info) /* write peer info */
+ {
+ struct env_set *es = session->opt->es;
+ struct env_item *e;
+ struct buffer out = alloc_buf_gc (512*3, &gc);
+
+ /* push version */
+ buf_printf (&out, "IV_VER=%s\n", PACKAGE_VERSION);
+
+ /* push platform */
+#if defined(TARGET_LINUX)
+ buf_printf (&out, "IV_PLAT=linux\n");
+#elif defined(TARGET_SOLARIS)
+ buf_printf (&out, "IV_PLAT=solaris\n");
+#elif defined(TARGET_OPENBSD)
+ buf_printf (&out, "IV_PLAT=openbsd\n");
+#elif defined(TARGET_DARWIN)
+ buf_printf (&out, "IV_PLAT=mac\n");
+#elif defined(TARGET_NETBSD)
+ buf_printf (&out, "IV_PLAT=netbsd\n");
+#elif defined(TARGET_FREEBSD)
+ buf_printf (&out, "IV_PLAT=freebsd\n");
+#elif defined(WIN32)
+ buf_printf (&out, "IV_PLAT=win\n");
+#endif
+
+ /* push mac addr */
+ {
+ bool get_default_gateway_mac_addr (unsigned char *macaddr);
+ uint8_t macaddr[6];
+ get_default_gateway_mac_addr (macaddr);
+ buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
+ }
+
+ /* push env vars that begin with UV_ */
+ for (e=es->list; e != NULL; e=e->next)
+ {
+ if (e->string)
+ {
+ if (!strncmp(e->string, "UV_", 3) && buf_safe(&out, strlen(e->string)+1))
+ buf_printf (&out, "%s\n", e->string);
+ }
+ }
+
+ if (!write_string(buf, BSTR(&out), -1))
+ goto error;
+ }
+ else
+#endif
+ {
+ if (!write_empty_string (buf)) /* no peer info */
+ goto error;
+ }
+ ret = true;
+
+ error:
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
+key_method_2_write (struct buffer *buf, struct tls_session *session)
+{
+ ASSERT (session->opt->key_method == 2);
+ ASSERT (buf_init (buf, 0));
+
+ /* write a uint32 0 */
+ if (!buf_write_u32 (buf, 0))
+ goto error;
+
+ /* write key_method + flags */
+ if (!buf_write_u8 (buf, (session->opt->key_method & KEY_METHOD_MASK)))
+ goto error;
+
+ /* write key source material */
+ if (!key_source2_randomize_write (ks->key_src, buf, session->opt->server))
+ goto error;
+
+ /* write options string */
+ {
+ if (!write_string (buf, local_options_string (session), TLS_OPTIONS_LEN))
+ goto error;
+ }
+
+ /* write username/password if specified */
+ if (auth_user_pass_enabled)
+ {
+ auth_user_pass_setup (NULL);
+ if (!write_string (buf, auth_user_pass.username, -1))
+ goto error;
+ if (!write_string (buf, auth_user_pass.password, -1))
+ goto error;
+ purge_user_pass (&auth_user_pass, false);
+ }
+ else
+ {
+ if (!write_empty_string (buf)) /* no username */
+ goto error;
+ if (!write_empty_string (buf)) /* no password */
+ goto error;
+ }
+
+ if (!push_peer_info (buf, session))
+ goto error;
+
+ /*
+ * generate tunnel keys if server
+ */
+ if (session->opt->server)
+ {
+ if (ks->authenticated)
+ {
+ if (!generate_key_expansion (&ks->key,
+ &session->opt->key_type,
+ ks->key_src,
+ &ks->session_id_remote,
+ &session->session_id,
+ true))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: server generate_key_expansion failed");
+ goto error;
+ }
+ }
+
+ CLEAR (*ks->key_src);
+ }
+
+ return true;
+
+ error:
+ msg (D_TLS_ERRORS, "TLS Error: Key Method #2 write failed");
+ CLEAR (*ks->key_src);
+ return false;
+}
+
+static bool
+key_method_1_read (struct buffer *buf, struct tls_session *session)
+{
+ int status;
+ struct key key;
+
+ ASSERT (session->opt->key_method == 1);
+
+ if (!session->verified)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Certificate verification failed (key-method 1)");
+ goto error;
+ }
+
+ status = read_key (&key, &session->opt->key_type, buf);
+ if (status != 1)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Error reading data channel key from plaintext buffer");
+ goto error;
+ }
+
+ if (!check_key (&key, &session->opt->key_type))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Bad decrypting key received from peer");
+ goto error;
+ }
+
+ if (buf->len < 1)
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Missing options string");
+ goto error;
+ }
+
+#ifdef ENABLE_OCC
+ /* compare received remote options string
+ with our locally computed options string */
+ if (!session->opt->disable_occ &&
+ !options_cmp_equal_safe ((char *) BPTR (buf), session->opt->remote_options, buf->len))
+ {
+ options_warning_safe ((char *) BPTR (buf), session->opt->remote_options, buf->len);
+ }
+#endif
+
+ buf_clear (buf);
+
+ init_key_ctx (&ks->key.decrypt, &key, &session->opt->key_type,
+ DO_DECRYPT, "Data Channel Decrypt");
+ CLEAR (key);
+ ks->authenticated = true;
+ return true;
+
+ error:
+ buf_clear (buf);
+ CLEAR (key);
+ return false;
+}
+
+static bool
+key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_session *session)
+{
+ struct gc_arena gc = gc_new ();
+ int key_method_flags;
+ char *options;
+ struct user_pass *up;
+
+ bool man_def_auth = KMDA_UNDEF;
+
+#ifdef MANAGEMENT_DEF_AUTH
+ if (management_enable_def_auth (management))
+ man_def_auth = KMDA_DEF;
+#endif
+
+ ASSERT (session->opt->key_method == 2);
+
+ /* allocate temporary objects */
+ ALLOC_ARRAY_CLEAR_GC (options, char, TLS_OPTIONS_LEN, &gc);
+
+ /* discard leading uint32 */
+ ASSERT (buf_advance (buf, 4));
+
+ /* get key method */
+ key_method_flags = buf_read_u8 (buf);
+ if ((key_method_flags & KEY_METHOD_MASK) != 2)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: Unknown key_method/flags=%d received from remote host",
+ key_method_flags);
+ goto error;
+ }
+
+ /* get key source material (not actual keys yet) */
+ if (!key_source2_read (ks->key_src, buf, session->opt->server))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Error reading remote data channel key source entropy from plaintext buffer");
+ goto error;
+ }
+
+ /* get options */
+ if (!read_string (buf, options, TLS_OPTIONS_LEN))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Failed to read required OCC options string");
+ goto error;
+ }
+
+ /* should we check username/password? */
+ ks->authenticated = false;
+ if (session->opt->auth_user_pass_verify_script
+ || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
+ || man_def_auth == KMDA_DEF)
+ {
+ int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
+ bool s2 = true;
+ char *raw_username;
+ bool username_status, password_status;
+
+ /* get username/password from plaintext buffer */
+ ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
+ username_status = read_string (buf, up->username, USER_PASS_LEN);
+ password_status = read_string (buf, up->password, USER_PASS_LEN);
+ if (!username_status || !password_status)
+ {
+ CLEAR (*up);
+ if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Auth Username/Password was not provided by peer");
+ goto error;
+ }
+ }
+
+ /* preserve raw username before string_mod remapping, for plugins */
+ ALLOC_ARRAY_CLEAR_GC (raw_username, char, USER_PASS_LEN, &gc);
+ strcpy (raw_username, up->username);
+ string_mod (raw_username, CC_PRINT, CC_CRLF, '_');
+
+ /* enforce character class restrictions in username/password */
+ string_mod_sslname (up->username, COMMON_NAME_CHAR_CLASS, session->opt->ssl_flags);
+ string_mod (up->password, CC_PRINT, CC_CRLF, '_');
+
+ /* call plugin(s) and/or script */
+#ifdef MANAGEMENT_DEF_AUTH
+ /* get peer info from control channel */
+ free (multi->peer_info);
+ multi->peer_info = read_string_alloc (buf);
+
+ if (man_def_auth == KMDA_DEF)
+ man_def_auth = verify_user_pass_management (session, up, raw_username);
+#endif
+ if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
+ s1 = verify_user_pass_plugin (session, up, raw_username);
+ if (session->opt->auth_user_pass_verify_script)
+ s2 = verify_user_pass_script (session, up);
+
+ /* check sizing of username if it will become our common name */
+ if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) && strlen (up->username) >= TLS_USERNAME_LEN)
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: --username-as-common name specified and username is longer than the maximum permitted Common Name length of %d characters", TLS_USERNAME_LEN);
+ s1 = OPENVPN_PLUGIN_FUNC_ERROR;
+ }
+
+ /* auth succeeded? */
+ if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
+#ifdef PLUGIN_DEF_AUTH
+ || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
+#endif
+ ) && s2 && man_def_auth != KMDA_ERROR
+ && tls_lock_username (multi, up->username))
+ {
+ ks->authenticated = true;
+#ifdef PLUGIN_DEF_AUTH
+ if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
+ ks->auth_deferred = true;
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+ if (man_def_auth != KMDA_UNDEF)
+ ks->auth_deferred = true;
+#endif
+ if ((session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME))
+ set_common_name (session, up->username);
+#ifdef ENABLE_DEF_AUTH
+ msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
+ ks->auth_deferred ? "deferred" : "succeeded",
+ up->username,
+ (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
+#else
+ msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
+ "succeeded",
+ up->username,
+ (session->opt->ssl_flags & SSLF_USERNAME_AS_COMMON_NAME) ? "[CN SET]" : "");
+#endif
+ }
+ else
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: Auth Username/Password verification failed for peer");
+ }
+
+ CLEAR (*up);
+ }
+ else
+ {
+ if (!session->verified)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Certificate verification failed (key-method 2)");
+ goto error;
+ }
+ ks->authenticated = true;
+ }
+
+ /* While it shouldn't really happen, don't allow the common name to be NULL */
+ if (!session->common_name)
+ set_common_name (session, "");
+
+ /* Don't allow the CN to change once it's been locked */
+ if (ks->authenticated && multi->locked_cn)
+ {
+ const char *cn = session->common_name;
+ if (cn && strcmp (cn, multi->locked_cn))
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN attempted to change from '%s' to '%s' -- tunnel disabled",
+ multi->locked_cn,
+ cn);
+
+ /* change the common name back to its original value and disable the tunnel */
+ set_common_name (session, multi->locked_cn);
+ tls_deauthenticate (multi);
+ }
+ }
+
+ /* Don't allow the cert hashes to change once they have been locked */
+ if (ks->authenticated && multi->locked_cert_hash_set)
+ {
+ const struct cert_hash_set *chs = session->cert_hash_set;
+ if (chs && !cert_hash_compare (chs, multi->locked_cert_hash_set))
+ {
+ msg (D_TLS_ERRORS, "TLS Auth Error: TLS object CN=%s client-provided SSL certs unexpectedly changed during mid-session reauth",
+ session->common_name);
+
+ /* disable the tunnel */
+ tls_deauthenticate (multi);
+ }
+ }
+
+ /* verify --client-config-dir based authentication */
+ if (ks->authenticated && session->opt->client_config_dir_exclusive)
+ {
+ const char *cn = session->common_name;
+ const char *path = gen_path (session->opt->client_config_dir_exclusive, cn, &gc);
+ if (!cn || !strcmp (cn, CCD_DEFAULT) || !test_file (path))
+ {
+ ks->authenticated = false;
+ msg (D_TLS_ERRORS, "TLS Auth Error: --client-config-dir authentication failed for common name '%s' file='%s'",
+ session->common_name,
+ path ? path : "UNDEF");
+ }
+ }
+
+#ifdef ENABLE_OCC
+ /* check options consistency */
+ if (!session->opt->disable_occ &&
+ !options_cmp_equal (options, session->opt->remote_options))
+ {
+ options_warning (options, session->opt->remote_options);
+ if (session->opt->ssl_flags & SSLF_OPT_VERIFY)
+ {
+ msg (D_TLS_ERRORS, "Option inconsistency warnings triggering disconnect due to --opt-verify");
+ ks->authenticated = false;
+ }
+ }
+#endif
+
+ buf_clear (buf);
+
+ /*
+ * Call OPENVPN_PLUGIN_TLS_FINAL plugin if defined, for final
+ * veto opportunity over authentication decision.
+ */
+ if (ks->authenticated && plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL))
+ {
+ if (plugin_call (session->opt->plugins, OPENVPN_PLUGIN_TLS_FINAL, NULL, NULL, session->opt->es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+ ks->authenticated = false;
+ }
+
+ /*
+ * Generate tunnel keys if client
+ */
+ if (!session->opt->server)
+ {
+ if (!generate_key_expansion (&ks->key,
+ &session->opt->key_type,
+ ks->key_src,
+ &session->session_id,
+ &ks->session_id_remote,
+ false))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: client generate_key_expansion failed");
+ goto error;
+ }
+
+ CLEAR (*ks->key_src);
+ }
+
+ gc_free (&gc);
+ return true;
+
+ error:
+ CLEAR (*ks->key_src);
+ buf_clear (buf);
+ gc_free (&gc);
+ return false;
+}
+
+static int
+auth_deferred_expire_window (const struct tls_options *o)
+{
+ int ret = o->handshake_window;
+ const int r2 = o->renegotiate_seconds / 2;
+
+ if (o->renegotiate_seconds && r2 < ret)
+ ret = r2;
+ return ret;
+}
+
+/*
+ * This is the primary routine for processing TLS stuff inside the
+ * the main event loop. When this routine exits
+ * with non-error status, it will set *wakeup to the number of seconds
+ * when it wants to be called again.
+ *
+ * Return value is true if we have placed a packet in *to_link which we
+ * want to send to our peer.
+ */
+static bool
+tls_process (struct tls_multi *multi,
+ struct tls_session *session,
+ struct buffer *to_link,
+ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer *buf;
+ bool state_change = false;
+ bool active = false;
+
+ /* Make sure we were initialized and that we're not in an error state */
+ ASSERT (ks->state != S_UNDEF);
+ ASSERT (ks->state != S_ERROR);
+ ASSERT (session_id_defined (&session->session_id));
+
+ /* Should we trigger a soft reset? -- new key, keeps old key for a while */
+ if (ks->state >= S_ACTIVE &&
+ ((session->opt->renegotiate_seconds
+ && now >= ks->established + session->opt->renegotiate_seconds)
+ || (session->opt->renegotiate_bytes
+ && ks->n_bytes >= session->opt->renegotiate_bytes)
+ || (session->opt->renegotiate_packets
+ && ks->n_packets >= session->opt->renegotiate_packets)
+ || (packet_id_close_to_wrapping (&ks->packet_id.send))))
+ {
+ msg (D_TLS_DEBUG_LOW,
+ "TLS: soft reset sec=%d bytes=" counter_format "/%d pkts=" counter_format "/%d",
+ (int)(ks->established + session->opt->renegotiate_seconds - now),
+ ks->n_bytes, session->opt->renegotiate_bytes,
+ ks->n_packets, session->opt->renegotiate_packets);
+ key_state_soft_reset (session);
+ }
+
+ /* Kill lame duck key transition_window seconds after primary key negotiation */
+ if (lame_duck_must_die (session, wakeup)) {
+ key_state_free (ks_lame, true);
+ msg (D_TLS_DEBUG_LOW, "TLS: tls_process: killed expiring key");
+ }
+
+ do
+ {
+ update_time ();
+
+ dmsg (D_TLS_DEBUG, "TLS: tls_process: chg=%d ks=%s lame=%s to_link->len=%d wakeup=%d",
+ state_change,
+ state_name (ks->state),
+ state_name (ks_lame->state),
+ to_link->len,
+ *wakeup);
+
+ state_change = false;
+
+ /*
+ * TLS activity is finished once we get to S_ACTIVE,
+ * though we will still process acknowledgements.
+ *
+ * CHANGED with 2.0 -> now we may send tunnel configuration
+ * info over the control channel.
+ */
+ if (true)
+ {
+ /* Initial handshake */
+ if (ks->state == S_INITIAL)
+ {
+ buf = reliable_get_buf_output_sequenced (ks->send_reliable);
+ if (buf)
+ {
+ ks->must_negotiate = now + session->opt->handshake_window;
+ ks->auth_deferred_expire = now + auth_deferred_expire_window (session->opt);
+
+ /* null buffer */
+ reliable_mark_active_outgoing (ks->send_reliable, buf, ks->initial_opcode);
+ INCR_GENERATED;
+
+ ks->state = S_PRE_START;
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "TLS: Initial Handshake, sid=%s",
+ session_id_print (&session->session_id, &gc));
+
+#ifdef ENABLE_MANAGEMENT
+ if (management && ks->initial_opcode != P_CONTROL_SOFT_RESET_V1)
+ {
+ management_set_state (management,
+ OPENVPN_STATE_WAIT,
+ NULL,
+ 0,
+ 0);
+ }
+#endif
+ }
+ }
+
+ /* Are we timed out on receive? */
+ if (now >= ks->must_negotiate)
+ {
+ if (ks->state < S_ACTIVE)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: TLS key negotiation failed to occur within %d seconds (check your network connectivity)",
+ session->opt->handshake_window);
+ goto error;
+ }
+ else /* assume that ks->state == S_ACTIVE */
+ {
+ dmsg (D_TLS_DEBUG_MED, "STATE S_NORMAL_OP");
+ ks->state = S_NORMAL_OP;
+ ks->must_negotiate = 0;
+ }
+ }
+
+ /* Wait for Initial Handshake ACK */
+ if (ks->state == S_PRE_START && FULL_SYNC)
+ {
+ ks->state = S_START;
+ state_change = true;
+ dmsg (D_TLS_DEBUG_MED, "STATE S_START");
+ }
+
+ /* Wait for ACK */
+ if (((ks->state == S_GOT_KEY && !session->opt->server) ||
+ (ks->state == S_SENT_KEY && session->opt->server)))
+ {
+ if (FULL_SYNC)
+ {
+ ks->established = now;
+ dmsg (D_TLS_DEBUG_MED, "STATE S_ACTIVE");
+ if (check_debug_level (D_HANDSHAKE))
+ print_details (ks->ssl, "Control Channel:");
+ state_change = true;
+ ks->state = S_ACTIVE;
+ INCR_SUCCESS;
+
+ /* Set outgoing address for data channel packets */
+ link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name, session->opt->es);
+
+ /* Flush any payload packets that were buffered before our state transitioned to S_ACTIVE */
+ flush_payload_buffer (multi, ks);
+
+#ifdef MEASURE_TLS_HANDSHAKE_STATS
+ show_tls_performance_stats();
+#endif
+ }
+ }
+
+ /* Reliable buffer to outgoing TCP/UDP (send up to CONTROL_SEND_ACK_MAX ACKs
+ for previously received packets) */
+ if (!to_link->len && reliable_can_send (ks->send_reliable))
+ {
+ int opcode;
+ struct buffer b;
+
+ buf = reliable_send (ks->send_reliable, &opcode);
+ ASSERT (buf);
+ b = *buf;
+ INCR_SENT;
+
+ write_control_auth (session, ks, &b, to_link_addr, opcode,
+ CONTROL_SEND_ACK_MAX, true);
+ *to_link = b;
+ active = true;
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Reliable -> TCP/UDP");
+ break;
+ }
+
+#ifndef TLS_AGGREGATE_ACK
+ /* Send 1 or more ACKs (each received control packet gets one ACK) */
+ if (!to_link->len && !reliable_ack_empty (ks->rec_ack))
+ {
+ buf = &ks->ack_write_buf;
+ ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
+ write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+ RELIABLE_ACK_SIZE, false);
+ *to_link = *buf;
+ active = true;
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP");
+ break;
+ }
+#endif
+
+ /* Write incoming ciphertext to TLS object */
+ buf = reliable_get_buf_sequenced (ks->rec_reliable);
+ if (buf)
+ {
+ int status = 0;
+ if (buf->len)
+ {
+ status = key_state_write_ciphertext (multi, ks, buf);
+ if (status == -1)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Incoming Ciphertext -> TLS object write error");
+ goto error;
+ }
+ }
+ else
+ {
+ status = 1;
+ }
+ if (status == 1)
+ {
+ reliable_mark_deleted (ks->rec_reliable, buf, true);
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Incoming Ciphertext -> TLS");
+ }
+ }
+
+ /* Read incoming plaintext from TLS object */
+ buf = &ks->plaintext_read_buf;
+ if (!buf->len)
+ {
+ int status;
+
+ ASSERT (buf_init (buf, 0));
+ status = key_state_read_plaintext (multi, ks, buf, TLS_CHANNEL_BUF_SIZE);
+ update_time ();
+ if (status == -1)
+ {
+ msg (D_TLS_ERRORS, "TLS Error: TLS object -> incoming plaintext read error");
+ goto error;
+ }
+ if (status == 1)
+ {
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "TLS -> Incoming Plaintext");
+ }
+#if 0 /* show null plaintext reads */
+ if (!status)
+ msg (M_INFO, "TLS plaintext read -> NULL return");
+#endif
+ }
+
+ /* Send Key */
+ buf = &ks->plaintext_write_buf;
+ if (!buf->len && ((ks->state == S_START && !session->opt->server) ||
+ (ks->state == S_GOT_KEY && session->opt->server)))
+ {
+ if (session->opt->key_method == 1)
+ {
+ if (!key_method_1_write (buf, session))
+ goto error;
+ }
+ else if (session->opt->key_method == 2)
+ {
+ if (!key_method_2_write (buf, session))
+ goto error;
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ state_change = true;
+ dmsg (D_TLS_DEBUG_MED, "STATE S_SENT_KEY");
+ ks->state = S_SENT_KEY;
+ }
+
+ /* Receive Key */
+ buf = &ks->plaintext_read_buf;
+ if (buf->len
+ && ((ks->state == S_SENT_KEY && !session->opt->server)
+ || (ks->state == S_START && session->opt->server)))
+ {
+ if (session->opt->key_method == 1)
+ {
+ if (!key_method_1_read (buf, session))
+ goto error;
+ }
+ else if (session->opt->key_method == 2)
+ {
+ if (!key_method_2_read (buf, multi, session))
+ goto error;
+ }
+ else
+ {
+ ASSERT (0);
+ }
+
+ state_change = true;
+ dmsg (D_TLS_DEBUG_MED, "STATE S_GOT_KEY");
+ ks->state = S_GOT_KEY;
+ }
+
+ /* Write outgoing plaintext to TLS object */
+ buf = &ks->plaintext_write_buf;
+ if (buf->len)
+ {
+ int status = key_state_write_plaintext (multi, ks, buf);
+ if (status == -1)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: Outgoing Plaintext -> TLS object write error");
+ goto error;
+ }
+ if (status == 1)
+ {
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Outgoing Plaintext -> TLS");
+ }
+ }
+
+ /* Outgoing Ciphertext to reliable buffer */
+ if (ks->state >= S_START)
+ {
+ buf = reliable_get_buf_output_sequenced (ks->send_reliable);
+ if (buf)
+ {
+ int status = key_state_read_ciphertext (multi, ks, buf, PAYLOAD_SIZE_DYNAMIC (&multi->opt.frame));
+ if (status == -1)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Ciphertext -> reliable TCP/UDP transport read error");
+ goto error;
+ }
+ if (status == 1)
+ {
+ reliable_mark_active_outgoing (ks->send_reliable, buf, P_CONTROL_V1);
+ INCR_GENERATED;
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Outgoing Ciphertext -> Reliable");
+ }
+ }
+ }
+ }
+ }
+ while (state_change);
+
+ update_time ();
+
+#ifdef TLS_AGGREGATE_ACK
+ /* Send 1 or more ACKs (each received control packet gets one ACK) */
+ if (!to_link->len && !reliable_ack_empty (ks->rec_ack))
+ {
+ buf = &ks->ack_write_buf;
+ ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
+ write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+ RELIABLE_ACK_SIZE, false);
+ *to_link = *buf;
+ active = true;
+ state_change = true;
+ dmsg (D_TLS_DEBUG, "Dedicated ACK -> TCP/UDP");
+ }
+#endif
+
+ /* When should we wake up again? */
+ {
+ if (ks->state >= S_INITIAL)
+ {
+ compute_earliest_wakeup (wakeup,
+ reliable_send_timeout (ks->send_reliable));
+
+ if (ks->must_negotiate)
+ compute_earliest_wakeup (wakeup, ks->must_negotiate - now);
+ }
+
+ if (ks->established && session->opt->renegotiate_seconds)
+ compute_earliest_wakeup (wakeup,
+ ks->established + session->opt->renegotiate_seconds - now);
+
+ /* prevent event-loop spinning by setting minimum wakeup of 1 second */
+ if (*wakeup <= 0)
+ {
+ *wakeup = 1;
+
+ /* if we had something to send to remote, but to_link was busy,
+ let caller know we need to be called again soon */
+ active = true;
+ }
+
+ dmsg (D_TLS_DEBUG, "TLS: tls_process: timeout set to %d", *wakeup);
+
+ gc_free (&gc);
+ return active;
+ }
+
+error:
+ ERR_clear_error ();
+ ks->state = S_ERROR;
+ msg (D_TLS_ERRORS, "TLS Error: TLS handshake failed");
+ INCR_ERROR;
+ gc_free (&gc);
+ return false;
+}
+
+#undef ks
+#undef ks_lame
+
+/*
+ * Called by the top-level event loop.
+ *
+ * Basically decides if we should call tls_process for
+ * the active or untrusted sessions.
+ */
+
+int
+tls_multi_process (struct tls_multi *multi,
+ struct buffer *to_link,
+ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup)
+{
+ struct gc_arena gc = gc_new ();
+ int i;
+ int active = TLSMP_INACTIVE;
+ bool error = false;
+ int tas;
+
+ perf_push (PERF_TLS_MULTI_PROCESS);
+
+ ERR_clear_error ();
+
+ /*
+ * Process each session object having state of S_INITIAL or greater,
+ * and which has a defined remote IP addr.
+ */
+
+ for (i = 0; i < TM_SIZE; ++i)
+ {
+ struct tls_session *session = &multi->session[i];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+ struct key_state *ks_lame = &session->key[KS_LAME_DUCK];
+
+ /* set initial remote address */
+ if (i == TM_ACTIVE && ks->state == S_INITIAL &&
+ link_socket_actual_defined (&to_link_socket_info->lsa->actual))
+ ks->remote_addr = to_link_socket_info->lsa->actual;
+
+ dmsg (D_TLS_DEBUG,
+ "TLS: tls_multi_process: i=%d state=%s, mysid=%s, stored-sid=%s, stored-ip=%s",
+ i,
+ state_name (ks->state),
+ session_id_print (&session->session_id, &gc),
+ session_id_print (&ks->session_id_remote, &gc),
+ print_link_socket_actual (&ks->remote_addr, &gc));
+
+ if (ks->state >= S_INITIAL && link_socket_actual_defined (&ks->remote_addr))
+ {
+ struct link_socket_actual *tla = NULL;
+
+ update_time ();
+
+ if (tls_process (multi, session, to_link, &tla,
+ to_link_socket_info, wakeup))
+ active = TLSMP_ACTIVE;
+
+ /*
+ * If tls_process produced an outgoing packet,
+ * return the link_socket_actual object (which
+ * contains the outgoing address).
+ */
+ if (tla)
+ {
+ multi->to_link_addr = *tla;
+ *to_link_addr = &multi->to_link_addr;
+ }
+
+ /*
+ * If tls_process hits an error:
+ * (1) If the session has an unexpired lame duck key, preserve it.
+ * (2) Reinitialize the session.
+ * (3) Increment soft error count
+ */
+ if (ks->state == S_ERROR)
+ {
+ ++multi->n_soft_errors;
+
+ if (i == TM_ACTIVE)
+ error = true;
+
+ if (i == TM_ACTIVE
+ && ks_lame->state >= S_ACTIVE
+ && !multi->opt.single_session)
+ move_session (multi, TM_LAME_DUCK, TM_ACTIVE, true);
+ else
+ reset_session (multi, session);
+ }
+ }
+ }
+
+ update_time ();
+
+ tas = tls_authentication_status (multi, TLS_MULTI_AUTH_STATUS_INTERVAL);
+
+ /*
+ * If lame duck session expires, kill it.
+ */
+ if (lame_duck_must_die (&multi->session[TM_LAME_DUCK], wakeup)) {
+ tls_session_free (&multi->session[TM_LAME_DUCK], true);
+ msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: killed expiring key");
+ }
+
+ /*
+ * If untrusted session achieves TLS authentication,
+ * move it to active session, usurping any prior session.
+ *
+ * A semi-trusted session is one in which the certificate authentication
+ * succeeded (if cert verification is enabled) but the username/password
+ * verification failed. A semi-trusted session can forward data on the
+ * TLS control channel but not on the tunnel channel.
+ */
+ if (DECRYPT_KEY_ENABLED (multi, &multi->session[TM_UNTRUSTED].key[KS_PRIMARY])) {
+ move_session (multi, TM_ACTIVE, TM_UNTRUSTED, true);
+ msg (D_TLS_DEBUG_LOW, "TLS: tls_multi_process: untrusted session promoted to %strusted",
+ tas == TLS_AUTHENTICATION_SUCCEEDED ? "" : "semi-");
+ }
+
+ /*
+ * A hard error means that TM_ACTIVE hit an S_ERROR state and that no
+ * other key state objects are S_ACTIVE or higher.
+ */
+ if (error)
+ {
+ for (i = 0; i < (int) SIZE (multi->key_scan); ++i)
+ {
+ if (multi->key_scan[i]->state >= S_ACTIVE)
+ goto nohard;
+ }
+ ++multi->n_hard_errors;
+ }
+ nohard:
+
+#ifdef ENABLE_DEBUG
+ /* DEBUGGING -- flood peer with repeating connection attempts */
+ {
+ const int throw_level = GREMLIN_CONNECTION_FLOOD_LEVEL (multi->opt.gremlin);
+ if (throw_level)
+ {
+ for (i = 0; i < (int) SIZE (multi->key_scan); ++i)
+ {
+ if (multi->key_scan[i]->state >= throw_level)
+ {
+ ++multi->n_hard_errors;
+ ++multi->n_soft_errors;
+ }
+ }
+ }
+ }
+#endif
+
+ perf_pop ();
+ gc_free (&gc);
+
+ return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active;
+}
+
+/*
+ * Pre and post-process the encryption & decryption buffers in order
+ * to implement a multiplexed TLS channel over the TCP/UDP port.
+ */
+
+/*
+ *
+ * When we are in TLS mode, this is the first routine which sees
+ * an incoming packet.
+ *
+ * If it's a data packet, we set opt so that our caller can
+ * decrypt it. We also give our caller the appropriate decryption key.
+ *
+ * If it's a control packet, we authenticate it and process it,
+ * possibly creating a new tls_session if it represents the
+ * first packet of a new session. For control packets, we will
+ * also zero the size of *buf so that our caller ignores the
+ * packet on our return.
+ *
+ * Note that openvpn only allows one active session at a time,
+ * so a new session (once authenticated) will always usurp
+ * an old session.
+ *
+ * Return true if input was an authenticated control channel
+ * packet.
+ *
+ * If we are running in TLS thread mode, all public routines
+ * below this point must be called with the L_TLS lock held.
+ */
+
+bool
+tls_pre_decrypt (struct tls_multi *multi,
+ const struct link_socket_actual *from,
+ struct buffer *buf,
+ struct crypto_options *opt)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+ if (buf->len > 0)
+ {
+ int i;
+ int op;
+ int key_id;
+
+ /* get opcode and key ID */
+ {
+ uint8_t c = *BPTR (buf);
+ op = c >> P_OPCODE_SHIFT;
+ key_id = c & P_KEY_ID_MASK;
+ }
+
+ if (op == P_DATA_V1)
+ { /* data channel packet */
+ for (i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = multi->key_scan[i];
+
+ /*
+ * This is the basic test of TLS state compatibility between a local OpenVPN
+ * instance and its remote peer.
+ *
+ * If the test fails, it tells us that we are getting a packet from a source
+ * which claims reference to a prior negotiated TLS session, but the local
+ * OpenVPN instance has no memory of such a negotiation.
+ *
+ * It almost always occurs on UDP sessions when the passive side of the
+ * connection is restarted without the active side restarting as well (the
+ * passive side is the server which only listens for the connections, the
+ * active side is the client which initiates connections).
+ */
+ if (DECRYPT_KEY_ENABLED (multi, ks)
+ && key_id == ks->key_id
+ && ks->authenticated
+#ifdef ENABLE_DEF_AUTH
+ && !ks->auth_deferred
+#endif
+ && link_socket_actual_match (from, &ks->remote_addr))
+ {
+ /* return appropriate data channel decrypt key in opt */
+ opt->key_ctx_bi = &ks->key;
+ opt->packet_id = multi->opt.replay ? &ks->packet_id : NULL;
+ opt->pid_persist = NULL;
+ opt->flags &= multi->opt.crypto_flags_and;
+ opt->flags |= multi->opt.crypto_flags_or;
+ ASSERT (buf_advance (buf, 1));
+ ++ks->n_packets;
+ ks->n_bytes += buf->len;
+ dmsg (D_TLS_KEYSELECT,
+ "TLS: tls_pre_decrypt, key_id=%d, IP=%s",
+ key_id, print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return ret;
+ }
+#if 0 /* keys out of sync? */
+ else
+ {
+ dmsg (D_TLS_ERRORS, "TLS_PRE_DECRYPT: [%d] dken=%d rkid=%d lkid=%d auth=%d def=%d match=%d",
+ i,
+ DECRYPT_KEY_ENABLED (multi, ks),
+ key_id,
+ ks->key_id,
+ ks->authenticated,
+#ifdef ENABLE_DEF_AUTH
+ ks->auth_deferred,
+#else
+ -1,
+#endif
+ link_socket_actual_match (from, &ks->remote_addr));
+ }
+#endif
+ }
+
+ msg (D_TLS_ERRORS,
+ "TLS Error: local/remote TLS keys are out of sync: %s [%d]",
+ print_link_socket_actual (from, &gc), key_id);
+ goto error_lite;
+ }
+ else /* control channel packet */
+ {
+ bool do_burst = false;
+ bool new_link = false;
+ struct session_id sid; /* remote session ID */
+
+ /* verify legal opcode */
+ if (op < P_FIRST_OPCODE || op > P_LAST_OPCODE)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: unknown opcode received from %s op=%d",
+ print_link_socket_actual (from, &gc), op);
+ goto error;
+ }
+
+ /* hard reset ? */
+ if (is_hard_reset (op, 0))
+ {
+ /* verify client -> server or server -> client connection */
+ if (((op == P_CONTROL_HARD_RESET_CLIENT_V1
+ || op == P_CONTROL_HARD_RESET_CLIENT_V2) && !multi->opt.server)
+ || ((op == P_CONTROL_HARD_RESET_SERVER_V1
+ || op == P_CONTROL_HARD_RESET_SERVER_V2) && multi->opt.server))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: client->client or server->server connection attempted from %s",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+ }
+
+ /*
+ * Authenticate Packet
+ */
+ dmsg (D_TLS_DEBUG, "TLS: control channel, op=%s, IP=%s",
+ packet_opcode_name (op), print_link_socket_actual (from, &gc));
+
+ /* get remote session-id */
+ {
+ struct buffer tmp = *buf;
+ buf_advance (&tmp, 1);
+ if (!session_id_read (&sid, &tmp) || !session_id_defined (&sid))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: session-id not found in packet from %s",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+ }
+
+ /* use session ID to match up packet with appropriate tls_session object */
+ for (i = 0; i < TM_SIZE; ++i)
+ {
+ struct tls_session *session = &multi->session[i];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+
+ dmsg (D_TLS_DEBUG,
+ "TLS: initial packet test, i=%d state=%s, mysid=%s, rec-sid=%s, rec-ip=%s, stored-sid=%s, stored-ip=%s",
+ i,
+ state_name (ks->state),
+ session_id_print (&session->session_id, &gc),
+ session_id_print (&sid, &gc),
+ print_link_socket_actual (from, &gc),
+ session_id_print (&ks->session_id_remote, &gc),
+ print_link_socket_actual (&ks->remote_addr, &gc));
+
+ if (session_id_equal (&ks->session_id_remote, &sid))
+ /* found a match */
+ {
+ if (i == TM_LAME_DUCK) {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: received control packet with stale session-id=%s",
+ session_id_print (&sid, &gc));
+ goto error;
+ }
+ dmsg (D_TLS_DEBUG,
+ "TLS: found match, session[%d], sid=%s",
+ i, session_id_print (&sid, &gc));
+ break;
+ }
+ }
+
+ /*
+ * Initial packet received.
+ */
+
+ if (i == TM_SIZE && is_hard_reset (op, 0))
+ {
+ struct tls_session *session = &multi->session[TM_ACTIVE];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+
+ if (!is_hard_reset (op, multi->opt.key_method))
+ {
+ msg (D_TLS_ERRORS, "TLS ERROR: initial packet local/remote key_method mismatch, local key_method=%d, op=%s",
+ multi->opt.key_method,
+ packet_opcode_name (op));
+ goto error;
+ }
+
+ /*
+ * If we have no session currently in progress, the initial packet will
+ * open a new session in TM_ACTIVE rather than TM_UNTRUSTED.
+ */
+ if (!session_id_defined (&ks->session_id_remote))
+ {
+ if (multi->opt.single_session && multi->n_sessions)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [1]",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_set_state (management,
+ OPENVPN_STATE_AUTH,
+ NULL,
+ 0,
+ 0);
+ }
+#endif
+
+ msg (D_TLS_DEBUG_LOW,
+ "TLS: Initial packet from %s, sid=%s",
+ print_link_socket_actual (from, &gc),
+ session_id_print (&sid, &gc));
+
+ do_burst = true;
+ new_link = true;
+ i = TM_ACTIVE;
+ session->untrusted_addr = *from;
+ }
+ }
+
+ if (i == TM_SIZE && is_hard_reset (op, 0))
+ {
+ /*
+ * No match with existing sessions,
+ * probably a new session.
+ */
+ struct tls_session *session = &multi->session[TM_UNTRUSTED];
+
+ /*
+ * If --single-session, don't allow any hard-reset connection request
+ * unless it the the first packet of the session.
+ */
+ if (multi->opt.single_session)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Cannot accept new session request from %s due to session context expire or --single-session [2]",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+ if (!is_hard_reset (op, multi->opt.key_method))
+ {
+ msg (D_TLS_ERRORS, "TLS ERROR: new session local/remote key_method mismatch, local key_method=%d, op=%s",
+ multi->opt.key_method,
+ packet_opcode_name (op));
+ goto error;
+ }
+
+ if (!read_control_auth (buf, &session->tls_auth, from))
+ goto error;
+
+ /*
+ * New session-initiating control packet is authenticated at this point,
+ * assuming that the --tls-auth command line option was used.
+ *
+ * Without --tls-auth, we leave authentication entirely up to TLS.
+ */
+ msg (D_TLS_DEBUG_LOW,
+ "TLS: new session incoming connection from %s",
+ print_link_socket_actual (from, &gc));
+
+ new_link = true;
+ i = TM_UNTRUSTED;
+ session->untrusted_addr = *from;
+ }
+ else
+ {
+ struct tls_session *session = &multi->session[i];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+
+ /*
+ * Packet must belong to an existing session.
+ */
+ if (i != TM_ACTIVE && i != TM_UNTRUSTED)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Unroutable control packet received from %s (si=%d op=%s)",
+ print_link_socket_actual (from, &gc),
+ i,
+ packet_opcode_name (op));
+ goto error;
+ }
+
+ /*
+ * Verify remote IP address
+ */
+ if (!new_link && !link_socket_actual_match (&ks->remote_addr, from))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+ /*
+ * Remote is requesting a key renegotiation
+ */
+ if (op == P_CONTROL_SOFT_RESET_V1
+ && DECRYPT_KEY_ENABLED (multi, ks))
+ {
+ if (!read_control_auth (buf, &session->tls_auth, from))
+ goto error;
+
+ key_state_soft_reset (session);
+
+ dmsg (D_TLS_DEBUG,
+ "TLS: received P_CONTROL_SOFT_RESET_V1 s=%d sid=%s",
+ i, session_id_print (&sid, &gc));
+ }
+ else
+ {
+ /*
+ * Remote responding to our key renegotiation request?
+ */
+ if (op == P_CONTROL_SOFT_RESET_V1)
+ do_burst = true;
+
+ if (!read_control_auth (buf, &session->tls_auth, from))
+ goto error;
+
+ dmsg (D_TLS_DEBUG,
+ "TLS: received control channel packet s#=%d sid=%s",
+ i, session_id_print (&sid, &gc));
+ }
+ }
+
+ /*
+ * We have an authenticated packet (if --tls-auth was set).
+ * Now pass to our reliability level which deals with
+ * packet acknowledgements, retransmits, sequencing, etc.
+ */
+ {
+ struct tls_session *session = &multi->session[i];
+ struct key_state *ks = &session->key[KS_PRIMARY];
+
+ /* Make sure we were initialized and that we're not in an error state */
+ ASSERT (ks->state != S_UNDEF);
+ ASSERT (ks->state != S_ERROR);
+ ASSERT (session_id_defined (&session->session_id));
+
+ /* Let our caller know we processed a control channel packet */
+ ret = true;
+
+ /*
+ * Set our remote address and remote session_id
+ */
+ if (new_link)
+ {
+ ks->session_id_remote = sid;
+ ks->remote_addr = *from;
+ ++multi->n_sessions;
+ }
+ else if (!link_socket_actual_match (&ks->remote_addr, from))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Existing session control channel packet from unknown IP address: %s",
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+ /*
+ * Should we do a retransmit of all unacknowledged packets in
+ * the send buffer? This improves the start-up efficiency of the
+ * initial key negotiation after the 2nd peer comes online.
+ */
+ if (do_burst && !session->burst)
+ {
+ reliable_schedule_now (ks->send_reliable);
+ session->burst = true;
+ }
+
+ /* Check key_id */
+ if (ks->key_id != key_id)
+ {
+ msg (D_TLS_ERRORS,
+ "TLS ERROR: local/remote key IDs out of sync (%d/%d) ID: %s",
+ ks->key_id, key_id, print_key_id (multi, &gc));
+ goto error;
+ }
+
+ /*
+ * Process incoming ACKs for packets we can now
+ * delete from reliable send buffer
+ */
+ {
+ /* buffers all packet IDs to delete from send_reliable */
+ struct reliable_ack send_ack;
+
+ send_ack.len = 0;
+ if (!reliable_ack_read (&send_ack, buf, &session->session_id))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: reading acknowledgement record from packet");
+ goto error;
+ }
+ reliable_send_purge (ks->send_reliable, &send_ack);
+ }
+
+ if (op != P_ACK_V1 && reliable_can_get (ks->rec_reliable))
+ {
+ packet_id_type id;
+
+ /* Extract the packet ID from the packet */
+ if (reliable_ack_read_packet_id (buf, &id))
+ {
+ /* Avoid deadlock by rejecting packet that would de-sequentialize receive buffer */
+ if (reliable_wont_break_sequentiality (ks->rec_reliable, id))
+ {
+ if (reliable_not_replay (ks->rec_reliable, id))
+ {
+ /* Save incoming ciphertext packet to reliable buffer */
+ struct buffer *in = reliable_get_buf (ks->rec_reliable);
+ ASSERT (in);
+ ASSERT (buf_copy (in, buf));
+ reliable_mark_active_incoming (ks->rec_reliable, in, id, op);
+ }
+
+ /* Process outgoing acknowledgment for packet just received, even if it's a replay */
+ reliable_ack_acknowledge_packet_id (ks->rec_ack, id);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ done:
+ buf->len = 0;
+ opt->key_ctx_bi = NULL;
+ opt->packet_id = NULL;
+ opt->pid_persist = NULL;
+ opt->flags &= multi->opt.crypto_flags_and;
+ gc_free (&gc);
+ return ret;
+
+ error:
+ ++multi->n_soft_errors;
+ error_lite:
+ ERR_clear_error ();
+ goto done;
+}
+
+/*
+ * This function is similar to tls_pre_decrypt, except it is called
+ * when we are in server mode and receive an initial incoming
+ * packet. Note that we don't modify
+ * any state in our parameter objects. The purpose is solely to
+ * determine whether we should generate a client instance
+ * object, in which case true is returned.
+ *
+ * This function is essentially the first-line HMAC firewall
+ * on the UDP port listener in --mode server mode.
+ */
+bool
+tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
+ const struct link_socket_actual *from,
+ const struct buffer *buf)
+
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+ if (buf->len > 0)
+ {
+ int op;
+ int key_id;
+
+ /* get opcode and key ID */
+ {
+ uint8_t c = *BPTR (buf);
+ op = c >> P_OPCODE_SHIFT;
+ key_id = c & P_KEY_ID_MASK;
+ }
+
+ /* this packet is from an as-yet untrusted source, so
+ scrutinize carefully */
+
+ if (op != P_CONTROL_HARD_RESET_CLIENT_V2)
+ {
+ /*
+ * This can occur due to bogus data or DoS packets.
+ */
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: No TLS state for client %s, opcode=%d",
+ print_link_socket_actual (from, &gc),
+ op);
+ goto error;
+ }
+
+ if (key_id != 0)
+ {
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: Unknown key ID (%d) received from %s -- 0 was expected",
+ key_id,
+ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+ if (buf->len > EXPANDED_SIZE_DYNAMIC (&tas->frame))
+ {
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: Large packet (size %d) received from %s -- a packet no larger than %d bytes was expected",
+ buf->len,
+ print_link_socket_actual (from, &gc),
+ EXPANDED_SIZE_DYNAMIC (&tas->frame));
+ goto error;
+ }
+
+ {
+ struct buffer newbuf = clone_buf (buf);
+ struct crypto_options co = tas->tls_auth_options;
+ bool status;
+
+ /*
+ * We are in read-only mode at this point with respect to TLS
+ * control channel state. After we build a new client instance
+ * object, we will process this session-initiating packet for real.
+ */
+ co.flags |= CO_IGNORE_PACKET_ID;
+
+ /* HMAC test, if --tls-auth was specified */
+ status = read_control_auth (&newbuf, &co, from);
+ free_buf (&newbuf);
+ if (!status)
+ goto error;
+
+ /*
+ * At this point, if --tls-auth is being used, we know that
+ * the packet has passed the HMAC test, but we don't know if
+ * it is a replay yet. We will attempt to defeat replays
+ * by not advancing to the S_START state until we
+ * receive an ACK from our first reply to the client
+ * that includes an HMAC of our randomly generated 64 bit
+ * session ID.
+ *
+ * On the other hand if --tls-auth is not being used, we
+ * will proceed to begin the TLS authentication
+ * handshake with only cursory integrity checks having
+ * been performed, since we will be leaving the task
+ * of authentication solely up to TLS.
+ */
+
+ ret = true;
+ }
+ }
+ gc_free (&gc);
+ return ret;
+
+ error:
+ ERR_clear_error ();
+ gc_free (&gc);
+ return ret;
+}
+
+/* Choose the key with which to encrypt a data packet */
+void
+tls_pre_encrypt (struct tls_multi *multi,
+ struct buffer *buf, struct crypto_options *opt)
+{
+ multi->save_ks = NULL;
+ if (buf->len > 0)
+ {
+ int i;
+ struct key_state *ks_select = NULL;
+ for (i = 0; i < KEY_SCAN_SIZE; ++i)
+ {
+ struct key_state *ks = multi->key_scan[i];
+ if (ks->state >= S_ACTIVE
+ && ks->authenticated
+#ifdef ENABLE_DEF_AUTH
+ && !ks->auth_deferred
+#endif
+ )
+ {
+ if (!ks_select)
+ ks_select = ks;
+ if (now >= ks->auth_deferred_expire)
+ {
+ ks_select = ks;
+ break;
+ }
+ }
+ }
+
+ if (ks_select)
+ {
+ opt->key_ctx_bi = &ks_select->key;
+ opt->packet_id = multi->opt.replay ? &ks_select->packet_id : NULL;
+ opt->pid_persist = NULL;
+ opt->flags &= multi->opt.crypto_flags_and;
+ opt->flags |= multi->opt.crypto_flags_or;
+ multi->save_ks = ks_select;
+ dmsg (D_TLS_KEYSELECT, "TLS: tls_pre_encrypt: key_id=%d", ks_select->key_id);
+ return;
+ }
+ else
+ {
+ struct gc_arena gc = gc_new ();
+ dmsg (D_TLS_KEYSELECT, "TLS Warning: no data channel send key available: %s",
+ print_key_id (multi, &gc));
+ gc_free (&gc);
+ }
+ }
+
+ buf->len = 0;
+ opt->key_ctx_bi = NULL;
+ opt->packet_id = NULL;
+ opt->pid_persist = NULL;
+ opt->flags &= multi->opt.crypto_flags_and;
+}
+
+/* Prepend the appropriate opcode to encrypted buffer prior to TCP/UDP send */
+void
+tls_post_encrypt (struct tls_multi *multi, struct buffer *buf)
+{
+ struct key_state *ks;
+ uint8_t *op;
+
+ ks = multi->save_ks;
+ multi->save_ks = NULL;
+ if (buf->len > 0)
+ {
+ ASSERT (ks);
+ ASSERT (op = buf_prepend (buf, 1));
+ *op = (P_DATA_V1 << P_OPCODE_SHIFT) | ks->key_id;
+ ++ks->n_packets;
+ ks->n_bytes += buf->len;
+ }
+}
+
+/*
+ * Send a payload over the TLS control channel.
+ * Called externally.
+ */
+
+bool
+tls_send_payload (struct tls_multi *multi,
+ const uint8_t *data,
+ int size)
+{
+ struct tls_session *session;
+ struct key_state *ks;
+ bool ret = false;
+
+ ERR_clear_error ();
+
+ ASSERT (multi);
+
+ session = &multi->session[TM_ACTIVE];
+ ks = &session->key[KS_PRIMARY];
+
+ if (ks->state >= S_ACTIVE)
+ {
+ if (key_state_write_plaintext_const (multi, ks, data, size) == 1)
+ ret = true;
+ }
+ else
+ {
+ if (!ks->paybuf)
+ ks->paybuf = buffer_list_new (0);
+ buffer_list_push_data (ks->paybuf, data, (size_t)size);
+ ret = true;
+ }
+
+ ERR_clear_error ();
+
+ return ret;
+}
+
+bool
+tls_rec_payload (struct tls_multi *multi,
+ struct buffer *buf)
+{
+ struct tls_session *session;
+ struct key_state *ks;
+ bool ret = false;
+
+ ERR_clear_error ();
+
+ ASSERT (multi);
+
+ session = &multi->session[TM_ACTIVE];
+ ks = &session->key[KS_PRIMARY];
+
+ if (ks->state >= S_ACTIVE && BLEN (&ks->plaintext_read_buf))
+ {
+ if (buf_copy (buf, &ks->plaintext_read_buf))
+ ret = true;
+ ks->plaintext_read_buf.len = 0;
+ }
+
+ ERR_clear_error ();
+
+ return ret;
+}
+
+/*
+ * Dump a human-readable rendition of an openvpn packet
+ * into a garbage collectable string which is returned.
+ */
+const char *
+protocol_dump (struct buffer *buffer, unsigned int flags, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ struct buffer buf = *buffer;
+
+ uint8_t c;
+ int op;
+ int key_id;
+
+ int tls_auth_hmac_size = (flags & PD_TLS_AUTH_HMAC_SIZE_MASK);
+
+ if (buf.len <= 0)
+ {
+ buf_printf (&out, "DATA UNDEF len=%d", buf.len);
+ goto done;
+ }
+
+ if (!(flags & PD_TLS))
+ goto print_data;
+
+ /*
+ * Initial byte (opcode)
+ */
+ if (!buf_read (&buf, &c, sizeof (c)))
+ goto done;
+ op = (c >> P_OPCODE_SHIFT);
+ key_id = c & P_KEY_ID_MASK;
+ buf_printf (&out, "%s kid=%d", packet_opcode_name (op), key_id);
+
+ if (op == P_DATA_V1)
+ goto print_data;
+
+ /*
+ * Session ID
+ */
+ {
+ struct session_id sid;
+
+ if (!session_id_read (&sid, &buf))
+ goto done;
+ if (flags & PD_VERBOSE)
+ buf_printf (&out, " sid=%s", session_id_print (&sid, gc));
+ }
+
+ /*
+ * tls-auth hmac + packet_id
+ */
+ if (tls_auth_hmac_size)
+ {
+ struct packet_id_net pin;
+ uint8_t tls_auth_hmac[MAX_HMAC_KEY_LENGTH];
+
+ ASSERT (tls_auth_hmac_size <= MAX_HMAC_KEY_LENGTH);
+
+ if (!buf_read (&buf, tls_auth_hmac, tls_auth_hmac_size))
+ goto done;
+ if (flags & PD_VERBOSE)
+ buf_printf (&out, " tls_hmac=%s", format_hex (tls_auth_hmac, tls_auth_hmac_size, 0, gc));
+
+ if (!packet_id_read (&pin, &buf, true))
+ goto done;
+ buf_printf(&out, " pid=%s", packet_id_net_print (&pin, (flags & PD_VERBOSE), gc));
+ }
+
+ /*
+ * ACK list
+ */
+ buf_printf (&out, " %s", reliable_ack_print(&buf, (flags & PD_VERBOSE), gc));
+
+ if (op == P_ACK_V1)
+ goto done;
+
+ /*
+ * Packet ID
+ */
+ {
+ packet_id_type l;
+ if (!buf_read (&buf, &l, sizeof (l)))
+ goto done;
+ l = ntohpid (l);
+ buf_printf (&out, " pid=" packet_id_format, (packet_id_print_type)l);
+ }
+
+print_data:
+ if (flags & PD_SHOW_DATA)
+ buf_printf (&out, " DATA %s", format_hex (BPTR (&buf), BLEN (&buf), 80, gc));
+ else
+ buf_printf (&out, " DATA len=%d", buf.len);
+
+done:
+ return BSTR (&out);
+}
+
+#else
+static void dummy(void) {}
+#endif /* USE_CRYPTO && USE_SSL*/
diff --git a/ssl.h b/ssl.h
new file mode 100644
index 0000000..8464492
--- /dev/null
+++ b/ssl.h
@@ -0,0 +1,849 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OPENVPN_SSL_H
+#define OPENVPN_SSL_H
+
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+
+#include "basic.h"
+#include "common.h"
+#include "crypto.h"
+#include "packet_id.h"
+#include "session_id.h"
+#include "reliable.h"
+#include "socket.h"
+#include "mtu.h"
+#include "options.h"
+#include "plugin.h"
+
+/*
+ * OpenVPN Protocol, taken from ssl.h in OpenVPN source code.
+ *
+ * TCP/UDP Packet: This represents the top-level encapsulation.
+ *
+ * TCP/UDP packet format:
+ *
+ * Packet length (16 bits, unsigned) -- TCP only, always sent as
+ * plaintext. Since TCP is a stream protocol, the packet
+ * length words define the packetization of the stream.
+ *
+ * Packet opcode/key_id (8 bits) -- TLS only, not used in
+ * pre-shared secret mode.
+ * packet message type, a P_* constant (high 5 bits)
+ * key_id (low 3 bits, see key_id in struct tls_session
+ * below for comment). The key_id refers to an
+ * already negotiated TLS session. OpenVPN seamlessly
+ * renegotiates the TLS session by using a new key_id
+ * for the new session. Overlap (controlled by
+ * user definable parameters) between old and new TLS
+ * sessions is allowed, providing a seamless transition
+ * during tunnel operation.
+ *
+ * Payload (n bytes), which may be a P_CONTROL, P_ACK, or P_DATA
+ * message.
+ *
+ * Message types:
+ *
+ * P_CONTROL_HARD_RESET_CLIENT_V1 -- Key method 1, initial key from
+ * client, forget previous state.
+ *
+ * P_CONTROL_HARD_RESET_SERVER_V1 -- Key method 2, initial key
+ * from server, forget previous state.
+ *
+ * P_CONTROL_SOFT_RESET_V1 -- New key, with a graceful transition
+ * from old to new key in the sense that a transition window
+ * exists where both the old or new key_id can be used. OpenVPN
+ * uses two different forms of key_id. The first form is 64 bits
+ * and is used for all P_CONTROL messages. P_DATA messages on the
+ * other hand use a shortened key_id of 3 bits for efficiency
+ * reasons since the vast majority of OpenVPN packets in an
+ * active tunnel will be P_DATA messages. The 64 bit form
+ * is referred to as a session_id, while the 3 bit form is
+ * referred to as a key_id.
+ *
+ * P_CONTROL_V1 -- Control channel packet (usually TLS ciphertext).
+ *
+ * P_ACK_V1 -- Acknowledgement for P_CONTROL packets received.
+ *
+ * P_DATA_V1 -- Data channel packet containing actual tunnel data
+ * ciphertext.
+ *
+ * P_CONTROL_HARD_RESET_CLIENT_V2 -- Key method 2, initial key from
+ * client, forget previous state.
+ *
+ * P_CONTROL_HARD_RESET_SERVER_V2 -- Key method 2, initial key from
+ * server, forget previous state.
+ *
+ * P_CONTROL* and P_ACK Payload: The P_CONTROL message type
+ * indicates a TLS ciphertext packet which has been encapsulated
+ * inside of a reliability layer. The reliability layer is
+ * implemented as a straightforward ACK and retransmit model.
+ *
+ * P_CONTROL message format:
+ *
+ * local session_id (random 64 bit value to identify TLS session).
+ * HMAC signature of entire encapsulation header for integrity
+ * check if --tls-auth is specified (usually 16 or 20 bytes).
+ * packet-id for replay protection (4 or 8 bytes, includes
+ * sequence number and optional time_t timestamp).
+ * P_ACK packet_id array length (1 byte).
+ * P_ACK packet-id array (if length > 0).
+ * P_ACK remote session_id (if length > 0).
+ * message packet-id (4 bytes).
+ * TLS payload ciphertext (n bytes) (only for P_CONTROL).
+ *
+ * Once the TLS session has been initialized and authenticated,
+ * the TLS channel is used to exchange random key material for
+ * bidirectional cipher and HMAC keys which will be
+ * used to secure actual tunnel packets. OpenVPN currently
+ * implements two key methods. Key method 1 directly
+ * derives keys using random bits obtained from the RAND_bytes
+ * OpenSSL function. Key method 2 mixes random key material
+ * from both sides of the connection using the TLS PRF mixing
+ * function. Key method 2 is the preferred method and is the default
+ * for OpenVPN 2.0.
+ *
+ * TLS plaintext content:
+ *
+ * TLS plaintext packet (if key_method == 1):
+ *
+ * Cipher key length in bytes (1 byte).
+ * Cipher key (n bytes).
+ * HMAC key length in bytes (1 byte).
+ * HMAC key (n bytes).
+ * Options string (n bytes, null terminated, client/server options
+ * string should match).
+ *
+ * TLS plaintext packet (if key_method == 2):
+ *
+ * Literal 0 (4 bytes).
+ * key_method type (1 byte).
+ * key_source structure (pre_master only defined for client ->
+ * server).
+ * options_string_length, including null (2 bytes).
+ * Options string (n bytes, null terminated, client/server options
+ * string must match).
+ * [The username/password data below is optional, record can end
+ * at this point.]
+ * username_string_length, including null (2 bytes).
+ * Username string (n bytes, null terminated).
+ * password_string_length, including null (2 bytes).
+ * Password string (n bytes, null terminated).
+ *
+ * The P_DATA payload represents encrypted, encapsulated tunnel
+ * packets which tend to be either IP packets or Ethernet frames.
+ * This is essentially the "payload" of the VPN.
+ *
+ * P_DATA message content:
+ * HMAC of ciphertext IV + ciphertext (if not disabled by
+ * --auth none).
+ * Ciphertext IV (size is cipher-dependent, if not disabled by
+ * --no-iv).
+ * Tunnel packet ciphertext.
+ *
+ * P_DATA plaintext
+ * packet_id (4 or 8 bytes, if not disabled by --no-replay).
+ * In SSL/TLS mode, 4 bytes are used because the implementation
+ * can force a TLS renegotation before 2^32 packets are sent.
+ * In pre-shared key mode, 8 bytes are used (sequence number
+ * and time_t value) to allow long-term key usage without
+ * packet_id collisions.
+ * User plaintext (n bytes).
+ *
+ * Notes:
+ * (1) ACK messages can be encoded in either the dedicated
+ * P_ACK record or they can be prepended to a P_CONTROL message.
+ * (2) P_DATA and P_CONTROL/P_ACK use independent packet-id
+ * sequences because P_DATA is an unreliable channel while
+ * P_CONTROL/P_ACK is a reliable channel. Each use their
+ * own independent HMAC keys.
+ * (3) Note that when --tls-auth is used, all message types are
+ * protected with an HMAC signature, even the initial packets
+ * of the TLS handshake. This makes it easy for OpenVPN to
+ * throw away bogus packets quickly, without wasting resources
+ * on attempting a TLS handshake which will ultimately fail.
+ */
+
+/* Used in the TLS PRF function */
+#define KEY_EXPANSION_ID "OpenVPN"
+
+/* passwords */
+#define UP_TYPE_AUTH "Auth"
+#define UP_TYPE_PRIVATE_KEY "Private Key"
+
+/* packet opcode (high 5 bits) and key-id (low 3 bits) are combined in one byte */
+#define P_KEY_ID_MASK 0x07
+#define P_OPCODE_SHIFT 3
+
+/* packet opcodes -- the V1 is intended to allow protocol changes in the future */
+#define P_CONTROL_HARD_RESET_CLIENT_V1 1 /* initial key from client, forget previous state */
+#define P_CONTROL_HARD_RESET_SERVER_V1 2 /* initial key from server, forget previous state */
+#define P_CONTROL_SOFT_RESET_V1 3 /* new key, graceful transition from old to new key */
+#define P_CONTROL_V1 4 /* control channel packet (usually TLS ciphertext) */
+#define P_ACK_V1 5 /* acknowledgement for packets received */
+#define P_DATA_V1 6 /* data channel packet */
+
+/* indicates key_method >= 2 */
+#define P_CONTROL_HARD_RESET_CLIENT_V2 7 /* initial key from client, forget previous state */
+#define P_CONTROL_HARD_RESET_SERVER_V2 8 /* initial key from server, forget previous state */
+
+/* define the range of legal opcodes */
+#define P_FIRST_OPCODE 1
+#define P_LAST_OPCODE 8
+
+/* key negotiation states */
+#define S_ERROR -1
+#define S_UNDEF 0
+#define S_INITIAL 1 /* tls_init() was called */
+#define S_PRE_START 2 /* waiting for initial reset & acknowledgement */
+#define S_START 3 /* ready to exchange keys */
+#define S_SENT_KEY 4 /* client does S_SENT_KEY -> S_GOT_KEY */
+#define S_GOT_KEY 5 /* server does S_GOT_KEY -> S_SENT_KEY */
+#define S_ACTIVE 6 /* ready to exchange data channel packets */
+#define S_NORMAL_OP 7 /* normal operations */
+
+/*
+ * Are we ready to receive data channel packets?
+ *
+ * Also, if true, we can safely assume session has been
+ * authenticated by TLS.
+ *
+ * NOTE: Assumes S_SENT_KEY + 1 == S_GOT_KEY.
+ */
+#define DECRYPT_KEY_ENABLED(multi, ks) ((ks)->state >= (S_GOT_KEY - (multi)->opt.server))
+
+/* Should we aggregate TLS acknowledgements, and tack them onto control packets? */
+#define TLS_AGGREGATE_ACK
+
+/*
+ * If TLS_AGGREGATE_ACK, set the
+ * max number of acknowledgments that
+ * can "hitch a ride" on an outgoing
+ * non-P_ACK_V1 control packet.
+ */
+#define CONTROL_SEND_ACK_MAX 4
+
+/*
+ * Define number of buffers for send and receive in the reliability layer.
+ */
+#define TLS_RELIABLE_N_SEND_BUFFERS 4 /* also window size for reliablity layer */
+#define TLS_RELIABLE_N_REC_BUFFERS 8
+
+/*
+ * Various timeouts
+ */
+
+#define TLS_MULTI_REFRESH 15 /* call tls_multi_process once every n seconds */
+#define TLS_MULTI_HORIZON 2 /* call tls_multi_process frequently for n seconds after
+ every packet sent/received action */
+
+/* The SSL/TLS worker thread will wait at most this many seconds for the interprocess
+ communication pipe to the main thread to be ready to accept writes. */
+#define TLS_MULTI_THREAD_SEND_TIMEOUT 5
+
+/* Interval that tls_multi_process should call tls_authentication_status */
+#define TLS_MULTI_AUTH_STATUS_INTERVAL 10
+
+/*
+ * Buffer sizes (also see mtu.h).
+ */
+
+/* Maximum length of the username in cert */
+#define TLS_USERNAME_LEN 64
+
+/* Legal characters in an X509 or common name */
+#define X509_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_COLON|CC_SLASH|CC_EQUAL)
+#define COMMON_NAME_CHAR_CLASS (CC_ALNUM|CC_UNDERBAR|CC_DASH|CC_DOT|CC_AT|CC_SLASH)
+
+/* Maximum length of OCC options string passed as part of auth handshake */
+#define TLS_OPTIONS_LEN 512
+
+/* Default field in X509 to be username */
+#define X509_USERNAME_FIELD_DEFAULT "CN"
+
+/*
+ * Range of key exchange methods
+ */
+#define KEY_METHOD_MIN 1
+#define KEY_METHOD_MAX 2
+
+/* key method taken from lower 4 bits */
+#define KEY_METHOD_MASK 0x0F
+
+/*
+ * Measure success rate of TLS handshakes, for debugging only
+ */
+/* #define MEASURE_TLS_HANDSHAKE_STATS */
+
+/*
+ * Keep track of certificate hashes at various depths
+ */
+
+/* Maximum certificate depth we will allow */
+#define MAX_CERT_DEPTH 16
+
+struct cert_hash {
+ unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+};
+
+struct cert_hash_set {
+ struct cert_hash *ch[MAX_CERT_DEPTH];
+};
+
+/*
+ * Key material, used as source for PRF-based
+ * key expansion.
+ */
+
+struct key_source {
+ uint8_t pre_master[48]; /* client generated */
+ uint8_t random1[32]; /* generated by both client and server */
+ uint8_t random2[32]; /* generated by both client and server */
+};
+
+struct key_source2 {
+ struct key_source client;
+ struct key_source server;
+};
+
+/*
+ * Represents a single instantiation of a TLS negotiation and
+ * data channel key exchange. 4 keys are kept: encrypt hmac,
+ * decrypt hmac, encrypt cipher, and decrypt cipher. The TLS
+ * control channel is used to exchange these keys.
+ * Each hard or soft reset will build
+ * a fresh key_state. Normally an openvpn session will contain two
+ * key_state objects, one for the current TLS connection, and other
+ * for the retiring or "lame duck" key. The lame duck key_state is
+ * used to maintain transmission continuity on the data-channel while
+ * a key renegotiation is taking place.
+ */
+struct key_state
+{
+ int state;
+ int key_id; /* inherited from struct tls_session below */
+
+ SSL *ssl; /* SSL object -- new obj created for each new key */
+ BIO *ssl_bio; /* read/write plaintext from here */
+ BIO *ct_in; /* write ciphertext to here */
+ BIO *ct_out; /* read ciphertext from here */
+
+ time_t established; /* when our state went S_ACTIVE */
+ time_t must_negotiate; /* key negotiation times out if not finished before this time */
+ time_t must_die; /* this object is destroyed at this time */
+
+ int initial_opcode; /* our initial P_ opcode */
+ struct session_id session_id_remote; /* peer's random session ID */
+ struct link_socket_actual remote_addr; /* peer's IP addr */
+ struct packet_id packet_id; /* for data channel, to prevent replay attacks */
+
+ struct key_ctx_bi key; /* data channel keys for encrypt/decrypt/hmac */
+
+ struct key_source2 *key_src; /* source entropy for key expansion */
+
+ struct buffer plaintext_read_buf;
+ struct buffer plaintext_write_buf;
+ struct buffer ack_write_buf;
+
+ struct reliable *send_reliable; /* holds a copy of outgoing packets until ACK received */
+ struct reliable *rec_reliable; /* order incoming ciphertext packets before we pass to TLS */
+ struct reliable_ack *rec_ack; /* buffers all packet IDs we want to ACK back to sender */
+
+ struct buffer_list *paybuf;
+
+ counter_type n_bytes; /* how many bytes sent/recvd since last key exchange */
+ counter_type n_packets; /* how many packets sent/recvd since last key exchange */
+
+ /*
+ * If bad username/password, TLS connection will come up but 'authenticated' will be false.
+ */
+ bool authenticated;
+ time_t auth_deferred_expire;
+
+#ifdef ENABLE_DEF_AUTH
+ /* If auth_deferred is true, authentication is being deferred */
+ bool auth_deferred;
+#ifdef MANAGEMENT_DEF_AUTH
+ unsigned int mda_key_id;
+ unsigned int mda_status;
+#endif
+#ifdef PLUGIN_DEF_AUTH
+ unsigned int auth_control_status;
+ time_t acf_last_mod;
+ char *auth_control_file;
+#endif
+#endif
+};
+
+/*
+ * Our const options, obtained directly or derived from
+ * command line options.
+ */
+struct tls_options
+{
+ /* our master SSL_CTX from which all SSL objects derived */
+ SSL_CTX *ssl_ctx;
+
+ /* data channel cipher, hmac, and key lengths */
+ struct key_type key_type;
+
+ /* true if we are a TLS server, client otherwise */
+ bool server;
+
+ /* if true, don't xmit until first packet from peer is received */
+ bool xmit_hold;
+
+#ifdef ENABLE_OCC
+ /* local and remote options strings
+ that must match between client and server */
+ const char *local_options;
+ const char *remote_options;
+#endif
+
+ /* from command line */
+ int key_method;
+ bool replay;
+ bool single_session;
+#ifdef ENABLE_OCC
+ bool disable_occ;
+#endif
+#ifdef ENABLE_PUSH_PEER_INFO
+ bool push_peer_info;
+#endif
+ int transition_window;
+ int handshake_window;
+ interval_t packet_timeout;
+ int renegotiate_bytes;
+ int renegotiate_packets;
+ interval_t renegotiate_seconds;
+
+ /* cert verification parms */
+ const char *verify_command;
+ const char *verify_export_cert;
+ const char *verify_x509name;
+ const char *crl_file;
+ int ns_cert_type;
+ unsigned remote_cert_ku[MAX_PARMS];
+ const char *remote_cert_eku;
+
+ /* allow openvpn config info to be
+ passed over control channel */
+ bool pass_config_info;
+
+ /* struct crypto_option flags */
+ unsigned int crypto_flags_and;
+ unsigned int crypto_flags_or;
+
+ int replay_window; /* --replay-window parm */
+ int replay_time; /* --replay-window parm */
+
+ /* packet authentication for TLS handshake */
+ struct crypto_options tls_auth;
+ struct key_ctx_bi tls_auth_key;
+
+ /* frame parameters for TLS control channel */
+ struct frame frame;
+
+ /* used for username/password authentication */
+ const char *auth_user_pass_verify_script;
+ bool auth_user_pass_verify_script_via_file;
+ const char *tmp_dir;
+
+ /* use the client-config-dir as a positive authenticator */
+ const char *client_config_dir_exclusive;
+
+ /* instance-wide environment variable set */
+ struct env_set *es;
+ const struct plugin_list *plugins;
+
+ /* configuration file boolean options */
+# define SSLF_CLIENT_CERT_NOT_REQUIRED (1<<0)
+# define SSLF_USERNAME_AS_COMMON_NAME (1<<1)
+# define SSLF_AUTH_USER_PASS_OPTIONAL (1<<2)
+# define SSLF_NO_NAME_REMAPPING (1<<3)
+# define SSLF_OPT_VERIFY (1<<4)
+ unsigned int ssl_flags;
+
+#ifdef MANAGEMENT_DEF_AUTH
+ struct man_def_auth_context *mda_context;
+#endif
+
+ /* --gremlin bits */
+ int gremlin;
+};
+
+/* index into tls_session.key */
+#define KS_PRIMARY 0 /* the primary key */
+#define KS_LAME_DUCK 1 /* the key that's going to retire soon */
+#define KS_SIZE 2
+
+/*
+ * A tls_session lives through multiple key_state life-cycles. Soft resets
+ * will reuse a tls_session object, but hard resets or errors will require
+ * that a fresh object be built. Normally three tls_session objects are maintained
+ * by an active openvpn session. The first is the current, TLS authenticated
+ * session, the second is used to process connection requests from a new
+ * client that would usurp the current session if successfully authenticated,
+ * and the third is used as a repository for a "lame-duck" key in the event
+ * that the primary session resets due to error while the lame-duck key still
+ * has time left before its expiration. Lame duck keys are used to maintain
+ * the continuity of the data channel connection while a new key is being
+ * negotiated.
+ */
+struct tls_session
+{
+ /* const options and config info */
+ const struct tls_options *opt;
+
+ /* during hard reset used to control burst retransmit */
+ bool burst;
+
+ /* authenticate control packets */
+ struct crypto_options tls_auth;
+ struct packet_id tls_auth_pid;
+
+ int initial_opcode; /* our initial P_ opcode */
+ struct session_id session_id; /* our random session ID */
+ int key_id; /* increments with each soft reset (for key renegotiation) */
+
+ int limit_next; /* used for traffic shaping on the control channel */
+
+ int verify_maxlevel;
+
+ char *common_name;
+
+ struct cert_hash_set *cert_hash_set;
+
+#ifdef ENABLE_PF
+ uint32_t common_name_hashval;
+#endif
+
+ bool verified; /* true if peer certificate was verified against CA */
+
+ /* not-yet-authenticated incoming client */
+ struct link_socket_actual untrusted_addr;
+
+ struct key_state key[KS_SIZE];
+};
+
+/* index into tls_multi.session */
+#define TM_ACTIVE 0
+#define TM_UNTRUSTED 1
+#define TM_LAME_DUCK 2
+#define TM_SIZE 3
+
+/*
+ * The number of keys we will scan on encrypt or decrypt. The first
+ * is the "active" key. The second is the lame_duck or retiring key
+ * associated with the active key's session ID. The third is a detached
+ * lame duck session that only occurs in situations where a key renegotiate
+ * failed on the active key, but a lame duck key was still valid. By
+ * preserving the lame duck session, we can be assured of having a data
+ * channel key available even when network conditions are so bad that
+ * we can't negotiate a new key within the time allotted.
+ */
+#define KEY_SCAN_SIZE 3
+
+/*
+ * An openvpn session running with TLS enabled has one tls_multi object.
+ */
+struct tls_multi
+{
+ /* const options and config info */
+ struct tls_options opt;
+
+ /*
+ * A list of key_state objects in the order they should be
+ * scanned by data channel encrypt and decrypt routines.
+ */
+ struct key_state* key_scan[KEY_SCAN_SIZE];
+
+ /*
+ * used by tls_pre_encrypt to communicate the encrypt key
+ * to tls_post_encrypt()
+ */
+ struct key_state *save_ks; /* temporary pointer used between pre/post routines */
+
+ /*
+ * Used to return outgoing address from
+ * tls_multi_process.
+ */
+ struct link_socket_actual to_link_addr;
+
+ /*
+ * Number of sessions negotiated thus far.
+ */
+ int n_sessions;
+
+ /*
+ * Number of errors.
+ */
+ int n_hard_errors; /* errors due to TLS negotiation failure */
+ int n_soft_errors; /* errors due to unrecognized or failed-to-authenticate incoming packets */
+
+ /*
+ * Our locked common name, username, and cert hashes (cannot change during the life of this tls_multi object)
+ */
+ char *locked_cn;
+ char *locked_username;
+ struct cert_hash_set *locked_cert_hash_set;
+
+#ifdef ENABLE_DEF_AUTH
+ /*
+ * An error message to send to client on AUTH_FAILED
+ */
+ char *client_reason;
+
+ /*
+ * A multi-line string of general-purpose info received from peer
+ * over control channel.
+ */
+ char *peer_info;
+
+ /* Time of last call to tls_authentication_status */
+ time_t tas_last;
+#endif
+
+ /*
+ * Our session objects.
+ */
+ struct tls_session session[TM_SIZE];
+};
+
+/*
+ * Used in --mode server mode to check tls-auth signature on initial
+ * packets received from new clients.
+ */
+struct tls_auth_standalone
+{
+ struct key_ctx_bi tls_auth_key;
+ struct crypto_options tls_auth_options;
+ struct frame frame;
+};
+
+void init_ssl_lib (void);
+void free_ssl_lib (void);
+
+/* Build master SSL_CTX object that serves for the whole of openvpn instantiation */
+SSL_CTX *init_ssl (const struct options *options);
+
+struct tls_multi *tls_multi_init (struct tls_options *tls_options);
+
+struct tls_auth_standalone *tls_auth_standalone_init (struct tls_options *tls_options,
+ struct gc_arena *gc);
+
+void tls_auth_standalone_finalize (struct tls_auth_standalone *tas,
+ const struct frame *frame);
+
+void tls_multi_init_finalize(struct tls_multi *multi,
+ const struct frame *frame);
+
+void tls_multi_init_set_options(struct tls_multi* multi,
+ const char *local,
+ const char *remote);
+
+#define TLSMP_INACTIVE 0
+#define TLSMP_ACTIVE 1
+#define TLSMP_KILL 2
+int tls_multi_process (struct tls_multi *multi,
+ struct buffer *to_link,
+ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup);
+
+void tls_multi_free (struct tls_multi *multi, bool clear);
+
+bool tls_pre_decrypt (struct tls_multi *multi,
+ const struct link_socket_actual *from,
+ struct buffer *buf,
+ struct crypto_options *opt);
+
+bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
+ const struct link_socket_actual *from,
+ const struct buffer *buf);
+
+void tls_pre_encrypt (struct tls_multi *multi,
+ struct buffer *buf, struct crypto_options *opt);
+
+void tls_post_encrypt (struct tls_multi *multi, struct buffer *buf);
+
+void show_available_tls_ciphers (void);
+void get_highest_preference_tls_cipher (char *buf, int size);
+
+void pem_password_setup (const char *auth_file);
+int pem_password_callback (char *buf, int size, int rwflag, void *u);
+void auth_user_pass_setup (const char *auth_file);
+void ssl_set_auth_nocache (void);
+void ssl_purge_auth (void);
+
+
+#ifdef ENABLE_CLIENT_CR
+/*
+ * ssl_get_auth_challenge will parse the server-pushed auth-failed
+ * reason string and return a dynamically allocated
+ * auth_challenge_info struct.
+ */
+void ssl_purge_auth_challenge (void);
+void ssl_put_auth_challenge (const char *cr_str);
+#endif
+
+void tls_set_verify_command (const char *cmd);
+void tls_set_crl_verify (const char *crl);
+void tls_set_verify_x509name (const char *x509name);
+
+void tls_adjust_frame_parameters(struct frame *frame);
+
+bool tls_send_payload (struct tls_multi *multi,
+ const uint8_t *data,
+ int size);
+
+bool tls_rec_payload (struct tls_multi *multi,
+ struct buffer *buf);
+
+const char *tls_common_name (const struct tls_multi* multi, const bool null);
+void tls_set_common_name (struct tls_multi *multi, const char *common_name);
+void tls_lock_common_name (struct tls_multi *multi);
+void tls_lock_cert_hash_set (struct tls_multi *multi);
+
+#define TLS_AUTHENTICATION_SUCCEEDED 0
+#define TLS_AUTHENTICATION_FAILED 1
+#define TLS_AUTHENTICATION_DEFERRED 2
+#define TLS_AUTHENTICATION_UNDEFINED 3
+int tls_authentication_status (struct tls_multi *multi, const int latency);
+void tls_deauthenticate (struct tls_multi *multi);
+
+#ifdef MANAGEMENT_DEF_AUTH
+bool tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason);
+
+static inline char *
+tls_get_peer_info(const struct tls_multi *multi)
+{
+ return multi->peer_info;
+}
+#endif
+
+/*
+ * inline functions
+ */
+
+static inline bool
+tls_initial_packet_received (const struct tls_multi *multi)
+{
+ return multi->n_sessions > 0;
+}
+
+static inline bool
+tls_test_auth_deferred_interval (const struct tls_multi *multi)
+{
+ if (multi)
+ {
+ const struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
+ return now < ks->auth_deferred_expire;
+ }
+ return false;
+}
+
+static inline int
+tls_test_payload_len (const struct tls_multi *multi)
+{
+ if (multi)
+ {
+ const struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY];
+ if (ks->state >= S_ACTIVE)
+ return BLEN (&ks->plaintext_read_buf);
+ }
+ return 0;
+}
+
+static inline void
+tls_set_single_session (struct tls_multi *multi)
+{
+ if (multi)
+ multi->opt.single_session = true;
+}
+
+static inline const char *
+tls_client_reason (struct tls_multi *multi)
+{
+#ifdef ENABLE_DEF_AUTH
+ return multi->client_reason;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef ENABLE_PF
+
+static inline bool
+tls_common_name_hash (const struct tls_multi *multi, const char **cn, uint32_t *cn_hash)
+{
+ if (multi)
+ {
+ const struct tls_session *s = &multi->session[TM_ACTIVE];
+ if (s->common_name && s->common_name[0] != '\0')
+ {
+ *cn = s->common_name;
+ *cn_hash = s->common_name_hashval;
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
+/*
+ * protocol_dump() flags
+ */
+#define PD_TLS_AUTH_HMAC_SIZE_MASK 0xFF
+#define PD_SHOW_DATA (1<<8)
+#define PD_TLS (1<<9)
+#define PD_VERBOSE (1<<10)
+
+const char *protocol_dump (struct buffer *buffer,
+ unsigned int flags,
+ struct gc_arena *gc);
+
+/*
+ * debugging code
+ */
+
+#ifdef MEASURE_TLS_HANDSHAKE_STATS
+void show_tls_performance_stats(void);
+#endif
+
+/*#define EXTRACT_X509_FIELD_TEST*/
+void extract_x509_field_test (void);
+
+#endif /* USE_CRYPTO && USE_SSL */
+
+#endif
diff --git a/status.c b/status.c
new file mode 100644
index 0000000..4bbea28
--- /dev/null
+++ b/status.c
@@ -0,0 +1,301 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "syshead.h"
+
+#include "status.h"
+#include "perf.h"
+
+#include "memdbg.h"
+
+/*
+ * printf-style interface for outputting status info
+ */
+
+static const char *
+print_status_mode (unsigned int flags)
+{
+ switch (flags)
+ {
+ case STATUS_OUTPUT_WRITE:
+ return "WRITE";
+ case STATUS_OUTPUT_READ:
+ return "READ";
+ case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
+ return "READ/WRITE";
+ default:
+ return "UNDEF";
+ }
+}
+
+struct status_output *
+status_open (const char *filename,
+ const int refresh_freq,
+ const int msglevel,
+ const struct virtual_output *vout,
+ const unsigned int flags)
+{
+ struct status_output *so = NULL;
+ if (filename || msglevel >= 0 || vout)
+ {
+ ALLOC_OBJ_CLEAR (so, struct status_output);
+ so->flags = flags;
+ so->msglevel = msglevel;
+ so->vout = vout;
+ so->fd = -1;
+ buf_reset (&so->read_buf);
+ event_timeout_clear (&so->et);
+ if (filename)
+ {
+ switch (so->flags)
+ {
+#ifdef _MSC_VER
+ case STATUS_OUTPUT_WRITE:
+ so->fd = open (filename,
+ O_CREAT | O_TRUNC | O_WRONLY,
+ _S_IREAD | _S_IWRITE);
+ break;
+ case STATUS_OUTPUT_READ:
+ so->fd = open (filename,
+ O_RDONLY,
+ _S_IREAD | _S_IWRITE);
+ break;
+ case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
+ so->fd = open (filename,
+ O_CREAT | O_RDWR,
+ _S_IREAD | _S_IWRITE);
+ break;
+#else
+ case STATUS_OUTPUT_WRITE:
+ so->fd = open (filename,
+ O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR);
+ break;
+ case STATUS_OUTPUT_READ:
+ so->fd = open (filename,
+ O_RDONLY,
+ S_IRUSR | S_IWUSR);
+ break;
+ case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE:
+ so->fd = open (filename,
+ O_CREAT | O_RDWR,
+ S_IRUSR | S_IWUSR);
+ break;
+#endif
+ default:
+ ASSERT (0);
+ }
+ if (so->fd >= 0)
+ {
+ so->filename = string_alloc (filename, NULL);
+
+ /* allocate read buffer */
+ if (so->flags & STATUS_OUTPUT_READ)
+ so->read_buf = alloc_buf (512);
+ }
+ else
+ {
+ msg (M_WARN, "Note: cannot open %s for %s", filename, print_status_mode (so->flags));
+ so->errors = true;
+ }
+ }
+ else
+ so->flags = STATUS_OUTPUT_WRITE;
+
+ if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0)
+ {
+ event_timeout_init (&so->et, refresh_freq, 0);
+ }
+ }
+ return so;
+}
+
+bool
+status_trigger (struct status_output *so)
+{
+ if (so)
+ {
+ struct timeval null;
+ CLEAR (null);
+ return event_timeout_trigger (&so->et, &null, ETT_DEFAULT);
+ }
+ else
+ return false;
+}
+
+bool
+status_trigger_tv (struct status_output *so, struct timeval *tv)
+{
+ if (so)
+ return event_timeout_trigger (&so->et, tv, ETT_DEFAULT);
+ else
+ return false;
+}
+
+void
+status_reset (struct status_output *so)
+{
+ if (so && so->fd >= 0)
+ lseek (so->fd, (off_t)0, SEEK_SET);
+}
+
+void
+status_flush (struct status_output *so)
+{
+ if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_WRITE))
+ {
+#if defined(HAVE_FTRUNCATE)
+ {
+ const off_t off = lseek (so->fd, (off_t)0, SEEK_CUR);
+ if (ftruncate (so->fd, off) != 0) {
+ msg (M_WARN, "Failed to truncate status file: %s", strerror(errno));
+ }
+ }
+#elif defined(HAVE_CHSIZE)
+ {
+ const long off = (long) lseek (so->fd, (off_t)0, SEEK_CUR);
+ chsize (so->fd, off);
+ }
+#else
+#warning both ftruncate and chsize functions appear to be missing from this OS
+#endif
+
+ /* clear read buffer */
+ if (buf_defined (&so->read_buf))
+ {
+ ASSERT (buf_init (&so->read_buf, 0));
+ }
+ }
+}
+
+/* return false if error occurred */
+bool
+status_close (struct status_output *so)
+{
+ bool ret = true;
+ if (so)
+ {
+ if (so->errors)
+ ret = false;
+ if (so->fd >= 0)
+ {
+ if (close (so->fd) < 0)
+ ret = false;
+ }
+ if (so->filename)
+ free (so->filename);
+ if (buf_defined (&so->read_buf))
+ free_buf (&so->read_buf);
+ free (so);
+ }
+ else
+ ret = false;
+ return ret;
+}
+
+#define STATUS_PRINTF_MAXLEN 512
+
+void
+status_printf (struct status_output *so, const char *format, ...)
+{
+ if (so && (so->flags & STATUS_OUTPUT_WRITE))
+ {
+ char buf[STATUS_PRINTF_MAXLEN+2]; /* leave extra bytes for CR, LF */
+ va_list arglist;
+ int stat;
+
+ va_start (arglist, format);
+ stat = vsnprintf (buf, STATUS_PRINTF_MAXLEN, format, arglist);
+ va_end (arglist);
+ buf[STATUS_PRINTF_MAXLEN - 1] = 0;
+
+ if (stat < 0 || stat >= STATUS_PRINTF_MAXLEN)
+ so->errors = true;
+
+ if (so->msglevel >= 0 && !so->errors)
+ msg (so->msglevel, "%s", buf);
+
+ if (so->fd >= 0 && !so->errors)
+ {
+ int len;
+ strcat (buf, "\n");
+ len = strlen (buf);
+ if (len > 0)
+ {
+ if (write (so->fd, buf, len) != len)
+ so->errors = true;
+ }
+ }
+
+ if (so->vout && !so->errors)
+ {
+ chomp (buf);
+ (*so->vout->func) (so->vout->arg, so->vout->flags_default, buf);
+ }
+ }
+}
+
+bool
+status_read (struct status_output *so, struct buffer *buf)
+{
+ bool ret = false;
+
+ if (so && so->fd >= 0 && (so->flags & STATUS_OUTPUT_READ))
+ {
+ ASSERT (buf_defined (&so->read_buf));
+ ASSERT (buf_defined (buf));
+ while (true)
+ {
+ const int c = buf_read_u8 (&so->read_buf);
+
+ /* read more of file into buffer */
+ if (c == -1)
+ {
+ int len;
+
+ ASSERT (buf_init (&so->read_buf, 0));
+ len = read (so->fd, BPTR (&so->read_buf), BCAP (&so->read_buf));
+ if (len <= 0)
+ break;
+
+ ASSERT (buf_inc_len (&so->read_buf, len));
+ continue;
+ }
+
+ ret = true;
+
+ if (c == '\r')
+ continue;
+
+ if (c == '\n')
+ break;
+
+ buf_write_u8 (buf, c);
+ }
+
+ buf_null_terminate (buf);
+ }
+
+ return ret;
+}
diff --git a/status.h b/status.h
new file mode 100644
index 0000000..0bdad4e
--- /dev/null
+++ b/status.h
@@ -0,0 +1,95 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef STATUS_H
+#define STATUS_H
+
+#include "interval.h"
+
+/*
+ * virtual function interface for status output
+ */
+struct virtual_output {
+ void *arg;
+ unsigned int flags_default;
+ void (*func) (void *arg, const unsigned int flags, const char *str);
+};
+
+static inline void
+virtual_output_print (const struct virtual_output *vo, const unsigned int flags, const char *str)
+{
+ (*vo->func) (vo->arg, flags, str);
+}
+
+/*
+ * printf-style interface for inputting/outputting status info
+ */
+
+struct status_output
+{
+# define STATUS_OUTPUT_READ (1<<0)
+# define STATUS_OUTPUT_WRITE (1<<1)
+ unsigned int flags;
+
+ char *filename;
+ int fd;
+ int msglevel;
+ const struct virtual_output *vout;
+
+ struct buffer read_buf;
+
+ struct event_timeout et;
+
+ bool errors;
+};
+
+struct status_output *status_open (const char *filename,
+ const int refresh_freq,
+ const int msglevel,
+ const struct virtual_output *vout,
+ const unsigned int flags);
+
+bool status_trigger_tv (struct status_output *so, struct timeval *tv);
+bool status_trigger (struct status_output *so);
+void status_reset (struct status_output *so);
+void status_flush (struct status_output *so);
+bool status_close (struct status_output *so);
+void status_printf (struct status_output *so, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+
+bool status_read (struct status_output *so, struct buffer *buf);
+
+static inline unsigned int
+status_rw_flags (const struct status_output *so)
+{
+ if (so)
+ return so->flags;
+ else
+ return 0;
+}
+
+#endif
diff --git a/suse/openvpn.init b/suse/openvpn.init
new file mode 100644
index 0000000..8f1060a
--- /dev/null
+++ b/suse/openvpn.init
@@ -0,0 +1,264 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: openvpn
+# Required-Start: $network
+# Required-Stop: $network
+# Default-Start: 3 5
+# Default-Stop: 0 1 2 6
+# Short-Description: This shell script takes care of starting and stopping OpenVPN.
+# Description: OpenVPN is a robust and highly flexible tunneling application that uses all of the encryption, authentication, and certification features of the OpenSSL library to securely tunnel IP networks over a single UDP port.
+### END INIT INFO
+
+# Contributed to the OpenVPN project by
+# Douglas Keller <doug@voidstar.dyndns.org>
+# 2002.05.15
+
+# Modified for SuSE by
+# Frank Plohmann <openvpn@franks-planet.de>
+# 2003.08.24
+# Please feel free to contact me if you have problems or suggestions
+# using this script.
+
+# To install:
+# copy this file to /etc/rc.d/init.d/openvpn
+# use the runlevel editor in Yast to add it to runlevel 3 and/or 5
+# shell> mkdir /etc/openvpn
+# make .conf or .sh files in /etc/openvpn (see below)
+
+# To uninstall:
+# use also Yast and the runlevel editor to uninstall
+
+# Author's Notes:
+#
+# I have created an /etc/init.d init script and enhanced openvpn.spec to
+# automatically register the init script. Once the RPM is installed you
+# can start and stop OpenVPN with "service openvpn start" and "service
+# openvpn stop".
+#
+# The init script does the following:
+#
+# - Starts an openvpn process for each .conf file it finds in
+# /etc/openvpn.
+#
+# - If /etc/openvpn/xxx.sh exists for a xxx.conf file then it executes
+# it before starting openvpn (useful for doing openvpn --mktun...).
+#
+# - In addition to start/stop you can do:
+#
+# /etc/init.d/openvpn reload - SIGHUP
+# /etc/init.d/openvpn reopen - SIGUSR1
+# /etc/init.d/openvpn status - SIGUSR2
+
+# Modifications 2003.05.02
+# * Changed == to = for sh compliance (Bishop Clark).
+# * If condrestart|reload|reopen|status, check that we were
+# actually started (James Yonan).
+# * Added lock, piddir, and work variables (James Yonan).
+# * If start is attempted twice, without an intervening stop, or
+# if start is attempted when previous start was not properly
+# shut down, then kill any previously started processes, before
+# commencing new start operation (James Yonan).
+# * Do a better job of flagging errors on start, and properly
+# returning success or failure status to caller (James Yonan).
+#
+# Modifications 2003.08.24
+# * Converted the script for SuSE Linux distribution.
+# Tested with version 8.2 (Frank Plohmann).
+# - removed "chkconfig" header
+# - added Yast header
+# - changed installation notes
+# - corrected path to openvpn binary
+# - removes sourcing "functions"
+# - removed sourcing "network"
+# - removed network checking. it seemed not to work with SuSE.
+# - added sourcing "rc.status", comments and "rc_reset" command
+# - removed "succes; echo" and "failure; echo" lines
+# - added "rc_status" lines at the end of each section
+# - changed "service" to "/etc/init.d/" in "In addition to start/stop"
+# section above.
+#
+# Modifications 2005.04.04
+# * Added openvpn-startup and openvpn-shutdown script calls (James Yonan).
+#
+
+# Location of openvpn binary
+openvpn="/usr/local/sbin/openvpn"
+
+# Lockfile
+lock="/var/lock/subsys/openvpn"
+
+# PID directory
+piddir="/var/run/openvpn"
+
+# Our working directory
+work=/etc/openvpn
+
+# Source rc functions
+. /etc/rc.status
+
+# Shell functions sourced from /etc/rc.status:
+# rc_check check and set local and overall rc status
+# rc_status check and set local and overall rc status
+# rc_status -v ditto but be verbose in local rc status
+# rc_status -v -r ditto and clear the local rc status
+# rc_failed set local and overall rc status to failed
+# rc_reset clear local rc status (overall remains)
+# rc_exit exit appropriate to overall rc status
+
+# rc_status check and set local and overall rc status
+# rc_status -v ditto but be verbose in local rc status
+# rc_status -v -r ditto and clear the local rc status
+# rc_failed set local and overall rc status to failed
+# rc_reset clear local rc status (overall remains)
+# rc_exit exit appropriate to overall rc status
+
+# First reset status of this service
+rc_reset
+
+[ -f $openvpn ] || exit 0
+
+# See how we were called.
+case "$1" in
+ start)
+ echo -n $"Starting openvpn: "
+
+ /sbin/modprobe tun >/dev/null 2>&1
+
+ # From a security perspective, I think it makes
+ # sense to remove this, and have users who need
+ # it explictly enable in their --up scripts or
+ # firewall setups.
+
+ #echo 1 > /proc/sys/net/ipv4/ip_forward
+
+ # Run startup script, if defined
+ if [ -f $work/openvpn-startup ]; then
+ $work/openvpn-startup
+ fi
+
+ if [ ! -d $piddir ]; then
+ mkdir $piddir
+ fi
+
+ if [ -f $lock ]; then
+ # we were not shut down correctly
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+ rm -f $pidf
+ done
+ rm -f $lock
+ sleep 2
+ fi
+
+ rm -f $piddir/*.pid
+ cd $work
+
+ # Start every .conf in $work and run .sh if exists
+ errors=0
+ successes=0
+ for c in `/bin/ls *.conf 2>/dev/null`; do
+ bn=${c%%.conf}
+ if [ -f "$bn.sh" ]; then
+ . $bn.sh
+ fi
+ rm -f $piddir/$bn.pid
+ $openvpn --daemon --writepid $piddir/$bn.pid --config $c --cd $work
+ if [ $? = 0 ]; then
+ successes=1
+ else
+ errors=1
+ fi
+ done
+
+ if [ $successes = 1 ]; then
+ touch $lock
+ fi
+
+ rc_status -v
+ ;;
+ stop)
+ echo -n $"Shutting down openvpn: "
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill `cat $pidf` >/dev/null 2>&1
+ fi
+ rm -f $pidf
+ done
+
+ # Run shutdown script, if defined
+ if [ -f $work/openvpn-shutdown ]; then
+ $work/openvpn-shutdown
+ fi
+
+ rm -f $lock
+
+ rc_status -v
+ ;;
+ restart)
+ $0 stop
+ sleep 2
+ $0 start
+
+ rc_status
+ ;;
+ reload)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -HUP `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+
+ rc_status -v
+ ;;
+ reopen)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR1 `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+
+ rc_status -v
+ ;;
+ condrestart)
+ if [ -f $lock ]; then
+ $0 stop
+ # avoid race
+ sleep 2
+ $0 start
+ fi
+
+ rc_status
+ ;;
+ status)
+ if [ -f $lock ]; then
+ for pidf in `/bin/ls $piddir/*.pid 2>/dev/null`; do
+ if [ -s $pidf ]; then
+ kill -USR2 `cat $pidf` >/dev/null 2>&1
+ fi
+ done
+ echo "Status written to /var/log/messages"
+ else
+ echo "openvpn: service not started"
+ exit 1
+ fi
+
+ rc_status -v
+ ;;
+ *)
+ echo "Usage: openvpn {start|stop|restart|condrestart|reload|reopen|status}"
+ exit 1
+esac
+
+exit 0
diff --git a/syshead.h b/syshead.h
new file mode 100644
index 0000000..b81ce59
--- /dev/null
+++ b/syshead.h
@@ -0,0 +1,658 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef SYSHEAD_H
+#define SYSHEAD_H
+
+/*
+ * Only include if not during configure
+ */
+#ifndef PACKAGE_NAME
+#include "config.h"
+#endif
+
+/* branch prediction hints */
+#if defined(__GNUC__)
+# define likely(x) __builtin_expect((x),1)
+# define unlikely(x) __builtin_expect((x),0)
+#else
+# define likely(x) (x)
+# define unlikely(x) (x)
+#endif
+
+#if defined(_WIN32) && !defined(WIN32)
+#define WIN32
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <winsock2.h>
+#define sleep(x) Sleep((x)*1000)
+#define random rand
+#define srandom srand
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifndef WIN32
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+#endif
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+# if defined(TARGET_LINUX) && !defined(_GNU_SOURCE)
+ /* needed for peercred support on glibc-2.8 */
+# define _GNU_SOURCE
+# endif
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef HAVE_ERR_H
+#include <err.h>
+#endif
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifdef USE_LIBDL
+#include <dlfcn.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+
+#ifdef HAVE_SYS_EPOLL_H
+#include <sys/epoll.h>
+#endif
+
+#ifdef HAVE_SETCON
+#include <selinux/selinux.h>
+#endif
+
+#ifdef TARGET_SOLARIS
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#else
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#ifdef TARGET_LINUX
+
+#if defined(HAVE_NETINET_IF_ETHER_H)
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef HAVE_LINUX_IF_TUN_H
+#include <linux/if_tun.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+
+#ifdef HAVE_LINUX_ERRQUEUE_H
+#include <linux/errqueue.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#endif /* TARGET_LINUX */
+
+#ifdef TARGET_SOLARIS
+
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#undef S_ERROR
+#endif
+
+#ifdef HAVE_NET_IF_TUN_H
+#include <net/if_tun.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#endif /* TARGET_SOLARIS */
+
+#ifdef TARGET_OPENBSD
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NET_IF_TUN_H
+#include <net/if_tun.h>
+#endif
+
+#endif /* TARGET_OPENBSD */
+
+#ifdef TARGET_FREEBSD
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NET_IF_TUN_H
+#include <net/if_tun.h>
+#endif
+
+#endif /* TARGET_FREEBSD */
+
+#ifdef TARGET_NETBSD
+
+#ifdef HAVE_NET_IF_TUN_H
+#include <net/if_tun.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#endif /* TARGET_NETBSD */
+
+#ifdef TARGET_DRAGONFLY
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifdef HAVE_NET_TUN_IF_TUN_H
+#include <net/tun/if_tun.h>
+#endif
+
+#endif /* TARGET_DRAGONFLY */
+
+#ifdef WIN32
+#include <iphlpapi.h>
+#include <wininet.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#ifdef TARGET_DARWIN
+#define _P1003_1B_VISIBLE
+#endif /* TARGET_DARWIN */
+#include <sys/mman.h>
+#endif
+
+/*
+ * Pedantic mode is meant to accomplish lint-style program checking,
+ * not to build a working executable.
+ */
+#ifdef __STRICT_ANSI__
+# define PEDANTIC 1
+# undef HAVE_CPP_VARARG_MACRO_GCC
+# undef HAVE_CPP_VARARG_MACRO_ISO
+# undef EMPTY_ARRAY_SIZE
+# define EMPTY_ARRAY_SIZE 1
+# undef inline
+# define inline
+#else
+# define PEDANTIC 0
+#endif
+
+/*
+ * Do we have the capability to support the --passtos option?
+ */
+#if defined(IPPROTO_IP) && defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
+#define PASSTOS_CAPABILITY 1
+#else
+#define PASSTOS_CAPABILITY 0
+#endif
+
+/*
+ * Do we have the capability to report extended socket errors?
+ */
+#if defined(HAVE_LINUX_TYPES_H) && defined(HAVE_LINUX_ERRQUEUE_H) && defined(HAVE_SOCK_EXTENDED_ERR) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(IP_RECVERR) && defined(MSG_ERRQUEUE) && defined(SOL_IP) && defined(HAVE_IOVEC)
+#define EXTENDED_SOCKET_ERROR_CAPABILITY 1
+#else
+#define EXTENDED_SOCKET_ERROR_CAPABILITY 0
+#endif
+
+/*
+ * Does this platform support linux-style IP_PKTINFO?
+ */
+#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+#define ENABLE_IP_PKTINFO 1
+#else
+#define ENABLE_IP_PKTINFO 0
+#endif
+
+/*
+ * Disable ESEC
+ */
+#if 0
+#undef EXTENDED_SOCKET_ERROR_CAPABILITY
+#define EXTENDED_SOCKET_ERROR_CAPABILITY 0
+#endif
+
+/*
+ * Do we have a syslog capability?
+ */
+#if defined(HAVE_OPENLOG) && defined(HAVE_SYSLOG)
+#define SYSLOG_CAPABILITY 1
+#else
+#define SYSLOG_CAPABILITY 0
+#endif
+
+/*
+ * Does this OS draw a distinction between binary and ascii files?
+ */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * Directory separation char
+ */
+#ifdef WIN32
+#define OS_SPECIFIC_DIRSEP '\\'
+#else
+#define OS_SPECIFIC_DIRSEP '/'
+#endif
+
+/*
+ * Define a boolean value based
+ * on Win32 status.
+ */
+#ifdef WIN32
+#define WIN32_0_1 1
+#else
+#define WIN32_0_1 0
+#endif
+
+/*
+ * Our socket descriptor type.
+ */
+#ifdef WIN32
+#define SOCKET_UNDEFINED (INVALID_SOCKET)
+typedef SOCKET socket_descriptor_t;
+#else
+#define SOCKET_UNDEFINED (-1)
+typedef int socket_descriptor_t;
+#endif
+
+static inline int
+socket_defined (const socket_descriptor_t sd)
+{
+ return sd != SOCKET_UNDEFINED;
+}
+
+/*
+ * Should statistics counters be 64 bits?
+ */
+#define USE_64_BIT_COUNTERS
+
+/*
+ * Should we enable the use of execve() for calling subprocesses,
+ * instead of system()?
+ */
+#if defined(HAVE_EXECVE) && defined(HAVE_FORK)
+#define ENABLE_EXECVE
+#endif
+
+/*
+ * Do we have point-to-multipoint capability?
+ */
+
+#if defined(ENABLE_CLIENT_SERVER) && defined(USE_CRYPTO) && defined(USE_SSL) && defined(HAVE_GETTIMEOFDAY)
+#define P2MP 1
+#else
+#define P2MP 0
+#endif
+
+#if P2MP && !defined(ENABLE_CLIENT_ONLY)
+#define P2MP_SERVER 1
+#else
+#define P2MP_SERVER 0
+#endif
+
+/*
+ * HTTPS port sharing capability
+ */
+#if defined(ENABLE_PORT_SHARE) && P2MP_SERVER && defined(SCM_RIGHTS) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
+#define PORT_SHARE 1
+#else
+#define PORT_SHARE 0
+#endif
+
+/*
+ * Do we have a plug-in capability?
+ */
+#if defined(USE_LIBDL) || defined(USE_LOAD_LIBRARY)
+#define ENABLE_PLUGIN
+#endif
+
+/*
+ * Enable deferred authentication?
+ */
+#if defined(CONFIGURE_DEF_AUTH) && P2MP_SERVER && defined(ENABLE_PLUGIN)
+#define PLUGIN_DEF_AUTH
+#endif
+#if defined(CONFIGURE_DEF_AUTH) && P2MP_SERVER && defined(ENABLE_MANAGEMENT)
+#define MANAGEMENT_DEF_AUTH
+#endif
+#if defined(PLUGIN_DEF_AUTH) || defined(MANAGEMENT_DEF_AUTH)
+#define ENABLE_DEF_AUTH
+#endif
+
+/*
+ * Enable packet filter?
+ */
+#if defined(CONFIGURE_PF) && P2MP_SERVER && defined(ENABLE_PLUGIN) && defined(HAVE_STAT)
+#define PLUGIN_PF
+#endif
+#if defined(CONFIGURE_PF) && P2MP_SERVER && defined(MANAGEMENT_DEF_AUTH)
+#define MANAGEMENT_PF
+#endif
+#if defined(PLUGIN_PF) || defined(MANAGEMENT_PF)
+#define ENABLE_PF
+#endif
+
+/*
+ * Do we support Unix domain sockets?
+ */
+#if defined(PF_UNIX) && !defined(WIN32)
+#define UNIX_SOCK_SUPPORT 1
+#else
+#define UNIX_SOCK_SUPPORT 0
+#endif
+
+/*
+ * Compile the struct buffer_list code
+ */
+#define ENABLE_BUFFER_LIST
+
+/*
+ * Should we include OCC (options consistency check) code?
+ */
+#ifndef ENABLE_SMALL
+#define ENABLE_OCC
+#endif
+
+/*
+ * Should we include NTLM proxy functionality
+ */
+#if defined(USE_CRYPTO) && defined(ENABLE_HTTP_PROXY)
+#define NTLM 1
+#else
+#define NTLM 0
+#endif
+
+/*
+ * Should we include proxy digest auth functionality
+ */
+#if defined(USE_CRYPTO) && defined(ENABLE_HTTP_PROXY)
+#define PROXY_DIGEST_AUTH 1
+#else
+#define PROXY_DIGEST_AUTH 0
+#endif
+
+/*
+ * Should we include code common to all proxy methods?
+ */
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS)
+#define GENERAL_PROXY_SUPPORT
+#endif
+
+/*
+ * Do we have PKCS11 capability?
+ */
+#if defined(USE_PKCS11) && defined(USE_CRYPTO) && defined(USE_SSL)
+#define ENABLE_PKCS11
+#endif
+
+/*
+ * Is poll available on this platform?
+ */
+#if defined(HAVE_POLL) && defined(HAVE_SYS_POLL_H)
+#define POLL 1
+#else
+#define POLL 0
+#endif
+
+/*
+ * Is epoll available on this platform?
+ */
+#if defined(HAVE_EPOLL_CREATE) && defined(HAVE_SYS_EPOLL_H)
+#define EPOLL 1
+#else
+#define EPOLL 0
+#endif
+
+/* Disable EPOLL */
+#if 0
+#undef EPOLL
+#define EPOLL 0
+#endif
+
+/*
+ * Should we allow ca/cert/key files to be
+ * included inline, in the configuration file?
+ */
+#define ENABLE_INLINE_FILES 1
+
+/*
+ * Support "connection" directive
+ */
+#if ENABLE_INLINE_FILES
+#define ENABLE_CONNECTION 1
+#endif
+
+/*
+ * Should we include http proxy fallback functionality
+ */
+#if defined(ENABLE_CONNECTION) && defined(ENABLE_MANAGEMENT) && defined(ENABLE_HTTP_PROXY)
+#define HTTP_PROXY_FALLBACK 1
+#else
+#define HTTP_PROXY_FALLBACK 0
+#endif
+
+/*
+ * Reduce sensitivity to system clock instability
+ * and backtracks.
+ */
+#define TIME_BACKTRACK_PROTECTION 1
+
+/*
+ * Is non-blocking connect() supported?
+ */
+#if defined(HAVE_GETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_ERROR) && defined(EINPROGRESS) && defined(ETIMEDOUT)
+#define CONNECT_NONBLOCK
+#endif
+
+/*
+ * Do we have the capability to support the AUTO_USERID feature?
+ */
+#if defined(ENABLE_AUTO_USERID)
+#define AUTO_USERID 1
+#else
+#define AUTO_USERID 0
+#endif
+
+/*
+ * Do we support challenge/response authentication, as a console-based client?
+ */
+#define ENABLE_CLIENT_CR
+
+/*
+ * Do we support pushing peer info?
+ */
+#if defined(USE_CRYPTO) && defined(USE_SSL)
+#define ENABLE_PUSH_PEER_INFO
+#endif
+
+#endif
diff --git a/t_client.sh b/t_client.sh
new file mode 100755
index 0000000..21698ef
--- /dev/null
+++ b/t_client.sh
@@ -0,0 +1,298 @@
+#!/bin/sh
+#
+# run OpenVPN client against ``test reference'' server
+# - check that ping, http, ... via tunnel works
+# - check that interface config / routes are properly cleaned after test end
+#
+# prerequisites:
+# - openvpn binary in current directory
+# - writable current directory to create subdir for logs
+# - t_client.rc in current directory OR source dir that specifies tests
+# - for "ping4" checks: fping binary in $PATH
+# - for "ping6" checks: fping6 binary in $PATH
+#
+
+if [ -r ./t_client.rc ] ; then
+ . ./t_client.rc
+elif [ -r "${srcdir}"/t_client.rc ] ; then
+ . "${srcdir}"/t_client.rc
+else
+ echo "$0: cannot find 't_client.rc' in current directory or" >&2
+ echo "$0: source dir ('${srcdir}'). SKIPPING TEST." >&2
+ exit 77
+fi
+
+if [ ! -x ./openvpn ]
+then
+ echo "no (executable) openvpn binary in current directory. FAIL." >&2
+ exit 1
+fi
+
+if [ ! -w . ]
+then
+ echo "current directory is not writable (required for logging). FAIL." >&2
+ exit 1
+fi
+
+if [ -z "$CA_CERT" ] ; then
+ echo "CA_CERT not defined in 't_client.rc'. SKIP test." >&2
+ exit 77
+fi
+
+if [ -z "$TEST_RUN_LIST" ] ; then
+ echo "TEST_RUN_LIST empty, no tests defined. SKIP test." >&2
+ exit 77
+fi
+
+# make sure we have permissions to run ifconfig/route from OpenVPN
+# can't use "id -u" here - doesn't work on Solaris
+ID=`id`
+if expr "$ID" : "uid=0" >/dev/null
+then :
+else
+ echo "$0: this test must run be as root. SKIP." >&2
+ exit 77
+fi
+
+LOGDIR=t_client-`hostname`-`date +%Y%m%d-%H%M%S`
+if mkdir $LOGDIR
+then :
+else
+ echo "can't create log directory '$LOGDIR'. FAIL." >&2
+ exit 1
+fi
+
+exit_code=0
+
+# ----------------------------------------------------------
+# helper functions
+# ----------------------------------------------------------
+# print failure message, increase FAIL counter
+fail()
+{
+ echo ""
+ echo "FAIL: $@" >&2
+ fail_count=$(( $fail_count + 1 ))
+}
+
+# print "all interface IP addresses" + "all routes"
+# this is higly system dependent...
+get_ifconfig_route()
+{
+ # linux / iproute2? (-> if configure got a path)
+ if [ "/sbin/ip" != "ip" ]
+ then
+ echo "-- linux iproute2 --"
+ /sbin/ip addr show | grep -v valid_lft
+ /sbin/ip route show
+ /sbin/ip -6 route show | sed -e 's/expires [0-9]*sec //'
+ return
+ fi
+
+ # try uname
+ case `uname -s` in
+ Linux)
+ echo "-- linux / ifconfig --"
+ LANG=C /sbin/ifconfig -a |egrep "( addr:|encap:)"
+ LANG=C /bin/netstat -rn -4 -6
+ return
+ ;;
+ FreeBSD|NetBSD|Darwin)
+ echo "-- FreeBSD/NetBSD/Darwin [MacOS X] --"
+ /sbin/ifconfig -a | egrep "(flags=|inet)"
+ /bin/netstat -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
+ return
+ ;;
+ OpenBSD)
+ echo "-- OpenBSD --"
+ /sbin/ifconfig -a | egrep "(flags=|inet)" | \
+ sed -e 's/pltime [0-9]*//' -e 's/vltime [0-9]*//'
+ /bin/netstat -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
+ return
+ ;;
+ SunOS)
+ echo "-- Solaris --"
+ /sbin/ifconfig -a | egrep "(flags=|inet)"
+ /bin/netstat -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }'
+ return
+ ;;
+ esac
+
+ echo "get_ifconfig_route(): no idea how to get info on your OS. FAIL." >&2
+ exit 20
+}
+
+# ----------------------------------------------------------
+# check ifconfig
+# arg1: "4" or "6" -> for message
+# arg2: IPv4/IPv6 address that must show up in out of "get_ifconfig_route"
+check_ifconfig()
+{
+ proto=$1 ; shift
+ expect_list="$@"
+
+ if [ -z "$expect_list" ] ; then return ; fi
+
+ for expect in $expect_list
+ do
+ if get_ifconfig_route | fgrep "$expect" >/dev/null
+ then :
+ else
+ fail "check_ifconfig(): expected IPv$proto address '$expect' not found in ifconfig output."
+ fi
+ done
+}
+
+# ----------------------------------------------------------
+# run pings
+# arg1: "4" or "6" -> fping/fing6
+# arg2: "want_ok" or "want_fail" (expected ping result)
+# arg3... -> fping arguments (host list)
+run_ping_tests()
+{
+ proto=$1 ; want=$2 ; shift ; shift
+ targetlist="$@"
+
+ # "no targets" is fine
+ if [ -z "$targetlist" ] ; then return ; fi
+
+ case $proto in
+ 4) cmd=fping ;;
+ 6) cmd=fping6 ;;
+ *) echo "internal error in run_ping_tests arg 1: '$proto'" >&2
+ exit 1 ;;
+ esac
+
+ case $want in
+ want_ok) sizes_list="64 1440 3000" ;;
+ want_fail) sizes_list="64" ;;
+ esac
+
+ for bytes in $sizes_list
+ do
+ echo "run IPv$proto ping tests ($want), $bytes byte packets..."
+
+ echo "$cmd -b $bytes -C 20 -p 250 -q $targetlist" >>$LOGDIR/$SUF:fping.out
+ $cmd -b $bytes -C 20 -p 250 -q $targetlist >>$LOGDIR/$SUF:fping.out 2>&1
+
+ # while OpenVPN is running, pings must succeed (want='want_ok')
+ # before OpenVPN is up, pings must NOT succeed (want='want_fail')
+
+ rc=$?
+ if [ $rc = 0 ] # all ping OK
+ then
+ if [ $want = "want_fail" ] # not what we want
+ then
+ fail "IPv$proto ping test succeeded, but needs to *fail*."
+ fi
+ else # ping failed
+ if [ $want = "want_ok" ] # not what we wanted
+ then
+ fail "IPv$proto ping test ($bytes bytes) failed, but should succeed."
+ fi
+ fi
+ done
+}
+
+# ----------------------------------------------------------
+# main test loop
+# ----------------------------------------------------------
+for SUF in $TEST_RUN_LIST
+do
+ echo -e "\n### test run $SUF ###\n"
+ fail_count=0
+
+ echo "save pre-openvpn ifconfig + route"
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_pre.txt
+
+ # get config variables
+ eval openvpn_conf=\"\$OPENVPN_CONF_$SUF\"
+ eval expect_ifconfig4=\"\$EXPECT_IFCONFIG4_$SUF\"
+ eval expect_ifconfig6=\"\$EXPECT_IFCONFIG6_$SUF\"
+ eval ping4_hosts=\"\$PING4_HOSTS_$SUF\"
+ eval ping6_hosts=\"\$PING6_HOSTS_$SUF\"
+
+ echo -e "\nrun pre-openvpn ping tests - targets must not be reachable..."
+ run_ping_tests 4 want_fail "$ping4_hosts"
+ run_ping_tests 6 want_fail "$ping6_hosts"
+ if [ "$fail_count" = 0 ] ; then
+ echo -e "OK.\n"
+ else
+ echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
+ exit_code=31
+ continue
+ fi
+
+ echo " run ./openvpn $openvpn_conf"
+ ./openvpn $openvpn_conf >$LOGDIR/$SUF:openvpn.log &
+ opid=$!
+
+ # make sure openvpn client is terminated in case shell exits
+ trap "kill $opid" 0
+ trap "kill $opid ; trap - 0 ; exit 1" 1 2 3 15
+
+ echo "wait for connection to establish..."
+ sleep 10
+
+ # test whether OpenVPN process is still there
+ if kill -0 $opid
+ then :
+ else
+ echo -e "OpenVPN process has failed to start up, check log ($LOGDIR/$SUF:openvpn.log). FAIL.\ntail of logfile follows:\n..." >&2
+ tail $LOGDIR/$SUF:openvpn.log >&2
+ trap - 0 1 2 3 15
+ exit 10
+ fi
+
+ # compare whether anything changed in ifconfig/route setup?
+ echo "save ifconfig+route"
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route.txt
+
+ echo -n "compare pre-openvpn ifconfig+route with current values..."
+ if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
+ $LOGDIR/$SUF:ifconfig_route.txt >/dev/null
+ then
+ fail "no differences between ifconfig/route before OpenVPN start and now."
+ else
+ echo -e " OK!\n"
+ fi
+
+ # expected ifconfig values in there?
+ check_ifconfig 4 "$expect_ifconfig4"
+ check_ifconfig 6 "$expect_ifconfig6"
+
+ run_ping_tests 4 want_ok "$ping4_hosts"
+ run_ping_tests 6 want_ok "$ping6_hosts"
+ echo -e "ping tests done.\n"
+
+ echo "stopping OpenVPN"
+ kill $opid
+ wait $!
+ rc=$?
+ if [ $rc != 0 ] ; then
+ fail "OpenVPN return code $rc, expect 0"
+ fi
+
+ echo -e "\nsave post-openvpn ifconfig + route..."
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_post.txt
+
+ echo -n "compare pre- and post-openvpn ifconfig + route..."
+ if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
+ $LOGDIR/$SUF:ifconfig_route_post.txt >$LOGDIR/$SUF:ifconfig_route_diff.txt
+ then
+ echo -e " OK.\n"
+ else
+ cat $LOGDIR/$SUF:ifconfig_route_diff.txt >&2
+ fail "differences between pre- and post-ifconfig/route"
+ fi
+ if [ "$fail_count" = 0 ] ; then
+ echo -e "test run $SUF: all tests OK.\n"
+ else
+ echo -e "test run $SUF: $fail_count test failures. FAIL.\n";
+ exit_code=30
+ fi
+done
+
+# remove trap handler
+trap - 0 1 2 3 15
+exit $exit_code
diff --git a/t_client.sh.in b/t_client.sh.in
new file mode 100755
index 0000000..b273964
--- /dev/null
+++ b/t_client.sh.in
@@ -0,0 +1,298 @@
+#!@SHELL@
+#
+# run OpenVPN client against ``test reference'' server
+# - check that ping, http, ... via tunnel works
+# - check that interface config / routes are properly cleaned after test end
+#
+# prerequisites:
+# - openvpn binary in current directory
+# - writable current directory to create subdir for logs
+# - t_client.rc in current directory OR source dir that specifies tests
+# - for "ping4" checks: fping binary in $PATH
+# - for "ping6" checks: fping6 binary in $PATH
+#
+
+if [ -r ./t_client.rc ] ; then
+ . ./t_client.rc
+elif [ -r "${srcdir}"/t_client.rc ] ; then
+ . "${srcdir}"/t_client.rc
+else
+ echo "$0: cannot find 't_client.rc' in current directory or" >&2
+ echo "$0: source dir ('${srcdir}'). SKIPPING TEST." >&2
+ exit 77
+fi
+
+if [ ! -x ./openvpn ]
+then
+ echo "no (executable) openvpn binary in current directory. FAIL." >&2
+ exit 1
+fi
+
+if [ ! -w . ]
+then
+ echo "current directory is not writable (required for logging). FAIL." >&2
+ exit 1
+fi
+
+if [ -z "$CA_CERT" ] ; then
+ echo "CA_CERT not defined in 't_client.rc'. SKIP test." >&2
+ exit 77
+fi
+
+if [ -z "$TEST_RUN_LIST" ] ; then
+ echo "TEST_RUN_LIST empty, no tests defined. SKIP test." >&2
+ exit 77
+fi
+
+# make sure we have permissions to run ifconfig/route from OpenVPN
+# can't use "id -u" here - doesn't work on Solaris
+ID=`id`
+if expr "$ID" : "uid=0" >/dev/null
+then :
+else
+ echo "$0: this test must run be as root. SKIP." >&2
+ exit 77
+fi
+
+LOGDIR=t_client-`hostname`-`date +%Y%m%d-%H%M%S`
+if mkdir $LOGDIR
+then :
+else
+ echo "can't create log directory '$LOGDIR'. FAIL." >&2
+ exit 1
+fi
+
+exit_code=0
+
+# ----------------------------------------------------------
+# helper functions
+# ----------------------------------------------------------
+# print failure message, increase FAIL counter
+fail()
+{
+ echo ""
+ echo "FAIL: $@" >&2
+ fail_count=$(( $fail_count + 1 ))
+}
+
+# print "all interface IP addresses" + "all routes"
+# this is higly system dependent...
+get_ifconfig_route()
+{
+ # linux / iproute2? (-> if configure got a path)
+ if [ "@IPROUTE@" != "ip" ]
+ then
+ echo "-- linux iproute2 --"
+ @IPROUTE@ addr show | grep -v valid_lft
+ @IPROUTE@ route show
+ @IPROUTE@ -6 route show | sed -e 's/expires [0-9]*sec //'
+ return
+ fi
+
+ # try uname
+ case `uname -s` in
+ Linux)
+ echo "-- linux / ifconfig --"
+ LANG=C @IFCONFIG@ -a |egrep "( addr:|encap:)"
+ LANG=C @NETSTAT@ -rn -4 -6
+ return
+ ;;
+ FreeBSD|NetBSD|Darwin)
+ echo "-- FreeBSD/NetBSD/Darwin [MacOS X] --"
+ @IFCONFIG@ -a | egrep "(flags=|inet)"
+ @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
+ return
+ ;;
+ OpenBSD)
+ echo "-- OpenBSD --"
+ @IFCONFIG@ -a | egrep "(flags=|inet)" | \
+ sed -e 's/pltime [0-9]*//' -e 's/vltime [0-9]*//'
+ @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$NF }'
+ return
+ ;;
+ SunOS)
+ echo "-- Solaris --"
+ @IFCONFIG@ -a | egrep "(flags=|inet)"
+ @NETSTAT@ -rn | awk '$3 !~ /^UHL/ { print $1,$2,$3,$6 }'
+ return
+ ;;
+ esac
+
+ echo "get_ifconfig_route(): no idea how to get info on your OS. FAIL." >&2
+ exit 20
+}
+
+# ----------------------------------------------------------
+# check ifconfig
+# arg1: "4" or "6" -> for message
+# arg2: IPv4/IPv6 address that must show up in out of "get_ifconfig_route"
+check_ifconfig()
+{
+ proto=$1 ; shift
+ expect_list="$@"
+
+ if [ -z "$expect_list" ] ; then return ; fi
+
+ for expect in $expect_list
+ do
+ if get_ifconfig_route | fgrep "$expect" >/dev/null
+ then :
+ else
+ fail "check_ifconfig(): expected IPv$proto address '$expect' not found in ifconfig output."
+ fi
+ done
+}
+
+# ----------------------------------------------------------
+# run pings
+# arg1: "4" or "6" -> fping/fing6
+# arg2: "want_ok" or "want_fail" (expected ping result)
+# arg3... -> fping arguments (host list)
+run_ping_tests()
+{
+ proto=$1 ; want=$2 ; shift ; shift
+ targetlist="$@"
+
+ # "no targets" is fine
+ if [ -z "$targetlist" ] ; then return ; fi
+
+ case $proto in
+ 4) cmd=fping ;;
+ 6) cmd=fping6 ;;
+ *) echo "internal error in run_ping_tests arg 1: '$proto'" >&2
+ exit 1 ;;
+ esac
+
+ case $want in
+ want_ok) sizes_list="64 1440 3000" ;;
+ want_fail) sizes_list="64" ;;
+ esac
+
+ for bytes in $sizes_list
+ do
+ echo "run IPv$proto ping tests ($want), $bytes byte packets..."
+
+ echo "$cmd -b $bytes -C 20 -p 250 -q $targetlist" >>$LOGDIR/$SUF:fping.out
+ $cmd -b $bytes -C 20 -p 250 -q $targetlist >>$LOGDIR/$SUF:fping.out 2>&1
+
+ # while OpenVPN is running, pings must succeed (want='want_ok')
+ # before OpenVPN is up, pings must NOT succeed (want='want_fail')
+
+ rc=$?
+ if [ $rc = 0 ] # all ping OK
+ then
+ if [ $want = "want_fail" ] # not what we want
+ then
+ fail "IPv$proto ping test succeeded, but needs to *fail*."
+ fi
+ else # ping failed
+ if [ $want = "want_ok" ] # not what we wanted
+ then
+ fail "IPv$proto ping test ($bytes bytes) failed, but should succeed."
+ fi
+ fi
+ done
+}
+
+# ----------------------------------------------------------
+# main test loop
+# ----------------------------------------------------------
+for SUF in $TEST_RUN_LIST
+do
+ echo -e "\n### test run $SUF ###\n"
+ fail_count=0
+
+ echo "save pre-openvpn ifconfig + route"
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_pre.txt
+
+ # get config variables
+ eval openvpn_conf=\"\$OPENVPN_CONF_$SUF\"
+ eval expect_ifconfig4=\"\$EXPECT_IFCONFIG4_$SUF\"
+ eval expect_ifconfig6=\"\$EXPECT_IFCONFIG6_$SUF\"
+ eval ping4_hosts=\"\$PING4_HOSTS_$SUF\"
+ eval ping6_hosts=\"\$PING6_HOSTS_$SUF\"
+
+ echo -e "\nrun pre-openvpn ping tests - targets must not be reachable..."
+ run_ping_tests 4 want_fail "$ping4_hosts"
+ run_ping_tests 6 want_fail "$ping6_hosts"
+ if [ "$fail_count" = 0 ] ; then
+ echo -e "OK.\n"
+ else
+ echo -e "FAIL: make sure that ping hosts are ONLY reachable via VPN, SKIP test $SUF".
+ exit_code=31
+ continue
+ fi
+
+ echo " run ./openvpn $openvpn_conf"
+ ./openvpn $openvpn_conf >$LOGDIR/$SUF:openvpn.log &
+ opid=$!
+
+ # make sure openvpn client is terminated in case shell exits
+ trap "kill $opid" 0
+ trap "kill $opid ; trap - 0 ; exit 1" 1 2 3 15
+
+ echo "wait for connection to establish..."
+ sleep 10
+
+ # test whether OpenVPN process is still there
+ if kill -0 $opid
+ then :
+ else
+ echo -e "OpenVPN process has failed to start up, check log ($LOGDIR/$SUF:openvpn.log). FAIL.\ntail of logfile follows:\n..." >&2
+ tail $LOGDIR/$SUF:openvpn.log >&2
+ trap - 0 1 2 3 15
+ exit 10
+ fi
+
+ # compare whether anything changed in ifconfig/route setup?
+ echo "save ifconfig+route"
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route.txt
+
+ echo -n "compare pre-openvpn ifconfig+route with current values..."
+ if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
+ $LOGDIR/$SUF:ifconfig_route.txt >/dev/null
+ then
+ fail "no differences between ifconfig/route before OpenVPN start and now."
+ else
+ echo -e " OK!\n"
+ fi
+
+ # expected ifconfig values in there?
+ check_ifconfig 4 "$expect_ifconfig4"
+ check_ifconfig 6 "$expect_ifconfig6"
+
+ run_ping_tests 4 want_ok "$ping4_hosts"
+ run_ping_tests 6 want_ok "$ping6_hosts"
+ echo -e "ping tests done.\n"
+
+ echo "stopping OpenVPN"
+ kill $opid
+ wait $!
+ rc=$?
+ if [ $rc != 0 ] ; then
+ fail "OpenVPN return code $rc, expect 0"
+ fi
+
+ echo -e "\nsave post-openvpn ifconfig + route..."
+ get_ifconfig_route >$LOGDIR/$SUF:ifconfig_route_post.txt
+
+ echo -n "compare pre- and post-openvpn ifconfig + route..."
+ if diff $LOGDIR/$SUF:ifconfig_route_pre.txt \
+ $LOGDIR/$SUF:ifconfig_route_post.txt >$LOGDIR/$SUF:ifconfig_route_diff.txt
+ then
+ echo -e " OK.\n"
+ else
+ cat $LOGDIR/$SUF:ifconfig_route_diff.txt >&2
+ fail "differences between pre- and post-ifconfig/route"
+ fi
+ if [ "$fail_count" = 0 ] ; then
+ echo -e "test run $SUF: all tests OK.\n"
+ else
+ echo -e "test run $SUF: $fail_count test failures. FAIL.\n";
+ exit_code=30
+ fi
+done
+
+# remove trap handler
+trap - 0 1 2 3 15
+exit $exit_code
diff --git a/t_cltsrv-down.sh b/t_cltsrv-down.sh
new file mode 100755
index 0000000..2ef852a
--- /dev/null
+++ b/t_cltsrv-down.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+echo "${role}:${signal}" >&3
diff --git a/t_cltsrv.sh b/t_cltsrv.sh
new file mode 100755
index 0000000..808d719
--- /dev/null
+++ b/t_cltsrv.sh
@@ -0,0 +1,87 @@
+#! /bin/sh
+#
+# t_cltsrv.sh - script to test OpenVPN's crypto loopback
+# Copyright (C) 2005, 2006, 2008 Matthias Andree
+#
+# 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.
+
+set -e
+trap "rm -f log.$$ log.$$.signal ; trap 0 ; exit 77" 1 2 15
+trap "rm -f log.$$ log.$$.signal ; exit 1" 0 3
+addopts=
+case `uname -s` in
+ FreeBSD)
+ # FreeBSD jails map the outgoing IP to the jail IP - we need to
+ # allow the real IP unless we want the test to run forever.
+ if test "`sysctl 2>/dev/null -n security.jail.jailed`" = 1 \
+ || ps -ostate= -p $$ | grep -q J; then
+ addopts="--float"
+ if test "x`ifconfig | grep inet`" = x ; then
+ echo "###"
+ echo "### To run the test in a FreeBSD jail, you MUST add an IP alias for the jail's IP."
+ echo "###"
+ exit 1
+ fi
+ fi
+ ;;
+esac
+
+# make sure that the --down script is executable -- fail (rather than
+# skip) test if it isn't.
+downscript="t_cltsrv-down.sh"
+test -x "${srcdir}"/$downscript || chmod +x "${srcdir}"/$downscript || { echo >&2 "$downscript is not executable, failing." ; exit 1 ; }
+echo "The following test will take about two minutes." >&2
+echo "If the addresses are in use, this test will retry up to two times." >&2
+
+# go
+success=0
+for i in 1 2 3 ; do
+ set +e
+ (
+ ./openvpn --script-security 2 --cd "${srcdir}" ${addopts} --setenv role srv --down "$downscript" --tls-exit --ping-exit 180 --config sample-config-files/loopback-server &
+ ./openvpn --script-security 2 --cd "${srcdir}" ${addopts} --setenv role clt --down "$downscript" --tls-exit --ping-exit 180 --config sample-config-files/loopback-client
+ ) 3>log.$$.signal >log.$$ 2>&1
+ e1=$?
+ wait $!
+ e2=$?
+ grep 'TCP/UDP: Socket bind failed on local address.*in use' log.$$ >/dev/null && {
+ echo 'address in use, retrying in 150 s'
+ sleep 150
+ continue
+ }
+ grep -v ':inactive$' log.$$.signal >/dev/null && { cat log.$$.signal ; echo ; cat log.$$ ; exit 1 ; }
+ success=1
+ break
+done
+
+set -e
+
+# exit code - defaults to 0, PASS
+ec=0
+
+if [ $success != 1 ] ; then
+ # couldn't run test -- addresses in use, skip test
+ cat log.$$
+ ec=77
+elif [ $e1 != 0 ] || [ $e2 != 0 ] ; then
+ # failure -- fail test
+ cat log.$$
+ ec=1
+fi
+
+rm log.$$ log.$$.signal
+trap 0
+exit $ec
diff --git a/t_lpback.sh b/t_lpback.sh
new file mode 100755
index 0000000..b860de4
--- /dev/null
+++ b/t_lpback.sh
@@ -0,0 +1,31 @@
+#! /bin/sh
+#
+# t_lpback.sh - script to test OpenVPN's crypto loopback
+# Copyright (C) 2005 Matthias Andree
+#
+# 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.
+
+set -e
+trap "rm -f key.$$ log.$$ ; trap 0 ; exit 77" 1 2 15
+trap "rm -f key.$$ log.$$ ; exit 1" 0 3
+./openvpn --genkey --secret key.$$
+set +e
+( ./openvpn --test-crypto --secret key.$$ ) >log.$$ 2>&1
+e=$?
+if [ $e != 0 ] ; then cat log.$$ ; fi
+rm key.$$ log.$$
+trap 0
+exit $e
diff --git a/tap-win32/MAKEFILE b/tap-win32/MAKEFILE
new file mode 100755
index 0000000..6ee4f43
--- /dev/null
+++ b/tap-win32/MAKEFILE
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/tap-win32/SOURCES.in b/tap-win32/SOURCES.in
new file mode 100755
index 0000000..cf030f4
--- /dev/null
+++ b/tap-win32/SOURCES.in
@@ -0,0 +1,64 @@
+# Build TAP-Win32 driver.
+# Build Command: build -cef
+
+MAJORCOMP=ntos
+MINORCOMP=ndis
+
+TARGETNAME=@@PRODUCT_TAP_ID@@
+TARGETTYPE=DRIVER
+TARGETPATH=.
+TARGETLIBS=$(DDK_LIB_PATH)\ndis.lib $(DDK_LIB_PATH)\ntstrsafe.lib
+INCLUDES=$(DDK_INCLUDE_PATH) ..
+
+# The TAP version numbers here must be >=
+# PRODUCT_TAP_WIN32_MIN_x values defined in version.m4
+C_DEFINES=
+C_DEFINES=$(C_DEFINES) -DTAP_DRIVER_MAJOR_VERSION=@@PRODUCT_TAP_MAJOR_VER@@
+C_DEFINES=$(C_DEFINES) -DTAP_DRIVER_MINOR_VERSION=@@PRODUCT_TAP_MINOR_VER@@
+
+# Produce the same symbolic information for both free & checked builds.
+# This will allow us to perform full source-level debugging on both
+# builds without affecting the free build's performance.
+!IF "$(DDKBUILDENV)" != "chk"
+NTDEBUGTYPE=both
+USE_PDB=1
+!ELSE
+NTDEBUGTYPE=both
+USE_PDB=1
+!ENDIF
+
+# Set compiler optimizations:
+# /Ox - Full optimization enabled
+# /Os - favor speed over size when optimizing
+# /Od - Disable all optimizations
+# /Oi - Enable optimization for intrinsic functions
+# /Fc - Generate mixed assembler/source code files
+#
+# For both checked and free builds, make sure that any intrinsic
+# functions are compiled correctly. To do this, ensure that /Oi
+# is selected for both free and checked builds. There is a bug in
+# VC++ 6.0 (at least through SP4) where, if you specify any
+# intrinsic functions in your code with "#pragma intrinsic" but
+# you don't have the /Oi optimization enabled, neither a call
+# to the function, nor the intrinsic inline version of the function
+# will end up in your object code. This bug only applies to free
+# builds, but just to be safe we'll make sure that the flag is
+# enabled for all builds.
+
+!IF "$(DDKBUILDENV)" != "chk"
+MSC_OPTIMIZATION=/Ox /Oi /Fc
+!ELSE
+MSC_OPTIMIZATION=/Od /Oi /Fc
+!ENDIF
+
+# Generate a linker map file just in case we need one for debugging
+LINKER_FLAGS=$(LINKER_FLAGS) /INCREMENTAL:NO /MAP /MAPINFO:EXPORTS
+
+# Generate a browser information file for use in IDE development
+#BROWSER_INFO=1
+#BROWSERFILE=$(TARGETNAME).BSC -n
+
+# Abort compilation on warnings by adding /WX
+MSC_WARNING_LEVEL=/W3
+
+SOURCES=tapdrvr.c resource.rc
diff --git a/tap-win32/common.h b/tap-win32/common.h
new file mode 100755
index 0000000..bb8ab90
--- /dev/null
+++ b/tap-win32/common.h
@@ -0,0 +1,82 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//===============================================
+// This file is included both by OpenVPN and
+// the TAP-Win32 driver and contains definitions
+// common to both.
+//===============================================
+
+#ifndef HAVE_CONFIG_H
+#include "autodefs.h"
+#endif
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+// Present in 8.1
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+// Added in 8.2
+
+/* obsoletes TAP_IOCTL_CONFIG_POINT_TO_POINT */
+#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE (10, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR "\\Device\\"
+#define USERDEVICEDIR "\\DosDevices\\Global\\"
+#define TAPSUFFIX ".tap"
+
+//=========================================================
+// TAP_COMPONENT_ID -- This string defines the TAP driver
+// type -- different component IDs can reside in the system
+// simultaneously.
+//=========================================================
+
+#define TAP_COMPONENT_ID TAP_ID
diff --git a/tap-win32/constants.h b/tap-win32/constants.h
new file mode 100755
index 0000000..9bbd7ee
--- /dev/null
+++ b/tap-win32/constants.h
@@ -0,0 +1,52 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//====================================================================
+// Product and Version public settings
+//====================================================================
+
+#define PRODUCT_STRING PRODUCT_TAP_DEVICE_DESCRIPTION
+
+#define TAP_NDIS_MAJOR_VERSION 5
+#define TAP_NDIS_MINOR_VERSION 0
+
+//===========================================================
+// Driver constants
+//===========================================================
+
+#define ETHERNET_HEADER_SIZE (sizeof (ETH_HEADER))
+#define ETHERNET_MTU 1500
+#define ETHERNET_PACKET_SIZE (ETHERNET_MTU + ETHERNET_HEADER_SIZE)
+#define DEFAULT_PACKET_LOOKAHEAD (ETHERNET_PACKET_SIZE)
+
+#define NIC_MAX_MCAST_LIST 32 // Max length of multicast address list
+
+#define MINIMUM_MTU 576 // USE TCP Minimum MTU
+#define MAXIMUM_MTU 65536 // IP maximum MTU
+
+#define PACKET_QUEUE_SIZE 64 // tap -> userspace queue size
+#define IRP_QUEUE_SIZE 16 // max number of simultaneous i/o operations from userspace
+#define INJECT_QUEUE_SIZE 16 // DHCP/ARP -> tap injection queue
+
+#define TAP_LITTLE_ENDIAN // affects ntohs, htonl, etc. functions
diff --git a/tap-win32/dhcp.c b/tap-win32/dhcp.c
new file mode 100755
index 0000000..3891d42
--- /dev/null
+++ b/tap-win32/dhcp.c
@@ -0,0 +1,599 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//=========================
+// Code to set DHCP options
+//=========================
+
+VOID
+SetDHCPOpt (DHCPMsg *m, void *data, unsigned int len)
+{
+ if (!m->overflow)
+ {
+ if (m->optlen + len <= DHCP_OPTIONS_BUFFER_SIZE)
+ {
+ if (len)
+ {
+ NdisMoveMemory (m->msg.options + m->optlen, data, len);
+ m->optlen += len;
+ }
+ }
+ else
+ {
+ m->overflow = TRUE;
+ }
+ }
+}
+
+VOID
+SetDHCPOpt0 (DHCPMsg *msg, int type)
+{
+ DHCPOPT0 opt;
+ opt.type = (UCHAR) type;
+ SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+VOID
+SetDHCPOpt8 (DHCPMsg *msg, int type, ULONG data)
+{
+ DHCPOPT8 opt;
+ opt.type = (UCHAR) type;
+ opt.len = sizeof (opt.data);
+ opt.data = (UCHAR) data;
+ SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+VOID
+SetDHCPOpt32 (DHCPMsg *msg, int type, ULONG data)
+{
+ DHCPOPT32 opt;
+ opt.type = (UCHAR) type;
+ opt.len = sizeof (opt.data);
+ opt.data = data;
+ SetDHCPOpt (msg, &opt, sizeof (opt));
+}
+
+//==============
+// Checksum code
+//==============
+
+USHORT
+ip_checksum (const UCHAR *buf, const int len_ip_header)
+{
+ USHORT word16;
+ ULONG sum = 0;
+ int i;
+
+ // make 16 bit words out of every two adjacent 8 bit words in the packet
+ // and add them up
+ for (i = 0; i < len_ip_header - 1; i += 2) {
+ word16 = ((buf[i] << 8) & 0xFF00) + (buf[i+1] & 0xFF);
+ sum += (ULONG) word16;
+ }
+
+ // take only 16 bits out of the 32 bit sum and add up the carries
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ // one's complement the result
+ return ((USHORT) ~sum);
+}
+
+USHORT
+udp_checksum (const UCHAR *buf,
+ const int len_udp,
+ const UCHAR *src_addr,
+ const UCHAR *dest_addr)
+{
+ USHORT word16;
+ ULONG sum = 0;
+ int i;
+
+ // make 16 bit words out of every two adjacent 8 bit words and
+ // calculate the sum of all 16 bit words
+ for (i = 0; i < len_udp; i += 2){
+ word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0);
+ sum += word16;
+ }
+
+ // add the UDP pseudo header which contains the IP source and destination addresses
+ for (i = 0; i < 4; i += 2){
+ word16 =((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+ for (i = 0; i < 4; i += 2){
+ word16 =((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF);
+ sum += word16;
+ }
+
+ // the protocol number and the length of the UDP packet
+ sum += (USHORT) IPPROTO_UDP + (USHORT) len_udp;
+
+ // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ // Take the one's complement of sum
+ return ((USHORT) ~sum);
+}
+
+//================================
+// Set IP and UDP packet checksums
+//================================
+
+VOID
+SetChecksumDHCPMsg (DHCPMsg *m)
+{
+ // Set IP checksum
+ m->msg.pre.ip.check = htons (ip_checksum ((UCHAR *) &m->msg.pre.ip, sizeof (IPHDR)));
+
+ // Set UDP Checksum
+ m->msg.pre.udp.check = htons (udp_checksum ((UCHAR *) &m->msg.pre.udp,
+ sizeof (UDPHDR) + sizeof (DHCP) + m->optlen,
+ (UCHAR *)&m->msg.pre.ip.saddr,
+ (UCHAR *)&m->msg.pre.ip.daddr));
+}
+
+//===================
+// DHCP message tests
+//===================
+
+int
+GetDHCPMessageType (const DHCP *dhcp, const int optlen)
+{
+ const UCHAR *p = (UCHAR *) (dhcp + 1);
+ int i;
+
+ for (i = 0; i < optlen; ++i)
+ {
+ const UCHAR type = p[i];
+ const int room = optlen - i - 1;
+ if (type == DHCP_END) // didn't find what we were looking for
+ return -1;
+ else if (type == DHCP_PAD) // no-operation
+ ;
+ else if (type == DHCP_MSG_TYPE) // what we are looking for
+ {
+ if (room >= 2)
+ {
+ if (p[i+1] == 1) // message length should be 1
+ return p[i+2]; // return message type
+ }
+ return -1;
+ }
+ else // some other message
+ {
+ if (room >= 1)
+ {
+ const int len = p[i+1]; // get message length
+ i += (len + 1); // advance to next message
+ }
+ }
+ }
+ return -1;
+}
+
+BOOLEAN
+DHCPMessageOurs (const TapAdapterPointer p_Adapter,
+ const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp)
+{
+ // Must be UDPv4 protocol
+ if (!(eth->proto == htons (ETH_P_IP) && ip->protocol == IPPROTO_UDP))
+ return FALSE;
+
+ // Source MAC must be our adapter
+ if (!MAC_EQUAL (eth->src, p_Adapter->m_MAC))
+ return FALSE;
+
+ // Dest MAC must be either broadcast or our virtual DHCP server
+ if (!(MAC_EQUAL (eth->dest, p_Adapter->m_MAC_Broadcast)
+ || MAC_EQUAL (eth->dest, p_Adapter->m_dhcp_server_mac)))
+ return FALSE;
+
+ // Port numbers must be correct
+ if (!(udp->dest == htons (BOOTPS_PORT)
+ && udp->source == htons (BOOTPC_PORT)))
+ return FALSE;
+
+ // Hardware address must be MAC addr sized
+ if (!(dhcp->hlen == sizeof (MACADDR)))
+ return FALSE;
+
+ // Hardware address must match our adapter
+ if (!MAC_EQUAL (eth->src, dhcp->chaddr))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+//=====================================================
+// Build all of DHCP packet except for DHCP options.
+// Assume that *p has been zeroed before we are called.
+//=====================================================
+
+VOID
+BuildDHCPPre (const TapAdapterPointer a,
+ DHCPPre *p,
+ const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp,
+ const int optlen,
+ const int type)
+{
+ // Should we broadcast or direct to a specific MAC / IP address?
+ const BOOLEAN broadcast = (type == DHCPNAK
+ || MAC_EQUAL (eth->dest, a->m_MAC_Broadcast));
+ // Build ethernet header
+
+ COPY_MAC (p->eth.src, a->m_dhcp_server_mac);
+
+ if (broadcast)
+ COPY_MAC (p->eth.dest, a->m_MAC_Broadcast);
+ else
+ COPY_MAC (p->eth.dest, eth->src);
+
+ p->eth.proto = htons (ETH_P_IP);
+
+ // Build IP header
+
+ p->ip.version_len = (4 << 4) | (sizeof (IPHDR) >> 2);
+ p->ip.tos = 0;
+ p->ip.tot_len = htons (sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen);
+ p->ip.id = 0;
+ p->ip.frag_off = 0;
+ p->ip.ttl = 16;
+ p->ip.protocol = IPPROTO_UDP;
+ p->ip.check = 0;
+ p->ip.saddr = a->m_dhcp_server_ip;
+
+ if (broadcast)
+ p->ip.daddr = ~0;
+ else
+ p->ip.daddr = a->m_dhcp_addr;
+
+ // Build UDP header
+
+ p->udp.source = htons (BOOTPS_PORT);
+ p->udp.dest = htons (BOOTPC_PORT);
+ p->udp.len = htons (sizeof (UDPHDR) + sizeof (DHCP) + optlen);
+ p->udp.check = 0;
+
+ // Build DHCP response
+
+ p->dhcp.op = BOOTREPLY;
+ p->dhcp.htype = 1;
+ p->dhcp.hlen = sizeof (MACADDR);
+ p->dhcp.hops = 0;
+ p->dhcp.xid = dhcp->xid;
+ p->dhcp.secs = 0;
+ p->dhcp.flags = 0;
+ p->dhcp.ciaddr = 0;
+
+ if (type == DHCPNAK)
+ p->dhcp.yiaddr = 0;
+ else
+ p->dhcp.yiaddr = a->m_dhcp_addr;
+
+ p->dhcp.siaddr = a->m_dhcp_server_ip;
+ p->dhcp.giaddr = 0;
+ COPY_MAC (p->dhcp.chaddr, eth->src);
+ p->dhcp.magic = htonl (0x63825363);
+}
+//=============================
+// Build specific DHCP messages
+//=============================
+
+VOID
+SendDHCPMsg (const TapAdapterPointer a,
+ const int type,
+ const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp)
+{
+ DHCPMsg *pkt;
+
+ if (!(type == DHCPOFFER || type == DHCPACK || type == DHCPNAK))
+ {
+ DEBUGP (("[TAP] SendDHCPMsg: Bad DHCP type: %d\n", type));
+ return;
+ }
+
+ pkt = (DHCPMsg *) MemAlloc (sizeof (DHCPMsg), TRUE);
+
+ if (pkt)
+ {
+ //-----------------------
+ // Build DHCP options
+ //-----------------------
+
+ // Message Type
+ SetDHCPOpt8 (pkt, DHCP_MSG_TYPE, type);
+
+ // Server ID
+ SetDHCPOpt32 (pkt, DHCP_SERVER_ID, a->m_dhcp_server_ip);
+
+ if (type == DHCPOFFER || type == DHCPACK)
+ {
+ // Lease Time
+ SetDHCPOpt32 (pkt, DHCP_LEASE_TIME, htonl (a->m_dhcp_lease_time));
+
+ // Netmask
+ SetDHCPOpt32 (pkt, DHCP_NETMASK, a->m_dhcp_netmask);
+
+ // Other user-defined options
+ SetDHCPOpt (pkt,
+ a->m_dhcp_user_supplied_options_buffer,
+ a->m_dhcp_user_supplied_options_buffer_len);
+ }
+
+ // End
+ SetDHCPOpt0 (pkt, DHCP_END);
+
+ if (!DHCPMSG_OVERFLOW (pkt))
+ {
+ // The initial part of the DHCP message (not including options) gets built here
+ BuildDHCPPre (a,
+ &pkt->msg.pre,
+ eth,
+ ip,
+ udp,
+ dhcp,
+ DHCPMSG_LEN_OPT (pkt),
+ type);
+
+ SetChecksumDHCPMsg (pkt);
+
+ DUMP_PACKET ("DHCPMsg",
+ DHCPMSG_BUF (pkt),
+ DHCPMSG_LEN_FULL (pkt));
+
+ // Return DHCP response to kernel
+ InjectPacketDeferred (a,
+ DHCPMSG_BUF (pkt),
+ DHCPMSG_LEN_FULL (pkt));
+ }
+ else
+ {
+ DEBUGP (("[TAP] SendDHCPMsg: DHCP buffer overflow\n"));
+ }
+
+ MemFree (pkt, sizeof (DHCPMsg));
+ }
+}
+
+//===================================================================
+// Handle a BOOTPS packet produced by the local system to
+// resolve the address/netmask of this adapter.
+// If we are in TAP_IOCTL_CONFIG_DHCP_MASQ mode, reply
+// to the message. Return TRUE if we processed the passed
+// message, so that downstream stages can ignore it.
+//===================================================================
+
+BOOLEAN
+ProcessDHCP (TapAdapterPointer p_Adapter,
+ const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp,
+ int optlen)
+{
+ int msg_type;
+
+ // Sanity check IP header
+ if (!(ntohs (ip->tot_len) == sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen
+ && (ntohs (ip->frag_off) & IP_OFFMASK) == 0))
+ return TRUE;
+
+ // Does this message belong to us?
+ if (!DHCPMessageOurs (p_Adapter, eth, ip, udp, dhcp))
+ return FALSE;
+
+ msg_type = GetDHCPMessageType (dhcp, optlen);
+
+ // Drop non-BOOTREQUEST messages
+ if (dhcp->op != BOOTREQUEST)
+ return TRUE;
+
+ // Drop any messages except DHCPDISCOVER or DHCPREQUEST
+ if (!(msg_type == DHCPDISCOVER || msg_type == DHCPREQUEST))
+ return TRUE;
+
+ // Should we reply with DHCPOFFER, DHCPACK, or DHCPNAK?
+ if (msg_type == DHCPREQUEST
+ && ((dhcp->ciaddr && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
+ || !p_Adapter->m_dhcp_received_discover
+ || p_Adapter->m_dhcp_bad_requests >= BAD_DHCPREQUEST_NAK_THRESHOLD))
+ SendDHCPMsg (p_Adapter,
+ DHCPNAK,
+ eth, ip, udp, dhcp);
+ else
+ SendDHCPMsg (p_Adapter,
+ (msg_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK),
+ eth, ip, udp, dhcp);
+
+ // Remember if we received a DHCPDISCOVER
+ if (msg_type == DHCPDISCOVER)
+ p_Adapter->m_dhcp_received_discover = TRUE;
+
+ // Is this a bad DHCPREQUEST?
+ if (msg_type == DHCPREQUEST && dhcp->ciaddr != p_Adapter->m_dhcp_addr)
+ ++p_Adapter->m_dhcp_bad_requests;
+
+ return TRUE;
+}
+
+#if DBG
+
+const char *
+message_op_text (int op)
+{
+ switch (op)
+ {
+ case BOOTREQUEST:
+ return "BOOTREQUEST";
+ case BOOTREPLY:
+ return "BOOTREPLY";
+ default:
+ return "???";
+ }
+}
+
+const char *
+message_type_text (int type)
+{
+ switch (type)
+ {
+ case DHCPDISCOVER:
+ return "DHCPDISCOVER";
+ case DHCPOFFER:
+ return "DHCPOFFER";
+ case DHCPREQUEST:
+ return "DHCPREQUEST";
+ case DHCPDECLINE:
+ return "DHCPDECLINE";
+ case DHCPACK:
+ return "DHCPACK";
+ case DHCPNAK:
+ return "DHCPNAK";
+ case DHCPRELEASE:
+ return "DHCPRELEASE";
+ case DHCPINFORM:
+ return "DHCPINFORM";
+ default:
+ return "???";
+ }
+}
+
+const char *
+port_name (int port)
+{
+ switch (port)
+ {
+ case BOOTPS_PORT:
+ return "BOOTPS";
+ case BOOTPC_PORT:
+ return "BOOTPC";
+ default:
+ return "unknown";
+ }
+}
+
+VOID
+DumpDHCP (const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp,
+ const int optlen)
+{
+ DEBUGP ((" %s", message_op_text (dhcp->op)));
+ DEBUGP ((" %s ", message_type_text (GetDHCPMessageType (dhcp, optlen))));
+ PrIP (ip->saddr);
+ DEBUGP ((":%s[", port_name (ntohs (udp->source))));
+ PrMac (eth->src);
+ DEBUGP (("] -> "));
+ PrIP (ip->daddr);
+ DEBUGP ((":%s[", port_name (ntohs (udp->dest))));
+ PrMac (eth->dest);
+ DEBUGP (("]"));
+ if (dhcp->ciaddr)
+ {
+ DEBUGP ((" ci="));
+ PrIP (dhcp->ciaddr);
+ }
+ if (dhcp->yiaddr)
+ {
+ DEBUGP ((" yi="));
+ PrIP (dhcp->yiaddr);
+ }
+ if (dhcp->siaddr)
+ {
+ DEBUGP ((" si="));
+ PrIP (dhcp->siaddr);
+ }
+ if (dhcp->hlen == sizeof (MACADDR))
+ {
+ DEBUGP ((" ch="));
+ PrMac (dhcp->chaddr);
+ }
+
+ DEBUGP ((" xid=0x%08x", ntohl (dhcp->xid)));
+
+ if (ntohl (dhcp->magic) != 0x63825363)
+ DEBUGP ((" ma=0x%08x", ntohl (dhcp->magic)));
+ if (dhcp->htype != 1)
+ DEBUGP ((" htype=%d", dhcp->htype));
+ if (dhcp->hops)
+ DEBUGP ((" hops=%d", dhcp->hops));
+ if (ntohs (dhcp->secs))
+ DEBUGP ((" secs=%d", ntohs (dhcp->secs)));
+ if (ntohs (dhcp->flags))
+ DEBUGP ((" flags=0x%04x", ntohs (dhcp->flags)));
+
+ // extra stuff
+
+ if (ip->version_len != 0x45)
+ DEBUGP ((" vl=0x%02x", ip->version_len));
+ if (ntohs (ip->tot_len) != sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP) + optlen)
+ DEBUGP ((" tl=%d", ntohs (ip->tot_len)));
+ if (ntohs (udp->len) != sizeof (UDPHDR) + sizeof (DHCP) + optlen)
+ DEBUGP ((" ul=%d", ntohs (udp->len)));
+
+ if (ip->tos)
+ DEBUGP ((" tos=0x%02x", ip->tos));
+ if (ntohs (ip->id))
+ DEBUGP ((" id=0x%04x", ntohs (ip->id)));
+ if (ntohs (ip->frag_off))
+ DEBUGP ((" frag_off=0x%04x", ntohs (ip->frag_off)));
+
+ DEBUGP ((" ttl=%d", ip->ttl));
+ DEBUGP ((" ic=0x%04x [0x%04x]", ntohs (ip->check),
+ ip_checksum ((UCHAR*)ip, sizeof (IPHDR))));
+ DEBUGP ((" uc=0x%04x [0x%04x/%d]", ntohs (udp->check),
+ udp_checksum ((UCHAR *) udp,
+ sizeof (UDPHDR) + sizeof (DHCP) + optlen,
+ (UCHAR *) &ip->saddr,
+ (UCHAR *) &ip->daddr),
+ optlen));
+
+ // Options
+ {
+ const UCHAR *opt = (UCHAR *) (dhcp + 1);
+ int i;
+
+ DEBUGP ((" OPT"));
+ for (i = 0; i < optlen; ++i)
+ {
+ const UCHAR data = opt[i];
+ DEBUGP ((".%d", data));
+ }
+ }
+}
+
+#endif /* DBG */
diff --git a/tap-win32/dhcp.h b/tap-win32/dhcp.h
new file mode 100755
index 0000000..4215f81
--- /dev/null
+++ b/tap-win32/dhcp.h
@@ -0,0 +1,164 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#pragma pack(1)
+
+//===================================================
+// How many bad DHCPREQUESTs do we receive before we
+// return a NAK?
+//
+// A bad DHCPREQUEST is defined to be one where the
+// requestor doesn't know its IP address.
+//===================================================
+
+#define BAD_DHCPREQUEST_NAK_THRESHOLD 3
+
+//==============================================
+// Maximum number of DHCP options bytes supplied
+//==============================================
+
+#define DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE 256
+#define DHCP_OPTIONS_BUFFER_SIZE 256
+
+//===================================
+// UDP port numbers of DHCP messages.
+//===================================
+
+#define BOOTPS_PORT 67
+#define BOOTPC_PORT 68
+
+//===========================
+// The DHCP message structure
+//===========================
+
+typedef struct {
+# define BOOTREQUEST 1
+# define BOOTREPLY 2
+ UCHAR op; /* message op */
+
+ UCHAR htype; /* hardware address type (e.g. '1' = 10Mb Ethernet) */
+ UCHAR hlen; /* hardware address length (e.g. '6' for 10Mb Ethernet) */
+ UCHAR hops; /* client sets to 0, may be used by relay agents */
+ ULONG xid; /* transaction ID, chosen by client */
+ USHORT secs; /* seconds since request process began, set by client */
+ USHORT flags;
+ ULONG ciaddr; /* client IP address, client sets if known */
+ ULONG yiaddr; /* 'your' IP address -- server's response to client */
+ ULONG siaddr; /* server IP address */
+ ULONG giaddr; /* relay agent IP address */
+ UCHAR chaddr[16]; /* client hardware address */
+ UCHAR sname[64]; /* optional server host name */
+ UCHAR file[128]; /* boot file name */
+ ULONG magic; /* must be 0x63825363 (network order) */
+} DHCP;
+
+typedef struct {
+ ETH_HEADER eth;
+ IPHDR ip;
+ UDPHDR udp;
+ DHCP dhcp;
+} DHCPPre;
+
+typedef struct {
+ DHCPPre pre;
+ UCHAR options[DHCP_OPTIONS_BUFFER_SIZE];
+} DHCPFull;
+
+typedef struct {
+ unsigned int optlen;
+ BOOLEAN overflow;
+ DHCPFull msg;
+} DHCPMsg;
+
+//===================
+// Macros for DHCPMSG
+//===================
+
+#define DHCPMSG_LEN_BASE(p) (sizeof (DHCPPre))
+#define DHCPMSG_LEN_OPT(p) ((p)->optlen)
+#define DHCPMSG_LEN_FULL(p) (DHCPMSG_LEN_BASE(p) + DHCPMSG_LEN_OPT(p))
+#define DHCPMSG_BUF(p) ((UCHAR*) &(p)->msg)
+#define DHCPMSG_OVERFLOW(p) ((p)->overflow)
+
+//========================================
+// structs to hold individual DHCP options
+//========================================
+
+typedef struct {
+ UCHAR type;
+} DHCPOPT0;
+
+typedef struct {
+ UCHAR type;
+ UCHAR len;
+ UCHAR data;
+} DHCPOPT8;
+
+typedef struct {
+ UCHAR type;
+ UCHAR len;
+ ULONG data;
+} DHCPOPT32;
+
+#pragma pack()
+
+//==================
+// DHCP Option types
+//==================
+
+#define DHCP_MSG_TYPE 53 /* message type (u8) */
+#define DHCP_PARM_REQ 55 /* parameter request list: c1 (u8), ... */
+#define DHCP_CLIENT_ID 61 /* client ID: type (u8), i1 (u8), ... */
+#define DHCP_IP 50 /* requested IP addr (u32) */
+#define DHCP_NETMASK 1 /* subnet mask (u32) */
+#define DHCP_LEASE_TIME 51 /* lease time sec (u32) */
+#define DHCP_RENEW_TIME 58 /* renewal time sec (u32) */
+#define DHCP_REBIND_TIME 59 /* rebind time sec (u32) */
+#define DHCP_SERVER_ID 54 /* server ID: IP addr (u32) */
+#define DHCP_PAD 0
+#define DHCP_END 255
+
+//====================
+// DHCP Messages types
+//====================
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+#if DBG
+
+VOID
+DumpDHCP (const ETH_HEADER *eth,
+ const IPHDR *ip,
+ const UDPHDR *udp,
+ const DHCP *dhcp,
+ const int optlen);
+
+#endif
diff --git a/tap-win32/endian.h b/tap-win32/endian.h
new file mode 100755
index 0000000..128029a
--- /dev/null
+++ b/tap-win32/endian.h
@@ -0,0 +1,35 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef TAP_LITTLE_ENDIAN
+#define ntohs(x) RtlUshortByteSwap(x)
+#define htons(x) RtlUshortByteSwap(x)
+#define ntohl(x) RtlUlongByteSwap(x)
+#define htonl(x) RtlUlongByteSwap(x)
+#else
+#define ntohs(x) ((USHORT)(x))
+#define htons(x) ((USHORT)(x))
+#define ntohl(x) ((ULONG)(x))
+#define htonl(x) ((ULONG)(x))
+#endif
diff --git a/tap-win32/error.c b/tap-win32/error.c
new file mode 100755
index 0000000..5b25f48
--- /dev/null
+++ b/tap-win32/error.c
@@ -0,0 +1,378 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//-----------------
+// DEBUGGING OUTPUT
+//-----------------
+
+const char *g_LastErrorFilename;
+int g_LastErrorLineNumber;
+
+#if DBG
+
+DebugOutput g_Debug;
+
+BOOLEAN
+NewlineExists (const char *str, int len)
+{
+ while (len-- > 0)
+ {
+ const char c = *str++;
+ if (c == '\n')
+ return TRUE;
+ else if (c == '\0')
+ break;
+ }
+ return FALSE;
+}
+
+VOID
+MyDebugInit (unsigned int bufsiz)
+{
+ NdisZeroMemory (&g_Debug, sizeof (g_Debug));
+ g_Debug.text = (char *) MemAlloc (bufsiz, FALSE);
+ if (g_Debug.text)
+ g_Debug.capacity = bufsiz;
+}
+
+VOID
+MyDebugFree ()
+{
+ if (g_Debug.text)
+ MemFree (g_Debug.text, g_Debug.capacity);
+ NdisZeroMemory (&g_Debug, sizeof (g_Debug));
+}
+
+VOID
+MyDebugPrint (const unsigned char* format, ...)
+{
+ if (g_Debug.text && g_Debug.capacity > 0 && CAN_WE_PRINT)
+ {
+ BOOLEAN owned;
+ ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned);
+ if (owned)
+ {
+ const int remaining = (int)g_Debug.capacity - (int)g_Debug.out;
+
+ if (remaining > 0)
+ {
+ va_list args;
+ NTSTATUS status;
+ char *end;
+
+ va_start (args, format);
+ status = RtlStringCchVPrintfExA (g_Debug.text + g_Debug.out,
+ remaining,
+ &end,
+ NULL,
+ STRSAFE_NO_TRUNCATION | STRSAFE_IGNORE_NULLS,
+ format,
+ args);
+ va_end (args);
+
+ if (status == STATUS_SUCCESS)
+ g_Debug.out = (unsigned int) (end - g_Debug.text);
+ else
+ g_Debug.error = TRUE;
+ }
+ else
+ g_Debug.error = TRUE;
+
+ RELEASE_MUTEX (&g_Debug.lock);
+ }
+ else
+ g_Debug.error = TRUE;
+ }
+}
+
+BOOLEAN
+GetDebugLine (char *buf, const int len)
+{
+ static const char *truncated = "[OUTPUT TRUNCATED]\n";
+ BOOLEAN ret = FALSE;
+
+ NdisZeroMemory (buf, len);
+
+ if (g_Debug.text && g_Debug.capacity > 0)
+ {
+ BOOLEAN owned;
+ ACQUIRE_MUTEX_ADAPTIVE (&g_Debug.lock, owned);
+ if (owned)
+ {
+ int i = 0;
+
+ if (g_Debug.error || NewlineExists (g_Debug.text + g_Debug.in, (int)g_Debug.out - (int)g_Debug.in))
+ {
+ while (i < (len - 1) && g_Debug.in < g_Debug.out)
+ {
+ const char c = g_Debug.text[g_Debug.in++];
+ if (c == '\n')
+ break;
+ buf[i++] = c;
+ }
+ if (i < len)
+ buf[i] = '\0';
+ }
+
+ if (!i)
+ {
+ if (g_Debug.in == g_Debug.out)
+ {
+ g_Debug.in = g_Debug.out = 0;
+ if (g_Debug.error)
+ {
+ const unsigned int tlen = strlen (truncated);
+ if (tlen < g_Debug.capacity)
+ {
+ NdisMoveMemory (g_Debug.text, truncated, tlen+1);
+ g_Debug.out = tlen;
+ }
+ g_Debug.error = FALSE;
+ }
+ }
+ }
+ else
+ ret = TRUE;
+
+ RELEASE_MUTEX (&g_Debug.lock);
+ }
+ }
+ return ret;
+}
+
+VOID
+MyAssert (const unsigned char *file, int line)
+{
+ DEBUGP (("MYASSERT failed %s/%d\n", file, line));
+ KeBugCheckEx (0x0F00BABA,
+ (ULONG_PTR) line,
+ (ULONG_PTR) 0,
+ (ULONG_PTR) 0,
+ (ULONG_PTR) 0);
+}
+
+VOID
+PrMac (const MACADDR mac)
+{
+ DEBUGP (("%x:%x:%x:%x:%x:%x",
+ mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]));
+}
+
+VOID
+PrIP (IPADDR ip_addr)
+{
+ const unsigned char *ip = (const unsigned char *) &ip_addr;
+
+ DEBUGP (("%d.%d.%d.%d",
+ ip[0], ip[1], ip[2], ip[3]));
+}
+
+const char *
+PrIPProto (int proto)
+{
+ switch (proto)
+ {
+ case IPPROTO_UDP:
+ return "UDP";
+ case IPPROTO_TCP:
+ return "TCP";
+ case IPPROTO_ICMP:
+ return "ICMP";
+ case IPPROTO_IGMP:
+ return "IGMP";
+ default:
+ return "???";
+ }
+}
+
+VOID
+DumpARP (const char *prefix, const ARP_PACKET *arp)
+{
+ DEBUGP (("%s ARP src=", prefix));
+ PrMac (arp->m_MAC_Source);
+ DEBUGP ((" dest="));
+ PrMac (arp->m_MAC_Destination);
+ DEBUGP ((" OP=0x%04x",
+ (int)ntohs(arp->m_ARP_Operation)));
+ DEBUGP ((" M=0x%04x(%d)",
+ (int)ntohs(arp->m_MAC_AddressType),
+ (int)arp->m_MAC_AddressSize));
+ DEBUGP ((" P=0x%04x(%d)",
+ (int)ntohs(arp->m_PROTO_AddressType),
+ (int)arp->m_PROTO_AddressSize));
+
+ DEBUGP ((" MacSrc="));
+ PrMac (arp->m_ARP_MAC_Source);
+ DEBUGP ((" MacDest="));
+ PrMac (arp->m_ARP_MAC_Destination);
+
+ DEBUGP ((" IPSrc="));
+ PrIP (arp->m_ARP_IP_Source);
+ DEBUGP ((" IPDest="));
+ PrIP (arp->m_ARP_IP_Destination);
+
+ DEBUGP (("\n"));
+}
+
+struct ethpayload {
+ ETH_HEADER eth;
+ UCHAR payload[DEFAULT_PACKET_LOOKAHEAD];
+};
+
+VOID
+DumpPacket2 (const char *prefix,
+ const ETH_HEADER *eth,
+ const unsigned char *data,
+ unsigned int len)
+{
+ struct ethpayload *ep = (struct ethpayload *) MemAlloc (sizeof (struct ethpayload), TRUE);
+ if (ep)
+ {
+ if (len > DEFAULT_PACKET_LOOKAHEAD)
+ len = DEFAULT_PACKET_LOOKAHEAD;
+ ep->eth = *eth;
+ NdisMoveMemory (ep->payload, data, len);
+ DumpPacket (prefix, (unsigned char *) ep, sizeof (ETH_HEADER) + len);
+ MemFree (ep, sizeof (struct ethpayload));
+ }
+}
+
+VOID
+DumpPacket (const char *prefix,
+ const unsigned char *data,
+ unsigned int len)
+{
+ const ETH_HEADER *eth = (const ETH_HEADER *) data;
+ const IPHDR *ip = (const IPHDR *) (data + sizeof (ETH_HEADER));
+
+ if (len < sizeof (ETH_HEADER))
+ {
+ DEBUGP (("%s TRUNCATED PACKET LEN=%d\n", prefix, len));
+ return;
+ }
+
+ // ARP Packet?
+ if (len >= sizeof (ARP_PACKET) && eth->proto == htons (ETH_P_ARP))
+ {
+ DumpARP (prefix, (const ARP_PACKET *) data);
+ return;
+ }
+
+ // IPv4 packet?
+ if (len >= (sizeof (IPHDR) + sizeof (ETH_HEADER))
+ && eth->proto == htons (ETH_P_IP)
+ && IPH_GET_VER (ip->version_len) == 4)
+ {
+ const int hlen = IPH_GET_LEN (ip->version_len);
+ const int blen = len - sizeof (ETH_HEADER);
+ BOOLEAN did = FALSE;
+
+ DEBUGP (("%s IPv4 %s[%d]", prefix, PrIPProto (ip->protocol), len));
+
+ if (!(ntohs (ip->tot_len) == blen && hlen <= blen))
+ {
+ DEBUGP ((" XXX"));
+ return;
+ }
+
+ // TCP packet?
+ if (ip->protocol == IPPROTO_TCP
+ && blen - hlen >= (sizeof (TCPHDR)))
+ {
+ const TCPHDR *tcp = (TCPHDR *) (data + sizeof (ETH_HEADER) + hlen);
+ DEBUGP ((" "));
+ PrIP (ip->saddr);
+ DEBUGP ((":%d", ntohs (tcp->source)));
+ DEBUGP ((" -> "));
+ PrIP (ip->daddr);
+ DEBUGP ((":%d", ntohs (tcp->dest)));
+ did = TRUE;
+ }
+
+ // UDP packet?
+ else if ((ntohs (ip->frag_off) & IP_OFFMASK) == 0
+ && ip->protocol == IPPROTO_UDP
+ && blen - hlen >= (sizeof (UDPHDR)))
+ {
+ const UDPHDR *udp = (UDPHDR *) (data + sizeof (ETH_HEADER) + hlen);
+
+ // DHCP packet?
+ if ((udp->dest == htons (BOOTPC_PORT) || udp->dest == htons (BOOTPS_PORT))
+ && blen - hlen >= (sizeof (UDPHDR) + sizeof (DHCP)))
+ {
+ const DHCP *dhcp = (DHCP *) (data
+ + hlen
+ + sizeof (ETH_HEADER)
+ + sizeof (UDPHDR));
+
+ int optlen = len
+ - sizeof (ETH_HEADER)
+ - hlen
+ - sizeof (UDPHDR)
+ - sizeof (DHCP);
+
+ if (optlen < 0)
+ optlen = 0;
+
+ DumpDHCP (eth, ip, udp, dhcp, optlen);
+ did = TRUE;
+ }
+
+ if (!did)
+ {
+ DEBUGP ((" "));
+ PrIP (ip->saddr);
+ DEBUGP ((":%d", ntohs (udp->source)));
+ DEBUGP ((" -> "));
+ PrIP (ip->daddr);
+ DEBUGP ((":%d", ntohs (udp->dest)));
+ did = TRUE;
+ }
+ }
+
+ if (!did)
+ {
+ DEBUGP ((" ipproto=%d ", ip->protocol));
+ PrIP (ip->saddr);
+ DEBUGP ((" -> "));
+ PrIP (ip->daddr);
+ }
+
+ DEBUGP (("\n"));
+ return;
+ }
+
+ {
+ DEBUGP (("%s ??? src=", prefix));
+ PrMac (eth->src);
+ DEBUGP ((" dest="));
+ PrMac (eth->dest);
+ DEBUGP ((" proto=0x%04x len=%d\n",
+ (int) ntohs(eth->proto),
+ len));
+ }
+}
+
+#endif
diff --git a/tap-win32/error.h b/tap-win32/error.h
new file mode 100755
index 0000000..d8436dc
--- /dev/null
+++ b/tap-win32/error.h
@@ -0,0 +1,88 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//-----------------
+// DEBUGGING OUTPUT
+//-----------------
+
+#define NOTE_ERROR() \
+{ \
+ g_LastErrorFilename = __FILE__; \
+ g_LastErrorLineNumber = __LINE__; \
+}
+
+#if DBG
+
+typedef struct {
+ unsigned int in;
+ unsigned int out;
+ unsigned int capacity;
+ char *text;
+ BOOLEAN error;
+ MUTEX lock;
+} DebugOutput;
+
+VOID MyDebugPrint (const unsigned char* format, ...);
+
+VOID MyAssert (const unsigned char *file, int line);
+
+VOID DumpPacket (const char *prefix,
+ const unsigned char *data,
+ unsigned int len);
+
+VOID DumpPacket2 (const char *prefix,
+ const ETH_HEADER *eth,
+ const unsigned char *data,
+ unsigned int len);
+
+#define CAN_WE_PRINT (DEBUGP_AT_DISPATCH || KeGetCurrentIrql () < DISPATCH_LEVEL)
+
+#if ALSO_DBGPRINT
+#define DEBUGP(fmt) { MyDebugPrint fmt; if (CAN_WE_PRINT) DbgPrint fmt; }
+#else
+#define DEBUGP(fmt) { MyDebugPrint fmt; }
+#endif
+
+#define MYASSERT(exp) \
+{ \
+ if (!(exp)) \
+ { \
+ MyAssert(__FILE__, __LINE__); \
+ } \
+}
+
+#define DUMP_PACKET(prefix, data, len) \
+ DumpPacket (prefix, data, len)
+
+#define DUMP_PACKET2(prefix, eth, data, len) \
+ DumpPacket2 (prefix, eth, data, len)
+
+#else
+
+#define DEBUGP(fmt)
+#define MYASSERT(exp)
+#define DUMP_PACKET(prefix, data, len)
+#define DUMP_PACKET2(prefix, eth, data, len)
+
+#endif
diff --git a/tap-win32/hexdump.c b/tap-win32/hexdump.c
new file mode 100755
index 0000000..49bc38c
--- /dev/null
+++ b/tap-win32/hexdump.c
@@ -0,0 +1,69 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hexdump.h"
+
+#ifndef NDIS_MINIPORT_DRIVER
+
+VOID (*DbgMessage)(char *p_Format, ...) = DisplayDebugString;
+
+VOID DisplayDebugString (char *p_Format, ...)
+ {
+ static char l_Buffer [4096];
+
+ va_list l_ArgumentList;
+ va_start (l_ArgumentList, p_Format);
+ vsprintf (l_Buffer, p_Format, l_ArgumentList);
+ va_end (l_ArgumentList);
+
+ OutputDebugStringA (l_Buffer);
+ }
+
+#endif
+
+VOID HexDump (unsigned char *p_Buffer, unsigned long p_Size)
+ {
+ unsigned long l_Index, l_Idx;
+ unsigned char l_Row [17];
+
+ for (l_Index = l_Row [16] = 0; l_Index < p_Size || l_Index % 16; ++l_Index)
+ {
+ if (l_Index % 16 == 0)
+ DEBUGP (("%05x ", l_Index));
+ DEBUGP (("%02x ", l_Row [l_Index % 16] = (l_Index < p_Size ? p_Buffer [l_Index] : 0)));
+ l_Row [l_Index % 16] = IfPrint (l_Row [l_Index % 16]);
+ if ((l_Index + 1) % 16 == 0)
+ DEBUGP ((" %s\n", l_Row));
+ }
+
+ DEBUGP (("\n"));
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/tap-win32/hexdump.h b/tap-win32/hexdump.h
new file mode 100755
index 0000000..c1af705
--- /dev/null
+++ b/tap-win32/hexdump.h
@@ -0,0 +1,63 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef HEXDUMP_DEFINED
+#define HEXDUMP_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//=====================================================================================
+// Debug Routines
+//=====================================================================================
+
+#ifndef NDIS_MINIPORT_DRIVER
+# include <stdio.h>
+# include <ctype.h>
+# include <windows.h>
+# include <winnt.h>
+# include <memory.h>
+
+# ifndef DEBUGP
+# define DEBUGP(fmt) { DbgMessage fmt; }
+# endif
+
+ extern VOID (*DbgMessage)(char *p_Format, ...);
+
+ VOID DisplayDebugString (char *p_Format, ...);
+#endif
+
+//===================================================================================
+// Reporting / Debugging
+//===================================================================================
+#define IfPrint(c) (c >= 32 && c < 127 ? c : '.')
+
+VOID HexDump (unsigned char *p_Buffer, unsigned long p_Size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tap-win32/i386/OemWin2k.inf.in b/tap-win32/i386/OemWin2k.inf.in
new file mode 100755
index 0000000..031b06c
--- /dev/null
+++ b/tap-win32/i386/OemWin2k.inf.in
@@ -0,0 +1,195 @@
+; ****************************************************************************
+; * Copyright (C) 2002-2010 OpenVPN Technologies, 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. *
+; ****************************************************************************
+
+; SYNTAX CHECKER
+; cd \WINDDK\3790\tools\chkinf
+; chkinf c:\src\openvpn\tap-win32\i386\oemwin2k.inf
+; OUTPUT -> file:///c:/WINDDK/3790/tools/chkinf/htm/c%23+src+openvpn+tap-win32+i386+__OemWin2k.htm
+
+; INSTALL/REMOVE DRIVER
+; tapinstall install OemWin2k.inf TAP0901
+; tapinstall update OemWin2k.inf TAP0901
+; tapinstall remove TAP0901
+
+;*********************************************************
+; Note to Developers:
+;
+; If you are bundling the TAP-Win32 driver with your app,
+; you should try to rename it in such a way that it will
+; not collide with other instances of TAP-Win32 defined
+; by other apps. Multiple versions of the TAP-Win32
+; driver, each installed by different apps, can coexist
+; on the same machine if you follow these guidelines.
+; NOTE: these instructions assume you are editing the
+; generated OemWin2k.inf file, not the source
+; OemWin2k.inf.in file which is preprocessed by winconfig
+; and uses macro definitions from settings.in.
+;
+; (1) Rename all tapXXXX instances in this file to
+; something different (use at least 5 characters
+; for this name!)
+; (2) Change the "!define TAP" definition in openvpn.nsi
+; to match what you changed tapXXXX to.
+; (3) Change TARGETNAME in SOURCES to match what you
+; changed tapXXXX to.
+; (4) Change TAP_COMPONENT_ID in common.h to match what
+; you changed tapXXXX to.
+; (5) Change SZDEPENDENCIES in service.h to match what
+; you changed tapXXXX to.
+; (6) Change DeviceDescription and Provider strings.
+; (7) Change PRODUCT_STRING in constants.h to what you
+; set DeviceDescription to.
+;
+;*********************************************************
+
+[Version]
+ Signature = "$Windows NT$"
+ CatalogFile = @@PRODUCT_TAP_ID@@.cat
+ ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
+ Provider = %Provider%
+ Class = Net
+
+; This version number should match the version
+; number given in SOURCES.
+ DriverVer=@@PRODUCT_TAP_RELDATE@@,@@PRODUCT_TAP_MAJOR_VER@@.00.00.@@PRODUCT_TAP_MINOR_VER@@
+
+[Strings]
+ DeviceDescription = "@@PRODUCT_TAP_DEVICE_DESCRIPTION@@"
+ Provider = "@@PRODUCT_TAP_PROVIDER@@"
+
+;----------------------------------------------------------------
+; Manufacturer + Product Section (Done)
+;----------------------------------------------------------------
+[Manufacturer]
+!ifdef AMD64
+ %Provider% = @@PRODUCT_TAP_ID@@, NTamd64
+!else
+ %Provider% = @@PRODUCT_TAP_ID@@
+!endif
+
+!ifdef AMD64
+[@@PRODUCT_TAP_ID@@.NTamd64]
+!else
+[@@PRODUCT_TAP_ID@@]
+!endif
+ %DeviceDescription% = @@PRODUCT_TAP_ID@@.ndi, @@PRODUCT_TAP_ID@@
+
+;---------------------------------------------------------------
+; Driver Section (Done)
+;---------------------------------------------------------------
+
+;----------------- Characteristics ------------
+; NCF_PHYSICAL = 0x04
+; NCF_VIRTUAL = 0x01
+; NCF_SOFTWARE_ENUMERATED = 0x02
+; NCF_HIDDEN = 0x08
+; NCF_NO_SERVICE = 0x10
+; NCF_HAS_UI = 0x80
+;----------------- Characteristics ------------
+
+[@@PRODUCT_TAP_ID@@.ndi]
+ CopyFiles = @@PRODUCT_TAP_ID@@.driver, @@PRODUCT_TAP_ID@@.files
+ AddReg = @@PRODUCT_TAP_ID@@.reg
+ AddReg = @@PRODUCT_TAP_ID@@.params.reg
+ Characteristics = @@PRODUCT_TAP_CHARACTERISTICS@@
+
+[@@PRODUCT_TAP_ID@@.ndi.Services]
+ AddService = @@PRODUCT_TAP_ID@@, 2, @@PRODUCT_TAP_ID@@.service
+
+[@@PRODUCT_TAP_ID@@.reg]
+ HKR, Ndi, Service, 0, "@@PRODUCT_TAP_ID@@"
+ HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
+ HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
+ HKR, , Manufacturer, 0, "%Provider%"
+ HKR, , ProductName, 0, "%DeviceDescription%"
+
+[@@PRODUCT_TAP_ID@@.params.reg]
+ HKR, Ndi\params\MTU, ParamDesc, 0, "MTU"
+ HKR, Ndi\params\MTU, Type, 0, "int"
+ HKR, Ndi\params\MTU, Default, 0, "1500"
+ HKR, Ndi\params\MTU, Optional, 0, "0"
+ HKR, Ndi\params\MTU, Min, 0, "100"
+ HKR, Ndi\params\MTU, Max, 0, "1500"
+ HKR, Ndi\params\MTU, Step, 0, "1"
+ HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status"
+ HKR, Ndi\params\MediaStatus, Type, 0, "enum"
+ HKR, Ndi\params\MediaStatus, Default, 0, "0"
+ HKR, Ndi\params\MediaStatus, Optional, 0, "0"
+ HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled"
+ HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected"
+ HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address"
+ HKR, Ndi\params\MAC, Type, 0, "edit"
+ HKR, Ndi\params\MAC, Optional, 0, "1"
+ HKR, Ndi\params\AllowNonAdmin, ParamDesc, 0, "Non-Admin Access"
+ HKR, Ndi\params\AllowNonAdmin, Type, 0, "enum"
+ HKR, Ndi\params\AllowNonAdmin, Default, 0, "1"
+ HKR, Ndi\params\AllowNonAdmin, Optional, 0, "0"
+ HKR, Ndi\params\AllowNonAdmin\enum, "0", 0, "Not Allowed"
+ HKR, Ndi\params\AllowNonAdmin\enum, "1", 0, "Allowed"
+
+;----------------------------------------------------------------
+; Service Section
+;----------------------------------------------------------------
+
+;---------- Service Type -------------
+; SERVICE_KERNEL_DRIVER = 0x01
+; SERVICE_WIN32_OWN_PROCESS = 0x10
+;---------- Service Type -------------
+
+;---------- Start Mode ---------------
+; SERVICE_BOOT_START = 0x0
+; SERVICE_SYSTEM_START = 0x1
+; SERVICE_AUTO_START = 0x2
+; SERVICE_DEMAND_START = 0x3
+; SERVICE_DISABLED = 0x4
+;---------- Start Mode ---------------
+
+[@@PRODUCT_TAP_ID@@.service]
+ DisplayName = %DeviceDescription%
+ ServiceType = 1
+ StartType = 3
+ ErrorControl = 1
+ LoadOrderGroup = NDIS
+ ServiceBinary = %12%\@@PRODUCT_TAP_ID@@.sys
+
+;-----------------------------------------------------------------
+; File Installation
+;-----------------------------------------------------------------
+
+;----------------- Copy Flags ------------
+; COPYFLG_NOSKIP = 0x02
+; COPYFLG_NOVERSIONCHECK = 0x04
+;----------------- Copy Flags ------------
+
+; SourceDisksNames
+; diskid = description[, [tagfile] [, <unused>, subdir]]
+; 1 = "Intel Driver Disk 1",e100bex.sys,,
+
+[SourceDisksNames]
+ 1 = %DeviceDescription%, @@PRODUCT_TAP_ID@@.sys
+
+; SourceDisksFiles
+; filename_on_source = diskID[, [subdir][, size]]
+; e100bex.sys = 1,, ; on distribution disk 1
+
+[SourceDisksFiles]
+@@PRODUCT_TAP_ID@@.sys = 1
+
+[DestinationDirs]
+ @@PRODUCT_TAP_ID@@.files = 11
+ @@PRODUCT_TAP_ID@@.driver = 12
+
+[@@PRODUCT_TAP_ID@@.files]
+; TapPanel.cpl,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+; cipsrvr.exe,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+
+[@@PRODUCT_TAP_ID@@.driver]
+ @@PRODUCT_TAP_ID@@.sys,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
+
+;---------------------------------------------------------------
+; End
+;---------------------------------------------------------------
diff --git a/tap-win32/instance.c b/tap-win32/instance.c
new file mode 100755
index 0000000..182cee8
--- /dev/null
+++ b/tap-win32/instance.c
@@ -0,0 +1,241 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define INSTANCE_KEY(a) ((PVOID)((a)->m_Extension.m_TapDevice))
+
+#define N_INSTANCE_BUCKETS 256
+
+typedef struct _INSTANCE {
+ struct _INSTANCE *next;
+ TapAdapterPointer m_Adapter;
+} INSTANCE;
+
+typedef struct {
+ INSTANCE *list;
+ MUTEX lock;
+} INSTANCE_BUCKET;
+
+typedef struct {
+ INSTANCE_BUCKET buckets[N_INSTANCE_BUCKETS];
+} INSTANCE_HASH;
+
+INSTANCE_HASH *g_InstanceHash = NULL;
+
+// must return a hash >= 0 and < N_INSTANCE_BUCKETS
+int
+InstanceHashValue (PVOID addr)
+{
+ UCHAR *p = (UCHAR *) &addr;
+
+ if (sizeof (addr) == 4)
+ return p[0] ^ p[1] ^ p[2] ^ p[3];
+ else if (sizeof (addr) == 8)
+ return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4] ^ p[5] ^ p[6] ^ p[7];
+ else
+ {
+ MYASSERT (0);
+ }
+}
+
+BOOLEAN
+InitInstanceList (VOID)
+{
+ MYASSERT (g_InstanceHash == NULL);
+ g_InstanceHash = MemAlloc (sizeof (INSTANCE_HASH), TRUE);
+ if (g_InstanceHash)
+ {
+ int i;
+ for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+ INIT_MUTEX (&g_InstanceHash->buckets[i].lock);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int
+NInstances (VOID)
+{
+ int i, n = 0;
+
+ if (g_InstanceHash)
+ {
+ for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+ {
+ BOOLEAN got_lock;
+ INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i];
+ ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+ if (got_lock)
+ {
+ INSTANCE *current;
+ for (current = ib->list; current != NULL; current = current->next)
+ ++n;
+ RELEASE_MUTEX (&ib->lock);
+ }
+ else
+ return -1;
+ }
+ }
+
+ return n;
+}
+
+int
+InstanceMaxBucketSize (VOID)
+{
+ int i, n = 0;
+
+ if (g_InstanceHash)
+ {
+ for (i = 0; i < N_INSTANCE_BUCKETS; ++i)
+ {
+ BOOLEAN got_lock;
+ int bucket_size = 0;
+ INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[i];
+ ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+ if (got_lock)
+ {
+ INSTANCE *current;
+ for (current = ib->list; current != NULL; current = current->next)
+ ++bucket_size;
+ if (bucket_size > n)
+ n = bucket_size;
+ RELEASE_MUTEX (&ib->lock);
+ }
+ else
+ return -1;
+ }
+ }
+
+ return n;
+}
+
+VOID
+FreeInstanceList (VOID)
+{
+ if (g_InstanceHash)
+ {
+ MYASSERT (NInstances() == 0);
+ MemFree (g_InstanceHash, sizeof (INSTANCE_HASH));
+ g_InstanceHash = NULL;
+ }
+}
+
+BOOLEAN
+AddAdapterToInstanceList (TapAdapterPointer p_Adapter)
+{
+ BOOLEAN got_lock;
+ BOOLEAN ret = FALSE;
+ const int hash = InstanceHashValue(INSTANCE_KEY(p_Adapter));
+ INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[hash];
+
+ DEBUGP (("[TAP] AddAdapterToInstanceList hash=%d\n", hash));
+
+ ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+ if (got_lock)
+ {
+ INSTANCE *i = MemAlloc (sizeof (INSTANCE), FALSE);
+ if (i)
+ {
+ MYASSERT (p_Adapter);
+ i->m_Adapter = p_Adapter;
+ i->next = ib->list;
+ ib->list = i;
+ ret = TRUE;
+ }
+ RELEASE_MUTEX (&ib->lock);
+ }
+
+ return ret;
+}
+
+BOOLEAN
+RemoveAdapterFromInstanceList (TapAdapterPointer p_Adapter)
+{
+ BOOLEAN got_lock;
+ BOOLEAN ret = FALSE;
+ INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue(INSTANCE_KEY(p_Adapter))];
+
+ ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+ if (got_lock)
+ {
+ INSTANCE *current, *prev=NULL;
+ for (current = ib->list; current != NULL; current = current->next)
+ {
+ if (current->m_Adapter == p_Adapter) // found match
+ {
+ if (prev)
+ prev->next = current->next;
+ else
+ ib->list = current->next;
+ MemFree (current->m_Adapter, sizeof (TapAdapter));
+ MemFree (current, sizeof (INSTANCE));
+ ret = TRUE;
+ break;
+ }
+ prev = current;
+ }
+ RELEASE_MUTEX (&ib->lock);
+ }
+
+ return ret;
+}
+
+TapAdapterPointer
+LookupAdapterInInstanceList (PDEVICE_OBJECT p_DeviceObject)
+{
+ BOOLEAN got_lock;
+ TapAdapterPointer ret = NULL;
+ INSTANCE_BUCKET *ib = &g_InstanceHash->buckets[InstanceHashValue((PVOID)p_DeviceObject)];
+
+ ACQUIRE_MUTEX_ADAPTIVE (&ib->lock, got_lock);
+
+ if (got_lock)
+ {
+ INSTANCE *current, *prev=NULL;
+ for (current = ib->list; current != NULL; current = current->next)
+ {
+ if (p_DeviceObject == INSTANCE_KEY (current->m_Adapter)) // found match
+ {
+ // move it to head of list
+ if (prev)
+ {
+ prev->next = current->next;
+ current->next = ib->list;
+ ib->list = current;
+ }
+ ret = ib->list->m_Adapter;
+ break;
+ }
+ prev = current;
+ }
+ RELEASE_MUTEX (&ib->lock);
+ }
+
+ return ret;
+}
diff --git a/tap-win32/lock.h b/tap-win32/lock.h
new file mode 100755
index 0000000..ea75414
--- /dev/null
+++ b/tap-win32/lock.h
@@ -0,0 +1,75 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+typedef struct
+{
+ volatile long count;
+} MUTEX;
+
+#define MUTEX_SLEEP_TIME 10000 // microseconds
+
+#define INIT_MUTEX(m) { (m)->count = 0; }
+
+#define ACQUIRE_MUTEX_BLOCKING(m) \
+{ \
+ while (NdisInterlockedIncrement (&((m)->count)) != 1) \
+ { \
+ NdisInterlockedDecrement(&((m)->count)); \
+ NdisMSleep(MUTEX_SLEEP_TIME); \
+ } \
+}
+
+#define RELEASE_MUTEX(m) \
+{ \
+ NdisInterlockedDecrement(&((m)->count)); \
+}
+
+#define ACQUIRE_MUTEX_NONBLOCKING(m, result) \
+{ \
+ if (NdisInterlockedIncrement (&((m)->count)) != 1) \
+ { \
+ NdisInterlockedDecrement(&((m)->count)); \
+ result = FALSE; \
+ } \
+ else \
+ { \
+ result = TRUE; \
+ } \
+}
+
+#define ACQUIRE_MUTEX_ADAPTIVE(m, result) \
+{ \
+ result = TRUE; \
+ while (NdisInterlockedIncrement (&((m)->count)) != 1) \
+ { \
+ NdisInterlockedDecrement(&((m)->count)); \
+ if (KeGetCurrentIrql () < DISPATCH_LEVEL) \
+ NdisMSleep(MUTEX_SLEEP_TIME); \
+ else \
+ { \
+ result = FALSE; \
+ break; \
+ } \
+ } \
+}
diff --git a/tap-win32/macinfo.c b/tap-win32/macinfo.c
new file mode 100755
index 0000000..8d512ec
--- /dev/null
+++ b/tap-win32/macinfo.c
@@ -0,0 +1,154 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "macinfo.h"
+
+int
+HexStringToDecimalInt (const int p_Character)
+{
+ int l_Value = 0;
+
+ if (p_Character >= 'A' && p_Character <= 'F')
+ l_Value = (p_Character - 'A') + 10;
+ else if (p_Character >= 'a' && p_Character <= 'f')
+ l_Value = (p_Character - 'a') + 10;
+ else if (p_Character >= '0' && p_Character <= '9')
+ l_Value = p_Character - '0';
+
+ return l_Value;
+}
+
+BOOLEAN
+ParseMAC (MACADDR dest, const char *src)
+{
+ int c;
+ int mac_index = 0;
+ BOOLEAN high_digit = FALSE;
+ int delim_action = 1;
+
+ MYASSERT (src);
+ MYASSERT (dest);
+
+ CLEAR_MAC (dest);
+
+ while (c = *src++)
+ {
+ if (IsMacDelimiter (c))
+ {
+ mac_index += delim_action;
+ high_digit = FALSE;
+ delim_action = 1;
+ }
+ else if (IsHexDigit (c))
+ {
+ const int digit = HexStringToDecimalInt (c);
+ if (mac_index < sizeof (MACADDR))
+ {
+ if (!high_digit)
+ {
+ dest[mac_index] = (char)(digit);
+ high_digit = TRUE;
+ delim_action = 1;
+ }
+ else
+ {
+ dest[mac_index] = (char)(dest[mac_index] * 16 + digit);
+ ++mac_index;
+ high_digit = FALSE;
+ delim_action = 0;
+ }
+ }
+ else
+ return FALSE;
+ }
+ else
+ return FALSE;
+ }
+
+ return (mac_index + delim_action) >= sizeof (MACADDR);
+}
+
+/*
+ * Generate a MAC using the GUID in the adapter name.
+ *
+ * The mac is constructed as 00:FF:xx:xx:xx:xx where
+ * the Xs are taken from the first 32 bits of the GUID in the
+ * adapter name. This is similar to the Linux 2.4 tap MAC
+ * generator, except linux uses 32 random bits for the Xs.
+ *
+ * In general, this solution is reasonable for most
+ * applications except for very large bridged TAP networks,
+ * where the probability of address collisions becomes more
+ * than infintesimal.
+ *
+ * Using the well-known "birthday paradox", on a 1000 node
+ * network the probability of collision would be
+ * 0.000116292153. On a 10,000 node network, the probability
+ * of collision would be 0.01157288998621678766.
+ */
+
+VOID GenerateRandomMac (MACADDR mac, const unsigned char *adapter_name)
+{
+ unsigned const char *cp = adapter_name;
+ unsigned char c;
+ unsigned int i = 2;
+ unsigned int byte = 0;
+ int brace = 0;
+ int state = 0;
+
+ CLEAR_MAC (mac);
+
+ mac[0] = 0x00;
+ mac[1] = 0xFF;
+
+ while (c = *cp++)
+ {
+ if (i >= sizeof (MACADDR))
+ break;
+ if (c == '{')
+ brace = 1;
+ if (IsHexDigit (c) && brace)
+ {
+ const unsigned int digit = HexStringToDecimalInt (c);
+ if (state)
+ {
+ byte <<= 4;
+ byte |= digit;
+ mac[i++] = (unsigned char) byte;
+ state = 0;
+ }
+ else
+ {
+ byte = digit;
+ state = 1;
+ }
+ }
+ }
+}
+
+VOID GenerateRelatedMAC (MACADDR dest, const MACADDR src, const int delta)
+{
+ COPY_MAC (dest, src);
+ dest[2] += (UCHAR) delta;
+}
diff --git a/tap-win32/macinfo.h b/tap-win32/macinfo.h
new file mode 100755
index 0000000..51e930d
--- /dev/null
+++ b/tap-win32/macinfo.h
@@ -0,0 +1,38 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MacInfoDefined
+#define MacInfoDefined
+
+//===================================================================================
+// Macros
+//===================================================================================
+#define IsMacDelimiter(a) (a == ':' || a == '-' || a == '.')
+#define IsHexDigit(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
+
+#define COPY_MAC(dest, src) NdisMoveMemory ((dest), (src), sizeof (MACADDR))
+#define CLEAR_MAC(dest) NdisZeroMemory ((dest), sizeof (MACADDR))
+#define MAC_EQUAL(a,b) (memcmp ((a), (b), sizeof (MACADDR)) == 0)
+
+#endif
diff --git a/tap-win32/mem.c b/tap-win32/mem.c
new file mode 100755
index 0000000..6fbbed5
--- /dev/null
+++ b/tap-win32/mem.c
@@ -0,0 +1,186 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//------------------
+// Memory Management
+//------------------
+
+PVOID
+MemAlloc (ULONG p_Size, BOOLEAN zero)
+{
+ PVOID l_Return = NULL;
+
+ if (p_Size)
+ {
+ __try
+ {
+ if (NdisAllocateMemoryWithTag (&l_Return, p_Size, 'APAT')
+ == NDIS_STATUS_SUCCESS)
+ {
+ if (zero)
+ NdisZeroMemory (l_Return, p_Size);
+ }
+ else
+ l_Return = NULL;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ l_Return = NULL;
+ }
+ }
+
+ return l_Return;
+}
+
+VOID
+MemFree (PVOID p_Addr, ULONG p_Size)
+{
+ if (p_Addr && p_Size)
+ {
+ __try
+ {
+#if DBG
+ NdisZeroMemory (p_Addr, p_Size);
+#endif
+ NdisFreeMemory (p_Addr, p_Size, 0);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ }
+}
+
+/*
+ * Circular queue management routines.
+ */
+
+#define QUEUE_BYTE_ALLOCATION(size) \
+ (sizeof (Queue) + (size * sizeof (PVOID)))
+
+#define QUEUE_ADD_INDEX(var, inc) \
+{ \
+ var += inc; \
+ if (var >= q->capacity) \
+ var -= q->capacity; \
+ MYASSERT (var < q->capacity); \
+}
+
+#define QUEUE_SANITY_CHECK() \
+ MYASSERT (q != NULL && q->base < q->capacity && q->size <= q->capacity)
+
+#define QueueCount(q) (q->size)
+
+#define UPDATE_MAX_SIZE() \
+{ \
+ if (q->size > q->max_size) \
+ q->max_size = q->size; \
+}
+
+Queue *
+QueueInit (ULONG capacity)
+{
+ Queue *q;
+
+ MYASSERT (capacity > 0);
+ q = (Queue *) MemAlloc (QUEUE_BYTE_ALLOCATION (capacity), TRUE);
+ if (!q)
+ return NULL;
+
+ q->base = q->size = 0;
+ q->capacity = capacity;
+ q->max_size = 0;
+ return q;
+}
+
+VOID
+QueueFree (Queue *q)
+{
+ if (q)
+ {
+ QUEUE_SANITY_CHECK ();
+ MemFree (q, QUEUE_BYTE_ALLOCATION (q->capacity));
+ }
+}
+
+PVOID
+QueuePush (Queue *q, PVOID item)
+{
+ ULONG dest;
+ QUEUE_SANITY_CHECK ();
+ if (q->size == q->capacity)
+ return NULL;
+ dest = q->base;
+ QUEUE_ADD_INDEX (dest, q->size);
+ q->data[dest] = item;
+ ++q->size;
+ UPDATE_MAX_SIZE();
+ return item;
+}
+
+PVOID
+QueuePop (Queue *q)
+{
+ ULONG oldbase;
+ QUEUE_SANITY_CHECK ();
+ if (!q->size)
+ return NULL;
+ oldbase = q->base;
+ QUEUE_ADD_INDEX (q->base, 1);
+ --q->size;
+ UPDATE_MAX_SIZE();
+ return q->data[oldbase];
+}
+
+PVOID
+QueueExtract (Queue *q, PVOID item)
+{
+ ULONG src, dest, count, n;
+ QUEUE_SANITY_CHECK ();
+ n = 0;
+ src = dest = q->base;
+ count = q->size;
+ while (count--)
+ {
+ if (item == q->data[src])
+ {
+ ++n;
+ --q->size;
+ }
+ else
+ {
+ q->data[dest] = q->data[src];
+ QUEUE_ADD_INDEX (dest, 1);
+ }
+ QUEUE_ADD_INDEX (src, 1);
+ }
+ if (n)
+ return item;
+ else
+ return NULL;
+}
+
+#undef QUEUE_BYTE_ALLOCATION
+#undef QUEUE_ADD_INDEX
+#undef QUEUE_SANITY_CHECK
+#undef UPDATE_MAX_SIZE
diff --git a/tap-win32/proto.h b/tap-win32/proto.h
new file mode 100755
index 0000000..894a37f
--- /dev/null
+++ b/tap-win32/proto.h
@@ -0,0 +1,224 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//============================================================
+// MAC address, Ethernet header, and ARP
+//============================================================
+
+#pragma pack(1)
+
+#define IP_HEADER_SIZE 20
+#define IPV6_HEADER_SIZE 40
+
+typedef unsigned char MACADDR [6];
+typedef unsigned long IPADDR;
+typedef unsigned char IPV6ADDR [16];
+
+//-----------------
+// Ethernet address
+//-----------------
+
+typedef struct {
+ MACADDR addr;
+} ETH_ADDR;
+
+typedef struct {
+ ETH_ADDR list[NIC_MAX_MCAST_LIST];
+} MC_LIST;
+
+//----------------
+// Ethernet header
+//----------------
+
+typedef struct
+{
+ MACADDR dest; /* destination eth addr */
+ MACADDR src; /* source ether addr */
+
+# define ETH_P_IP 0x0800 /* IPv4 protocol */
+# define ETH_P_IPV6 0x86DD /* IPv6 protocol */
+# define ETH_P_ARP 0x0806 /* ARP protocol */
+ USHORT proto; /* packet type ID field */
+} ETH_HEADER, *PETH_HEADER;
+
+//----------------
+// ARP packet
+//----------------
+
+typedef struct
+ {
+ MACADDR m_MAC_Destination; // Reverse these two
+ MACADDR m_MAC_Source; // to answer ARP requests
+ USHORT m_Proto; // 0x0806
+
+# define MAC_ADDR_TYPE 0x0001
+ USHORT m_MAC_AddressType; // 0x0001
+
+ USHORT m_PROTO_AddressType; // 0x0800
+ UCHAR m_MAC_AddressSize; // 0x06
+ UCHAR m_PROTO_AddressSize; // 0x04
+
+# define ARP_REQUEST 0x0001
+# define ARP_REPLY 0x0002
+ USHORT m_ARP_Operation; // 0x0001 for ARP request, 0x0002 for ARP reply
+
+ MACADDR m_ARP_MAC_Source;
+ IPADDR m_ARP_IP_Source;
+ MACADDR m_ARP_MAC_Destination;
+ IPADDR m_ARP_IP_Destination;
+ }
+ARP_PACKET, *PARP_PACKET;
+
+//----------
+// IP Header
+//----------
+
+typedef struct {
+# define IPH_GET_VER(v) (((v) >> 4) & 0x0F)
+# define IPH_GET_LEN(v) (((v) & 0x0F) << 2)
+ UCHAR version_len;
+
+ UCHAR tos;
+ USHORT tot_len;
+ USHORT id;
+
+# define IP_OFFMASK 0x1fff
+ USHORT frag_off;
+
+ UCHAR ttl;
+
+# define IPPROTO_UDP 17 /* UDP protocol */
+# define IPPROTO_TCP 6 /* TCP protocol */
+# define IPPROTO_ICMP 1 /* ICMP protocol */
+# define IPPROTO_IGMP 2 /* IGMP protocol */
+ UCHAR protocol;
+
+ USHORT check;
+ ULONG saddr;
+ ULONG daddr;
+ /* The options start here. */
+} IPHDR;
+
+//-----------
+// UDP header
+//-----------
+
+typedef struct {
+ USHORT source;
+ USHORT dest;
+ USHORT len;
+ USHORT check;
+} UDPHDR;
+
+//--------------------------
+// TCP header, per RFC 793.
+//--------------------------
+
+typedef struct {
+ USHORT source; /* source port */
+ USHORT dest; /* destination port */
+ ULONG seq; /* sequence number */
+ ULONG ack_seq; /* acknowledgement number */
+
+# define TCPH_GET_DOFF(d) (((d) & 0xF0) >> 2)
+ UCHAR doff_res;
+
+# define TCPH_FIN_MASK (1<<0)
+# define TCPH_SYN_MASK (1<<1)
+# define TCPH_RST_MASK (1<<2)
+# define TCPH_PSH_MASK (1<<3)
+# define TCPH_ACK_MASK (1<<4)
+# define TCPH_URG_MASK (1<<5)
+# define TCPH_ECE_MASK (1<<6)
+# define TCPH_CWR_MASK (1<<7)
+ UCHAR flags;
+
+ USHORT window;
+ USHORT check;
+ USHORT urg_ptr;
+} TCPHDR;
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+
+//------------
+// IPv6 Header
+//------------
+
+typedef struct {
+ UCHAR version_prio;
+ UCHAR flow_lbl[3];
+ USHORT payload_len;
+# define IPPROTO_ICMPV6 0x3a /* ICMP protocol v6 */
+ UCHAR nexthdr;
+ UCHAR hop_limit;
+ IPV6ADDR saddr;
+ IPV6ADDR daddr;
+} IPV6HDR;
+
+//--------------------------------------------
+// IPCMPv6 NS/NA Packets (RFC4443 and RFC4861)
+//--------------------------------------------
+
+// Neighbor Solictiation - RFC 4861, 4.3
+// (this is just the ICMPv6 part of the packet)
+typedef struct {
+ UCHAR type;
+# define ICMPV6_TYPE_NS 135 // neighbour solicitation
+ UCHAR code;
+# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA
+ USHORT checksum;
+ ULONG reserved;
+ IPV6ADDR target_addr;
+} ICMPV6_NS;
+
+// Neighbor Advertisement - RFC 4861, 4.4 + 4.6/4.6.1
+// (this is just the ICMPv6 payload)
+typedef struct {
+ UCHAR type;
+# define ICMPV6_TYPE_NA 136 // neighbour advertisement
+ UCHAR code;
+# define ICMPV6_CODE_0 0 // no specific sub-code for NS/NA
+ USHORT checksum;
+ UCHAR rso_bits; // Router(0), Solicited(2), Ovrrd(4)
+ UCHAR reserved[3];
+ IPV6ADDR target_addr;
+// always include "Target Link-layer Address" option (RFC 4861 4.6.1)
+ UCHAR opt_type;
+#define ICMPV6_OPTION_TLLA 2
+ UCHAR opt_length;
+#define ICMPV6_LENGTH_TLLA 1 // multiplied by 8 -> 1 = 8 bytes
+ MACADDR target_macaddr;
+} ICMPV6_NA;
+
+// this is the complete packet with Ethernet and IPv6 headers
+typedef struct {
+ ETH_HEADER eth;
+ IPV6HDR ipv6;
+ ICMPV6_NA icmpv6;
+} ICMPV6_NA_PKT;
+
+#pragma pack()
diff --git a/tap-win32/prototypes.h b/tap-win32/prototypes.h
new file mode 100755
index 0000000..55454d5
--- /dev/null
+++ b/tap-win32/prototypes.h
@@ -0,0 +1,260 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TAP_PROTOTYPES_DEFINED
+#define TAP_PROTOTYPES_DEFINED
+
+NTSTATUS DriverEntry
+ (
+ IN PDRIVER_OBJECT p_DriverObject,
+ IN PUNICODE_STRING p_RegistryPath
+ );
+
+VOID TapDriverUnload
+ (
+ IN PDRIVER_OBJECT p_DriverObject
+ );
+
+NDIS_STATUS AdapterCreate
+ (
+ OUT PNDIS_STATUS p_ErrorStatus,
+ OUT PUINT p_MediaIndex,
+ IN PNDIS_MEDIUM p_Media,
+ IN UINT p_MediaCount,
+ IN NDIS_HANDLE p_AdapterHandle,
+ IN NDIS_HANDLE p_ConfigurationHandle
+ );
+
+VOID AdapterHalt
+ (
+ IN NDIS_HANDLE p_AdapterContext
+ );
+
+VOID AdapterFreeResources
+ (
+ TapAdapterPointer p_Adapter
+ );
+
+NDIS_STATUS AdapterReset
+ (
+ OUT PBOOLEAN p_AddressingReset,
+ IN NDIS_HANDLE p_AdapterContext
+ );
+
+NDIS_STATUS AdapterQuery
+ (
+ IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesWritten,
+ OUT PULONG p_BytesNeeded
+ );
+
+NDIS_STATUS AdapterModify
+ (
+ IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesRead,
+ OUT PULONG p_BytesNeeded
+ );
+
+NDIS_STATUS AdapterTransmit
+ (
+ IN NDIS_HANDLE p_AdapterContext,
+ IN PNDIS_PACKET p_Packet,
+ IN UINT p_Flags
+ );
+
+NDIS_STATUS AdapterReceive
+ (
+ OUT PNDIS_PACKET p_Packet,
+ OUT PUINT p_Transferred,
+ IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_HANDLE p_ReceiveContext,
+ IN UINT p_Offset,
+ IN UINT p_ToTransfer
+ );
+
+NTSTATUS TapDeviceHook
+ (
+ IN PDEVICE_OBJECT p_DeviceObject,
+ IN PIRP p_IRP
+ );
+
+NDIS_STATUS CreateTapDevice
+ (
+ TapExtensionPointer p_Extension,
+ const char *p_Name
+ );
+
+VOID DestroyTapDevice
+ (
+ TapExtensionPointer p_Extension
+ );
+
+VOID TapDeviceFreeResources
+ (
+ TapExtensionPointer p_Extension
+ );
+
+NTSTATUS CompleteIRP
+ (
+ IN PIRP p_IRP,
+ IN TapPacketPointer p_PacketBuffer,
+ IN CCHAR PriorityBoost
+ );
+
+VOID CancelIRPCallback
+ (
+ IN PDEVICE_OBJECT p_DeviceObject,
+ IN PIRP p_IRP
+ );
+
+VOID CancelIRP
+ (
+ TapExtensionPointer p_Extension,
+ IN PIRP p_IRP,
+ BOOLEAN callback
+ );
+
+VOID FlushQueues
+ (
+ TapExtensionPointer p_Extension
+ );
+
+VOID ResetTapAdapterState
+ (
+ TapAdapterPointer p_Adapter
+ );
+
+BOOLEAN ProcessARP
+ (
+ TapAdapterPointer p_Adapter,
+ const PARP_PACKET src,
+ const IPADDR adapter_ip,
+ const IPADDR ip_network,
+ const IPADDR ip_netmask,
+ const MACADDR mac
+ );
+
+VOID SetMediaStatus
+ (
+ TapAdapterPointer p_Adapter,
+ BOOLEAN state
+ );
+
+VOID InjectPacketDeferred
+ (
+ TapAdapterPointer p_Adapter,
+ UCHAR *packet,
+ const unsigned int len
+ );
+
+VOID InjectPacketNow
+ (
+ TapAdapterPointer p_Adapter,
+ UCHAR *packet,
+ const unsigned int len
+ );
+
+// for KDEFERRED_ROUTINE and Static Driver Verifier
+//#include <wdm.h>
+//KDEFERRED_ROUTINE InjectPacketDpc;
+
+VOID InjectPacketDpc
+ (
+ KDPC *Dpc,
+ PVOID DeferredContext,
+ PVOID SystemArgument1,
+ PVOID SystemArgument2
+ );
+
+VOID CheckIfDhcpAndTunMode
+ (
+ TapAdapterPointer p_Adapter
+ );
+
+VOID HookDispatchFunctions();
+
+#if ENABLE_NONADMIN
+
+#if DDKVER_MAJOR < 5600
+/*
+ * Better solution for use on Vista DDK, but possibly not compatible with
+ * earlier DDKs:
+ *
+ * Eliminate the definition of SECURITY_DESCRIPTOR (and even ZwSetSecurityObject),
+ * and at the top of tapdrv.c change:
+ *
+ * #include <ndis.h>
+ * #include <ntstrsafe.h>
+ * #include <ntddk.h>
+ *
+ * To
+ *
+ * #include <ntifs.h>
+ * #include <ndis.h>
+ * #include <ntstrsafe.h>
+ */
+typedef struct _SECURITY_DESCRIPTOR {
+ unsigned char opaque[64];
+} SECURITY_DESCRIPTOR;
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwSetSecurityObject (
+ IN HANDLE Handle,
+ IN SECURITY_INFORMATION SecurityInformation,
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor);
+
+#endif
+
+VOID AllowNonAdmin (TapExtensionPointer p_Extension);
+
+#endif
+
+struct WIN2K_NDIS_MINIPORT_BLOCK
+{
+ unsigned char opaque[16];
+ UNICODE_STRING MiniportName; // how mini-port refers to us
+};
+
+#if PACKET_TRUNCATION_CHECK
+
+VOID IPv4PacketSizeVerify
+ (
+ const UCHAR *data,
+ ULONG length,
+ BOOLEAN tun,
+ const char *prefix,
+ LONG *counter
+ );
+
+#endif
+
+#endif
diff --git a/tap-win32/resource.rc b/tap-win32/resource.rc
new file mode 100755
index 0000000..84884cf
--- /dev/null
+++ b/tap-win32/resource.rc
@@ -0,0 +1,58 @@
+#include <windows.h>
+#include <ntverp.h>
+
+/* get VERSION */
+#include "common.h"
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+
+#define VER_FILETYPE VFT_DRV
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+
+#define VER_COMPANYNAME_STR "The OpenVPN Project"
+#define VER_FILEDESCRIPTION_STR "TAP-Win32 Virtual Network Driver"
+#define VER_ORIGINALFILENAME_STR TAP_COMPONENT_ID ".sys"
+#define VER_LEGALCOPYRIGHT_YEARS "2003-2010"
+#define VER_LEGALCOPYRIGHT_STR "OpenVPN Technologies, Inc."
+
+
+#define VER_PRODUCTNAME_STR VER_FILEDESCRIPTION_STR
+#define VER_PRODUCTVERSION TAP_DRIVER_MAJOR_VERSION,00,00,TAP_DRIVER_MINOR_VERSION
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#define VSTRING PACKAGE_VERSION " " XSTR(TAP_DRIVER_MAJOR_VERSION) "/" XSTR(TAP_DRIVER_MINOR_VERSION)
+
+#ifdef DBG
+#define VER_PRODUCTVERSION_STR VSTRING " (DEBUG)"
+#else
+#define VER_PRODUCTVERSION_STR VSTRING
+#endif
+
+#define VER_INTERNALNAME_STR VER_ORIGINALFILENAME_STR
+
+#include "common.ver"
diff --git a/tap-win32/tapdrvr.c b/tap-win32/tapdrvr.c
new file mode 100755
index 0000000..7ab3916
--- /dev/null
+++ b/tap-win32/tapdrvr.c
@@ -0,0 +1,3145 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//======================================================
+// This driver is designed to work on Win 2000 or higher
+// versions of Windows.
+//
+// It is SMP-safe and handles NDIS 5 power management.
+//
+// By default we operate as a "tap" virtual ethernet
+// 802.3 interface, but we can emulate a "tun"
+// interface (point-to-point IPv4) through the
+// TAP_IOCTL_CONFIG_POINT_TO_POINT or
+// TAP_IOCTL_CONFIG_TUN ioctl.
+//======================================================
+
+#include "common.h"
+#ifndef DDKVER_MAJOR
+#error DDKVER_MAJOR must be defined as the major number of the DDK Version
+#endif
+
+#define NDIS_MINIPORT_DRIVER
+#define BINARY_COMPATIBLE 0
+#define NDIS50_MINIPORT 1
+#define NDIS_WDM 0
+#define NDIS50 1
+#define NTSTRSAFE_LIB
+
+// Debug info output
+#define ALSO_DBGPRINT 1
+#define DEBUGP_AT_DISPATCH 0
+
+//========================================================
+// Check for truncated IPv4 packets, log errors if found.
+//========================================================
+#define PACKET_TRUNCATION_CHECK 0
+
+//========================================================
+// EXPERIMENTAL -- Configure TAP device object to be
+// accessible from non-administrative accounts, based
+// on an advanced properties setting.
+//
+// Duplicates the functionality of OpenVPN's
+// --allow-nonadmin directive.
+//========================================================
+#define ENABLE_NONADMIN 1
+
+#if DDKVER_MAJOR < 5600
+#include <ndis.h>
+#include <ntstrsafe.h>
+#include <ntddk.h>
+#else
+#include <ntifs.h>
+#include <ndis.h>
+#include <ntstrsafe.h>
+#endif
+
+#include "lock.h"
+#include "constants.h"
+#include "proto.h"
+#include "error.h"
+#include "endian.h"
+#include "dhcp.h"
+#include "types.h"
+#include "prototypes.h"
+
+#include "mem.c"
+#include "macinfo.c"
+#include "error.c"
+#include "dhcp.c"
+#include "instance.c"
+
+#define IS_UP(ta) \
+ ((ta)->m_InterfaceIsRunning && (ta)->m_Extension.m_TapIsRunning)
+
+#define INCREMENT_STAT(s) ++(s)
+
+#define NAME_BUFFER_SIZE 80
+
+//========================================================
+// Globals
+//========================================================
+
+NDIS_HANDLE g_NdisWrapperHandle;
+
+const UINT g_SupportedOIDList[] = {
+ OID_GEN_HARDWARE_STATUS,
+ OID_GEN_MEDIA_SUPPORTED,
+ OID_GEN_MEDIA_IN_USE,
+ OID_GEN_MAXIMUM_LOOKAHEAD,
+ OID_GEN_MAC_OPTIONS,
+ OID_GEN_LINK_SPEED,
+ OID_GEN_TRANSMIT_BLOCK_SIZE,
+ OID_GEN_RECEIVE_BLOCK_SIZE,
+ OID_GEN_VENDOR_DESCRIPTION,
+ OID_GEN_DRIVER_VERSION,
+ OID_GEN_XMIT_OK,
+ OID_GEN_RCV_OK,
+ OID_GEN_XMIT_ERROR,
+ OID_GEN_RCV_ERROR,
+ OID_802_3_PERMANENT_ADDRESS,
+ OID_802_3_CURRENT_ADDRESS,
+ OID_GEN_RCV_NO_BUFFER,
+ OID_802_3_RCV_ERROR_ALIGNMENT,
+ OID_802_3_XMIT_ONE_COLLISION,
+ OID_802_3_XMIT_MORE_COLLISIONS,
+ OID_802_3_MULTICAST_LIST,
+ OID_802_3_MAXIMUM_LIST_SIZE,
+ OID_GEN_VENDOR_ID,
+ OID_GEN_CURRENT_LOOKAHEAD,
+ OID_GEN_CURRENT_PACKET_FILTER,
+ OID_GEN_PROTOCOL_OPTIONS,
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_TRANSMIT_BUFFER_SPACE,
+ OID_GEN_RECEIVE_BUFFER_SPACE,
+ OID_GEN_MAXIMUM_FRAME_SIZE,
+ OID_GEN_VENDOR_DRIVER_VERSION,
+ OID_GEN_MAXIMUM_SEND_PACKETS,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_SUPPORTED_LIST
+};
+
+//============================================================
+// Driver Entry
+//============================================================
+#pragma NDIS_INIT_FUNCTION (DriverEntry)
+
+NTSTATUS
+DriverEntry (IN PDRIVER_OBJECT p_DriverObject,
+ IN PUNICODE_STRING p_RegistryPath)
+{
+ NDIS_STATUS l_Status = NDIS_STATUS_FAILURE;
+ NDIS_MINIPORT_CHARACTERISTICS *l_Properties = NULL;
+
+ //========================================================
+ // Notify NDIS that a new miniport driver is initializing.
+ //========================================================
+
+ NdisMInitializeWrapper (&g_NdisWrapperHandle,
+ p_DriverObject,
+ p_RegistryPath, NULL);
+
+ //======================
+ // Global initialization
+ //======================
+
+#if DBG
+ MyDebugInit (10000); // Allocate debugging text space
+#endif
+
+ if (!InitInstanceList ())
+ {
+ DEBUGP (("[TAP] Allocation failed for adapter instance list\n"));
+ goto cleanup;
+ }
+
+ //=======================================
+ // Set and register miniport entry points
+ //=======================================
+
+ l_Properties = MemAlloc (sizeof (NDIS_MINIPORT_CHARACTERISTICS), TRUE);
+
+ if (l_Properties == NULL)
+ {
+ DEBUGP (("[TAP] Allocation failed for miniport entry points\n"));
+ goto cleanup;
+ }
+
+ l_Properties->MajorNdisVersion = TAP_NDIS_MAJOR_VERSION;
+ l_Properties->MinorNdisVersion = TAP_NDIS_MINOR_VERSION;
+ l_Properties->InitializeHandler = AdapterCreate;
+ l_Properties->HaltHandler = AdapterHalt;
+ l_Properties->ResetHandler = AdapterReset; /* DISPATCH_LEVEL */
+ l_Properties->TransferDataHandler = AdapterReceive; /* DISPATCH_LEVEL */
+ l_Properties->SendHandler = AdapterTransmit; /* DISPATCH_LEVEL */
+ l_Properties->QueryInformationHandler = AdapterQuery; /* DISPATCH_LEVEL */
+ l_Properties->SetInformationHandler = AdapterModify; /* DISPATCH_LEVEL */
+
+ switch (l_Status =
+ NdisMRegisterMiniport (g_NdisWrapperHandle, l_Properties,
+ sizeof (NDIS_MINIPORT_CHARACTERISTICS)))
+ {
+ case NDIS_STATUS_SUCCESS:
+ {
+ DEBUGP (("[TAP] version [%d.%d] %s %s registered miniport successfully\n",
+ TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION,
+ __DATE__,
+ __TIME__));
+ DEBUGP (("Registry Path: '%.*S'\n", p_RegistryPath->Length/2, p_RegistryPath->Buffer));
+ break;
+ }
+
+ case NDIS_STATUS_BAD_CHARACTERISTICS:
+ {
+ DEBUGP (("[TAP] Miniport characteristics were badly defined\n"));
+ NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+ break;
+ }
+
+ case NDIS_STATUS_BAD_VERSION:
+ {
+ DEBUGP
+ (("[TAP] NDIS Version is wrong for the given characteristics\n"));
+ NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+ break;
+ }
+
+ case NDIS_STATUS_RESOURCES:
+ {
+ DEBUGP (("[TAP] Insufficient resources\n"));
+ NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+ break;
+ }
+
+ default:
+ case NDIS_STATUS_FAILURE:
+ {
+ DEBUGP (("[TAP] Unknown fatal registration error\n"));
+ NdisTerminateWrapper (g_NdisWrapperHandle, NULL);
+ break;
+ }
+ }
+
+ cleanup:
+ if (l_Properties)
+ MemFree (l_Properties, sizeof (NDIS_MINIPORT_CHARACTERISTICS));
+
+ if (l_Status == NDIS_STATUS_SUCCESS)
+ NdisMRegisterUnloadHandler (g_NdisWrapperHandle, TapDriverUnload);
+ else
+ TapDriverUnload (p_DriverObject);
+
+ return l_Status;
+}
+
+//============================================================
+// Driver Unload
+//============================================================
+VOID
+TapDriverUnload (IN PDRIVER_OBJECT p_DriverObject)
+{
+ DEBUGP (("[TAP] version [%d.%d] %s %s unloaded, instances=%d, imbs=%d\n",
+ TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION,
+ __DATE__,
+ __TIME__,
+ NInstances(),
+ InstanceMaxBucketSize()));
+
+ FreeInstanceList ();
+
+ //==============================
+ // Free debugging text space
+ //==============================
+#if DBG
+ MyDebugFree ();
+#endif
+}
+
+//==========================================================
+// Adapter Initialization
+//==========================================================
+NDIS_STATUS AdapterCreate
+(OUT PNDIS_STATUS p_ErrorStatus,
+ OUT PUINT p_MediaIndex,
+ IN PNDIS_MEDIUM p_Media,
+ IN UINT p_MediaCount,
+ IN NDIS_HANDLE p_AdapterHandle,
+ IN NDIS_HANDLE p_ConfigurationHandle)
+{
+ TapAdapterPointer l_Adapter = NULL;
+
+ NDIS_MEDIUM l_PreferredMedium = NdisMedium802_3; // Ethernet
+ BOOLEAN l_MacFromRegistry = FALSE;
+ UINT l_Index;
+ NDIS_STATUS status;
+
+#if ENABLE_NONADMIN
+ BOOLEAN enable_non_admin = FALSE;
+#endif
+
+ DEBUGP (("[TAP] AdapterCreate called\n"));
+
+ //====================================
+ // Make sure adapter type is supported
+ //====================================
+
+ for (l_Index = 0;
+ l_Index < p_MediaCount && p_Media[l_Index] != l_PreferredMedium;
+ ++l_Index);
+
+ if (l_Index == p_MediaCount)
+ {
+ DEBUGP (("[TAP] Unsupported adapter type [wanted: %d]\n",
+ l_PreferredMedium));
+ return NDIS_STATUS_UNSUPPORTED_MEDIA;
+ }
+
+ *p_MediaIndex = l_Index;
+
+ //=========================================
+ // Allocate memory for TapAdapter structure
+ //=========================================
+
+ l_Adapter = MemAlloc (sizeof (TapAdapter), TRUE);
+
+ if (l_Adapter == NULL)
+ {
+ DEBUGP (("[TAP] Couldn't allocate adapter memory\n"));
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ //==========================================
+ // Inform the NDIS library about significant
+ // features of our virtual NIC.
+ //==========================================
+
+ NdisMSetAttributesEx
+ (p_AdapterHandle,
+ (NDIS_HANDLE) l_Adapter,
+ 16,
+ NDIS_ATTRIBUTE_DESERIALIZE
+ | NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT
+ | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT
+ | NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+ NdisInterfaceInternal);
+
+ //=====================================
+ // Initialize simple Adapter parameters
+ //=====================================
+
+ l_Adapter->m_Lookahead = DEFAULT_PACKET_LOOKAHEAD;
+ l_Adapter->m_Medium = l_PreferredMedium;
+ l_Adapter->m_DeviceState = '?';
+ l_Adapter->m_MiniportAdapterHandle = p_AdapterHandle;
+
+ //==================================
+ // Allocate spinlock for controlling
+ // access to multicast address list.
+ //==================================
+ NdisAllocateSpinLock (&l_Adapter->m_MCLock);
+ l_Adapter->m_MCLockAllocated = TRUE;
+
+ //====================================================
+ // Register a shutdown handler which will be called
+ // on system restart/shutdown to halt our virtual NIC.
+ //====================================================
+
+ NdisMRegisterAdapterShutdownHandler (p_AdapterHandle, l_Adapter,
+ AdapterHalt);
+ l_Adapter->m_RegisteredAdapterShutdownHandler = TRUE;
+
+ //============================================
+ // Get parameters from registry which were set
+ // in the adapter advanced properties dialog.
+ //============================================
+ {
+ NDIS_STATUS status;
+ NDIS_HANDLE configHandle;
+ NDIS_CONFIGURATION_PARAMETER *parm;
+
+ // set defaults in case our registry query fails
+ l_Adapter->m_MTU = ETHERNET_MTU;
+ l_Adapter->m_MediaStateAlwaysConnected = FALSE;
+ l_Adapter->m_MediaState = FALSE;
+
+ NdisOpenConfiguration (&status, &configHandle, p_ConfigurationHandle);
+ if (status != NDIS_STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] Couldn't open adapter registry\n"));
+ AdapterFreeResources (l_Adapter);
+ return status;
+ }
+
+ //====================================
+ // Allocate and construct adapter name
+ //====================================
+ {
+
+ NDIS_STRING mkey = NDIS_STRING_CONST("MiniportName");
+ NDIS_STRING vkey = NDIS_STRING_CONST("NdisVersion");
+ NDIS_STATUS vstatus;
+ NDIS_CONFIGURATION_PARAMETER *vparm;
+
+ NdisReadConfiguration (&vstatus, &vparm, configHandle, &vkey, NdisParameterInteger);
+ if (vstatus == NDIS_STATUS_SUCCESS)
+ DEBUGP (("[TAP] NdisReadConfiguration NdisVersion=%X\n", vparm->ParameterData.IntegerData));
+
+ NdisReadConfiguration (&status, &parm, configHandle, &mkey, NdisParameterString);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ if (parm->ParameterType == NdisParameterString)
+ {
+ DEBUGP (("[TAP] NdisReadConfiguration (MiniportName=%.*S)\n",
+ parm->ParameterData.StringData.Length/2,
+ parm->ParameterData.StringData.Buffer));
+
+ if (RtlUnicodeStringToAnsiString (
+ &l_Adapter->m_NameAnsi,
+ &parm->ParameterData.StringData,
+ TRUE) != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] MiniportName failed\n"));
+ status = NDIS_STATUS_RESOURCES;
+ }
+ }
+ }
+ else
+ {
+ /* "MiniportName" is available only XP and above. Not on Windows 2000. */
+ if (vstatus == NDIS_STATUS_SUCCESS && vparm->ParameterData.IntegerData == 0x50000)
+ {
+ /* Fallback for Windows 2000 with NDIS version 5.00.00
+ Don't use this on Vista, 'NDIS_MINIPORT_BLOCK' was changed! */
+ if (RtlUnicodeStringToAnsiString (&l_Adapter->m_NameAnsi,
+ &((struct WIN2K_NDIS_MINIPORT_BLOCK *) p_AdapterHandle)->MiniportName,
+ TRUE) != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] MiniportName (W2K) failed\n"));
+ status = NDIS_STATUS_RESOURCES;
+ }
+ else
+ {
+ DEBUGP (("[TAP] MiniportName (W2K) succeeded: %s\n", l_Adapter->m_NameAnsi.Buffer));
+ status = NDIS_STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+
+ /* Can't continue without name (see macro 'NAME') */
+ if (status != NDIS_STATUS_SUCCESS || !l_Adapter->m_NameAnsi.Buffer)
+ {
+ NdisCloseConfiguration (configHandle);
+ AdapterFreeResources (l_Adapter);
+ DEBUGP (("[TAP] failed to get miniport name\n"));
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ /* Read MTU setting from registry */
+ {
+ NDIS_STRING key = NDIS_STRING_CONST("MTU");
+ NdisReadConfiguration (&status, &parm, configHandle,
+ &key, NdisParameterInteger);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ if (parm->ParameterType == NdisParameterInteger)
+ {
+ int mtu = parm->ParameterData.IntegerData;
+ if (mtu < MINIMUM_MTU)
+ mtu = MINIMUM_MTU;
+ if (mtu > MAXIMUM_MTU)
+ mtu = MAXIMUM_MTU;
+ l_Adapter->m_MTU = mtu;
+ }
+ }
+ }
+
+ /* Read Media Status setting from registry */
+ {
+ NDIS_STRING key = NDIS_STRING_CONST("MediaStatus");
+ NdisReadConfiguration (&status, &parm, configHandle,
+ &key, NdisParameterInteger);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ if (parm->ParameterType == NdisParameterInteger)
+ {
+ if (parm->ParameterData.IntegerData)
+ {
+ l_Adapter->m_MediaStateAlwaysConnected = TRUE;
+ l_Adapter->m_MediaState = TRUE;
+ }
+ }
+ }
+ }
+
+#if ENABLE_NONADMIN
+ /* Read AllowNonAdmin setting from registry */
+ {
+ NDIS_STRING key = NDIS_STRING_CONST("AllowNonAdmin");
+ NdisReadConfiguration (&status, &parm, configHandle,
+ &key, NdisParameterInteger);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ if (parm->ParameterType == NdisParameterInteger)
+ {
+ if (parm->ParameterData.IntegerData)
+ {
+ enable_non_admin = TRUE;
+ }
+ }
+ }
+ }
+#endif
+
+ /* Read optional MAC setting from registry */
+ {
+ NDIS_STRING key = NDIS_STRING_CONST("MAC");
+ ANSI_STRING mac_string;
+ NdisReadConfiguration (&status, &parm, configHandle,
+ &key, NdisParameterString);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ if (parm->ParameterType == NdisParameterString)
+ {
+ if (RtlUnicodeStringToAnsiString (&mac_string, &parm->ParameterData.StringData, TRUE) == STATUS_SUCCESS)
+ {
+ l_MacFromRegistry = ParseMAC (l_Adapter->m_MAC, mac_string.Buffer);
+ RtlFreeAnsiString (&mac_string);
+ }
+ }
+ }
+ }
+
+ NdisCloseConfiguration (configHandle);
+
+ DEBUGP (("[%s] MTU=%d\n", NAME (l_Adapter), l_Adapter->m_MTU));
+ }
+
+ //==================================
+ // Store and update MAC address info
+ //==================================
+
+ if (!l_MacFromRegistry)
+ GenerateRandomMac (l_Adapter->m_MAC, NAME (l_Adapter));
+
+ DEBUGP (("[%s] Using MAC %x:%x:%x:%x:%x:%x\n",
+ NAME (l_Adapter),
+ l_Adapter->m_MAC[0], l_Adapter->m_MAC[1], l_Adapter->m_MAC[2],
+ l_Adapter->m_MAC[3], l_Adapter->m_MAC[4], l_Adapter->m_MAC[5]));
+
+ //==================
+ // Set broadcast MAC
+ //==================
+ {
+ int i;
+ for (i = 0; i < sizeof (MACADDR); ++i)
+ l_Adapter->m_MAC_Broadcast[i] = 0xFF;
+ }
+
+ //====================================
+ // Initialize TAP device
+ //====================================
+ {
+ NDIS_STATUS tap_status;
+ tap_status = CreateTapDevice (&l_Adapter->m_Extension, NAME (l_Adapter));
+ if (tap_status != NDIS_STATUS_SUCCESS)
+ {
+ AdapterFreeResources (l_Adapter);
+ DEBUGP (("[TAP] CreateTapDevice failed\n"));
+ return tap_status;
+ }
+ }
+
+ if (!AddAdapterToInstanceList (l_Adapter))
+ {
+ NOTE_ERROR ();
+ TapDeviceFreeResources (&l_Adapter->m_Extension);
+ AdapterFreeResources (l_Adapter);
+ DEBUGP (("[TAP] AddAdapterToInstanceList failed\n"));
+ return NDIS_STATUS_RESOURCES;
+ }
+
+ l_Adapter->m_InterfaceIsRunning = TRUE;
+
+#if ENABLE_NONADMIN
+ if (enable_non_admin)
+ AllowNonAdmin (&l_Adapter->m_Extension);
+#endif
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+VOID
+AdapterHalt (IN NDIS_HANDLE p_AdapterContext)
+{
+ BOOLEAN status;
+
+ TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+
+ NOTE_ERROR ();
+
+ l_Adapter->m_InterfaceIsRunning = FALSE;
+
+ DEBUGP (("[%s] is being halted\n", NAME (l_Adapter)));
+
+ DestroyTapDevice (&l_Adapter->m_Extension);
+
+ // Free resources
+ DEBUGP (("[%s] Freeing Resources\n", NAME (l_Adapter)));
+ AdapterFreeResources (l_Adapter);
+
+ status = RemoveAdapterFromInstanceList (l_Adapter);
+ DEBUGP (("[TAP] RemoveAdapterFromInstanceList returned %d\n", (int) status));
+
+ DEBUGP (("[TAP] version [%d.%d] %s %s AdapterHalt returning\n",
+ TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION,
+ __DATE__,
+ __TIME__));
+}
+
+VOID
+AdapterFreeResources (TapAdapterPointer p_Adapter)
+{
+ MYASSERT (!p_Adapter->m_CalledAdapterFreeResources);
+ p_Adapter->m_CalledAdapterFreeResources = TRUE;
+
+ if (p_Adapter->m_NameAnsi.Buffer)
+ RtlFreeAnsiString (&p_Adapter->m_NameAnsi);
+
+ if (p_Adapter->m_RegisteredAdapterShutdownHandler)
+ NdisMDeregisterAdapterShutdownHandler (p_Adapter->m_MiniportAdapterHandle);
+
+ if (p_Adapter->m_MCLockAllocated)
+ NdisFreeSpinLock (&p_Adapter->m_MCLock);
+}
+
+VOID
+DestroyTapDevice (TapExtensionPointer p_Extension)
+{
+ DEBUGP (("[%s] Destroying tap device\n", p_Extension->m_TapName));
+
+ //======================================
+ // Let clients know we are shutting down
+ //======================================
+ p_Extension->m_TapIsRunning = FALSE;
+ p_Extension->m_TapOpens = 0;
+ p_Extension->m_Halt = TRUE;
+
+ //=====================================
+ // If we are concurrently executing in
+ // TapDeviceHook or AdapterTransmit,
+ // give those calls time to finish.
+ // Note that we must be running at IRQL
+ // < DISPATCH_LEVEL in order to call
+ // NdisMSleep.
+ //=====================================
+ NdisMSleep (500000);
+
+ //===========================================================
+ // Exhaust IRP and packet queues. Any pending IRPs will
+ // be cancelled, causing user-space to get this error
+ // on overlapped reads:
+ // The I/O operation has been aborted because of either a
+ // thread exit or an application request. (code=995)
+ // It's important that user-space close the device handle
+ // when this code is returned, so that when we finally
+ // do a NdisMDeregisterDevice, the device reference count
+ // is 0. Otherwise the driver will not unload even if the
+ // the last adapter has been halted.
+ //===========================================================
+ FlushQueues (p_Extension);
+ NdisMSleep (500000); // give user space time to respond to IRP cancel
+
+ TapDeviceFreeResources (p_Extension);
+}
+
+VOID
+TapDeviceFreeResources (TapExtensionPointer p_Extension)
+{
+ MYASSERT (p_Extension);
+ MYASSERT (!p_Extension->m_CalledTapDeviceFreeResources);
+ p_Extension->m_CalledTapDeviceFreeResources = TRUE;
+
+ if (p_Extension->m_PacketQueue)
+ QueueFree (p_Extension->m_PacketQueue);
+ if (p_Extension->m_IrpQueue)
+ QueueFree (p_Extension->m_IrpQueue);
+ if (p_Extension->m_InjectQueue)
+ QueueFree (p_Extension->m_InjectQueue);
+
+ if (p_Extension->m_CreatedUnicodeLinkName)
+ RtlFreeUnicodeString (&p_Extension->m_UnicodeLinkName);
+
+ //==========================================================
+ // According to DDK docs, the device is not actually deleted
+ // until its reference count falls to zero. That means we
+ // still need to gracefully fail TapDeviceHook requests
+ // after this point, otherwise ugly things would happen if
+ // the device was disabled (e.g. in the network connections
+ // control panel) while a userspace app still held an open
+ // file handle to it.
+ //==========================================================
+
+ if (p_Extension->m_TapDevice)
+ {
+ BOOLEAN status;
+ status = (NdisMDeregisterDevice (p_Extension->m_TapDeviceHandle)
+ == NDIS_STATUS_SUCCESS);
+ DEBUGP (("[TAP] Deregistering TAP device, status=%d\n", (int)status));
+ }
+
+ if (p_Extension->m_TapName)
+ MemFree (p_Extension->m_TapName, NAME_BUFFER_SIZE);
+
+ if (p_Extension->m_InjectDpcInitialized)
+ KeRemoveQueueDpc (&p_Extension->m_InjectDpc);
+
+ if (p_Extension->m_AllocatedSpinlocks)
+ {
+ NdisFreeSpinLock (&p_Extension->m_QueueLock);
+ NdisFreeSpinLock (&p_Extension->m_InjectLock);
+ }
+}
+
+//========================================================================
+// Tap Device Initialization
+//========================================================================
+
+NDIS_STATUS
+CreateTapDevice (TapExtensionPointer p_Extension, const char *p_Name)
+{
+# define SIZEOF_DISPATCH (sizeof(PDRIVER_DISPATCH) * (IRP_MJ_MAXIMUM_FUNCTION + 1))
+ PDRIVER_DISPATCH *l_Dispatch = NULL;
+ ANSI_STRING l_TapString, l_LinkString;
+ UNICODE_STRING l_TapUnicode;
+ BOOLEAN l_FreeTapUnicode = FALSE;
+ NTSTATUS l_Status, l_Return = NDIS_STATUS_SUCCESS;
+ const char *l_UsableName;
+
+ DEBUGP (("[TAP] version [%d.%d] creating tap device: %s\n",
+ TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION,
+ p_Name));
+
+ NdisZeroMemory (p_Extension, sizeof (TapExtension));
+
+ INIT_MUTEX (&p_Extension->m_OpenCloseMutex);
+
+ l_LinkString.Buffer = NULL;
+ l_TapString.Buffer = NULL;
+
+ l_TapString.MaximumLength = l_LinkString.MaximumLength = NAME_BUFFER_SIZE;
+
+ //=======================================
+ // Set TAP device entry points
+ //=======================================
+
+ if ((l_Dispatch = MemAlloc (SIZEOF_DISPATCH, TRUE)) == NULL)
+ {
+ DEBUGP (("[%s] couldn't alloc TAP dispatch table\n", p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+
+ l_Dispatch[IRP_MJ_DEVICE_CONTROL] = TapDeviceHook;
+ l_Dispatch[IRP_MJ_READ] = TapDeviceHook;
+ l_Dispatch[IRP_MJ_WRITE] = TapDeviceHook;
+ l_Dispatch[IRP_MJ_CREATE] = TapDeviceHook;
+ l_Dispatch[IRP_MJ_CLOSE] = TapDeviceHook;
+
+ //==================================
+ // Find the beginning of the GUID
+ //==================================
+ l_UsableName = p_Name;
+ while (*l_UsableName != '{')
+ {
+ if (*l_UsableName == '\0')
+ {
+ DEBUGP (("[%s] couldn't find leading '{' in name\n", p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+ ++l_UsableName;
+ }
+
+ //==================================
+ // Allocate pool for TAP device name
+ //==================================
+
+ if ((p_Extension->m_TapName = l_TapString.Buffer =
+ MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
+ {
+ DEBUGP (("[%s] couldn't alloc TAP name buffer\n", p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+
+ //================================================
+ // Allocate pool for TAP symbolic link name buffer
+ //================================================
+
+ if ((l_LinkString.Buffer =
+ MemAlloc (NAME_BUFFER_SIZE, TRUE)) == NULL)
+ {
+ DEBUGP (("[%s] couldn't alloc TAP symbolic link name buffer\n",
+ p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+
+ //=======================================================
+ // Set TAP device name
+ //=======================================================
+
+ l_Status = RtlStringCchPrintfExA
+ (l_TapString.Buffer,
+ l_TapString.MaximumLength,
+ NULL,
+ NULL,
+ STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+ "%s%s%s",
+ SYSDEVICEDIR,
+ l_UsableName,
+ TAPSUFFIX);
+
+ if (l_Status != STATUS_SUCCESS)
+ {
+ DEBUGP (("[%s] couldn't format TAP device name\n",
+ p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+ l_TapString.Length = (USHORT) strlen (l_TapString.Buffer);
+
+ DEBUGP (("TAP DEV NAME: '%s'\n", l_TapString.Buffer));
+
+ //=======================================================
+ // Set TAP link name
+ //=======================================================
+
+ l_Status = RtlStringCchPrintfExA
+ (l_LinkString.Buffer,
+ l_LinkString.MaximumLength,
+ NULL,
+ NULL,
+ STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+ "%s%s%s",
+ USERDEVICEDIR,
+ l_UsableName,
+ TAPSUFFIX);
+
+ if (l_Status != STATUS_SUCCESS)
+ {
+ DEBUGP (("[%s] couldn't format TAP device symbolic link\n",
+ p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+ l_LinkString.Length = (USHORT) strlen (l_LinkString.Buffer);
+
+ DEBUGP (("TAP LINK NAME: '%s'\n", l_LinkString.Buffer));
+
+ //==================================================
+ // Convert strings to unicode
+ //==================================================
+ if (RtlAnsiStringToUnicodeString (&l_TapUnicode, &l_TapString, TRUE) !=
+ STATUS_SUCCESS)
+ {
+ DEBUGP (("[%s] couldn't alloc TAP unicode name buffer\n",
+ p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+ l_FreeTapUnicode = TRUE;
+
+ if (RtlAnsiStringToUnicodeString
+ (&p_Extension->m_UnicodeLinkName, &l_LinkString, TRUE)
+ != STATUS_SUCCESS)
+ {
+ DEBUGP
+ (("[%s] Couldn't allocate unicode string for symbolic link name\n",
+ p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+ p_Extension->m_CreatedUnicodeLinkName = TRUE;
+
+ //==================================================
+ // Create new TAP device with symbolic
+ // link and associate with adapter.
+ //==================================================
+
+ l_Status = NdisMRegisterDevice
+ (g_NdisWrapperHandle,
+ &l_TapUnicode,
+ &p_Extension->m_UnicodeLinkName,
+ l_Dispatch,
+ &p_Extension->m_TapDevice,
+ &p_Extension->m_TapDeviceHandle
+ );
+
+ if (l_Status != STATUS_SUCCESS)
+ {
+ DEBUGP (("[%s] couldn't be created\n", p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+
+ /* Set TAP device flags */
+ p_Extension->m_TapDevice->Flags |= DO_DIRECT_IO;
+
+ //========================================================
+ // Initialize Packet and IRP queues.
+ //
+ // The packet queue is used to buffer data which has been
+ // "transmitted" by the virtual NIC, before user space
+ // has had a chance to read it.
+ //
+ // The IRP queue is used to buffer pending I/O requests
+ // from userspace, i.e. read requests on the TAP device
+ // waiting for the system to "transmit" something through
+ // the virtual NIC.
+ //
+ // Basically, packets in the packet queue are used
+ // to satisfy IRP requests in the IRP queue.
+ //
+ // QueueLock is used to lock the packet queue used
+ // for the TAP-Win32 NIC -> User Space packet flow direction.
+ //
+ // All accesses to packet or IRP queues should be
+ // bracketed by the QueueLock spinlock,
+ // in order to be SMP-safe.
+ //========================================================
+
+ NdisAllocateSpinLock (&p_Extension->m_QueueLock);
+ NdisAllocateSpinLock (&p_Extension->m_InjectLock);
+ p_Extension->m_AllocatedSpinlocks = TRUE;
+
+ p_Extension->m_PacketQueue = QueueInit (PACKET_QUEUE_SIZE);
+ p_Extension->m_IrpQueue = QueueInit (IRP_QUEUE_SIZE);
+ p_Extension->m_InjectQueue = QueueInit (INJECT_QUEUE_SIZE);
+ if (!p_Extension->m_PacketQueue
+ || !p_Extension->m_IrpQueue
+ || !p_Extension->m_InjectQueue)
+ {
+ DEBUGP (("[%s] couldn't alloc TAP queues\n", p_Name));
+ l_Return = NDIS_STATUS_RESOURCES;
+ goto cleanup;
+ }
+
+ //=================================================================
+ // Initialize deferred procedure call for DHCP/ARP packet injection
+ //=================================================================
+
+ KeInitializeDpc (&p_Extension->m_InjectDpc, InjectPacketDpc, NULL);
+ p_Extension->m_InjectDpcInitialized = TRUE;
+
+ //========================
+ // Finalize initialization
+ //========================
+
+ p_Extension->m_TapIsRunning = TRUE;
+
+ DEBUGP (("[%s] successfully created TAP device [%s]\n", p_Name,
+ p_Extension->m_TapName));
+
+ cleanup:
+ if (l_FreeTapUnicode)
+ RtlFreeUnicodeString (&l_TapUnicode);
+ if (l_LinkString.Buffer)
+ MemFree (l_LinkString.Buffer, NAME_BUFFER_SIZE);
+ if (l_Dispatch)
+ MemFree (l_Dispatch, SIZEOF_DISPATCH);
+
+ if (l_Return != NDIS_STATUS_SUCCESS)
+ TapDeviceFreeResources (p_Extension);
+
+ return l_Return;
+}
+#undef SIZEOF_DISPATCH
+
+//========================================================
+// Adapter Control
+//========================================================
+NDIS_STATUS
+AdapterReset (OUT PBOOLEAN p_AddressingReset, IN NDIS_HANDLE p_AdapterContext)
+{
+ TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+ DEBUGP (("[%s] is resetting\n", NAME (l_Adapter)));
+ return NDIS_STATUS_SUCCESS;
+}
+
+NDIS_STATUS AdapterReceive
+ (OUT PNDIS_PACKET p_Packet,
+ OUT PUINT p_Transferred,
+ IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_HANDLE p_ReceiveContext,
+ IN UINT p_Offset,
+ IN UINT p_ToTransfer)
+{
+ return NDIS_STATUS_SUCCESS;
+}
+
+//==============================================================
+// Adapter Option Query/Modification
+//==============================================================
+NDIS_STATUS AdapterQuery
+(IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesWritten, OUT PULONG p_BytesNeeded)
+{
+ TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+ TapAdapterQuery l_Query, *l_QueryPtr = &l_Query;
+ NDIS_STATUS l_Status = NDIS_STATUS_SUCCESS;
+ UINT l_QueryLength = 4;
+ BOOLEAN lock_succeeded;
+
+ NdisZeroMemory (&l_Query, sizeof (l_Query));
+
+ switch (p_OID)
+ {
+ //===================================================================
+ // Vendor & Driver version Info
+ //===================================================================
+ case OID_GEN_VENDOR_DESCRIPTION:
+ l_QueryPtr = (TapAdapterQueryPointer) PRODUCT_STRING;
+ l_QueryLength = strlen (PRODUCT_STRING) + 1;
+ break;
+
+ case OID_GEN_VENDOR_ID:
+ l_Query.m_Long = 0xffffff;
+ break;
+
+ case OID_GEN_DRIVER_VERSION:
+ l_Query.m_Short =
+ (((USHORT) TAP_NDIS_MAJOR_VERSION) << 8 | (USHORT)
+ TAP_NDIS_MINOR_VERSION);
+ l_QueryLength = sizeof (unsigned short);
+ break;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ l_Query.m_Long =
+ (((USHORT) TAP_DRIVER_MAJOR_VERSION) << 8 | (USHORT)
+ TAP_DRIVER_MINOR_VERSION);
+ break;
+
+ //=================================================================
+ // Statistics
+ //=================================================================
+ case OID_GEN_RCV_NO_BUFFER:
+ l_Query.m_Long = 0;
+ break;
+
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ l_Query.m_Long = 0;
+ break;
+
+ case OID_802_3_XMIT_ONE_COLLISION:
+ l_Query.m_Long = 0;
+ break;
+
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ l_Query.m_Long = 0;
+ break;
+
+ case OID_GEN_XMIT_OK:
+ l_Query.m_Long = l_Adapter->m_Tx;
+ break;
+
+ case OID_GEN_RCV_OK:
+ l_Query.m_Long = l_Adapter->m_Rx;
+ break;
+
+ case OID_GEN_XMIT_ERROR:
+ l_Query.m_Long = l_Adapter->m_TxErr;
+ break;
+
+ case OID_GEN_RCV_ERROR:
+ l_Query.m_Long = l_Adapter->m_RxErr;
+ break;
+
+ //===================================================================
+ // Device & Protocol Options
+ //===================================================================
+ case OID_GEN_SUPPORTED_LIST:
+ l_QueryPtr = (TapAdapterQueryPointer) g_SupportedOIDList;
+ l_QueryLength = sizeof (g_SupportedOIDList);
+ break;
+
+ case OID_GEN_MAC_OPTIONS:
+ // This MUST be here !!!
+ l_Query.m_Long = (NDIS_MAC_OPTION_RECEIVE_SERIALIZED
+ | NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA
+ | NDIS_MAC_OPTION_NO_LOOPBACK
+ | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND);
+
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ l_Query.m_Long =
+ (NDIS_PACKET_TYPE_ALL_LOCAL |
+ NDIS_PACKET_TYPE_BROADCAST |
+ NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL);
+
+ break;
+
+ case OID_GEN_PROTOCOL_OPTIONS:
+ l_Query.m_Long = 0;
+ break;
+
+ //==================================================================
+ // Device Info
+ //==================================================================
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+ l_Query.m_Long = l_Adapter->m_MediaState
+ ? NdisMediaStateConnected : NdisMediaStateDisconnected;
+ break;
+
+ case OID_GEN_HARDWARE_STATUS:
+ l_Query.m_HardwareStatus = NdisHardwareStatusReady;
+ l_QueryLength = sizeof (NDIS_HARDWARE_STATUS);
+ break;
+
+ case OID_GEN_MEDIA_SUPPORTED:
+ case OID_GEN_MEDIA_IN_USE:
+ l_Query.m_Medium = l_Adapter->m_Medium;
+ l_QueryLength = sizeof (NDIS_MEDIUM);
+ break;
+
+ case OID_GEN_PHYSICAL_MEDIUM:
+ l_Query.m_PhysicalMedium = NdisPhysicalMediumUnspecified;
+ l_QueryLength = sizeof (NDIS_PHYSICAL_MEDIUM);
+ break;
+
+ case OID_GEN_LINK_SPEED:
+ l_Query.m_Long = 100000; // rate / 100 bps
+ break;
+
+ case OID_802_3_PERMANENT_ADDRESS:
+ case OID_802_3_CURRENT_ADDRESS:
+ COPY_MAC (l_Query.m_MacAddress, l_Adapter->m_MAC);
+ l_QueryLength = sizeof (MACADDR);
+ break;
+
+ //==================================================================
+ // Limits
+ //==================================================================
+
+ case OID_GEN_MAXIMUM_SEND_PACKETS:
+ l_Query.m_Long = 1;
+ break;
+
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ l_Query.m_Long = NIC_MAX_MCAST_LIST;
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ l_Query.m_Long = l_Adapter->m_Lookahead;
+ break;
+
+ case OID_GEN_MAXIMUM_LOOKAHEAD:
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ l_Query.m_Long = DEFAULT_PACKET_LOOKAHEAD;
+ break;
+
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+ l_Query.m_Long = l_Adapter->m_MTU;
+ break;
+
+ case OID_PNP_CAPABILITIES:
+ do
+ {
+ PNDIS_PNP_CAPABILITIES pPNPCapabilities;
+ PNDIS_PM_WAKE_UP_CAPABILITIES pPMstruct;
+
+ if (p_BufferLength >= sizeof (NDIS_PNP_CAPABILITIES))
+ {
+ pPNPCapabilities = (PNDIS_PNP_CAPABILITIES) (p_Buffer);
+
+ //
+ // Setting up the buffer to be returned
+ // to the Protocol above the Passthru miniport
+ //
+ pPMstruct = &pPNPCapabilities->WakeUpCapabilities;
+ pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified;
+ pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+ }
+ l_QueryLength = sizeof (NDIS_PNP_CAPABILITIES);
+ }
+ while (FALSE);
+ break;
+ case OID_PNP_QUERY_POWER:
+ break;
+
+ // Required OIDs that we don't support
+
+ case OID_GEN_SUPPORTED_GUIDS:
+ case OID_GEN_MEDIA_CAPABILITIES:
+ case OID_TCP_TASK_OFFLOAD:
+ case OID_FFP_SUPPORT:
+ l_Status = NDIS_STATUS_INVALID_OID;
+ break;
+
+ // Optional stats OIDs
+
+ case OID_GEN_DIRECTED_BYTES_XMIT:
+ case OID_GEN_DIRECTED_FRAMES_XMIT:
+ case OID_GEN_MULTICAST_BYTES_XMIT:
+ case OID_GEN_MULTICAST_FRAMES_XMIT:
+ case OID_GEN_BROADCAST_BYTES_XMIT:
+ case OID_GEN_BROADCAST_FRAMES_XMIT:
+ case OID_GEN_DIRECTED_BYTES_RCV:
+ case OID_GEN_DIRECTED_FRAMES_RCV:
+ case OID_GEN_MULTICAST_BYTES_RCV:
+ case OID_GEN_MULTICAST_FRAMES_RCV:
+ case OID_GEN_BROADCAST_BYTES_RCV:
+ case OID_GEN_BROADCAST_FRAMES_RCV:
+ l_Status = NDIS_STATUS_INVALID_OID;
+ break;
+
+ //===================================================================
+ // Not Handled
+ //===================================================================
+ default:
+ DEBUGP (("[%s] Unhandled OID %lx\n", NAME (l_Adapter), p_OID));
+ l_Status = NDIS_STATUS_INVALID_OID;
+ break;
+ }
+
+ if (l_Status != NDIS_STATUS_SUCCESS)
+ ;
+ else if (l_QueryLength > p_BufferLength)
+ {
+ l_Status = NDIS_STATUS_INVALID_LENGTH;
+ *p_BytesNeeded = l_QueryLength;
+ }
+ else
+ NdisMoveMemory (p_Buffer, (PVOID) l_QueryPtr,
+ (*p_BytesWritten = l_QueryLength));
+
+ return l_Status;
+}
+
+NDIS_STATUS AdapterModify
+(IN NDIS_HANDLE p_AdapterContext,
+ IN NDIS_OID p_OID,
+ IN PVOID p_Buffer,
+ IN ULONG p_BufferLength,
+ OUT PULONG p_BytesRead,
+ OUT PULONG p_BytesNeeded)
+{
+ TapAdapterQueryPointer l_Query = (TapAdapterQueryPointer) p_Buffer;
+ TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+ NDIS_STATUS l_Status = NDIS_STATUS_INVALID_OID;
+ ULONG l_Long;
+
+ switch (p_OID)
+ {
+ //==================================================================
+ // Device Info
+ //==================================================================
+ case OID_802_3_MULTICAST_LIST:
+ DEBUGP (("[%s] Setting [OID_802_3_MULTICAST_LIST]\n",
+ NAME (l_Adapter)));
+
+ *p_BytesNeeded = sizeof (ETH_ADDR);
+ *p_BytesRead = p_BufferLength;
+
+ if (p_BufferLength % sizeof (ETH_ADDR))
+ l_Status = NDIS_STATUS_INVALID_LENGTH;
+ else if (p_BufferLength > sizeof (MC_LIST))
+ {
+ l_Status = NDIS_STATUS_MULTICAST_FULL;
+ *p_BytesNeeded = sizeof (MC_LIST);
+ }
+ else
+ {
+ NdisAcquireSpinLock (&l_Adapter->m_MCLock);
+
+ NdisZeroMemory(&l_Adapter->m_MCList, sizeof (MC_LIST));
+
+ NdisMoveMemory(&l_Adapter->m_MCList,
+ p_Buffer,
+ p_BufferLength);
+
+ l_Adapter->m_MCListSize = p_BufferLength / sizeof (ETH_ADDR);
+
+ NdisReleaseSpinLock (&l_Adapter->m_MCLock);
+
+ l_Status = NDIS_STATUS_SUCCESS;
+ }
+ break;
+
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ l_Status = NDIS_STATUS_INVALID_LENGTH;
+ *p_BytesNeeded = 4;
+
+ if (p_BufferLength >= sizeof (ULONG))
+ {
+ DEBUGP
+ (("[%s] Setting [OID_GEN_CURRENT_PACKET_FILTER] to [0x%02lx]\n",
+ NAME (l_Adapter), l_Query->m_Long));
+ l_Status = NDIS_STATUS_SUCCESS;
+ *p_BytesRead = sizeof (ULONG);
+ }
+ break;
+
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ if (p_BufferLength < sizeof (ULONG))
+ {
+ l_Status = NDIS_STATUS_INVALID_LENGTH;
+ *p_BytesNeeded = 4;
+ }
+ else if (l_Query->m_Long > DEFAULT_PACKET_LOOKAHEAD
+ || l_Query->m_Long <= 0)
+ {
+ l_Status = NDIS_STATUS_INVALID_DATA;
+ }
+ else
+ {
+ DEBUGP (("[%s] Setting [OID_GEN_CURRENT_LOOKAHEAD] to [%d]\n",
+ NAME (l_Adapter), l_Query->m_Long));
+ l_Adapter->m_Lookahead = l_Query->m_Long;
+ l_Status = NDIS_STATUS_SUCCESS;
+ *p_BytesRead = sizeof (ULONG);
+ }
+ break;
+
+ case OID_GEN_NETWORK_LAYER_ADDRESSES:
+ l_Status = NDIS_STATUS_SUCCESS;
+ *p_BytesRead = *p_BytesNeeded = 0;
+ break;
+
+ case OID_GEN_TRANSPORT_HEADER_OFFSET:
+ l_Status = NDIS_STATUS_SUCCESS;
+ *p_BytesRead = *p_BytesNeeded = 0;
+ break;
+
+ case OID_PNP_SET_POWER:
+ do
+ {
+ NDIS_DEVICE_POWER_STATE NewDeviceState;
+
+ NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE) p_Buffer);
+
+ switch (NewDeviceState)
+ {
+ case NdisDeviceStateD0:
+ l_Adapter->m_DeviceState = '0';
+ break;
+ case NdisDeviceStateD1:
+ l_Adapter->m_DeviceState = '1';
+ break;
+ case NdisDeviceStateD2:
+ l_Adapter->m_DeviceState = '2';
+ break;
+ case NdisDeviceStateD3:
+ l_Adapter->m_DeviceState = '3';
+ break;
+ default:
+ l_Adapter->m_DeviceState = '?';
+ break;
+ }
+
+ l_Status = NDIS_STATUS_FAILURE;
+
+ //
+ // Check for invalid length
+ //
+ if (p_BufferLength < sizeof (NDIS_DEVICE_POWER_STATE))
+ {
+ l_Status = NDIS_STATUS_INVALID_LENGTH;
+ break;
+ }
+
+ if (NewDeviceState > NdisDeviceStateD0)
+ {
+ l_Adapter->m_InterfaceIsRunning = FALSE;
+ DEBUGP (("[%s] Power management device state OFF\n",
+ NAME (l_Adapter)));
+ }
+ else
+ {
+ l_Adapter->m_InterfaceIsRunning = TRUE;
+ DEBUGP (("[%s] Power management device state ON\n",
+ NAME (l_Adapter)));
+ }
+
+ l_Status = NDIS_STATUS_SUCCESS;
+ }
+ while (FALSE);
+
+ if (l_Status == NDIS_STATUS_SUCCESS)
+ {
+ *p_BytesRead = sizeof (NDIS_DEVICE_POWER_STATE);
+ *p_BytesNeeded = 0;
+ }
+ else
+ {
+ *p_BytesRead = 0;
+ *p_BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+ }
+ break;
+
+ case OID_PNP_REMOVE_WAKE_UP_PATTERN:
+ case OID_PNP_ADD_WAKE_UP_PATTERN:
+ l_Status = NDIS_STATUS_SUCCESS;
+ *p_BytesRead = *p_BytesNeeded = 0;
+ break;
+
+ default:
+ DEBUGP (("[%s] Can't set value for OID %lx\n", NAME (l_Adapter),
+ p_OID));
+ l_Status = NDIS_STATUS_INVALID_OID;
+ *p_BytesRead = *p_BytesNeeded = 0;
+ break;
+ }
+
+ return l_Status;
+}
+
+// checksum code for ICMPv6 packet, taken from dhcp.c / udp_checksum
+// see RFC 4443, 2.3, and RFC 2460, 8.1
+USHORT
+icmpv6_checksum (const UCHAR *buf,
+ const int len_icmpv6,
+ const UCHAR *saddr6,
+ const UCHAR *daddr6)
+{
+ USHORT word16;
+ ULONG sum = 0;
+ int i;
+
+ // make 16 bit words out of every two adjacent 8 bit words and
+ // calculate the sum of all 16 bit words
+ for (i = 0; i < len_icmpv6; i += 2){
+ word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_icmpv6) ? (buf[i+1] & 0xFF) : 0);
+ sum += word16;
+ }
+
+ // add the IPv6 pseudo header which contains the IP source and destination addresses
+ for (i = 0; i < 16; i += 2){
+ word16 =((saddr6[i] << 8) & 0xFF00) + (saddr6[i+1] & 0xFF);
+ sum += word16;
+ }
+ for (i = 0; i < 16; i += 2){
+ word16 =((daddr6[i] << 8) & 0xFF00) + (daddr6[i+1] & 0xFF);
+ sum += word16;
+ }
+
+ // the next-header number and the length of the ICMPv6 packet
+ sum += (USHORT) IPPROTO_ICMPV6 + (USHORT) len_icmpv6;
+
+ // keep only the last 16 bits of the 32 bit calculated sum and add the carries
+ while (sum >> 16)
+ sum = (sum & 0xFFFF) + (sum >> 16);
+
+ // Take the one's complement of sum
+ return ((USHORT) ~sum);
+}
+
+// check IPv6 packet for "is this an IPv6 Neighbor Solicitation that
+// the tap driver needs to answer?"
+// see RFC 4861 4.3 for the different cases
+static IPV6ADDR IPV6_NS_TARGET_MCAST =
+ { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x08 };
+static IPV6ADDR IPV6_NS_TARGET_UNICAST =
+ { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 };
+
+BOOLEAN
+HandleIPv6NeighborDiscovery( TapAdapterPointer p_Adapter, UCHAR * m_Data )
+{
+ const ETH_HEADER * e = (ETH_HEADER *) m_Data;
+ const IPV6HDR *ipv6 = (IPV6HDR *) (m_Data + sizeof (ETH_HEADER));
+ const ICMPV6_NS * icmpv6_ns = (ICMPV6_NS *) (m_Data + sizeof (ETH_HEADER) + sizeof (IPV6HDR));
+ ICMPV6_NA_PKT *na;
+ USHORT icmpv6_len, icmpv6_csum;
+
+ // we don't really care about the destination MAC address here
+ // - it's either a multicast MAC, or the userland destination MAC
+ // but since the TAP driver is point-to-point, all packets are "for us"
+
+ // IPv6 target address must be ff02::1::ff00:8 (multicast for
+ // initial NS) or fe80::1 (unicast for recurrent NUD)
+ if ( memcmp( ipv6->daddr, IPV6_NS_TARGET_MCAST,
+ sizeof(IPV6ADDR) ) != 0 &&
+ memcmp( ipv6->daddr, IPV6_NS_TARGET_UNICAST,
+ sizeof(IPV6ADDR) ) != 0 )
+ {
+ return FALSE; // wrong target address
+ }
+
+ // IPv6 Next-Header must be ICMPv6
+ if ( ipv6->nexthdr != IPPROTO_ICMPV6 )
+ {
+ return FALSE; // wrong next-header
+ }
+
+ // ICMPv6 type+code must be 135/0 for NS
+ if ( icmpv6_ns->type != ICMPV6_TYPE_NS ||
+ icmpv6_ns->code != ICMPV6_CODE_0 )
+ {
+ return FALSE; // wrong ICMPv6 type
+ }
+
+ // ICMPv6 target address must be fe80::8 (magic)
+ if ( memcmp( icmpv6_ns->target_addr, IPV6_NS_TARGET_UNICAST,
+ sizeof(IPV6ADDR) ) != 0 )
+ {
+ return FALSE; // not for us
+ }
+
+ // packet identified, build magic response packet
+
+ na = (ICMPV6_NA_PKT *) MemAlloc (sizeof (ICMPV6_NA_PKT), TRUE);
+ if ( !na ) return FALSE;
+
+ //------------------------------------------------
+ // Initialize Neighbour Advertisement reply packet
+ //------------------------------------------------
+
+ // ethernet header
+ na->eth.proto = htons(ETH_P_IPV6);
+ COPY_MAC(na->eth.dest, p_Adapter->m_MAC);
+ COPY_MAC(na->eth.src, p_Adapter->m_TapToUser.dest);
+
+ // IPv6 header
+ na->ipv6.version_prio = ipv6->version_prio;
+ NdisMoveMemory( na->ipv6.flow_lbl, ipv6->flow_lbl,
+ sizeof(na->ipv6.flow_lbl) );
+ icmpv6_len = sizeof(ICMPV6_NA_PKT) - sizeof(ETH_HEADER) - sizeof(IPV6HDR);
+ na->ipv6.payload_len = htons(icmpv6_len);
+ na->ipv6.nexthdr = IPPROTO_ICMPV6;
+ na->ipv6.hop_limit = 255;
+ NdisMoveMemory( na->ipv6.saddr, IPV6_NS_TARGET_UNICAST,
+ sizeof(IPV6ADDR) );
+ NdisMoveMemory( na->ipv6.daddr, ipv6->saddr,
+ sizeof(IPV6ADDR) );
+
+ // ICMPv6
+ na->icmpv6.type = ICMPV6_TYPE_NA;
+ na->icmpv6.code = ICMPV6_CODE_0;
+ na->icmpv6.checksum = 0;
+ na->icmpv6.rso_bits = 0x60; // Solicited + Override
+ NdisZeroMemory( na->icmpv6.reserved, sizeof(na->icmpv6.reserved) );
+ NdisMoveMemory( na->icmpv6.target_addr, IPV6_NS_TARGET_UNICAST,
+ sizeof(IPV6ADDR) );
+
+ // ICMPv6 option "Target Link Layer Address"
+ na->icmpv6.opt_type = ICMPV6_OPTION_TLLA;
+ na->icmpv6.opt_length = ICMPV6_LENGTH_TLLA;
+ COPY_MAC( na->icmpv6.target_macaddr, p_Adapter->m_TapToUser.dest );
+
+ // calculate and set checksum
+ icmpv6_csum = icmpv6_checksum ( (UCHAR*) &(na->icmpv6),
+ icmpv6_len,
+ na->ipv6.saddr,
+ na->ipv6.daddr );
+ na->icmpv6.checksum = htons( icmpv6_csum );
+
+ DUMP_PACKET ("HandleIPv6NeighborDiscovery",
+ (unsigned char *) na,
+ sizeof (ICMPV6_NA_PKT));
+
+ InjectPacketDeferred (p_Adapter, (UCHAR *) na, sizeof (ICMPV6_NA_PKT));
+
+ MemFree (na, sizeof (ICMPV6_NA_PKT));
+
+ return TRUE; // all fine
+}
+
+//====================================================================
+// Adapter Transmission
+//====================================================================
+NDIS_STATUS
+AdapterTransmit (IN NDIS_HANDLE p_AdapterContext,
+ IN PNDIS_PACKET p_Packet,
+ IN UINT p_Flags)
+{
+ TapAdapterPointer l_Adapter = (TapAdapterPointer) p_AdapterContext;
+ ULONG l_Index = 0, l_PacketLength = 0;
+ UINT l_BufferLength = 0;
+ PIRP l_IRP;
+ TapPacketPointer l_PacketBuffer;
+ PNDIS_BUFFER l_NDIS_Buffer;
+ PUCHAR l_Buffer;
+ PVOID result;
+
+ NdisQueryPacket (p_Packet, NULL, NULL, &l_NDIS_Buffer, &l_PacketLength);
+
+ //====================================================
+ // Here we abandon the transmission attempt if any of
+ // the parameters is wrong or memory allocation fails
+ // but we do not indicate failure. The packet is
+ // silently dropped.
+ //====================================================
+
+ if (l_PacketLength < ETHERNET_HEADER_SIZE || l_PacketLength > 65535)
+ goto exit_fail;
+ else if (!l_Adapter->m_Extension.m_TapOpens || !l_Adapter->m_MediaState)
+ goto exit_success; // Nothing is bound to the TAP device
+
+ if (NdisAllocateMemoryWithTag (&l_PacketBuffer,
+ TAP_PACKET_SIZE (l_PacketLength),
+ '5PAT') != NDIS_STATUS_SUCCESS)
+ goto exit_no_resources;
+
+ if (l_PacketBuffer == NULL)
+ goto exit_no_resources;
+
+ l_PacketBuffer->m_SizeFlags = (l_PacketLength & TP_SIZE_MASK);
+
+ //===========================
+ // Reassemble packet contents
+ //===========================
+
+ __try
+ {
+ l_Index = 0;
+ while (l_NDIS_Buffer && l_Index < l_PacketLength)
+ {
+ ULONG newlen;
+ NdisQueryBuffer (l_NDIS_Buffer, (PVOID *) & l_Buffer,
+ &l_BufferLength);
+ newlen = l_Index + l_BufferLength;
+ if (newlen > l_PacketLength)
+ {
+ NOTE_ERROR ();
+ goto no_queue; /* overflow */
+ }
+ NdisMoveMemory (l_PacketBuffer->m_Data + l_Index, l_Buffer,
+ l_BufferLength);
+ l_Index = newlen;
+ NdisGetNextBuffer (l_NDIS_Buffer, &l_NDIS_Buffer);
+ }
+ if (l_Index != l_PacketLength)
+ {
+ NOTE_ERROR ();
+ goto no_queue; /* underflow */
+ }
+
+ DUMP_PACKET ("AdapterTransmit", l_PacketBuffer->m_Data, l_PacketLength);
+
+ //=====================================================
+ // If IPv4 packet, check whether or not packet
+ // was truncated.
+ //=====================================================
+#if PACKET_TRUNCATION_CHECK
+ IPv4PacketSizeVerify (l_PacketBuffer->m_Data, l_PacketLength, FALSE, "TX", &l_Adapter->m_TxTrunc);
+#endif
+
+ //=====================================================
+ // Are we running in DHCP server masquerade mode?
+ //
+ // If so, catch both DHCP requests and ARP queries
+ // to resolve the address of our virtual DHCP server.
+ //=====================================================
+ if (l_Adapter->m_dhcp_enabled)
+ {
+ const ETH_HEADER *eth = (ETH_HEADER *) l_PacketBuffer->m_Data;
+ const IPHDR *ip = (IPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER));
+ const UDPHDR *udp = (UDPHDR *) (l_PacketBuffer->m_Data + sizeof (ETH_HEADER) + sizeof (IPHDR));
+
+ // ARP packet?
+ if (l_PacketLength == sizeof (ARP_PACKET)
+ && eth->proto == htons (ETH_P_ARP)
+ && l_Adapter->m_dhcp_server_arp)
+ {
+ if (ProcessARP (l_Adapter,
+ (PARP_PACKET) l_PacketBuffer->m_Data,
+ l_Adapter->m_dhcp_addr,
+ l_Adapter->m_dhcp_server_ip,
+ ~0,
+ l_Adapter->m_dhcp_server_mac))
+ goto no_queue;
+ }
+
+ // DHCP packet?
+ else if (l_PacketLength >= sizeof (ETH_HEADER) + sizeof (IPHDR) + sizeof (UDPHDR) + sizeof (DHCP)
+ && eth->proto == htons (ETH_P_IP)
+ && ip->version_len == 0x45 // IPv4, 20 byte header
+ && ip->protocol == IPPROTO_UDP
+ && udp->dest == htons (BOOTPS_PORT))
+ {
+ const DHCP *dhcp = (DHCP *) (l_PacketBuffer->m_Data
+ + sizeof (ETH_HEADER)
+ + sizeof (IPHDR)
+ + sizeof (UDPHDR));
+
+ const int optlen = l_PacketLength
+ - sizeof (ETH_HEADER)
+ - sizeof (IPHDR)
+ - sizeof (UDPHDR)
+ - sizeof (DHCP);
+
+ if (optlen > 0) // we must have at least one DHCP option
+ {
+ if (ProcessDHCP (l_Adapter, eth, ip, udp, dhcp, optlen))
+ goto no_queue;
+ }
+ else
+ goto no_queue;
+ }
+ }
+
+ //===============================================
+ // In Point-To-Point mode, check to see whether
+ // packet is ARP (handled) or IPv4 (sent to app).
+ // IPv6 packets are inspected for neighbour discovery
+ // (to be handled locally), and the rest is forwarded
+ // all other protocols are dropped
+ //===============================================
+ if (l_Adapter->m_tun)
+ {
+ ETH_HEADER *e;
+
+ if (l_PacketLength < ETHERNET_HEADER_SIZE)
+ goto no_queue;
+
+ e = (ETH_HEADER *) l_PacketBuffer->m_Data;
+
+ switch (ntohs (e->proto))
+ {
+ case ETH_P_ARP:
+
+ // Make sure that packet is the
+ // right size for ARP.
+ if (l_PacketLength != sizeof (ARP_PACKET))
+ goto no_queue;
+
+ ProcessARP (l_Adapter,
+ (PARP_PACKET) l_PacketBuffer->m_Data,
+ l_Adapter->m_localIP,
+ l_Adapter->m_remoteNetwork,
+ l_Adapter->m_remoteNetmask,
+ l_Adapter->m_TapToUser.dest);
+
+ default:
+ goto no_queue;
+
+ case ETH_P_IP:
+
+ // Make sure that packet is large
+ // enough to be IPv4.
+ if (l_PacketLength
+ < ETHERNET_HEADER_SIZE + IP_HEADER_SIZE)
+ goto no_queue;
+
+ // Only accept directed packets,
+ // not broadcasts.
+ if (memcmp (e, &l_Adapter->m_TapToUser, ETHERNET_HEADER_SIZE))
+ goto no_queue;
+
+ // Packet looks like IPv4, queue it.
+ l_PacketBuffer->m_SizeFlags |= TP_TUN;
+
+ case ETH_P_IPV6:
+ // make sure that packet is large
+ // enough to be IPv6
+ if (l_PacketLength
+ < ETHERNET_HEADER_SIZE + IPV6_HEADER_SIZE)
+ goto no_queue;
+
+ // broadcasts and multicasts are handled specially
+ // (to be implemented)
+
+ // neighbor discovery packets to fe80::8 are special
+ // OpenVPN sets this next-hop to signal "handled by tapdrv"
+ if ( HandleIPv6NeighborDiscovery( l_Adapter,
+ l_PacketBuffer->m_Data ))
+ {
+ goto no_queue;
+ }
+
+ // Packet looks like IPv6, queue it :-)
+ l_PacketBuffer->m_SizeFlags |= TP_TUN;
+ }
+ }
+
+ //===============================================
+ // Push packet onto queue to wait for read from
+ // userspace.
+ //===============================================
+
+ NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ result = NULL;
+ if (IS_UP (l_Adapter))
+ result = QueuePush (l_Adapter->m_Extension.m_PacketQueue, l_PacketBuffer);
+
+ NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if ((TapPacketPointer) result != l_PacketBuffer)
+ {
+ // adapter receive overrun
+ INCREMENT_STAT (l_Adapter->m_TxErr);
+ goto no_queue;
+ }
+ else
+ {
+ INCREMENT_STAT (l_Adapter->m_Tx);
+ }
+
+ //============================================================
+ // Cycle through IRPs and packets, try to satisfy each pending
+ // IRP with a queued packet.
+ //============================================================
+ while (TRUE)
+ {
+ l_IRP = NULL;
+ l_PacketBuffer = NULL;
+
+ NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if (IS_UP (l_Adapter)
+ && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
+ && QueueCount (l_Adapter->m_Extension.m_IrpQueue))
+ {
+ l_IRP = (PIRP) QueuePop (l_Adapter->m_Extension.m_IrpQueue);
+ l_PacketBuffer = (TapPacketPointer)
+ QueuePop (l_Adapter->m_Extension.m_PacketQueue);
+ }
+
+ NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ MYASSERT ((l_IRP != NULL) + (l_PacketBuffer != NULL) != 1);
+
+ if (l_IRP && l_PacketBuffer)
+ {
+ CompleteIRP (l_IRP,
+ l_PacketBuffer,
+ IO_NETWORK_INCREMENT);
+ }
+ else
+ break;
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+
+ return NDIS_STATUS_SUCCESS;
+
+ no_queue:
+ NdisFreeMemory (l_PacketBuffer,
+ TAP_PACKET_SIZE (l_PacketLength),
+ 0);
+
+ exit_success:
+ return NDIS_STATUS_SUCCESS;
+
+ exit_fail:
+ return NDIS_STATUS_FAILURE;
+
+ exit_no_resources:
+ return NDIS_STATUS_RESOURCES;
+}
+
+//======================================================================
+// Hooks for catching TAP device IRP's.
+//======================================================================
+
+NTSTATUS
+TapDeviceHook (IN PDEVICE_OBJECT p_DeviceObject, IN PIRP p_IRP)
+{
+ TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
+ PIO_STACK_LOCATION l_IrpSp;
+ NTSTATUS l_Status = STATUS_SUCCESS;
+ BOOLEAN accessible;
+
+ l_IrpSp = IoGetCurrentIrpStackLocation (p_IRP);
+
+ p_IRP->IoStatus.Status = STATUS_SUCCESS;
+ p_IRP->IoStatus.Information = 0;
+
+ if (!l_Adapter || l_Adapter->m_Extension.m_Halt)
+ {
+ DEBUGP (("TapDeviceHook called when TAP device is halted, MajorFunction=%d\n",
+ (int)l_IrpSp->MajorFunction));
+
+ if (l_IrpSp->MajorFunction == IRP_MJ_CLOSE)
+ {
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ p_IRP->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ return STATUS_NO_SUCH_DEVICE;
+ }
+ }
+
+ switch (l_IrpSp->MajorFunction)
+ {
+ //===========================================================
+ // Ioctl call handlers
+ //===========================================================
+ case IRP_MJ_DEVICE_CONTROL:
+ {
+ switch (l_IrpSp->Parameters.DeviceIoControl.IoControlCode)
+ {
+ case TAP_IOCTL_GET_MAC:
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+ >= sizeof (MACADDR))
+ {
+ COPY_MAC (p_IRP->AssociatedIrp.SystemBuffer,
+ l_Adapter->m_MAC);
+ p_IRP->IoStatus.Information = sizeof (MACADDR);
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+ case TAP_IOCTL_GET_VERSION:
+ {
+ const ULONG size = sizeof (ULONG) * 3;
+ if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+ >= size)
+ {
+ ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
+ = TAP_DRIVER_MAJOR_VERSION;
+ ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[1]
+ = TAP_DRIVER_MINOR_VERSION;
+ ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[2]
+#if DBG
+ = 1;
+#else
+ = 0;
+#endif
+ p_IRP->IoStatus.Information = size;
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+ case TAP_IOCTL_GET_MTU:
+ {
+ const ULONG size = sizeof (ULONG) * 1;
+ if (l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength
+ >= size)
+ {
+ ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0]
+ = l_Adapter->m_MTU;
+ p_IRP->IoStatus.Information = size;
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ break;
+ }
+ case TAP_IOCTL_GET_INFO:
+ {
+ char state[16];
+ if (l_Adapter->m_InterfaceIsRunning)
+ state[0] = 'A';
+ else
+ state[0] = 'a';
+ if (l_Adapter->m_Extension.m_TapIsRunning)
+ state[1] = 'T';
+ else
+ state[1] = 't';
+ state[2] = l_Adapter->m_DeviceState;
+ if (l_Adapter->m_MediaStateAlwaysConnected)
+ state[3] = 'C';
+ else
+ state[3] = 'c';
+ state[4] = '\0';
+
+ p_IRP->IoStatus.Status = l_Status = RtlStringCchPrintfExA (
+ ((LPTSTR) (p_IRP->AssociatedIrp.SystemBuffer)),
+ l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
+ NULL,
+ NULL,
+ STRSAFE_FILL_BEHIND_NULL | STRSAFE_IGNORE_NULLS,
+#if PACKET_TRUNCATION_CHECK
+ "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d,%d] Rx=[%d,%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%d,%d,%d]",
+#else
+ "State=%s Err=[%s/%d] #O=%d Tx=[%d,%d] Rx=[%d,%d] IrpQ=[%d,%d,%d] PktQ=[%d,%d,%d] InjQ=[%d,%d,%d]",
+#endif
+ state,
+ g_LastErrorFilename,
+ g_LastErrorLineNumber,
+ (int)l_Adapter->m_Extension.m_NumTapOpens,
+ (int)l_Adapter->m_Tx,
+ (int)l_Adapter->m_TxErr,
+#if PACKET_TRUNCATION_CHECK
+ (int)l_Adapter->m_TxTrunc,
+#endif
+ (int)l_Adapter->m_Rx,
+ (int)l_Adapter->m_RxErr,
+#if PACKET_TRUNCATION_CHECK
+ (int)l_Adapter->m_RxTrunc,
+#endif
+ (int)l_Adapter->m_Extension.m_IrpQueue->size,
+ (int)l_Adapter->m_Extension.m_IrpQueue->max_size,
+ (int)IRP_QUEUE_SIZE,
+ (int)l_Adapter->m_Extension.m_PacketQueue->size,
+ (int)l_Adapter->m_Extension.m_PacketQueue->max_size,
+ (int)PACKET_QUEUE_SIZE,
+ (int)l_Adapter->m_Extension.m_InjectQueue->size,
+ (int)l_Adapter->m_Extension.m_InjectQueue->max_size,
+ (int)INJECT_QUEUE_SIZE
+ );
+
+ p_IRP->IoStatus.Information
+ = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ break;
+ }
+
+#if DBG
+ case TAP_IOCTL_GET_LOG_LINE:
+ {
+ if (GetDebugLine ((LPTSTR)p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength))
+ p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+ else
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+
+ p_IRP->IoStatus.Information
+ = l_IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ break;
+ }
+#endif
+
+ case TAP_IOCTL_CONFIG_TUN:
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ (sizeof (IPADDR) * 3))
+ {
+ MACADDR dest;
+
+ l_Adapter->m_tun = FALSE;
+
+ GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);
+
+ l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+ l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+ l_Adapter->m_remoteNetmask = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];
+
+ // sanity check on network/netmask
+ if ((l_Adapter->m_remoteNetwork & l_Adapter->m_remoteNetmask) != l_Adapter->m_remoteNetwork)
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
+ COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
+ COPY_MAC (l_Adapter->m_UserToTap.src, dest);
+ COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
+
+ l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
+ l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
+ l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
+
+ l_Adapter->m_tun = TRUE;
+
+ CheckIfDhcpAndTunMode (l_Adapter);
+
+ p_IRP->IoStatus.Information = 1; // Simple boolean value
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+
+ case TAP_IOCTL_CONFIG_POINT_TO_POINT: // Obsoleted by TAP_IOCTL_CONFIG_TUN
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ (sizeof (IPADDR) * 2))
+ {
+ MACADDR dest;
+
+ l_Adapter->m_tun = FALSE;
+
+ GenerateRelatedMAC (dest, l_Adapter->m_MAC, 1);
+
+ l_Adapter->m_localIP = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+ l_Adapter->m_remoteNetwork = ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+ l_Adapter->m_remoteNetmask = ~0;
+
+ COPY_MAC (l_Adapter->m_TapToUser.src, l_Adapter->m_MAC);
+ COPY_MAC (l_Adapter->m_TapToUser.dest, dest);
+ COPY_MAC (l_Adapter->m_UserToTap.src, dest);
+ COPY_MAC (l_Adapter->m_UserToTap.dest, l_Adapter->m_MAC);
+
+ l_Adapter->m_TapToUser.proto = l_Adapter->m_UserToTap.proto = htons (ETH_P_IP);
+ l_Adapter->m_UserToTap_IPv6 = l_Adapter->m_UserToTap;
+ l_Adapter->m_UserToTap_IPv6.proto = htons(ETH_P_IPV6);
+
+ l_Adapter->m_tun = TRUE;
+
+ CheckIfDhcpAndTunMode (l_Adapter);
+
+ p_IRP->IoStatus.Information = 1; // Simple boolean value
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+
+ case TAP_IOCTL_SET_MEDIA_STATUS:
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ (sizeof (ULONG) * 1))
+ {
+ ULONG parm = ((PULONG) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+ SetMediaStatus (l_Adapter, (BOOLEAN) parm);
+ p_IRP->IoStatus.Information = 1;
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+ }
+
+ case TAP_IOCTL_CONFIG_DHCP_MASQ:
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
+ (sizeof (IPADDR) * 4))
+ {
+ l_Adapter->m_dhcp_enabled = FALSE;
+ l_Adapter->m_dhcp_server_arp = FALSE;
+ l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+
+ // Adapter IP addr / netmask
+ l_Adapter->m_dhcp_addr =
+ ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[0];
+ l_Adapter->m_dhcp_netmask =
+ ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[1];
+
+ // IP addr of DHCP masq server
+ l_Adapter->m_dhcp_server_ip =
+ ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[2];
+
+ // Lease time in seconds
+ l_Adapter->m_dhcp_lease_time =
+ ((IPADDR*) (p_IRP->AssociatedIrp.SystemBuffer))[3];
+
+ GenerateRelatedMAC (l_Adapter->m_dhcp_server_mac, l_Adapter->m_MAC, 2);
+
+ l_Adapter->m_dhcp_enabled = TRUE;
+ l_Adapter->m_dhcp_server_arp = TRUE;
+
+ CheckIfDhcpAndTunMode (l_Adapter);
+
+ p_IRP->IoStatus.Information = 1; // Simple boolean value
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+
+ case TAP_IOCTL_CONFIG_DHCP_SET_OPT:
+ {
+ if (l_IrpSp->Parameters.DeviceIoControl.InputBufferLength <=
+ DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE
+ && l_Adapter->m_dhcp_enabled)
+ {
+ l_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+
+ NdisMoveMemory (l_Adapter->m_dhcp_user_supplied_options_buffer,
+ p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.DeviceIoControl.InputBufferLength);
+
+ l_Adapter->m_dhcp_user_supplied_options_buffer_len =
+ l_IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ p_IRP->IoStatus.Information = 1; // Simple boolean value
+ }
+ else
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //===========================================================
+ // User mode thread issued a read request on the tap device
+ // If there are packets waiting to be read, then the request
+ // will be satisfied here. If not, then the request will be
+ // queued and satisfied by any packet that is not used to
+ // satisfy requests ahead of it.
+ //===========================================================
+ case IRP_MJ_READ:
+ {
+ TapPacketPointer l_PacketBuffer;
+ BOOLEAN pending = FALSE;
+
+ // Save IRP-accessible copy of buffer length
+ p_IRP->IoStatus.Information = l_IrpSp->Parameters.Read.Length;
+
+ if (p_IRP->MdlAddress == NULL)
+ {
+ DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_READ\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ p_IRP->IoStatus.Information = 0;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+ else if ((p_IRP->AssociatedIrp.SystemBuffer =
+ MmGetSystemAddressForMdlSafe
+ (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
+ {
+ DEBUGP (("[%s] Could not map address in IRP_MJ_READ\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
+ p_IRP->IoStatus.Information = 0;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+ else if (!l_Adapter->m_InterfaceIsRunning)
+ {
+ DEBUGP (("[%s] Interface is down in IRP_MJ_READ\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //==================================
+ // Can we provide immediate service?
+ //==================================
+
+ l_PacketBuffer = NULL;
+
+ NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if (IS_UP (l_Adapter)
+ && QueueCount (l_Adapter->m_Extension.m_PacketQueue)
+ && QueueCount (l_Adapter->m_Extension.m_IrpQueue) == 0)
+ {
+ l_PacketBuffer = (TapPacketPointer)
+ QueuePop (l_Adapter->m_Extension.m_PacketQueue);
+ }
+
+ NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if (l_PacketBuffer)
+ {
+ l_Status = CompleteIRP (p_IRP,
+ l_PacketBuffer,
+ IO_NO_INCREMENT);
+ break;
+ }
+
+ //=============================
+ // Attempt to pend read request
+ //=============================
+
+ NdisAcquireSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if (IS_UP (l_Adapter)
+ && QueuePush (l_Adapter->m_Extension.m_IrpQueue, p_IRP) == (PIRP) p_IRP)
+ {
+ IoSetCancelRoutine (p_IRP, CancelIRPCallback);
+ l_Status = STATUS_PENDING;
+ IoMarkIrpPending (p_IRP);
+ pending = TRUE;
+ }
+
+ NdisReleaseSpinLock (&l_Adapter->m_Extension.m_QueueLock);
+
+ if (pending)
+ break;
+
+ // Can't queue anymore IRP's
+ DEBUGP (("[%s] TAP [%s] read IRP overrun\n",
+ NAME (l_Adapter), l_Adapter->m_Extension.m_TapName));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //==============================================================
+ // User mode issued a WriteFile request on the TAP file handle.
+ // The request will always get satisfied here. The call may
+ // fail if there are too many pending packets (queue full).
+ //==============================================================
+ case IRP_MJ_WRITE:
+ {
+ if (p_IRP->MdlAddress == NULL)
+ {
+ DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INVALID_PARAMETER;
+ p_IRP->IoStatus.Information = 0;
+ }
+ else if ((p_IRP->AssociatedIrp.SystemBuffer =
+ MmGetSystemAddressForMdlSafe
+ (p_IRP->MdlAddress, NormalPagePriority)) == NULL)
+ {
+ DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_INSUFFICIENT_RESOURCES;
+ p_IRP->IoStatus.Information = 0;
+ }
+ else if (!l_Adapter->m_InterfaceIsRunning)
+ {
+ DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+ else if (!l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE))
+ {
+ __try
+ {
+ p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;
+
+ DUMP_PACKET ("IRP_MJ_WRITE ETH",
+ (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.Write.Length);
+
+ //=====================================================
+ // If IPv4 packet, check whether or not packet
+ // was truncated.
+ //=====================================================
+#if PACKET_TRUNCATION_CHECK
+ IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.Write.Length,
+ FALSE,
+ "RX",
+ &l_Adapter->m_RxTrunc);
+#endif
+
+ NdisMEthIndicateReceive
+ (l_Adapter->m_MiniportAdapterHandle,
+ (NDIS_HANDLE) l_Adapter,
+ (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ ETHERNET_HEADER_SIZE,
+ (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer + ETHERNET_HEADER_SIZE,
+ l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE,
+ l_IrpSp->Parameters.Write.Length - ETHERNET_HEADER_SIZE);
+
+ NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);
+
+ p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+ }
+ else if (l_Adapter->m_tun && ((l_IrpSp->Parameters.Write.Length) >= IP_HEADER_SIZE))
+ {
+ __try
+ {
+ ETH_HEADER * p_UserToTap = &l_Adapter->m_UserToTap;
+
+ // for IPv6, need to use ethernet header with IPv6 proto
+ if ( IPH_GET_VER( ((IPHDR*) p_IRP->AssociatedIrp.SystemBuffer)->version_len) == 6 )
+ {
+ p_UserToTap = &l_Adapter->m_UserToTap_IPv6;
+ }
+
+ p_IRP->IoStatus.Information = l_IrpSp->Parameters.Write.Length;
+
+ DUMP_PACKET2 ("IRP_MJ_WRITE P2P",
+ p_UserToTap,
+ (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.Write.Length);
+
+ //=====================================================
+ // If IPv4 packet, check whether or not packet
+ // was truncated.
+ //=====================================================
+#if PACKET_TRUNCATION_CHECK
+ IPv4PacketSizeVerify ((unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.Write.Length,
+ TRUE,
+ "RX",
+ &l_Adapter->m_RxTrunc);
+#endif
+
+ NdisMEthIndicateReceive
+ (l_Adapter->m_MiniportAdapterHandle,
+ (NDIS_HANDLE) l_Adapter,
+ (unsigned char *) p_UserToTap,
+ sizeof (ETH_HEADER),
+ (unsigned char *) p_IRP->AssociatedIrp.SystemBuffer,
+ l_IrpSp->Parameters.Write.Length,
+ l_IrpSp->Parameters.Write.Length);
+
+ NdisMEthIndicateReceiveComplete (l_Adapter->m_MiniportAdapterHandle);
+
+ p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ DEBUGP (("[%s] NdisMEthIndicateReceive failed in IRP_MJ_WRITE (P2P)\n",
+ NAME (l_Adapter)));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+ }
+ else
+ {
+ DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n",
+ NAME (l_Adapter),
+ l_IrpSp->Parameters.Write.Length));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Information = 0; // ETHERNET_HEADER_SIZE;
+ p_IRP->IoStatus.Status = l_Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ if (l_Status == STATUS_SUCCESS)
+ INCREMENT_STAT (l_Adapter->m_Rx);
+ else
+ INCREMENT_STAT (l_Adapter->m_RxErr);
+
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //--------------------------------------------------------------
+ // User mode thread has called CreateFile() on the tap device
+ //--------------------------------------------------------------
+ case IRP_MJ_CREATE:
+ {
+ BOOLEAN succeeded = FALSE;
+ BOOLEAN mutex_succeeded;
+
+ DEBUGP
+ (("[%s] [TAP] release [%d.%d] open request (m_TapOpens=%d)\n",
+ NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION, l_Adapter->m_Extension.m_TapOpens));
+
+ ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
+ if (mutex_succeeded)
+ {
+ if (l_Adapter->m_Extension.m_TapIsRunning && !l_Adapter->m_Extension.m_TapOpens)
+ {
+ ResetTapAdapterState (l_Adapter);
+ l_Adapter->m_Extension.m_TapOpens = 1;
+ succeeded = TRUE;
+ }
+
+ if (succeeded)
+ {
+ INCREMENT_STAT (l_Adapter->m_Extension.m_NumTapOpens);
+ p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+ p_IRP->IoStatus.Information = 0;
+ }
+ else
+ {
+ DEBUGP (("[%s] TAP is presently unavailable (m_TapOpens=%d)\n",
+ NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+
+ RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
+ }
+ else
+ {
+ DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
+ NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //-----------------------------------------------------------
+ // User mode thread called CloseHandle() on the tap device
+ //-----------------------------------------------------------
+ case IRP_MJ_CLOSE:
+ {
+ BOOLEAN mutex_succeeded;
+
+ DEBUGP (("[%s] [TAP] release [%d.%d] close/cleanup request\n",
+ NAME (l_Adapter), TAP_DRIVER_MAJOR_VERSION,
+ TAP_DRIVER_MINOR_VERSION));
+
+ ACQUIRE_MUTEX_ADAPTIVE (&l_Adapter->m_Extension.m_OpenCloseMutex, mutex_succeeded);
+ if (mutex_succeeded)
+ {
+ l_Adapter->m_Extension.m_TapOpens = 0;
+ ResetTapAdapterState (l_Adapter);
+ FlushQueues (&l_Adapter->m_Extension);
+ SetMediaStatus (l_Adapter, FALSE);
+ RELEASE_MUTEX (&l_Adapter->m_Extension.m_OpenCloseMutex);
+ }
+ else
+ {
+ DEBUGP (("[%s] TAP is presently locked (m_TapOpens=%d)\n",
+ NAME (l_Adapter), l_Adapter->m_Extension.m_TapOpens));
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+
+ //------------------
+ // Strange Request
+ //------------------
+ default:
+ {
+ //NOTE_ERROR ();
+ p_IRP->IoStatus.Status = l_Status = STATUS_UNSUCCESSFUL;
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+ break;
+ }
+ }
+
+ return l_Status;
+}
+
+//=============================================================
+// CompleteIRP is normally called with an adapter -> userspace
+// network packet and an IRP (Pending I/O request) from userspace.
+//
+// The IRP will normally represent a queued overlapped read
+// operation from userspace that is in a wait state.
+//
+// Use the ethernet packet to satisfy the IRP.
+//=============================================================
+
+NTSTATUS
+CompleteIRP (IN PIRP p_IRP,
+ IN TapPacketPointer p_PacketBuffer,
+ IN CCHAR PriorityBoost)
+{
+ NTSTATUS l_Status = STATUS_UNSUCCESSFUL;
+
+ int offset;
+ int len;
+
+ MYASSERT (p_IRP);
+ MYASSERT (p_PacketBuffer);
+
+ IoSetCancelRoutine (p_IRP, NULL); // Disable cancel routine
+
+ //-------------------------------------------
+ // While p_PacketBuffer always contains a
+ // full ethernet packet, including the
+ // ethernet header, in point-to-point mode,
+ // we only want to return the IPv4
+ // component.
+ //-------------------------------------------
+
+ if (p_PacketBuffer->m_SizeFlags & TP_TUN)
+ {
+ offset = ETHERNET_HEADER_SIZE;
+ len = (int) (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK) - ETHERNET_HEADER_SIZE;
+ }
+ else
+ {
+ offset = 0;
+ len = (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK);
+ }
+
+ if (len < 0 || (int) p_IRP->IoStatus.Information < len)
+ {
+ p_IRP->IoStatus.Information = 0;
+ p_IRP->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
+ NOTE_ERROR ();
+ }
+ else
+ {
+ p_IRP->IoStatus.Information = len;
+ p_IRP->IoStatus.Status = l_Status = STATUS_SUCCESS;
+
+ __try
+ {
+ NdisMoveMemory (p_IRP->AssociatedIrp.SystemBuffer,
+ p_PacketBuffer->m_Data + offset,
+ len);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ NOTE_ERROR ();
+ p_IRP->IoStatus.Status = STATUS_UNSUCCESSFUL;
+ p_IRP->IoStatus.Information = 0;
+ }
+ }
+
+ __try
+ {
+ NdisFreeMemory (p_PacketBuffer,
+ TAP_PACKET_SIZE (p_PacketBuffer->m_SizeFlags & TP_SIZE_MASK),
+ 0);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+
+ if (l_Status == STATUS_SUCCESS)
+ {
+ IoCompleteRequest (p_IRP, PriorityBoost);
+ }
+ else
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+
+ return l_Status;
+}
+
+//==============================================
+// IRPs get cancelled for a number of reasons.
+//
+// The TAP device could be closed by userspace
+// when there are still pending read operations.
+//
+// The user could disable the TAP adapter in the
+// network connections control panel, while the
+// device is still open by a process.
+//==============================================
+VOID
+CancelIRPCallback (IN PDEVICE_OBJECT p_DeviceObject,
+ IN PIRP p_IRP)
+{
+ TapAdapterPointer l_Adapter = LookupAdapterInInstanceList (p_DeviceObject);
+ CancelIRP (l_Adapter ? &l_Adapter->m_Extension : NULL, p_IRP, TRUE);
+}
+
+VOID
+CancelIRP (TapExtensionPointer p_Extension,
+ IN PIRP p_IRP,
+ BOOLEAN callback)
+{
+ BOOLEAN exists = FALSE;
+
+ MYASSERT (p_IRP);
+
+ if (p_Extension)
+ {
+ NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+ exists = (QueueExtract (p_Extension->m_IrpQueue, p_IRP) == p_IRP);
+ NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+ }
+ else
+ exists = TRUE;
+
+ if (exists)
+ {
+ IoSetCancelRoutine (p_IRP, NULL);
+ p_IRP->IoStatus.Status = STATUS_CANCELLED;
+ p_IRP->IoStatus.Information = 0;
+ }
+
+ if (callback)
+ IoReleaseCancelSpinLock (p_IRP->CancelIrql);
+
+ if (exists)
+ IoCompleteRequest (p_IRP, IO_NO_INCREMENT);
+}
+
+//===========================================
+// Exhaust packet, IRP, and injection queues.
+//===========================================
+VOID
+FlushQueues (TapExtensionPointer p_Extension)
+{
+ PIRP l_IRP;
+ TapPacketPointer l_PacketBuffer;
+ InjectPacketPointer l_InjectBuffer;
+ int n_IRP=0, n_Packet=0, n_Inject=0;
+
+ MYASSERT (p_Extension);
+ MYASSERT (p_Extension->m_TapDevice);
+
+ while (TRUE)
+ {
+ NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+ l_IRP = QueuePop (p_Extension->m_IrpQueue);
+ NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+ if (l_IRP)
+ {
+ ++n_IRP;
+ CancelIRP (NULL, l_IRP, FALSE);
+ }
+ else
+ break;
+ }
+
+ while (TRUE)
+ {
+ NdisAcquireSpinLock (&p_Extension->m_QueueLock);
+ l_PacketBuffer = QueuePop (p_Extension->m_PacketQueue);
+ NdisReleaseSpinLock (&p_Extension->m_QueueLock);
+ if (l_PacketBuffer)
+ {
+ ++n_Packet;
+ MemFree (l_PacketBuffer, TAP_PACKET_SIZE (l_PacketBuffer->m_SizeFlags & TP_SIZE_MASK));
+ }
+ else
+ break;
+ }
+
+ while (TRUE)
+ {
+ NdisAcquireSpinLock (&p_Extension->m_InjectLock);
+ l_InjectBuffer = QueuePop (p_Extension->m_InjectQueue);
+ NdisReleaseSpinLock (&p_Extension->m_InjectLock);
+ if (l_InjectBuffer)
+ {
+ ++n_Inject;
+ INJECT_PACKET_FREE(l_InjectBuffer);
+ }
+ else
+ break;
+ }
+
+ DEBUGP ((
+ "[%s] [TAP] FlushQueues n_IRP=[%d,%d,%d] n_Packet=[%d,%d,%d] n_Inject=[%d,%d,%d]\n",
+ p_Extension->m_TapName,
+ n_IRP,
+ p_Extension->m_IrpQueue->max_size,
+ IRP_QUEUE_SIZE,
+ n_Packet,
+ p_Extension->m_PacketQueue->max_size,
+ PACKET_QUEUE_SIZE,
+ n_Inject,
+ p_Extension->m_InjectQueue->max_size,
+ INJECT_QUEUE_SIZE
+ ));
+}
+
+//===================================================
+// Tell Windows whether the TAP device should be
+// considered "connected" or "disconnected".
+//===================================================
+VOID
+SetMediaStatus (TapAdapterPointer p_Adapter, BOOLEAN state)
+{
+ if (p_Adapter->m_MediaState != state && !p_Adapter->m_MediaStateAlwaysConnected)
+ {
+ if (state)
+ NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
+ NDIS_STATUS_MEDIA_CONNECT, NULL, 0);
+ else
+ NdisMIndicateStatus (p_Adapter->m_MiniportAdapterHandle,
+ NDIS_STATUS_MEDIA_DISCONNECT, NULL, 0);
+
+ NdisMIndicateStatusComplete (p_Adapter->m_MiniportAdapterHandle);
+ p_Adapter->m_MediaState = state;
+ }
+}
+
+
+//======================================================
+// If DHCP mode is used together with tun
+// mode, consider the fact that the P2P remote subnet
+// might enclose the DHCP masq server address.
+//======================================================
+VOID
+CheckIfDhcpAndTunMode (TapAdapterPointer p_Adapter)
+{
+ if (p_Adapter->m_tun && p_Adapter->m_dhcp_enabled)
+ {
+ if ((p_Adapter->m_dhcp_server_ip & p_Adapter->m_remoteNetmask) == p_Adapter->m_remoteNetwork)
+ {
+ COPY_MAC (p_Adapter->m_dhcp_server_mac, p_Adapter->m_TapToUser.dest);
+ p_Adapter->m_dhcp_server_arp = FALSE;
+ }
+ }
+}
+
+//===================================================
+// Generate an ARP reply message for specific kinds
+// ARP queries.
+//===================================================
+BOOLEAN
+ProcessARP (TapAdapterPointer p_Adapter,
+ const PARP_PACKET src,
+ const IPADDR adapter_ip,
+ const IPADDR ip_network,
+ const IPADDR ip_netmask,
+ const MACADDR mac)
+{
+ //-----------------------------------------------
+ // Is this the kind of packet we are looking for?
+ //-----------------------------------------------
+ if (src->m_Proto == htons (ETH_P_ARP)
+ && MAC_EQUAL (src->m_MAC_Source, p_Adapter->m_MAC)
+ && MAC_EQUAL (src->m_ARP_MAC_Source, p_Adapter->m_MAC)
+ && MAC_EQUAL (src->m_MAC_Destination, p_Adapter->m_MAC_Broadcast)
+ && src->m_ARP_Operation == htons (ARP_REQUEST)
+ && src->m_MAC_AddressType == htons (MAC_ADDR_TYPE)
+ && src->m_MAC_AddressSize == sizeof (MACADDR)
+ && src->m_PROTO_AddressType == htons (ETH_P_IP)
+ && src->m_PROTO_AddressSize == sizeof (IPADDR)
+ && src->m_ARP_IP_Source == adapter_ip
+ && (src->m_ARP_IP_Destination & ip_netmask) == ip_network
+ && src->m_ARP_IP_Destination != adapter_ip)
+ {
+ ARP_PACKET *arp = (ARP_PACKET *) MemAlloc (sizeof (ARP_PACKET), TRUE);
+ if (arp)
+ {
+ //----------------------------------------------
+ // Initialize ARP reply fields
+ //----------------------------------------------
+ arp->m_Proto = htons (ETH_P_ARP);
+ arp->m_MAC_AddressType = htons (MAC_ADDR_TYPE);
+ arp->m_PROTO_AddressType = htons (ETH_P_IP);
+ arp->m_MAC_AddressSize = sizeof (MACADDR);
+ arp->m_PROTO_AddressSize = sizeof (IPADDR);
+ arp->m_ARP_Operation = htons (ARP_REPLY);
+
+ //----------------------------------------------
+ // ARP addresses
+ //----------------------------------------------
+ COPY_MAC (arp->m_MAC_Source, mac);
+ COPY_MAC (arp->m_MAC_Destination, p_Adapter->m_MAC);
+ COPY_MAC (arp->m_ARP_MAC_Source, mac);
+ COPY_MAC (arp->m_ARP_MAC_Destination, p_Adapter->m_MAC);
+ arp->m_ARP_IP_Source = src->m_ARP_IP_Destination;
+ arp->m_ARP_IP_Destination = adapter_ip;
+
+ DUMP_PACKET ("ProcessARP",
+ (unsigned char *) arp,
+ sizeof (ARP_PACKET));
+
+ InjectPacketDeferred (p_Adapter, (UCHAR *) arp, sizeof (ARP_PACKET));
+
+ MemFree (arp, sizeof (ARP_PACKET));
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+//===============================================================
+// Used in cases where internally generated packets such as
+// ARP or DHCP replies must be returned to the kernel, to be
+// seen as an incoming packet "arriving" on the interface.
+//===============================================================
+
+// Defer packet injection till IRQL < DISPATCH_LEVEL
+VOID
+InjectPacketDeferred (TapAdapterPointer p_Adapter,
+ UCHAR *packet,
+ const unsigned int len)
+{
+ InjectPacketPointer l_InjectBuffer;
+ PVOID result;
+
+ if (NdisAllocateMemoryWithTag (&l_InjectBuffer,
+ INJECT_PACKET_SIZE (len),
+ 'IPAT') == NDIS_STATUS_SUCCESS)
+ {
+ l_InjectBuffer->m_Size = len;
+ NdisMoveMemory (l_InjectBuffer->m_Data, packet, len);
+ NdisAcquireSpinLock (&p_Adapter->m_Extension.m_InjectLock);
+ result = QueuePush (p_Adapter->m_Extension.m_InjectQueue, l_InjectBuffer);
+ NdisReleaseSpinLock (&p_Adapter->m_Extension.m_InjectLock);
+ if (result)
+ KeInsertQueueDpc (&p_Adapter->m_Extension.m_InjectDpc, p_Adapter, NULL);
+ else
+ INJECT_PACKET_FREE(l_InjectBuffer);
+ }
+}
+
+// Handle the injection of previously deferred packets
+VOID
+InjectPacketDpc(KDPC *Dpc,
+ PVOID DeferredContext,
+ PVOID SystemArgument1,
+ PVOID SystemArgument2)
+{
+ InjectPacketPointer l_InjectBuffer;
+ TapAdapterPointer l_Adapter = (TapAdapterPointer)SystemArgument1;
+ while (TRUE)
+ {
+ NdisAcquireSpinLock (&l_Adapter->m_Extension.m_InjectLock);
+ l_InjectBuffer = QueuePop (l_Adapter->m_Extension.m_InjectQueue);
+ NdisReleaseSpinLock (&l_Adapter->m_Extension.m_InjectLock);
+ if (l_InjectBuffer)
+ {
+ InjectPacketNow(l_Adapter, l_InjectBuffer->m_Data, l_InjectBuffer->m_Size);
+ INJECT_PACKET_FREE(l_InjectBuffer);
+ }
+ else
+ break;
+ }
+}
+
+// Do packet injection now
+VOID
+InjectPacketNow (TapAdapterPointer p_Adapter,
+ UCHAR *packet,
+ const unsigned int len)
+{
+ MYASSERT (len >= ETHERNET_HEADER_SIZE);
+
+ __try
+ {
+ //------------------------------------------------------------
+ // NdisMEthIndicateReceive and NdisMEthIndicateReceiveComplete
+ // could potentially be called reentrantly both here and in
+ // TapDeviceHook/IRP_MJ_WRITE.
+ //
+ // The DDK docs imply that this is okay.
+ //
+ // Note that reentrant behavior could only occur if the
+ // non-deferred version of InjectPacket is used.
+ //------------------------------------------------------------
+ NdisMEthIndicateReceive
+ (p_Adapter->m_MiniportAdapterHandle,
+ (NDIS_HANDLE) p_Adapter,
+ packet,
+ ETHERNET_HEADER_SIZE,
+ packet + ETHERNET_HEADER_SIZE,
+ len - ETHERNET_HEADER_SIZE,
+ len - ETHERNET_HEADER_SIZE);
+
+ NdisMEthIndicateReceiveComplete (p_Adapter->m_MiniportAdapterHandle);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ DEBUGP (("[%s] NdisMEthIndicateReceive failed in InjectPacketNow\n",
+ NAME (p_Adapter)));
+ NOTE_ERROR ();
+ }
+}
+
+//===================================================================
+// Go back to default TAP mode from Point-To-Point mode.
+// Also reset (i.e. disable) DHCP Masq mode.
+//===================================================================
+VOID ResetTapAdapterState (TapAdapterPointer p_Adapter)
+{
+ // Point-To-Point
+ p_Adapter->m_tun = FALSE;
+ p_Adapter->m_localIP = 0;
+ p_Adapter->m_remoteNetwork = 0;
+ p_Adapter->m_remoteNetmask = 0;
+ NdisZeroMemory (&p_Adapter->m_TapToUser, sizeof (p_Adapter->m_TapToUser));
+ NdisZeroMemory (&p_Adapter->m_UserToTap, sizeof (p_Adapter->m_UserToTap));
+ NdisZeroMemory (&p_Adapter->m_UserToTap_IPv6, sizeof (p_Adapter->m_UserToTap_IPv6));
+
+ // DHCP Masq
+ p_Adapter->m_dhcp_enabled = FALSE;
+ p_Adapter->m_dhcp_server_arp = FALSE;
+ p_Adapter->m_dhcp_user_supplied_options_buffer_len = 0;
+ p_Adapter->m_dhcp_addr = 0;
+ p_Adapter->m_dhcp_netmask = 0;
+ p_Adapter->m_dhcp_server_ip = 0;
+ p_Adapter->m_dhcp_lease_time = 0;
+ p_Adapter->m_dhcp_received_discover = FALSE;
+ p_Adapter->m_dhcp_bad_requests = 0;
+ NdisZeroMemory (p_Adapter->m_dhcp_server_mac, sizeof (MACADDR));
+}
+
+#if ENABLE_NONADMIN
+
+//===================================================================
+// Set TAP device handle to be accessible without admin privileges.
+//===================================================================
+VOID AllowNonAdmin (TapExtensionPointer p_Extension)
+{
+ NTSTATUS stat;
+ SECURITY_DESCRIPTOR sd;
+ OBJECT_ATTRIBUTES oa;
+ IO_STATUS_BLOCK isb;
+ HANDLE hand = NULL;
+
+ NdisZeroMemory (&sd, sizeof (sd));
+ NdisZeroMemory (&oa, sizeof (oa));
+ NdisZeroMemory (&isb, sizeof (isb));
+
+ if (!p_Extension->m_CreatedUnicodeLinkName)
+ {
+ DEBUGP (("[TAP] AllowNonAdmin: UnicodeLinkName is uninitialized\n"));
+ NOTE_ERROR ();
+ return;
+ }
+
+ stat = RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
+ if (stat != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] AllowNonAdmin: RtlCreateSecurityDescriptor failed\n"));
+ NOTE_ERROR ();
+ return;
+ }
+
+ InitializeObjectAttributes (
+ &oa,
+ &p_Extension->m_UnicodeLinkName,
+ OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL
+ );
+
+ stat = ZwOpenFile (
+ &hand,
+ WRITE_DAC,
+ &oa,
+ &isb,
+ 0,
+ 0
+ );
+ if (stat != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] AllowNonAdmin: ZwOpenFile failed, status=0x%08x\n", (unsigned int)stat));
+ NOTE_ERROR ();
+ return;
+ }
+
+ stat = ZwSetSecurityObject (hand, DACL_SECURITY_INFORMATION, &sd);
+ if (stat != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] AllowNonAdmin: ZwSetSecurityObject failed\n"));
+ NOTE_ERROR ();
+ return;
+ }
+
+ stat = ZwClose (hand);
+ if (stat != STATUS_SUCCESS)
+ {
+ DEBUGP (("[TAP] AllowNonAdmin: ZwClose failed\n"));
+ NOTE_ERROR ();
+ return;
+ }
+
+ DEBUGP (("[TAP] AllowNonAdmin: SUCCEEDED\n"));
+}
+
+#endif
+
+#if PACKET_TRUNCATION_CHECK
+
+VOID
+IPv4PacketSizeVerify (const UCHAR *data, ULONG length, BOOLEAN tun, const char *prefix, LONG *counter)
+{
+ const IPHDR *ip;
+ int len = length;
+
+ if (tun)
+ {
+ ip = (IPHDR *) data;
+ }
+ else
+ {
+ if (length >= sizeof (ETH_HEADER))
+ {
+ const ETH_HEADER *eth = (ETH_HEADER *) data;
+
+ if (eth->proto != htons (ETH_P_IP))
+ return;
+
+ ip = (IPHDR *) (data + sizeof (ETH_HEADER));
+ len -= sizeof (ETH_HEADER);
+ }
+ else
+ return;
+ }
+
+ if (len >= sizeof (IPHDR))
+ {
+ const int totlen = ntohs (ip->tot_len);
+
+ DEBUGP (("[TAP] IPv4PacketSizeVerify %s len=%d totlen=%d\n", prefix, len, totlen));
+
+ if (len != totlen)
+ ++(*counter);
+ }
+}
+
+#endif
+
+//======================================================================
+// End of Source
+//======================================================================
diff --git a/tap-win32/types.h b/tap-win32/types.h
new file mode 100755
index 0000000..bdc08e7
--- /dev/null
+++ b/tap-win32/types.h
@@ -0,0 +1,178 @@
+/*
+ * TAP-Win32/TAP-Win64 -- A kernel driver to provide virtual tap
+ * device functionality on Windows.
+ *
+ * This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
+ *
+ * This source code is Copyright (C) 2002-2010 OpenVPN Technologies, Inc.,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TAP_TYPES_DEFINED
+#define TAP_TYPES_DEFINED
+
+typedef struct _Queue
+{
+ ULONG base;
+ ULONG size;
+ ULONG capacity;
+ ULONG max_size;
+ PVOID data[];
+} Queue;
+
+typedef struct _TapAdapter;
+typedef struct _TapPacket;
+
+typedef union _TapAdapterQuery
+{
+ NDIS_HARDWARE_STATUS m_HardwareStatus;
+ NDIS_MEDIUM m_Medium;
+ NDIS_PHYSICAL_MEDIUM m_PhysicalMedium;
+ UCHAR m_MacAddress [6];
+ UCHAR m_Buffer [256];
+ ULONG m_Long;
+ USHORT m_Short;
+ UCHAR m_Byte;
+}
+TapAdapterQuery, *TapAdapterQueryPointer;
+
+typedef struct _TapExtension
+{
+ // TAP device object and packet queues
+ Queue *m_PacketQueue, *m_IrpQueue;
+ PDEVICE_OBJECT m_TapDevice;
+ NDIS_HANDLE m_TapDeviceHandle;
+ ULONG m_TapOpens;
+
+ // Used to lock packet queues
+ NDIS_SPIN_LOCK m_QueueLock;
+ BOOLEAN m_AllocatedSpinlocks;
+
+ // Used to bracket open/close
+ // state changes.
+ MUTEX m_OpenCloseMutex;
+
+ // True if device has been permanently halted
+ BOOLEAN m_Halt;
+
+ // TAP device name
+ unsigned char *m_TapName;
+ UNICODE_STRING m_UnicodeLinkName;
+ BOOLEAN m_CreatedUnicodeLinkName;
+
+ // Used for device status ioctl only
+ const char *m_LastErrorFilename;
+ int m_LastErrorLineNumber;
+ LONG m_NumTapOpens;
+
+ // Flags
+ BOOLEAN m_TapIsRunning;
+ BOOLEAN m_CalledTapDeviceFreeResources;
+
+ // DPC queue for deferred packet injection
+ BOOLEAN m_InjectDpcInitialized;
+ KDPC m_InjectDpc;
+ NDIS_SPIN_LOCK m_InjectLock;
+ Queue *m_InjectQueue;
+}
+TapExtension, *TapExtensionPointer;
+
+typedef struct _TapPacket
+ {
+# define TAP_PACKET_SIZE(data_size) (sizeof (TapPacket) + (data_size))
+# define TP_TUN 0x80000000
+# define TP_SIZE_MASK (~TP_TUN)
+ ULONG m_SizeFlags;
+ UCHAR m_Data []; // m_Data must be the last struct member
+ }
+TapPacket, *TapPacketPointer;
+
+typedef struct _InjectPacket
+ {
+# define INJECT_PACKET_SIZE(data_size) (sizeof (InjectPacket) + (data_size))
+# define INJECT_PACKET_FREE(ib) NdisFreeMemory ((ib), INJECT_PACKET_SIZE ((ib)->m_Size), 0)
+ ULONG m_Size;
+ UCHAR m_Data []; // m_Data must be the last struct member
+ }
+InjectPacket, *InjectPacketPointer;
+
+typedef struct _TapAdapter
+{
+# define NAME(a) ((a)->m_NameAnsi.Buffer)
+ ANSI_STRING m_NameAnsi;
+ MACADDR m_MAC;
+ BOOLEAN m_InterfaceIsRunning;
+ NDIS_HANDLE m_MiniportAdapterHandle;
+ LONG m_Rx, m_Tx, m_RxErr, m_TxErr;
+#if PACKET_TRUNCATION_CHECK
+ LONG m_RxTrunc, m_TxTrunc;
+#endif
+ NDIS_MEDIUM m_Medium;
+ ULONG m_Lookahead;
+ ULONG m_MTU;
+
+ // TRUE if adapter should always be
+ // "connected" even when device node
+ // is not open by a userspace process.
+ BOOLEAN m_MediaStateAlwaysConnected;
+
+ // TRUE if device is "connected"
+ BOOLEAN m_MediaState;
+
+ // Adapter power state
+ char m_DeviceState;
+
+ // Info for point-to-point mode
+ BOOLEAN m_tun;
+ IPADDR m_localIP;
+ IPADDR m_remoteNetwork;
+ IPADDR m_remoteNetmask;
+ ETH_HEADER m_TapToUser;
+ ETH_HEADER m_UserToTap;
+ ETH_HEADER m_UserToTap_IPv6; // same as UserToTap but proto=ipv6
+ MACADDR m_MAC_Broadcast;
+
+ // Used for DHCP server masquerade
+ BOOLEAN m_dhcp_enabled;
+ IPADDR m_dhcp_addr;
+ ULONG m_dhcp_netmask;
+ IPADDR m_dhcp_server_ip;
+ BOOLEAN m_dhcp_server_arp;
+ MACADDR m_dhcp_server_mac;
+ ULONG m_dhcp_lease_time;
+ UCHAR m_dhcp_user_supplied_options_buffer[DHCP_USER_SUPPLIED_OPTIONS_BUFFER_SIZE];
+ ULONG m_dhcp_user_supplied_options_buffer_len;
+ BOOLEAN m_dhcp_received_discover;
+ ULONG m_dhcp_bad_requests;
+
+ // Help to tear down the adapter by keeping
+ // some state information on allocated
+ // resources.
+ BOOLEAN m_CalledAdapterFreeResources;
+ BOOLEAN m_RegisteredAdapterShutdownHandler;
+
+ // Multicast list info
+ NDIS_SPIN_LOCK m_MCLock;
+ BOOLEAN m_MCLockAllocated;
+ ULONG m_MCListSize;
+ MC_LIST m_MCList;
+
+ // Information on the TAP device
+ TapExtension m_Extension;
+} TapAdapter, *TapAdapterPointer;
+
+#endif
diff --git a/tun.c b/tun.c
new file mode 100644
index 0000000..28aa368
--- /dev/null
+++ b/tun.c
@@ -0,0 +1,4557 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Support routines for configuring and accessing TUN/TAP
+ * virtual network adapters.
+ *
+ * This file is based on the TUN/TAP driver interface routines
+ * from VTun by Maxim Krasnyansky <max_mk@yahoo.com>.
+ */
+
+#include "syshead.h"
+
+#include "tun.h"
+#include "fdmisc.h"
+#include "common.h"
+#include "misc.h"
+#include "socket.h"
+#include "manage.h"
+#include "route.h"
+#include "win32.h"
+
+#include "memdbg.h"
+
+#ifdef WIN32
+
+/* #define SIMULATE_DHCP_FAILED */ /* simulate bad DHCP negotiation */
+
+#define NI_TEST_FIRST (1<<0)
+#define NI_IP_NETMASK (1<<1)
+#define NI_OPTIONS (1<<2)
+
+static void netsh_ifconfig (const struct tuntap_options *to,
+ const char *flex_name,
+ const in_addr_t ip,
+ const in_addr_t netmask,
+ const unsigned int flags);
+
+static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
+
+#endif
+
+#ifdef TARGET_SOLARIS
+static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
+#include <stropts.h>
+#endif
+
+bool
+is_dev_type (const char *dev, const char *dev_type, const char *match_type)
+{
+ ASSERT (match_type);
+ if (!dev)
+ return false;
+ if (dev_type)
+ return !strcmp (dev_type, match_type);
+ else
+ return !strncmp (dev, match_type, strlen (match_type));
+}
+
+int
+dev_type_enum (const char *dev, const char *dev_type)
+{
+ if (is_dev_type (dev, dev_type, "tun"))
+ return DEV_TYPE_TUN;
+ else if (is_dev_type (dev, dev_type, "tap"))
+ return DEV_TYPE_TAP;
+ else if (is_dev_type (dev, dev_type, "null"))
+ return DEV_TYPE_NULL;
+ else
+ return DEV_TYPE_UNDEF;
+}
+
+const char *
+dev_type_string (const char *dev, const char *dev_type)
+{
+ switch (dev_type_enum (dev, dev_type))
+ {
+ case DEV_TYPE_TUN:
+ return "tun";
+ case DEV_TYPE_TAP:
+ return "tap";
+ case DEV_TYPE_NULL:
+ return "null";
+ default:
+ return "[unknown-dev-type]";
+ }
+}
+
+/*
+ * Try to predict the actual TUN/TAP device instance name,
+ * before the device is actually opened.
+ */
+const char *
+guess_tuntap_dev (const char *dev,
+ const char *dev_type,
+ const char *dev_node,
+ struct gc_arena *gc)
+{
+#ifdef WIN32
+ const int dt = dev_type_enum (dev, dev_type);
+ if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP)
+ {
+ return netsh_get_id (dev_node, gc);
+ }
+#endif
+
+ /* default case */
+ return dev;
+}
+
+/*
+ * Called by the open_tun function of OSes to check if we
+ * explicitly support IPv6.
+ *
+ * In this context, explicit means that the OS expects us to
+ * do something special to the tun socket in order to support
+ * IPv6, i.e. it is not transparent.
+ *
+ * ipv6_explicitly_supported should be set to false if we don't
+ * have any explicit IPv6 code in the tun device handler.
+ *
+ * If ipv6_explicitly_supported is true, then we have explicit
+ * OS-specific tun dev code for handling IPv6. If so, tt->ipv6
+ * is set according to the --tun-ipv6 command line option.
+ */
+static void
+ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
+{
+ tt->ipv6 = false;
+ if (ipv6_explicitly_supported)
+ tt->ipv6 = ipv6;
+ else if (ipv6)
+ msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
+}
+
+/* --ifconfig-nowarn disables some options sanity checking */
+static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
+
+/*
+ * If !tun, make sure ifconfig_remote_netmask looks
+ * like a netmask.
+ *
+ * If tun, make sure ifconfig_remote_netmask looks
+ * like an IPv4 address.
+ */
+static void
+ifconfig_sanity_check (bool tun, in_addr_t addr, int topology)
+{
+ struct gc_arena gc = gc_new ();
+ const bool looks_like_netmask = ((addr & 0xFF000000) == 0xFF000000);
+ if (tun)
+ {
+ if (looks_like_netmask && (topology == TOP_NET30 || topology == TOP_P2P))
+ msg (M_WARN, "WARNING: Since you are using --dev tun with a point-to-point topology, the second argument to --ifconfig must be an IP address. You are using something (%s) that looks more like a netmask. %s",
+ print_in_addr_t (addr, 0, &gc),
+ ifconfig_warn_how_to_silence);
+ }
+ else /* tap */
+ {
+ if (!looks_like_netmask)
+ msg (M_WARN, "WARNING: Since you are using --dev tap, the second argument to --ifconfig must be a netmask, for example something like 255.255.255.0. %s",
+ ifconfig_warn_how_to_silence);
+ }
+ gc_free (&gc);
+}
+
+/*
+ * For TAP-style devices, generate a broadcast address.
+ */
+static in_addr_t
+generate_ifconfig_broadcast_addr (in_addr_t local,
+ in_addr_t netmask)
+{
+ return local | ~netmask;
+}
+
+/*
+ * Check that --local and --remote addresses do not
+ * clash with ifconfig addresses or subnet.
+ */
+static void
+check_addr_clash (const char *name,
+ int type,
+ in_addr_t public,
+ in_addr_t local,
+ in_addr_t remote_netmask)
+{
+ struct gc_arena gc = gc_new ();
+#if 0
+ msg (M_INFO, "CHECK_ADDR_CLASH type=%d public=%s local=%s, remote_netmask=%s",
+ type,
+ print_in_addr_t (public, 0, &gc),
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote_netmask, 0, &gc));
+#endif
+
+ if (public)
+ {
+ if (type == DEV_TYPE_TUN)
+ {
+ const in_addr_t test_netmask = 0xFFFFFF00;
+ const in_addr_t public_net = public & test_netmask;
+ const in_addr_t local_net = local & test_netmask;
+ const in_addr_t remote_net = remote_netmask & test_netmask;
+
+ if (public == local || public == remote_netmask)
+ msg (M_WARN,
+ "WARNING: --%s address [%s] conflicts with --ifconfig address pair [%s, %s]. %s",
+ name,
+ print_in_addr_t (public, 0, &gc),
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote_netmask, 0, &gc),
+ ifconfig_warn_how_to_silence);
+
+ if (public_net == local_net || public_net == remote_net)
+ msg (M_WARN,
+ "WARNING: potential conflict between --%s address [%s] and --ifconfig address pair [%s, %s] -- this is a warning only that is triggered when local/remote addresses exist within the same /24 subnet as --ifconfig endpoints. %s",
+ name,
+ print_in_addr_t (public, 0, &gc),
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote_netmask, 0, &gc),
+ ifconfig_warn_how_to_silence);
+ }
+ else if (type == DEV_TYPE_TAP)
+ {
+ const in_addr_t public_network = public & remote_netmask;
+ const in_addr_t virtual_network = local & remote_netmask;
+ if (public_network == virtual_network)
+ msg (M_WARN,
+ "WARNING: --%s address [%s] conflicts with --ifconfig subnet [%s, %s] -- local and remote addresses cannot be inside of the --ifconfig subnet. %s",
+ name,
+ print_in_addr_t (public, 0, &gc),
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote_netmask, 0, &gc),
+ ifconfig_warn_how_to_silence);
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Issue a warning if ip/netmask (on the virtual IP network) conflicts with
+ * the settings on the local LAN. This is designed to flag issues where
+ * (for example) the OpenVPN server LAN is running on 192.168.1.x, but then
+ * an OpenVPN client tries to connect from a public location that is also running
+ * off of a router set to 192.168.1.x.
+ */
+void
+check_subnet_conflict (const in_addr_t ip,
+ const in_addr_t netmask,
+ const char *prefix)
+{
+ struct gc_arena gc = gc_new ();
+ in_addr_t lan_gw = 0;
+ in_addr_t lan_netmask = 0;
+
+ if (get_default_gateway (&lan_gw, &lan_netmask))
+ {
+ const in_addr_t lan_network = lan_gw & lan_netmask;
+ const in_addr_t network = ip & netmask;
+
+ /* do the two subnets defined by network/netmask and lan_network/lan_netmask intersect? */
+ if ((network & lan_netmask) == lan_network
+ || (lan_network & netmask) == network)
+ {
+ msg (M_WARN, "WARNING: potential %s subnet conflict between local LAN [%s/%s] and remote VPN [%s/%s]",
+ prefix,
+ print_in_addr_t (lan_network, 0, &gc),
+ print_in_addr_t (lan_netmask, 0, &gc),
+ print_in_addr_t (network, 0, &gc),
+ print_in_addr_t (netmask, 0, &gc));
+ }
+ }
+ gc_free (&gc);
+}
+
+void
+warn_on_use_of_common_subnets (void)
+{
+ struct gc_arena gc = gc_new ();
+ in_addr_t lan_gw = 0;
+ in_addr_t lan_netmask = 0;
+
+ if (get_default_gateway (&lan_gw, &lan_netmask))
+ {
+ const in_addr_t lan_network = lan_gw & lan_netmask;
+ if (lan_network == 0xC0A80000 || lan_network == 0xC0A80100)
+ msg (M_WARN, "NOTE: your local LAN uses the extremely common subnet address 192.168.0.x or 192.168.1.x. Be aware that this might create routing conflicts if you connect to the VPN server from public locations such as internet cafes that use the same subnet.");
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Complain if --dev tap and --ifconfig is used on an OS for which
+ * we don't have a custom tap ifconfig template below.
+ */
+static void
+no_tap_ifconfig ()
+{
+ msg (M_FATAL, "Sorry but you cannot use --dev tap and --ifconfig together on this OS because I have not yet been programmed to understand the appropriate ifconfig syntax to use for TAP-style devices on this OS. Your best alternative is to use an --up script and do the ifconfig command manually.");
+}
+
+/*
+ * Return a string to be used for options compatibility check
+ * between peers.
+ */
+const char *
+ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ if (tt->did_ifconfig_setup && !disable)
+ {
+ if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
+ {
+ buf_printf (&out, "%s %s",
+ print_in_addr_t (tt->local & tt->remote_netmask, 0, gc),
+ print_in_addr_t (tt->remote_netmask, 0, gc));
+ }
+ else if (tt->type == DEV_TYPE_TUN)
+ {
+ const char *l, *r;
+ if (remote)
+ {
+ r = print_in_addr_t (tt->local, 0, gc);
+ l = print_in_addr_t (tt->remote_netmask, 0, gc);
+ }
+ else
+ {
+ l = print_in_addr_t (tt->local, 0, gc);
+ r = print_in_addr_t (tt->remote_netmask, 0, gc);
+ }
+ buf_printf (&out, "%s %s", r, l);
+ }
+ else
+ buf_printf (&out, "[undef]");
+ }
+ return BSTR (&out);
+}
+
+/*
+ * Return a status string describing wait state.
+ */
+const char *
+tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (64, gc);
+ if (tt)
+ {
+ if (rwflags & EVENT_READ)
+ {
+ buf_printf (&out, "T%s",
+ (tt->rwflags_debug & EVENT_READ) ? "R" : "r");
+#ifdef WIN32
+ buf_printf (&out, "%s",
+ overlapped_io_state_ascii (&tt->reads));
+#endif
+ }
+ if (rwflags & EVENT_WRITE)
+ {
+ buf_printf (&out, "T%s",
+ (tt->rwflags_debug & EVENT_WRITE) ? "W" : "w");
+#ifdef WIN32
+ buf_printf (&out, "%s",
+ overlapped_io_state_ascii (&tt->writes));
+#endif
+ }
+ }
+ else
+ {
+ buf_printf (&out, "T?");
+ }
+ return BSTR (&out);
+}
+
+/*
+ * Return true for point-to-point topology, false for subnet topology
+ */
+bool
+is_tun_p2p (const struct tuntap *tt)
+{
+ bool tun = false;
+
+ if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
+ tun = false;
+ else if (tt->type == DEV_TYPE_TUN)
+ tun = true;
+ else
+ msg (M_FATAL, "Error: problem with tun vs. tap setting"); /* JYFIXME -- needs to be caught earlier, in init_tun? */
+
+ return tun;
+}
+
+/*
+ * Init tun/tap object.
+ *
+ * Set up tuntap structure for ifconfig,
+ * but don't execute yet.
+ */
+struct tuntap *
+init_tun (const char *dev, /* --dev option */
+ const char *dev_type, /* --dev-type option */
+ int topology, /* one of the TOP_x values */
+ const char *ifconfig_local_parm, /* --ifconfig parm 1 */
+ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
+ in_addr_t local_public,
+ in_addr_t remote_public,
+ const bool strict_warn,
+ struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+ struct tuntap *tt;
+
+ ALLOC_OBJ (tt, struct tuntap);
+ clear_tuntap (tt);
+
+ tt->type = dev_type_enum (dev, dev_type);
+ tt->topology = topology;
+
+ if (ifconfig_local_parm && ifconfig_remote_netmask_parm)
+ {
+ bool tun = false;
+ const char *ifconfig_local = NULL;
+ const char *ifconfig_remote_netmask = NULL;
+ const char *ifconfig_broadcast = NULL;
+
+ /*
+ * We only handle TUN/TAP devices here, not --dev null devices.
+ */
+ tun = is_tun_p2p (tt);
+
+ /*
+ * Convert arguments to binary IPv4 addresses.
+ */
+
+ tt->local = getaddr (
+ GETADDR_RESOLVE
+ | GETADDR_HOST_ORDER
+ | GETADDR_FATAL_ON_SIGNAL
+ | GETADDR_FATAL,
+ ifconfig_local_parm,
+ 0,
+ NULL,
+ NULL);
+
+ tt->remote_netmask = getaddr (
+ (tun ? GETADDR_RESOLVE : 0)
+ | GETADDR_HOST_ORDER
+ | GETADDR_FATAL_ON_SIGNAL
+ | GETADDR_FATAL,
+ ifconfig_remote_netmask_parm,
+ 0,
+ NULL,
+ NULL);
+
+ /*
+ * Look for common errors in --ifconfig parms
+ */
+ if (strict_warn)
+ {
+ ifconfig_sanity_check (tt->type == DEV_TYPE_TUN, tt->remote_netmask, tt->topology);
+
+ /*
+ * If local_public or remote_public addresses are defined,
+ * make sure they do not clash with our virtual subnet.
+ */
+
+ check_addr_clash ("local",
+ tt->type,
+ local_public,
+ tt->local,
+ tt->remote_netmask);
+
+ check_addr_clash ("remote",
+ tt->type,
+ remote_public,
+ tt->local,
+ tt->remote_netmask);
+
+ if (tt->type == DEV_TYPE_TAP || (tt->type == DEV_TYPE_TUN && tt->topology == TOP_SUBNET))
+ check_subnet_conflict (tt->local, tt->remote_netmask, "TUN/TAP adapter");
+ else if (tt->type == DEV_TYPE_TUN)
+ check_subnet_conflict (tt->local, ~0, "TUN/TAP adapter");
+ }
+
+ /*
+ * Set ifconfig parameters
+ */
+ ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
+ ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
+
+ /*
+ * If TAP-style interface, generate broadcast address.
+ */
+ if (!tun)
+ {
+ tt->broadcast = generate_ifconfig_broadcast_addr (tt->local, tt->remote_netmask);
+ ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc);
+ }
+
+ /*
+ * Set environmental variables with ifconfig parameters.
+ */
+ if (es)
+ {
+ setenv_str (es, "ifconfig_local", ifconfig_local);
+ if (tun)
+ {
+ setenv_str (es, "ifconfig_remote", ifconfig_remote_netmask);
+ }
+ else
+ {
+ setenv_str (es, "ifconfig_netmask", ifconfig_remote_netmask);
+ setenv_str (es, "ifconfig_broadcast", ifconfig_broadcast);
+ }
+ }
+
+ tt->did_ifconfig_setup = true;
+ }
+ gc_free (&gc);
+ return tt;
+}
+
+/*
+ * Platform specific tun initializations
+ */
+void
+init_tun_post (struct tuntap *tt,
+ const struct frame *frame,
+ const struct tuntap_options *options)
+{
+ tt->options = *options;
+#ifdef WIN32
+ overlapped_io_init (&tt->reads, frame, FALSE, true);
+ overlapped_io_init (&tt->writes, frame, TRUE, true);
+ tt->rw_handle.read = tt->reads.overlapped.hEvent;
+ tt->rw_handle.write = tt->writes.overlapped.hEvent;
+ tt->adapter_index = ~0;
+#endif
+}
+
+/* execute the ifconfig command through the shell */
+void
+do_ifconfig (struct tuntap *tt,
+ const char *actual, /* actual device name */
+ int tun_mtu,
+ const struct env_set *es)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (tt->did_ifconfig_setup)
+ {
+ bool tun = false;
+ const char *ifconfig_local = NULL;
+ const char *ifconfig_remote_netmask = NULL;
+ const char *ifconfig_broadcast = NULL;
+ struct argv argv;
+
+ argv_init (&argv);
+
+ /*
+ * We only handle TUN/TAP devices here, not --dev null devices.
+ */
+ tun = is_tun_p2p (tt);
+
+ /*
+ * Set ifconfig parameters
+ */
+ ifconfig_local = print_in_addr_t (tt->local, 0, &gc);
+ ifconfig_remote_netmask = print_in_addr_t (tt->remote_netmask, 0, &gc);
+
+ /*
+ * If TAP-style device, generate broadcast address.
+ */
+ if (!tun)
+ ifconfig_broadcast = print_in_addr_t (tt->broadcast, 0, &gc);
+
+#ifdef ENABLE_MANAGEMENT
+ if (management)
+ {
+ management_set_state (management,
+ OPENVPN_STATE_ASSIGN_IP,
+ NULL,
+ tt->local,
+ 0);
+ }
+#endif
+
+
+#if defined(TARGET_LINUX)
+#ifdef CONFIG_FEATURE_IPROUTE
+ /*
+ * Set the MTU for the device
+ */
+ argv_printf (&argv,
+ "%s link set dev %s up mtu %d",
+ iproute_path,
+ actual,
+ tun_mtu
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip link set failed");
+
+ if (tun) {
+
+ /*
+ * Set the address for the device
+ */
+ argv_printf (&argv,
+ "%s addr add dev %s local %s peer %s",
+ iproute_path,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
+ } else {
+ argv_printf (&argv,
+ "%s addr add dev %s %s/%d broadcast %s",
+ iproute_path,
+ actual,
+ ifconfig_local,
+ count_netmask_bits(ifconfig_remote_netmask),
+ ifconfig_broadcast
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
+ }
+ tt->did_ifconfig = true;
+#else
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s pointopoint %s mtu %d",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu,
+ ifconfig_broadcast
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Linux ifconfig failed");
+ tt->did_ifconfig = true;
+
+#endif /*CONFIG_FEATURE_IPROUTE*/
+#elif defined(TARGET_SOLARIS)
+
+ /* Solaris 2.6 (and 7?) cannot set all parameters in one go...
+ * example:
+ * ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 up
+ * ifconfig tun2 netmask 255.255.255.255
+ */
+ if (tun)
+ {
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-1 failed"))
+ solaris_error_close (tt, es, actual);
+
+ argv_printf (&argv,
+ "%s %s netmask 255.255.255.255",
+ IFCONFIG_PATH,
+ actual
+ );
+ }
+ else
+ if (tt->topology == TOP_SUBNET)
+ {
+ argv_printf (&argv,
+ "%s %s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ }
+ else
+ argv_printf (&argv,
+ " %s %s %s netmask %s broadcast + up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask
+ );
+
+ argv_msg (M_INFO, &argv);
+ if (!openvpn_execve_check (&argv, es, 0, "Solaris ifconfig phase-2 failed"))
+ solaris_error_close (tt, es, actual);
+
+ if (!tun && tt->topology == TOP_SUBNET)
+ {
+ /* Add a network route for the local tun interface */
+ struct route r;
+ CLEAR (r);
+ r.defined = true;
+ r.network = tt->local & tt->remote_netmask;
+ r.netmask = tt->remote_netmask;
+ r.gateway = tt->local;
+ r.metric_defined = true;
+ r.metric = 0;
+ add_route (&r, tt, 0, es);
+ }
+
+ tt->did_ifconfig = true;
+
+#elif defined(TARGET_OPENBSD)
+
+ /*
+ * OpenBSD tun devices appear to be persistent by default. It seems in order
+ * to make this work correctly, we need to delete the previous instance
+ * (if it exists), and re-ifconfig. Let me know if you know a better way.
+ */
+
+ argv_printf (&argv,
+ "%s %s destroy",
+ IFCONFIG_PATH,
+ actual);
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
+ argv_printf (&argv,
+ "%s %s create",
+ IFCONFIG_PATH,
+ actual);
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
+ msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
+
+ /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s link0",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu,
+ ifconfig_broadcast
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "OpenBSD ifconfig failed");
+ tt->did_ifconfig = true;
+
+#elif defined(TARGET_NETBSD)
+
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ /*
+ * NetBSD has distinct tun and tap devices
+ * so we don't need the "link0" extra parameter to specify we want to do
+ * tunneling at the ethernet level
+ */
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d broadcast %s",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu,
+ ifconfig_broadcast
+ );
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
+ tt->did_ifconfig = true;
+
+#elif defined(TARGET_DARWIN)
+
+ /*
+ * Darwin (i.e. Mac OS X) seems to exhibit similar behaviour to OpenBSD...
+ */
+
+ argv_printf (&argv,
+ "%s %s delete",
+ IFCONFIG_PATH,
+ actual);
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, NULL);
+ msg (M_INFO, "NOTE: Tried to delete pre-existing tun/tap instance -- No Problem if failure");
+
+
+ /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ {
+ if (tt->topology == TOP_SUBNET)
+ argv_printf (&argv,
+ "%s %s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ }
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "Mac OS X ifconfig failed");
+ tt->did_ifconfig = true;
+
+ /* Add a network route for the local tun interface */
+ if (!tun && tt->topology == TOP_SUBNET)
+ {
+ struct route r;
+ CLEAR (r);
+ r.defined = true;
+ r.network = tt->local & tt->remote_netmask;
+ r.netmask = tt->remote_netmask;
+ r.gateway = tt->local;
+ add_route (&r, tt, 0, es);
+ }
+
+#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
+
+ /* example: ifconfig tun2 10.2.0.2 10.2.0.1 mtu 1450 netmask 255.255.255.255 up */
+ if (tun)
+ argv_printf (&argv,
+ "%s %s %s %s mtu %d netmask 255.255.255.255 up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+ else
+ argv_printf (&argv,
+ "%s %s %s netmask %s mtu %d up",
+ IFCONFIG_PATH,
+ actual,
+ ifconfig_local,
+ ifconfig_remote_netmask,
+ tun_mtu
+ );
+
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, S_FATAL, "FreeBSD ifconfig failed");
+ tt->did_ifconfig = true;
+
+ /* Add a network route for the local tun interface */
+ if (!tun && tt->topology == TOP_SUBNET)
+ {
+ struct route r;
+ CLEAR (r);
+ r.defined = true;
+ r.network = tt->local & tt->remote_netmask;
+ r.netmask = tt->remote_netmask;
+ r.gateway = tt->local;
+ add_route (&r, tt, 0, es);
+ }
+
+#elif defined (WIN32)
+ {
+ /*
+ * Make sure that both ifconfig addresses are part of the
+ * same .252 subnet.
+ */
+ if (tun)
+ {
+ verify_255_255_255_252 (tt->local, tt->remote_netmask);
+ tt->adapter_netmask = ~3;
+ }
+ else
+ {
+ tt->adapter_netmask = tt->remote_netmask;
+ }
+
+ switch (tt->options.ip_win32_type)
+ {
+ case IPW32_SET_MANUAL:
+ msg (M_INFO, "******** NOTE: Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)",
+ actual,
+ ifconfig_local,
+ print_in_addr_t (tt->adapter_netmask, 0, &gc));
+ break;
+ case IPW32_SET_NETSH:
+ if (!strcmp (actual, "NULL"))
+ msg (M_FATAL, "Error: When using --ip-win32 netsh, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
+
+ netsh_ifconfig (&tt->options,
+ actual,
+ tt->local,
+ tt->adapter_netmask,
+ NI_IP_NETMASK|NI_OPTIONS);
+
+ break;
+ }
+ tt->did_ifconfig = true;
+ }
+
+#else
+ msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system. You should ifconfig your TUN/TAP device manually or use an --up script.");
+#endif
+ argv_reset (&argv);
+ }
+ gc_free (&gc);
+}
+
+void
+clear_tuntap (struct tuntap *tuntap)
+{
+ CLEAR (*tuntap);
+#ifdef WIN32
+ tuntap->hand = NULL;
+#else
+ tuntap->fd = -1;
+#endif
+#ifdef TARGET_SOLARIS
+ tuntap->ip_fd = -1;
+#endif
+ tuntap->ipv6 = false;
+}
+
+static void
+open_null (struct tuntap *tt)
+{
+ tt->actual_name = string_alloc ("null", NULL);
+}
+
+#ifndef WIN32
+static void
+open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
+ bool ipv6, bool ipv6_explicitly_supported, bool dynamic,
+ struct tuntap *tt)
+{
+ char tunname[256];
+ char dynamic_name[256];
+ bool dynamic_opened = false;
+
+ ipv6_support (ipv6, ipv6_explicitly_supported, tt);
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+ open_null (tt);
+ }
+ else
+ {
+ /*
+ * --dev-node specified, so open an explicit device node
+ */
+ if (dev_node)
+ {
+ openvpn_snprintf (tunname, sizeof (tunname), "%s", dev_node);
+ }
+ else
+ {
+ /*
+ * dynamic open is indicated by --dev specified without
+ * explicit unit number. Try opening /dev/[dev]n
+ * where n = [0, 255].
+ */
+ if (dynamic && !has_digit((unsigned char *)dev))
+ {
+ int i;
+ for (i = 0; i < 256; ++i)
+ {
+ openvpn_snprintf (tunname, sizeof (tunname),
+ "/dev/%s%d", dev, i);
+ openvpn_snprintf (dynamic_name, sizeof (dynamic_name),
+ "%s%d", dev, i);
+ if ((tt->fd = open (tunname, O_RDWR)) > 0)
+ {
+ dynamic_opened = true;
+ break;
+ }
+ msg (D_READ_WRITE | M_ERRNO, "Tried opening %s (failed)", tunname);
+ }
+ if (!dynamic_opened)
+ msg (M_FATAL, "Cannot allocate TUN/TAP dev dynamically");
+ }
+ /*
+ * explicit unit number specified
+ */
+ else
+ {
+ openvpn_snprintf (tunname, sizeof (tunname), "/dev/%s", dev);
+ }
+ }
+
+ if (!dynamic_opened)
+ {
+ if ((tt->fd = open (tunname, O_RDWR)) < 0)
+ msg (M_ERR, "Cannot open TUN/TAP dev %s", tunname);
+ }
+
+ set_nonblock (tt->fd);
+ set_cloexec (tt->fd); /* don't pass fd to scripts */
+ msg (M_INFO, "TUN/TAP device %s opened", tunname);
+
+ /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
+ tt->actual_name = string_alloc (dynamic_opened ? dynamic_name : dev, NULL);
+ }
+}
+
+static void
+close_tun_generic (struct tuntap *tt)
+{
+ if (tt->fd >= 0)
+ close (tt->fd);
+ if (tt->actual_name)
+ free (tt->actual_name);
+ clear_tuntap (tt);
+}
+
+#endif
+
+#if defined(TARGET_LINUX)
+
+#ifdef HAVE_LINUX_IF_TUN_H /* New driver support */
+
+#ifndef HAVE_LINUX_SOCKIOS_H
+#error header file linux/sockios.h required
+#endif
+
+#if defined(HAVE_TUN_PI) && defined(HAVE_IPHDR) && defined(HAVE_IOVEC) && defined(ETH_P_IPV6) && defined(ETH_P_IP) && defined(HAVE_READV) && defined(HAVE_WRITEV)
+#define LINUX_IPV6 1
+/* #warning IPv6 ON */
+#else
+#define LINUX_IPV6 0
+/* #warning IPv6 OFF */
+#endif
+
+#if !PEDANTIC
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ struct ifreq ifr;
+
+ /*
+ * Set tt->ipv6 to true if
+ * (a) we have the capability of supporting --tun-ipv6, and
+ * (b) --tun-ipv6 was specified.
+ */
+ ipv6_support (ipv6, LINUX_IPV6, tt);
+
+ /*
+ * We handle --dev null specially, we do not open /dev/null for this.
+ */
+ if (tt->type == DEV_TYPE_NULL)
+ {
+ open_null (tt);
+ }
+ else
+ {
+ /*
+ * Process --dev-node
+ */
+ const char *node = dev_node;
+ if (!node)
+ node = "/dev/net/tun";
+
+ /*
+ * Open the interface
+ */
+ if ((tt->fd = open (node, O_RDWR)) < 0)
+ {
+ msg (M_WARN | M_ERRNO, "Note: Cannot open TUN/TAP dev %s", node);
+ return;
+ }
+
+ /*
+ * Process --tun-ipv6
+ */
+ CLEAR (ifr);
+ if (!tt->ipv6)
+ ifr.ifr_flags = IFF_NO_PI;
+
+#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+#endif
+
+ /*
+ * Figure out if tun or tap device
+ */
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ ifr.ifr_flags |= IFF_TUN;
+ }
+ else if (tt->type == DEV_TYPE_TAP)
+ {
+ ifr.ifr_flags |= IFF_TAP;
+ }
+ else
+ {
+ msg (M_FATAL, "I don't recognize device %s as a tun or tap device",
+ dev);
+ }
+
+ /*
+ * Set an explicit name, if --dev is not tun or tap
+ */
+ if (strcmp(dev, "tun") && strcmp(dev, "tap"))
+ strncpynt (ifr.ifr_name, dev, IFNAMSIZ);
+
+ /*
+ * Use special ioctl that configures tun/tap device with the parms
+ * we set in ifr
+ */
+ if (ioctl (tt->fd, TUNSETIFF, (void *) &ifr) < 0)
+ {
+ msg (M_WARN | M_ERRNO, "Note: Cannot ioctl TUNSETIFF %s", dev);
+ return;
+ }
+
+ msg (M_INFO, "TUN/TAP device %s opened", ifr.ifr_name);
+
+ /*
+ * Try making the TX send queue bigger
+ */
+#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
+ if (tt->options.txqueuelen) {
+ struct ifreq netifr;
+ int ctl_fd;
+
+ if ((ctl_fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0)
+ {
+ CLEAR (netifr);
+ strncpynt (netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
+ netifr.ifr_qlen = tt->options.txqueuelen;
+ if (ioctl (ctl_fd, SIOCSIFTXQLEN, (void *) &netifr) >= 0)
+ msg (D_OSBUF, "TUN/TAP TX queue length set to %d", tt->options.txqueuelen);
+ else
+ msg (M_WARN | M_ERRNO, "Note: Cannot set tx queue length on %s", ifr.ifr_name);
+ close (ctl_fd);
+ }
+ else
+ {
+ msg (M_WARN | M_ERRNO, "Note: Cannot open control socket on %s", ifr.ifr_name);
+ }
+ }
+#endif
+
+ set_nonblock (tt->fd);
+ set_cloexec (tt->fd);
+ tt->actual_name = string_alloc (ifr.ifr_name, NULL);
+ }
+ return;
+}
+
+#else
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ ASSERT (0);
+}
+
+#endif
+
+#else
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+}
+
+#endif /* HAVE_LINUX_IF_TUN_H */
+
+#ifdef TUNSETPERSIST
+
+/*
+ * This can be removed in future
+ * when all systems will use newer
+ * linux-headers
+ */
+#ifndef TUNSETOWNER
+#define TUNSETOWNER _IOW('T', 204, int)
+#endif
+#ifndef TUNSETGROUP
+#define TUNSETGROUP _IOW('T', 206, int)
+#endif
+
+void
+tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+{
+ struct tuntap *tt;
+
+ ALLOC_OBJ (tt, struct tuntap);
+ clear_tuntap (tt);
+ tt->type = dev_type_enum (dev, dev_type);
+ tt->options = *options;
+ open_tun (dev, dev_type, dev_node, ipv6, tt);
+ if (ioctl (tt->fd, TUNSETPERSIST, persist_mode) < 0)
+ msg (M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
+ if (username != NULL)
+ {
+ struct user_state user_state;
+
+ if (!get_user (username, &user_state))
+ msg (M_ERR, "Cannot get user entry for %s", username);
+ else
+ if (ioctl (tt->fd, TUNSETOWNER, user_state.pw->pw_uid) < 0)
+ msg (M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", username, dev);
+ }
+ if (groupname != NULL)
+ {
+ struct group_state group_state;
+
+ if (!get_group (groupname, &group_state))
+ msg (M_ERR, "Cannot get group entry for %s", groupname);
+ else
+ if (ioctl (tt->fd, TUNSETGROUP, group_state.gr->gr_gid) < 0)
+ msg (M_ERR, "Cannot ioctl TUNSETOWNER(%s) %s", groupname, dev);
+ }
+ close_tun (tt);
+ msg (M_INFO, "Persist state set to: %s", (persist_mode ? "ON" : "OFF"));
+}
+
+#endif /* TUNSETPERSIST */
+
+void
+close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
+ {
+ struct argv argv;
+ struct gc_arena gc = gc_new ();
+ argv_init (&argv);
+
+#ifdef CONFIG_FEATURE_IPROUTE
+ if (is_tun_p2p (tt))
+ {
+ argv_printf (&argv,
+ "%s addr del dev %s local %s peer %s",
+ iproute_path,
+ tt->actual_name,
+ print_in_addr_t (tt->local, 0, &gc),
+ print_in_addr_t (tt->remote_netmask, 0, &gc)
+ );
+ }
+ else
+ {
+ argv_printf (&argv,
+ "%s addr del dev %s %s/%d",
+ iproute_path,
+ tt->actual_name,
+ print_in_addr_t (tt->local, 0, &gc),
+ count_netmask_bits(print_in_addr_t (tt->remote_netmask, 0, &gc))
+ );
+ }
+#else
+ argv_printf (&argv,
+ "%s %s 0.0.0.0",
+ IFCONFIG_PATH,
+ tt->actual_name
+ );
+#endif
+
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, NULL, 0, "Linux ip addr del failed");
+
+ argv_reset (&argv);
+ gc_free (&gc);
+ }
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+#if LINUX_IPV6
+ if (tt->ipv6)
+ {
+ struct tun_pi pi;
+ struct iphdr *iph;
+ struct iovec vect[2];
+ int ret;
+
+ iph = (struct iphdr *)buf;
+
+ pi.flags = 0;
+
+ if(iph->version == 6)
+ pi.proto = htons(ETH_P_IPV6);
+ else
+ pi.proto = htons(ETH_P_IP);
+
+ vect[0].iov_len = sizeof(pi);
+ vect[0].iov_base = &pi;
+ vect[1].iov_len = len;
+ vect[1].iov_base = buf;
+
+ ret = writev(tt->fd, vect, 2);
+ return(ret - sizeof(pi));
+ }
+ else
+#endif
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+#if LINUX_IPV6
+ if (tt->ipv6)
+ {
+ struct iovec vect[2];
+ struct tun_pi pi;
+ int ret;
+
+ vect[0].iov_len = sizeof(pi);
+ vect[0].iov_base = &pi;
+ vect[1].iov_len = len;
+ vect[1].iov_base = buf;
+
+ ret = readv(tt->fd, vect, 2);
+ return(ret - sizeof(pi));
+ }
+ else
+#endif
+ return read (tt->fd, buf, len);
+}
+
+#elif defined(TARGET_SOLARIS)
+
+#ifndef TUNNEWPPA
+#error I need the symbol TUNNEWPPA from net/if_tun.h
+#endif
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
+ struct lifreq ifr;
+ const char *ptr;
+ const char *ip_node, *arp_node;
+ const char *dev_tuntap_type;
+ int link_type;
+ bool is_tun;
+ struct strioctl strioc_if, strioc_ppa;
+
+ ipv6_support (ipv6, true, tt);
+ memset(&ifr, 0x0, sizeof(ifr));
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+ open_null (tt);
+ return;
+ }
+
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ ip_node = "/dev/udp";
+ if (!dev_node)
+ dev_node = "/dev/tun";
+ dev_tuntap_type = "tun";
+ link_type = I_PLINK;
+ is_tun = true;
+ }
+ else if (tt->type == DEV_TYPE_TAP)
+ {
+ ip_node = "/dev/udp";
+ if (!dev_node)
+ dev_node = "/dev/tap";
+ arp_node = dev_node;
+ dev_tuntap_type = "tap";
+ link_type = I_PLINK; /* was: I_LINK */
+ is_tun = false;
+ }
+ else
+ {
+ msg (M_FATAL, "I don't recognize device %s as a tun or tap device",
+ dev);
+ }
+
+ /* get unit number */
+ if (*dev)
+ {
+ ptr = dev;
+ while (*ptr && !isdigit ((int) *ptr))
+ ptr++;
+ ppa = atoi (ptr);
+ }
+
+ if ((tt->ip_fd = open (ip_node, O_RDWR, 0)) < 0)
+ msg (M_ERR, "Can't open %s", ip_node);
+
+ if ((tt->fd = open (dev_node, O_RDWR, 0)) < 0)
+ msg (M_ERR, "Can't open %s", dev_node);
+
+ /* Assign a new PPA and get its unit number. */
+ strioc_ppa.ic_cmd = TUNNEWPPA;
+ strioc_ppa.ic_timout = 0;
+ strioc_ppa.ic_len = sizeof(ppa);
+ strioc_ppa.ic_dp = (char *)&ppa;
+ if ((ppa = ioctl (tt->fd, I_STR, &strioc_ppa)) < 0)
+ msg (M_ERR, "Can't assign new interface");
+
+ if ((if_fd = open (dev_node, O_RDWR, 0)) < 0)
+ msg (M_ERR, "Can't open %s (2)", dev_node);
+
+ if (ioctl (if_fd, I_PUSH, "ip") < 0)
+ msg (M_ERR, "Can't push IP module");
+
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ /* Assign ppa according to the unit number returned by tun device */
+ if (ioctl (if_fd, IF_UNITSEL, (char *) &ppa) < 0)
+ msg (M_ERR, "Can't set PPA %d", ppa);
+ }
+
+ tt->actual_name = (char *) malloc (32);
+ check_malloc_return (tt->actual_name);
+
+ openvpn_snprintf (tt->actual_name, 32, "%s%d", dev_tuntap_type, ppa);
+
+ if (tt->type == DEV_TYPE_TAP)
+ {
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
+ msg (M_ERR, "Can't get flags\n");
+ strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
+ ifr.lifr_ppa = ppa;
+ /* Assign ppa according to the unit number returned by tun device */
+ if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
+ msg (M_ERR, "Can't set PPA %d", ppa);
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
+ msg (M_ERR, "Can't get flags\n");
+ /* Push arp module to if_fd */
+ if (ioctl (if_fd, I_PUSH, "arp") < 0)
+ msg (M_ERR, "Can't push ARP module");
+
+ /* Pop any modules on the stream */
+ while (true)
+ {
+ if (ioctl (tt->ip_fd, I_POP, NULL) < 0)
+ break;
+ }
+ /* Push arp module to ip_fd */
+ if (ioctl (tt->ip_fd, I_PUSH, "arp") < 0)
+ msg (M_ERR, "Can't push ARP module\n");
+
+ /* Open arp_fd */
+ if ((arp_fd = open (arp_node, O_RDWR, 0)) < 0)
+ msg (M_ERR, "Can't open %s\n", arp_node);
+ /* Push arp module to arp_fd */
+ if (ioctl (arp_fd, I_PUSH, "arp") < 0)
+ msg (M_ERR, "Can't push ARP module\n");
+
+ /* Set ifname to arp */
+ strioc_if.ic_cmd = SIOCSLIFNAME;
+ strioc_if.ic_timout = 0;
+ strioc_if.ic_len = sizeof(ifr);
+ strioc_if.ic_dp = (char *)&ifr;
+ if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
+ msg (M_ERR, "Can't set ifname to arp\n");
+ }
+ }
+
+ if ((ip_muxid = ioctl (tt->ip_fd, link_type, if_fd)) < 0)
+ msg (M_ERR, "Can't link %s device to IP", dev_tuntap_type);
+
+ if (tt->type == DEV_TYPE_TAP) {
+ if ((arp_muxid = ioctl (tt->ip_fd, link_type, arp_fd)) < 0)
+ msg (M_ERR, "Can't link %s device to ARP", dev_tuntap_type);
+ close (arp_fd);
+ }
+
+ CLEAR (ifr);
+ strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
+ ifr.lifr_ip_muxid = ip_muxid;
+ if (tt->type == DEV_TYPE_TAP) {
+ ifr.lifr_arp_muxid = arp_muxid;
+ }
+
+ if (ioctl (tt->ip_fd, SIOCSLIFMUXID, &ifr) < 0)
+ {
+ if (tt->type == DEV_TYPE_TAP)
+ {
+ ioctl (tt->ip_fd, I_PUNLINK , arp_muxid);
+ }
+ ioctl (tt->ip_fd, I_PUNLINK, ip_muxid);
+ msg (M_ERR, "Can't set multiplexor id");
+ }
+
+ set_nonblock (tt->fd);
+ set_cloexec (tt->fd);
+ set_cloexec (tt->ip_fd);
+
+ msg (M_INFO, "TUN/TAP device %s opened", tt->actual_name);
+}
+
+static void
+solaris_close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ if (tt->ip_fd >= 0)
+ {
+ struct lifreq ifr;
+ CLEAR (ifr);
+ strncpynt (ifr.lifr_name, tt->actual_name, sizeof (ifr.lifr_name));
+
+ if (ioctl (tt->ip_fd, SIOCGLIFFLAGS, &ifr) < 0)
+ msg (M_WARN | M_ERRNO, "Can't get iface flags");
+
+ if (ioctl (tt->ip_fd, SIOCGLIFMUXID, &ifr) < 0)
+ msg (M_WARN | M_ERRNO, "Can't get multiplexor id");
+
+ if (tt->type == DEV_TYPE_TAP)
+ {
+ if (ioctl (tt->ip_fd, I_PUNLINK, ifr.lifr_arp_muxid) < 0)
+ msg (M_WARN | M_ERRNO, "Can't unlink interface(arp)");
+ }
+
+ if (ioctl (tt->ip_fd, I_PUNLINK, ifr.lifr_ip_muxid) < 0)
+ msg (M_WARN | M_ERRNO, "Can't unlink interface(ip)");
+
+ close (tt->ip_fd);
+ tt->ip_fd = -1;
+ }
+
+ if (tt->fd >= 0)
+ {
+ close (tt->fd);
+ tt->fd = -1;
+ }
+ }
+}
+
+/*
+ * Close TUN device.
+ */
+void
+close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ solaris_close_tun (tt);
+
+ if (tt->actual_name)
+ free (tt->actual_name);
+
+ clear_tuntap (tt);
+ free (tt);
+ }
+}
+
+static void
+solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual)
+{
+ struct argv argv;
+ argv_init (&argv);
+
+ argv_printf (&argv,
+ "%s %s unplumb",
+ IFCONFIG_PATH,
+ actual);
+
+ argv_msg (M_INFO, &argv);
+ openvpn_execve_check (&argv, es, 0, "Solaris ifconfig unplumb failed");
+ close_tun (tt);
+ msg (M_FATAL, "Solaris ifconfig failed");
+ argv_reset (&argv);
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ struct strbuf sbuf;
+ sbuf.len = len;
+ sbuf.buf = (char *)buf;
+ return putmsg (tt->fd, NULL, &sbuf, 0) >= 0 ? sbuf.len : -1;
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ struct strbuf sbuf;
+ int f = 0;
+
+ sbuf.maxlen = len;
+ sbuf.buf = (char *)buf;
+ return getmsg (tt->fd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
+}
+
+#elif defined(TARGET_OPENBSD)
+
+#if !defined(HAVE_READV) || !defined(HAVE_WRITEV)
+#error openbsd build requires readv & writev library functions
+#endif
+
+/*
+ * OpenBSD has a slightly incompatible TUN device from
+ * the rest of the world, in that it prepends a
+ * uint32 to the beginning of the IP header
+ * to designate the protocol (why not just
+ * look at the version field in the IP header to
+ * determine v4 or v6?).
+ *
+ * We strip off this field on reads and
+ * put it back on writes.
+ *
+ * I have not tested TAP devices on OpenBSD,
+ * but I have conditionalized the special
+ * TUN handling code described above to
+ * go away for TAP devices.
+ */
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+
+ /* Enable multicast on the interface */
+ if (tt->fd >= 0)
+ {
+ struct tuninfo info;
+
+ if (ioctl (tt->fd, TUNGIFINFO, &info) < 0) {
+ msg (M_WARN | M_ERRNO, "Can't get interface info: %s",
+ strerror(errno));
+ }
+
+ info.flags |= IFF_MULTICAST;
+
+ if (ioctl (tt->fd, TUNSIFINFO, &info) < 0) {
+ msg (M_WARN | M_ERRNO, "Can't set interface info: %s",
+ strerror(errno));
+ }
+ }
+}
+
+void
+close_tun (struct tuntap* tt)
+{
+ if (tt)
+ {
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+static inline int
+openbsd_modify_read_write_return (int len)
+{
+ if (len > 0)
+ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
+ else
+ return len;
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+ struct ip *iph;
+
+ iph = (struct ip *) buf;
+
+ if (tt->ipv6 && iph->ip_v == 6)
+ type = htonl (AF_INET6);
+ else
+ type = htonl (AF_INET);
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return openbsd_modify_read_write_return (writev (tt->fd, iv, 2));
+ }
+ else
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return openbsd_modify_read_write_return (readv (tt->fd, iv, 2));
+ }
+ else
+ return read (tt->fd, buf, len);
+}
+
+#elif defined(TARGET_NETBSD)
+
+/*
+ * NetBSD does not support IPv6 on tun out of the box,
+ * but there exists a patch. When this patch is applied,
+ * only two things are left to openvpn:
+ * 1. Activate multicasting (this has already been done
+ * before by the kernel, but we make sure that nobody
+ * has deactivated multicasting inbetween.
+ * 2. Deactivate "link layer mode" (otherwise NetBSD
+ * prepends the address family to the packet, and we
+ * would run into the same trouble as with OpenBSD.
+ */
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+ if (tt->fd >= 0)
+ {
+ int i = IFF_POINTOPOINT|IFF_MULTICAST;
+ ioctl (tt->fd, TUNSIFMODE, &i); /* multicast on */
+ i = 0;
+ ioctl (tt->fd, TUNSLMODE, &i); /* link layer mode off */
+ }
+}
+
+void
+close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ return read (tt->fd, buf, len);
+}
+
+#elif defined(TARGET_FREEBSD)
+
+static inline int
+freebsd_modify_read_write_return (int len)
+{
+ if (len > 0)
+ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
+ else
+ return len;
+}
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+
+ if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
+ {
+ int i = 0;
+
+ i = tt->topology == TOP_SUBNET ? IFF_BROADCAST : IFF_POINTOPOINT;
+ i |= IFF_MULTICAST;
+ if (ioctl (tt->fd, TUNSIFMODE, &i) < 0) {
+ msg (M_WARN | M_ERRNO, "ioctl(TUNSIFMODE): %s", strerror(errno));
+ }
+ i = 1;
+ if (ioctl (tt->fd, TUNSIFHEAD, &i) < 0) {
+ msg (M_WARN | M_ERRNO, "ioctl(TUNSIFHEAD): %s", strerror(errno));
+ }
+ }
+}
+
+void
+close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+ struct ip *iph;
+
+ iph = (struct ip *) buf;
+
+ if (tt->ipv6 && iph->ip_v == 6)
+ type = htonl (AF_INET6);
+ else
+ type = htonl (AF_INET);
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return freebsd_modify_read_write_return (writev (tt->fd, iv, 2));
+ }
+ else
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return freebsd_modify_read_write_return (readv (tt->fd, iv, 2));
+ }
+ else
+ return read (tt->fd, buf, len);
+}
+
+#elif defined(TARGET_DRAGONFLY)
+
+static inline int
+dragonfly_modify_read_write_return (int len)
+{
+ if (len > 0)
+ return len > sizeof (u_int32_t) ? len - sizeof (u_int32_t) : 0;
+ else
+ return len;
+}
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+
+ if (tt->fd >= 0)
+ {
+ int i = 0;
+
+ /* Disable extended modes */
+ ioctl (tt->fd, TUNSLMODE, &i);
+ i = 1;
+ ioctl (tt->fd, TUNSIFHEAD, &i);
+ }
+}
+
+void
+close_tun (struct tuntap *tt)
+{
+ if (tt)
+ {
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+ struct ip *iph;
+
+ iph = (struct ip *) buf;
+
+ if (tt->ipv6 && iph->ip_v == 6)
+ type = htonl (AF_INET6);
+ else
+ type = htonl (AF_INET);
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return dragonfly_modify_read_write_return (writev (tt->fd, iv, 2));
+ }
+ else
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ u_int32_t type;
+ struct iovec iv[2];
+
+ iv[0].iov_base = (char *)&type;
+ iv[0].iov_len = sizeof (type);
+ iv[1].iov_base = buf;
+ iv[1].iov_len = len;
+
+ return dragonfly_modify_read_write_return (readv (tt->fd, iv, 2));
+ }
+ else
+ return read (tt->fd, buf, len);
+}
+
+#elif defined(WIN32)
+
+int
+tun_read_queue (struct tuntap *tt, int maxsize)
+{
+ if (tt->reads.iostate == IOSTATE_INITIAL)
+ {
+ DWORD len;
+ BOOL status;
+ int err;
+
+ /* reset buf to its initial state */
+ tt->reads.buf = tt->reads.buf_init;
+
+ len = maxsize ? maxsize : BLEN (&tt->reads.buf);
+ ASSERT (len <= BLEN (&tt->reads.buf));
+
+ /* the overlapped read will signal this event on I/O completion */
+ ASSERT (ResetEvent (tt->reads.overlapped.hEvent));
+
+ status = ReadFile(
+ tt->hand,
+ BPTR (&tt->reads.buf),
+ len,
+ &tt->reads.size,
+ &tt->reads.overlapped
+ );
+
+ if (status) /* operation completed immediately? */
+ {
+ /* since we got an immediate return, we must signal the event object ourselves */
+ ASSERT (SetEvent (tt->reads.overlapped.hEvent));
+
+ tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
+ tt->reads.status = 0;
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read immediate return [%d,%d]",
+ (int) len,
+ (int) tt->reads.size);
+ }
+ else
+ {
+ err = GetLastError ();
+ if (err == ERROR_IO_PENDING) /* operation queued? */
+ {
+ tt->reads.iostate = IOSTATE_QUEUED;
+ tt->reads.status = err;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read queued [%d]",
+ (int) len);
+ }
+ else /* error occurred */
+ {
+ struct gc_arena gc = gc_new ();
+ ASSERT (SetEvent (tt->reads.overlapped.hEvent));
+ tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN;
+ tt->reads.status = err;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Read error [%d] : %s",
+ (int) len,
+ strerror_win32 (status, &gc));
+ gc_free (&gc);
+ }
+ }
+ }
+ return tt->reads.iostate;
+}
+
+int
+tun_write_queue (struct tuntap *tt, struct buffer *buf)
+{
+ if (tt->writes.iostate == IOSTATE_INITIAL)
+ {
+ BOOL status;
+ int err;
+
+ /* make a private copy of buf */
+ tt->writes.buf = tt->writes.buf_init;
+ tt->writes.buf.len = 0;
+ ASSERT (buf_copy (&tt->writes.buf, buf));
+
+ /* the overlapped write will signal this event on I/O completion */
+ ASSERT (ResetEvent (tt->writes.overlapped.hEvent));
+
+ status = WriteFile(
+ tt->hand,
+ BPTR (&tt->writes.buf),
+ BLEN (&tt->writes.buf),
+ &tt->writes.size,
+ &tt->writes.overlapped
+ );
+
+ if (status) /* operation completed immediately? */
+ {
+ tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
+
+ /* since we got an immediate return, we must signal the event object ourselves */
+ ASSERT (SetEvent (tt->writes.overlapped.hEvent));
+
+ tt->writes.status = 0;
+
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write immediate return [%d,%d]",
+ BLEN (&tt->writes.buf),
+ (int) tt->writes.size);
+ }
+ else
+ {
+ err = GetLastError ();
+ if (err == ERROR_IO_PENDING) /* operation queued? */
+ {
+ tt->writes.iostate = IOSTATE_QUEUED;
+ tt->writes.status = err;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write queued [%d]",
+ BLEN (&tt->writes.buf));
+ }
+ else /* error occurred */
+ {
+ struct gc_arena gc = gc_new ();
+ ASSERT (SetEvent (tt->writes.overlapped.hEvent));
+ tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN;
+ tt->writes.status = err;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Write error [%d] : %s",
+ BLEN (&tt->writes.buf),
+ strerror_win32 (err, &gc));
+ gc_free (&gc);
+ }
+ }
+ }
+ return tt->writes.iostate;
+}
+
+int
+tun_finalize (
+ HANDLE h,
+ struct overlapped_io *io,
+ struct buffer *buf)
+{
+ int ret = -1;
+ BOOL status;
+
+ switch (io->iostate)
+ {
+ case IOSTATE_QUEUED:
+ status = GetOverlappedResult(
+ h,
+ &io->overlapped,
+ &io->size,
+ FALSE
+ );
+ if (status)
+ {
+ /* successful return for a queued operation */
+ if (buf)
+ *buf = io->buf;
+ ret = io->size;
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion success [%d]", ret);
+ }
+ else
+ {
+ /* error during a queued operation */
+ ret = -1;
+ if (GetLastError() != ERROR_IO_INCOMPLETE)
+ {
+ /* if no error (i.e. just not finished yet),
+ then DON'T execute this code */
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+ msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: TAP Completion error");
+ }
+ }
+ break;
+
+ case IOSTATE_IMMEDIATE_RETURN:
+ io->iostate = IOSTATE_INITIAL;
+ ASSERT (ResetEvent (io->overlapped.hEvent));
+ if (io->status)
+ {
+ /* error return for a non-queued operation */
+ SetLastError (io->status);
+ ret = -1;
+ msg (D_WIN32_IO | M_ERRNO, "WIN32 I/O: TAP Completion non-queued error");
+ }
+ else
+ {
+ /* successful return for a non-queued operation */
+ if (buf)
+ *buf = io->buf;
+ ret = io->size;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion non-queued success [%d]", ret);
+ }
+ break;
+
+ case IOSTATE_INITIAL: /* were we called without proper queueing? */
+ SetLastError (ERROR_INVALID_FUNCTION);
+ ret = -1;
+ dmsg (D_WIN32_IO, "WIN32 I/O: TAP Completion BAD STATE");
+ break;
+
+ default:
+ ASSERT (0);
+ }
+
+ if (buf)
+ buf->len = ret;
+ return ret;
+}
+
+const struct tap_reg *
+get_tap_reg (struct gc_arena *gc)
+{
+ HKEY adapter_key;
+ LONG status;
+ DWORD len;
+ struct tap_reg *first = NULL;
+ struct tap_reg *last = NULL;
+ int i = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ ADAPTER_KEY,
+ 0,
+ KEY_READ,
+ &adapter_key);
+
+ if (status != ERROR_SUCCESS)
+ msg (M_FATAL, "Error opening registry key: %s", ADAPTER_KEY);
+
+ while (true)
+ {
+ char enum_name[256];
+ char unit_string[256];
+ HKEY unit_key;
+ char component_id_string[] = "ComponentId";
+ char component_id[256];
+ char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+ char net_cfg_instance_id[256];
+ DWORD data_type;
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ adapter_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS)
+ msg (M_FATAL, "Error enumerating registry subkeys of key: %s",
+ ADAPTER_KEY);
+
+ openvpn_snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+ ADAPTER_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ unit_string,
+ 0,
+ KEY_READ,
+ &unit_key);
+
+ if (status != ERROR_SUCCESS)
+ dmsg (D_REGISTRY, "Error opening registry key: %s", unit_string);
+ else
+ {
+ len = sizeof (component_id);
+ status = RegQueryValueEx(
+ unit_key,
+ component_id_string,
+ NULL,
+ &data_type,
+ component_id,
+ &len);
+
+ if (status != ERROR_SUCCESS || data_type != REG_SZ)
+ dmsg (D_REGISTRY, "Error opening registry key: %s\\%s",
+ unit_string, component_id_string);
+ else
+ {
+ len = sizeof (net_cfg_instance_id);
+ status = RegQueryValueEx(
+ unit_key,
+ net_cfg_instance_id_string,
+ NULL,
+ &data_type,
+ net_cfg_instance_id,
+ &len);
+
+ if (status == ERROR_SUCCESS && data_type == REG_SZ)
+ {
+ if (!strcmp (component_id, TAP_COMPONENT_ID))
+ {
+ struct tap_reg *reg;
+ ALLOC_OBJ_CLEAR_GC (reg, struct tap_reg, gc);
+ reg->guid = string_alloc (net_cfg_instance_id, gc);
+
+ /* link into return list */
+ if (!first)
+ first = reg;
+ if (last)
+ last->next = reg;
+ last = reg;
+ }
+ }
+ }
+ RegCloseKey (unit_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (adapter_key);
+ return first;
+}
+
+const struct panel_reg *
+get_panel_reg (struct gc_arena *gc)
+{
+ LONG status;
+ HKEY network_connections_key;
+ DWORD len;
+ struct panel_reg *first = NULL;
+ struct panel_reg *last = NULL;
+ int i = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ NETWORK_CONNECTIONS_KEY,
+ 0,
+ KEY_READ,
+ &network_connections_key);
+
+ if (status != ERROR_SUCCESS)
+ msg (M_FATAL, "Error opening registry key: %s", NETWORK_CONNECTIONS_KEY);
+
+ while (true)
+ {
+ char enum_name[256];
+ char connection_string[256];
+ HKEY connection_key;
+ char name_data[256];
+ DWORD name_type;
+ const char name_string[] = "Name";
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ network_connections_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS)
+ msg (M_FATAL, "Error enumerating registry subkeys of key: %s",
+ NETWORK_CONNECTIONS_KEY);
+
+ openvpn_snprintf (connection_string, sizeof(connection_string),
+ "%s\\%s\\Connection",
+ NETWORK_CONNECTIONS_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ connection_string,
+ 0,
+ KEY_READ,
+ &connection_key);
+
+ if (status != ERROR_SUCCESS)
+ dmsg (D_REGISTRY, "Error opening registry key: %s", connection_string);
+ else
+ {
+ len = sizeof (name_data);
+ status = RegQueryValueEx(
+ connection_key,
+ name_string,
+ NULL,
+ &name_type,
+ name_data,
+ &len);
+
+ if (status != ERROR_SUCCESS || name_type != REG_SZ)
+ dmsg (D_REGISTRY, "Error opening registry key: %s\\%s\\%s",
+ NETWORK_CONNECTIONS_KEY, connection_string, name_string);
+ else
+ {
+ struct panel_reg *reg;
+
+ ALLOC_OBJ_CLEAR_GC (reg, struct panel_reg, gc);
+ reg->name = string_alloc (name_data, gc);
+ reg->guid = string_alloc (enum_name, gc);
+
+ /* link into return list */
+ if (!first)
+ first = reg;
+ if (last)
+ last->next = reg;
+ last = reg;
+ }
+ RegCloseKey (connection_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (network_connections_key);
+
+ return first;
+}
+
+/*
+ * Check that two addresses are part of the same 255.255.255.252 subnet.
+ */
+void
+verify_255_255_255_252 (in_addr_t local, in_addr_t remote)
+{
+ struct gc_arena gc = gc_new ();
+ const unsigned int mask = 3;
+ const char *err = NULL;
+
+ if (local == remote)
+ {
+ err = "must be different";
+ goto error;
+ }
+ if ((local & (~mask)) != (remote & (~mask)))
+ {
+ err = "must exist within the same 255.255.255.252 subnet. This is a limitation of --dev tun when used with the TAP-WIN32 driver";
+ goto error;
+ }
+ if ((local & mask) == 0
+ || (local & mask) == 3
+ || (remote & mask) == 0
+ || (remote & mask) == 3)
+ {
+ err = "cannot use the first or last address within a given 255.255.255.252 subnet. This is a limitation of --dev tun when used with the TAP-WIN32 driver";
+ goto error;
+ }
+
+ gc_free (&gc);
+ return;
+
+ error:
+ msg (M_FATAL, "There is a problem in your selection of --ifconfig endpoints [local=%s, remote=%s]. The local and remote VPN endpoints %s. Try '" PACKAGE " --show-valid-subnets' option for more info.",
+ print_in_addr_t (local, 0, &gc),
+ print_in_addr_t (remote, 0, &gc),
+ err);
+ gc_free (&gc);
+}
+
+void show_valid_win32_tun_subnets (void)
+{
+ int i;
+ int col = 0;
+
+ printf ("On Windows, point-to-point IP support (i.e. --dev tun)\n");
+ printf ("is emulated by the TAP-Win32 driver. The major limitation\n");
+ printf ("imposed by this approach is that the --ifconfig local and\n");
+ printf ("remote endpoints must be part of the same 255.255.255.252\n");
+ printf ("subnet. The following list shows examples of endpoint\n");
+ printf ("pairs which satisfy this requirement. Only the final\n");
+ printf ("component of the IP address pairs is at issue.\n\n");
+ printf ("As an example, the following option would be correct:\n");
+ printf (" --ifconfig 10.7.0.5 10.7.0.6 (on host A)\n");
+ printf (" --ifconfig 10.7.0.6 10.7.0.5 (on host B)\n");
+ printf ("because [5,6] is part of the below list.\n\n");
+
+ for (i = 0; i < 256; i += 4)
+ {
+ printf("[%3d,%3d] ", i+1, i+2);
+ if (++col > 4)
+ {
+ col = 0;
+ printf ("\n");
+ }
+ }
+ if (col)
+ printf ("\n");
+}
+
+void
+show_tap_win32_adapters (int msglev, int warnlev)
+{
+ struct gc_arena gc = gc_new ();
+
+ bool warn_panel_null = false;
+ bool warn_panel_dup = false;
+ bool warn_tap_dup = false;
+
+ int links;
+
+ const struct tap_reg *tr;
+ const struct tap_reg *tr1;
+ const struct panel_reg *pr;
+
+ const struct tap_reg *tap_reg = get_tap_reg (&gc);
+ const struct panel_reg *panel_reg = get_panel_reg (&gc);
+
+ msg (msglev, "Available TAP-WIN32 adapters [name, GUID]:");
+
+ /* loop through each TAP-Win32 adapter registry entry */
+ for (tr = tap_reg; tr != NULL; tr = tr->next)
+ {
+ links = 0;
+
+ /* loop through each network connections entry in the control panel */
+ for (pr = panel_reg; pr != NULL; pr = pr->next)
+ {
+ if (!strcmp (tr->guid, pr->guid))
+ {
+ msg (msglev, "'%s' %s", pr->name, tr->guid);
+ ++links;
+ }
+ }
+
+ if (links > 1)
+ {
+ warn_panel_dup = true;
+ }
+ else if (links == 0)
+ {
+ /* a TAP adapter exists without a link from the network
+ connections control panel */
+ warn_panel_null = true;
+ msg (msglev, "[NULL] %s", tr->guid);
+ }
+ }
+
+ /* check for TAP-Win32 adapter duplicated GUIDs */
+ for (tr = tap_reg; tr != NULL; tr = tr->next)
+ {
+ for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next)
+ {
+ if (tr != tr1 && !strcmp (tr->guid, tr1->guid))
+ warn_tap_dup = true;
+ }
+ }
+
+ /* warn on registry inconsistencies */
+ if (warn_tap_dup)
+ msg (warnlev, "WARNING: Some TAP-Win32 adapters have duplicate GUIDs");
+
+ if (warn_panel_dup)
+ msg (warnlev, "WARNING: Some TAP-Win32 adapters have duplicate links from the Network Connections control panel");
+
+ if (warn_panel_null)
+ msg (warnlev, "WARNING: Some TAP-Win32 adapters have no link from the Network Connections control panel");
+
+ gc_free (&gc);
+}
+
+/*
+ * Confirm that GUID is a TAP-Win32 adapter.
+ */
+static bool
+is_tap_win32 (const char *guid, const struct tap_reg *tap_reg)
+{
+ const struct tap_reg *tr;
+
+ for (tr = tap_reg; tr != NULL; tr = tr->next)
+ {
+ if (guid && !strcmp (tr->guid, guid))
+ return true;
+ }
+
+ return false;
+}
+
+static const char *
+guid_to_name (const char *guid, const struct panel_reg *panel_reg)
+{
+ const struct panel_reg *pr;
+
+ for (pr = panel_reg; pr != NULL; pr = pr->next)
+ {
+ if (guid && !strcmp (pr->guid, guid))
+ return pr->name;
+ }
+
+ return NULL;
+}
+
+static const char *
+name_to_guid (const char *name, const struct tap_reg *tap_reg, const struct panel_reg *panel_reg)
+{
+ const struct panel_reg *pr;
+
+ for (pr = panel_reg; pr != NULL; pr = pr->next)
+ {
+ if (name && !strcmp (pr->name, name) && is_tap_win32 (pr->guid, tap_reg))
+ return pr->guid;
+ }
+
+ return NULL;
+}
+
+static void
+at_least_one_tap_win32 (const struct tap_reg *tap_reg)
+{
+ if (!tap_reg)
+ msg (M_FATAL, "There are no TAP-Win32 adapters on this system. You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> " PACKAGE_NAME " -> Add a new TAP-Win32 virtual ethernet adapter.");
+}
+
+/*
+ * Get an adapter GUID and optional actual_name from the
+ * registry for the TAP device # = device_number.
+ */
+static const char *
+get_unspecified_device_guid (const int device_number,
+ char *actual_name,
+ int actual_name_size,
+ const struct tap_reg *tap_reg_src,
+ const struct panel_reg *panel_reg_src,
+ struct gc_arena *gc)
+{
+ const struct tap_reg *tap_reg = tap_reg_src;
+ struct buffer ret = clear_buf ();
+ struct buffer actual = clear_buf ();
+ int i;
+
+ ASSERT (device_number >= 0);
+
+ /* Make sure we have at least one TAP adapter */
+ if (!tap_reg)
+ return NULL;
+
+ /* The actual_name output buffer may be NULL */
+ if (actual_name)
+ {
+ ASSERT (actual_name_size > 0);
+ buf_set_write (&actual, actual_name, actual_name_size);
+ }
+
+ /* Move on to specified device number */
+ for (i = 0; i < device_number; i++)
+ {
+ tap_reg = tap_reg->next;
+ if (!tap_reg)
+ return NULL;
+ }
+
+ /* Save Network Panel name (if exists) in actual_name */
+ if (actual_name)
+ {
+ const char *act = guid_to_name (tap_reg->guid, panel_reg_src);
+ if (act)
+ buf_printf (&actual, "%s", act);
+ else
+ buf_printf (&actual, "%s", tap_reg->guid);
+ }
+
+ /* Save GUID for return value */
+ ret = alloc_buf_gc (256, gc);
+ buf_printf (&ret, "%s", tap_reg->guid);
+ return BSTR (&ret);
+}
+
+/*
+ * Lookup a --dev-node adapter name in the registry
+ * returning the GUID and optional actual_name.
+ */
+static const char *
+get_device_guid (const char *name,
+ char *actual_name,
+ int actual_name_size,
+ const struct tap_reg *tap_reg,
+ const struct panel_reg *panel_reg,
+ struct gc_arena *gc)
+{
+ struct buffer ret = alloc_buf_gc (256, gc);
+ struct buffer actual = clear_buf ();
+
+ /* Make sure we have at least one TAP adapter */
+ if (!tap_reg)
+ return NULL;
+
+ /* The actual_name output buffer may be NULL */
+ if (actual_name)
+ {
+ ASSERT (actual_name_size > 0);
+ buf_set_write (&actual, actual_name, actual_name_size);
+ }
+
+ /* Check if GUID was explicitly specified as --dev-node parameter */
+ if (is_tap_win32 (name, tap_reg))
+ {
+ const char *act = guid_to_name (name, panel_reg);
+ buf_printf (&ret, "%s", name);
+ if (act)
+ buf_printf (&actual, "%s", act);
+ else
+ buf_printf (&actual, "%s", name);
+ return BSTR (&ret);
+ }
+
+ /* Lookup TAP adapter in network connections list */
+ {
+ const char *guid = name_to_guid (name, tap_reg, panel_reg);
+ if (guid)
+ {
+ buf_printf (&actual, "%s", name);
+ buf_printf (&ret, "%s", guid);
+ return BSTR (&ret);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Get adapter info list
+ */
+const IP_ADAPTER_INFO *
+get_adapter_info_list (struct gc_arena *gc)
+{
+ ULONG size = 0;
+ IP_ADAPTER_INFO *pi = NULL;
+ DWORD status;
+
+ if ((status = GetAdaptersInfo (NULL, &size)) != ERROR_BUFFER_OVERFLOW)
+ {
+ msg (M_INFO, "GetAdaptersInfo #1 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ else
+ {
+ pi = (PIP_ADAPTER_INFO) gc_malloc (size, false, gc);
+ if ((status = GetAdaptersInfo (pi, &size)) == NO_ERROR)
+ return pi;
+ else
+ {
+ msg (M_INFO, "GetAdaptersInfo #2 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ }
+ return pi;
+}
+
+const IP_PER_ADAPTER_INFO *
+get_per_adapter_info (const DWORD index, struct gc_arena *gc)
+{
+ ULONG size = 0;
+ IP_PER_ADAPTER_INFO *pi = NULL;
+ DWORD status;
+
+ if (index != ~0)
+ {
+ if ((status = GetPerAdapterInfo (index, NULL, &size)) != ERROR_BUFFER_OVERFLOW)
+ {
+ msg (M_INFO, "GetPerAdapterInfo #1 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ else
+ {
+ pi = (PIP_PER_ADAPTER_INFO) gc_malloc (size, false, gc);
+ if ((status = GetPerAdapterInfo ((ULONG)index, pi, &size)) == ERROR_SUCCESS)
+ return pi;
+ else
+ {
+ msg (M_INFO, "GetPerAdapterInfo #2 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ }
+ }
+ return pi;
+}
+
+static const IP_INTERFACE_INFO *
+get_interface_info_list (struct gc_arena *gc)
+{
+ ULONG size = 0;
+ IP_INTERFACE_INFO *ii = NULL;
+ DWORD status;
+
+ if ((status = GetInterfaceInfo (NULL, &size)) != ERROR_INSUFFICIENT_BUFFER)
+ {
+ msg (M_INFO, "GetInterfaceInfo #1 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ else
+ {
+ ii = (PIP_INTERFACE_INFO) gc_malloc (size, false, gc);
+ if ((status = GetInterfaceInfo (ii, &size)) == NO_ERROR)
+ return ii;
+ else
+ {
+ msg (M_INFO, "GetInterfaceInfo #2 failed (status=%u) : %s",
+ (unsigned int)status,
+ strerror_win32 (status, gc));
+ }
+ }
+ return ii;
+}
+
+static const IP_ADAPTER_INDEX_MAP *
+get_interface_info (DWORD index, struct gc_arena *gc)
+{
+ const IP_INTERFACE_INFO *list = get_interface_info_list (gc);
+ if (list)
+ {
+ int i;
+ for (i = 0; i < list->NumAdapters; ++i)
+ {
+ const IP_ADAPTER_INDEX_MAP *inter = &list->Adapter[i];
+ if (index == inter->Index)
+ return inter;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Given an adapter index, return a pointer to the
+ * IP_ADAPTER_INFO structure for that adapter.
+ */
+
+const IP_ADAPTER_INFO *
+get_adapter (const IP_ADAPTER_INFO *ai, DWORD index)
+{
+ if (ai && index != (DWORD)~0)
+ {
+ const IP_ADAPTER_INFO *a;
+
+ /* find index in the linked list */
+ for (a = ai; a != NULL; a = a->Next)
+ {
+ if (a->Index == index)
+ return a;
+ }
+ }
+ return NULL;
+}
+
+const IP_ADAPTER_INFO *
+get_adapter_info (DWORD index, struct gc_arena *gc)
+{
+ return get_adapter (get_adapter_info_list (gc), index);
+}
+
+static int
+get_adapter_n_ip_netmask (const IP_ADAPTER_INFO *ai)
+{
+ if (ai)
+ {
+ int n = 0;
+ const IP_ADDR_STRING *ip = &ai->IpAddressList;
+
+ while (ip)
+ {
+ ++n;
+ ip = ip->Next;
+ }
+ return n;
+ }
+ else
+ return 0;
+}
+
+static bool
+get_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const int n, in_addr_t *ip, in_addr_t *netmask)
+{
+ bool ret = false;
+ *ip = 0;
+ *netmask = 0;
+
+ if (ai)
+ {
+ const IP_ADDR_STRING *iplist = &ai->IpAddressList;
+ int i = 0;
+
+ while (iplist)
+ {
+ if (i == n)
+ break;
+ ++i;
+ iplist = iplist->Next;
+ }
+
+ if (iplist)
+ {
+ const unsigned int getaddr_flags = GETADDR_HOST_ORDER;
+ const char *ip_str = iplist->IpAddress.String;
+ const char *netmask_str = iplist->IpMask.String;
+ bool succeed1 = false;
+ bool succeed2 = false;
+
+ if (ip_str && netmask_str && strlen (ip_str) && strlen (netmask_str))
+ {
+ *ip = getaddr (getaddr_flags, ip_str, 0, &succeed1, NULL);
+ *netmask = getaddr (getaddr_flags, netmask_str, 0, &succeed2, NULL);
+ ret = (succeed1 == true && succeed2 == true);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool
+test_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const in_addr_t ip, const in_addr_t netmask)
+{
+ if (ai)
+ {
+ in_addr_t ip_adapter = 0;
+ in_addr_t netmask_adapter = 0;
+ const bool status = get_adapter_ip_netmask (ai, 0, &ip_adapter, &netmask_adapter);
+ return (status && ip_adapter == ip && netmask_adapter == netmask);
+ }
+ else
+ return false;
+}
+
+const IP_ADAPTER_INFO *
+get_tun_adapter (const struct tuntap *tt, const IP_ADAPTER_INFO *list)
+{
+ if (list && tt)
+ return get_adapter (list, tt->adapter_index);
+ else
+ return NULL;
+}
+
+bool
+is_adapter_up (const struct tuntap *tt, const IP_ADAPTER_INFO *list)
+{
+ int i;
+ bool ret = false;
+
+ const IP_ADAPTER_INFO *ai = get_tun_adapter (tt, list);
+
+ if (ai)
+ {
+ const int n = get_adapter_n_ip_netmask (ai);
+
+ /* loop once for every IP/netmask assigned to adapter */
+ for (i = 0; i < n; ++i)
+ {
+ in_addr_t ip, netmask;
+ if (get_adapter_ip_netmask (ai, i, &ip, &netmask))
+ {
+ if (tt->local && tt->adapter_netmask)
+ {
+ /* wait for our --ifconfig parms to match the actual adapter parms */
+ if (tt->local == ip && tt->adapter_netmask == netmask)
+ ret = true;
+ }
+ else
+ {
+ /* --ifconfig was not defined, maybe using a real DHCP server */
+ if (ip && netmask)
+ ret = true;
+ }
+ }
+ }
+ }
+ else
+ ret = true; /* this can occur when TAP adapter is bridged */
+
+ return ret;
+}
+
+bool
+is_ip_in_adapter_subnet (const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask)
+{
+ int i;
+ bool ret = false;
+
+ if (highest_netmask)
+ *highest_netmask = 0;
+
+ if (ai)
+ {
+ const int n = get_adapter_n_ip_netmask (ai);
+ for (i = 0; i < n; ++i)
+ {
+ in_addr_t adapter_ip, adapter_netmask;
+ if (get_adapter_ip_netmask (ai, i, &adapter_ip, &adapter_netmask))
+ {
+ if (adapter_ip && adapter_netmask && (ip & adapter_netmask) == (adapter_ip & adapter_netmask))
+ {
+ if (highest_netmask && adapter_netmask > *highest_netmask)
+ *highest_netmask = adapter_netmask;
+ ret = true;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+DWORD
+adapter_index_of_ip (const IP_ADAPTER_INFO *list,
+ const in_addr_t ip,
+ int *count,
+ in_addr_t *netmask)
+{
+ struct gc_arena gc = gc_new ();
+ DWORD ret = ~0;
+ in_addr_t highest_netmask = 0;
+ bool first = true;
+
+ if (count)
+ *count = 0;
+
+ while (list)
+ {
+ in_addr_t hn;
+
+ if (is_ip_in_adapter_subnet (list, ip, &hn))
+ {
+ if (first || hn > highest_netmask)
+ {
+ highest_netmask = hn;
+ if (count)
+ *count = 1;
+ ret = list->Index;
+ first = false;
+ }
+ else if (hn == highest_netmask)
+ {
+ if (count)
+ ++*count;
+ }
+ }
+ list = list->Next;
+ }
+
+ dmsg (D_ROUTE_DEBUG, "DEBUG: IP Locate: ip=%s nm=%s index=%d count=%d",
+ print_in_addr_t (ip, 0, &gc),
+ print_in_addr_t (highest_netmask, 0, &gc),
+ (int)ret,
+ count ? *count : -1);
+
+ if (ret == ~0 && count)
+ *count = 0;
+
+ if (netmask)
+ *netmask = highest_netmask;
+
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Given an adapter index, return true if the adapter
+ * is DHCP disabled.
+ */
+
+#define DHCP_STATUS_UNDEF 0
+#define DHCP_STATUS_ENABLED 1
+#define DHCP_STATUS_DISABLED 2
+
+static int
+dhcp_status (DWORD index)
+{
+ struct gc_arena gc = gc_new ();
+ int ret = DHCP_STATUS_UNDEF;
+ if (index != ~0)
+ {
+ const IP_ADAPTER_INFO *ai = get_adapter_info (index, &gc);
+
+ if (ai)
+ {
+ if (ai->DhcpEnabled)
+ ret = DHCP_STATUS_ENABLED;
+ else
+ ret = DHCP_STATUS_DISABLED;
+ }
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+/*
+ * Delete all temporary address/netmask pairs which were added
+ * to adapter (given by index) by previous calls to AddIPAddress.
+ */
+static void
+delete_temp_addresses (DWORD index)
+{
+ struct gc_arena gc = gc_new ();
+ const IP_ADAPTER_INFO *a = get_adapter_info (index, &gc);
+
+ if (a)
+ {
+ const IP_ADDR_STRING *ip = &a->IpAddressList;
+ while (ip)
+ {
+ DWORD status;
+ const DWORD context = ip->Context;
+
+ if ((status = DeleteIPAddress ((ULONG) context)) == NO_ERROR)
+ {
+ msg (M_INFO, "Successfully deleted previously set dynamic IP/netmask: %s/%s",
+ ip->IpAddress.String,
+ ip->IpMask.String);
+ }
+ else
+ {
+ const char *empty = "0.0.0.0";
+ if (strcmp (ip->IpAddress.String, empty)
+ || strcmp (ip->IpMask.String, empty))
+ msg (M_INFO, "NOTE: could not delete previously set dynamic IP/netmask: %s/%s (status=%u)",
+ ip->IpAddress.String,
+ ip->IpMask.String,
+ (unsigned int)status);
+ }
+ ip = ip->Next;
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Get interface index for use with IP Helper API functions.
+ */
+static DWORD
+get_adapter_index_method_1 (const char *guid)
+{
+ struct gc_arena gc = gc_new ();
+ ULONG index = ~0;
+ DWORD status;
+ wchar_t wbuf[256];
+ snwprintf (wbuf, SIZE (wbuf), L"\\DEVICE\\TCPIP_%S", guid);
+ wbuf [SIZE(wbuf) - 1] = 0;
+ if ((status = GetAdapterIndex (wbuf, &index)) != NO_ERROR)
+ index = ~0;
+ gc_free (&gc);
+ return index;
+}
+
+static DWORD
+get_adapter_index_method_2 (const char *guid)
+{
+ struct gc_arena gc = gc_new ();
+ DWORD index = ~0;
+
+ const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
+
+ while (list)
+ {
+ if (!strcmp (guid, list->AdapterName))
+ {
+ index = list->Index;
+ break;
+ }
+ list = list->Next;
+ }
+
+ gc_free (&gc);
+ return index;
+}
+
+static DWORD
+get_adapter_index (const char *guid)
+{
+ DWORD index;
+ index = get_adapter_index_method_1 (guid);
+ if (index == ~0)
+ index = get_adapter_index_method_2 (guid);
+ if (index == ~0)
+ msg (M_INFO, "NOTE: could not get adapter index for %s", guid);
+ return index;
+}
+
+static DWORD
+get_adapter_index_flexible (const char *name) /* actual name or GUID */
+{
+ struct gc_arena gc = gc_new ();
+ DWORD index;
+ index = get_adapter_index_method_1 (name);
+ if (index == ~0)
+ index = get_adapter_index_method_2 (name);
+ if (index == ~0)
+ {
+ const struct tap_reg *tap_reg = get_tap_reg (&gc);
+ const struct panel_reg *panel_reg = get_panel_reg (&gc);
+ const char *guid = name_to_guid (name, tap_reg, panel_reg);
+ index = get_adapter_index_method_1 (guid);
+ if (index == ~0)
+ index = get_adapter_index_method_2 (guid);
+ }
+ if (index == ~0)
+ msg (M_INFO, "NOTE: could not get adapter index for name/GUID '%s'", name);
+ gc_free (&gc);
+ return index;
+}
+
+/*
+ * Return a string representing a PIP_ADDR_STRING
+ */
+static const char *
+format_ip_addr_string (const IP_ADDR_STRING *ip, struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ while (ip)
+ {
+ buf_printf (&out, "%s", ip->IpAddress.String);
+ if (strlen (ip->IpMask.String))
+ {
+ buf_printf (&out, "/");
+ buf_printf (&out, "%s", ip->IpMask.String);
+ }
+ buf_printf (&out, " ");
+ ip = ip->Next;
+ }
+ return BSTR (&out);
+}
+
+/*
+ * Show info for a single adapter
+ */
+static void
+show_adapter (int msglev, const IP_ADAPTER_INFO *a, struct gc_arena *gc)
+{
+ msg (msglev, "%s", a->Description);
+ msg (msglev, " Index = %d", (int)a->Index);
+ msg (msglev, " GUID = %s", a->AdapterName);
+ msg (msglev, " IP = %s", format_ip_addr_string (&a->IpAddressList, gc));
+ msg (msglev, " MAC = %s", format_hex_ex (a->Address, a->AddressLength, 0, 1, ":", gc));
+ msg (msglev, " GATEWAY = %s", format_ip_addr_string (&a->GatewayList, gc));
+ if (a->DhcpEnabled)
+ {
+ msg (msglev, " DHCP SERV = %s", format_ip_addr_string (&a->DhcpServer, gc));
+ msg (msglev, " DHCP LEASE OBTAINED = %s", time_string (a->LeaseObtained, 0, false, gc));
+ msg (msglev, " DHCP LEASE EXPIRES = %s", time_string (a->LeaseExpires, 0, false, gc));
+ }
+ if (a->HaveWins)
+ {
+ msg (msglev, " PRI WINS = %s", format_ip_addr_string (&a->PrimaryWinsServer, gc));
+ msg (msglev, " SEC WINS = %s", format_ip_addr_string (&a->SecondaryWinsServer, gc));
+ }
+
+ {
+ const IP_PER_ADAPTER_INFO *pai = get_per_adapter_info (a->Index, gc);
+ if (pai)
+ {
+ msg (msglev, " DNS SERV = %s", format_ip_addr_string (&pai->DnsServerList, gc));
+ }
+ }
+}
+
+/*
+ * Show current adapter list
+ */
+void
+show_adapters (int msglev)
+{
+ struct gc_arena gc = gc_new ();
+ const IP_ADAPTER_INFO *ai = get_adapter_info_list (&gc);
+
+ msg (msglev, "SYSTEM ADAPTER LIST");
+ if (ai)
+ {
+ const IP_ADAPTER_INFO *a;
+
+ /* find index in the linked list */
+ for (a = ai; a != NULL; a = a->Next)
+ {
+ show_adapter (msglev, a, &gc);
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Set a particular TAP-Win32 adapter (or all of them if
+ * adapter_name == NULL) to allow it to be opened from
+ * a non-admin account. This setting will only persist
+ * for the lifetime of the device object.
+ */
+
+static void
+tap_allow_nonadmin_access_handle (const char *device_path, HANDLE hand)
+{
+ struct security_attributes sa;
+ BOOL status;
+
+ if (!init_security_attributes_allow_all (&sa))
+ msg (M_ERR, "Error: init SA failed");
+
+ status = SetKernelObjectSecurity (hand, DACL_SECURITY_INFORMATION, &sa.sd);
+ if (!status)
+ {
+ msg (M_ERRNO, "Error: SetKernelObjectSecurity failed on %s", device_path);
+ }
+ else
+ {
+ msg (M_INFO|M_NOPREFIX, "TAP-Win32 device: %s [Non-admin access allowed]", device_path);
+ }
+}
+
+void
+tap_allow_nonadmin_access (const char *dev_node)
+{
+ struct gc_arena gc = gc_new ();
+ const struct tap_reg *tap_reg = get_tap_reg (&gc);
+ const struct panel_reg *panel_reg = get_panel_reg (&gc);
+ const char *device_guid = NULL;
+ HANDLE hand;
+ char actual_buffer[256];
+ char device_path[256];
+
+ at_least_one_tap_win32 (tap_reg);
+
+ if (dev_node)
+ {
+ /* Get the device GUID for the device specified with --dev-node. */
+ device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
+
+ if (!device_guid)
+ msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
+
+ /* Open Windows TAP-Win32 adapter */
+ openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ hand = CreateFile (
+ device_path,
+ MAXIMUM_ALLOWED,
+ 0, /* was: FILE_SHARE_READ */
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0
+ );
+
+ if (hand == INVALID_HANDLE_VALUE)
+ msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
+
+ tap_allow_nonadmin_access_handle (device_path, hand);
+ CloseHandle (hand);
+ }
+ else
+ {
+ int device_number = 0;
+
+ /* Try opening all TAP devices */
+ while (true)
+ {
+ device_guid = get_unspecified_device_guid (device_number,
+ actual_buffer,
+ sizeof (actual_buffer),
+ tap_reg,
+ panel_reg,
+ &gc);
+
+ if (!device_guid)
+ break;
+
+ /* Open Windows TAP-Win32 adapter */
+ openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ hand = CreateFile (
+ device_path,
+ MAXIMUM_ALLOWED,
+ 0, /* was: FILE_SHARE_READ */
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0
+ );
+
+ if (hand == INVALID_HANDLE_VALUE)
+ msg (M_WARN, "CreateFile failed on TAP device: %s", device_path);
+ else
+ {
+ tap_allow_nonadmin_access_handle (device_path, hand);
+ CloseHandle (hand);
+ }
+
+ device_number++;
+ }
+ }
+ gc_free (&gc);
+}
+
+/*
+ * DHCP release/renewal
+ */
+bool
+dhcp_release_by_adapter_index(const DWORD adapter_index)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+ const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc);
+
+ if (inter)
+ {
+ DWORD status = IpReleaseAddress ((IP_ADAPTER_INDEX_MAP *)inter);
+ if (status == NO_ERROR)
+ {
+ msg (D_TUNTAP_INFO, "TAP: DHCP address released");
+ ret = true;
+ }
+ else
+ msg (M_WARN, "NOTE: Release of DHCP-assigned IP address lease on TAP-Win32 adapter failed: %s (code=%u)",
+ strerror_win32 (status, &gc),
+ (unsigned int)status);
+ }
+
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
+dhcp_release (const struct tuntap *tt)
+{
+ if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0)
+ return dhcp_release_by_adapter_index (tt->adapter_index);
+ else
+ return false;
+}
+
+bool
+dhcp_renew_by_adapter_index (const DWORD adapter_index)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+ const IP_ADAPTER_INDEX_MAP *inter = get_interface_info (adapter_index, &gc);
+
+ if (inter)
+ {
+ DWORD status = IpRenewAddress ((IP_ADAPTER_INDEX_MAP *)inter);
+ if (status == NO_ERROR)
+ {
+ msg (D_TUNTAP_INFO, "TAP: DHCP address renewal succeeded");
+ ret = true;
+ }
+ else
+ msg (M_WARN, "WARNING: Failed to renew DHCP IP address lease on TAP-Win32 adapter: %s (code=%u)",
+ strerror_win32 (status, &gc),
+ (unsigned int)status);
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
+dhcp_renew (const struct tuntap *tt)
+{
+ if (tt && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ && tt->adapter_index != ~0)
+ return dhcp_renew_by_adapter_index (tt->adapter_index);
+ else
+ return false;
+}
+
+/*
+ * netsh functions
+ */
+
+static void
+netsh_command (const struct argv *a, int n)
+{
+ int i;
+ for (i = 0; i < n; ++i)
+ {
+ bool status;
+ openvpn_sleep (1);
+ netcmd_semaphore_lock ();
+ argv_msg_prefix (M_INFO, a, "NETSH");
+ status = openvpn_execve_check (a, NULL, 0, "ERROR: netsh command failed");
+ netcmd_semaphore_release ();
+ if (status)
+ return;
+ openvpn_sleep (4);
+ }
+ msg (M_FATAL, "NETSH: command failed");
+}
+
+void
+ipconfig_register_dns (const struct env_set *es)
+{
+ struct argv argv;
+ bool status;
+ const char err[] = "ERROR: Windows ipconfig command failed";
+
+ msg (D_TUNTAP_INFO, "Start net commands...");
+ netcmd_semaphore_lock ();
+
+ argv_init (&argv);
+
+ argv_printf (&argv, "%s%sc stop dnscache",
+ get_win_sys_path(),
+ WIN_NET_PATH_SUFFIX);
+ argv_msg (D_TUNTAP_INFO, &argv);
+ status = openvpn_execve_check (&argv, es, 0, err);
+ argv_reset(&argv);
+
+ argv_printf (&argv, "%s%sc start dnscache",
+ get_win_sys_path(),
+ WIN_NET_PATH_SUFFIX);
+ argv_msg (D_TUNTAP_INFO, &argv);
+ status = openvpn_execve_check (&argv, es, 0, err);
+ argv_reset(&argv);
+
+ argv_printf (&argv, "%s%sc /flushdns",
+ get_win_sys_path(),
+ WIN_IPCONFIG_PATH_SUFFIX);
+ argv_msg (D_TUNTAP_INFO, &argv);
+ status = openvpn_execve_check (&argv, es, 0, err);
+ argv_reset(&argv);
+
+ argv_printf (&argv, "%s%sc /registerdns",
+ get_win_sys_path(),
+ WIN_IPCONFIG_PATH_SUFFIX);
+ argv_msg (D_TUNTAP_INFO, &argv);
+ status = openvpn_execve_check (&argv, es, 0, err);
+ argv_reset(&argv);
+
+ netcmd_semaphore_release ();
+ msg (D_TUNTAP_INFO, "End net commands...");
+}
+
+void
+ip_addr_string_to_array (in_addr_t *dest, int *dest_len, const IP_ADDR_STRING *src)
+{
+ int i = 0;
+ while (src)
+ {
+ const unsigned int getaddr_flags = GETADDR_HOST_ORDER;
+ const char *ip_str = src->IpAddress.String;
+ in_addr_t ip = 0;
+ bool succeed = false;
+
+ if (i >= *dest_len)
+ break;
+ if (!ip_str || !strlen (ip_str))
+ break;
+
+ ip = getaddr (getaddr_flags, ip_str, 0, &succeed, NULL);
+ if (!succeed)
+ break;
+ dest[i++] = ip;
+
+ src = src->Next;
+ }
+ *dest_len = i;
+
+#if 0
+ {
+ struct gc_arena gc = gc_new ();
+ msg (M_INFO, "ip_addr_string_to_array [%d]", *dest_len);
+ for (i = 0; i < *dest_len; ++i)
+ {
+ msg (M_INFO, "%s", print_in_addr_t (dest[i], 0, &gc));
+ }
+ gc_free (&gc);
+ }
+#endif
+}
+
+static bool
+ip_addr_one_to_one (const in_addr_t *a1, const int a1len, const IP_ADDR_STRING *ias)
+{
+ in_addr_t a2[8];
+ int a2len = SIZE(a2);
+ int i;
+
+ ip_addr_string_to_array (a2, &a2len, ias);
+ /*msg (M_INFO, "a1len=%d a2len=%d", a1len, a2len);*/
+ if (a1len != a2len)
+ return false;
+
+ for (i = 0; i < a1len; ++i)
+ {
+ if (a1[i] != a2[i])
+ return false;
+ }
+ return true;
+}
+
+static bool
+ip_addr_member_of (const in_addr_t addr, const IP_ADDR_STRING *ias)
+{
+ in_addr_t aa[8];
+ int len = SIZE(aa);
+ int i;
+
+ ip_addr_string_to_array (aa, &len, ias);
+ for (i = 0; i < len; ++i)
+ {
+ if (addr == aa[i])
+ return true;
+ }
+ return false;
+}
+
+static void
+netsh_ifconfig_options (const char *type,
+ const in_addr_t *addr_list,
+ const int addr_len,
+ const IP_ADDR_STRING *current,
+ const char *flex_name,
+ const bool test_first)
+{
+ struct gc_arena gc = gc_new ();
+ struct argv argv = argv_new ();
+ bool delete_first = false;
+
+ /* first check if we should delete existing DNS/WINS settings from TAP interface */
+ if (test_first)
+ {
+ if (!ip_addr_one_to_one (addr_list, addr_len, current))
+ delete_first = true;
+ }
+ else
+ delete_first = true;
+
+ /* delete existing DNS/WINS settings from TAP interface */
+ if (delete_first)
+ {
+ argv_printf (&argv, "%s%sc interface ip delete %s %s all",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ type,
+ flex_name);
+ netsh_command (&argv, 2);
+ }
+
+ /* add new DNS/WINS settings to TAP interface */
+ {
+ int count = 0;
+ int i;
+ for (i = 0; i < addr_len; ++i)
+ {
+ if (delete_first || !test_first || !ip_addr_member_of (addr_list[i], current))
+ {
+ const char *fmt = count ?
+ "%s%sc interface ip add %s %s %s"
+ : "%s%sc interface ip set %s %s static %s";
+
+ argv_printf (&argv, fmt,
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ type,
+ flex_name,
+ print_in_addr_t (addr_list[i], 0, &gc));
+ netsh_command (&argv, 2);
+
+ ++count;
+ }
+ else
+ {
+ msg (M_INFO, "NETSH: \"%s\" %s %s [already set]",
+ flex_name,
+ type,
+ print_in_addr_t (addr_list[i], 0, &gc));
+ }
+ }
+ }
+
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
+static void
+init_ip_addr_string2 (IP_ADDR_STRING *dest, const IP_ADDR_STRING *src1, const IP_ADDR_STRING *src2)
+{
+ CLEAR (dest[0]);
+ CLEAR (dest[1]);
+ if (src1)
+ {
+ dest[0] = *src1;
+ dest[0].Next = NULL;
+ }
+ if (src2)
+ {
+ dest[1] = *src2;
+ dest[0].Next = &dest[1];
+ dest[1].Next = NULL;
+ }
+}
+
+static void
+netsh_ifconfig (const struct tuntap_options *to,
+ const char *flex_name,
+ const in_addr_t ip,
+ const in_addr_t netmask,
+ const unsigned int flags)
+{
+ struct gc_arena gc = gc_new ();
+ struct argv argv = argv_new ();
+ const IP_ADAPTER_INFO *ai = NULL;
+ const IP_PER_ADAPTER_INFO *pai = NULL;
+
+ if (flags & NI_TEST_FIRST)
+ {
+ const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
+ const int index = get_adapter_index_flexible (flex_name);
+ ai = get_adapter (list, index);
+ pai = get_per_adapter_info (index, &gc);
+ }
+
+ if (flags & NI_IP_NETMASK)
+ {
+ if (test_adapter_ip_netmask (ai, ip, netmask))
+ {
+ msg (M_INFO, "NETSH: \"%s\" %s/%s [already set]",
+ flex_name,
+ print_in_addr_t (ip, 0, &gc),
+ print_in_addr_t (netmask, 0, &gc));
+ }
+ else
+ {
+ /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
+ argv_printf (&argv, "%s%sc interface ip set address %s static %s %s",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ flex_name,
+ print_in_addr_t (ip, 0, &gc),
+ print_in_addr_t (netmask, 0, &gc));
+
+ netsh_command (&argv, 4);
+ }
+ }
+
+ /* set WINS/DNS options */
+ if (flags & NI_OPTIONS)
+ {
+ IP_ADDR_STRING wins[2];
+ CLEAR (wins[0]);
+ CLEAR (wins[1]);
+
+ netsh_ifconfig_options ("dns",
+ to->dns,
+ to->dns_len,
+ pai ? &pai->DnsServerList : NULL,
+ flex_name,
+ BOOL_CAST (flags & NI_TEST_FIRST));
+ if (ai && ai->HaveWins)
+ init_ip_addr_string2 (wins, &ai->PrimaryWinsServer, &ai->SecondaryWinsServer);
+
+ netsh_ifconfig_options ("wins",
+ to->wins,
+ to->wins_len,
+ ai ? wins : NULL,
+ flex_name,
+ BOOL_CAST (flags & NI_TEST_FIRST));
+ }
+
+ argv_reset (&argv);
+ gc_free (&gc);
+}
+
+static void
+netsh_enable_dhcp (const struct tuntap_options *to,
+ const char *actual_name)
+{
+ struct argv argv;
+ argv_init (&argv);
+
+ /* example: netsh interface ip set address my-tap dhcp */
+ argv_printf (&argv,
+ "%s%sc interface ip set address %s dhcp",
+ get_win_sys_path(),
+ NETSH_PATH_SUFFIX,
+ actual_name);
+
+ netsh_command (&argv, 4);
+
+ argv_reset (&argv);
+}
+
+/*
+ * Return a TAP name for netsh commands.
+ */
+static const char *
+netsh_get_id (const char *dev_node, struct gc_arena *gc)
+{
+ const struct tap_reg *tap_reg = get_tap_reg (gc);
+ const struct panel_reg *panel_reg = get_panel_reg (gc);
+ struct buffer actual = alloc_buf_gc (256, gc);
+ const char *guid;
+
+ at_least_one_tap_win32 (tap_reg);
+
+ if (dev_node)
+ {
+ guid = get_device_guid (dev_node, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
+ }
+ else
+ {
+ guid = get_unspecified_device_guid (0, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
+
+ if (get_unspecified_device_guid (1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Win32 adapter */
+ guid = NULL;
+ }
+
+ if (!guid)
+ return "NULL"; /* not found */
+ else if (strcmp (BPTR (&actual), "NULL"))
+ return BPTR (&actual); /* control panel name */
+ else
+ return guid; /* no control panel name, return GUID instead */
+}
+
+/*
+ * Called iteratively on TAP-Win32 wait-for-initialization polling loop
+ */
+void
+tun_standby_init (struct tuntap *tt)
+{
+ tt->standby_iter = 0;
+}
+
+bool
+tun_standby (struct tuntap *tt)
+{
+ bool ret = true;
+ ++tt->standby_iter;
+ if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
+ {
+ if (tt->standby_iter == IPW32_SET_ADAPTIVE_TRY_NETSH)
+ {
+ msg (M_INFO, "NOTE: now trying netsh (this may take some time)");
+ netsh_ifconfig (&tt->options,
+ tt->actual_name,
+ tt->local,
+ tt->adapter_netmask,
+ NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
+ }
+ else if (tt->standby_iter >= IPW32_SET_ADAPTIVE_TRY_NETSH*2)
+ {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Convert DHCP options from the command line / config file
+ * into a raw DHCP-format options string.
+ */
+
+static void
+write_dhcp_u8 (struct buffer *buf, const int type, const int data, bool *error)
+{
+ if (!buf_safe (buf, 3))
+ {
+ *error = true;
+ msg (M_WARN, "write_dhcp_u8: buffer overflow building DHCP options");
+ return;
+ }
+ buf_write_u8 (buf, type);
+ buf_write_u8 (buf, 1);
+ buf_write_u8 (buf, data);
+}
+
+static void
+write_dhcp_u32_array (struct buffer *buf, const int type, const uint32_t *data, const unsigned int len, bool *error)
+{
+ if (len > 0)
+ {
+ int i;
+ const int size = len * sizeof (uint32_t);
+
+ if (!buf_safe (buf, 2 + size))
+ {
+ *error = true;
+ msg (M_WARN, "write_dhcp_u32_array: buffer overflow building DHCP options");
+ return;
+ }
+ if (size < 1 || size > 255)
+ {
+ *error = true;
+ msg (M_WARN, "write_dhcp_u32_array: size (%d) must be > 0 and <= 255", size);
+ return;
+ }
+ buf_write_u8 (buf, type);
+ buf_write_u8 (buf, size);
+ for (i = 0; i < len; ++i)
+ buf_write_u32 (buf, data[i]);
+ }
+}
+
+static void
+write_dhcp_str (struct buffer *buf, const int type, const char *str, bool *error)
+{
+ const int len = strlen (str);
+ if (!buf_safe (buf, 2 + len))
+ {
+ *error = true;
+ msg (M_WARN, "write_dhcp_str: buffer overflow building DHCP options");
+ return;
+ }
+ if (len < 1 || len > 255)
+ {
+ *error = true;
+ msg (M_WARN, "write_dhcp_str: string '%s' must be > 0 bytes and <= 255 bytes", str);
+ return;
+ }
+ buf_write_u8 (buf, type);
+ buf_write_u8 (buf, len);
+ buf_write (buf, str, len);
+}
+
+static bool
+build_dhcp_options_string (struct buffer *buf, const struct tuntap_options *o)
+{
+ bool error = false;
+ if (o->domain)
+ write_dhcp_str (buf, 15, o->domain, &error);
+
+ if (o->netbios_scope)
+ write_dhcp_str (buf, 47, o->netbios_scope, &error);
+
+ if (o->netbios_node_type)
+ write_dhcp_u8 (buf, 46, o->netbios_node_type, &error);
+
+ write_dhcp_u32_array (buf, 6, (uint32_t*)o->dns, o->dns_len, &error);
+ write_dhcp_u32_array (buf, 44, (uint32_t*)o->wins, o->wins_len, &error);
+ write_dhcp_u32_array (buf, 42, (uint32_t*)o->ntp, o->ntp_len, &error);
+ write_dhcp_u32_array (buf, 45, (uint32_t*)o->nbdd, o->nbdd_len, &error);
+
+ /* the MS DHCP server option 'Disable Netbios-over-TCP/IP
+ is implemented as vendor option 001, value 002.
+ A value of 001 means 'leave NBT alone' which is the default */
+ if (o->disable_nbt)
+ {
+ if (!buf_safe (buf, 8))
+ {
+ msg (M_WARN, "build_dhcp_options_string: buffer overflow building DHCP options");
+ return false;
+ }
+ buf_write_u8 (buf, 43);
+ buf_write_u8 (buf, 6); /* total length field */
+ buf_write_u8 (buf, 0x001);
+ buf_write_u8 (buf, 4); /* length of the vendor specified field */
+ buf_write_u32 (buf, 0x002);
+ }
+ return !error;
+}
+
+static void
+fork_dhcp_action (struct tuntap *tt)
+{
+ if (tt->options.dhcp_pre_release || tt->options.dhcp_renew)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer cmd = alloc_buf_gc (256, &gc);
+ const int verb = 3;
+ const int pre_sleep = 1;
+
+ buf_printf (&cmd, "openvpn --verb %d --tap-sleep %d", verb, pre_sleep);
+ if (tt->options.dhcp_pre_release)
+ buf_printf (&cmd, " --dhcp-pre-release");
+ if (tt->options.dhcp_renew)
+ buf_printf (&cmd, " --dhcp-renew");
+ buf_printf (&cmd, " --dhcp-internal %u", (unsigned int)tt->adapter_index);
+
+ fork_to_self (BSTR (&cmd));
+ gc_free (&gc);
+ }
+}
+
+void
+fork_register_dns_action (struct tuntap *tt)
+{
+ if (tt && tt->options.register_dns)
+ {
+ struct gc_arena gc = gc_new ();
+ struct buffer cmd = alloc_buf_gc (256, &gc);
+ const int verb = 3;
+
+ buf_printf (&cmd, "openvpn --verb %d --register-dns --rdns-internal", verb);
+ fork_to_self (BSTR (&cmd));
+ gc_free (&gc);
+ }
+}
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+ char device_path[256];
+ const char *device_guid = NULL;
+ DWORD len;
+ bool dhcp_masq = false;
+ bool dhcp_masq_post = false;
+
+ /*netcmd_semaphore_lock ();*/
+
+ ipv6_support (ipv6, false, tt);
+
+ if (tt->type == DEV_TYPE_NULL)
+ {
+ open_null (tt);
+ gc_free (&gc);
+ return;
+ }
+ else if (tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN)
+ {
+ ;
+ }
+ else
+ {
+ msg (M_FATAL|M_NOPREFIX, "Unknown virtual device type: '%s'", dev);
+ }
+
+ /*
+ * Lookup the device name in the registry, using the --dev-node high level name.
+ */
+ {
+ const struct tap_reg *tap_reg = get_tap_reg (&gc);
+ const struct panel_reg *panel_reg = get_panel_reg (&gc);
+ char actual_buffer[256];
+
+ at_least_one_tap_win32 (tap_reg);
+
+ if (dev_node)
+ {
+ /* Get the device GUID for the device specified with --dev-node. */
+ device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
+
+ if (!device_guid)
+ msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
+
+ /* Open Windows TAP-Win32 adapter */
+ openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ tt->hand = CreateFile (
+ device_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, /* was: FILE_SHARE_READ */
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0
+ );
+
+ if (tt->hand == INVALID_HANDLE_VALUE)
+ msg (M_ERR, "CreateFile failed on TAP device: %s", device_path);
+ }
+ else
+ {
+ int device_number = 0;
+
+ /* Try opening all TAP devices until we find one available */
+ while (true)
+ {
+ device_guid = get_unspecified_device_guid (device_number,
+ actual_buffer,
+ sizeof (actual_buffer),
+ tap_reg,
+ panel_reg,
+ &gc);
+
+ if (!device_guid)
+ msg (M_FATAL, "All TAP-Win32 adapters on this system are currently in use.");
+
+ /* Open Windows TAP-Win32 adapter */
+ openvpn_snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+
+ tt->hand = CreateFile (
+ device_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0, /* was: FILE_SHARE_READ */
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0
+ );
+
+ if (tt->hand == INVALID_HANDLE_VALUE)
+ msg (D_TUNTAP_INFO, "CreateFile failed on TAP device: %s", device_path);
+ else
+ break;
+
+ device_number++;
+ }
+ }
+
+ /* translate high-level device name into a device instance
+ GUID using the registry */
+ tt->actual_name = string_alloc (actual_buffer, NULL);
+ }
+
+ msg (M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path);
+ tt->adapter_index = get_adapter_index (device_guid);
+
+ /* get driver version info */
+ {
+ ULONG info[3];
+ CLEAR (info);
+ if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_VERSION,
+ &info, sizeof (info),
+ &info, sizeof (info), &len, NULL))
+ {
+ msg (D_TUNTAP_INFO, "TAP-Win32 Driver Version %d.%d %s",
+ (int) info[0],
+ (int) info[1],
+ (info[2] ? "(DEBUG)" : ""));
+
+ }
+ if (!(info[0] == TAP_WIN32_MIN_MAJOR && info[1] >= TAP_WIN32_MIN_MINOR))
+ msg (M_FATAL, "ERROR: This version of " PACKAGE_NAME " requires a TAP-Win32 driver that is at least version %d.%d -- If you recently upgraded your " PACKAGE_NAME " distribution, a reboot is probably required at this point to get Windows to see the new driver.",
+ TAP_WIN32_MIN_MAJOR,
+ TAP_WIN32_MIN_MINOR);
+ }
+
+ /* get driver MTU */
+ {
+ ULONG mtu;
+ if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_MTU,
+ &mtu, sizeof (mtu),
+ &mtu, sizeof (mtu), &len, NULL))
+ {
+ tt->post_open_mtu = (int) mtu;
+ msg (D_MTU_INFO, "TAP-Win32 MTU=%d", (int) mtu);
+ }
+ }
+
+ /*
+ * Preliminaries for setting TAP-Win32 adapter TCP/IP
+ * properties via --ip-win32 dynamic or --ip-win32 adaptive.
+ */
+ if (tt->did_ifconfig_setup)
+ {
+ if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
+ {
+ /*
+ * If adapter is set to non-DHCP, set to DHCP mode.
+ */
+ if (dhcp_status (tt->adapter_index) == DHCP_STATUS_DISABLED)
+ netsh_enable_dhcp (&tt->options, tt->actual_name);
+ dhcp_masq = true;
+ dhcp_masq_post = true;
+ }
+ else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
+ {
+ /*
+ * If adapter is set to non-DHCP, use netsh right away.
+ */
+ if (dhcp_status (tt->adapter_index) != DHCP_STATUS_ENABLED)
+ {
+ netsh_ifconfig (&tt->options,
+ tt->actual_name,
+ tt->local,
+ tt->adapter_netmask,
+ NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
+ }
+ else
+ {
+ dhcp_masq = true;
+ }
+ }
+ }
+
+ /* set point-to-point mode if TUN device */
+
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ if (!tt->did_ifconfig_setup)
+ {
+ msg (M_FATAL, "ERROR: --dev tun also requires --ifconfig");
+ }
+
+ if (tt->topology == TOP_SUBNET)
+ {
+ in_addr_t ep[3];
+ BOOL status;
+
+ ep[0] = htonl (tt->local);
+ ep[1] = htonl (tt->local & tt->remote_netmask);
+ ep[2] = htonl (tt->remote_netmask);
+
+ status = DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_TUN,
+ ep, sizeof (ep),
+ ep, sizeof (ep), &len, NULL);
+
+ msg (status ? M_INFO : M_FATAL, "Set TAP-Win32 TUN subnet mode network/local/netmask = %s/%s/%s [%s]",
+ print_in_addr_t (ep[1], IA_NET_ORDER, &gc),
+ print_in_addr_t (ep[0], IA_NET_ORDER, &gc),
+ print_in_addr_t (ep[2], IA_NET_ORDER, &gc),
+ status ? "SUCCEEDED" : "FAILED");
+
+ } else {
+
+ in_addr_t ep[2];
+ ep[0] = htonl (tt->local);
+ ep[1] = htonl (tt->remote_netmask);
+
+ if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_POINT_TO_POINT,
+ ep, sizeof (ep),
+ ep, sizeof (ep), &len, NULL))
+ msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set Point-to-Point mode, which is required for --dev tun");
+ }
+ }
+
+ /* should we tell the TAP-Win32 driver to masquerade as a DHCP server as a means
+ of setting the adapter address? */
+ if (dhcp_masq)
+ {
+ uint32_t ep[4];
+
+ /* We will answer DHCP requests with a reply to set IP/subnet to these values */
+ ep[0] = htonl (tt->local);
+ ep[1] = htonl (tt->adapter_netmask);
+
+ /* At what IP address should the DHCP server masquerade at? */
+ if (tt->type == DEV_TYPE_TUN)
+ {
+ if (tt->topology == TOP_SUBNET)
+ {
+ const in_addr_t netmask_inv = ~tt->remote_netmask;
+ ep[2] = netmask_inv ? htonl ((tt->local | netmask_inv) - 1) : 0;
+ }
+ else
+ ep[2] = htonl (tt->remote_netmask);
+
+ if (tt->options.dhcp_masq_custom_offset)
+ msg (M_WARN, "WARNING: because you are using '--dev tun' mode, the '--ip-win32 dynamic [offset]' option is ignoring the offset parameter");
+ }
+ else
+ {
+ in_addr_t dsa; /* DHCP server addr */
+
+ ASSERT (tt->type == DEV_TYPE_TAP);
+
+ if (tt->options.dhcp_masq_offset < 0)
+ dsa = (tt->local | (~tt->adapter_netmask)) + tt->options.dhcp_masq_offset;
+ else
+ dsa = (tt->local & tt->adapter_netmask) + tt->options.dhcp_masq_offset;
+
+ if (dsa == tt->local)
+ msg (M_FATAL, "ERROR: There is a clash between the --ifconfig local address and the internal DHCP server address -- both are set to %s -- please use the --ip-win32 dynamic option to choose a different free address from the --ifconfig subnet for the internal DHCP server", print_in_addr_t (dsa, 0, &gc));
+
+ if ((tt->local & tt->adapter_netmask) != (dsa & tt->adapter_netmask))
+ msg (M_FATAL, "ERROR: --tap-win32 dynamic [offset] : offset is outside of --ifconfig subnet");
+
+ ep[2] = htonl (dsa);
+ }
+
+ /* lease time in seconds */
+ ep[3] = (uint32_t) tt->options.dhcp_lease_time;
+
+ ASSERT (ep[3] > 0);
+
+#ifndef SIMULATE_DHCP_FAILED /* this code is disabled to simulate bad DHCP negotiation */
+ if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_DHCP_MASQ,
+ ep, sizeof (ep),
+ ep, sizeof (ep), &len, NULL))
+ msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a DeviceIoControl call to set TAP_IOCTL_CONFIG_DHCP_MASQ mode");
+
+ msg (M_INFO, "Notified TAP-Win32 driver to set a DHCP IP/netmask of %s/%s on interface %s [DHCP-serv: %s, lease-time: %d]",
+ print_in_addr_t (tt->local, 0, &gc),
+ print_in_addr_t (tt->adapter_netmask, 0, &gc),
+ device_guid,
+ print_in_addr_t (ep[2], IA_NET_ORDER, &gc),
+ ep[3]
+ );
+
+ /* user-supplied DHCP options capability */
+ if (tt->options.dhcp_options)
+ {
+ struct buffer buf = alloc_buf (256);
+ if (build_dhcp_options_string (&buf, &tt->options))
+ {
+ msg (D_DHCP_OPT, "DHCP option string: %s", format_hex (BPTR (&buf), BLEN (&buf), 0, &gc));
+ if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_DHCP_SET_OPT,
+ BPTR (&buf), BLEN (&buf),
+ BPTR (&buf), BLEN (&buf), &len, NULL))
+ msg (M_FATAL, "ERROR: The TAP-Win32 driver rejected a TAP_IOCTL_CONFIG_DHCP_SET_OPT DeviceIoControl call");
+ }
+ else
+ msg (M_WARN, "DHCP option string not set due to error");
+ free_buf (&buf);
+ }
+#endif
+ }
+
+ /* set driver media status to 'connected' */
+ {
+ ULONG status = TRUE;
+ if (!DeviceIoControl (tt->hand, TAP_IOCTL_SET_MEDIA_STATUS,
+ &status, sizeof (status),
+ &status, sizeof (status), &len, NULL))
+ msg (M_WARN, "WARNING: The TAP-Win32 driver rejected a TAP_IOCTL_SET_MEDIA_STATUS DeviceIoControl call.");
+ }
+
+ /* possible wait for adapter to come up */
+ {
+ int s = tt->options.tap_sleep;
+ if (s > 0)
+ {
+ msg (M_INFO, "Sleeping for %d seconds...", s);
+ openvpn_sleep (s);
+ }
+ }
+
+ /* possibly use IP Helper API to set IP address on adapter */
+ {
+ const DWORD index = tt->adapter_index;
+
+ /* flush arp cache */
+ if (index != (DWORD)~0)
+ {
+ DWORD status;
+
+ if ((status = FlushIpNetTable (index)) == NO_ERROR)
+ msg (M_INFO, "Successful ARP Flush on interface [%u] %s",
+ (unsigned int)index,
+ device_guid);
+ else
+ msg (D_TUNTAP_INFO, "NOTE: FlushIpNetTable failed on interface [%u] %s (status=%u) : %s",
+ (unsigned int)index,
+ device_guid,
+ (unsigned int)status,
+ strerror_win32 (status, &gc));
+ }
+
+ /*
+ * If the TAP-Win32 driver is masquerading as a DHCP server
+ * make sure the TCP/IP properties for the adapter are
+ * set correctly.
+ */
+ if (dhcp_masq_post)
+ {
+ /* check dhcp enable status */
+ if (dhcp_status (index) == DHCP_STATUS_DISABLED)
+ msg (M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
+
+ /* force an explicit DHCP lease renewal on TAP adapter? */
+ if (tt->options.dhcp_pre_release)
+ dhcp_release (tt);
+ if (tt->options.dhcp_renew)
+ dhcp_renew (tt);
+ }
+ else
+ fork_dhcp_action (tt);
+
+ if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_IPAPI)
+ {
+ DWORD status;
+ const char *error_suffix = "I am having trouble using the Windows 'IP helper API' to automatically set the IP address -- consider using other --ip-win32 methods (not 'ipapi')";
+
+ /* couldn't get adapter index */
+ if (index == (DWORD)~0)
+ {
+ msg (M_FATAL, "ERROR: unable to get adapter index for interface %s -- %s",
+ device_guid,
+ error_suffix);
+ }
+
+ /* check dhcp enable status */
+ if (dhcp_status (index) == DHCP_STATUS_DISABLED)
+ msg (M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
+
+ /* delete previously added IP addresses which were not
+ correctly deleted */
+ delete_temp_addresses (index);
+
+ /* add a new IP address */
+ if ((status = AddIPAddress (htonl(tt->local),
+ htonl(tt->adapter_netmask),
+ index,
+ &tt->ipapi_context,
+ &tt->ipapi_instance)) == NO_ERROR)
+ msg (M_INFO, "Succeeded in adding a temporary IP/netmask of %s/%s to interface %s using the Win32 IP Helper API",
+ print_in_addr_t (tt->local, 0, &gc),
+ print_in_addr_t (tt->adapter_netmask, 0, &gc),
+ device_guid
+ );
+ else
+ msg (M_FATAL, "ERROR: AddIPAddress %s/%s failed on interface %s, index=%d, status=%u (windows error: '%s') -- %s",
+ print_in_addr_t (tt->local, 0, &gc),
+ print_in_addr_t (tt->adapter_netmask, 0, &gc),
+ device_guid,
+ (int)index,
+ (unsigned int)status,
+ strerror_win32 (status, &gc),
+ error_suffix);
+ tt->ipapi_context_defined = true;
+ }
+ }
+ /*netcmd_semaphore_release ();*/
+ gc_free (&gc);
+}
+
+const char *
+tap_win32_getinfo (const struct tuntap *tt, struct gc_arena *gc)
+{
+ if (tt && tt->hand != NULL)
+ {
+ struct buffer out = alloc_buf_gc (256, gc);
+ DWORD len;
+ if (DeviceIoControl (tt->hand, TAP_IOCTL_GET_INFO,
+ BSTR (&out), BCAP (&out),
+ BSTR (&out), BCAP (&out),
+ &len, NULL))
+ {
+ return BSTR (&out);
+ }
+ }
+ return NULL;
+}
+
+void
+tun_show_debug (struct tuntap *tt)
+{
+ if (tt && tt->hand != NULL)
+ {
+ struct buffer out = alloc_buf (1024);
+ DWORD len;
+ while (DeviceIoControl (tt->hand, TAP_IOCTL_GET_LOG_LINE,
+ BSTR (&out), BCAP (&out),
+ BSTR (&out), BCAP (&out),
+ &len, NULL))
+ {
+ msg (D_TAP_WIN32_DEBUG, "TAP-Win32: %s", BSTR (&out));
+ }
+ free_buf (&out);
+ }
+}
+
+void
+close_tun (struct tuntap *tt)
+{
+ struct gc_arena gc = gc_new ();
+
+ if (tt)
+ {
+#if 1
+ if (tt->ipapi_context_defined)
+ {
+ DWORD status;
+ if ((status = DeleteIPAddress (tt->ipapi_context)) != NO_ERROR)
+ {
+ msg (M_WARN, "Warning: DeleteIPAddress[%u] failed on TAP-Win32 adapter, status=%u : %s",
+ (unsigned int)tt->ipapi_context,
+ (unsigned int)status,
+ strerror_win32 (status, &gc));
+ }
+ }
+#endif
+
+ if (tt->options.dhcp_release)
+ dhcp_release (tt);
+
+ if (tt->hand != NULL)
+ {
+ dmsg (D_WIN32_IO_LOW, "Attempting CancelIO on TAP-Win32 adapter");
+ if (!CancelIo (tt->hand))
+ msg (M_WARN | M_ERRNO, "Warning: CancelIO failed on TAP-Win32 adapter");
+ }
+
+ dmsg (D_WIN32_IO_LOW, "Attempting close of overlapped read event on TAP-Win32 adapter");
+ overlapped_io_close (&tt->reads);
+
+ dmsg (D_WIN32_IO_LOW, "Attempting close of overlapped write event on TAP-Win32 adapter");
+ overlapped_io_close (&tt->writes);
+
+ if (tt->hand != NULL)
+ {
+ dmsg (D_WIN32_IO_LOW, "Attempting CloseHandle on TAP-Win32 adapter");
+ if (!CloseHandle (tt->hand))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle failed on TAP-Win32 adapter");
+ }
+
+ if (tt->actual_name)
+ free (tt->actual_name);
+
+ clear_tuntap (tt);
+ free (tt);
+ }
+ gc_free (&gc);
+}
+
+/*
+ * Convert --ip-win32 constants between index and ascii form.
+ */
+
+struct ipset_names {
+ const char *short_form;
+};
+
+/* Indexed by IPW32_SET_x */
+static const struct ipset_names ipset_names[] = {
+ {"manual"},
+ {"netsh"},
+ {"ipapi"},
+ {"dynamic"},
+ {"adaptive"}
+};
+
+int
+ascii2ipset (const char* name)
+{
+ int i;
+ ASSERT (IPW32_SET_N == SIZE (ipset_names));
+ for (i = 0; i < IPW32_SET_N; ++i)
+ if (!strcmp (name, ipset_names[i].short_form))
+ return i;
+ return -1;
+}
+
+const char *
+ipset2ascii (int index)
+{
+ ASSERT (IPW32_SET_N == SIZE (ipset_names));
+ if (index < 0 || index >= IPW32_SET_N)
+ return "[unknown --ip-win32 type]";
+ else
+ return ipset_names[index].short_form;
+}
+
+const char *
+ipset2ascii_all (struct gc_arena *gc)
+{
+ struct buffer out = alloc_buf_gc (256, gc);
+ int i;
+
+ ASSERT (IPW32_SET_N == SIZE (ipset_names));
+ for (i = 0; i < IPW32_SET_N; ++i)
+ {
+ if (i)
+ buf_printf(&out, " ");
+ buf_printf(&out, "[%s]", ipset2ascii(i));
+ }
+ return BSTR (&out);
+}
+
+#else /* generic */
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+{
+ open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+}
+
+void
+close_tun (struct tuntap* tt)
+{
+ if (tt)
+ {
+ close_tun_generic (tt);
+ free (tt);
+ }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+ return read (tt->fd, buf, len);
+}
+
+#endif
diff --git a/tun.h b/tun.h
new file mode 100644
index 0000000..011ab54
--- /dev/null
+++ b/tun.h
@@ -0,0 +1,459 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TUN_H
+#define TUN_H
+
+#ifdef WIN32
+#include <winioctl.h>
+#include "tap-win32/common.h"
+#endif
+
+#include "buffer.h"
+#include "error.h"
+#include "mtu.h"
+#include "win32.h"
+#include "event.h"
+#include "proto.h"
+#include "misc.h"
+
+#ifdef WIN32
+
+/* time constants for --ip-win32 adaptive */
+#define IPW32_SET_ADAPTIVE_DELAY_WINDOW 300
+#define IPW32_SET_ADAPTIVE_TRY_NETSH 20
+
+struct tuntap_options {
+ /* --ip-win32 options */
+ bool ip_win32_defined;
+
+# define IPW32_SET_MANUAL 0 /* "--ip-win32 manual" */
+# define IPW32_SET_NETSH 1 /* "--ip-win32 netsh" */
+# define IPW32_SET_IPAPI 2 /* "--ip-win32 ipapi" */
+# define IPW32_SET_DHCP_MASQ 3 /* "--ip-win32 dynamic" */
+# define IPW32_SET_ADAPTIVE 4 /* "--ip-win32 adaptive" */
+# define IPW32_SET_N 5
+ int ip_win32_type;
+
+ /* --ip-win32 dynamic options */
+ bool dhcp_masq_custom_offset;
+ int dhcp_masq_offset;
+ int dhcp_lease_time;
+
+ /* --tap-sleep option */
+ int tap_sleep;
+
+ /* --dhcp-option options */
+
+ bool dhcp_options;
+
+ const char *domain; /* DOMAIN (15) */
+
+ const char *netbios_scope; /* NBS (47) */
+
+ int netbios_node_type; /* NBT 1,2,4,8 (46) */
+
+#define N_DHCP_ADDR 4 /* Max # of addresses allowed for
+ DNS, WINS, etc. */
+
+ /* DNS (6) */
+ in_addr_t dns[N_DHCP_ADDR];
+ int dns_len;
+
+ /* WINS (44) */
+ in_addr_t wins[N_DHCP_ADDR];
+ int wins_len;
+
+ /* NTP (42) */
+ in_addr_t ntp[N_DHCP_ADDR];
+ int ntp_len;
+
+ /* NBDD (45) */
+ in_addr_t nbdd[N_DHCP_ADDR];
+ int nbdd_len;
+
+ /* DISABLE_NBT (43, Vendor option 001) */
+ bool disable_nbt;
+
+ bool dhcp_renew;
+ bool dhcp_pre_release;
+ bool dhcp_release;
+
+ bool register_dns;
+};
+
+#elif TARGET_LINUX
+
+struct tuntap_options {
+ int txqueuelen;
+};
+
+#else
+
+struct tuntap_options {
+ int dummy; /* not used */
+};
+
+#endif
+
+/*
+ * Define a TUN/TAP dev.
+ */
+
+struct tuntap
+{
+# define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF)
+ int type; /* DEV_TYPE_x as defined in proto.h */
+
+# define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF)
+ int topology; /* one of the TOP_x values */
+
+ bool did_ifconfig_setup;
+ bool did_ifconfig;
+
+ bool ipv6;
+
+ struct tuntap_options options; /* options set on command line */
+
+ char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */
+
+ /* number of TX buffers */
+ int txqueuelen;
+
+ /* ifconfig parameters */
+ in_addr_t local;
+ in_addr_t remote_netmask;
+ in_addr_t broadcast;
+
+#ifdef WIN32
+ HANDLE hand;
+ struct overlapped_io reads;
+ struct overlapped_io writes;
+ struct rw_handle rw_handle;
+
+ /* used for setting interface address via IP Helper API
+ or DHCP masquerade */
+ bool ipapi_context_defined;
+ ULONG ipapi_context;
+ ULONG ipapi_instance;
+ in_addr_t adapter_netmask;
+
+ /* Windows adapter index for TAP-Win32 adapter,
+ ~0 if undefined */
+ DWORD adapter_index;
+
+ int standby_iter;
+#else
+ int fd; /* file descriptor for TUN/TAP dev */
+#endif
+
+#ifdef TARGET_SOLARIS
+ int ip_fd;
+#endif
+
+ /* used for printing status info only */
+ unsigned int rwflags_debug;
+
+ /* Some TUN/TAP drivers like to be ioctled for mtu
+ after open */
+ int post_open_mtu;
+};
+
+static inline bool
+tuntap_defined (const struct tuntap *tt)
+{
+#ifdef WIN32
+ return tt && tt->hand != NULL;
+#else
+ return tt && tt->fd >= 0;
+#endif
+}
+
+/*
+ * Function prototypes
+ */
+
+void clear_tuntap (struct tuntap *tuntap);
+
+void open_tun (const char *dev, const char *dev_type, const char *dev_node,
+ bool ipv6, struct tuntap *tt);
+
+void close_tun (struct tuntap *tt);
+
+int write_tun (struct tuntap* tt, uint8_t *buf, int len);
+
+int read_tun (struct tuntap* tt, uint8_t *buf, int len);
+
+void tuncfg (const char *dev, const char *dev_type, const char *dev_node,
+ bool ipv6, int persist_mode, const char *username,
+ const char *groupname, const struct tuntap_options *options);
+
+const char *guess_tuntap_dev (const char *dev,
+ const char *dev_type,
+ const char *dev_node,
+ struct gc_arena *gc);
+
+struct tuntap *init_tun (const char *dev, /* --dev option */
+ const char *dev_type, /* --dev-type option */
+ int topology, /* one of the TOP_x values */
+ const char *ifconfig_local_parm, /* --ifconfig parm 1 */
+ const char *ifconfig_remote_netmask_parm, /* --ifconfig parm 2 */
+ in_addr_t local_public,
+ in_addr_t remote_public,
+ const bool strict_warn,
+ struct env_set *es);
+
+void init_tun_post (struct tuntap *tt,
+ const struct frame *frame,
+ const struct tuntap_options *options);
+
+void do_ifconfig (struct tuntap *tt,
+ const char *actual, /* actual device name */
+ int tun_mtu,
+ const struct env_set *es);
+
+bool is_dev_type (const char *dev, const char *dev_type, const char *match_type);
+int dev_type_enum (const char *dev, const char *dev_type);
+const char *dev_type_string (const char *dev, const char *dev_type);
+
+const char *ifconfig_options_string (const struct tuntap* tt, bool remote, bool disable, struct gc_arena *gc);
+
+bool is_tun_p2p (const struct tuntap *tt);
+
+void check_subnet_conflict (const in_addr_t ip,
+ const in_addr_t netmask,
+ const char *prefix);
+
+void warn_on_use_of_common_subnets (void);
+
+/*
+ * Inline functions
+ */
+
+static inline void
+tun_adjust_frame_parameters (struct frame* frame, int size)
+{
+ frame_add_to_extra_tun (frame, size);
+}
+
+/*
+ * Should ifconfig be called before or after
+ * tun dev open?
+ */
+
+#define IFCONFIG_BEFORE_TUN_OPEN 0
+#define IFCONFIG_AFTER_TUN_OPEN 1
+
+#define IFCONFIG_DEFAULT IFCONFIG_AFTER_TUN_OPEN
+
+static inline int
+ifconfig_order(void)
+{
+#if defined(TARGET_LINUX)
+ return IFCONFIG_AFTER_TUN_OPEN;
+#elif defined(TARGET_SOLARIS)
+ return IFCONFIG_AFTER_TUN_OPEN;
+#elif defined(TARGET_OPENBSD)
+ return IFCONFIG_BEFORE_TUN_OPEN;
+#elif defined(TARGET_DARWIN)
+ return IFCONFIG_AFTER_TUN_OPEN;
+#elif defined(TARGET_NETBSD)
+ return IFCONFIG_AFTER_TUN_OPEN;
+#elif defined(WIN32)
+ return IFCONFIG_BEFORE_TUN_OPEN;
+#else
+ return IFCONFIG_DEFAULT;
+#endif
+}
+
+#ifdef WIN32
+
+#define TUN_PASS_BUFFER
+
+struct tap_reg
+{
+ const char *guid;
+ struct tap_reg *next;
+};
+
+struct panel_reg
+{
+ const char *name;
+ const char *guid;
+ struct panel_reg *next;
+};
+
+int ascii2ipset (const char* name);
+const char *ipset2ascii (int index);
+const char *ipset2ascii_all (struct gc_arena *gc);
+
+void verify_255_255_255_252 (in_addr_t local, in_addr_t remote);
+
+const IP_ADAPTER_INFO *get_adapter_info_list (struct gc_arena *gc);
+const IP_ADAPTER_INFO *get_tun_adapter (const struct tuntap *tt, const IP_ADAPTER_INFO *list);
+
+const IP_ADAPTER_INFO *get_adapter_info (DWORD index, struct gc_arena *gc);
+const IP_PER_ADAPTER_INFO *get_per_adapter_info (const DWORD index, struct gc_arena *gc);
+const IP_ADAPTER_INFO *get_adapter (const IP_ADAPTER_INFO *ai, DWORD index);
+
+bool is_adapter_up (const struct tuntap *tt, const IP_ADAPTER_INFO *list);
+bool is_ip_in_adapter_subnet (const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask);
+
+DWORD adapter_index_of_ip (const IP_ADAPTER_INFO *list,
+ const in_addr_t ip,
+ int *count,
+ in_addr_t *netmask);
+
+void show_tap_win32_adapters (int msglev, int warnlev);
+void show_adapters (int msglev);
+
+void tap_allow_nonadmin_access (const char *dev_node);
+
+void show_valid_win32_tun_subnets (void);
+const char *tap_win32_getinfo (const struct tuntap *tt, struct gc_arena *gc);
+void tun_show_debug (struct tuntap *tt);
+
+bool dhcp_release_by_adapter_index(const DWORD adapter_index);
+bool dhcp_renew_by_adapter_index (const DWORD adapter_index);
+
+void fork_register_dns_action (struct tuntap *tt);
+void ipconfig_register_dns (const struct env_set *es);
+
+void tun_standby_init (struct tuntap *tt);
+bool tun_standby (struct tuntap *tt);
+
+int tun_read_queue (struct tuntap *tt, int maxsize);
+int tun_write_queue (struct tuntap *tt, struct buffer *buf);
+int tun_finalize (HANDLE h, struct overlapped_io *io, struct buffer *buf);
+
+static inline bool
+tuntap_stop (int status)
+{
+ /*
+ * This corresponds to the STATUS_NO_SUCH_DEVICE
+ * error in tapdrvr.c.
+ */
+ if (status < 0)
+ {
+ return openvpn_errno () == ERROR_FILE_NOT_FOUND;
+ }
+ return false;
+}
+
+static inline int
+tun_write_win32 (struct tuntap *tt, struct buffer *buf)
+{
+ int err = 0;
+ int status = 0;
+ if (overlapped_io_active (&tt->writes))
+ {
+ status = tun_finalize (tt->hand, &tt->writes, NULL);
+ if (status < 0)
+ err = GetLastError ();
+ }
+ tun_write_queue (tt, buf);
+ if (status < 0)
+ {
+ SetLastError (err);
+ return status;
+ }
+ else
+ return BLEN (buf);
+}
+
+static inline int
+read_tun_buffered (struct tuntap *tt, struct buffer *buf, int maxsize)
+{
+ return tun_finalize (tt->hand, &tt->reads, buf);
+}
+
+static inline int
+write_tun_buffered (struct tuntap *tt, struct buffer *buf)
+{
+ return tun_write_win32 (tt, buf);
+}
+
+#else
+
+static inline bool
+tuntap_stop (int status)
+{
+ return false;
+}
+
+static inline void
+tun_standby_init (struct tuntap *tt)
+{
+}
+
+static inline bool
+tun_standby (struct tuntap *tt)
+{
+ return true;
+}
+
+#endif
+
+/*
+ * TUN/TAP I/O wait functions
+ */
+
+static inline event_t
+tun_event_handle (const struct tuntap *tt)
+{
+#ifdef WIN32
+ return &tt->rw_handle;
+#else
+ return tt->fd;
+#endif
+}
+
+static inline unsigned int
+tun_set (struct tuntap *tt,
+ struct event_set *es,
+ unsigned int rwflags,
+ void *arg,
+ unsigned int *persistent)
+{
+ if (tuntap_defined (tt))
+ {
+ /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
+ if (!persistent || *persistent != rwflags)
+ {
+ event_ctl (es, tun_event_handle (tt), rwflags, arg);
+ if (persistent)
+ *persistent = rwflags;
+ }
+#ifdef WIN32
+ if (rwflags & EVENT_READ)
+ tun_read_queue (tt, 0);
+#endif
+ tt->rwflags_debug = rwflags;
+ }
+ return rwflags;
+}
+
+const char *tun_stat (const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
+
+#endif /* TUN_H */
diff --git a/version.m4 b/version.m4
new file mode 100644
index 0000000..ff44b92
--- /dev/null
+++ b/version.m4
@@ -0,0 +1,6 @@
+dnl define the OpenVPN version
+define(PRODUCT_VERSION,[2.2.1])
+dnl define the TAP version
+define(PRODUCT_TAP_ID,[tap0901])
+define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])
+define(PRODUCT_TAP_WIN32_MIN_MINOR,[8])
diff --git a/win/__init__.py b/win/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/win/__init__.py
diff --git a/win/autodefs.h.in b/win/autodefs.h.in
new file mode 100644
index 0000000..ad0af19
--- /dev/null
+++ b/win/autodefs.h.in
@@ -0,0 +1,31 @@
+#ifndef AUTODEFS_H
+#define AUTODEFS_H
+
+/*
+ * Minimum TAP-Win32 version number expected by userspace
+ *
+ * The TAP-Win32 version number is defined in tap-win32/SOURCES
+ */
+#define TAP_ID "@PRODUCT_TAP_ID@"
+#define TAP_WIN32_MIN_MAJOR @PRODUCT_TAP_WIN32_MIN_MAJOR@
+#define TAP_WIN32_MIN_MINOR @PRODUCT_TAP_WIN32_MIN_MINOR@
+
+/* Friendly name for TAP driver */
+#define PRODUCT_TAP_DEVICE_DESCRIPTION "@PRODUCT_TAP_DEVICE_DESCRIPTION@"
+
+/* Version number of DDK/WDK used to build TAP driver */
+#define DDKVER_MAJOR @DDKVER_MAJOR@
+
+/* Name of package */
+#define PACKAGE "@PRODUCT_UNIX_NAME@"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "@PRODUCT_NAME@"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "@PRODUCT_UNIX_NAME@"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "@PRODUCT_VERSION@"
+
+#endif
diff --git a/win/build.py b/win/build.py
new file mode 100644
index 0000000..69137f2
--- /dev/null
+++ b/win/build.py
@@ -0,0 +1,23 @@
+import os, sys
+from wb import system, config, home_fn, cd_home, cd_service_win32, run_in_vs_shell
+
+def main():
+ """Build openvpn.exe and openvpnserv.exe"""
+ cd_home()
+ run_in_vs_shell("nmake /f %s" % (home_fn('msvc.mak'),))
+ cd_service_win32()
+ run_in_vs_shell("nmake /f %s" % ('msvc.mak'))
+
+def clean():
+ """Clean up after openvpn.exe and openvpnserv.exe build"""
+ cd_home()
+ run_in_vs_shell("nmake /f %s clean" % (home_fn('msvc.mak'),))
+ os.chdir("service-win32")
+ run_in_vs_shell("nmake /f %s clean" % ('msvc.mak'))
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ if len(sys.argv) == 2 and sys.argv[1] == 'clean':
+ clean()
+ else:
+ main()
diff --git a/win/build_all.py b/win/build_all.py
new file mode 100644
index 0000000..47716a1
--- /dev/null
+++ b/win/build_all.py
@@ -0,0 +1,69 @@
+import getopt, sys
+from config_all import main as config_all
+from build import main as build_openvpn
+from build_ddk import main as build_ddk
+from make_dist import main as make_dist
+
+def Usage():
+ '''Show usage information'''
+ print "Usage: build_all.py [OPTIONS]..."
+ print "Build OpenVPN using Visual Studio tools"
+ print
+ print " -h, --help Show this help"
+ print " -u, --unsigned Do not sign the TAP drivers"
+ print " -n, --notap Don't build the TAP driver"
+ sys.exit(1)
+
+def main(config):
+
+ # Do a signed build by default
+ signedBuild=True
+
+ # Build the TAP driver by default
+ tap=True
+
+ # Parse the command line argument(s)
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hun", ["help", "unsigned", "notap"])
+ except getopt.GetoptError:
+ Usage()
+
+ for o, a in opts:
+ if o in ("-h","--help"):
+ Usage()
+ if o in ("-u", "--unsigned"):
+ signedBuild=False
+ if o in ("-n", "--notap"):
+ tap=False
+
+ # Check if the SignTool module is present. This avoids ImportErrors popping
+ # up annoyingly _after_ the build.
+ if signedBuild:
+ try:
+ from signtool import SignTool
+ except (ImportError):
+ print "ERROR: SignTool python module not found! Can't do a signed build."
+ sys.exit(1)
+ else:
+ print "Doing an unsigned build as requested"
+
+ # Start the build
+ config_all(config)
+ build_openvpn()
+
+ if tap:
+ build_ddk(config, 'tap', 'all')
+ build_ddk(config, 'tapinstall', 'all')
+ if signedBuild:
+ sign(config, 'all')
+ make_dist(config,tap=True)
+
+ else:
+ if 'TAP_PREBUILT' in config:
+ print "Using prebuilt TAP driver"
+ make_dist(config,tap=False)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/build_ddk.py b/win/build_ddk.py
new file mode 100644
index 0000000..a197bf5
--- /dev/null
+++ b/win/build_ddk.py
@@ -0,0 +1,55 @@
+import os
+from wb import system, home_fn, choose_arch
+
+def build_ddk(config, dir, x64):
+ ddk_path = config['DDK_PATH']
+ ddk_major = int(config['DDKVER_MAJOR'])
+ debug = 'PRODUCT_TAP_DEBUG' in config
+ return build_tap(ddk_path, ddk_major, debug, dir, x64)
+
+def build_tap(ddk_path, ddk_major, debug, dir, x64):
+ """Build drivers using WinDDK tools"""
+ setenv_bat = os.path.realpath(os.path.join(ddk_path, 'bin/setenv.bat'))
+ target = 'chk' if debug else 'fre'
+ if x64:
+ target += ' x64'
+ else:
+ target += ' x86'
+ if ddk_major >= 7600:
+ if x64:
+ target += ' wlh' # vista
+ else:
+ target += ' wnet' # server 2003
+ else:
+ if x64:
+ target += ' wnet' # server 2003
+ else:
+ target += ' w2k' # 2000
+
+ system('cmd /c "%s %s %s && cd %s && build -cef"' % (
+ setenv_bat,
+ os.path.realpath(ddk_path),
+ target,
+ dir
+ ))
+
+def main(config, proj, arch):
+ if proj == 'tap':
+ dir = home_fn('tap-win32')
+ elif proj == 'tapinstall':
+ dir = home_fn('tapinstall')
+ else:
+ raise ValueError("unknown project: %s" % (proj,))
+
+ for x64 in choose_arch(arch):
+ build_ddk(config, dir, x64)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ import sys
+ from wb import config
+ if len(sys.argv) >= 3:
+ main(config, sys.argv[1], sys.argv[2])
+ else:
+ print "usage: build <tap|tapinstall> <x64|x86|all>"
+ sys.exit(2)
diff --git a/win/build_exe.py b/win/build_exe.py
new file mode 100644
index 0000000..dae4825
--- /dev/null
+++ b/win/build_exe.py
@@ -0,0 +1,15 @@
+from config import main as config_main
+from build import main as build_openvpn
+from build_ddk import main as build_ddk
+from sign import main as sign
+from make_dist import main as make_dist
+
+def main(config):
+ config_main(config)
+ build_openvpn()
+ make_dist(config, tap=False)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/config.h.in b/win/config.h.in
new file mode 100644
index 0000000..82344a0
--- /dev/null
+++ b/win/config.h.in
@@ -0,0 +1,323 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Configuration header for Win32 using the MSVC environment.
+ */
+
+#ifndef OPENVPN_CONFIG_H
+#define OPENVPN_CONFIG_H
+
+#include <windows.h>
+#include <winsock2.h>
+#include "autodefs.h" /* machine generated */
+
+//#define sleep(x) Sleep((x)*1000)
+
+//#define random rand
+//#define srandom srand
+
+typedef unsigned long in_addr_t;
+
+#ifndef _SSIZE_T_
+#define _SSIZE_T_
+ typedef unsigned int ssize_t;
+#endif
+
+/* Append a label to program startup title */
+/*#define DEBUG_LABEL "DEBUG1"*/
+
+/* Should we print debug info from driver? */
+#ifdef PRODUCT_TAP_DEBUG
+#define TAP_WIN32_DEBUG
+#endif
+
+/* Enable reading credentials from a file */
+#if @ENABLE_PASSWORD_SAVE@ != 0
+#define ENABLE_PASSWORD_SAVE @ENABLE_PASSWORD_SAVE@
+#endif
+
+/* Enable client/server capability */
+#if @ENABLE_CLIENT_SERVER@ != 0
+#define ENABLE_CLIENT_SERVER @ENABLE_CLIENT_SERVER@
+#endif
+
+/* Enable client capability only */
+#if @ENABLE_CLIENT_ONLY@ != 0
+#define ENABLE_CLIENT_ONLY @ENABLE_CLIENT_ONLY@
+#endif
+
+/* Enable management server capability */
+#if @ENABLE_MANAGEMENT@ != 0
+#define ENABLE_MANAGEMENT @ENABLE_MANAGEMENT@
+#endif
+
+/* Enable PKCS#11 support */
+/* #define USE_PKCS11 1 */
+
+/* Enable HTTP proxy support */
+#if @ENABLE_HTTP_PROXY@ != 0
+#define ENABLE_HTTP_PROXY @ENABLE_HTTP_PROXY@
+#endif
+
+/* Enable Socks proxy support */
+#if @ENABLE_SOCKS@ != 0
+#define ENABLE_SOCKS @ENABLE_SOCKS@
+#endif
+
+/* Enable internal fragmentation support */
+#if @ENABLE_FRAGMENT@ != 0
+#define ENABLE_FRAGMENT @ENABLE_FRAGMENT@
+#endif
+
+/* Enable smaller executable size */
+/* #undef ENABLE_SMALL */
+
+/* Enable debugging support */
+#if @ENABLE_DEBUG@ != 0
+#define ENABLE_DEBUG @ENABLE_DEBUG@
+#endif
+
+/* if defined, will allow usage of the --plugin directive */
+#define USE_LOAD_LIBRARY
+
+/* Dimension size to use for empty array declaration */
+#define EMPTY_ARRAY_SIZE 0
+
+/* Define to 1 if you have the `getsockname' function. */
+#define HAVE_GETSOCKNAME 1
+
+/* Define to 1 if you have the <openssl/engine.h> header file. */
+#define HAVE_OPENSSL_ENGINE_H 1
+
+/* 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 `ENGINE_cleanup' function. */
+#define HAVE_ENGINE_CLEANUP 1
+
+/* gettimeofday() is implemented in otime.c for Windows */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the 'chsize' function. */
+#define HAVE_CHSIZE 1
+
+/* Define to 1 if you have the `chdir' function. */
+#define HAVE_CHDIR 1
+
+/* Define to 1 if your compiler supports GNU GCC-style variadic macros */
+#ifndef _MSC_VER /* Defines MSFT compiler version. Defined as 1200 for MSVC++ 6.0. */
+#define HAVE_CPP_VARARG_MACRO_GCC 1
+#endif
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 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 <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getsockopt' function. */
+#define HAVE_GETSOCKOPT 1
+
+/* Define to 1 if you have the `inet_ntoa' function. */
+#define HAVE_INET_NTOA 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `setsockopt' function. */
+#define HAVE_SETSOCKOPT 1
+
+/* Define to 1 if you have the `socket' function. */
+#define HAVE_SOCKET 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. */
+#ifndef _MSC_VER
+#define HAVE_STDINT_H 1
+#endif
+
+/* 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 `strerror' function. */
+#define HAVE_STRERROR 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 `system' function. */
+#define HAVE_SYSTEM 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#ifndef _MSC_VER
+#define HAVE_SYS_FILE_H 1
+#endif
+
+/* 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. */
+#ifndef _MSC_VER
+#define HAVE_SYS_TIME_H 1
+#endif
+
+/* 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 `time' function. */
+#define HAVE_TIME 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#ifndef _MSC_VER
+#define HAVE_UNISTD_H 1
+#endif
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Special Windows version of getpass() defined in io.c */
+#define HAVE_GETPASS 1
+
+/* Define to the full name and version of this package. */
+#ifdef DEBUG_LABEL
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION " " DEBUG_LABEL
+#else
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
+#endif
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* The size of a `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of a `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* A string representing our target */
+#ifdef _MSC_VER
+#define TARGET_ALIAS "Win32-MSVC++"
+#else
+#define TARGET_ALIAS "Win32-MinGW"
+#endif
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#ifndef _MSC_VER
+#define TIME_WITH_SYS_TIME 1
+#endif
+
+/* Use OpenSSL crypto library */
+#define USE_CRYPTO 1
+
+/* Use LZO compression library */
+#define USE_LZO 1
+
+/* LZO version number */
+#define LZO_VERSION_NUM "2"
+
+/* Use lzo/ directory prefix for LZO header files (for LZO 2.0) */
+#define LZO_HEADER_DIR 1
+
+/* Use OpenSSL SSL library */
+#define USE_SSL 1
+
+/* Version number of package */
+#define VERSION PACKAGE_VERSION
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+ if it is not supported. */
+#define inline __inline
+
+/* type to use in place of socklen_t if not defined */
+#define socklen_t unsigned int
+
+#ifndef __MINGW32__
+/* 32-bit unsigned type */
+#define uint32_t unsigned int
+
+/* 16-bit unsigned type */
+#define uint16_t unsigned short
+
+/* 8-bit unsigned type */
+#define uint8_t unsigned char
+#endif /* __MINGW32__ */
+
+/* Route command */
+#define ROUTE_PATH "route"
+
+#ifdef _MSC_VER
+/* MSVC++ hacks */
+#pragma warning(disable:4244) // conversion from 'foo' to 'bar', possible loss of data
+#pragma warning(disable:4018) // signed/unsigned mismatch
+#include <io.h>
+#include <direct.h>
+//#define vsnprintf _vsnprintf
+//#define vsnwprintf _vsnwprintf
+#define snwprintf _snwprintf
+#define write _write
+#define open _open
+#define read _read
+#define close _close
+#define lseek _lseek
+#define chdir _chdir
+#define strdup _strdup
+#define strcasecmp _stricmp
+#define chsize _chsize
+#define S_IRUSR 0
+#define S_IWUSR 0
+#define TV_SEC_CAST (long)
+#define TV_USEC_CAST (long)
+typedef int intptr_t;
+/* Visual Studio 2005 supports vararg macros */
+#if _MSC_VER >= 1400
+#define HAVE_CPP_VARARG_MACRO_ISO 1
+#endif
+#endif
+
+#endif /* OPENVPN_CONFIG_H */
diff --git a/win/config.py b/win/config.py
new file mode 100644
index 0000000..b820510
--- /dev/null
+++ b/win/config.py
@@ -0,0 +1,21 @@
+from wb import preprocess, autogen, mod_fn, home_fn, build_config_h, build_configure_h, build_version_m4_vars, build_autodefs, make_headers_objs, dict_def
+
+def main(config):
+ build_config_h(config)
+ build_configure_h(config, mod_fn(home_fn('configure.h')), head_comment='/* %s */\n\n' % autogen)
+ build_version_m4_vars(mod_fn(mod_fn('version_m4_vars.tmp')), head_comment='/* %s */\n\n' % autogen)
+ build_autodefs(config, mod_fn('autodefs.h.in'), home_fn('autodefs.h'))
+ ho = make_headers_objs(home_fn('Makefile.am'))
+
+ preprocess(dict_def(config, [('HEADERS_OBJS', ho)]),
+ in_fn=mod_fn('msvc.mak.in'),
+ out_fn=home_fn('msvc.mak'),
+ quote_begin='@',
+ quote_end='@',
+ if_prefix='!',
+ head_comment='# %s\n\n' % autogen)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/config_all.py b/win/config_all.py
new file mode 100644
index 0000000..ba6affe
--- /dev/null
+++ b/win/config_all.py
@@ -0,0 +1,13 @@
+from config import main as config_main
+from config_tap import main as config_tap
+from config_ti import main as config_ti
+
+def main(config):
+ config_main(config)
+ config_tap(config)
+ config_ti(config)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/config_tap.py b/win/config_tap.py
new file mode 100644
index 0000000..a5b1d6c
--- /dev/null
+++ b/win/config_tap.py
@@ -0,0 +1,35 @@
+import os
+from wb import preprocess, home_fn, autogen, dict_def
+
+def main(config):
+ preprocess(config,
+ in_fn=home_fn('tap-win32/SOURCES.in'),
+ out_fn=home_fn('tap-win32/SOURCES'),
+ quote_begin='@@',
+ quote_end='@@',
+ head_comment='# %s\n\n' % autogen)
+
+ preprocess(config,
+ in_fn=home_fn('tap-win32/i386/OemWin2k.inf.in'),
+ out_fn=home_fn('tap-win32/i386/OemWin2k.inf'),
+ quote_begin='@@',
+ quote_end='@@',
+ if_prefix='!',
+ head_comment='; %s\n\n' % autogen)
+
+ try:
+ os.mkdir(home_fn('tap-win32/amd64'))
+ except:
+ pass
+ preprocess(dict_def(config, [('AMD64', '1')]),
+ in_fn=home_fn('tap-win32/i386/OemWin2k.inf.in'),
+ out_fn=home_fn('tap-win32/amd64/OemWin2k.inf'),
+ quote_begin='@@',
+ quote_end='@@',
+ if_prefix='!',
+ head_comment='; %s\n\n' % autogen)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/config_ti.py b/win/config_ti.py
new file mode 100644
index 0000000..8742caa
--- /dev/null
+++ b/win/config_ti.py
@@ -0,0 +1,18 @@
+import os, shutil
+from wb import preprocess, home_fn, autogen
+
+def main(config):
+ src = os.path.join(home_fn(config['TISRC']), config['DDKVER_MAJOR'])
+ dest = home_fn('tapinstall')
+ shutil.rmtree(dest, ignore_errors=True)
+ shutil.copytree(src, dest)
+ preprocess(config,
+ in_fn=os.path.join(src, 'sources'),
+ out_fn=os.path.join(dest, 'sources'),
+ if_prefix='!',
+ head_comment='# %s\n\n' % autogen)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/js.py b/win/js.py
new file mode 100644
index 0000000..d1d4db3
--- /dev/null
+++ b/win/js.py
@@ -0,0 +1,10 @@
+import json
+
+# usage:
+# print JSON().encode(kv)
+
+class JSON(json.JSONEncoder):
+ def __init__(self, **kwargs):
+ args = dict(sort_keys=True, indent=2)
+ args.update(kwargs)
+ json.JSONEncoder.__init__(self, **args)
diff --git a/win/make_dist.py b/win/make_dist.py
new file mode 100644
index 0000000..edb0e6a
--- /dev/null
+++ b/win/make_dist.py
@@ -0,0 +1,107 @@
+import os
+from wb import home_fn, rm_rf, mkdir, cp_a, cp, rename, run_in_vs_shell
+
+def main(config, tap=True):
+ dist = config['DIST']
+ assert dist
+ dist = home_fn(dist)
+ bin = os.path.join(dist, 'bin')
+ i386 = os.path.join(dist, 'i386')
+ amd64 = os.path.join(dist, 'amd64')
+ samples = os.path.join(dist, 'samples')
+
+ # build dist and subdirectories
+ rm_rf(dist)
+ mkdir(dist)
+ mkdir(bin)
+ mkdir(i386)
+ mkdir(amd64)
+ mkdir(samples)
+
+ # copy openvpn.exe, openvpnserv.exe and their manifests
+ cp(home_fn('openvpn.exe'), bin)
+ cp(home_fn('openvpn.exe.manifest'), bin)
+ cp(home_fn('service-win32/openvpnserv.exe'), bin)
+ cp(home_fn('service-win32/openvpnserv.exe.manifest'), bin)
+
+ # copy openvpn-gui
+ cp(home_fn(config['OPENVPN_GUI_DIR']+"/"+config['OPENVPN_GUI']), bin)
+
+ # copy DLL dependencies
+ cp(home_fn(config['LZO_DIR']+'/bin/lzo2.dll'), bin)
+ cp(home_fn(config['LZO_DIR']+'/bin/lzo2.dll.manifest'), bin)
+ cp(home_fn(config['OPENSSL_DIR']+'/bin/libeay32.dll'), bin)
+ cp(home_fn(config['OPENSSL_DIR']+'/bin/ssleay32.dll'), bin)
+ cp(home_fn(config['PKCS11_HELPER_DIR']+'/lib/libpkcs11-helper-1.dll'), bin)
+ cp(home_fn(config['PKCS11_HELPER_DIR']+'/lib/libpkcs11-helper-1.dll.manifest'), bin)
+
+ # copy OpenSSL utilities (=openvpn.exe)
+ cp(home_fn(config['OPENSSL_DIR']+'/bin/openssl.exe'), bin)
+
+ # copy sample config files; renaming is necessary due to openvpn.nsi script
+ cp(home_fn('install-win32/sample.ovpn'), samples)
+ cp(home_fn('sample-config-files/client.conf'), samples)
+ cp(home_fn('sample-config-files/server.conf'), samples)
+ rename(os.path.join(samples,'client.conf'), os.path.join(samples, 'client.ovpn'))
+ rename(os.path.join(samples,'server.conf'), os.path.join(samples, 'server.ovpn'))
+
+ # embed manifests to executables and DLLs
+ for f in [ "openvpn.exe", "openvpnserv.exe", "lzo2.dll", "libpkcs11-helper-1.dll" ]:
+
+ outputresource = os.path.join(bin,f)
+ manifest = outputresource+".manifest"
+
+ # EXEs and DLLs require slightly different treatment
+ if f.endswith(".exe"):
+ type = "1"
+ elif f.endswith(".dll"):
+ type = "2"
+ else:
+ print "ERROR: Could not embed manifest to "+outputresouce+", bailing out."
+ sys.exit(1)
+
+ # Embed the manifest
+ run_in_vs_shell('mt.exe -manifest %s -outputresource:%s;%s' % (manifest, outputresource, type))
+
+ # copy MSVC CRT
+ cp_a(home_fn(config['MSVC_CRT']), bin)
+
+ # TAP-driver and tapinstall.exe were built, so copy those over
+ if tap:
+ drv_dir = 'tap-win32'
+ ti_dir = 'tapinstall'
+
+ # we're using prebuilt TAP-driver and tapinstall.exe
+ elif 'TAP_PREBUILT' in config:
+ drv_dir = config['TAP_PREBUILT']
+ ti_dir = config['TAP_PREBUILT']
+
+ else:
+ print "ERROR: Could not find prebuilt TAP-drivers or tapinstall.exe. Please check win/settings.in"
+ sys.exit(1)
+
+ # copy TAP drivers
+ for dir_name, dest in (('amd64', amd64), ('i386', i386)):
+ dir = home_fn(os.path.join(drv_dir, dir_name))
+ for dirpath, dirnames, filenames in os.walk(dir):
+ for f in filenames:
+ root, ext = os.path.splitext(f)
+ if ext in ('.inf', '.cat', '.sys'):
+ cp(os.path.join(dir, f), dest)
+ break
+
+ # Copy tapinstall.exe (usually known as devcon.exe)
+ dest = {'amd64' : amd64, 'i386' : i386}
+ for dirpath, dirnames, filenames in os.walk(home_fn(ti_dir)):
+ for f in filenames:
+ if f in ( 'devcon.exe', 'tapinstall.exe' ):
+ dir_name = os.path.basename(dirpath)
+ src = os.path.join(dirpath, f)
+ dst = os.path.join(dest[dir_name],'tapinstall.exe')
+ if dir_name in dest:
+ cp(src, dst, dest_is_dir=False)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ from wb import config
+ main(config)
diff --git a/win/msvc.mak.in b/win/msvc.mak.in
new file mode 100644
index 0000000..ac17ae9
--- /dev/null
+++ b/win/msvc.mak.in
@@ -0,0 +1,57 @@
+# This makefile builds the user-mode component of OpenVPN for Windows in the
+# Visual Studio 2008 environment. Note that this file is basis for the real
+# makefile (..\msvc.mak) but unusable as is. The real makefile is automatically
+# generated during the build process by the Python build scripts.
+#
+# A few details are in order:
+#
+# - Everything between @<< and << is inserted into a s.c. "in-line file". This
+# file drives the linker (link.exe).
+# - HEADERS_OBJS is expanded to all all header and source files listed in
+# ..\Makefile.am
+# - OPENSSL_DIR and LZO_DIR are dynamically created from settings.in
+
+OPENSSL = @OPENSSL_DIR@
+OPENSSL_DYNAMIC = libeay32.lib ssleay32.lib
+
+LZO = @LZO_DIR@
+LZO_DYNAMIC = lzo2.lib
+
+INCLUDE_DIRS = -I$(OPENSSL)/include -I$(LZO)/include
+
+LIBS = $(OPENSSL_DYNAMIC) $(LZO_DYNAMIC) ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib
+
+LIB_DIRS = -LIBPATH:$(OPENSSL)\lib -LIBPATH:$(LZO)\lib
+
+EXE = openvpn.exe
+
+CPP=cl.exe
+CPP_ARG_COMMON=/nologo /W3 -DWIN32 -DWIN32_LEAN_AND_MEAN -D_CONSOLE -D_MBCS -D_CRT_SECURE_NO_DEPRECATE $(INCLUDE_DIRS) /FD /c
+
+LINK32=link.exe
+
+!ifdef PRODUCT_OPENVPN_DEBUG
+# debug:
+CPP_PROJ=$(CPP_ARG_COMMON) /MD /Z7
+LINK32_FLAGS=/nologo /subsystem:console /incremental:no /opt:ref /opt:icf /debug
+!else
+# release:
+CPP_PROJ=$(CPP_ARG_COMMON) /O2 /MD -DNDEBUG
+LINK32_FLAGS=/nologo /subsystem:console /incremental:no
+!endif
+
+# HEADERS and OBJS definitions, automatically generated from ../Makefile.am
+@HEADERS_OBJS@
+
+openvpn : $(OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) "/out:$(EXE)" $(LIB_DIRS) $(LIBS) $(OBJS)
+<<
+
+clean :
+ del /Q $(OBJS) $(EXE) *.idb *.pdb
+
+.c.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
diff --git a/win/openvpn.nsi b/win/openvpn.nsi
new file mode 100755
index 0000000..29d34f1
--- /dev/null
+++ b/win/openvpn.nsi
@@ -0,0 +1,822 @@
+; ****************************************************************************
+; * Copyright (C) 2002-2010 OpenVPN Technologies, 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. *
+; ****************************************************************************
+
+; OpenVPN install script for Windows, using NSIS
+
+; Start menu entries don't get uninstalled properly on Windows Vista/7 unless we
+; explicitly state that the installer requires admin privileges. This is
+; caused by backwards compatibility tricks used on those platforms. For details,
+; see http://nsis.sourceforge.net/Shortcuts_removal_fails_on_Windows_Vista
+RequestExecutionLevel admin
+
+SetCompressor lzma
+
+!include "MUI.nsh"
+
+# Include basic build settings
+!include "settings.in"
+
+# Include variables generated dynamically from version.m4 by wb.py
+!include "version_m4_vars.tmp"
+
+;!include "guidefs.nsi"
+!include "setpath.nsi"
+
+!ifdef EXTRACT_FILES
+!include "MultiFileExtract.nsi"
+!endif
+
+!define GEN "..\dist"
+!define BIN "${GEN}\bin"
+!define EASYRSA "..\easy-rsa"
+
+!define PRODUCT_ICON "icon.ico"
+
+!ifdef PRODUCT_TAP_DEBUG
+!define DBG_POSTFIX "-DBG"
+!else
+!define DBG_POSTFIX ""
+!endif
+
+!define VERSION "${PRODUCT_VERSION}${DBG_POSTFIX}"
+
+!define TAP "${PRODUCT_TAP_ID}"
+!define TAPDRV "${TAP}.sys"
+
+; Default service settings
+!define SERV_CONFIG_DIR "$INSTDIR\config"
+!define SERV_CONFIG_EXT "${PRODUCT_FILE_EXT}"
+!define SERV_EXE_PATH "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+!define SERV_LOG_DIR "$INSTDIR\log"
+!define SERV_PRIORITY "NORMAL_PRIORITY_CLASS"
+!define SERV_LOG_APPEND "0"
+
+;--------------------------------
+;Configuration
+
+ ;General
+
+ OutFile "${GEN}\${PRODUCT_UNIX_NAME}-${VERSION}${OUTFILE_LABEL}-install.exe"
+
+ ShowInstDetails show
+ ShowUninstDetails show
+
+ ;Folder selection page
+ InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
+
+ ;Remember install folder
+ InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" ""
+
+;--------------------------------
+;Modern UI Configuration
+
+ Name "${PRODUCT_NAME} ${VERSION} ${TITLE_LABEL}"
+
+ !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of ${PRODUCT_NAME}, an Open Source VPN package by James Yonan.\r\n\r\nNote that the Windows version of ${PRODUCT_NAME} only runs on XP, or higher.\r\n\r\n\r\n"
+
+ !define MUI_COMPONENTSPAGE_TEXT_TOP "Select the components to install/upgrade. Stop any ${PRODUCT_NAME} processes or the ${PRODUCT_NAME} service if it is running. All DLLs are installed locally."
+
+ !define MUI_COMPONENTSPAGE_SMALLDESC
+ !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\INSTALL-win32.txt"
+ !define MUI_FINISHPAGE_NOAUTOCLOSE
+ !define MUI_ABORTWARNING
+ !define MUI_ICON "..\images\${PRODUCT_ICON}"
+ !define MUI_UNICON "..\images\${PRODUCT_ICON}"
+ !define MUI_HEADERIMAGE
+ !define MUI_HEADERIMAGE_BITMAP "..\images\install-whirl.bmp"
+ !define MUI_UNFINISHPAGE_NOAUTOCLOSE
+
+ !insertmacro MUI_PAGE_WELCOME
+ !insertmacro MUI_PAGE_LICENSE "..\COPYRIGHT.GPL"
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+ !insertmacro MUI_PAGE_INSTFILES
+ !insertmacro MUI_PAGE_FINISH
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro MUI_UNPAGE_FINISH
+
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+ LangString DESC_SecOpenVPNUserSpace ${LANG_ENGLISH} "Install ${PRODUCT_NAME} user-space components, including ${PRODUCT_UNIX_NAME}.exe."
+
+!ifdef USE_GUI
+ LangString DESC_SecOpenVPNGUI ${LANG_ENGLISH} "Install ${PRODUCT_NAME} GUI by Mathias Sundman"
+!endif
+
+ LangString DESC_SecOpenVPNEasyRSA ${LANG_ENGLISH} "Install ${PRODUCT_NAME} RSA scripts for X509 certificate management."
+
+ LangString DESC_SecOpenSSLDLLs ${LANG_ENGLISH} "Install OpenSSL DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecPKCS11DLLs ${LANG_ENGLISH} "Install PKCS#11 helper DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecLZO2DLLs ${LANG_ENGLISH} "Install LZO2 DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecMSVCR90DLL ${LANG_ENGLISH} "Install Microsoft Visual C 9.0 Runtime (may be omitted if it is already installed globally)."
+
+ LangString DESC_SecTAP ${LANG_ENGLISH} "Install/Upgrade the TAP virtual device driver. Will not interfere with CIPE."
+
+ LangString DESC_SecService ${LANG_ENGLISH} "Install the ${PRODUCT_NAME} service wrapper (${PRODUCT_UNIX_NAME}serv.exe)"
+
+ LangString DESC_SecOpenSSLUtilities ${LANG_ENGLISH} "Install the OpenSSL Utilities (used for generating public/private key pairs)."
+
+ LangString DESC_SecAddPath ${LANG_ENGLISH} "Add ${PRODUCT_NAME} executable directory to the current user's PATH."
+
+ LangString DESC_SecAddShortcuts ${LANG_ENGLISH} "Add ${PRODUCT_NAME} shortcuts to the current user's Start Menu."
+
+ LangString DESC_SecFileAssociation ${LANG_ENGLISH} "Register ${PRODUCT_NAME} config file association (*.${SERV_CONFIG_EXT})"
+
+;--------------------------------
+;Reserve Files
+
+ ;Things that need to be extracted on first (keep these lines before any File command!)
+ ;Only useful for BZIP2 compression
+
+ ReserveFile "..\images\install-whirl.bmp"
+
+;--------------------------------
+;Macros
+
+!macro WriteRegStringIfUndef ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 "" +1 +2
+WriteRegStr "${ROOT}" "${SUBKEY}" "${KEY}" '${VALUE}'
+Pop $R0
+!macroend
+
+!macro DelRegStringIfUnchanged ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegValue "${ROOT}" "${SUBKEY}" "${KEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfUnchanged ROOT SUBKEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" ""
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegKey "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfEmpty ROOT SUBKEY
+Push $R0
+EnumRegValue $R0 "${ROOT}" "${SUBKEY}" 1
+StrCmp $R0 "" +1 +2
+DeleteRegKey /ifempty "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+;------------------------------------------
+;Set reboot flag based on tapinstall return
+
+Function CheckReboot
+ IntCmp $R0 1 "" noreboot noreboot
+ IntOp $R0 0 & 0
+ SetRebootFlag true
+ DetailPrint "REBOOT flag set"
+ noreboot:
+FunctionEnd
+
+;--------------------------------
+;Installer Sections
+
+Function .onInit
+ ClearErrors
+
+# Verify that user has admin privs
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to install ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+
+# Delete previous start menu
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+# FIXME: reimplement Windows version checking code that was located here, but
+# disabled intentionally to avoid Windows 7 issues. This should do it:
+#
+# http://nsis.sourceforge.net/Get_Windows_version
+#
+# Blacklisting should be safer than whitelisting used originally.
+
+FunctionEnd
+
+!ifndef SF_SELECTED
+!define SF_SELECTED 1
+!endif
+
+;--------------------
+;Pre-install section
+
+Section -pre
+
+ ; Stop OpenVPN if currently running
+ DetailPrint "Previous Service REMOVE (if exists)"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 3000
+
+ # Fix for Trac ticket 120. Remove after 2.3 has been released.
+ !ifdef USE_GUI
+ SetShellVarContext current
+ Delete "$DESKTOP\${PRODUCT_NAME} GUI.lnk"
+ !endif
+
+SectionEnd
+
+Section "${PRODUCT_NAME} User-Space Components" SecOpenVPNUserSpace
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${PRODUCT_UNIX_NAME}.exe"
+
+SectionEnd
+
+!ifdef USE_GUI
+Section "${PRODUCT_NAME} GUI" SecOpenVPNGUI
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${OPENVPN_GUI}"
+
+SectionEnd
+!endif
+
+Section "${PRODUCT_NAME} RSA Certificate Management Scripts" SecOpenVPNEasyRSA
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\easy-rsa"
+
+ # FIXME: the easy-rsa directory would need cleaning up
+
+ # Original nsi script looked for ${EASYRSA}\2.0\openssl.cnf.sample. A newer
+ # openssl.cnf is needed on OpenVPN 2.2+.
+ File "${EASYRSA}\2.0\openssl-1.0.0.cnf"
+
+ File "${EASYRSA}\Windows\vars.bat.sample"
+
+ File "${EASYRSA}\Windows\init-config.bat"
+
+ File "${EASYRSA}\Windows\README.txt"
+ File "${EASYRSA}\Windows\build-ca.bat"
+ File "${EASYRSA}\Windows\build-dh.bat"
+ File "${EASYRSA}\Windows\build-key-server.bat"
+ File "${EASYRSA}\Windows\build-key.bat"
+ File "${EASYRSA}\Windows\build-key-pkcs12.bat"
+ File "${EASYRSA}\Windows\clean-all.bat"
+ File "${EASYRSA}\Windows\index.txt.start"
+ File "${EASYRSA}\Windows\revoke-full.bat"
+ File "${EASYRSA}\Windows\serial.start"
+
+SectionEnd
+
+Section "${PRODUCT_NAME} Service" SecService
+
+ SetOverwrite on
+
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\${PRODUCT_UNIX_NAME}serv.exe"
+
+ SetOutPath "$INSTDIR\config"
+
+ FileOpen $R0 "$INSTDIR\config\README.txt" w
+ FileWrite $R0 "This directory should contain ${PRODUCT_NAME} configuration files$\r$\n"
+ FileWrite $R0 "each having an extension of .${SERV_CONFIG_EXT}$\r$\n"
+ FileWrite $R0 "$\r$\n"
+ FileWrite $R0 "When ${PRODUCT_NAME} is started as a service, a separate ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "process will be instantiated for each configuration file.$\r$\n"
+ FileClose $R0
+
+ SetOutPath "$INSTDIR\sample-config"
+ File "${GEN}\samples\sample.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\client.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\server.${SERV_CONFIG_EXT}"
+
+ CreateDirectory "$INSTDIR\log"
+ FileOpen $R0 "$INSTDIR\log\README.txt" w
+ FileWrite $R0 "This directory will contain the log files for ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "sessions which are being run as a service.$\r$\n"
+ FileClose $R0
+
+SectionEnd
+
+Section "${PRODUCT_NAME} File Associations" SecFileAssociation
+SectionEnd
+
+Section "OpenSSL DLLs" SecOpenSSLDLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\libeay32.dll"
+ File "${BIN}\ssleay32.dll"
+
+SectionEnd
+
+Section "OpenSSL Utilities" SecOpenSSLUtilities
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\openssl.exe"
+
+SectionEnd
+
+Section "PKCS#11 DLLs" SecPKCS11DLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\libpkcs11-helper-1.dll"
+
+SectionEnd
+
+Section "LZO2 DLLs" SecLZO2DLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\lzo2.dll"
+
+SectionEnd
+
+Section "Microsoft Visual C 9.0 Runtime DLL" SecMSVCR90DLL
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\Microsoft.VC90.CRT\msvcr90.dll"
+ File "${BIN}\Microsoft.VC90.CRT\Microsoft.VC90.CRT.manifest"
+
+SectionEnd
+
+
+
+
+Section "TAP Virtual Ethernet Adapter" SecTAP
+
+ SetOverwrite on
+
+ # Generate TAP driver install script dynamically
+ FileOpen $R0 "$INSTDIR\bin\addtap.bat" w
+ FileWrite $R0 "rem Add a new TAP virtual ethernet adapter$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ # Generate TAP driver removal script dynamically
+ FileOpen $R0 "$INSTDIR\bin\deltapall.bat" w
+ FileWrite $R0 "echo WARNING: this script will delete ALL TAP virtual adapters (use the device manager to delete adapters one at a time)$\r$\n"
+ FileWrite $R0 "pause$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ ; Check if we are running on a 64 bit system.
+ System::Call "kernel32::GetCurrentProcess() i .s"
+ System::Call "kernel32::IsWow64Process(i s, *i .r0)"
+ IntCmp $0 0 tap-32bit
+
+; tap-64bit:
+
+ DetailPrint "We are running on a 64-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+
+ File "${GEN}\amd64\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+
+ File "${GEN}\amd64\OemWin2k.inf"
+ File "${GEN}\amd64\${TAPDRV}"
+
+ # Don't try to install TAP driver signature if it does not exist.
+ File /nonfatal "${GEN}\amd64\${PRODUCT_TAP_ID}.cat"
+
+goto tapend
+
+tap-32bit:
+
+ DetailPrint "We are running on a 32-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+ File "${GEN}\i386\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+ File "${GEN}\i386\OemWin2k.inf"
+ File "${GEN}\i386\${TAPDRV}"
+
+ # Don't try to install TAP driver signature if it does not exist.
+ File /nonfatal "${GEN}\i386\${PRODUCT_TAP_ID}.cat"
+
+ tapend:
+
+SectionEnd
+
+Section "Add ${PRODUCT_NAME} to PATH" SecAddPath
+
+ ; remove previously set path (if any)
+ Push "$INSTDIR\bin"
+ Call RemoveFromPath
+
+ ; append our bin directory to end of current user path
+ Push "$INSTDIR\bin"
+ Call AddToPath
+
+SectionEnd
+
+Section "Add Shortcuts to Start Menu" SecAddShortcuts
+
+ ; Required to handle shortcuts properly on Vista/7
+ SetShellVarContext all
+ SetOverwrite on
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Documentation"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Windows Notes.url" "InternetShortcut" "URL" "http://openvpn.net/INSTALL-win32.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Manual Page.url" "InternetShortcut" "URL" "http://openvpn.net/man.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} HOWTO.url" "InternetShortcut" "URL" "http://openvpn.net/howto.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Web Site.url" "InternetShortcut" "URL" "http://openvpn.net/"
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\Uninstall.exe"
+
+SectionEnd
+
+;--------------------
+;Post-install section
+
+Section -post
+
+ SetOverwrite on
+
+ ; delete old tapinstall.exe
+ ;Delete "$INSTDIR\bin\tapinstall.exe"
+
+ ; Store README, license, icon
+ SetOverwrite on
+ SetOutPath $INSTDIR
+ File "..\INSTALL-win32.txt"
+ File "..\COPYRIGHT.GPL"
+ File "..\images\${PRODUCT_ICON}"
+
+ ; store sample config files
+ !ifdef SAMPCONF_DIR
+ SetOverwrite on
+ SetOutPath "$INSTDIR\config"
+ !ifdef SAMPCONF_CONF
+ File "${GEN}\conf\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ File "${GEN}\conf\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ File "${GEN}\conf\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ File "${GEN}\conf\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ File "${GEN}\conf\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ File "${GEN}\conf\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ File "${GEN}\conf\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ File "${GEN}\conf\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ ; Try to extract files if present
+ !ifdef EXTRACT_FILES
+ Push "$INSTDIR"
+ Call MultiFileExtract
+ Pop $R0
+ IntCmp $R0 0 +3 +1 +1
+ DetailPrint "MultiFileExtract Failed status=$R0"
+ goto +2
+ DetailPrint "MultiFileExtract Succeeded"
+ !endif
+
+ ;
+ ; install/upgrade TAP driver if selected, using tapinstall.exe
+ ;
+ SectionGetFlags ${SecTAP} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" notap notap
+ ; TAP install/update was selected.
+ ; Should we install or update?
+ ; If tapinstall error occurred, $5 will
+ ; be nonzero.
+ IntOp $5 0 & 0
+ nsExec::ExecToStack '"$INSTDIR\bin\tapinstall.exe" hwids ${TAP}'
+ Pop $R0 # return value/error/timeout
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall hwids returned: $R0"
+
+ ; If tapinstall output string contains "${TAP}" we assume
+ ; that TAP device has been previously installed,
+ ; therefore we will update, not install.
+ Push "${TAP}"
+ Call StrStr
+ Pop $R0
+
+ IntCmp $5 0 "" tapinstall_check_error tapinstall_check_error
+ IntCmp $R0 -1 tapinstall
+
+ ;tapupdate:
+ DetailPrint "TAP UPDATE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" update "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall update returned: $R0"
+ Goto tapinstall_check_error
+
+ tapinstall:
+ DetailPrint "TAP REMOVE OLD TAP"
+
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove TAP0801'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove TAP0801 returned: $R0"
+
+ DetailPrint "TAP INSTALL (${TAP})"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall install returned: $R0"
+
+ tapinstall_check_error:
+ DetailPrint "tapinstall cumulative status: $5"
+ IntCmp $5 0 notap
+ MessageBox MB_OK "An error occurred installing the TAP device driver."
+
+ notap:
+
+ ; Store install folder in registry
+ WriteRegStr HKLM SOFTWARE\${PRODUCT_NAME} "" $INSTDIR
+
+ ; install as a service if requested
+ SectionGetFlags ${SecService} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noserv noserv
+
+ ; set registry parameters for openvpnserv
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_dir" "${SERV_CONFIG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_ext" "${SERV_CONFIG_EXT}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "exe_path" "${SERV_EXE_PATH}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_dir" "${SERV_LOG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "priority" "${SERV_PRIORITY}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_append" "${SERV_LOG_APPEND}"
+
+ ; install openvpnserv as a service (to be started manually from service control manager)
+ DetailPrint "Service INSTALL"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -install'
+ Pop $R0 # return value/error/timeout
+
+ noserv:
+
+ ; Create file association if requested
+ fileass:
+ SectionGetFlags ${SecFileAssociation} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noass noass
+ WriteRegStr HKCR ".${SERV_CONFIG_EXT}" "" "${PRODUCT_NAME}File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File" "" "${PRODUCT_NAME} Config File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell" "" "open"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\DefaultIcon" "" "$INSTDIR\${PRODUCT_ICON},0"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\open\command" "" 'notepad.exe "%1"'
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run" "" "Start ${PRODUCT_NAME} on this config file"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run\command" "" '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" --pause-exit --config "%1"'
+
+ ; Create start menu folders
+ noass:
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Utilities"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts"
+
+ ; Create start menu and desktop shortcuts to OpenVPN GUI
+ !ifdef USE_GUI
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}" ""
+ CreateShortcut "$DESKTOP\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}"
+ !endif
+
+ ; Create start menu shortcuts to addtap.bat and deltapall.bat
+ tryaddtap:
+ IfFileExists "$INSTDIR\bin\addtap.bat" "" trydeltap
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Add a new TAP virtual ethernet adapter.lnk" "$INSTDIR\bin\addtap.bat" ""
+
+ trydeltap:
+ IfFileExists "$INSTDIR\bin\deltapall.bat" "" config_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Delete ALL TAP virtual ethernet adapters.lnk" "$INSTDIR\bin\deltapall.bat" ""
+
+ ; Create start menu shortcuts for config and log directories
+ config_shortcut:
+ IfFileExists "$INSTDIR\config" "" log_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} configuration file directory.lnk" "$INSTDIR\config" ""
+
+ log_shortcut:
+ IfFileExists "$INSTDIR\log" "" samp_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} log file directory.lnk" "$INSTDIR\log" ""
+
+ samp_shortcut:
+ IfFileExists "$INSTDIR\sample-config" "" genkey_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} Sample Configuration Files.lnk" "$INSTDIR\sample-config" ""
+
+ genkey_shortcut:
+ IfFileExists "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" "" noshortcuts
+ IfFileExists "$INSTDIR\config" "" noshortcuts
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Generate a static ${PRODUCT_NAME} key.lnk" "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" '--pause-exit --verb 3 --genkey --secret "$INSTDIR\config\key.txt"' "$INSTDIR\${PRODUCT_ICON}" 0
+
+ noshortcuts:
+ ; Create uninstaller
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ ; Show up in Add/Remove programs
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME} ${VERSION}"
+ WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$INSTDIR\${PRODUCT_ICON}"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${VERSION}"
+
+ ; Advise a reboot
+ ;Messagebox MB_OK "IMPORTANT: Rebooting the system is advised in order to finalize TAP driver installation/upgrade (this is an informational message only, pressing OK will not reboot)."
+
+SectionEnd
+
+;--------------------------------
+;Descriptions
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNUserSpace} $(DESC_SecOpenVPNUserSpace)
+ !ifdef USE_GUI
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNGUI} $(DESC_SecOpenVPNGUI)
+ !endif
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNEasyRSA} $(DESC_SecOpenVPNEasyRSA)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecTAP} $(DESC_SecTAP)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLUtilities} $(DESC_SecOpenSSLUtilities)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLDLLs} $(DESC_SecOpenSSLDLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecPKCS11DLLs} $(DESC_SecPKCS11DLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecLZO2DLLs} $(DESC_SecLZO2DLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecMSVCR90DLL} $(DESC_SecMSVCR90DLL)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddPath} $(DESC_SecAddPath)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddShortcuts} $(DESC_SecAddShortcuts)
+
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecFileAssociation} $(DESC_SecFileAssociation)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Function un.onInit
+ ClearErrors
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to uninstall ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+FunctionEnd
+
+Section "Uninstall"
+
+ ; Required to handle shortcuts properly on Vista/7
+ SetShellVarContext all
+
+ ; Stop OpenVPN if currently running
+
+ DetailPrint "Service REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 3000
+
+ DetailPrint "TAP REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove returned: $R0"
+
+ Push "$INSTDIR\bin"
+ Call un.RemoveFromPath
+
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+ ; delete sample config files
+ !ifdef SAMPCONF_DIR
+ !ifdef SAMPCONF_CONF
+ Delete "$INSTDIR\config\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ Delete "$INSTDIR\config\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ Delete "$INSTDIR\config\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ Delete "$INSTDIR\config\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ Delete "$INSTDIR\config\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ Delete "$INSTDIR\config\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ Delete "$INSTDIR\config\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ Delete "$INSTDIR\config\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ !ifdef USE_GUI
+ Delete "$INSTDIR\bin\${OPENVPN_GUI}"
+ Delete "$DESKTOP\${PRODUCT_NAME} GUI.lnk"
+ !endif
+
+ # Files installed by openvpn-2.2-beta5 and earlier
+ Delete "$INSTDIR\easy-rsa\openssl.cnf.sample"
+ Delete "$INSTDIR\license"
+ Delete "$INSTDIR\bin\libssl32.dll"
+
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe"
+ Delete "$INSTDIR\bin\libeay32.dll"
+ Delete "$INSTDIR\bin\ssleay32.dll"
+ Delete "$INSTDIR\bin\libpkcs11-helper-1.dll"
+ Delete "$INSTDIR\bin\lzo2.dll"
+ Delete "$INSTDIR\bin\msvcr90.dll"
+ Delete "$INSTDIR\bin\Microsoft.VC90.CRT.manifest"
+ Delete "$INSTDIR\bin\tapinstall.exe"
+ Delete "$INSTDIR\bin\addtap.bat"
+ Delete "$INSTDIR\bin\deltapall.bat"
+
+ Delete "$INSTDIR\config\README.txt"
+ Delete "$INSTDIR\config\sample.${SERV_CONFIG_EXT}.txt"
+
+ Delete "$INSTDIR\log\README.txt"
+
+ Delete "$INSTDIR\driver\OemWin2k.inf"
+ Delete "$INSTDIR\driver\${PRODUCT_TAP_ID}.cat"
+ Delete "$INSTDIR\driver\${TAPDRV}"
+
+ Delete "$INSTDIR\bin\openssl.exe"
+
+ Delete "$INSTDIR\INSTALL-win32.txt"
+ Delete "$INSTDIR\${PRODUCT_ICON}"
+ Delete "$INSTDIR\COPYRIGHT.GPL"
+ Delete "$INSTDIR\Uninstall.exe"
+
+ Delete "$INSTDIR\easy-rsa\openssl.cnf"
+ Delete "$INSTDIR\easy-rsa\vars.bat.sample"
+ Delete "$INSTDIR\easy-rsa\init-config.bat"
+ Delete "$INSTDIR\easy-rsa\README.txt"
+ Delete "$INSTDIR\easy-rsa\build-ca.bat"
+ Delete "$INSTDIR\easy-rsa\build-dh.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-server.bat"
+ Delete "$INSTDIR\easy-rsa\build-key.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-pkcs12.bat"
+ Delete "$INSTDIR\easy-rsa\clean-all.bat"
+ Delete "$INSTDIR\easy-rsa\index.txt.start"
+ Delete "$INSTDIR\easy-rsa\revoke-key.bat"
+ Delete "$INSTDIR\easy-rsa\revoke-full.bat"
+ Delete "$INSTDIR\easy-rsa\serial.start"
+
+ Delete "$INSTDIR\sample-config\*.${PRODUCT_FILE_EXT}"
+
+ RMDir "$INSTDIR\bin"
+ RMDir "$INSTDIR\config"
+ RMDir "$INSTDIR\driver"
+ RMDir "$INSTDIR\easy-rsa"
+ RMDir "$INSTDIR\sample-config"
+ RMDir /r "$INSTDIR\log"
+ RMDir "$INSTDIR"
+
+ !insertmacro DelRegKeyIfUnchanged HKCR ".${SERV_CONFIG_EXT}" "${PRODUCT_NAME}File"
+ DeleteRegKey HKCR "${PRODUCT_NAME}File"
+ DeleteRegKey HKLM SOFTWARE\${PRODUCT_NAME}
+ DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+
+SectionEnd
diff --git a/win/openvpn.nsi.orig b/win/openvpn.nsi.orig
new file mode 100755
index 0000000..d667d76
--- /dev/null
+++ b/win/openvpn.nsi.orig
@@ -0,0 +1,822 @@
+; ****************************************************************************
+; * Copyright (C) 2002-2010 OpenVPN Technologies, 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. *
+; ****************************************************************************
+
+; OpenVPN install script for Windows, using NSIS
+
+; Start menu entries don't get uninstalled properly on Windows Vista/7 unless we
+; explicitly state that the installer requires admin privileges. This is
+; caused by backwards compatibility tricks used on those platforms. For details,
+; see http://nsis.sourceforge.net/Shortcuts_removal_fails_on_Windows_Vista
+RequestExecutionLevel admin
+
+SetCompressor lzma
+
+!include "MUI.nsh"
+
+# Include basic build settings
+!include "settings.in"
+
+# Include variables generated dynamically from version.m4 by wb.py
+!include "version_m4_vars.tmp"
+
+;!include "guidefs.nsi"
+!include "setpath.nsi"
+
+!ifdef EXTRACT_FILES
+!include "MultiFileExtract.nsi"
+!endif
+
+!define GEN "..\dist"
+!define BIN "${GEN}\bin"
+!define EASYRSA "..\easy-rsa"
+
+!define PRODUCT_ICON "icon.ico"
+
+!ifdef PRODUCT_TAP_DEBUG
+!define DBG_POSTFIX "-DBG"
+!else
+!define DBG_POSTFIX ""
+!endif
+
+!define VERSION "${PRODUCT_VERSION}${DBG_POSTFIX}"
+
+!define TAP "${PRODUCT_TAP_ID}"
+!define TAPDRV "${TAP}.sys"
+
+; Default service settings
+!define SERV_CONFIG_DIR "$INSTDIR\config"
+!define SERV_CONFIG_EXT "${PRODUCT_FILE_EXT}"
+!define SERV_EXE_PATH "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+!define SERV_LOG_DIR "$INSTDIR\log"
+!define SERV_PRIORITY "NORMAL_PRIORITY_CLASS"
+!define SERV_LOG_APPEND "0"
+
+;--------------------------------
+;Configuration
+
+ ;General
+
+ OutFile "${GEN}\${PRODUCT_UNIX_NAME}-${VERSION}${OUTFILE_LABEL}-install.exe"
+
+ ShowInstDetails show
+ ShowUninstDetails show
+
+ ;Folder selection page
+ InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
+
+ ;Remember install folder
+ InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" ""
+
+;--------------------------------
+;Modern UI Configuration
+
+ Name "${PRODUCT_NAME} ${VERSION} ${TITLE_LABEL}"
+
+ !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of ${PRODUCT_NAME}, an Open Source VPN package by James Yonan.\r\n\r\nNote that the Windows version of ${PRODUCT_NAME} only runs on XP, or higher.\r\n\r\n\r\n"
+
+ !define MUI_COMPONENTSPAGE_TEXT_TOP "Select the components to install/upgrade. Stop any ${PRODUCT_NAME} processes or the ${PRODUCT_NAME} service if it is running. All DLLs are installed locally."
+
+ !define MUI_COMPONENTSPAGE_SMALLDESC
+ !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\INSTALL-win32.txt"
+ !define MUI_FINISHPAGE_NOAUTOCLOSE
+ !define MUI_ABORTWARNING
+ !define MUI_ICON "..\images\${PRODUCT_ICON}"
+ !define MUI_UNICON "..\images\${PRODUCT_ICON}"
+ !define MUI_HEADERIMAGE
+ !define MUI_HEADERIMAGE_BITMAP "..\images\install-whirl.bmp"
+ !define MUI_UNFINISHPAGE_NOAUTOCLOSE
+
+ !insertmacro MUI_PAGE_WELCOME
+ !insertmacro MUI_PAGE_LICENSE "..\COPYRIGHT.GPL"
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+ !insertmacro MUI_PAGE_INSTFILES
+ !insertmacro MUI_PAGE_FINISH
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro MUI_UNPAGE_FINISH
+
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+ LangString DESC_SecOpenVPNUserSpace ${LANG_ENGLISH} "Install ${PRODUCT_NAME} user-space components, including ${PRODUCT_UNIX_NAME}.exe."
+
+!ifdef USE_GUI
+ LangString DESC_SecOpenVPNGUI ${LANG_ENGLISH} "Install ${PRODUCT_NAME} GUI by Mathias Sundman"
+!endif
+
+ LangString DESC_SecOpenVPNEasyRSA ${LANG_ENGLISH} "Install ${PRODUCT_NAME} RSA scripts for X509 certificate management."
+
+ LangString DESC_SecOpenSSLDLLs ${LANG_ENGLISH} "Install OpenSSL DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecPKCS11DLLs ${LANG_ENGLISH} "Install PKCS#11 helper DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecLZO2DLLs ${LANG_ENGLISH} "Install LZO2 DLLs locally (may be omitted if DLLs are already installed globally)."
+
+ LangString DESC_SecMSVCR90DLL ${LANG_ENGLISH} "Install Microsoft Visual C 9.0 Runtime (may be omitted if it is already installed globally)."
+
+ LangString DESC_SecTAP ${LANG_ENGLISH} "Install/Upgrade the TAP virtual device driver. Will not interfere with CIPE."
+
+ LangString DESC_SecService ${LANG_ENGLISH} "Install the ${PRODUCT_NAME} service wrapper (${PRODUCT_UNIX_NAME}serv.exe)"
+
+ LangString DESC_SecOpenSSLUtilities ${LANG_ENGLISH} "Install the OpenSSL Utilities (used for generating public/private key pairs)."
+
+ LangString DESC_SecAddPath ${LANG_ENGLISH} "Add ${PRODUCT_NAME} executable directory to the current user's PATH."
+
+ LangString DESC_SecAddShortcuts ${LANG_ENGLISH} "Add ${PRODUCT_NAME} shortcuts to the current user's Start Menu."
+
+ LangString DESC_SecFileAssociation ${LANG_ENGLISH} "Register ${PRODUCT_NAME} config file association (*.${SERV_CONFIG_EXT})"
+
+;--------------------------------
+;Reserve Files
+
+ ;Things that need to be extracted on first (keep these lines before any File command!)
+ ;Only useful for BZIP2 compression
+
+ ReserveFile "..\images\install-whirl.bmp"
+
+;--------------------------------
+;Macros
+
+!macro WriteRegStringIfUndef ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 "" +1 +2
+WriteRegStr "${ROOT}" "${SUBKEY}" "${KEY}" '${VALUE}'
+Pop $R0
+!macroend
+
+!macro DelRegStringIfUnchanged ROOT SUBKEY KEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" "${KEY}"
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegValue "${ROOT}" "${SUBKEY}" "${KEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfUnchanged ROOT SUBKEY VALUE
+Push $R0
+ReadRegStr $R0 "${ROOT}" "${SUBKEY}" ""
+StrCmp $R0 '${VALUE}' +1 +2
+DeleteRegKey "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+!macro DelRegKeyIfEmpty ROOT SUBKEY
+Push $R0
+EnumRegValue $R0 "${ROOT}" "${SUBKEY}" 1
+StrCmp $R0 "" +1 +2
+DeleteRegKey /ifempty "${ROOT}" "${SUBKEY}"
+Pop $R0
+!macroend
+
+;------------------------------------------
+;Set reboot flag based on tapinstall return
+
+Function CheckReboot
+ IntCmp $R0 1 "" noreboot noreboot
+ IntOp $R0 0 & 0
+ SetRebootFlag true
+ DetailPrint "REBOOT flag set"
+ noreboot:
+FunctionEnd
+
+;--------------------------------
+;Installer Sections
+
+Function .onInit
+ ClearErrors
+
+# Verify that user has admin privs
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to install ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+
+# Delete previous start menu
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+# FIXME: reimplement Windows version checking code that was located here, but
+# disabled intentionally to avoid Windows 7 issues. This should do it:
+#
+# http://nsis.sourceforge.net/Get_Windows_version
+#
+# Blacklisting should be safer than whitelisting used originally.
+
+FunctionEnd
+
+!ifndef SF_SELECTED
+!define SF_SELECTED 1
+!endif
+
+;--------------------
+;Pre-install section
+
+Section -pre
+
+ ; Stop OpenVPN if currently running
+ DetailPrint "Previous Service REMOVE (if exists)"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 3000
+
+ # Fix for Trac ticket 120. Remove after 2.3 has been released.
+ !ifdef USE_GUI
+ SetShellVarContext current
+ Delete "$DESKTOP\${PRODUCT_NAME} GUI.lnk"
+ !endif
+
+SectionEnd
+
+Section "${PRODUCT_NAME} User-Space Components" SecOpenVPNUserSpace
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${PRODUCT_UNIX_NAME}.exe"
+
+SectionEnd
+
+!ifdef USE_GUI
+Section "${PRODUCT_NAME} GUI" SecOpenVPNGUI
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+
+ File "${BIN}\${OPENVPN_GUI}"
+
+SectionEnd
+!endif
+
+Section "${PRODUCT_NAME} RSA Certificate Management Scripts" SecOpenVPNEasyRSA
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\easy-rsa"
+
+ # FIXME: the easy-rsa directory would need cleaning up
+
+ # Original nsi script looked for ${EASYRSA}\2.0\openssl.cnf.sample. A newer
+ # openssl.cnf is needed on OpenVPN 2.2+.
+ File "${EASYRSA}\Windows\openssl.cnf"
+
+ File "${EASYRSA}\Windows\vars.bat.sample"
+
+ File "${EASYRSA}\Windows\init-config.bat"
+
+ File "${EASYRSA}\Windows\README.txt"
+ File "${EASYRSA}\Windows\build-ca.bat"
+ File "${EASYRSA}\Windows\build-dh.bat"
+ File "${EASYRSA}\Windows\build-key-server.bat"
+ File "${EASYRSA}\Windows\build-key.bat"
+ File "${EASYRSA}\Windows\build-key-pkcs12.bat"
+ File "${EASYRSA}\Windows\clean-all.bat"
+ File "${EASYRSA}\Windows\index.txt.start"
+ File "${EASYRSA}\Windows\revoke-full.bat"
+ File "${EASYRSA}\Windows\serial.start"
+
+SectionEnd
+
+Section "${PRODUCT_NAME} Service" SecService
+
+ SetOverwrite on
+
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\${PRODUCT_UNIX_NAME}serv.exe"
+
+ SetOutPath "$INSTDIR\config"
+
+ FileOpen $R0 "$INSTDIR\config\README.txt" w
+ FileWrite $R0 "This directory should contain ${PRODUCT_NAME} configuration files$\r$\n"
+ FileWrite $R0 "each having an extension of .${SERV_CONFIG_EXT}$\r$\n"
+ FileWrite $R0 "$\r$\n"
+ FileWrite $R0 "When ${PRODUCT_NAME} is started as a service, a separate ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "process will be instantiated for each configuration file.$\r$\n"
+ FileClose $R0
+
+ SetOutPath "$INSTDIR\sample-config"
+ File "${GEN}\samples\sample.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\client.${SERV_CONFIG_EXT}"
+ File "${GEN}\samples\server.${SERV_CONFIG_EXT}"
+
+ CreateDirectory "$INSTDIR\log"
+ FileOpen $R0 "$INSTDIR\log\README.txt" w
+ FileWrite $R0 "This directory will contain the log files for ${PRODUCT_NAME}$\r$\n"
+ FileWrite $R0 "sessions which are being run as a service.$\r$\n"
+ FileClose $R0
+
+SectionEnd
+
+Section "${PRODUCT_NAME} File Associations" SecFileAssociation
+SectionEnd
+
+Section "OpenSSL DLLs" SecOpenSSLDLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\libeay32.dll"
+ File "${BIN}\ssleay32.dll"
+
+SectionEnd
+
+Section "OpenSSL Utilities" SecOpenSSLUtilities
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\openssl.exe"
+
+SectionEnd
+
+Section "PKCS#11 DLLs" SecPKCS11DLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\libpkcs11-helper-1.dll"
+
+SectionEnd
+
+Section "LZO2 DLLs" SecLZO2DLLs
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\lzo2.dll"
+
+SectionEnd
+
+Section "Microsoft Visual C 9.0 Runtime DLL" SecMSVCR90DLL
+
+ SetOverwrite on
+ SetOutPath "$INSTDIR\bin"
+ File "${BIN}\Microsoft.VC90.CRT\msvcr90.dll"
+ File "${BIN}\Microsoft.VC90.CRT\Microsoft.VC90.CRT.manifest"
+
+SectionEnd
+
+
+
+
+Section "TAP Virtual Ethernet Adapter" SecTAP
+
+ SetOverwrite on
+
+ # Generate TAP driver install script dynamically
+ FileOpen $R0 "$INSTDIR\bin\addtap.bat" w
+ FileWrite $R0 "rem Add a new TAP virtual ethernet adapter$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ # Generate TAP driver removal script dynamically
+ FileOpen $R0 "$INSTDIR\bin\deltapall.bat" w
+ FileWrite $R0 "echo WARNING: this script will delete ALL TAP virtual adapters (use the device manager to delete adapters one at a time)$\r$\n"
+ FileWrite $R0 "pause$\r$\n"
+ FileWrite $R0 '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}$\r$\n'
+ FileWrite $R0 "pause$\r$\n"
+ FileClose $R0
+
+ ; Check if we are running on a 64 bit system.
+ System::Call "kernel32::GetCurrentProcess() i .s"
+ System::Call "kernel32::IsWow64Process(i s, *i .r0)"
+ IntCmp $0 0 tap-32bit
+
+; tap-64bit:
+
+ DetailPrint "We are running on a 64-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+
+ File "${GEN}\amd64\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+
+ File "${GEN}\amd64\OemWin2k.inf"
+ File "${GEN}\amd64\${TAPDRV}"
+
+ # Don't try to install TAP driver signature if it does not exist.
+ File /nonfatal "${GEN}\amd64\${PRODUCT_TAP_ID}.cat"
+
+goto tapend
+
+tap-32bit:
+
+ DetailPrint "We are running on a 32-bit system."
+
+ SetOutPath "$INSTDIR\bin"
+ File "${GEN}\i386\tapinstall.exe"
+
+ SetOutPath "$INSTDIR\driver"
+ File "${GEN}\i386\OemWin2k.inf"
+ File "${GEN}\i386\${TAPDRV}"
+
+ # Don't try to install TAP driver signature if it does not exist.
+ File /nonfatal "${GEN}\i386\${PRODUCT_TAP_ID}.cat"
+
+ tapend:
+
+SectionEnd
+
+Section "Add ${PRODUCT_NAME} to PATH" SecAddPath
+
+ ; remove previously set path (if any)
+ Push "$INSTDIR\bin"
+ Call RemoveFromPath
+
+ ; append our bin directory to end of current user path
+ Push "$INSTDIR\bin"
+ Call AddToPath
+
+SectionEnd
+
+Section "Add Shortcuts to Start Menu" SecAddShortcuts
+
+ ; Required to handle shortcuts properly on Vista/7
+ SetShellVarContext all
+ SetOverwrite on
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Documentation"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Windows Notes.url" "InternetShortcut" "URL" "http://openvpn.net/INSTALL-win32.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Manual Page.url" "InternetShortcut" "URL" "http://openvpn.net/man.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} HOWTO.url" "InternetShortcut" "URL" "http://openvpn.net/howto.html"
+ WriteINIStr "$SMPROGRAMS\${PRODUCT_NAME}\Documentation\${PRODUCT_NAME} Web Site.url" "InternetShortcut" "URL" "http://openvpn.net/"
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\Uninstall.exe"
+
+SectionEnd
+
+;--------------------
+;Post-install section
+
+Section -post
+
+ SetOverwrite on
+
+ ; delete old tapinstall.exe
+ ;Delete "$INSTDIR\bin\tapinstall.exe"
+
+ ; Store README, license, icon
+ SetOverwrite on
+ SetOutPath $INSTDIR
+ File "..\INSTALL-win32.txt"
+ File "..\COPYRIGHT.GPL"
+ File "..\images\${PRODUCT_ICON}"
+
+ ; store sample config files
+ !ifdef SAMPCONF_DIR
+ SetOverwrite on
+ SetOutPath "$INSTDIR\config"
+ !ifdef SAMPCONF_CONF
+ File "${GEN}\conf\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ File "${GEN}\conf\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ File "${GEN}\conf\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ File "${GEN}\conf\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ File "${GEN}\conf\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ File "${GEN}\conf\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ File "${GEN}\conf\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ File "${GEN}\conf\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ ; Try to extract files if present
+ !ifdef EXTRACT_FILES
+ Push "$INSTDIR"
+ Call MultiFileExtract
+ Pop $R0
+ IntCmp $R0 0 +3 +1 +1
+ DetailPrint "MultiFileExtract Failed status=$R0"
+ goto +2
+ DetailPrint "MultiFileExtract Succeeded"
+ !endif
+
+ ;
+ ; install/upgrade TAP driver if selected, using tapinstall.exe
+ ;
+ SectionGetFlags ${SecTAP} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" notap notap
+ ; TAP install/update was selected.
+ ; Should we install or update?
+ ; If tapinstall error occurred, $5 will
+ ; be nonzero.
+ IntOp $5 0 & 0
+ nsExec::ExecToStack '"$INSTDIR\bin\tapinstall.exe" hwids ${TAP}'
+ Pop $R0 # return value/error/timeout
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall hwids returned: $R0"
+
+ ; If tapinstall output string contains "${TAP}" we assume
+ ; that TAP device has been previously installed,
+ ; therefore we will update, not install.
+ Push "${TAP}"
+ Call StrStr
+ Pop $R0
+
+ IntCmp $5 0 "" tapinstall_check_error tapinstall_check_error
+ IntCmp $R0 -1 tapinstall
+
+ ;tapupdate:
+ DetailPrint "TAP UPDATE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" update "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall update returned: $R0"
+ Goto tapinstall_check_error
+
+ tapinstall:
+ DetailPrint "TAP REMOVE OLD TAP"
+
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove TAP0801'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove TAP0801 returned: $R0"
+
+ DetailPrint "TAP INSTALL (${TAP})"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" install "$INSTDIR\driver\OemWin2k.inf" ${TAP}'
+ Pop $R0 # return value/error/timeout
+ Call CheckReboot
+ IntOp $5 $5 | $R0
+ DetailPrint "tapinstall install returned: $R0"
+
+ tapinstall_check_error:
+ DetailPrint "tapinstall cumulative status: $5"
+ IntCmp $5 0 notap
+ MessageBox MB_OK "An error occurred installing the TAP device driver."
+
+ notap:
+
+ ; Store install folder in registry
+ WriteRegStr HKLM SOFTWARE\${PRODUCT_NAME} "" $INSTDIR
+
+ ; install as a service if requested
+ SectionGetFlags ${SecService} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noserv noserv
+
+ ; set registry parameters for openvpnserv
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_dir" "${SERV_CONFIG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "config_ext" "${SERV_CONFIG_EXT}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "exe_path" "${SERV_EXE_PATH}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_dir" "${SERV_LOG_DIR}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "priority" "${SERV_PRIORITY}"
+ !insertmacro WriteRegStringIfUndef HKLM "SOFTWARE\${PRODUCT_NAME}" "log_append" "${SERV_LOG_APPEND}"
+
+ ; install openvpnserv as a service (to be started manually from service control manager)
+ DetailPrint "Service INSTALL"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -install'
+ Pop $R0 # return value/error/timeout
+
+ noserv:
+
+ ; Create file association if requested
+ fileass:
+ SectionGetFlags ${SecFileAssociation} $R0
+ IntOp $R0 $R0 & ${SF_SELECTED}
+ IntCmp $R0 ${SF_SELECTED} "" noass noass
+ WriteRegStr HKCR ".${SERV_CONFIG_EXT}" "" "${PRODUCT_NAME}File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File" "" "${PRODUCT_NAME} Config File"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell" "" "open"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\DefaultIcon" "" "$INSTDIR\${PRODUCT_ICON},0"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\open\command" "" 'notepad.exe "%1"'
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run" "" "Start ${PRODUCT_NAME} on this config file"
+ WriteRegStr HKCR "${PRODUCT_NAME}File\shell\run\command" "" '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" --pause-exit --config "%1"'
+
+ ; Create start menu folders
+ noass:
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Utilities"
+ CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts"
+
+ ; Create start menu and desktop shortcuts to OpenVPN GUI
+ !ifdef USE_GUI
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}" ""
+ CreateShortcut "$DESKTOP\${PRODUCT_NAME} GUI.lnk" "$INSTDIR\bin\${OPENVPN_GUI}"
+ !endif
+
+ ; Create start menu shortcuts to addtap.bat and deltapall.bat
+ tryaddtap:
+ IfFileExists "$INSTDIR\bin\addtap.bat" "" trydeltap
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Add a new TAP virtual ethernet adapter.lnk" "$INSTDIR\bin\addtap.bat" ""
+
+ trydeltap:
+ IfFileExists "$INSTDIR\bin\deltapall.bat" "" config_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Delete ALL TAP virtual ethernet adapters.lnk" "$INSTDIR\bin\deltapall.bat" ""
+
+ ; Create start menu shortcuts for config and log directories
+ config_shortcut:
+ IfFileExists "$INSTDIR\config" "" log_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} configuration file directory.lnk" "$INSTDIR\config" ""
+
+ log_shortcut:
+ IfFileExists "$INSTDIR\log" "" samp_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} log file directory.lnk" "$INSTDIR\log" ""
+
+ samp_shortcut:
+ IfFileExists "$INSTDIR\sample-config" "" genkey_shortcut
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Shortcuts\${PRODUCT_NAME} Sample Configuration Files.lnk" "$INSTDIR\sample-config" ""
+
+ genkey_shortcut:
+ IfFileExists "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" "" noshortcuts
+ IfFileExists "$INSTDIR\config" "" noshortcuts
+ CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Utilities\Generate a static ${PRODUCT_NAME} key.lnk" "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe" '--pause-exit --verb 3 --genkey --secret "$INSTDIR\config\key.txt"' "$INSTDIR\${PRODUCT_ICON}" 0
+
+ noshortcuts:
+ ; Create uninstaller
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ ; Show up in Add/Remove programs
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME} ${VERSION}"
+ WriteRegExpandStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayIcon" "$INSTDIR\${PRODUCT_ICON}"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${VERSION}"
+
+ ; Advise a reboot
+ ;Messagebox MB_OK "IMPORTANT: Rebooting the system is advised in order to finalize TAP driver installation/upgrade (this is an informational message only, pressing OK will not reboot)."
+
+SectionEnd
+
+;--------------------------------
+;Descriptions
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNUserSpace} $(DESC_SecOpenVPNUserSpace)
+ !ifdef USE_GUI
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNGUI} $(DESC_SecOpenVPNGUI)
+ !endif
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenVPNEasyRSA} $(DESC_SecOpenVPNEasyRSA)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecTAP} $(DESC_SecTAP)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLUtilities} $(DESC_SecOpenSSLUtilities)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecOpenSSLDLLs} $(DESC_SecOpenSSLDLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecPKCS11DLLs} $(DESC_SecPKCS11DLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecLZO2DLLs} $(DESC_SecLZO2DLLs)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecMSVCR90DLL} $(DESC_SecMSVCR90DLL)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddPath} $(DESC_SecAddPath)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecAddShortcuts} $(DESC_SecAddShortcuts)
+
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecFileAssociation} $(DESC_SecFileAssociation)
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Function un.onInit
+ ClearErrors
+ UserInfo::GetName
+ IfErrors ok
+ Pop $R0
+ UserInfo::GetAccountType
+ Pop $R1
+ StrCmp $R1 "Admin" ok
+ Messagebox MB_OK "Administrator privileges required to uninstall ${PRODUCT_NAME} [$R0/$R1]"
+ Abort
+ ok:
+FunctionEnd
+
+Section "Uninstall"
+
+ ; Required to handle shortcuts properly on Vista/7
+ SetShellVarContext all
+
+ ; Stop OpenVPN if currently running
+
+ DetailPrint "Service REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe" -remove'
+ Pop $R0 # return value/error/timeout
+
+ Sleep 3000
+
+ DetailPrint "TAP REMOVE"
+ nsExec::ExecToLog '"$INSTDIR\bin\tapinstall.exe" remove ${TAP}'
+ Pop $R0 # return value/error/timeout
+ DetailPrint "tapinstall remove returned: $R0"
+
+ Push "$INSTDIR\bin"
+ Call un.RemoveFromPath
+
+ RMDir /r $SMPROGRAMS\${PRODUCT_NAME}
+
+ ; delete sample config files
+ !ifdef SAMPCONF_DIR
+ !ifdef SAMPCONF_CONF
+ Delete "$INSTDIR\config\${SAMPCONF_CONF}"
+ !endif
+ !ifdef SAMPCONF_CONF2
+ Delete "$INSTDIR\config\${SAMPCONF_CONF2}"
+ !endif
+ !ifdef SAMPCONF_P12
+ Delete "$INSTDIR\config\${SAMPCONF_P12}"
+ !endif
+ !ifdef SAMPCONF_TA
+ Delete "$INSTDIR\config\${SAMPCONF_TA}"
+ !endif
+ !ifdef SAMPCONF_CA
+ Delete "$INSTDIR\config\${SAMPCONF_CA}"
+ !endif
+ !ifdef SAMPCONF_CRT
+ Delete "$INSTDIR\config\${SAMPCONF_CRT}"
+ !endif
+ !ifdef SAMPCONF_KEY
+ Delete "$INSTDIR\config\${SAMPCONF_KEY}"
+ !endif
+ !ifdef SAMPCONF_DH
+ Delete "$INSTDIR\config\${SAMPCONF_DH}"
+ !endif
+ !endif
+
+ !ifdef USE_GUI
+ Delete "$INSTDIR\bin\${OPENVPN_GUI}"
+ Delete "$DESKTOP\${PRODUCT_NAME} GUI.lnk"
+ !endif
+
+ # Files installed by openvpn-2.2-beta5 and earlier
+ Delete "$INSTDIR\easy-rsa\openssl.cnf.sample"
+ Delete "$INSTDIR\license"
+ Delete "$INSTDIR\bin\libssl32.dll"
+
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}.exe"
+ Delete "$INSTDIR\bin\${PRODUCT_UNIX_NAME}serv.exe"
+ Delete "$INSTDIR\bin\libeay32.dll"
+ Delete "$INSTDIR\bin\ssleay32.dll"
+ Delete "$INSTDIR\bin\libpkcs11-helper-1.dll"
+ Delete "$INSTDIR\bin\lzo2.dll"
+ Delete "$INSTDIR\bin\msvcr90.dll"
+ Delete "$INSTDIR\bin\Microsoft.VC90.CRT.manifest"
+ Delete "$INSTDIR\bin\tapinstall.exe"
+ Delete "$INSTDIR\bin\addtap.bat"
+ Delete "$INSTDIR\bin\deltapall.bat"
+
+ Delete "$INSTDIR\config\README.txt"
+ Delete "$INSTDIR\config\sample.${SERV_CONFIG_EXT}.txt"
+
+ Delete "$INSTDIR\log\README.txt"
+
+ Delete "$INSTDIR\driver\OemWin2k.inf"
+ Delete "$INSTDIR\driver\${PRODUCT_TAP_ID}.cat"
+ Delete "$INSTDIR\driver\${TAPDRV}"
+
+ Delete "$INSTDIR\bin\openssl.exe"
+
+ Delete "$INSTDIR\INSTALL-win32.txt"
+ Delete "$INSTDIR\${PRODUCT_ICON}"
+ Delete "$INSTDIR\COPYRIGHT.GPL"
+ Delete "$INSTDIR\Uninstall.exe"
+
+ Delete "$INSTDIR\easy-rsa\openssl.cnf"
+ Delete "$INSTDIR\easy-rsa\vars.bat.sample"
+ Delete "$INSTDIR\easy-rsa\init-config.bat"
+ Delete "$INSTDIR\easy-rsa\README.txt"
+ Delete "$INSTDIR\easy-rsa\build-ca.bat"
+ Delete "$INSTDIR\easy-rsa\build-dh.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-server.bat"
+ Delete "$INSTDIR\easy-rsa\build-key.bat"
+ Delete "$INSTDIR\easy-rsa\build-key-pkcs12.bat"
+ Delete "$INSTDIR\easy-rsa\clean-all.bat"
+ Delete "$INSTDIR\easy-rsa\index.txt.start"
+ Delete "$INSTDIR\easy-rsa\revoke-key.bat"
+ Delete "$INSTDIR\easy-rsa\revoke-full.bat"
+ Delete "$INSTDIR\easy-rsa\serial.start"
+
+ Delete "$INSTDIR\sample-config\*.${PRODUCT_FILE_EXT}"
+
+ RMDir "$INSTDIR\bin"
+ RMDir "$INSTDIR\config"
+ RMDir "$INSTDIR\driver"
+ RMDir "$INSTDIR\easy-rsa"
+ RMDir "$INSTDIR\sample-config"
+ RMDir /r "$INSTDIR\log"
+ RMDir "$INSTDIR"
+
+ !insertmacro DelRegKeyIfUnchanged HKCR ".${SERV_CONFIG_EXT}" "${PRODUCT_NAME}File"
+ DeleteRegKey HKCR "${PRODUCT_NAME}File"
+ DeleteRegKey HKLM SOFTWARE\${PRODUCT_NAME}
+ DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+
+SectionEnd
diff --git a/win/setpath.nsi b/win/setpath.nsi
new file mode 100755
index 0000000..a9626c3
--- /dev/null
+++ b/win/setpath.nsi
@@ -0,0 +1,231 @@
+; Modify the user's PATH variable.
+;
+; Modified by JY to have both a RemoveFromPath
+; and an un.RemoveFromPath which are basically
+; copies of each other. Why does NSIS demand
+; this nonsense?
+;
+; Modified Feb 14, 2005 by Mathias Sundman:
+; Added code to remove the semicolon at the end of the path
+; when uninstalling.
+;
+; Added code to make sure we don't insert an extra semicolon
+; before our path if there already exist one at the end of
+; the original path.
+;
+; Removed duplicated "un. and install" functions and made
+; macros to duplicate the code instead.
+
+; example usage
+;
+;Section "Add to path"
+; Push $INSTDIR
+; Call AddToPath
+;SectionEnd
+;
+;# ...
+;
+;Section "uninstall"
+; # ...
+; Push $INSTDIR
+; Call un.RemoveFromPath
+; # ...
+;SectionEnd
+
+!verbose 3
+!include "WinMessages.NSH"
+!verbose 4
+
+;====================================================
+; AddToPath - Adds the given dir to the search path.
+; Input - head of the stack
+; Note - Win9x systems requires reboot
+;====================================================
+Function AddToPath
+ Exch $0
+ Push $1
+ Push $2
+
+ Call IsNT
+ Pop $1
+ StrCmp $1 1 AddToPath_NT
+ ; Not on NT
+ StrCpy $1 $WINDIR 2
+ FileOpen $1 "$1\autoexec.bat" a
+ FileSeek $1 0 END
+ GetFullPathName /SHORT $0 $0
+ FileWrite $1 "$\r$\nSET PATH=%PATH%;$0$\r$\n"
+ FileClose $1
+ Goto AddToPath_done
+
+ AddToPath_NT:
+ ReadRegStr $1 HKCU "Environment" "PATH"
+ StrCpy $2 $1 1 -1 # copy last char
+ StrCmp $2 ";" 0 +2 # if last char == ;
+ StrCpy $1 $1 -1 # remove last char
+
+ StrCmp $1 "" AddToPath_NTdoIt
+ StrCpy $0 "$1;$0"
+ Goto AddToPath_NTdoIt
+ AddToPath_NTdoIt:
+ WriteRegExpandStr HKCU "Environment" "PATH" $0
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+ AddToPath_done:
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+;====================================================
+; RemoveFromPath - Remove a given dir from the path
+; Input: head of the stack
+;====================================================
+!macro RemoveFromPath un
+Function ${un}RemoveFromPath
+ Exch $0
+ Push $1
+ Push $2
+ Push $3
+ Push $4
+ Push $5
+
+ Call ${un}IsNT
+ Pop $1
+ StrCmp $1 1 RemoveFromPath_NT
+ ; Not on NT
+ StrCpy $1 $WINDIR 2
+ FileOpen $1 "$1\autoexec.bat" r
+ GetTempFileName $4
+ FileOpen $2 $4 w
+ GetFullPathName /SHORT $0 $0
+ StrCpy $0 "SET PATH=%PATH%;$0"
+ SetRebootFlag true
+ Goto RemoveFromPath_dosLoop
+
+ RemoveFromPath_dosLoop:
+ FileRead $1 $3
+ StrCmp $3 "$0$\r$\n" RemoveFromPath_dosLoop
+ StrCmp $3 "$0$\n" RemoveFromPath_dosLoop
+ StrCmp $3 "$0" RemoveFromPath_dosLoop
+ StrCmp $3 "" RemoveFromPath_dosLoopEnd
+ FileWrite $2 $3
+ Goto RemoveFromPath_dosLoop
+
+ RemoveFromPath_dosLoopEnd:
+ FileClose $2
+ FileClose $1
+ StrCpy $1 $WINDIR 2
+ Delete "$1\autoexec.bat"
+ CopyFiles /SILENT $4 "$1\autoexec.bat"
+ Delete $4
+ Goto RemoveFromPath_done
+
+ RemoveFromPath_NT:
+ StrLen $2 $0
+ ReadRegStr $1 HKCU "Environment" "PATH"
+ Push $1
+ Push $0
+ Call ${un}StrStr ; Find $0 in $1
+ Pop $0 ; pos of our dir
+ IntCmp $0 -1 RemoveFromPath_done
+ ; else, it is in path
+ StrCpy $3 $1 $0 ; $3 now has the part of the path before our dir
+ IntOp $2 $2 + $0 ; $2 now contains the pos after our dir in the path (';')
+ IntOp $2 $2 + 1 ; $2 now containts the pos after our dir and the semicolon.
+ StrLen $0 $1
+ StrCpy $1 $1 $0 $2
+ StrCpy $3 "$3$1"
+
+ StrCpy $5 $3 1 -1 # copy last char
+ StrCmp $5 ";" 0 +2 # if last char == ;
+ StrCpy $3 $3 -1 # remove last char
+
+ WriteRegExpandStr HKCU "Environment" "PATH" $3
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+ RemoveFromPath_done:
+ Pop $5
+ Pop $4
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+!macroend
+!insertmacro RemoveFromPath ""
+!insertmacro RemoveFromPath "un."
+
+
+;====================================================
+; StrStr - Finds a given string in another given string.
+; Returns -1 if not found and the pos if found.
+; Input: head of the stack - string to find
+; second in the stack - string to find in
+; Output: head of the stack
+;====================================================
+!macro StrStr un
+Function ${un}StrStr
+ Push $0
+ Exch
+ Pop $0 ; $0 now have the string to find
+ Push $1
+ Exch 2
+ Pop $1 ; $1 now have the string to find in
+ Exch
+ Push $2
+ Push $3
+ Push $4
+ Push $5
+
+ StrCpy $2 -1
+ StrLen $3 $0
+ StrLen $4 $1
+ IntOp $4 $4 - $3
+
+ StrStr_loop:
+ IntOp $2 $2 + 1
+ IntCmp $2 $4 0 0 StrStrReturn_notFound
+ StrCpy $5 $1 $3 $2
+ StrCmp $5 $0 StrStr_done StrStr_loop
+
+ StrStrReturn_notFound:
+ StrCpy $2 -1
+
+ StrStr_done:
+ Pop $5
+ Pop $4
+ Pop $3
+ Exch $2
+ Exch 2
+ Pop $0
+ Pop $1
+FunctionEnd
+!macroend
+!insertmacro StrStr ""
+!insertmacro StrStr "un."
+
+;====================================================
+; IsNT - Returns 1 if the current system is NT, 0
+; otherwise.
+; Output: head of the stack
+;====================================================
+!macro IsNT un
+Function ${un}IsNT
+ Push $0
+ ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion
+ StrCmp $0 "" 0 IsNT_yes
+ ; we are not NT.
+ Pop $0
+ Push 0
+ Return
+
+ IsNT_yes:
+ ; NT!!!
+ Pop $0
+ Push 1
+FunctionEnd
+!macroend
+!insertmacro IsNT ""
+!insertmacro IsNT "un."
+
diff --git a/win/settings.in b/win/settings.in
new file mode 100644
index 0000000..10c7926
--- /dev/null
+++ b/win/settings.in
@@ -0,0 +1,87 @@
+# Version numbers, settings, and dependencies
+# for Windows OpenVPN installer.
+#
+# Note that some variables are parsed by wb.py from version.m4 and are not
+# stored in this file. This is done to allow using the old and new Windows build
+# systems side-by-side
+
+# Features to include. DO NOT comment these out, use 1 to enable and 0 to
+# disable.
+!define ENABLE_PASSWORD_SAVE 1
+
+# ENABLE_CLIENT_SERVER enables the point-to-multipoint support. Normally you
+# want to have this enabled.
+!define ENABLE_CLIENT_SERVER 1
+
+# ENABLE_CLIENT_ONLY removes server-side point-to-multipoint features. This
+# depends on ENABLE_CLIENT_SERVER being set to 1.
+!define ENABLE_CLIENT_ONLY 0
+
+!define ENABLE_MANAGEMENT 1
+!define ENABLE_HTTP_PROXY 1
+!define ENABLE_SOCKS 1
+!define ENABLE_FRAGMENT 1
+!define ENABLE_DEBUG 1
+
+# Branding
+!define PRODUCT_NAME "OpenVPN"
+!define PRODUCT_UNIX_NAME "openvpn"
+!define PRODUCT_FILE_EXT "ovpn"
+
+# Include the OpenVPN GUI exe in the installer. Comment out USE_GUI to disable.
+!define USE_GUI
+!define OPENVPN_GUI_DIR "../openvpn-gui"
+!define OPENVPN_GUI "openvpn-gui-1.0.3.exe"
+
+# Prebuilt libraries. DMALLOC is optional.
+!define OPENSSL_DIR "../openssl"
+!define LZO_DIR "../lzo"
+!define PKCS11_HELPER_DIR "../pkcs11-helper"
+
+# write output files here
+!define DIST "dist"
+
+# tapinstall.exe (a.k.a. devcon.exe) source code. Not needed if DRVBINSRC is
+# defined (or if using pre-built mode).
+!define TISRC "../tapinstall"
+
+# TAP adapter icon -- visible=0x81 or hidden=0x89
+!define PRODUCT_TAP_CHARACTERISTICS 0x81
+
+# TAP adapter metadata. Version information in ../version.m4.
+!define PRODUCT_TAP_RELDATE "04/19/2010"
+!define PRODUCT_TAP_DEVICE_DESCRIPTION "TAP-Win32 Adapter V9"
+!define PRODUCT_TAP_PROVIDER "TAP-Win32 Provider V9"
+
+# Build debugging version of TAP driver
+;!define PRODUCT_TAP_DEBUG
+
+# Build debugging version of openvpn.exe
+;!define PRODUCT_OPENVPN_DEBUG
+
+# DDK path -- currently Windows 7 WDK
+!define DDK_PATH "c:/winddk/7600.16385.1"
+;!define DDK_PATH "c:/winddk/6001.18002"
+
+# output path for tap_span.py
+!define TAP_DIST "tap_dist"
+
+# Visual studio path
+!define MSVC "C:/Program Files/Microsoft Visual Studio 9.0"
+
+# Visual studio C run-time library path
+!define MSVC_CRT "../Microsoft.VC90.CRT"
+
+# Code Signing.
+# If undefined, don't sign any files.
+!define SIGNTOOL "../signtool"
+!define PRODUCT_SIGN_CN "openvpn"
+
+# Directory with prebuilt TAP drivers and tapinstall.exes
+!define TAP_PREBUILT "../tap-prebuilt"
+
+; DEBUGGING -- set to something like "-DBG2"
+!define OUTFILE_LABEL ""
+
+; DEBUGGING -- set to something like "DEBUG2"
+!define TITLE_LABEL ""
diff --git a/win/show.py b/win/show.py
new file mode 100644
index 0000000..ac56e98
--- /dev/null
+++ b/win/show.py
@@ -0,0 +1,9 @@
+from wb import get_config
+from js import JSON
+
+def main():
+ print JSON().encode(get_config())
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ main()
diff --git a/win/sign.py b/win/sign.py
new file mode 100644
index 0000000..67d1cbc
--- /dev/null
+++ b/win/sign.py
@@ -0,0 +1,19 @@
+import sys
+from wb import config, choose_arch, home_fn
+
+if 'SIGNTOOL' in config:
+ sys.path.append(home_fn(config['SIGNTOOL']))
+
+def main(conf, arch):
+ from signtool import SignTool
+ st = SignTool(conf)
+ for x64 in choose_arch(arch):
+ st.sign_verify(x64=x64)
+
+# if we are run directly, and not loaded as a module
+if __name__ == "__main__":
+ if len(sys.argv) >= 2:
+ main(config, sys.argv[1])
+ else:
+ print "usage: sign <x64|x86|all>"
+ sys.exit(2)
diff --git a/win/tap_span.py b/win/tap_span.py
new file mode 100644
index 0000000..749f6f3
--- /dev/null
+++ b/win/tap_span.py
@@ -0,0 +1,129 @@
+import sys, os, shutil
+from wb import config, home_fn, mod_fn, preprocess, autogen, dict_def, build_autodefs, rm_rf, mkdir_silent, cp
+if 'SIGNTOOL' in config:
+ sys.path.append(home_fn(config['SIGNTOOL']))
+from signtool import SignTool
+from build_ddk import build_tap
+
+ti_dir = "c:/src/tapinstall"
+hi = ("c:/winddk/7600.16385.1", 7600, 7600, ("i386", "amd64"))
+low = ("c:/winddk/6001.18002", 6001, 5600, ("win2k",))
+dest_top = home_fn('tap_build')
+dist = home_fn(config['TAP_DIST'])
+
+def copy_tap(src, dest, x64):
+ dir = os.path.join(src, { False : 'i386', True: 'amd64' }[x64])
+ mkdir_silent(dest)
+ for dirpath, dirnames, filenames in os.walk(dir):
+ for f in filenames:
+ root, ext = os.path.splitext(f)
+ if ext in ('.inf', '.cat', '.sys'):
+ cp(os.path.join(dir, f), dest)
+ break
+
+def copy_tapinstall(src, dest, x64):
+ base = { False : 'i386', True: 'amd64' }[x64]
+ mkdir_silent(dest)
+ for dirpath, dirnames, filenames in os.walk(home_fn(src)):
+ for f in filenames:
+ if f == 'devcon.exe':
+ dir_name = os.path.basename(dirpath)
+ s = os.path.join(dirpath, f)
+ if dir_name == base:
+ cp(s, dest)
+
+def main():
+ rm_rf(dest_top)
+ os.mkdir(dest_top)
+
+ rm_rf(dist)
+ os.mkdir(dist)
+
+ for ver in hi, low:
+ top = os.path.join(dest_top, str(ver[1]))
+ os.mkdir(top)
+ tap_dest = os.path.join(top, "tap-win32")
+ ti_dest = os.path.join(top, "tapinstall")
+ ti_src = os.path.join(ti_dir, str(ver[2]))
+ shutil.copytree(home_fn("tap-win32"), tap_dest)
+ shutil.copytree(ti_src, ti_dest)
+
+ i386 = os.path.join(tap_dest, "i386")
+ amd64 = os.path.join(tap_dest, "amd64")
+
+ build_amd64 = (len(ver[3]) >= 2)
+
+ build_autodefs(config, mod_fn('autodefs.h.in'), os.path.join(top, 'autodefs.h'))
+
+ st = SignTool(config, tap_dest)
+
+ preprocess(config,
+ in_fn=os.path.join(tap_dest, 'SOURCES.in'),
+ out_fn=os.path.join(tap_dest, 'SOURCES'),
+ quote_begin='@@',
+ quote_end='@@',
+ head_comment='# %s\n\n' % autogen)
+
+ preprocess(config,
+ in_fn=os.path.join(i386, 'OemWin2k.inf.in'),
+ out_fn=os.path.join(i386, 'OemWin2k.inf'),
+ quote_begin='@@',
+ quote_end='@@',
+ if_prefix='!',
+ head_comment='; %s\n\n' % autogen)
+
+ preprocess(config,
+ in_fn=os.path.join(ti_dest, 'sources.in'),
+ out_fn=os.path.join(ti_dest, 'sources'),
+ if_prefix='!',
+ head_comment='# %s\n\n' % autogen)
+
+ build_tap(ddk_path=ver[0],
+ ddk_major=ver[1],
+ debug=False,
+ dir=tap_dest,
+ x64=False)
+
+ st.sign_verify(x64=False)
+
+ build_tap(ddk_path=ver[0],
+ ddk_major=ver[1],
+ debug=False,
+ dir=ti_dest,
+ x64=False)
+
+ tap_dist = os.path.join(dist, ver[3][0])
+
+ copy_tap(tap_dest, tap_dist, x64=False)
+ copy_tapinstall(ti_dest, tap_dist, x64=False)
+
+ if build_amd64:
+ os.mkdir(amd64)
+ preprocess(dict_def(config, [('AMD64', '1')]),
+ in_fn=os.path.join(i386, 'OemWin2k.inf.in'),
+ out_fn=os.path.join(amd64, 'OemWin2k.inf'),
+ quote_begin='@@',
+ quote_end='@@',
+ if_prefix='!',
+ head_comment='; %s\n\n' % autogen)
+
+ build_tap(ddk_path=ver[0],
+ ddk_major=ver[1],
+ debug=False,
+ dir=tap_dest,
+ x64=True)
+
+ build_tap(ddk_path=ver[0],
+ ddk_major=ver[1],
+ debug=False,
+ dir=ti_dest,
+ x64=True)
+
+ st.sign_verify(x64=True)
+
+ tap_dist_x64 = os.path.join(dist, ver[3][1])
+
+ copy_tap(tap_dest, tap_dist_x64, x64=True)
+ copy_tapinstall(ti_dest, tap_dist_x64, x64=True)
+
+main()
diff --git a/win/wb.py b/win/wb.py
new file mode 100644
index 0000000..39eadec
--- /dev/null
+++ b/win/wb.py
@@ -0,0 +1,322 @@
+# Python module containing general build functions
+# for OpenVPN on Windows
+
+import os, re, shutil, stat
+
+autogen = "Automatically generated by OpenVPN Windows build system"
+
+def get_config():
+ kv = {}
+ parse_version_m4(kv, home_fn('version.m4'))
+ parse_settings_in(kv, mod_fn('settings.in'))
+
+ # config fixups
+ kv['DDKVER'] = os.path.basename(kv['DDK_PATH'])
+ kv['DDKVER_MAJOR'] = re.match(r'^(\d+)\.', kv['DDKVER']).groups()[0]
+
+ if 'VERSION_SUFFIX' in kv:
+ kv['PRODUCT_VERSION'] += kv['VERSION_SUFFIX']
+
+ return kv
+
+def get_build_params():
+ kv = {}
+ parse_build_params(kv,mod_fn('settings.in'))
+
+ return kv
+
+def mod_fn(fn, src=__file__, real=True):
+ p = os.path.join(os.path.dirname(src), os.path.normpath(fn))
+ if real:
+ p = os.path.realpath(p)
+ return p
+
+def home_fn(fn, real=True):
+ return mod_fn(os.path.join('..', fn), real=real)
+
+def cd_home():
+ os.chdir(os.path.join(os.path.dirname(__file__), '..'))
+
+def cd_service_win32():
+ os.chdir(os.path.join(os.path.dirname(__file__), '../service-win32'))
+
+def system(cmd):
+ print "RUN:", cmd
+ os.system(cmd)
+
+def run_in_vs_shell(cmd):
+ """Make sure environment variables are setup before running command"""
+ os.environ['PATH'] += ";%s\\VC" % (os.path.normpath(config['MSVC']),)
+ system('cmd /c "vcvarsall.bat x86 && %s"' % (cmd,))
+
+def parse_version_m4(kv, version_m4):
+ '''Parse define lines in version.m4'''
+ r = re.compile(r'^define\((\w+),\[(.*)\]\)$')
+ f = open(version_m4)
+ for line in f:
+ line = line.rstrip()
+ m = re.match(r, line)
+
+ if m:
+ g = m.groups()
+
+ # If we encounter PRODUCT_TAP_WIN32_MIN_MAJOR or
+ # PRODUCT_TAP_WIN32_MIN_MAJOR then we need to generate extra
+ # variables, PRODUCT_TAP_MAJOR_VER and PRODUCT_TAP_MINOR_VER with
+ # the same contents. This is necessary because tap-win32/tapdrv.c
+ # build depends on those.
+ if g[0] == 'PRODUCT_TAP_WIN32_MIN_MAJOR':
+ kv['PRODUCT_TAP_MAJOR_VER'] = g[1]
+ elif g[0] == 'PRODUCT_TAP_WIN32_MIN_MINOR':
+ kv['PRODUCT_TAP_MINOR_VER'] = g[1]
+
+ # Add the variable to build configuration
+ kv[g[0]] = g[1]
+ f.close()
+
+def parse_settings_in(kv, settings_in):
+ r = re.compile(r'^!define\s+(\w+)(?:\s+"?(.*?)"?)?$')
+ f = open(settings_in)
+ for line in f:
+ line = line.rstrip()
+ m = re.match(r, line)
+ if m:
+ g = m.groups()
+ kv[g[0]] = g[1] or ''
+ f.close()
+
+def parse_build_params(kv, settings_in):
+ r = re.compile(r'^!define\s+(ENABLE_\w+)\s+(\w+)')
+
+ f = open(settings_in)
+
+ for line in f:
+ line = line.rstrip()
+
+ # Check if this is a #define line starts with ENABLE_
+ m = re.match(r, line)
+
+ if m:
+ g = m.groups()
+ kv[g[0]] = g[1] or ''
+ f.close()
+
+def dict_def(dict, newdefs):
+ ret = dict.copy()
+ ret.update(newdefs)
+ return ret
+
+def build_autodefs(kv, autodefs_in, autodefs_out):
+ preprocess(kv,
+ in_fn=autodefs_in,
+ out_fn=autodefs_out,
+ quote_begin='@',
+ quote_end='@',
+ head_comment='/* %s */\n\n' % autogen)
+
+def build_config_h(kv):
+ """Generate static win/config.h to config.h to mimic autotools behavior"""
+ preprocess(kv,
+ in_fn=mod_fn('config.h.in'),
+ out_fn=home_fn('config.h'),
+ quote_begin='@',
+ quote_end='@',
+ head_comment='/* %s */\n\n' % autogen)
+
+def build_configure_h(kv, configure_h_out, head_comment):
+ """Generate a configure.h dynamically"""
+ fout = open(configure_h_out, 'w')
+
+ # These two variables are required to view build parameters during runtime
+ configure_defines='#define CONFIGURE_DEFINES \"'
+ configure_call='#define CONFIGURE_CALL \" config_all.py \"'
+
+ # Initialize the list of enabled features
+ features = ''
+
+ # Write the header
+ fout.write(head_comment)
+
+ dict = get_build_params()
+
+ for key, value in dict.iteritems():
+ # Add enabled features
+ features = features + "#define " + key + " " + value + "\n"
+
+ # Add each enabled feature to CONFIGURE_DEFINES list
+ configure_defines = configure_defines + " " + key + "=" + value + ","
+
+ configure_defines = configure_defines + "\"" + "\n"
+
+ fout.write(features)
+ fout.write(configure_defines)
+ fout.write(configure_call)
+
+
+ fout.close()
+
+def build_version_m4_vars(version_m4_vars_out, head_comment):
+ """Generate a temporary file containing variables from version.m4 in
+win/settings.in format. This done to allow importing them in win/openvpn.nsi"""
+
+ fout = open(version_m4_vars_out, 'w')
+ fout.write(head_comment)
+
+ kv = {}
+ parse_version_m4(kv, home_fn('version.m4'))
+
+ for key, value in kv.iteritems():
+ line = "!define " + key + "\t" + "\"" + value + "\"" + "\n"
+ fout.write(line)
+
+ fout.close()
+
+def preprocess(kv, in_fn, out_fn, quote_begin=None, quote_end=None, if_prefix=None, head_comment=None):
+ def repfn(m):
+ var, = m.groups()
+ return kv.get(var, '')
+
+ re_macro = re_ifdef = None
+
+ if quote_begin and quote_end:
+ re_macro = re.compile(r'%s(\w+)%s' % (re.escape(quote_begin), re.escape(quote_end)))
+
+ if if_prefix:
+ re_ifdef = re.compile(r'^\s*%sifdef\s+(\w+)\b' % (re.escape(if_prefix),))
+ re_else = re.compile(r'^\s*%selse\b' % (re.escape(if_prefix),))
+ re_endif = re.compile(r'^\s*%sendif\b' % (re.escape(if_prefix),))
+
+ if_stack = []
+ fin = open(in_fn)
+ fout = open(out_fn, 'w')
+ if head_comment:
+ fout.write(head_comment)
+ for line in fin:
+ if re_ifdef:
+ m = re.match(re_ifdef, line)
+ if m:
+ var, = m.groups()
+ if_stack.append(int(var in kv))
+ continue
+ elif re.match(re_else, line):
+ if_stack[-1] ^= 1
+ continue
+ elif re.match(re_endif, line):
+ if_stack.pop()
+ continue
+ if not if_stack or min(if_stack):
+ if re_macro:
+ line = re.sub(re_macro, repfn, line)
+ fout.write(line)
+ assert not if_stack
+ fin.close()
+ fout.close()
+
+def print_key_values(kv):
+ for k, v in sorted(kv.items()):
+ print "%s%s%s" % (k, ' '*(32-len(k)), repr(v))
+
+def get_sources(makefile_am):
+ """Parse ../Makefile.am to obtain a list of .h and .c files"""
+ c = set()
+ h = set()
+ f = open(makefile_am)
+ state = False
+ for line in f:
+ line = line.rstrip()
+ if line == 'openvpn_SOURCES = \\':
+ state = True
+ elif not line:
+ state = False
+ elif state:
+ for sf in line.split():
+ if sf.endswith('.c'):
+ c.add(sf[:-2])
+ elif sf.endswith('.h'):
+ h.add(sf[:-2])
+ elif sf == '\\':
+ pass
+ else:
+ print >>sys.stderr, "Unrecognized filename:", sf
+ f.close()
+ return [ sorted(list(s)) for s in (c, h) ]
+
+def output_mak_list(title, srclist, ext):
+ ret = "%s =" % (title,)
+ for x in srclist:
+ ret += " \\\n\t%s.%s" % (x, ext)
+ ret += '\n\n'
+ return ret
+
+def make_headers_objs(makefile_am):
+ """Generate HEADER and OBJS entries dynamically from ../Makefile.am"""
+ c, h = get_sources(makefile_am)
+ ret = output_mak_list('HEADERS', h, 'h')
+ ret += output_mak_list('OBJS', c, 'obj')
+ return ret
+
+def choose_arch(arch_name):
+ if arch_name == 'x64':
+ return (True,)
+ elif arch_name == 'x86':
+ return (False,)
+ elif arch_name == 'all':
+ return (True, False)
+ else:
+ raise ValueError("architecture ('%s') must be x86, x64, or all" % (arch_name,))
+
+def rm_rf(dir):
+ print "REMOVE", dir
+ shutil.rmtree(dir, ignore_errors=True)
+
+def mkdir(dir):
+ print "MKDIR", dir
+ os.mkdir(dir)
+
+def cp_a(src, dest, dest_is_dir=True):
+ if dest_is_dir:
+ dest = os.path.join(dest, os.path.basename(src))
+ print "COPY_DIR %s %s" % (src, dest)
+ shutil.copytree(src, dest)
+
+def cp(src, dest, dest_is_dir=True):
+ if dest_is_dir:
+ dest = os.path.join(dest, os.path.basename(src))
+ print "COPY %s %s" % (src, dest)
+ shutil.copyfile(src, dest)
+
+def rename(src, dest):
+ print "RENAME %s %s" % (src, dest)
+ shutil.move(src, dest)
+
+def rm_rf(path):
+ try:
+ shutil.rmtree(path, onerror=onerror)
+ except:
+ pass
+
+def onerror(func, path, exc_info):
+ """
+ Error handler for ``shutil.rmtree``.
+
+ If the error is due to an access error (read only file)
+ it attempts to add write permission and then retries.
+
+ If the error is for another reason it re-raises the error.
+
+ Usage : ``shutil.rmtree(path, onerror=onerror)``
+ """
+ if not os.access(path, os.W_OK):
+ # Is the error an access error ?
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+
+def mkdir_silent(dir):
+ try:
+ os.mkdir(dir)
+ except:
+ pass
+
+config = get_config()
diff --git a/win32.c b/win32.c
new file mode 100644
index 0000000..2b7bf7b
--- /dev/null
+++ b/win32.c
@@ -0,0 +1,1115 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Win32-specific OpenVPN code, targetted at the mingw
+ * development environment.
+ */
+#include "syshead.h"
+
+#ifdef WIN32
+
+#include "buffer.h"
+#include "error.h"
+#include "mtu.h"
+#include "sig.h"
+#include "win32.h"
+#include "misc.h"
+
+#include "memdbg.h"
+
+/*
+ * Windows internal socket API state (opaque).
+ */
+static struct WSAData wsa_state; /* GLOBAL */
+
+/*
+ * Should we call win32_pause() on program exit?
+ */
+static bool pause_exit_enabled = false; /* GLOBAL */
+
+/*
+ * win32_signal is used to get input from the keyboard
+ * if we are running in a console, or get input from an
+ * event object if we are running as a service.
+ */
+
+struct win32_signal win32_signal; /* GLOBAL */
+
+/*
+ * Save our old window title so we can restore
+ * it on exit.
+ */
+struct window_title window_title; /* GLOBAL*/
+
+/*
+ * Special global semaphore used to protect network
+ * shell commands from simultaneous instantiation.
+ */
+
+struct semaphore netcmd_semaphore; /* GLOBAL */
+
+/*
+ * Windows system pathname such as c:\windows
+ */
+static char *win_sys_path = NULL; /* GLOBAL */
+
+/*
+ * Configure PATH. On Windows, sometimes PATH is not set correctly
+ * by default.
+ */
+static void
+configure_win_path (void)
+{
+ static bool done = false; /* GLOBAL */
+ if (!done)
+ {
+ FILE *fp;
+ fp = fopen ("c:\\windows\\system32\\route.exe", "rb");
+ if (fp)
+ {
+ const int bufsiz = 4096;
+ struct gc_arena gc = gc_new ();
+ struct buffer oldpath = alloc_buf_gc (bufsiz, &gc);
+ struct buffer newpath = alloc_buf_gc (bufsiz, &gc);
+ const char* delim = ";";
+ DWORD status;
+ fclose (fp);
+ status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath));
+#if 0
+ status = 0;
+#endif
+ if (!status)
+ {
+ *BPTR(&oldpath) = '\0';
+ delim = "";
+ }
+ buf_printf (&newpath, "C:\\WINDOWS\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem%s%s",
+ delim,
+ BSTR(&oldpath));
+ SetEnvironmentVariable ("PATH", BSTR(&newpath));
+#if 0
+ status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath));
+ if (status > 0)
+ printf ("PATH: %s\n", BSTR(&oldpath));
+#endif
+ gc_free (&gc);
+ done = true;
+ }
+ }
+}
+
+void
+init_win32 (void)
+{
+ if (WSAStartup(0x0101, &wsa_state))
+ {
+ msg (M_ERR, "WSAStartup failed");
+ }
+ window_title_clear (&window_title);
+ win32_signal_clear (&win32_signal);
+ netcmd_semaphore_init ();
+}
+
+void
+uninit_win32 (void)
+{
+ netcmd_semaphore_close ();
+ if (pause_exit_enabled)
+ {
+ if (win32_signal.mode == WSO_MODE_UNDEF)
+ {
+ struct win32_signal w;
+ win32_signal_open (&w, WSO_FORCE_CONSOLE, NULL, false);
+ win32_pause (&w);
+ win32_signal_close (&w);
+ }
+ else
+ win32_pause (&win32_signal);
+ }
+ window_title_restore (&window_title);
+ win32_signal_close (&win32_signal);
+ WSACleanup ();
+ free (win_sys_path);
+}
+
+void
+set_pause_exit_win32 (void)
+{
+ pause_exit_enabled = true;
+}
+
+bool
+init_security_attributes_allow_all (struct security_attributes *obj)
+{
+ CLEAR (*obj);
+
+ obj->sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ obj->sa.lpSecurityDescriptor = &obj->sd;
+ obj->sa.bInheritHandle = FALSE;
+ if (!InitializeSecurityDescriptor (&obj->sd, SECURITY_DESCRIPTOR_REVISION))
+ return false;
+ if (!SetSecurityDescriptorDacl (&obj->sd, TRUE, NULL, FALSE))
+ return false;
+ return true;
+}
+
+void
+overlapped_io_init (struct overlapped_io *o,
+ const struct frame *frame,
+ BOOL event_state,
+ bool tuntap_buffer) /* if true: tuntap buffer, if false: socket buffer */
+{
+ CLEAR (*o);
+
+ /* manual reset event, initially set according to event_state */
+ o->overlapped.hEvent = CreateEvent (NULL, TRUE, event_state, NULL);
+ if (o->overlapped.hEvent == NULL)
+ msg (M_ERR, "Error: overlapped_io_init: CreateEvent failed");
+
+ /* allocate buffer for overlapped I/O */
+ alloc_buf_sock_tun (&o->buf_init, frame, tuntap_buffer, 0);
+}
+
+void
+overlapped_io_close (struct overlapped_io *o)
+{
+ if (o->overlapped.hEvent)
+ {
+ if (!CloseHandle (o->overlapped.hEvent))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle failed on overlapped I/O event object");
+ }
+ free_buf (&o->buf_init);
+}
+
+char *
+overlapped_io_state_ascii (const struct overlapped_io *o)
+{
+ switch (o->iostate)
+ {
+ case IOSTATE_INITIAL:
+ return "0";
+ case IOSTATE_QUEUED:
+ return "Q";
+ case IOSTATE_IMMEDIATE_RETURN:
+ return "1";
+ }
+ return "?";
+}
+
+/*
+ * Event-based notification of network events
+ */
+
+void
+init_net_event_win32 (struct rw_handle *event, long network_events, socket_descriptor_t sd, unsigned int flags)
+{
+ /* manual reset events, initially set to unsignaled */
+
+ /* initialize write event */
+ if (!(flags & NE32_PERSIST_EVENT) || !event->write)
+ {
+ if (flags & NE32_WRITE_EVENT)
+ {
+ event->write = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (event->write == NULL)
+ msg (M_ERR, "Error: init_net_event_win32: CreateEvent (write) failed");
+ }
+ else
+ event->write = NULL;
+ }
+
+ /* initialize read event */
+ if (!(flags & NE32_PERSIST_EVENT) || !event->read)
+ {
+ event->read = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (event->read == NULL)
+ msg (M_ERR, "Error: init_net_event_win32: CreateEvent (read) failed");
+ }
+
+ /* setup network events to change read event state */
+ if (WSAEventSelect (sd, event->read, network_events) != 0)
+ msg (M_FATAL | M_ERRNO_SOCK, "Error: init_net_event_win32: WSAEventSelect call failed");
+}
+
+long
+reset_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd)
+{
+ WSANETWORKEVENTS wne;
+ if (WSAEnumNetworkEvents (sd, event->read, &wne) != 0)
+ {
+ msg (M_FATAL | M_ERRNO_SOCK, "Error: reset_net_event_win32: WSAEnumNetworkEvents call failed");
+ return 0; /* NOTREACHED */
+ }
+ else
+ return wne.lNetworkEvents;
+}
+
+void
+close_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd, unsigned int flags)
+{
+ if (event->read)
+ {
+ if (socket_defined (sd))
+ {
+ if (WSAEventSelect (sd, event->read, 0) != 0)
+ msg (M_WARN | M_ERRNO_SOCK, "Warning: close_net_event_win32: WSAEventSelect call failed");
+ }
+ if (!ResetEvent (event->read))
+ msg (M_WARN | M_ERRNO, "Warning: ResetEvent (read) failed in close_net_event_win32");
+ if (!(flags & NE32_PERSIST_EVENT))
+ {
+ if (!CloseHandle (event->read))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle (read) failed in close_net_event_win32");
+ event->read = NULL;
+ }
+ }
+
+ if (event->write)
+ {
+ if (!ResetEvent (event->write))
+ msg (M_WARN | M_ERRNO, "Warning: ResetEvent (write) failed in close_net_event_win32");
+ if (!(flags & NE32_PERSIST_EVENT))
+ {
+ if (!CloseHandle (event->write))
+ msg (M_WARN | M_ERRNO, "Warning: CloseHandle (write) failed in close_net_event_win32");
+ event->write = NULL;
+ }
+ }
+}
+
+/*
+ * struct net_event_win32
+ */
+
+void
+net_event_win32_init (struct net_event_win32 *ne)
+{
+ CLEAR (*ne);
+ ne->sd = SOCKET_UNDEFINED;
+}
+
+void
+net_event_win32_start (struct net_event_win32 *ne, long network_events, socket_descriptor_t sd)
+{
+ ASSERT (!socket_defined (ne->sd));
+ ne->sd = sd;
+ ne->event_mask = 0;
+ init_net_event_win32 (&ne->handle, network_events, sd, NE32_PERSIST_EVENT|NE32_WRITE_EVENT);
+}
+
+void
+net_event_win32_reset_write (struct net_event_win32 *ne)
+{
+ BOOL status;
+ if (ne->event_mask & FD_WRITE)
+ status = SetEvent (ne->handle.write);
+ else
+ status = ResetEvent (ne->handle.write);
+ if (!status)
+ msg (M_WARN | M_ERRNO, "Warning: SetEvent/ResetEvent failed in net_event_win32_reset_write");
+}
+
+void
+net_event_win32_reset (struct net_event_win32 *ne)
+{
+ ne->event_mask |= reset_net_event_win32 (&ne->handle, ne->sd);
+}
+
+void
+net_event_win32_stop (struct net_event_win32 *ne)
+{
+ if (net_event_win32_defined (ne))
+ close_net_event_win32 (&ne->handle, ne->sd, NE32_PERSIST_EVENT);
+ ne->sd = SOCKET_UNDEFINED;
+ ne->event_mask = 0;
+}
+
+void
+net_event_win32_close (struct net_event_win32 *ne)
+{
+ if (net_event_win32_defined (ne))
+ close_net_event_win32 (&ne->handle, ne->sd, 0);
+ net_event_win32_init (ne);
+}
+
+/*
+ * Simulate *nix signals on Windows.
+ *
+ * Two modes:
+ * (1) Console mode -- map keyboard function keys to signals
+ * (2) Service mode -- map Windows event object to SIGTERM
+ */
+
+void
+win32_signal_clear (struct win32_signal *ws)
+{
+ CLEAR (*ws);
+}
+
+void
+win32_signal_open (struct win32_signal *ws,
+ int force,
+ const char *exit_event_name,
+ bool exit_event_initial_state)
+{
+ CLEAR (*ws);
+
+ ws->mode = WSO_MODE_UNDEF;
+ ws->in.read = INVALID_HANDLE_VALUE;
+ ws->in.write = INVALID_HANDLE_VALUE;
+ ws->console_mode_save = 0;
+ ws->console_mode_save_defined = false;
+
+ if (force == WSO_NOFORCE || force == WSO_FORCE_CONSOLE)
+ {
+ /*
+ * Try to open console.
+ */
+ ws->in.read = GetStdHandle (STD_INPUT_HANDLE);
+ if (ws->in.read != INVALID_HANDLE_VALUE)
+ {
+ if (GetConsoleMode (ws->in.read, &ws->console_mode_save))
+ {
+ /* running on a console */
+ const DWORD new_console_mode = ws->console_mode_save
+ & ~(ENABLE_WINDOW_INPUT
+ | ENABLE_PROCESSED_INPUT
+ | ENABLE_LINE_INPUT
+ | ENABLE_ECHO_INPUT
+ | ENABLE_MOUSE_INPUT);
+
+ if (new_console_mode != ws->console_mode_save)
+ {
+ if (!SetConsoleMode (ws->in.read, new_console_mode))
+ msg (M_ERR, "Error: win32_signal_open: SetConsoleMode failed");
+ ws->console_mode_save_defined = true;
+ }
+ ws->mode = WSO_MODE_CONSOLE;
+ }
+ else
+ ws->in.read = INVALID_HANDLE_VALUE; /* probably running as a service */
+ }
+ }
+
+ /*
+ * If console open failed, assume we are running
+ * as a service.
+ */
+ if ((force == WSO_NOFORCE || force == WSO_FORCE_SERVICE)
+ && !HANDLE_DEFINED (ws->in.read) && exit_event_name)
+ {
+ struct security_attributes sa;
+
+ if (!init_security_attributes_allow_all (&sa))
+ msg (M_ERR, "Error: win32_signal_open: init SA failed");
+
+ ws->in.read = CreateEvent (&sa.sa,
+ TRUE,
+ exit_event_initial_state ? TRUE : FALSE,
+ exit_event_name);
+ if (ws->in.read == NULL)
+ {
+ msg (M_WARN|M_ERRNO, "NOTE: CreateEvent '%s' failed", exit_event_name);
+ }
+ else
+ {
+ if (WaitForSingleObject (ws->in.read, 0) != WAIT_TIMEOUT)
+ msg (M_FATAL, "ERROR: Exit Event ('%s') is signaled", exit_event_name);
+ else
+ ws->mode = WSO_MODE_SERVICE;
+ }
+ }
+}
+
+static bool
+keyboard_input_available (struct win32_signal *ws)
+{
+ ASSERT (ws->mode == WSO_MODE_CONSOLE);
+ if (HANDLE_DEFINED (ws->in.read))
+ {
+ DWORD n;
+ if (GetNumberOfConsoleInputEvents (ws->in.read, &n))
+ return n > 0;
+ }
+ return false;
+}
+
+static unsigned int
+keyboard_ir_to_key (INPUT_RECORD *ir)
+{
+ if (ir->Event.KeyEvent.uChar.AsciiChar == 0)
+ return ir->Event.KeyEvent.wVirtualScanCode;
+
+ if ((ir->Event.KeyEvent.dwControlKeyState
+ & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+ && (ir->Event.KeyEvent.wVirtualKeyCode != 18))
+ return ir->Event.KeyEvent.wVirtualScanCode * 256;
+
+ return ir->Event.KeyEvent.uChar.AsciiChar;
+}
+
+static unsigned int
+win32_keyboard_get (struct win32_signal *ws)
+{
+ ASSERT (ws->mode == WSO_MODE_CONSOLE);
+ if (HANDLE_DEFINED (ws->in.read))
+ {
+ INPUT_RECORD ir;
+ do {
+ DWORD n;
+ if (!keyboard_input_available (ws))
+ return 0;
+ if (!ReadConsoleInput (ws->in.read, &ir, 1, &n))
+ return 0;
+ } while (ir.EventType != KEY_EVENT || ir.Event.KeyEvent.bKeyDown != TRUE);
+
+ return keyboard_ir_to_key (&ir);
+ }
+ else
+ return 0;
+}
+
+void
+win32_signal_close (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_SERVICE && HANDLE_DEFINED (ws->in.read))
+ CloseHandle (ws->in.read);
+ if (ws->console_mode_save_defined)
+ {
+ if (!SetConsoleMode (ws->in.read, ws->console_mode_save))
+ msg (M_ERR, "Error: win32_signal_close: SetConsoleMode failed");
+ }
+ CLEAR (*ws);
+}
+
+/*
+ * Return true if interrupt occurs in service mode.
+ */
+static bool
+win32_service_interrupt (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_SERVICE)
+ {
+ if (HANDLE_DEFINED (ws->in.read)
+ && WaitForSingleObject (ws->in.read, 0) == WAIT_OBJECT_0)
+ return true;
+ }
+ return false;
+}
+
+int
+win32_signal_get (struct win32_signal *ws)
+{
+ int ret = 0;
+ if (siginfo_static.signal_received)
+ {
+ ret = siginfo_static.signal_received;
+ }
+ else
+ {
+ if (ws->mode == WSO_MODE_SERVICE)
+ {
+ if (win32_service_interrupt (ws))
+ ret = SIGTERM;
+ }
+ else if (ws->mode == WSO_MODE_CONSOLE)
+ {
+ switch (win32_keyboard_get (ws))
+ {
+ case 0x3B: /* F1 -> USR1 */
+ ret = SIGUSR1;
+ break;
+ case 0x3C: /* F2 -> USR2 */
+ ret = SIGUSR2;
+ break;
+ case 0x3D: /* F3 -> HUP */
+ ret = SIGHUP;
+ break;
+ case 0x3E: /* F4 -> TERM */
+ ret = SIGTERM;
+ break;
+ }
+ }
+ if (ret)
+ {
+ siginfo_static.signal_received = ret;
+ siginfo_static.hard = true;
+ }
+ }
+ return ret;
+}
+
+void
+win32_pause (struct win32_signal *ws)
+{
+ if (ws->mode == WSO_MODE_CONSOLE && HANDLE_DEFINED (ws->in.read))
+ {
+ int status;
+ msg (M_INFO|M_NOPREFIX, "Press any key to continue...");
+ do {
+ status = WaitForSingleObject (ws->in.read, INFINITE);
+ } while (!win32_keyboard_get (ws));
+ }
+}
+
+/* window functions */
+
+void
+window_title_clear (struct window_title *wt)
+{
+ CLEAR (*wt);
+}
+
+void
+window_title_save (struct window_title *wt)
+{
+ if (!wt->saved)
+ {
+ if (!GetConsoleTitle (wt->old_window_title, sizeof (wt->old_window_title)))
+ {
+ wt->old_window_title[0] = 0;
+ wt->saved = false;
+ }
+ else
+ wt->saved = true;
+ }
+}
+
+void
+window_title_restore (const struct window_title *wt)
+{
+ if (wt->saved)
+ SetConsoleTitle (wt->old_window_title);
+}
+
+void
+window_title_generate (const char *title)
+{
+ struct gc_arena gc = gc_new ();
+ struct buffer out = alloc_buf_gc (256, &gc);
+ if (!title)
+ title = "";
+ buf_printf (&out, "[%s] " PACKAGE_NAME " " VERSION " F4:EXIT F1:USR1 F2:USR2 F3:HUP", title);
+ SetConsoleTitle (BSTR (&out));
+ gc_free (&gc);
+}
+
+/* semaphore functions */
+
+void
+semaphore_clear (struct semaphore *s)
+{
+ CLEAR (*s);
+}
+
+void
+semaphore_open (struct semaphore *s, const char *name)
+{
+ struct security_attributes sa;
+
+ s->locked = false;
+ s->name = name;
+ s->hand = NULL;
+
+ if (init_security_attributes_allow_all (&sa))
+ s->hand = CreateSemaphore(&sa.sa, 1, 1, name);
+
+ if (s->hand == NULL)
+ msg (M_WARN|M_ERRNO, "WARNING: Cannot create Win32 semaphore '%s'", name);
+ else
+ dmsg (D_SEMAPHORE, "Created Win32 semaphore '%s'", s->name);
+}
+
+bool
+semaphore_lock (struct semaphore *s, int timeout_milliseconds)
+{
+ bool ret = true;
+
+ if (s->hand)
+ {
+ DWORD status;
+ ASSERT (!s->locked);
+
+ dmsg (D_SEMAPHORE_LOW, "Attempting to lock Win32 semaphore '%s' prior to net shell command (timeout = %d sec)",
+ s->name,
+ timeout_milliseconds / 1000);
+ status = WaitForSingleObject (s->hand, timeout_milliseconds);
+ if (status == WAIT_FAILED)
+ msg (M_ERR, "Wait failed on Win32 semaphore '%s'", s->name);
+ ret = (status == WAIT_TIMEOUT) ? false : true;
+ if (ret)
+ {
+ dmsg (D_SEMAPHORE, "Locked Win32 semaphore '%s'", s->name);
+ s->locked = true;
+ }
+ else
+ {
+ dmsg (D_SEMAPHORE, "Wait on Win32 semaphore '%s' timed out after %d milliseconds",
+ s->name,
+ timeout_milliseconds);
+ }
+ }
+ return ret;
+}
+
+void
+semaphore_release (struct semaphore *s)
+{
+ if (s->hand)
+ {
+ ASSERT (s->locked);
+ dmsg (D_SEMAPHORE, "Releasing Win32 semaphore '%s'", s->name);
+ if (!ReleaseSemaphore(s->hand, 1, NULL))
+ msg (M_WARN | M_ERRNO, "ReleaseSemaphore failed on Win32 semaphore '%s'",
+ s->name);
+ s->locked = false;
+ }
+}
+
+void
+semaphore_close (struct semaphore *s)
+{
+ if (s->hand)
+ {
+ if (s->locked)
+ semaphore_release (s);
+ dmsg (D_SEMAPHORE, "Closing Win32 semaphore '%s'", s->name);
+ CloseHandle (s->hand);
+ s->hand = NULL;
+ }
+}
+
+/*
+ * Special global semaphore used to protect network
+ * shell commands from simultaneous instantiation.
+ */
+
+void
+netcmd_semaphore_init (void)
+{
+ semaphore_open (&netcmd_semaphore, PACKAGE "_netcmd");
+}
+
+void
+netcmd_semaphore_close (void)
+{
+ semaphore_close (&netcmd_semaphore);
+}
+
+void
+netcmd_semaphore_lock (void)
+{
+ const int timeout_seconds = 600;
+ if (!semaphore_lock (&netcmd_semaphore, timeout_seconds * 1000))
+ msg (M_FATAL, "Cannot lock net command semaphore");
+}
+
+void
+netcmd_semaphore_release (void)
+{
+ semaphore_release (&netcmd_semaphore);
+}
+
+/*
+ * Get input from console.
+ *
+ * Return false on input error, or if service
+ * exit event is signaled.
+ */
+
+bool
+get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity)
+{
+ HANDLE in = INVALID_HANDLE_VALUE;
+ HANDLE err = INVALID_HANDLE_VALUE;
+ DWORD len = 0;
+
+ ASSERT (prompt);
+ ASSERT (input);
+ ASSERT (capacity > 0);
+
+ input[0] = '\0';
+
+ in = GetStdHandle (STD_INPUT_HANDLE);
+ err = get_orig_stderr ();
+
+ if (in != INVALID_HANDLE_VALUE
+ && err != INVALID_HANDLE_VALUE
+ && !win32_service_interrupt (&win32_signal)
+ && WriteFile (err, prompt, strlen (prompt), &len, NULL))
+ {
+ bool is_console = (GetFileType (in) == FILE_TYPE_CHAR);
+ DWORD flags_save = 0;
+ int status = 0;
+
+ if (is_console)
+ {
+ if (GetConsoleMode (in, &flags_save))
+ {
+ DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+ if (echo)
+ flags |= ENABLE_ECHO_INPUT;
+ SetConsoleMode (in, flags);
+ }
+ else
+ is_console = 0;
+ }
+
+ status = ReadFile (in, input, capacity, &len, NULL);
+
+ string_null_terminate (input, (int)len, capacity);
+ chomp (input);
+
+ if (!echo)
+ WriteFile (err, "\r\n", 2, &len, NULL);
+ if (is_console)
+ SetConsoleMode (in, flags_save);
+ if (status && !win32_service_interrupt (&win32_signal))
+ return true;
+ }
+
+ return false;
+}
+
+/* get password from console */
+
+char *
+getpass (const char *prompt)
+{
+ static char line[256];
+ if (get_console_input_win32 (prompt, false, line, sizeof (line)))
+ return line;
+ else
+ return NULL;
+}
+
+/*
+ * Return true if filename is safe to be used on Windows,
+ * by avoiding the following reserved names:
+ *
+ * CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9,
+ * LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, and CLOCK$
+ *
+ * See: http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ * and http://msdn.microsoft.com/en-us/library/86k9f82k(VS.80).aspx
+ */
+
+static bool
+cmp_prefix (const char *str, const bool n, const char *pre)
+{
+ size_t i = 0;
+
+ if (!str)
+ return false;
+
+ while (true)
+ {
+ const int c1 = pre[i];
+ int c2 = str[i];
+ ++i;
+ if (c1 == '\0')
+ {
+ if (n)
+ {
+ if (isdigit (c2))
+ c2 = str[i];
+ else
+ return false;
+ }
+ return c2 == '\0' || c2 == '.';
+ }
+ else if (c2 == '\0')
+ return false;
+ if (c1 != tolower(c2))
+ return false;
+ }
+}
+
+bool
+win_safe_filename (const char *fn)
+{
+ if (cmp_prefix (fn, false, "con"))
+ return false;
+ if (cmp_prefix (fn, false, "prn"))
+ return false;
+ if (cmp_prefix (fn, false, "aux"))
+ return false;
+ if (cmp_prefix (fn, false, "nul"))
+ return false;
+ if (cmp_prefix (fn, true, "com"))
+ return false;
+ if (cmp_prefix (fn, true, "lpt"))
+ return false;
+ if (cmp_prefix (fn, false, "clock$"))
+ return false;
+ return true;
+}
+
+/*
+ * Service functions for openvpn_execve
+ */
+
+static char *
+env_block (const struct env_set *es)
+{
+ if (es)
+ {
+ struct env_item *e;
+ char *ret;
+ char *p;
+ size_t nchars = 1;
+
+ for (e = es->list; e != NULL; e = e->next)
+ nchars += strlen (e->string) + 1;
+
+ ret = (char *) malloc (nchars);
+ check_malloc_return (ret);
+
+ p = ret;
+ for (e = es->list; e != NULL; e = e->next)
+ {
+ if (env_allowed (e->string))
+ {
+ strcpy (p, e->string);
+ p += strlen (e->string) + 1;
+ }
+ }
+ *p = '\0';
+ return ret;
+ }
+ else
+ return NULL;
+}
+
+static char *
+cmd_line (const struct argv *a)
+{
+ size_t nchars = 1;
+ size_t maxlen = 0;
+ size_t i;
+ struct buffer buf;
+ char *work = NULL;
+
+ if (!a)
+ return NULL;
+
+ for (i = 0; i < a->argc; ++i)
+ {
+ const char *arg = a->argv[i];
+ const size_t len = strlen (arg);
+ nchars += len + 3;
+ if (len > maxlen)
+ maxlen = len;
+ }
+
+ work = (char *) malloc (maxlen + 1);
+ check_malloc_return (work);
+ buf = alloc_buf (nchars);
+
+ for (i = 0; i < a->argc; ++i)
+ {
+ const char *arg = a->argv[i];
+ strcpy (work, arg);
+ string_mod (work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_');
+ if (i)
+ buf_printf (&buf, " ");
+ if (string_class (work, CC_ANY, CC_SPACE))
+ buf_printf (&buf, "%s", work);
+ else
+ buf_printf (&buf, "\"%s\"", work);
+ }
+
+ free (work);
+ return BSTR(&buf);
+}
+
+/*
+ * Attempt to simulate fork/execve on Windows
+ */
+int
+openvpn_execve (const struct argv *a, const struct env_set *es, const unsigned int flags)
+{
+ int ret = -1;
+ static bool exec_warn = false;
+
+ if (a && a->argv[0])
+ {
+ if (openvpn_execve_allowed (flags))
+ {
+ if (script_method == SM_EXECVE)
+ {
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+
+ char *env = env_block (es);
+ char *cl = cmd_line (a);
+ char *cmd = a->argv[0];
+
+ CLEAR (start_info);
+ CLEAR (proc_info);
+
+ /* fill in STARTUPINFO struct */
+ GetStartupInfo(&start_info);
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+
+ if (CreateProcess (cmd, cl, NULL, NULL, FALSE, 0, env, NULL, &start_info, &proc_info))
+ {
+ DWORD exit_status = 0;
+ CloseHandle (proc_info.hThread);
+ WaitForSingleObject (proc_info.hProcess, INFINITE);
+ if (GetExitCodeProcess (proc_info.hProcess, &exit_status))
+ ret = (int)exit_status;
+ else
+ msg (M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %s failed", cmd);
+ CloseHandle (proc_info.hProcess);
+ }
+ else
+ {
+ msg (M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %s failed", cmd);
+ }
+ free (cl);
+ free (env);
+ }
+ else if (script_method == SM_SYSTEM)
+ {
+ configure_win_path ();
+ ret = openvpn_system (argv_system_str (a), es, flags);
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
+ else if (!exec_warn && (script_security < SSEC_SCRIPTS))
+ {
+ msg (M_WARN, SCRIPT_SECURITY_WARNING);
+ exec_warn = true;
+ }
+ }
+ else
+ {
+ msg (M_WARN, "openvpn_execve: called with empty argv");
+ }
+ return ret;
+}
+
+/*
+ * call ourself in another process
+ */
+void
+fork_to_self (const char *cmdline)
+{
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+ char self_exe[256];
+ char *cl = string_alloc (cmdline, NULL);
+ DWORD status;
+
+ CLEAR (start_info);
+ CLEAR (proc_info);
+ CLEAR (self_exe);
+
+ status = GetModuleFileName (NULL, self_exe, sizeof(self_exe));
+ if (status == 0 || status == sizeof(self_exe))
+ {
+ msg (M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: cannot get module name via GetModuleFileName");
+ goto done;
+ }
+
+ /* fill in STARTUPINFO struct */
+ GetStartupInfo(&start_info);
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+
+ if (CreateProcess (self_exe, cl, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info))
+ {
+ CloseHandle (proc_info.hThread);
+ CloseHandle (proc_info.hProcess);
+ }
+ else
+ {
+ msg (M_WARN|M_ERRNO, "fork_to_self: CreateProcess failed: %s", cmdline);
+ }
+
+ done:
+ free (cl);
+}
+
+char *
+get_win_sys_path (void)
+{
+ ASSERT (win_sys_path);
+ return win_sys_path;
+}
+
+void
+set_win_sys_path (const char *newpath, struct env_set *es)
+{
+ free (win_sys_path);
+ win_sys_path = string_alloc (newpath, NULL);
+ setenv_str (es, SYS_PATH_ENV_VAR_NAME, win_sys_path); /* route.exe needs this */
+}
+
+void
+set_win_sys_path_via_env (struct env_set *es)
+{
+ char buf[256];
+ DWORD status = GetEnvironmentVariable (SYS_PATH_ENV_VAR_NAME, buf, sizeof(buf));
+ if (!status)
+ msg (M_ERR, "Cannot find environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+ if (status > sizeof (buf) - 1)
+ msg (M_FATAL, "String overflow attempting to read environmental variable %s", SYS_PATH_ENV_VAR_NAME);
+ set_win_sys_path (buf, es);
+}
+
+void
+env_set_add_win32 (struct env_set *es)
+{
+ set_win_sys_path (DEFAULT_WIN_SYS_PATH, es);
+}
+
+
+const char *
+win_get_tempdir()
+{
+ static char buf[MAX_PATH];
+ char *tmpdir = buf;
+
+ CLEAR(buf);
+
+ if (!GetTempPath(sizeof(buf),buf)) {
+ /* Warn if we can't find a valid temporary directory, which should
+ * be unlikely.
+ */
+ msg (M_WARN, "Could not find a suitable temporary directory."
+ " (GetTempPath() failed). Consider to use --tmp-dir");
+ tmpdir = NULL;
+ }
+ return tmpdir;
+}
+#endif
diff --git a/win32.h b/win32.h
new file mode 100644
index 0000000..b6a162e
--- /dev/null
+++ b/win32.h
@@ -0,0 +1,277 @@
+/*
+ * 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-2010 OpenVPN Technologies, 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef WIN32
+#ifndef OPENVPN_WIN32_H
+#define OPENVPN_WIN32_H
+
+#include "mtu.h"
+
+/* location of executables */
+#define SYS_PATH_ENV_VAR_NAME "SystemRoot" /* environmental variable name that normally contains the system path */
+#define DEFAULT_WIN_SYS_PATH "C:\\WINDOWS" /* --win-sys default value */
+#define NETSH_PATH_SUFFIX "\\system32\\netsh.exe"
+#define WIN_ROUTE_PATH_SUFFIX "\\system32\\route.exe"
+#define WIN_IPCONFIG_PATH_SUFFIX "\\system32\\ipconfig.exe"
+#define WIN_NET_PATH_SUFFIX "\\system32\\net.exe"
+
+/*
+ * Win32-specific OpenVPN code, targetted at the mingw
+ * development environment.
+ */
+
+void init_win32 (void);
+void uninit_win32 (void);
+
+void set_pause_exit_win32 (void);
+
+/*
+ * Use keyboard input or events
+ * to simulate incoming signals
+ */
+
+#define SIGUSR1 1
+#define SIGUSR2 2
+#define SIGHUP 3
+#define SIGTERM 4
+#define SIGINT 5
+
+struct security_attributes
+{
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR sd;
+};
+
+#define HANDLE_DEFINED(h) ((h) != NULL && (h) != INVALID_HANDLE_VALUE)
+
+/*
+ * Save old window title.
+ */
+struct window_title
+{
+ bool saved;
+ char old_window_title [256];
+};
+
+struct rw_handle {
+ HANDLE read;
+ HANDLE write;
+};
+
+/*
+ * Event-based notification of incoming TCP connections
+ */
+
+#define NE32_PERSIST_EVENT (1<<0)
+#define NE32_WRITE_EVENT (1<<1)
+
+static inline bool
+defined_net_event_win32 (const struct rw_handle *event)
+{
+ return event->read != NULL;
+}
+
+void init_net_event_win32 (struct rw_handle *event, long network_events, socket_descriptor_t sd, unsigned int flags);
+long reset_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd);
+void close_net_event_win32 (struct rw_handle *event, socket_descriptor_t sd, unsigned int flags);
+
+/*
+ * A stateful variant of the net_event_win32 functions above
+ */
+
+struct net_event_win32
+{
+ struct rw_handle handle;
+ socket_descriptor_t sd;
+ long event_mask;
+};
+
+void net_event_win32_init (struct net_event_win32 *ne);
+void net_event_win32_start (struct net_event_win32 *ne, long network_events, socket_descriptor_t sd);
+void net_event_win32_reset (struct net_event_win32 *ne);
+void net_event_win32_reset_write (struct net_event_win32 *ne);
+void net_event_win32_stop (struct net_event_win32 *ne);
+void net_event_win32_close (struct net_event_win32 *ne);
+
+static inline bool
+net_event_win32_defined (const struct net_event_win32 *ne)
+{
+ return defined_net_event_win32 (&ne->handle);
+}
+
+static inline struct rw_handle *
+net_event_win32_get_event (struct net_event_win32 *ne)
+{
+ return &ne->handle;
+}
+
+static inline long
+net_event_win32_get_event_mask (const struct net_event_win32 *ne)
+{
+ return ne->event_mask;
+}
+
+static inline void
+net_event_win32_clear_selected_events (struct net_event_win32 *ne, long selected_events)
+{
+ ne->event_mask &= ~selected_events;
+}
+
+/*
+ * Signal handling
+ */
+struct win32_signal {
+# define WSO_MODE_UNDEF 0
+# define WSO_MODE_SERVICE 1
+# define WSO_MODE_CONSOLE 2
+ int mode;
+ struct rw_handle in;
+ DWORD console_mode_save;
+ bool console_mode_save_defined;
+};
+
+extern struct win32_signal win32_signal; /* static/global */
+extern struct window_title window_title; /* static/global */
+
+void win32_signal_clear (struct win32_signal *ws);
+
+/* win32_signal_open startup type */
+#define WSO_NOFORCE 0
+#define WSO_FORCE_SERVICE 1
+#define WSO_FORCE_CONSOLE 2
+
+void win32_signal_open (struct win32_signal *ws,
+ int force, /* set to WSO force parm */
+ const char *exit_event_name,
+ bool exit_event_initial_state);
+
+void win32_signal_close (struct win32_signal *ws);
+
+int win32_signal_get (struct win32_signal *ws);
+
+void win32_pause (struct win32_signal *ws);
+
+/*
+ * Set the text on the window title bar
+ */
+
+void window_title_clear (struct window_title *wt);
+void window_title_save (struct window_title *wt);
+void window_title_restore (const struct window_title *wt);
+void window_title_generate (const char *title);
+
+/*
+ * We try to do all Win32 I/O using overlapped
+ * (i.e. asynchronous) I/O for a performance win.
+ */
+struct overlapped_io {
+# define IOSTATE_INITIAL 0
+# define IOSTATE_QUEUED 1 /* overlapped I/O has been queued */
+# define IOSTATE_IMMEDIATE_RETURN 2 /* I/O function returned immediately without queueing */
+ int iostate;
+ OVERLAPPED overlapped;
+ DWORD size;
+ DWORD flags;
+ int status;
+ bool addr_defined;
+ struct sockaddr_in addr;
+ int addrlen;
+ struct buffer buf_init;
+ struct buffer buf;
+};
+
+void overlapped_io_init (struct overlapped_io *o,
+ const struct frame *frame,
+ BOOL event_state,
+ bool tuntap_buffer);
+
+void overlapped_io_close (struct overlapped_io *o);
+
+static inline bool
+overlapped_io_active (struct overlapped_io *o)
+{
+ return o->iostate == IOSTATE_QUEUED || o->iostate == IOSTATE_IMMEDIATE_RETURN;
+}
+
+char *overlapped_io_state_ascii (const struct overlapped_io *o);
+
+/*
+ * Use to control access to resources that only one
+ * OpenVPN process on a given machine can access at
+ * a given time.
+ */
+
+struct semaphore
+{
+ const char *name;
+ bool locked;
+ HANDLE hand;
+};
+
+void semaphore_clear (struct semaphore *s);
+void semaphore_open (struct semaphore *s, const char *name);
+bool semaphore_lock (struct semaphore *s, int timeout_milliseconds);
+void semaphore_release (struct semaphore *s);
+void semaphore_close (struct semaphore *s);
+
+/*
+ * Special global semaphore used to protect network
+ * shell commands from simultaneous instantiation.
+ *
+ * It seems you can't run more than one instance
+ * of netsh on the same machine at the same time.
+ */
+
+extern struct semaphore netcmd_semaphore;
+void netcmd_semaphore_init (void);
+void netcmd_semaphore_close (void);
+void netcmd_semaphore_lock (void);
+void netcmd_semaphore_release (void);
+
+bool get_console_input_win32 (const char *prompt, const bool echo, char *input, const int capacity);
+char *getpass (const char *prompt);
+
+/* Set Win32 security attributes structure to allow all access */
+bool init_security_attributes_allow_all (struct security_attributes *obj);
+
+/* return true if filename is safe to be used on Windows */
+bool win_safe_filename (const char *fn);
+
+/* add constant environmental variables needed by Windows */
+struct env_set;
+void env_set_add_win32 (struct env_set *es);
+
+/* get and set the current windows system path */
+void set_win_sys_path (const char *newpath, struct env_set *es);
+void set_win_sys_path_via_env (struct env_set *es);
+char *get_win_sys_path (void);
+
+/* call self in a subprocess */
+void fork_to_self (const char *cmdline);
+
+/* Find temporary directory */
+const char *win_get_tempdir();
+
+#endif
+#endif