1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apr_arch_misc.h"
18#include "apr_strings.h"
19#include "apr_lib.h"
20#include "apr_dso.h"
21
22#if APR_HAVE_NETDB_H
23#include <netdb.h>
24#endif
25#ifdef HAVE_DLFCN_H
26#include <dlfcn.h>
27#endif
28
29/*
30 * stuffbuffer - like apr_cpystrn() but returns the address of the
31 * dest buffer instead of the address of the terminating '\0'
32 */
33static char *stuffbuffer(char *buf, apr_size_t bufsize, const char *s)
34{
35    apr_cpystrn(buf,s,bufsize);
36    return buf;
37}
38
39static char *apr_error_string(apr_status_t statcode)
40{
41    switch (statcode) {
42    case APR_ENOSTAT:
43        return "Could not perform a stat on the file.";
44    case APR_ENOPOOL:
45        return "A new pool could not be created.";
46    case APR_EBADDATE:
47        return "An invalid date has been provided";
48    case APR_EINVALSOCK:
49        return "An invalid socket was returned";
50    case APR_ENOPROC:
51        return "No process was provided and one was required.";
52    case APR_ENOTIME:
53        return "No time was provided and one was required.";
54    case APR_ENODIR:
55        return "No directory was provided and one was required.";
56    case APR_ENOLOCK:
57        return "No lock was provided and one was required.";
58    case APR_ENOPOLL:
59        return "No poll structure was provided and one was required.";
60    case APR_ENOSOCKET:
61        return "No socket was provided and one was required.";
62    case APR_ENOTHREAD:
63        return "No thread was provided and one was required.";
64    case APR_ENOTHDKEY:
65        return "No thread key structure was provided and one was required.";
66    case APR_ENOSHMAVAIL:
67        return "No shared memory is currently available";
68    case APR_EDSOOPEN:
69#if APR_HAS_DSO && defined(HAVE_LIBDL)
70        return dlerror();
71#else
72        return "DSO load failed";
73#endif /* HAVE_LIBDL */
74    case APR_EBADIP:
75        return "The specified IP address is invalid.";
76    case APR_EBADMASK:
77        return "The specified network mask is invalid.";
78    case APR_ESYMNOTFOUND:
79        return "Could not find the requested symbol.";
80    case APR_ENOTENOUGHENTROPY:
81        return "Not enough entropy to continue.";
82    case APR_INCHILD:
83        return
84	    "Your code just forked, and you are currently executing in the "
85	    "child process";
86    case APR_INPARENT:
87        return
88	    "Your code just forked, and you are currently executing in the "
89	    "parent process";
90    case APR_DETACH:
91        return "The specified thread is detached";
92    case APR_NOTDETACH:
93        return "The specified thread is not detached";
94    case APR_CHILD_DONE:
95        return "The specified child process is done executing";
96    case APR_CHILD_NOTDONE:
97        return "The specified child process is not done executing";
98    case APR_TIMEUP:
99        return "The timeout specified has expired";
100    case APR_INCOMPLETE:
101        return "Partial results are valid but processing is incomplete";
102    case APR_BADCH:
103        return "Bad character specified on command line";
104    case APR_BADARG:
105        return "Missing parameter for the specified command line option";
106    case APR_EOF:
107        return "End of file found";
108    case APR_NOTFOUND:
109        return "Could not find specified socket in poll list.";
110    case APR_ANONYMOUS:
111        return "Shared memory is implemented anonymously";
112    case APR_FILEBASED:
113        return "Shared memory is implemented using files";
114    case APR_KEYBASED:
115        return "Shared memory is implemented using a key system";
116    case APR_EINIT:
117        return
118	    "There is no error, this value signifies an initialized "
119	    "error code";
120    case APR_ENOTIMPL:
121        return "This function has not been implemented on this platform";
122    case APR_EMISMATCH:
123        return "passwords do not match";
124    case APR_EABSOLUTE:
125        return "The given path is absolute";
126    case APR_ERELATIVE:
127        return "The given path is relative";
128    case APR_EINCOMPLETE:
129        return "The given path is incomplete";
130    case APR_EABOVEROOT:
131        return "The given path was above the root path";
132    case APR_EBADPATH:
133        return "The given path is misformatted or contained invalid characters";
134    case APR_EPATHWILD:
135        return "The given path contained wildcard characters";
136    case APR_EBUSY:
137        return "The given lock was busy.";
138    case APR_EPROC_UNKNOWN:
139        return "The process is not recognized.";
140    case APR_EGENERAL:
141        return "Internal error (specific information not available)";
142    default:
143        return "Error string not specified yet";
144    }
145}
146
147
148#ifdef OS2
149#include <ctype.h>
150
151int apr_canonical_error(apr_status_t err);
152
153static char *apr_os_strerror(char* buf, apr_size_t bufsize, int err)
154{
155  char result[200];
156  unsigned char message[HUGE_STRING_LEN];
157  ULONG len;
158  char *pos;
159  int c;
160
161  if (err >= 10000 && err < 12000) {  /* socket error codes */
162      return stuffbuffer(buf, bufsize,
163                         strerror(apr_canonical_error(err+APR_OS_START_SYSERR)));
164  }
165  else if (DosGetMessage(NULL, 0, message, HUGE_STRING_LEN, err,
166			 "OSO001.MSG", &len) == 0) {
167      len--;
168      message[len] = 0;
169      pos = result;
170
171      if (len >= sizeof(result))
172        len = sizeof(result) - 1;
173
174      for (c=0; c<len; c++) {
175	  /* skip multiple whitespace */
176          while (apr_isspace(message[c]) && apr_isspace(message[c+1]))
177              c++;
178          *(pos++) = apr_isspace(message[c]) ? ' ' : message[c];
179      }
180
181      *pos = 0;
182  }
183  else {
184      sprintf(result, "OS/2 error %d", err);
185  }
186
187  /* Stuff the string into the caller supplied buffer, then return
188   * a pointer to it.
189   */
190  return stuffbuffer(buf, bufsize, result);
191}
192
193#elif defined(WIN32) || (defined(NETWARE) && defined(USE_WINSOCK))
194
195static const struct {
196    apr_status_t code;
197    const char *msg;
198} gaErrorList[] = {
199    {WSAEINTR,           "Interrupted system call"},
200    {WSAEBADF,           "Bad file number"},
201    {WSAEACCES,          "Permission denied"},
202    {WSAEFAULT,          "Bad address"},
203    {WSAEINVAL,          "Invalid argument"},
204    {WSAEMFILE,          "Too many open sockets"},
205    {WSAEWOULDBLOCK,     "Operation would block"},
206    {WSAEINPROGRESS,     "Operation now in progress"},
207    {WSAEALREADY,        "Operation already in progress"},
208    {WSAENOTSOCK,        "Socket operation on non-socket"},
209    {WSAEDESTADDRREQ,    "Destination address required"},
210    {WSAEMSGSIZE,        "Message too long"},
211    {WSAEPROTOTYPE,      "Protocol wrong type for socket"},
212    {WSAENOPROTOOPT,     "Bad protocol option"},
213    {WSAEPROTONOSUPPORT, "Protocol not supported"},
214    {WSAESOCKTNOSUPPORT, "Socket type not supported"},
215    {WSAEOPNOTSUPP,      "Operation not supported on socket"},
216    {WSAEPFNOSUPPORT,    "Protocol family not supported"},
217    {WSAEAFNOSUPPORT,    "Address family not supported"},
218    {WSAEADDRINUSE,      "Address already in use"},
219    {WSAEADDRNOTAVAIL,   "Can't assign requested address"},
220    {WSAENETDOWN,        "Network is down"},
221    {WSAENETUNREACH,     "Network is unreachable"},
222    {WSAENETRESET,       "Net connection reset"},
223    {WSAECONNABORTED,    "Software caused connection abort"},
224    {WSAECONNRESET,      "Connection reset by peer"},
225    {WSAENOBUFS,         "No buffer space available"},
226    {WSAEISCONN,         "Socket is already connected"},
227    {WSAENOTCONN,        "Socket is not connected"},
228    {WSAESHUTDOWN,       "Can't send after socket shutdown"},
229    {WSAETOOMANYREFS,    "Too many references, can't splice"},
230    {WSAETIMEDOUT,       "Connection timed out"},
231    {WSAECONNREFUSED,    "Connection refused"},
232    {WSAELOOP,           "Too many levels of symbolic links"},
233    {WSAENAMETOOLONG,    "File name too long"},
234    {WSAEHOSTDOWN,       "Host is down"},
235    {WSAEHOSTUNREACH,    "No route to host"},
236    {WSAENOTEMPTY,       "Directory not empty"},
237    {WSAEPROCLIM,        "Too many processes"},
238    {WSAEUSERS,          "Too many users"},
239    {WSAEDQUOT,          "Disc quota exceeded"},
240    {WSAESTALE,          "Stale NFS file handle"},
241    {WSAEREMOTE,         "Too many levels of remote in path"},
242    {WSASYSNOTREADY,     "Network system is unavailable"},
243    {WSAVERNOTSUPPORTED, "Winsock version out of range"},
244    {WSANOTINITIALISED,  "WSAStartup not yet called"},
245    {WSAEDISCON,         "Graceful shutdown in progress"},
246    {WSAHOST_NOT_FOUND,  "Host not found"},
247    {WSANO_DATA,         "No host data of that type was found"},
248    {0,                  NULL}
249};
250
251
252static char *apr_os_strerror(char *buf, apr_size_t bufsize, apr_status_t errcode)
253{
254    apr_size_t len=0, i;
255
256#ifndef NETWARE
257#ifndef _WIN32_WCE
258    len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM
259                      | FORMAT_MESSAGE_IGNORE_INSERTS,
260                        NULL,
261                        errcode,
262                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
263                        buf,
264                        (DWORD)bufsize,
265                        NULL);
266#else /* _WIN32_WCE speaks unicode */
267     LPTSTR msg = (LPTSTR) buf;
268     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
269                       | FORMAT_MESSAGE_IGNORE_INSERTS,
270                         NULL,
271                         errcode,
272                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
273                         msg,
274                         (DWORD) (bufsize/sizeof(TCHAR)),
275                         NULL);
276     /* in-place convert to US-ASCII, substituting '?' for non ASCII   */
277     for(i = 0; i <= len; i++) {
278        if (msg[i] < 0x80 && msg[i] >= 0) {
279            buf[i] = (char) msg[i];
280        } else {
281            buf[i] = '?';
282        }
283    }
284#endif
285#endif
286
287    if (!len) {
288        for (i = 0; gaErrorList[i].msg; ++i) {
289            if (gaErrorList[i].code == errcode) {
290                apr_cpystrn(buf, gaErrorList[i].msg, bufsize);
291                len = strlen(buf);
292                break;
293            }
294        }
295    }
296
297    if (len) {
298        /* FormatMessage put the message in the buffer, but it may
299         * have embedded a newline (\r\n), and possible more than one.
300         * Remove the newlines replacing them with a space. This is not
301         * as visually perfect as moving all the remaining message over,
302         * but more efficient.
303         */
304        i = len;
305        while (i) {
306            i--;
307            if ((buf[i] == '\r') || (buf[i] == '\n'))
308                buf[i] = ' ';
309        }
310    }
311    else {
312        /* Windows didn't provide us with a message.  Even stuff like                    * WSAECONNREFUSED won't get a message.
313         */
314        apr_snprintf(buf, bufsize, "Unrecognized Win32 error code %d", errcode);
315    }
316
317    return buf;
318}
319
320#else
321/* On Unix, apr_os_strerror() handles error codes from the resolver
322 * (h_errno).
323 */
324static char *apr_os_strerror(char* buf, apr_size_t bufsize, int err)
325{
326#ifdef HAVE_HSTRERROR
327    return stuffbuffer(buf, bufsize, hstrerror(err));
328#else /* HAVE_HSTRERROR */
329    const char *msg;
330
331    switch(err) {
332    case HOST_NOT_FOUND:
333        msg = "Unknown host";
334        break;
335#if defined(NO_DATA)
336    case NO_DATA:
337#if defined(NO_ADDRESS) && (NO_DATA != NO_ADDRESS)
338    case NO_ADDRESS:
339#endif
340        msg = "No address for host";
341        break;
342#elif defined(NO_ADDRESS)
343    case NO_ADDRESS:
344        msg = "No address for host";
345        break;
346#endif /* NO_DATA */
347    default:
348        msg = "Unrecognized resolver error";
349    }
350    return stuffbuffer(buf, bufsize, msg);
351#endif /* HAVE_STRERROR */
352}
353#endif
354
355#if defined(HAVE_STRERROR_R) && defined(STRERROR_R_RC_INT) && !defined(BEOS)
356/* AIX and Tru64 style */
357static char *native_strerror(apr_status_t statcode, char *buf,
358                             apr_size_t bufsize)
359{
360    if (strerror_r(statcode, buf, bufsize) < 0) {
361        return stuffbuffer(buf, bufsize,
362                           "APR does not understand this error code");
363    }
364    else {
365        return buf;
366    }
367}
368#elif defined(HAVE_STRERROR_R)
369/* glibc style */
370
371/* BeOS has the function available, but it doesn't provide
372 * the prototype publicly (doh!), so to avoid a build warning
373 * we add a suitable prototype here.
374 */
375#if defined(BEOS)
376const char *strerror_r(apr_status_t, char *, apr_size_t);
377#endif
378
379static char *native_strerror(apr_status_t statcode, char *buf,
380                             apr_size_t bufsize)
381{
382    const char *msg;
383
384    buf[0] = '\0';
385    msg = strerror_r(statcode, buf, bufsize);
386    if (buf[0] == '\0') { /* libc didn't use our buffer */
387        return stuffbuffer(buf, bufsize, msg);
388    }
389    else {
390        return buf;
391    }
392}
393#else
394/* plain old strerror();
395 * thread-safe on some platforms (e.g., Solaris, OS/390)
396 */
397static char *native_strerror(apr_status_t statcode, char *buf,
398                             apr_size_t bufsize)
399{
400#ifdef _WIN32_WCE
401    static char err[32];
402    sprintf(err, "Native Error #%d", statcode);
403    return stuffbuffer(buf, bufsize, err);
404#else
405    const char *err = strerror(statcode);
406    if (err) {
407        return stuffbuffer(buf, bufsize, err);
408    } else {
409        return stuffbuffer(buf, bufsize,
410                           "APR does not understand this error code");
411    }
412#endif
413}
414#endif
415
416APR_DECLARE(char *) apr_strerror(apr_status_t statcode, char *buf,
417                                 apr_size_t bufsize)
418{
419    if (statcode < APR_OS_START_ERROR) {
420        return native_strerror(statcode, buf, bufsize);
421    }
422    else if (statcode < APR_OS_START_USERERR) {
423        return stuffbuffer(buf, bufsize, apr_error_string(statcode));
424    }
425    else if (statcode < APR_OS_START_EAIERR) {
426        return stuffbuffer(buf, bufsize, "APR does not understand this error code");
427    }
428    else if (statcode < APR_OS_START_SYSERR) {
429#if defined(HAVE_GAI_STRERROR)
430        statcode -= APR_OS_START_EAIERR;
431#if defined(NEGATIVE_EAI)
432        statcode = -statcode;
433#endif
434        return stuffbuffer(buf, bufsize, gai_strerror(statcode));
435#else
436        return stuffbuffer(buf, bufsize, "APR does not understand this error code");
437#endif
438    }
439    else {
440        return apr_os_strerror(buf, bufsize, statcode - APR_OS_START_SYSERR);
441    }
442}
443
444