PageRenderTime 10ms CodeModel.GetById 66ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/src/openvpn/error.c

https://github.com/alex-vpn/openvpn
C | 912 lines | 696 code | 111 blank | 105 comment | 102 complexity | e81f23a3c68090ffc3fb6b5520fcc6e2 MD5 | raw file
  1/*
  2 *  OpenVPN -- An application to securely tunnel IP networks
  3 *             over a single TCP/UDP port, with support for SSL/TLS-based
  4 *             session authentication and key exchange,
  5 *             packet encryption, packet authentication, and
  6 *             packet compression.
  7 *
  8 *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
  9 *
 10 *  This program is free software; you can redistribute it and/or modify
 11 *  it under the terms of the GNU General Public License version 2
 12 *  as published by the Free Software Foundation.
 13 *
 14 *  This program is distributed in the hope that it will be useful,
 15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17 *  GNU General Public License for more details.
 18 *
 19 *  You should have received a copy of the GNU General Public License
 20 *  along with this program (see the file COPYING included with this
 21 *  distribution); if not, write to the Free Software Foundation, Inc.,
 22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 23 */
 24
 25#ifdef HAVE_CONFIG_H
 26#include "config.h"
 27#elif defined(_MSC_VER)
 28#include "config-msvc.h"
 29#endif
 30
 31#include "syshead.h"
 32
 33#include "error.h"
 34#include "buffer.h"
 35#include "misc.h"
 36#include "win32.h"
 37#include "socket.h"
 38#include "tun.h"
 39#include "otime.h"
 40#include "perf.h"
 41#include "status.h"
 42#include "integer.h"
 43#include "ps.h"
 44#include "mstats.h"
 45
 46#ifdef ENABLE_CRYPTO
 47#ifdef ENABLE_CRYPTO_OPENSSL
 48#include <openssl/err.h>
 49#endif
 50#endif
 51
 52#include "memdbg.h"
 53
 54#if SYSLOG_CAPABILITY
 55#ifndef LOG_OPENVPN
 56#define LOG_OPENVPN LOG_DAEMON
 57#endif
 58#endif
 59
 60/* Globals */
 61unsigned int x_debug_level; /* GLOBAL */
 62
 63/* Mute state */
 64static int mute_cutoff;     /* GLOBAL */
 65static int mute_count;      /* GLOBAL */
 66static int mute_category;   /* GLOBAL */
 67
 68/*
 69 * Output mode priorities are as follows:
 70 *
 71 *  (1) --log-x overrides everything
 72 *  (2) syslog is used if --daemon or --inetd is defined and not --log-x
 73 *  (3) if OPENVPN_DEBUG_COMMAND_LINE is defined, output
 74 *      to constant logfile name.
 75 *  (4) Output to stdout.
 76 */
 77
 78/* If true, indicates that stdin/stdout/stderr
 79   have been redirected due to --log */
 80static bool std_redir;      /* GLOBAL */
 81
 82/* Should messages be written to the syslog? */
 83static bool use_syslog;     /* GLOBAL */
 84
 85/* Should stdout/stderr be be parsable and always be prefixed with time
 86 * and message flags */
 87static bool machine_readable_output;   /* GLOBAL */
 88
 89/* Should timestamps be included on messages to stdout/stderr? */
 90static bool suppress_timestamps; /* GLOBAL */
 91
 92/* The program name passed to syslog */
 93#if SYSLOG_CAPABILITY
 94static char *pgmname_syslog;  /* GLOBAL */
 95#endif
 96
 97/* If non-null, messages should be written here (used for debugging only) */
 98static FILE *msgfp;         /* GLOBAL */
 99
100/* If true, we forked from main OpenVPN process */
101static bool forked;         /* GLOBAL */
102
103/* our default output targets */
104static FILE *default_out; /* GLOBAL */
105static FILE *default_err; /* GLOBAL */
106
107void
108msg_forked (void)
109{
110  forked = true;
111}
112
113bool
114set_debug_level (const int level, const unsigned int flags)
115{
116  const int ceiling = 15;
117
118  if (level >= 0 && level <= ceiling)
119    {
120      x_debug_level = level;
121      return true;
122    }
123  else if (flags & SDL_CONSTRAIN)
124    {
125      x_debug_level = constrain_int (level, 0, ceiling);
126      return true;
127    }
128  return false;
129}
130
131bool
132set_mute_cutoff (const int cutoff)
133{
134  if (cutoff >= 0)
135    {
136      mute_cutoff = cutoff;
137      return true;
138    }
139  else
140    return false;
141}
142
143int
144get_debug_level (void)
145{
146  return x_debug_level;
147}
148
149int
150get_mute_cutoff (void)
151{
152  return mute_cutoff;
153}
154
155void
156set_suppress_timestamps (bool suppressed)
157{
158  suppress_timestamps = suppressed;
159}
160
161void
162set_machine_readable_output (bool parsable)
163{
164  machine_readable_output = parsable;
165}
166
167void
168error_reset ()
169{
170  use_syslog = std_redir = false;
171  suppress_timestamps = false;
172  machine_readable_output = false;
173  x_debug_level = 1;
174  mute_cutoff = 0;
175  mute_count = 0;
176  mute_category = 0;
177  default_out = OPENVPN_MSG_FP;
178  default_err = OPENVPN_MSG_FP;
179
180#ifdef OPENVPN_DEBUG_COMMAND_LINE
181  msgfp = fopen (OPENVPN_DEBUG_FILE, "w");
182  if (!msgfp)
183    openvpn_exit (OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE); /* exit point */
184#else
185  msgfp = NULL;
186#endif
187}
188
189void
190errors_to_stderr (void)
191{
192  default_err = OPENVPN_ERROR_FP;  
193}
194
195/*
196 * Return a file to print messages to before syslog is opened.
197 */
198FILE *
199msg_fp(const unsigned int flags)
200{
201  FILE *fp = msgfp;
202  if (!fp)
203    fp = (flags & (M_FATAL|M_USAGE_SMALL)) ? default_err : default_out;
204  if (!fp)
205    openvpn_exit (OPENVPN_EXIT_STATUS_CANNOT_OPEN_DEBUG_FILE); /* exit point */
206  return fp;
207}
208
209#define SWAP { tmp = m1; m1 = m2; m2 = tmp; }
210
211int x_msg_line_num; /* GLOBAL */
212
213void x_msg (const unsigned int flags, const char *format, ...)
214{
215  va_list arglist;
216  va_start (arglist, format);
217  x_msg_va (flags, format, arglist);
218  va_end (arglist);
219}
220
221void x_msg_va (const unsigned int flags, const char *format, va_list arglist)
222{
223  struct gc_arena gc;
224#if SYSLOG_CAPABILITY
225  int level;
226#endif
227  char *m1;
228  char *m2;
229  char *tmp;
230  int e;
231  const char *prefix;
232  const char *prefix_sep;
233
234  void usage_small (void);
235
236#ifndef HAVE_VARARG_MACROS
237  /* the macro has checked this otherwise */
238  if (!MSG_TEST (flags))
239    return;
240#endif
241
242  e = openvpn_errno ();
243
244  /*
245   * Apply muting filter.
246   */
247#ifndef HAVE_VARARG_MACROS
248  /* the macro has checked this otherwise */
249  if (!dont_mute (flags))
250    return;
251#endif
252
253  gc_init (&gc);
254
255  m1 = (char *) gc_malloc (ERR_BUF_SIZE, false, &gc);
256  m2 = (char *) gc_malloc (ERR_BUF_SIZE, false, &gc);
257
258  vsnprintf (m1, ERR_BUF_SIZE, format, arglist);
259  m1[ERR_BUF_SIZE - 1] = 0; /* windows vsnprintf needs this */
260
261  if ((flags & M_ERRNO) && e)
262    {
263      openvpn_snprintf (m2, ERR_BUF_SIZE, "%s: %s (errno=%d)",
264			m1, strerror_ts (e, &gc), e);
265      SWAP;
266    }
267
268#ifdef ENABLE_CRYPTO
269#ifdef ENABLE_CRYPTO_OPENSSL
270  if (flags & M_SSL)
271    {
272      int nerrs = 0;
273      size_t err;
274      while ((err = ERR_get_error ()))
275	{
276	  openvpn_snprintf (m2, ERR_BUF_SIZE, "%s: %s",
277			    m1, ERR_error_string (err, NULL));
278	  SWAP;
279	  ++nerrs;
280	}
281      if (!nerrs)
282	{
283	  openvpn_snprintf (m2, ERR_BUF_SIZE, "%s (OpenSSL)", m1);
284	  SWAP;
285	}
286    }
287#endif
288#endif
289
290  if (flags & M_OPTERR)
291    {
292      openvpn_snprintf (m2, ERR_BUF_SIZE, "Options error: %s", m1);
293      SWAP;
294    }
295
296#if SYSLOG_CAPABILITY
297  if (flags & (M_FATAL|M_NONFATAL|M_USAGE_SMALL))
298    level = LOG_ERR;
299  else if (flags & M_WARN)
300    level = LOG_WARNING;
301  else
302    level = LOG_NOTICE;
303#endif
304
305  /* set up client prefix */
306  if (flags & M_NOIPREFIX)
307    prefix = NULL;
308  else
309    prefix = msg_get_prefix ();
310  prefix_sep = " ";
311  if (!prefix)
312    prefix_sep = prefix = "";
313
314  /* virtual output capability used to copy output to management subsystem */
315  if (!forked)
316    {
317      const struct virtual_output *vo = msg_get_virtual_output ();
318      if (vo)
319	{
320	  openvpn_snprintf (m2, ERR_BUF_SIZE, "%s%s%s",
321			    prefix,
322			    prefix_sep,
323			    m1);
324	  virtual_output_print (vo, flags, m2);
325	}
326    }
327
328  if (!(flags & M_MSG_VIRT_OUT))
329    {
330      if (use_syslog && !std_redir && !forked)
331	{
332#if SYSLOG_CAPABILITY
333	  syslog (level, "%s%s%s",
334		  prefix,
335		  prefix_sep,
336		  m1);
337#endif
338	}
339      else
340	{
341	  FILE *fp = msg_fp(flags);
342	  const bool show_usec = check_debug_level (DEBUG_LEVEL_USEC_TIME);
343
344	  if (machine_readable_output)
345	    {
346	      struct timeval tv;
347	      gettimeofday (&tv, NULL);
348
349	      fprintf (fp, "%lu.%06lu %x %s%s%s%s",
350		       tv.tv_sec,
351		       tv.tv_usec,
352		       flags,
353		       prefix,
354		       prefix_sep,
355		       m1,
356		       "\n");
357
358	    }
359	  else if ((flags & M_NOPREFIX) || suppress_timestamps)
360	    {
361	      fprintf (fp, "%s%s%s%s",
362		       prefix,
363		       prefix_sep,
364		       m1,
365		       (flags&M_NOLF) ? "" : "\n");
366	    }
367	  else
368	    {
369	      fprintf (fp, "%s %s%s%s%s",
370		       time_string (0, 0, show_usec, &gc),
371		       prefix,
372		       prefix_sep,
373		       m1,
374		       (flags&M_NOLF) ? "" : "\n");
375	    }
376	  fflush(fp);
377	  ++x_msg_line_num;
378	}
379    }
380
381  if (flags & M_FATAL)
382    msg (M_INFO, "Exiting due to fatal error");
383
384  if (flags & M_FATAL)
385    openvpn_exit (OPENVPN_EXIT_STATUS_ERROR); /* exit point */
386
387  if (flags & M_USAGE_SMALL)
388    usage_small ();
389
390  gc_free (&gc);
391}
392
393/*
394 * Apply muting filter.
395 */
396bool
397dont_mute (unsigned int flags)
398{
399  bool ret = true;
400  if (mute_cutoff > 0 && !(flags & M_NOMUTE))
401    {
402      const int mute_level = DECODE_MUTE_LEVEL (flags);
403      if (mute_level > 0 && mute_level == mute_category)
404	{
405	  if (mute_count == mute_cutoff)
406	    msg (M_INFO | M_NOMUTE, "NOTE: --mute triggered...");
407	  if (++mute_count > mute_cutoff)
408	    ret = false;
409	}
410      else
411	{
412	  const int suppressed = mute_count - mute_cutoff;
413	  if (suppressed > 0)
414	    msg (M_INFO | M_NOMUTE,
415		 "%d variation(s) on previous %d message(s) suppressed by --mute",
416		 suppressed,
417		 mute_cutoff);
418	  mute_count = 1;
419	  mute_category = mute_level;
420	}
421    }
422  return ret;
423}
424
425void
426assert_failed (const char *filename, int line)
427{
428  msg (M_FATAL, "Assertion failed at %s:%d", filename, line);
429}
430
431/*
432 * Fail memory allocation.  Don't use msg() because it tries
433 * to allocate memory as part of its operation.
434 */
435void
436out_of_memory (void)
437{
438  fprintf (stderr, PACKAGE_NAME ": Out of Memory\n");
439  exit (1);
440}
441
442void
443open_syslog (const char *pgmname, bool stdio_to_null)
444{
445#if SYSLOG_CAPABILITY
446  if (!msgfp && !std_redir)
447    {
448      if (!use_syslog)
449	{
450	  pgmname_syslog = string_alloc (pgmname ? pgmname : PACKAGE, NULL);
451	  openlog (pgmname_syslog, LOG_PID, LOG_OPENVPN);
452	  use_syslog = true;
453
454	  /* Better idea: somehow pipe stdout/stderr output to msg() */
455	  if (stdio_to_null)
456	    set_std_files_to_null (false);
457	}
458    }
459#else
460  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");
461#endif
462}
463
464void
465close_syslog ()
466{
467#if SYSLOG_CAPABILITY
468  if (use_syslog)
469    {
470      closelog();
471      use_syslog = false;
472      if (pgmname_syslog)
473	{
474	  free (pgmname_syslog);
475	  pgmname_syslog = NULL;
476	}
477    }
478#endif
479}
480
481#ifdef WIN32
482
483static HANDLE orig_stderr;
484
485HANDLE
486get_orig_stderr (void)
487{
488  if (orig_stderr)
489    return orig_stderr;
490  else
491    return GetStdHandle (STD_ERROR_HANDLE);
492}
493
494#endif
495
496void
497redirect_stdout_stderr (const char *file, bool append)
498{
499#if defined(WIN32)
500  if (!std_redir)
501    {
502      struct gc_arena gc = gc_new ();
503      HANDLE log_handle;
504      int log_fd;
505
506      SECURITY_ATTRIBUTES saAttr; 
507      saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
508      saAttr.bInheritHandle = TRUE; 
509      saAttr.lpSecurityDescriptor = NULL; 
510
511      log_handle = CreateFileW (wide_string (file, &gc),
512                                GENERIC_WRITE,
513                                FILE_SHARE_READ,
514                                &saAttr,
515                                append ? OPEN_ALWAYS : CREATE_ALWAYS,
516                                FILE_ATTRIBUTE_NORMAL,
517                                NULL);
518
519      gc_free (&gc);
520
521      if (log_handle == INVALID_HANDLE_VALUE)
522	{
523	  msg (M_WARN|M_ERRNO, "Warning: cannot open --log file: %s", file);
524	  return;
525	}
526
527      /* append to logfile? */
528      if (append)
529	{
530	  if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
531	    msg (M_ERR, "Error: cannot seek to end of --log file: %s", file);
532	}
533      
534      /* save original stderr for password prompts */
535      orig_stderr = GetStdHandle (STD_ERROR_HANDLE);
536
537#if 0 /* seems not be necessary with stdout/stderr redirection below*/
538      /* set up for redirection */
539      if (!SetStdHandle (STD_OUTPUT_HANDLE, log_handle)
540	  || !SetStdHandle (STD_ERROR_HANDLE, log_handle))
541	msg (M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file);
542#endif
543
544      /* direct stdout/stderr to point to log_handle */
545      log_fd = _open_osfhandle ((intptr_t)log_handle, _O_TEXT);
546      if (log_fd == -1)
547	msg (M_ERR, "Error: --log redirect failed due to _open_osfhandle failure");
548      
549      /* open log_handle as FILE stream */
550      ASSERT (msgfp == NULL);
551      msgfp = _fdopen (log_fd, "wt");
552      if (msgfp == NULL)
553	msg (M_ERR, "Error: --log redirect failed due to _fdopen");
554
555      /* redirect C-library stdout/stderr to log file */
556      if (_dup2 (log_fd, 1) == -1 || _dup2 (log_fd, 2) == -1)
557	msg (M_WARN, "Error: --log redirect of stdout/stderr failed");
558
559      std_redir = true;
560    }
561#elif defined(HAVE_DUP2)
562  if (!std_redir)
563    {
564      int out = open (file,
565		      O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC),
566		      S_IRUSR | S_IWUSR);
567
568      if (out < 0)
569	{
570	  msg (M_WARN|M_ERRNO, "Warning: Error redirecting stdout/stderr to --log file: %s", file);
571	  return;
572	}
573
574      if (dup2 (out, 1) == -1)
575	msg (M_ERR, "--log file redirection error on stdout");
576      if (dup2 (out, 2) == -1)
577	msg (M_ERR, "--log file redirection error on stderr");
578
579      if (out > 2)
580	close (out);
581
582      std_redir = true;
583    }
584
585#else
586  msg (M_WARN, "WARNING: The --log option is not supported on this OS because it lacks the dup2 function");
587#endif
588}
589
590/*
591 * Functions used to check return status
592 * of I/O operations.
593 */
594
595unsigned int x_cs_info_level;    /* GLOBAL */
596unsigned int x_cs_verbose_level; /* GLOBAL */
597unsigned int x_cs_err_delay_ms;  /* GLOBAL */
598
599void
600reset_check_status ()
601{
602  x_cs_info_level = 0;
603  x_cs_verbose_level = 0;
604}
605
606void
607set_check_status (unsigned int info_level, unsigned int verbose_level)
608{
609  x_cs_info_level = info_level;
610  x_cs_verbose_level = verbose_level;
611}
612
613/*
614 * Called after most socket or tun/tap operations, via the inline
615 * function check_status().
616 *
617 * Decide if we should print an error message, and see if we can
618 * extract any useful info from the error, such as a Path MTU hint
619 * from the OS.
620 */
621void
622x_check_status (int status,
623		const char *description,
624		struct link_socket *sock,
625		struct tuntap *tt)
626{
627  const int my_errno = openvpn_errno ();
628  const char *extended_msg = NULL;
629
630  msg (x_cs_verbose_level, "%s %s returned %d",
631       sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "",
632       description,
633       status);
634
635  if (status < 0)
636    {
637      struct gc_arena gc = gc_new ();
638#if EXTENDED_SOCKET_ERROR_CAPABILITY
639      /* get extended socket error message and possible PMTU hint from OS */
640      if (sock)
641	{
642	  int mtu;
643	  extended_msg = format_extended_socket_error (sock->sd, &mtu, &gc);
644	  if (mtu > 0 && sock->mtu != mtu)
645	    {
646	      sock->mtu = mtu;
647	      sock->info.mtu_changed = true;
648	    }
649	}
650#elif defined(WIN32)
651      /* get possible driver error from TAP-Windows driver */
652      extended_msg = tap_win_getinfo (tt, &gc);
653#endif
654      if (!ignore_sys_error (my_errno))
655	{
656	  if (extended_msg)
657	    msg (x_cs_info_level, "%s %s [%s]: %s (code=%d)",
658		 description,
659		 sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "",
660		 extended_msg,
661		 strerror_ts (my_errno, &gc),
662		 my_errno);
663	  else
664	    msg (x_cs_info_level, "%s %s: %s (code=%d)",
665		 description,
666		 sock ? proto2ascii (sock->info.proto, sock->info.af, true) : "",
667		 strerror_ts (my_errno, &gc),
668		 my_errno);
669
670	  if (x_cs_err_delay_ms)
671	    platform_sleep_milliseconds (x_cs_err_delay_ms);
672	}
673      gc_free (&gc);
674    }
675}
676
677/*
678 * In multiclient mode, put a client-specific prefix
679 * before each message.
680 */
681const char *x_msg_prefix; /* GLOBAL */
682
683/*
684 * Allow MSG to be redirected through a virtual_output object
685 */
686
687const struct virtual_output *x_msg_virtual_output; /* GLOBAL */
688
689/*
690 * Exiting.
691 */
692
693void
694openvpn_exit (const int status)
695{
696  if (!forked)
697    {
698      void tun_abort();
699#ifdef ENABLE_PLUGIN
700      void plugin_abort (void);
701#endif
702
703      tun_abort();
704
705#ifdef WIN32
706      uninit_win32 ();
707#endif
708
709      close_syslog ();
710
711#ifdef ENABLE_PLUGIN
712      plugin_abort ();
713#endif
714
715#if PORT_SHARE
716      if (port_share)
717	port_share_abort (port_share);
718#endif
719
720#ifdef ENABLE_MEMSTATS
721      mstats_close();
722#endif
723
724#ifdef ABORT_ON_ERROR
725      if (status == OPENVPN_EXIT_STATUS_ERROR)
726	abort ();
727#endif
728
729      if (status == OPENVPN_EXIT_STATUS_GOOD)
730	perf_output_results ();
731    }
732
733  exit (status);
734}
735
736/*
737 * Translate msg flags into a string
738 */
739const char *
740msg_flags_string (const unsigned int flags, struct gc_arena *gc)
741{
742  struct buffer out = alloc_buf_gc (16, gc);
743  if (flags == M_INFO)
744    buf_printf (&out, "I");
745  if (flags & M_FATAL)
746    buf_printf (&out, "F");
747  if (flags & M_NONFATAL)
748    buf_printf (&out, "N");
749  if (flags & M_WARN)
750    buf_printf (&out, "W");
751  if (flags & M_DEBUG)
752    buf_printf (&out, "D");
753  return BSTR (&out);
754}
755
756#ifdef ENABLE_DEBUG
757void
758crash (void)
759{
760  char *null = NULL;
761  *null = 0;
762}
763#endif
764
765#ifdef WIN32
766
767const char *
768strerror_win32 (DWORD errnum, struct gc_arena *gc)
769{
770  /*
771   * This code can be omitted, though often the Windows
772   * WSA error messages are less informative than the
773   * Posix equivalents.
774   */
775#if 1
776  switch (errnum) {
777    /*
778     * When the TAP-Windows driver returns STATUS_UNSUCCESSFUL, this code
779     * gets returned to user space.
780     */
781  case ERROR_GEN_FAILURE:
782    return "General failure (ERROR_GEN_FAILURE)";
783  case ERROR_IO_PENDING:
784    return "I/O Operation in progress (ERROR_IO_PENDING)";
785  case WSA_IO_INCOMPLETE:
786    return "I/O Operation in progress (WSA_IO_INCOMPLETE)";
787  case WSAEINTR:
788    return "Interrupted system call (WSAEINTR)";
789  case WSAEBADF:
790    return "Bad file number (WSAEBADF)";
791  case WSAEACCES:
792    return "Permission denied (WSAEACCES)";
793  case WSAEFAULT:
794    return "Bad address (WSAEFAULT)";
795  case WSAEINVAL:
796    return "Invalid argument (WSAEINVAL)";
797  case WSAEMFILE:
798    return "Too many open files (WSAEMFILE)";
799  case WSAEWOULDBLOCK:
800    return "Operation would block (WSAEWOULDBLOCK)";
801  case WSAEINPROGRESS:
802    return "Operation now in progress (WSAEINPROGRESS)";
803  case WSAEALREADY:
804    return "Operation already in progress (WSAEALREADY)";
805  case WSAEDESTADDRREQ:
806    return "Destination address required (WSAEDESTADDRREQ)";
807  case WSAEMSGSIZE:
808    return "Message too long (WSAEMSGSIZE)";
809  case WSAEPROTOTYPE:
810    return "Protocol wrong type for socket (WSAEPROTOTYPE)";
811  case WSAENOPROTOOPT:
812    return "Bad protocol option (WSAENOPROTOOPT)";
813  case WSAEPROTONOSUPPORT:
814    return "Protocol not supported (WSAEPROTONOSUPPORT)";
815  case WSAESOCKTNOSUPPORT:
816    return "Socket type not supported (WSAESOCKTNOSUPPORT)";
817  case WSAEOPNOTSUPP:
818    return "Operation not supported on socket (WSAEOPNOTSUPP)";
819  case WSAEPFNOSUPPORT:
820    return "Protocol family not supported (WSAEPFNOSUPPORT)";
821  case WSAEAFNOSUPPORT:
822    return "Address family not supported by protocol family (WSAEAFNOSUPPORT)";
823  case WSAEADDRINUSE:
824    return "Address already in use (WSAEADDRINUSE)";
825  case WSAENETDOWN:
826    return "Network is down (WSAENETDOWN)";
827  case WSAENETUNREACH:
828    return "Network is unreachable (WSAENETUNREACH)";
829  case WSAENETRESET:
830    return "Net dropped connection or reset (WSAENETRESET)";
831  case WSAECONNABORTED:
832    return "Software caused connection abort (WSAECONNABORTED)";
833  case WSAECONNRESET:
834    return "Connection reset by peer (WSAECONNRESET)";
835  case WSAENOBUFS:
836    return "No buffer space available (WSAENOBUFS)";
837  case WSAEISCONN:
838    return "Socket is already connected (WSAEISCONN)";
839  case WSAENOTCONN:
840    return "Socket is not connected (WSAENOTCONN)";
841  case WSAETIMEDOUT:
842    return "Connection timed out (WSAETIMEDOUT)";
843  case WSAECONNREFUSED:
844    return "Connection refused (WSAECONNREFUSED)";
845  case WSAELOOP:
846    return "Too many levels of symbolic links (WSAELOOP)";
847  case WSAENAMETOOLONG:
848    return "File name too long (WSAENAMETOOLONG)";
849  case WSAEHOSTDOWN:
850    return "Host is down (WSAEHOSTDOWN)";
851  case WSAEHOSTUNREACH:
852    return "No Route to Host (WSAEHOSTUNREACH)";
853  case WSAENOTEMPTY:
854    return "Directory not empty (WSAENOTEMPTY)";
855  case WSAEPROCLIM:
856    return "Too many processes (WSAEPROCLIM)";
857  case WSAEUSERS:
858    return "Too many users (WSAEUSERS)";
859  case WSAEDQUOT:
860    return "Disc Quota Exceeded (WSAEDQUOT)";
861  case WSAESTALE:
862    return "Stale NFS file handle (WSAESTALE)";
863  case WSASYSNOTREADY:
864    return "Network SubSystem is unavailable (WSASYSNOTREADY)";
865  case WSAVERNOTSUPPORTED:
866    return "WINSOCK DLL Version out of range (WSAVERNOTSUPPORTED)";
867  case WSANOTINITIALISED:
868    return "Successful WSASTARTUP not yet performed (WSANOTINITIALISED)";
869  case WSAEREMOTE:
870    return "Too many levels of remote in path (WSAEREMOTE)";
871  case WSAHOST_NOT_FOUND:
872    return "Host not found (WSAHOST_NOT_FOUND)";
873  default:
874    break;
875  }
876#endif
877
878  /* format a windows error message */
879  {
880    char message[256];
881    struct buffer out = alloc_buf_gc (256, gc);
882    const int status =  FormatMessage (
883				       FORMAT_MESSAGE_IGNORE_INSERTS
884				       | FORMAT_MESSAGE_FROM_SYSTEM
885				       | FORMAT_MESSAGE_ARGUMENT_ARRAY,
886				       NULL,
887				       errnum,
888				       0,
889				       message,
890				       sizeof (message),
891				       NULL);
892    if (!status)
893      {
894	buf_printf (&out, "[Unknown Win32 Error]");
895      }
896    else
897      {
898	char *cp;
899	for (cp = message; *cp != '\0'; ++cp)
900	  {
901	    if (*cp == '\n' || *cp == '\r')
902	      *cp = ' ';
903	  }
904	
905	buf_printf(&out, "%s", message);
906      }
907    
908    return BSTR (&out);
909  }
910}
911
912#endif