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