smtp: use the upload buffer size for scratch buffer malloc
[platform/upstream/curl.git] / lib / strerror.c
index fcb617c..0295d6c 100644 (file)
@@ -5,11 +5,11 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2004 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2004 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at http://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.haxx.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
@@ -20,7 +20,7 @@
  *
  ***************************************************************************/
 
-#include "setup.h"
+#include "curl_setup.h"
 
 #ifdef HAVE_STRERROR_R
 #  if (!defined(HAVE_POSIX_STRERROR_R) && \
 
 #include <curl/curl.h>
 
-#ifdef USE_LIBIDN
-#include <idna.h>
+#ifdef USE_LIBIDN2
+#include <idn2.h>
 #endif
 
-#include "strerror.h"
+#ifdef USE_WINDOWS_SSPI
+#include "curl_sspi.h"
+#endif
 
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
+#include "strerror.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
 
+#if defined(WIN32) || defined(_WIN32_WCE)
+#define PRESERVE_WINDOWS_ERROR_CODE
+#endif
 
 const char *
 curl_easy_strerror(CURLcode error)
 {
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
+  switch(error) {
   case CURLE_OK:
     return "No error";
 
@@ -75,12 +83,18 @@ curl_easy_strerror(CURLcode error)
   case CURLE_COULDNT_CONNECT:
     return "Couldn't connect to server";
 
-  case CURLE_FTP_WEIRD_SERVER_REPLY:
-    return "FTP: weird server reply";
+  case CURLE_WEIRD_SERVER_REPLY:
+    return "Weird server reply";
 
   case CURLE_REMOTE_ACCESS_DENIED:
     return "Access denied to remote resource";
 
+  case CURLE_FTP_ACCEPT_FAILED:
+    return "FTP: The server failed to connect to data port";
+
+  case CURLE_FTP_ACCEPT_TIMEOUT:
+    return "FTP: Accepting server connect has timed out";
+
   case CURLE_FTP_PRET_FAILED:
     return "FTP: The server did not accept the PRET command.";
 
@@ -96,6 +110,9 @@ curl_easy_strerror(CURLcode error)
   case CURLE_FTP_CANT_GET_HOST:
     return "FTP: can't figure out the host in the PASV response";
 
+  case CURLE_HTTP2:
+    return "Error in the HTTP2 framing layer";
+
   case CURLE_FTP_COULDNT_SET_TYPE:
     return "FTP: couldn't set file type";
 
@@ -283,10 +300,22 @@ curl_easy_strerror(CURLcode error)
   case CURLE_CHUNK_FAILED:
     return "Chunk callback failed";
 
+  case CURLE_NO_CONNECTION_AVAILABLE:
+    return "The max connection limit is reached";
+
+  case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+    return "SSL public key does not match pinned public key";
+
+  case CURLE_SSL_INVALIDCERTSTATUS:
+    return "SSL server certificate status verification FAILED";
+
+  case CURLE_HTTP2_STREAM:
+    return "Stream error in the HTTP/2 framing layer";
+
+  case CURLE_RECURSIVE_API_CALL:
+    return "API function called from within callback";
+
     /* error codes not used by current libcurl */
-  case CURLE_OBSOLETE10:
-  case CURLE_OBSOLETE12:
-  case CURLE_OBSOLETE16:
   case CURLE_OBSOLETE20:
   case CURLE_OBSOLETE24:
   case CURLE_OBSOLETE29:
@@ -315,7 +344,7 @@ curl_easy_strerror(CURLcode error)
    */
   return "Unknown error";
 #else
-  if(error == CURLE_OK)
+  if(!error)
     return "No error";
   else
     return "Error";
@@ -326,7 +355,7 @@ const char *
 curl_multi_strerror(CURLMcode error)
 {
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
+  switch(error) {
   case CURLM_CALL_MULTI_PERFORM:
     return "Please call curl_multi_perform() soon";
 
@@ -351,6 +380,12 @@ curl_multi_strerror(CURLMcode error)
   case CURLM_UNKNOWN_OPTION:
     return "Unknown option";
 
+  case CURLM_ADDED_ALREADY:
+    return "The easy handle is already added to a multi handle";
+
+  case CURLM_RECURSIVE_API_CALL:
+    return "API function called from within callback";
+
   case CURLM_LAST:
     break;
   }
@@ -368,7 +403,7 @@ const char *
 curl_share_strerror(CURLSHcode error)
 {
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (error) {
+  switch(error) {
   case CURLSHE_OK:
     return "No error";
 
@@ -402,15 +437,19 @@ curl_share_strerror(CURLSHcode error)
 
 #ifdef USE_WINSOCK
 
-/* This function handles most / all (?) Winsock errors cURL is able to produce.
+/* This function handles most / all (?) Winsock errors curl is able to produce.
  */
 static const char *
 get_winsock_error (int err, char *buf, size_t len)
 {
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  DWORD old_win_err = GetLastError();
+#endif
+  int old_errno = errno;
   const char *p;
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch (err) {
+  switch(err) {
   case WSAEINTR:
     p = "Call interrupted";
     break;
@@ -579,13 +618,22 @@ get_winsock_error (int err, char *buf, size_t len)
     return NULL;
   }
 #else
-  if(err == CURLE_OK)
+  if(!err)
     return NULL;
   else
     p = "error";
 #endif
-  strncpy (buf, p, len);
+  strncpy(buf, p, len);
   buf [len-1] = '\0';
+
+  if(errno != old_errno)
+    errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  if(old_win_err != GetLastError())
+    SetLastError(old_win_err);
+#endif
+
   return buf;
 }
 #endif   /* USE_WINSOCK */
@@ -603,9 +651,12 @@ get_winsock_error (int err, char *buf, size_t len)
  */
 const char *Curl_strerror(struct connectdata *conn, int err)
 {
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  DWORD old_win_err = GetLastError();
+#endif
+  int old_errno = errno;
   char *buf, *p;
   size_t max;
-  int old_errno = ERRNO;
 
   DEBUGASSERT(conn);
   DEBUGASSERT(err >= 0);
@@ -623,7 +674,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
 
     FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
                   LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL);
-    wcstombs(buf,wbuf,max);
+    wcstombs(buf, wbuf, max);
   }
 #else
   /* 'sys_nerr' is the maximum errno number, it is not widely portable */
@@ -631,7 +682,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
     strncpy(buf, strerror(err), max);
   else {
     if(!get_winsock_error(err, buf, max) &&
-        !FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
+       !FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
                        LANG_NEUTRAL, buf, (DWORD)max, NULL))
       snprintf(buf, max, "Unknown error %d (%#x)", err, err);
   }
@@ -666,7 +717,7 @@ const char *Curl_strerror(struct connectdata *conn, int err)
 #elif defined(HAVE_STRERROR_R) && defined(HAVE_VXWORKS_STRERROR_R)
  /*
   * The vxworks-style strerror_r() does use the buffer we pass to the function.
-  * The buffer size should be at least MAXERRSTR_SIZE (150) defined in rtsold.h
+  * The buffer size should be at least NAME_MAX (256)
   */
   {
     char buffer[256];
@@ -690,90 +741,369 @@ const char *Curl_strerror(struct connectdata *conn, int err)
   buf[max] = '\0'; /* make sure the string is zero terminated */
 
   /* strip trailing '\r\n' or '\n'. */
-  if((p = strrchr(buf,'\n')) != NULL && (p - buf) >= 2)
-     *p = '\0';
-  if((p = strrchr(buf,'\r')) != NULL && (p - buf) >= 1)
-     *p = '\0';
-
-  if(old_errno != ERRNO)
-    SET_ERRNO(old_errno);
+  p = strrchr(buf, '\n');
+  if(p && (p - buf) >= 2)
+    *p = '\0';
+  p = strrchr(buf, '\r');
+  if(p && (p - buf) >= 1)
+    *p = '\0';
+
+  if(errno != old_errno)
+    errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  if(old_win_err != GetLastError())
+    SetLastError(old_win_err);
+#endif
 
   return buf;
 }
 
-#ifdef USE_LIBIDN
-/*
- * Return error-string for libidn status as returned from idna_to_ascii_lz().
- */
-const char *Curl_idn_strerror (struct connectdata *conn, int err)
+#ifdef USE_WINDOWS_SSPI
+const char *Curl_sspi_strerror (struct connectdata *conn, int err)
 {
-#ifdef HAVE_IDNA_STRERROR
-  (void)conn;
-  return idna_strerror((Idna_rc) err);
-#else
-  const char *str;
-  char *buf;
-  size_t max;
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  DWORD old_win_err = GetLastError();
+#endif
+  int old_errno = errno;
+  const char *txt;
+  char *outbuf;
+  size_t outmax;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  char txtbuf[80];
+  char msgbuf[sizeof(conn->syserr_buf)];
+  char *p, *str, *msg = NULL;
+  bool msg_formatted = FALSE;
+#endif
 
   DEBUGASSERT(conn);
 
-  buf = conn->syserr_buf;
-  max = sizeof(conn->syserr_buf)-1;
-  *buf = '\0';
+  outbuf = conn->syserr_buf;
+  outmax = sizeof(conn->syserr_buf)-1;
+  *outbuf = '\0';
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  switch ((Idna_rc)err) {
-    case IDNA_SUCCESS:
-      str = "No error";
+
+  switch(err) {
+    case SEC_E_OK:
+      txt = "No error";
       break;
-    case IDNA_STRINGPREP_ERROR:
-      str = "Error in string preparation";
+    case CRYPT_E_REVOKED:
+      txt = "CRYPT_E_REVOKED";
       break;
-    case IDNA_PUNYCODE_ERROR:
-      str = "Error in Punycode operation";
+    case SEC_E_ALGORITHM_MISMATCH:
+      txt = "SEC_E_ALGORITHM_MISMATCH";
       break;
-    case IDNA_CONTAINS_NON_LDH:
-      str = "Illegal ASCII characters";
+    case SEC_E_BAD_BINDINGS:
+      txt = "SEC_E_BAD_BINDINGS";
       break;
-    case IDNA_CONTAINS_MINUS:
-      str = "Contains minus";
+    case SEC_E_BAD_PKGID:
+      txt = "SEC_E_BAD_PKGID";
       break;
-    case IDNA_INVALID_LENGTH:
-      str = "Invalid output length";
+    case SEC_E_BUFFER_TOO_SMALL:
+      txt = "SEC_E_BUFFER_TOO_SMALL";
       break;
-    case IDNA_NO_ACE_PREFIX:
-      str = "No ACE prefix (\"xn--\")";
+    case SEC_E_CANNOT_INSTALL:
+      txt = "SEC_E_CANNOT_INSTALL";
       break;
-    case IDNA_ROUNDTRIP_VERIFY_ERROR:
-      str = "Round trip verify error";
+    case SEC_E_CANNOT_PACK:
+      txt = "SEC_E_CANNOT_PACK";
       break;
-    case IDNA_CONTAINS_ACE_PREFIX:
-      str = "Already have ACE prefix (\"xn--\")";
+    case SEC_E_CERT_EXPIRED:
+      txt = "SEC_E_CERT_EXPIRED";
       break;
-    case IDNA_ICONV_ERROR:
-      str = "Locale conversion failed";
+    case SEC_E_CERT_UNKNOWN:
+      txt = "SEC_E_CERT_UNKNOWN";
       break;
-    case IDNA_MALLOC_ERROR:
-      str = "Allocation failed";
+    case SEC_E_CERT_WRONG_USAGE:
+      txt = "SEC_E_CERT_WRONG_USAGE";
       break;
-    case IDNA_DLOPEN_ERROR:
-      str = "dlopen() error";
+    case SEC_E_CONTEXT_EXPIRED:
+      txt = "SEC_E_CONTEXT_EXPIRED";
       break;
-    default:
-      snprintf(buf, max, "error %d", err);
-      str = NULL;
+    case SEC_E_CROSSREALM_DELEGATION_FAILURE:
+      txt = "SEC_E_CROSSREALM_DELEGATION_FAILURE";
+      break;
+    case SEC_E_CRYPTO_SYSTEM_INVALID:
+      txt = "SEC_E_CRYPTO_SYSTEM_INVALID";
+      break;
+    case SEC_E_DECRYPT_FAILURE:
+      txt = "SEC_E_DECRYPT_FAILURE";
+      break;
+    case SEC_E_DELEGATION_POLICY:
+      txt = "SEC_E_DELEGATION_POLICY";
+      break;
+    case SEC_E_DELEGATION_REQUIRED:
+      txt = "SEC_E_DELEGATION_REQUIRED";
+      break;
+    case SEC_E_DOWNGRADE_DETECTED:
+      txt = "SEC_E_DOWNGRADE_DETECTED";
+      break;
+    case SEC_E_ENCRYPT_FAILURE:
+      txt = "SEC_E_ENCRYPT_FAILURE";
+      break;
+    case SEC_E_ILLEGAL_MESSAGE:
+      txt = "SEC_E_ILLEGAL_MESSAGE";
+      break;
+    case SEC_E_INCOMPLETE_CREDENTIALS:
+      txt = "SEC_E_INCOMPLETE_CREDENTIALS";
+      break;
+    case SEC_E_INCOMPLETE_MESSAGE:
+      txt = "SEC_E_INCOMPLETE_MESSAGE";
+      break;
+    case SEC_E_INSUFFICIENT_MEMORY:
+      txt = "SEC_E_INSUFFICIENT_MEMORY";
+      break;
+    case SEC_E_INTERNAL_ERROR:
+      txt = "SEC_E_INTERNAL_ERROR";
+      break;
+    case SEC_E_INVALID_HANDLE:
+      txt = "SEC_E_INVALID_HANDLE";
+      break;
+    case SEC_E_INVALID_PARAMETER:
+      txt = "SEC_E_INVALID_PARAMETER";
+      break;
+    case SEC_E_INVALID_TOKEN:
+      txt = "SEC_E_INVALID_TOKEN";
+      break;
+    case SEC_E_ISSUING_CA_UNTRUSTED:
+      txt = "SEC_E_ISSUING_CA_UNTRUSTED";
+      break;
+    case SEC_E_ISSUING_CA_UNTRUSTED_KDC:
+      txt = "SEC_E_ISSUING_CA_UNTRUSTED_KDC";
+      break;
+    case SEC_E_KDC_CERT_EXPIRED:
+      txt = "SEC_E_KDC_CERT_EXPIRED";
+      break;
+    case SEC_E_KDC_CERT_REVOKED:
+      txt = "SEC_E_KDC_CERT_REVOKED";
+      break;
+    case SEC_E_KDC_INVALID_REQUEST:
+      txt = "SEC_E_KDC_INVALID_REQUEST";
+      break;
+    case SEC_E_KDC_UNABLE_TO_REFER:
+      txt = "SEC_E_KDC_UNABLE_TO_REFER";
+      break;
+    case SEC_E_KDC_UNKNOWN_ETYPE:
+      txt = "SEC_E_KDC_UNKNOWN_ETYPE";
+      break;
+    case SEC_E_LOGON_DENIED:
+      txt = "SEC_E_LOGON_DENIED";
+      break;
+    case SEC_E_MAX_REFERRALS_EXCEEDED:
+      txt = "SEC_E_MAX_REFERRALS_EXCEEDED";
+      break;
+    case SEC_E_MESSAGE_ALTERED:
+      txt = "SEC_E_MESSAGE_ALTERED";
+      break;
+    case SEC_E_MULTIPLE_ACCOUNTS:
+      txt = "SEC_E_MULTIPLE_ACCOUNTS";
+      break;
+    case SEC_E_MUST_BE_KDC:
+      txt = "SEC_E_MUST_BE_KDC";
       break;
+    case SEC_E_NOT_OWNER:
+      txt = "SEC_E_NOT_OWNER";
+      break;
+    case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+      txt = "SEC_E_NO_AUTHENTICATING_AUTHORITY";
+      break;
+    case SEC_E_NO_CREDENTIALS:
+      txt = "SEC_E_NO_CREDENTIALS";
+      break;
+    case SEC_E_NO_IMPERSONATION:
+      txt = "SEC_E_NO_IMPERSONATION";
+      break;
+    case SEC_E_NO_IP_ADDRESSES:
+      txt = "SEC_E_NO_IP_ADDRESSES";
+      break;
+    case SEC_E_NO_KERB_KEY:
+      txt = "SEC_E_NO_KERB_KEY";
+      break;
+    case SEC_E_NO_PA_DATA:
+      txt = "SEC_E_NO_PA_DATA";
+      break;
+    case SEC_E_NO_S4U_PROT_SUPPORT:
+      txt = "SEC_E_NO_S4U_PROT_SUPPORT";
+      break;
+    case SEC_E_NO_TGT_REPLY:
+      txt = "SEC_E_NO_TGT_REPLY";
+      break;
+    case SEC_E_OUT_OF_SEQUENCE:
+      txt = "SEC_E_OUT_OF_SEQUENCE";
+      break;
+    case SEC_E_PKINIT_CLIENT_FAILURE:
+      txt = "SEC_E_PKINIT_CLIENT_FAILURE";
+      break;
+    case SEC_E_PKINIT_NAME_MISMATCH:
+      txt = "SEC_E_PKINIT_NAME_MISMATCH";
+      break;
+    case SEC_E_POLICY_NLTM_ONLY:
+      txt = "SEC_E_POLICY_NLTM_ONLY";
+      break;
+    case SEC_E_QOP_NOT_SUPPORTED:
+      txt = "SEC_E_QOP_NOT_SUPPORTED";
+      break;
+    case SEC_E_REVOCATION_OFFLINE_C:
+      txt = "SEC_E_REVOCATION_OFFLINE_C";
+      break;
+    case SEC_E_REVOCATION_OFFLINE_KDC:
+      txt = "SEC_E_REVOCATION_OFFLINE_KDC";
+      break;
+    case SEC_E_SECPKG_NOT_FOUND:
+      txt = "SEC_E_SECPKG_NOT_FOUND";
+      break;
+    case SEC_E_SECURITY_QOS_FAILED:
+      txt = "SEC_E_SECURITY_QOS_FAILED";
+      break;
+    case SEC_E_SHUTDOWN_IN_PROGRESS:
+      txt = "SEC_E_SHUTDOWN_IN_PROGRESS";
+      break;
+    case SEC_E_SMARTCARD_CERT_EXPIRED:
+      txt = "SEC_E_SMARTCARD_CERT_EXPIRED";
+      break;
+    case SEC_E_SMARTCARD_CERT_REVOKED:
+      txt = "SEC_E_SMARTCARD_CERT_REVOKED";
+      break;
+    case SEC_E_SMARTCARD_LOGON_REQUIRED:
+      txt = "SEC_E_SMARTCARD_LOGON_REQUIRED";
+      break;
+    case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
+      txt = "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
+      break;
+    case SEC_E_TARGET_UNKNOWN:
+      txt = "SEC_E_TARGET_UNKNOWN";
+      break;
+    case SEC_E_TIME_SKEW:
+      txt = "SEC_E_TIME_SKEW";
+      break;
+    case SEC_E_TOO_MANY_PRINCIPALS:
+      txt = "SEC_E_TOO_MANY_PRINCIPALS";
+      break;
+    case SEC_E_UNFINISHED_CONTEXT_DELETED:
+      txt = "SEC_E_UNFINISHED_CONTEXT_DELETED";
+      break;
+    case SEC_E_UNKNOWN_CREDENTIALS:
+      txt = "SEC_E_UNKNOWN_CREDENTIALS";
+      break;
+    case SEC_E_UNSUPPORTED_FUNCTION:
+      txt = "SEC_E_UNSUPPORTED_FUNCTION";
+      break;
+    case SEC_E_UNSUPPORTED_PREAUTH:
+      txt = "SEC_E_UNSUPPORTED_PREAUTH";
+      break;
+    case SEC_E_UNTRUSTED_ROOT:
+      txt = "SEC_E_UNTRUSTED_ROOT";
+      break;
+    case SEC_E_WRONG_CREDENTIAL_HANDLE:
+      txt = "SEC_E_WRONG_CREDENTIAL_HANDLE";
+      break;
+    case SEC_E_WRONG_PRINCIPAL:
+      txt = "SEC_E_WRONG_PRINCIPAL";
+      break;
+    case SEC_I_COMPLETE_AND_CONTINUE:
+      txt = "SEC_I_COMPLETE_AND_CONTINUE";
+      break;
+    case SEC_I_COMPLETE_NEEDED:
+      txt = "SEC_I_COMPLETE_NEEDED";
+      break;
+    case SEC_I_CONTEXT_EXPIRED:
+      txt = "SEC_I_CONTEXT_EXPIRED";
+      break;
+    case SEC_I_CONTINUE_NEEDED:
+      txt = "SEC_I_CONTINUE_NEEDED";
+      break;
+    case SEC_I_INCOMPLETE_CREDENTIALS:
+      txt = "SEC_I_INCOMPLETE_CREDENTIALS";
+      break;
+    case SEC_I_LOCAL_LOGON:
+      txt = "SEC_I_LOCAL_LOGON";
+      break;
+    case SEC_I_NO_LSA_CONTEXT:
+      txt = "SEC_I_NO_LSA_CONTEXT";
+      break;
+    case SEC_I_RENEGOTIATE:
+      txt = "SEC_I_RENEGOTIATE";
+      break;
+    case SEC_I_SIGNATURE_NEEDED:
+      txt = "SEC_I_SIGNATURE_NEEDED";
+      break;
+    default:
+      txt = "Unknown error";
+  }
+
+  if(err == SEC_E_OK)
+    strncpy(outbuf, txt, outmax);
+  else if(err == SEC_E_ILLEGAL_MESSAGE)
+    snprintf(outbuf, outmax,
+             "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
+             "when a fatal SSL/TLS alert is received (e.g. handshake failed). "
+             "More detail may be available in the Windows System event log.",
+             err);
+  else {
+    str = txtbuf;
+    snprintf(txtbuf, sizeof(txtbuf), "%s (0x%08X)", txt, err);
+    txtbuf[sizeof(txtbuf)-1] = '\0';
+
+#ifdef _WIN32_WCE
+    {
+      wchar_t wbuf[256];
+      wbuf[0] = L'\0';
+
+      if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL, err, LANG_NEUTRAL,
+                       wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
+        wcstombs(msgbuf, wbuf, sizeof(msgbuf)-1);
+        msg_formatted = TRUE;
+      }
+    }
+#else
+    if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+                      FORMAT_MESSAGE_IGNORE_INSERTS,
+                      NULL, err, LANG_NEUTRAL,
+                      msgbuf, sizeof(msgbuf)-1, NULL)) {
+      msg_formatted = TRUE;
+    }
+#endif
+    if(msg_formatted) {
+      msgbuf[sizeof(msgbuf)-1] = '\0';
+      /* strip trailing '\r\n' or '\n' */
+      p = strrchr(msgbuf, '\n');
+      if(p && (p - msgbuf) >= 2)
+        *p = '\0';
+      p = strrchr(msgbuf, '\r');
+      if(p && (p - msgbuf) >= 1)
+        *p = '\0';
+      msg = msgbuf;
+    }
+    if(msg)
+      snprintf(outbuf, outmax, "%s - %s", str, msg);
+    else
+      strncpy(outbuf, str, outmax);
   }
+
 #else
-  if((Idna_rc)err == IDNA_SUCCESS)
-    str = "No error";
+
+  if(err == SEC_E_OK)
+    txt = "No error";
   else
-    str = "Error";
+    txt = "Error";
+
+  strncpy(outbuf, txt, outmax);
+
 #endif
-  if(str)
-    strncpy(buf, str, max);
-  buf[max] = '\0';
-  return (buf);
+
+  outbuf[outmax] = '\0';
+
+  if(errno != old_errno)
+    errno = old_errno;
+
+#ifdef PRESERVE_WINDOWS_ERROR_CODE
+  if(old_win_err != GetLastError())
+    SetLastError(old_win_err);
 #endif
+
+  return outbuf;
 }
-#endif  /* USE_LIBIDN */
+#endif /* USE_WINDOWS_SSPI */