FreeRDP
Loading...
Searching...
No Matches
handle.c
1
21#include <winpr/config.h>
22
23#include <winpr/handle.h>
24#include <winpr/assert.h>
25
26#include "../log.h"
27#define TAG WINPR_TAG("handle")
28
29#ifndef _WIN32
30
31#include <errno.h>
32#include <fcntl.h>
33#include <pthread.h>
34#include <string.h>
35
36#include "../synch/synch.h"
37#include "../thread/thread.h"
38#include "../pipe/pipe.h"
39#include "../comm/comm.h"
40#include "../security/security.h"
41
42#ifdef WINPR_HAVE_UNISTD_H
43#include <unistd.h>
44#endif
45
46#include "../handle/handle.h"
47
48#endif
49
50/* finds the "{}" placeholder in `format`, splitting it into the literal text before and after it.
51 * Returns FALSE (and leaves *prefixLen and *suffix untouched) if `format` has none. */
52WINPR_ATTR_NODISCARD
53static BOOL winpr_handle_format_split(const char* format, size_t* prefixLen, const char** suffix)
54{
55 const char* placeholder = strstr(format, "{}");
56 if (!placeholder)
57 {
58 WLog_ERR(TAG, "format string '%s' has no {} placeholder for the handle value", format);
59 return FALSE;
60 }
61
62 *prefixLen = (size_t)(placeholder - format);
63 *suffix = placeholder + 2;
64 return TRUE;
65}
66
67#ifndef _WIN32
68/* single-character tag identifying a handle's type in the exported value (see
69 * winpr_exportHandleToString()/winpr_importHandleFromString()), so a POSIX-side import knows how
70 * to rebuild the right kind of HANDLE around the fd. Only pipes (as returned by CreatePipe()) are
71 * supported for now; add a case here (and a matching one in winpr_importHandleFromString()) when
72 * another handle type needs to cross a fork()+exec(). */
73WINPR_ATTR_NODISCARD
74static char winpr_handle_export_type_tag(ULONG type)
75{
76 switch (type)
77 {
78 case HANDLE_TYPE_ANONYMOUS_PIPE:
79 return 'P';
80 default:
81 return '\0';
82 }
83}
84#endif
85
86BOOL winpr_exportHandleToString(HANDLE h, const char* format, char* outStr, size_t outSz)
87{
88 if (!format || !outStr || (outSz == 0))
89 return FALSE;
90
91 size_t prefixLen = 0;
92 const char* suffix = nullptr;
93 if (!winpr_handle_format_split(format, &prefixLen, &suffix))
94 return FALSE;
95
96 if (!h || (h == INVALID_HANDLE_VALUE))
97 {
98 WLog_ERR(TAG, "refusing to export an invalid handle");
99 return FALSE;
100 }
101 size_t remaining = outSz - 1;
102 if (remaining <= prefixLen)
103 return FALSE;
104 remaining -= prefixLen;
105
106 size_t suffixLen = strlen(suffix);
107 if (remaining <= suffixLen)
108 return FALSE;
109 remaining -= suffixLen;
110
111 char value[32] = WINPR_C_ARRAY_INIT;
112#ifdef _WIN32
113 const int valueLenInt = snprintf(value, sizeof(value), "%llx", (unsigned long long)(UINT_PTR)h);
114#else
115 ULONG type = 0;
116 WINPR_HANDLE* hdl = nullptr;
117 if (!winpr_Handle_GetInfo(h, &type, &hdl))
118 {
119 WLog_ERR(TAG, "unable to resolve handle info");
120 return FALSE;
121 }
122
123 const char typeTag = winpr_handle_export_type_tag(type);
124 if (typeTag == '\0')
125 {
126 WLog_ERR(TAG,
127 "exporting handle type %" PRIu32 " is not supported, only pipe handles can be "
128 "exported for now",
129 type);
130 return FALSE;
131 }
132
133 const int fd = winpr_Handle_getFd(h);
134 if (fd < 0)
135 {
136 WLog_ERR(TAG, "unable to resolve a file descriptor for this handle");
137 return FALSE;
138 }
139 const int valueLenInt = snprintf(value, sizeof(value), "%c%x", typeTag, (unsigned)fd);
140#endif
141 if (valueLenInt <= 0)
142 return FALSE;
143 const size_t valueLen = (size_t)valueLenInt;
144
145 if (valueLen > remaining)
146 return FALSE;
147
148 memcpy(outStr, format, prefixLen);
149 memcpy(outStr + prefixLen, value, valueLen);
150 memcpy(outStr + prefixLen + valueLen, suffix, suffixLen + 1);
151 return TRUE;
152}
153
154HANDLE winpr_importHandleFromString(const char* str, const char* format)
155{
156 if (!str || !format)
157 return INVALID_HANDLE_VALUE;
158
159 size_t prefixLen = 0;
160 const char* suffix = nullptr;
161 if (!winpr_handle_format_split(format, &prefixLen, &suffix))
162 return INVALID_HANDLE_VALUE;
163
164 const size_t suffixLen = strlen(suffix);
165 const size_t strLen = strlen(str);
166 if ((strLen < prefixLen + suffixLen) || (strncmp(str, format, prefixLen) != 0) ||
167 (strcmp(str + strLen - suffixLen, suffix) != 0))
168 {
169 WLog_ERR(TAG, "input string '%s' does not match format '%s'", str, format);
170 return INVALID_HANDLE_VALUE;
171 }
172
173 char value[32] = WINPR_C_ARRAY_INIT;
174 size_t valueLen = strLen - prefixLen - suffixLen;
175 const char* valueStart = str + prefixLen;
176 if ((valueLen == 0) || (valueLen >= sizeof(value)))
177 {
178 WLog_ERR(TAG, "input string '%s' has no usable handle value", str);
179 return INVALID_HANDLE_VALUE;
180 }
181
182#ifndef _WIN32
183 /* first character is the type tag written by winpr_exportHandleToString() (see
184 * winpr_handle_export_type_tag()) - only pipes are supported for now. */
185 const char typeTag = valueStart[0];
186 if (typeTag != 'P')
187 {
188 WLog_ERR(TAG,
189 "unsupported handle type tag '%c' in '%s', only pipe handles ('P') can be "
190 "imported for now",
191 typeTag, str);
192 return INVALID_HANDLE_VALUE;
193 }
194 valueStart++;
195 valueLen--;
196 if (valueLen == 0)
197 {
198 WLog_ERR(TAG, "input string '%s' has no usable handle value after the type tag", str);
199 return INVALID_HANDLE_VALUE;
200 }
201#endif
202
203 memcpy(value, valueStart, valueLen);
204 value[valueLen] = '\0';
205
206 char* end = nullptr;
207 const unsigned long long numeric = strtoull(value, &end, 16);
208 if (!end || (*end != '\0'))
209 {
210 WLog_ERR(TAG, "invalid handle value '%s'", value);
211 return INVALID_HANDLE_VALUE;
212 }
213
214#ifdef _WIN32
215 return (HANDLE)(UINT_PTR)numeric;
216#else
217 if (numeric > INT32_MAX)
218 {
219 WLog_ERR(TAG, "handle value '%s' out of range for a file descriptor", value);
220 return INVALID_HANDLE_VALUE;
221 }
222 return winpr_Pipe_FromFd((int)numeric);
223#endif
224}
225
226#ifndef _WIN32
227
228BOOL CloseHandle(HANDLE hObject)
229{
230 ULONG type = 0;
231 WINPR_HANDLE* hdl = nullptr;
232
233 if (!winpr_Handle_GetInfo(hObject, &type, &hdl) || !hdl || !hdl->ops)
234 return FALSE;
235
236 BOOL ok = TRUE;
237 if (hdl->ops->CloseHandle)
238 ok = hdl->ops->CloseHandle(hObject);
239
240 /* a type's CloseHandle op can legitimately refuse (e.g. file.c won't close the pStdHandleFile
241 * singleton unless forced) - if it did, this handle wasn't actually closed, so leave its
242 * refcount/memory alone entirely. */
243 if (!ok)
244 return FALSE;
245
246 /* WINPR_THREAD manages its own close/free lifecycle: closing the handle of a still-running
247 * thread detaches it instead of freeing it, and the free only happens later, from the
248 * thread's own pthread routine once it actually returns (see ThreadCloseHandle() /
249 * cleanup_handle() in thread.c). Applying the generic release-then-maybe-free sequence below
250 * on top of that would free the struct while the thread may still be running against it. */
251 if (type == HANDLE_TYPE_THREAD)
252 return TRUE;
253
254 if (!winpr_Handle_Release(hObject))
255 winpr_Handle_ConvertToNone(hObject);
256
257 return TRUE;
258}
259
260BOOL DuplicateHandle(WINPR_ATTR_UNUSED HANDLE hSourceProcessHandle,
261 WINPR_ATTR_UNUSED HANDLE hSourceHandle,
262 WINPR_ATTR_UNUSED HANDLE hTargetProcessHandle,
263 WINPR_ATTR_UNUSED LPHANDLE lpTargetHandle,
264 WINPR_ATTR_UNUSED DWORD dwDesiredAccess, WINPR_ATTR_UNUSED BOOL bInheritHandle,
265 WINPR_ATTR_UNUSED DWORD dwOptions)
266{
267 WLog_ERR(TAG, "DuplicateHandle not implemented");
268 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
269 return FALSE;
270}
271
272BOOL GetHandleInformation(HANDLE hObject, LPDWORD lpdwFlags)
273{
274 if (!lpdwFlags)
275 return FALSE;
276
277 const int fd = winpr_Handle_getFd(hObject);
278 if (fd < 0)
279 {
280 WLog_ERR(TAG, "unable to resolve a file descriptor for this handle type");
281 return FALSE;
282 }
283
284 const int flags = fcntl(fd, F_GETFD);
285 if (flags < 0)
286 {
287 char buffer[64] = WINPR_C_ARRAY_INIT;
288 WLog_ERR(TAG, "fcntl(F_GETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
289 return FALSE;
290 }
291
292 *lpdwFlags = (flags & FD_CLOEXEC) ? 0 : HANDLE_FLAG_INHERIT;
293 return TRUE;
294}
295
296BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags)
297{
298 const int fd = winpr_Handle_getFd(hObject);
299 if (fd < 0)
300 {
301 WLog_ERR(TAG, "unable to resolve a file descriptor for this handle type");
302 return FALSE;
303 }
304
305 /* only HANDLE_FLAG_INHERIT is meaningful on POSIX; ignore any other bit in the mask rather
306 * than failing, matching how Windows treats masks it doesn't recognize on other handle
307 * types */
308 if (!(dwMask & HANDLE_FLAG_INHERIT))
309 return TRUE;
310
311 int flags = fcntl(fd, F_GETFD);
312 if (flags < 0)
313 {
314 char buffer[64] = WINPR_C_ARRAY_INIT;
315 WLog_ERR(TAG, "fcntl(F_GETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
316 return FALSE;
317 }
318
319 flags = (dwFlags & HANDLE_FLAG_INHERIT) ? (flags & ~FD_CLOEXEC) : (flags | FD_CLOEXEC);
320 if (fcntl(fd, F_SETFD, flags) < 0)
321 {
322 char buffer[64] = WINPR_C_ARRAY_INIT;
323 WLog_ERR(TAG, "fcntl(F_SETFD) failed: %s", winpr_strerror(errno, buffer, sizeof(buffer)));
324 return FALSE;
325 }
326
327 return TRUE;
328}
329
330BOOL winpr_set_cloexec(int fd, BOOL cloexec)
331{
332 const int flags = fcntl(fd, F_GETFD);
333 if (flags < 0)
334 return FALSE;
335
336 const int newFlags = cloexec ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC);
337 return fcntl(fd, F_SETFD, newFlags) >= 0;
338}
339
340#endif