Coverage Report

Created: 2026-03-12 06:35

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