src/openvpn/error.c
6fbf66fa
 /*
  *  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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
caa54ac3
  *  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.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #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"
6add6b2f
 #include "ps.h"
ffea644c
 #include "mstats.h"
6fbf66fa
 
 
 #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
81d882d5
  * have been redirected due to --log */
6fbf66fa
 static bool std_redir;      /* GLOBAL */
 
 /* Should messages be written to the syslog? */
 static bool use_syslog;     /* GLOBAL */
 
8f7d5e67
 /* Should stdout/stderr be be parsable and always be prefixed with time
  * and message flags */
 static bool machine_readable_output;   /* GLOBAL */
 
6fbf66fa
 /* Should timestamps be included on messages to stdout/stderr? */
 static bool suppress_timestamps; /* GLOBAL */
 
 /* The program name passed to syslog */
c373382c
 #if SYSLOG_CAPABILITY
6fbf66fa
 static char *pgmname_syslog;  /* GLOBAL */
c373382c
 #endif
6fbf66fa
 
 /* If non-null, messages should be written here (used for debugging only) */
 static FILE *msgfp;         /* GLOBAL */
 
6add6b2f
 /* If true, we forked from main OpenVPN process */
 static bool forked;         /* GLOBAL */
 
b16cd4d2
 /* our default output targets */
 static FILE *default_out; /* GLOBAL */
 static FILE *default_err; /* GLOBAL */
 
6add6b2f
 void
81d882d5
 msg_forked(void)
6add6b2f
 {
81d882d5
     forked = true;
6add6b2f
 }
 
6fbf66fa
 bool
81d882d5
 set_debug_level(const int level, const unsigned int flags)
6fbf66fa
 {
81d882d5
     const int ceiling = 15;
6fbf66fa
 
81d882d5
     if (level >= 0 && level <= ceiling)
6fbf66fa
     {
81d882d5
         x_debug_level = level;
         return true;
6fbf66fa
     }
81d882d5
     else if (flags & SDL_CONSTRAIN)
6fbf66fa
     {
81d882d5
         x_debug_level = constrain_int(level, 0, ceiling);
         return true;
6fbf66fa
     }
81d882d5
     return false;
6fbf66fa
 }
 
 bool
81d882d5
 set_mute_cutoff(const int cutoff)
6fbf66fa
 {
81d882d5
     if (cutoff >= 0)
6fbf66fa
     {
81d882d5
         mute_cutoff = cutoff;
         return true;
     }
     else
     {
         return false;
6fbf66fa
     }
 }
 
 int
81d882d5
 get_debug_level(void)
6fbf66fa
 {
81d882d5
     return x_debug_level;
6fbf66fa
 }
 
 int
81d882d5
 get_mute_cutoff(void)
6fbf66fa
 {
81d882d5
     return mute_cutoff;
6fbf66fa
 }
 
 void
81d882d5
 set_suppress_timestamps(bool suppressed)
6fbf66fa
 {
81d882d5
     suppress_timestamps = suppressed;
6fbf66fa
 }
 
 void
81d882d5
 set_machine_readable_output(bool parsable)
8f7d5e67
 {
81d882d5
     machine_readable_output = parsable;
8f7d5e67
 }
 
 void
e2a0cad4
 error_reset(void)
6fbf66fa
 {
81d882d5
     use_syslog = std_redir = false;
     suppress_timestamps = false;
     machine_readable_output = false;
     x_debug_level = 1;
     mute_cutoff = 0;
     mute_count = 0;
     mute_category = 0;
     default_out = OPENVPN_MSG_FP;
     default_err = OPENVPN_MSG_FP;
6fbf66fa
 
 #ifdef OPENVPN_DEBUG_COMMAND_LINE
81d882d5
     msgfp = fopen(OPENVPN_DEBUG_FILE, "w");
     if (!msgfp)
     {
         openvpn_exit(OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE); /* exit point */
     }
 #else  /* ifdef OPENVPN_DEBUG_COMMAND_LINE */
     msgfp = NULL;
6fbf66fa
 #endif
 }
 
b16cd4d2
 void
81d882d5
 errors_to_stderr(void)
b16cd4d2
 {
81d882d5
     default_err = OPENVPN_ERROR_FP;
b16cd4d2
 }
 
6fbf66fa
 /*
  * Return a file to print messages to before syslog is opened.
  */
 FILE *
b16cd4d2
 msg_fp(const unsigned int flags)
6fbf66fa
 {
81d882d5
     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;
6fbf66fa
 }
 
 #define SWAP { tmp = m1; m1 = m2; m2 = tmp; }
 
 int x_msg_line_num; /* GLOBAL */
 
81d882d5
 void
 x_msg(const unsigned int flags, const char *format, ...)
6fbf66fa
 {
81d882d5
     va_list arglist;
     va_start(arglist, format);
     x_msg_va(flags, format, arglist);
     va_end(arglist);
004ca97c
 }
 
81d882d5
 void
 x_msg_va(const unsigned int flags, const char *format, va_list arglist)
004ca97c
 {
81d882d5
     struct gc_arena gc;
6fbf66fa
 #if SYSLOG_CAPABILITY
81d882d5
     int level;
6fbf66fa
 #endif
81d882d5
     char *m1;
     char *m2;
     char *tmp;
     int e;
     const char *prefix;
     const char *prefix_sep;
6fbf66fa
 
81d882d5
     void usage_small(void);
6fbf66fa
 
 #ifndef HAVE_VARARG_MACROS
81d882d5
     /* the macro has checked this otherwise */
     if (!msg_test(flags))
     {
         return;
     }
6fbf66fa
 #endif
 
81d882d5
     e = openvpn_errno();
6fbf66fa
 
81d882d5
     /*
      * Apply muting filter.
      */
6fbf66fa
 #ifndef HAVE_VARARG_MACROS
81d882d5
     /* the macro has checked this otherwise */
     if (!dont_mute(flags))
     {
         return;
     }
6fbf66fa
 #endif
 
81d882d5
     gc_init(&gc);
6fbf66fa
 
81d882d5
     m1 = (char *) gc_malloc(ERR_BUF_SIZE, false, &gc);
     m2 = (char *) gc_malloc(ERR_BUF_SIZE, false, &gc);
6fbf66fa
 
81d882d5
     vsnprintf(m1, ERR_BUF_SIZE, format, arglist);
     m1[ERR_BUF_SIZE - 1] = 0; /* windows vsnprintf needs this */
6fbf66fa
 
81d882d5
     if ((flags & M_ERRNO) && e)
6fbf66fa
     {
81d882d5
         openvpn_snprintf(m2, ERR_BUF_SIZE, "%s: %s (errno=%d)",
fd2a29ab
                          m1, strerror(e), e);
81d882d5
         SWAP;
6fbf66fa
     }
 
81d882d5
     if (flags & M_OPTERR)
6fbf66fa
     {
81d882d5
         openvpn_snprintf(m2, ERR_BUF_SIZE, "Options error: %s", m1);
         SWAP;
6fbf66fa
     }
 
 #if SYSLOG_CAPABILITY
81d882d5
     if (flags & (M_FATAL|M_NONFATAL|M_USAGE_SMALL))
     {
         level = LOG_ERR;
     }
     else if (flags & M_WARN)
     {
         level = LOG_WARNING;
     }
     else
     {
         level = LOG_NOTICE;
     }
6fbf66fa
 #endif
 
81d882d5
     /* 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)
         {
6fbf66fa
 #if SYSLOG_CAPABILITY
81d882d5
             syslog(level, "%s%s%s",
                    prefix,
                    prefix_sep,
                    m1);
6fbf66fa
 #endif
81d882d5
         }
         else
         {
             FILE *fp = msg_fp(flags);
             const bool show_usec = check_debug_level(DEBUG_LEVEL_USEC_TIME);
 
             if (machine_readable_output)
             {
                 struct timeval tv;
                 gettimeofday(&tv, NULL);
 
31b5c0e9
                 fprintf(fp, "%lld.%06ld %x %s%s%s%s",
4ac769fb
                         (long long)tv.tv_sec,
31b5c0e9
                         (long)tv.tv_usec,
81d882d5
                         flags,
                         prefix,
                         prefix_sep,
                         m1,
                         "\n");
 
             }
             else 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);
6fbf66fa
 }
 
 /*
  * Apply muting filter.
  */
 bool
81d882d5
 dont_mute(unsigned int flags)
6fbf66fa
 {
81d882d5
     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;
6fbf66fa
 }
 
 void
81d882d5
 assert_failed(const char *filename, int line, const char *condition)
6fbf66fa
 {
81d882d5
     if (condition)
     {
         msg(M_FATAL, "Assertion failed at %s:%d (%s)", filename, line, condition);
     }
     else
     {
         msg(M_FATAL, "Assertion failed at %s:%d", filename, line);
     }
     _exit(1);
6fbf66fa
 }
 
 /*
  * Fail memory allocation.  Don't use msg() because it tries
  * to allocate memory as part of its operation.
  */
 void
81d882d5
 out_of_memory(void)
6fbf66fa
 {
81d882d5
     fprintf(stderr, PACKAGE_NAME ": Out of Memory\n");
     exit(1);
6fbf66fa
 }
 
 void
81d882d5
 open_syslog(const char *pgmname, bool stdio_to_null)
6fbf66fa
 {
 #if SYSLOG_CAPABILITY
81d882d5
     if (!msgfp && !std_redir)
6fbf66fa
     {
81d882d5
         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);
             }
         }
6fbf66fa
     }
81d882d5
 #else  /* if SYSLOG_CAPABILITY */
     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");
6fbf66fa
 #endif
 }
 
 void
e2a0cad4
 close_syslog(void)
6fbf66fa
 {
 #if SYSLOG_CAPABILITY
81d882d5
     if (use_syslog)
6fbf66fa
     {
81d882d5
         closelog();
         use_syslog = false;
         if (pgmname_syslog)
         {
             free(pgmname_syslog);
             pgmname_syslog = NULL;
         }
6fbf66fa
     }
 #endif
 }
 
445b192a
 #ifdef _WIN32
6fbf66fa
 
 static HANDLE orig_stderr;
 
 HANDLE
81d882d5
 get_orig_stderr(void)
6fbf66fa
 {
81d882d5
     if (orig_stderr)
     {
         return orig_stderr;
     }
     else
     {
         return GetStdHandle(STD_ERROR_HANDLE);
     }
6fbf66fa
 }
 
 #endif
 
 void
81d882d5
 redirect_stdout_stderr(const char *file, bool append)
6fbf66fa
 {
445b192a
 #if defined(_WIN32)
81d882d5
     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);
6fbf66fa
 
75dfe3d7
 #if 0 /* seems not be necessary with stdout/stderr redirection below*/
81d882d5
         /* 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);
         }
75dfe3d7
 #endif
6fbf66fa
 
81d882d5
         /* 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;
6fbf66fa
     }
 #elif defined(HAVE_DUP2)
81d882d5
     if (!std_redir)
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
 
81d882d5
 #else  /* if defined(_WIN32) */
     msg(M_WARN, "WARNING: The --log option is not supported on this OS because it lacks the dup2 function");
 #endif /* if defined(_WIN32) */
6fbf66fa
 }
 
 /*
  * 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
e2a0cad4
 reset_check_status(void)
6fbf66fa
 {
81d882d5
     x_cs_info_level = 0;
     x_cs_verbose_level = 0;
6fbf66fa
 }
 
 void
81d882d5
 set_check_status(unsigned int info_level, unsigned int verbose_level)
6fbf66fa
 {
81d882d5
     x_cs_info_level = info_level;
     x_cs_verbose_level = verbose_level;
6fbf66fa
 }
 
 /*
  * 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
81d882d5
 x_check_status(int status,
                const char *description,
                struct link_socket *sock,
                struct tuntap *tt)
6fbf66fa
 {
81d882d5
     const int my_errno = openvpn_errno();
     const char *extended_msg = NULL;
6fbf66fa
 
81d882d5
     msg(x_cs_verbose_level, "%s %s returned %d",
         sock ? proto2ascii(sock->info.proto, sock->info.af, true) : "",
         description,
         status);
6fbf66fa
 
81d882d5
     if (status < 0)
6fbf66fa
     {
81d882d5
         struct gc_arena gc = gc_new();
6fbf66fa
 #if EXTENDED_SOCKET_ERROR_CAPABILITY
81d882d5
         /* 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;
             }
         }
445b192a
 #elif defined(_WIN32)
81d882d5
         /* get possible driver error from TAP-Windows driver */
         extended_msg = tap_win_getinfo(tt, &gc);
6fbf66fa
 #endif
81d882d5
         if (!ignore_sys_error(my_errno))
         {
             if (extended_msg)
             {
fd2a29ab
                 msg(x_cs_info_level, "%s %s [%s]: %s (code=%d)", description,
81d882d5
                     sock ? proto2ascii(sock->info.proto, sock->info.af, true) : "",
fd2a29ab
                     extended_msg, strerror(my_errno), my_errno);
81d882d5
             }
             else
             {
fd2a29ab
                 msg(x_cs_info_level, "%s %s: %s (code=%d)", description,
81d882d5
                     sock ? proto2ascii(sock->info.proto, sock->info.af, true) : "",
fd2a29ab
                     strerror(my_errno), my_errno);
81d882d5
             }
 
             if (x_cs_err_delay_ms)
             {
                 platform_sleep_milliseconds(x_cs_err_delay_ms);
             }
         }
         gc_free(&gc);
6fbf66fa
     }
 }
 
 /*
  * 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
81d882d5
 openvpn_exit(const int status)
6fbf66fa
 {
81d882d5
     if (!forked)
1c5ff772
     {
81d882d5
         void tun_abort();
 
6fbf66fa
 #ifdef ENABLE_PLUGIN
81d882d5
         void plugin_abort(void);
 
6fbf66fa
 #endif
 
81d882d5
         tun_abort();
0c9eb1d3
 
445b192a
 #ifdef _WIN32
81d882d5
         uninit_win32();
6fbf66fa
 #endif
 
81d882d5
         close_syslog();
6fbf66fa
 
 #ifdef ENABLE_PLUGIN
81d882d5
         plugin_abort();
6fbf66fa
 #endif
 
6add6b2f
 #if PORT_SHARE
81d882d5
         if (port_share)
         {
             port_share_abort(port_share);
         }
6add6b2f
 #endif
 
ffea644c
 #ifdef ENABLE_MEMSTATS
81d882d5
         mstats_close();
ffea644c
 #endif
 
6fbf66fa
 #ifdef ABORT_ON_ERROR
81d882d5
         if (status == OPENVPN_EXIT_STATUS_ERROR)
         {
             abort();
         }
6fbf66fa
 #endif
 
81d882d5
         if (status == OPENVPN_EXIT_STATUS_GOOD)
         {
             perf_output_results();
         }
1c5ff772
     }
6fbf66fa
 
81d882d5
     exit(status);
6fbf66fa
 }
 
 /*
  * Translate msg flags into a string
  */
 const char *
81d882d5
 msg_flags_string(const unsigned int flags, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     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);
6fbf66fa
 }
 
8d33c060
 #ifdef ENABLE_DEBUG
 void
81d882d5
 crash(void)
8d33c060
 {
81d882d5
     char *null = NULL;
     *null = 0;
8d33c060
 }
 #endif
 
445b192a
 #ifdef _WIN32
6fbf66fa
 
 const char *
81d882d5
 strerror_win32(DWORD errnum, struct gc_arena *gc)
6fbf66fa
 {
     /*
81d882d5
      * This code can be omitted, though often the Windows
      * WSA error messages are less informative than the
      * Posix equivalents.
6fbf66fa
      */
81d882d5
 #if 1
4cd4899e
     switch (errnum)
     {
81d882d5
         /*
          * 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)";
6fbf66fa
 
81d882d5
         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 /* if 1 */
 
     /* 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);
     }
6fbf66fa
 }
 
81d882d5
 #endif /* ifdef _WIN32 */