diff options
Diffstat (limited to 'openvpn/src/openvpn/error.c')
-rw-r--r-- | openvpn/src/openvpn/error.c | 893 |
1 files changed, 0 insertions, 893 deletions
diff --git a/openvpn/src/openvpn/error.c b/openvpn/src/openvpn/error.c deleted file mode 100644 index 98611a1b..00000000 --- a/openvpn/src/openvpn/error.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * 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 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#elif defined(_MSC_VER) -#include "config-msvc.h" -#endif - -#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 ENABLE_CRYPTO -#ifdef ENABLE_CRYPTO_OPENSSL -#include <openssl/err.h> -#endif -#endif - -#include "memdbg.h" - -#if SYSLOG_CAPABILITY -#ifndef LOG_OPENVPN -#define LOG_OPENVPN LOG_DAEMON -#endif -#endif - -#ifdef GOOGLE_BREAKPAD -#include "breakpad.h" -#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, ...) -{ - va_list arglist; - va_start (arglist, format); - x_msg_va (flags, format, arglist); - va_end (arglist); -} - -void x_msg_va (const unsigned int flags, const char *format, va_list arglist) -{ - struct gc_arena gc; -#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 - - 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); - - vsnprintf (m1, ERR_BUF_SIZE, format, arglist); - m1[ERR_BUF_SIZE - 1] = 0; /* windows vsnprintf needs this */ - - if ((flags & M_ERRNO) && e) - { - openvpn_snprintf (m2, ERR_BUF_SIZE, "%s: %s (errno=%d)", - m1, strerror_ts (e, &gc), e); - SWAP; - } - -#ifdef ENABLE_CRYPTO -#ifdef ENABLE_CRYPTO_OPENSSL - if (flags & M_SSL) - { - int nerrs = 0; - size_t 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) -{ -#ifdef GOOGLE_BREAKPAD - breakpad_dodump(); -#endif - 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 = openvpn_errno (); - const char *extended_msg = NULL; - - msg (x_cs_verbose_level, "%s %s returned %d", - sock ? proto2ascii (sock->info.proto, sock->info.af, 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-Windows driver */ - extended_msg = tap_win_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, sock->info.af, 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, sock->info.af, true) : "", - strerror_ts (my_errno, &gc), - my_errno); - - if (x_cs_err_delay_ms) - platform_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-Windows 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 |