362 lines
9.0 KiB
C
362 lines
9.0 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) 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 https://curl.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
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
* SPDX-License-Identifier: curl
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "../curl_setup.h"
|
|
|
|
#ifdef HAVE_STRERROR_R
|
|
# if (!defined(HAVE_POSIX_STRERROR_R) && \
|
|
!defined(HAVE_GLIBC_STRERROR_R)) || \
|
|
(defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
|
|
# error "strerror_r MUST be either POSIX, glibc style"
|
|
# endif
|
|
#endif
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#ifndef WITHOUT_LIBCURL
|
|
#include <curl/mprintf.h>
|
|
#define SNPRINTF curl_msnprintf
|
|
#else
|
|
/* when built for the test servers */
|
|
|
|
/* adjust for old MSVC */
|
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
#define SNPRINTF _snprintf
|
|
#else
|
|
#define SNPRINTF snprintf
|
|
#endif
|
|
#endif /* !WITHOUT_LIBCURL */
|
|
|
|
#include "winapi.h"
|
|
#include "strerr.h"
|
|
/* The last 2 #include files should be in this order */
|
|
#include "../curl_memory.h"
|
|
#include "../memdebug.h"
|
|
|
|
#ifdef USE_WINSOCK
|
|
/* This is a helper function for curlx_strerror that converts Winsock error
|
|
* codes (WSAGetLastError) to error messages.
|
|
* Returns NULL if no error message was found for error code.
|
|
*/
|
|
static const char *
|
|
get_winsock_error(int err, char *buf, size_t len)
|
|
{
|
|
#ifndef CURL_DISABLE_VERBOSE_STRINGS
|
|
const char *p;
|
|
size_t alen;
|
|
#endif
|
|
|
|
if(!len)
|
|
return NULL;
|
|
|
|
*buf = '\0';
|
|
|
|
#ifdef CURL_DISABLE_VERBOSE_STRINGS
|
|
(void)err;
|
|
return NULL;
|
|
#else
|
|
switch(err) {
|
|
case WSAEINTR:
|
|
p = "Call interrupted";
|
|
break;
|
|
case WSAEBADF:
|
|
p = "Bad file";
|
|
break;
|
|
case WSAEACCES:
|
|
p = "Bad access";
|
|
break;
|
|
case WSAEFAULT:
|
|
p = "Bad argument";
|
|
break;
|
|
case WSAEINVAL:
|
|
p = "Invalid arguments";
|
|
break;
|
|
case WSAEMFILE:
|
|
p = "Out of file descriptors";
|
|
break;
|
|
case WSAEWOULDBLOCK:
|
|
p = "Call would block";
|
|
break;
|
|
case WSAEINPROGRESS:
|
|
case WSAEALREADY:
|
|
p = "Blocking call in progress";
|
|
break;
|
|
case WSAENOTSOCK:
|
|
p = "Descriptor is not a socket";
|
|
break;
|
|
case WSAEDESTADDRREQ:
|
|
p = "Need destination address";
|
|
break;
|
|
case WSAEMSGSIZE:
|
|
p = "Bad message size";
|
|
break;
|
|
case WSAEPROTOTYPE:
|
|
p = "Bad protocol";
|
|
break;
|
|
case WSAENOPROTOOPT:
|
|
p = "Protocol option is unsupported";
|
|
break;
|
|
case WSAEPROTONOSUPPORT:
|
|
p = "Protocol is unsupported";
|
|
break;
|
|
case WSAESOCKTNOSUPPORT:
|
|
p = "Socket is unsupported";
|
|
break;
|
|
case WSAEOPNOTSUPP:
|
|
p = "Operation not supported";
|
|
break;
|
|
case WSAEAFNOSUPPORT:
|
|
p = "Address family not supported";
|
|
break;
|
|
case WSAEPFNOSUPPORT:
|
|
p = "Protocol family not supported";
|
|
break;
|
|
case WSAEADDRINUSE:
|
|
p = "Address already in use";
|
|
break;
|
|
case WSAEADDRNOTAVAIL:
|
|
p = "Address not available";
|
|
break;
|
|
case WSAENETDOWN:
|
|
p = "Network down";
|
|
break;
|
|
case WSAENETUNREACH:
|
|
p = "Network unreachable";
|
|
break;
|
|
case WSAENETRESET:
|
|
p = "Network has been reset";
|
|
break;
|
|
case WSAECONNABORTED:
|
|
p = "Connection was aborted";
|
|
break;
|
|
case WSAECONNRESET:
|
|
p = "Connection was reset";
|
|
break;
|
|
case WSAENOBUFS:
|
|
p = "No buffer space";
|
|
break;
|
|
case WSAEISCONN:
|
|
p = "Socket is already connected";
|
|
break;
|
|
case WSAENOTCONN:
|
|
p = "Socket is not connected";
|
|
break;
|
|
case WSAESHUTDOWN:
|
|
p = "Socket has been shut down";
|
|
break;
|
|
case WSAETOOMANYREFS:
|
|
p = "Too many references";
|
|
break;
|
|
case WSAETIMEDOUT:
|
|
p = "Timed out";
|
|
break;
|
|
case WSAECONNREFUSED:
|
|
p = "Connection refused";
|
|
break;
|
|
case WSAELOOP:
|
|
p = "Loop??";
|
|
break;
|
|
case WSAENAMETOOLONG:
|
|
p = "Name too long";
|
|
break;
|
|
case WSAEHOSTDOWN:
|
|
p = "Host down";
|
|
break;
|
|
case WSAEHOSTUNREACH:
|
|
p = "Host unreachable";
|
|
break;
|
|
case WSAENOTEMPTY:
|
|
p = "Not empty";
|
|
break;
|
|
case WSAEPROCLIM:
|
|
p = "Process limit reached";
|
|
break;
|
|
case WSAEUSERS:
|
|
p = "Too many users";
|
|
break;
|
|
case WSAEDQUOT:
|
|
p = "Bad quota";
|
|
break;
|
|
case WSAESTALE:
|
|
p = "Something is stale";
|
|
break;
|
|
case WSAEREMOTE:
|
|
p = "Remote error";
|
|
break;
|
|
case WSAEDISCON:
|
|
p = "Disconnected";
|
|
break;
|
|
/* Extended Winsock errors */
|
|
case WSASYSNOTREADY:
|
|
p = "Winsock library is not ready";
|
|
break;
|
|
case WSANOTINITIALISED:
|
|
p = "Winsock library not initialised";
|
|
break;
|
|
case WSAVERNOTSUPPORTED:
|
|
p = "Winsock version not supported";
|
|
break;
|
|
|
|
/* getXbyY() errors (already handled in herrmsg):
|
|
* Authoritative Answer: Host not found */
|
|
case WSAHOST_NOT_FOUND:
|
|
p = "Host not found";
|
|
break;
|
|
|
|
/* Non-Authoritative: Host not found, or SERVERFAIL */
|
|
case WSATRY_AGAIN:
|
|
p = "Host not found, try again";
|
|
break;
|
|
|
|
/* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
|
|
case WSANO_RECOVERY:
|
|
p = "Unrecoverable error in call to nameserver";
|
|
break;
|
|
|
|
/* Valid name, no data record of requested type */
|
|
case WSANO_DATA:
|
|
p = "No data record of requested type";
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
alen = strlen(p);
|
|
if(alen < len)
|
|
strcpy(buf, p);
|
|
return buf;
|
|
#endif
|
|
}
|
|
#endif /* USE_WINSOCK */
|
|
|
|
/*
|
|
* Our thread-safe and smart strerror() replacement.
|
|
*
|
|
* The 'err' argument passed in to this function MUST be a true errno number
|
|
* as reported on this system. We do no range checking on the number before
|
|
* we pass it to the "number-to-message" conversion function and there might
|
|
* be systems that do not do proper range checking in there themselves.
|
|
*
|
|
* We do not do range checking (on systems other than Windows) since there is
|
|
* no good reliable and portable way to do it.
|
|
*
|
|
* On Windows different types of error codes overlap. This function has an
|
|
* order of preference when trying to match error codes:
|
|
* CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError).
|
|
*
|
|
* It may be more correct to call one of the variant functions instead:
|
|
* Call Curl_sspi_strerror if the error code is definitely Windows SSPI.
|
|
* Call curlx_winapi_strerror if the error code is definitely Windows API.
|
|
*/
|
|
const char *curlx_strerror(int err, char *buf, size_t buflen)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD old_win_err = GetLastError();
|
|
#endif
|
|
int old_errno = errno;
|
|
char *p;
|
|
|
|
if(!buflen)
|
|
return NULL;
|
|
|
|
#ifndef _WIN32
|
|
DEBUGASSERT(err >= 0);
|
|
#endif
|
|
|
|
*buf = '\0';
|
|
|
|
#ifdef _WIN32
|
|
#ifndef UNDER_CE
|
|
/* 'sys_nerr' is the maximum errno number, it is not widely portable */
|
|
if(err >= 0 && err < sys_nerr)
|
|
SNPRINTF(buf, buflen, "%s", sys_errlist[err]);
|
|
else
|
|
#endif
|
|
{
|
|
if(
|
|
#ifdef USE_WINSOCK
|
|
!get_winsock_error(err, buf, buflen) &&
|
|
#endif
|
|
!curlx_get_winapi_error((DWORD)err, buf, buflen))
|
|
SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err);
|
|
}
|
|
#else /* !_WIN32 */
|
|
|
|
#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
|
|
/*
|
|
* The POSIX-style strerror_r() may set errno to ERANGE if insufficient
|
|
* storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
|
|
* message string, or EINVAL if 'errnum' is not a valid error number.
|
|
*/
|
|
if(strerror_r(err, buf, buflen) &&
|
|
buflen > sizeof("Unknown error ") + 20) {
|
|
if(buf[0] == '\0')
|
|
SNPRINTF(buf, buflen, "Unknown error %d", err);
|
|
}
|
|
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
|
|
/*
|
|
* The glibc-style strerror_r() only *might* use the buffer we pass to
|
|
* the function, but it always returns the error message as a pointer,
|
|
* so we must copy that string unconditionally (if non-NULL).
|
|
*/
|
|
{
|
|
char buffer[256];
|
|
char *msg = strerror_r(err, buffer, sizeof(buffer));
|
|
if(msg && buflen > 1)
|
|
SNPRINTF(buf, buflen, "%s", msg);
|
|
else if(buflen > sizeof("Unknown error ") + 20)
|
|
SNPRINTF(buf, buflen, "Unknown error %d", err);
|
|
}
|
|
#else
|
|
{
|
|
/* !checksrc! disable BANNEDFUNC 1 */
|
|
const char *msg = strerror(err);
|
|
if(msg && buflen > 1)
|
|
SNPRINTF(buf, buflen, "%s", msg);
|
|
else if(buflen > sizeof("Unknown error ") + 20)
|
|
SNPRINTF(buf, buflen, "Unknown error %d", err);
|
|
}
|
|
#endif
|
|
|
|
#endif /* _WIN32 */
|
|
|
|
/* strip trailing '\r\n' or '\n'. */
|
|
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)
|
|
CURL_SETERRNO(old_errno);
|
|
|
|
#ifdef _WIN32
|
|
if(old_win_err != GetLastError())
|
|
SetLastError(old_win_err);
|
|
#endif
|
|
|
|
return buf;
|
|
}
|