1 /*
2 * SRT - Secure, Reliable, Transport
3 * Copyright (c) 2018 Haivision Systems Inc.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 */
10
11 // Implementation file for srt_compat.h
12 /*****************************************************************************
13 written by
14 Haivision Systems Inc.
15 *****************************************************************************/
16
17 // Prevents from misconfiguration through preprocessor.
18
19 #include "platform_sys.h"
20
21 #include <srt_compat.h>
22
23 #include <string.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #if defined(__unix__) && !defined(BSD) && !defined(SUNOS)
27 #include <features.h>
28 #endif
29
30 #if defined(_WIN32)
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 #endif
34
35
SysStrError_Fallback(int errnum,char * buf,size_t buflen)36 static const char* SysStrError_Fallback(int errnum, char* buf, size_t buflen)
37 {
38 #if defined(_MSC_VER) && _MSC_VER < 1900
39 _snprintf(buf, buflen - 1, "ERROR CODE %d", errnum);
40 buf[buflen - 1] = '\0';
41 #else
42 snprintf(buf, buflen, "ERROR CODE %d", errnum);
43 #endif
44 return buf;
45 }
46
47 // This function is a portable and thread-safe version of `strerror`.
48 // It requires a user-supplied buffer to store the message. The returned
49 // value is always equal to the given buffer pointer. If the system
50 // error message is longer than the given buflen, it will be trimmed.
51 // When the error code is incorrect for the given error message function,
52 // a fallback message will be returned, either as returned by the underlying
53 // function, or crafted by this function as a response to error in an
54 // underlying function.
SysStrError(int errnum,char * buf,size_t buflen)55 extern const char * SysStrError(int errnum, char * buf, size_t buflen)
56 {
57 if (buf == NULL || buflen < 4) // Required to put ??? into it as a fallback
58 {
59 errno = EFAULT;
60 return buf;
61 }
62
63 buf[0] = '\0';
64
65 #if defined(_WIN32)
66 const char* lpMsgBuf;
67
68 // Note: Intentionally the "fixed char size" types are used despite using
69 // character size dependent FormatMessage (instead of FormatMessageA) so that
70 // your compilation fails when you use wide characters.
71 // The problem is that when TCHAR != char, then the buffer written this way
72 // would have to be converted to ASCII, not just copied by strncpy.
73 FormatMessage(0
74 | FORMAT_MESSAGE_ALLOCATE_BUFFER
75 | FORMAT_MESSAGE_FROM_SYSTEM
76 | FORMAT_MESSAGE_IGNORE_INSERTS,
77 NULL, // no lpSource
78 errnum, // dwMessageId (as controlled by FORMAT_MESSAGE_FROM_SYSTEM)
79 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
80 // This below parameter normally should contain a pointer to an allocated buffer,
81 // and this way it's LPTSTR. But when FORMAT_MESSAGE_ALLOCATE_BUFFER, then it is
82 // expected to be a the value of LPTSTR* type, converted to LPTSTR, that designates
83 // a pointer to a variable of type LPTSTR, to which the newly allocated buffer is
84 // assigned. This buffer should be freed afterwards using LocalFree().
85 (LPSTR)&lpMsgBuf,
86 0, NULL);
87
88 if (lpMsgBuf)
89 {
90 strncpy(buf, lpMsgBuf, buflen-1);
91 buf[buflen-1] = 0;
92 LocalFree((HLOCAL)lpMsgBuf);
93 }
94 else
95 {
96 SysStrError_Fallback(errnum, buf, buflen);
97 }
98
99 return buf;
100
101 #elif (!defined(__GNU_LIBRARY__) && !defined(__GLIBC__) ) \
102 || (( (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && ! _GNU_SOURCE )
103 // POSIX/XSI-compliant version.
104 // Overall general POSIX version: returns status.
105 // 0 for success, otherwise it's:
106 // - possibly -1 and the error code is in ::errno
107 // - possibly the error code itself
108 // The details of the errror are not interesting; simply
109 // craft a fallback message in this case.
110 if (strerror_r(errnum, buf, buflen) != 0)
111 {
112 return SysStrError_Fallback(errnum, buf, buflen);
113 }
114 return buf;
115 #else
116 // GLIBC is non-standard under these conditions.
117 // GNU version: returns the pointer to the message.
118 // This is either equal to the local buffer (buf)
119 // or some system-wide (constant) storage. To maintain
120 // stability of the API, this overall function shall
121 // always return the local buffer and the message in
122 // this buffer - so these cases should be distinguished
123 // and the internal storage copied to the buffer.
124
125 char * gnu_buffer = strerror_r(errnum, buf, buflen);
126 if (!gnu_buffer)
127 {
128 // This should never happen, so just a paranoid check
129 return SysStrError_Fallback(errnum, buf, buflen);
130 }
131
132 // If they are the same, the message is already copied
133 // (and it's usually a "fallback message" for an error case).
134 if (gnu_buffer != buf)
135 {
136 strncpy(buf, gnu_buffer, buflen-1);
137 buf[buflen-1] = 0; // guarantee what strncpy doesn't
138 }
139
140 return buf;
141 #endif
142 }
143