Coverage Report

Created: 2025-10-10 06:31

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