Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcurl/lib/curlx/strerr.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifdef HAVE_STRERROR_R
27
#  if (!defined(HAVE_POSIX_STRERROR_R) && \
28
       !defined(HAVE_GLIBC_STRERROR_R)) || \
29
      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
30
#    error "strerror_r MUST be either POSIX, glibc style"
31
#  endif
32
#endif
33
34
#include "curlx/winapi.h"
35
#include "curlx/snprintf.h"
36
#include "curlx/strerr.h"
37
#include "curlx/strcopy.h"
38
39
#ifdef USE_WINSOCK
40
/* This is a helper function for curlx_strerror that converts Winsock error
41
 * codes (WSAGetLastError) to error messages.
42
 * Returns NULL if no error message was found for error code.
43
 */
44
static const char *get_winsock_error(int err, char *buf, size_t len)
45
{
46
  VERBOSE(const char *p);
47
48
  if(!len)
49
    return NULL;
50
51
  *buf = '\0';
52
53
#ifndef CURLVERBOSE
54
  (void)err;
55
  return NULL;
56
#else
57
  switch(err) {
58
  case WSAEINTR:
59
    p = "Call interrupted";
60
    break;
61
  case WSAEBADF:
62
    p = "Bad file";
63
    break;
64
  case WSAEACCES:
65
    p = "Bad access";
66
    break;
67
  case WSAEFAULT:
68
    p = "Bad argument";
69
    break;
70
  case WSAEINVAL:
71
    p = "Invalid arguments";
72
    break;
73
  case WSAEMFILE:
74
    p = "Out of file descriptors";
75
    break;
76
  case WSAEWOULDBLOCK:
77
    p = "Call would block";
78
    break;
79
  case WSAEINPROGRESS:
80
  case WSAEALREADY:
81
    p = "Blocking call in progress";
82
    break;
83
  case WSAENOTSOCK:
84
    p = "Descriptor is not a socket";
85
    break;
86
  case WSAEDESTADDRREQ:
87
    p = "Need destination address";
88
    break;
89
  case WSAEMSGSIZE:
90
    p = "Bad message size";
91
    break;
92
  case WSAEPROTOTYPE:
93
    p = "Bad protocol";
94
    break;
95
  case WSAENOPROTOOPT:
96
    p = "Protocol option is unsupported";
97
    break;
98
  case WSAEPROTONOSUPPORT:
99
    p = "Protocol is unsupported";
100
    break;
101
  case WSAESOCKTNOSUPPORT:
102
    p = "Socket is unsupported";
103
    break;
104
  case WSAEOPNOTSUPP:
105
    p = "Operation not supported";
106
    break;
107
  case WSAEAFNOSUPPORT:
108
    p = "Address family not supported";
109
    break;
110
  case WSAEPFNOSUPPORT:
111
    p = "Protocol family not supported";
112
    break;
113
  case WSAEADDRINUSE:
114
    p = "Address already in use";
115
    break;
116
  case WSAEADDRNOTAVAIL:
117
    p = "Address not available";
118
    break;
119
  case WSAENETDOWN:
120
    p = "Network down";
121
    break;
122
  case WSAENETUNREACH:
123
    p = "Network unreachable";
124
    break;
125
  case WSAENETRESET:
126
    p = "Network has been reset";
127
    break;
128
  case WSAECONNABORTED:
129
    p = "Connection was aborted";
130
    break;
131
  case WSAECONNRESET:
132
    p = "Connection was reset";
133
    break;
134
  case WSAENOBUFS:
135
    p = "No buffer space";
136
    break;
137
  case WSAEISCONN:
138
    p = "Socket is already connected";
139
    break;
140
  case WSAENOTCONN:
141
    p = "Socket is not connected";
142
    break;
143
  case WSAESHUTDOWN:
144
    p = "Socket has been shut down";
145
    break;
146
  case WSAETOOMANYREFS:
147
    p = "Too many references";
148
    break;
149
  case WSAETIMEDOUT:
150
    p = "Timed out";
151
    break;
152
  case WSAECONNREFUSED:
153
    p = "Connection refused";
154
    break;
155
  case WSAELOOP:
156
    p = "Loop??";
157
    break;
158
  case WSAENAMETOOLONG:
159
    p = "Name too long";
160
    break;
161
  case WSAEHOSTDOWN:
162
    p = "Host down";
163
    break;
164
  case WSAEHOSTUNREACH:
165
    p = "Host unreachable";
166
    break;
167
  case WSAENOTEMPTY:
168
    p = "Not empty";
169
    break;
170
  case WSAEPROCLIM:
171
    p = "Process limit reached";
172
    break;
173
  case WSAEUSERS:
174
    p = "Too many users";
175
    break;
176
  case WSAEDQUOT:
177
    p = "Bad quota";
178
    break;
179
  case WSAESTALE:
180
    p = "Something is stale";
181
    break;
182
  case WSAEREMOTE:
183
    p = "Remote error";
184
    break;
185
  case WSAEDISCON:
186
    p = "Disconnected";
187
    break;
188
    /* Extended Winsock errors */
189
  case WSASYSNOTREADY:
190
    p = "Winsock library is not ready";
191
    break;
192
  case WSANOTINITIALISED:
193
    p = "Winsock library not initialised";
194
    break;
195
  case WSAVERNOTSUPPORTED:
196
    p = "Winsock version not supported";
197
    break;
198
199
    /* getXbyY() errors (already handled in herrmsg):
200
     * Authoritative Answer: Host not found */
201
  case WSAHOST_NOT_FOUND:
202
    p = "Host not found";
203
    break;
204
205
    /* Non-Authoritative: Host not found, or SERVERFAIL */
206
  case WSATRY_AGAIN:
207
    p = "Host not found, try again";
208
    break;
209
210
    /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
211
  case WSANO_RECOVERY:
212
    p = "Unrecoverable error in call to nameserver";
213
    break;
214
215
    /* Valid name, no data record of requested type */
216
  case WSANO_DATA:
217
    p = "No data record of requested type";
218
    break;
219
220
  default:
221
    return NULL;
222
  }
223
  curlx_strcopy(buf, len, p, strlen(p));
224
  return buf;
225
#endif
226
}
227
#endif /* USE_WINSOCK */
228
229
/*
230
 * Our thread-safe and smart strerror() replacement.
231
 *
232
 * The 'err' argument passed in to this function MUST be a true errno number
233
 * as reported on this system. We do no range checking on the number before
234
 * we pass it to the "number-to-message" conversion function and there might
235
 * be systems that do not do proper range checking in there themselves.
236
 *
237
 * We do not do range checking (on systems other than Windows) since there is
238
 * no good reliable and portable way to do it.
239
 *
240
 * On Windows different types of error codes overlap. This function has an
241
 * order of preference when trying to match error codes:
242
 * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError).
243
 *
244
 * It may be more correct to call one of the variant functions instead:
245
 * Call Curl_sspi_strerror if the error code is definitely Windows SSPI.
246
 * Call curlx_winapi_strerror if the error code is definitely Windows API.
247
 */
248
const char *curlx_strerror(int err, char *buf, size_t buflen)
249
0
{
250
#ifdef _WIN32
251
  DWORD old_win_err = GetLastError();
252
#endif
253
0
  int old_errno = errno;
254
0
  char *p;
255
256
0
  if(!buflen)
257
0
    return NULL;
258
259
0
#ifndef _WIN32
260
0
  DEBUGASSERT(err >= 0);
261
0
#endif
262
263
0
  *buf = '\0';
264
265
#ifdef _WIN32
266
  if((!strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) &&
267
#ifdef USE_WINSOCK
268
     !get_winsock_error(err, buf, buflen) &&
269
#endif
270
     !curlx_get_winapi_error((DWORD)err, buf, buflen))
271
    SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err);
272
#else /* !_WIN32 */
273
274
#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
275
  /*
276
   * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
277
   * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
278
   * message string, or EINVAL if 'errnum' is not a valid error number.
279
   */
280
  if(strerror_r(err, buf, buflen) &&
281
     buflen > sizeof("Unknown error ") + 20) {
282
    if(buf[0] == '\0')
283
      SNPRINTF(buf, buflen, "Unknown error %d", err);
284
  }
285
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
286
  /*
287
   * The glibc-style strerror_r() only *might* use the buffer we pass to
288
   * the function, but it always returns the error message as a pointer,
289
   * so we must copy that string unconditionally (if non-NULL).
290
   */
291
0
  {
292
0
    char buffer[256];
293
0
    const char *msg = strerror_r(err, buffer, sizeof(buffer));
294
0
    if(msg && buflen > 1)
295
0
      SNPRINTF(buf, buflen, "%s", msg);
296
0
    else if(buflen > sizeof("Unknown error ") + 20)
297
0
      SNPRINTF(buf, buflen, "Unknown error %d", err);
298
0
  }
299
#else
300
  {
301
    /* !checksrc! disable BANNEDFUNC 1 */
302
    const char *msg = strerror(err);
303
    if(msg && buflen > 1)
304
      SNPRINTF(buf, buflen, "%s", msg);
305
    else if(buflen > sizeof("Unknown error ") + 20)
306
      SNPRINTF(buf, buflen, "Unknown error %d", err);
307
  }
308
#endif
309
310
0
#endif /* _WIN32 */
311
312
  /* strip trailing '\r\n' or '\n'. */
313
0
  p = strrchr(buf, '\n');
314
0
  if(p && (p - buf) >= 2)
315
0
    *p = '\0';
316
0
  p = strrchr(buf, '\r');
317
0
  if(p && (p - buf) >= 1)
318
0
    *p = '\0';
319
320
0
  if(errno != old_errno)
321
0
    errno = old_errno;
322
323
#ifdef _WIN32
324
  if(old_win_err != GetLastError())
325
    SetLastError(old_win_err);
326
#endif
327
328
0
  return buf;
329
0
}