mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-27 11:43:51 +08:00
71bbbd76c6
Openvpn for Windows is not compiled as a Unicode binary and thus cannot handle paths which contain non-ASCII characters using the argv vector. Characters that are not present in the system codepage are simply replaced with a question mark, e.g. if started as 'openvpn --config домой.ovpn' the file '?????.ovpn' is tried to be opened as configuration. The same applies to paths in config files which need to be UTF-8 encoded if they contain non ASCII characters. The option line 'key лев.pem' will lead to openvpn trying to open 'лев.pem' on a system with codepage 1252. This patch makes openvpn read the command line in UCS-2 and convert it to UTF-8 internally. Windows stores names in the filesystem in UCS-2. When using a paths openvpn converts it from UTF-8 to UCS-2 and uses the wide character Windows API function. Signed-off-by: Heiko Hund <heiko.hund@sophos.com> Acked-by: David Sommerseth <davids@redhat.com> Signed-off-by: David Sommerseth <davids@redhat.com>
879 lines
20 KiB
C
879 lines
20 KiB
C
/*
|
|
* 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"
|
|
#include "mstats.h"
|
|
|
|
#ifdef USE_CRYPTO
|
|
#ifdef USE_OPENSSL
|
|
#include <openssl/err.h>
|
|
#endif
|
|
#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
|
|
#ifdef USE_OPENSSL
|
|
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
|
|
#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 due to fatal error");
|
|
|
|
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)
|
|
{
|
|
struct gc_arena gc = gc_new ();
|
|
HANDLE log_handle;
|
|
int log_fd;
|
|
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
log_handle = CreateFileW (wide_string (file, &gc),
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
&saAttr,
|
|
append ? OPEN_ALWAYS : CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
gc_free (&gc);
|
|
|
|
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)
|
|
{
|
|
if (!forked)
|
|
{
|
|
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 ENABLE_MEMSTATS
|
|
mstats_close();
|
|
#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
|