| /*************************************************************************** |
| * _ _ ____ _ |
| * 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 "curlx/winapi.h" |
| #include "curlx/snprintf.h" |
| #include "curlx/strerr.h" |
| #include "curlx/strcopy.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) |
| { |
| VERBOSE(const char *p); |
| |
| if(!len) |
| return NULL; |
| |
| *buf = '\0'; |
| |
| #ifndef CURLVERBOSE |
| (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; |
| } |
| curlx_strcopy(buf, len, p, strlen(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 |
| if((strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && |
| #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]; |
| const 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) |
| errno = old_errno; |
| |
| #ifdef _WIN32 |
| if(old_win_err != GetLastError()) |
| SetLastError(old_win_err); |
| #endif |
| |
| return buf; |
| } |