22#include <winpr/library.h>
23#include <winpr/assert.h>
24#include <winpr/print.h>
25#include <winpr/sysinfo.h>
27#include <freerdp/utils/ringbuffer.h>
29#include "childsession.h"
31#define TAG FREERDP_TAG("childsession")
41 BYTE tmpReadBuffer[4096];
46static int transport_bio_named_uninit(BIO* bio);
48static int transport_bio_named_write(BIO* bio,
const char* buf,
int size)
53 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
58 BIO_clear_flags(bio, BIO_FLAGS_WRITE);
61 const UINT64 start = GetTickCount64();
63 WriteFile(ptr->hFile, buf, WINPR_ASSERTING_INT_CAST(uint32_t, size), &written,
nullptr);
68 WLog_VRB(TAG,
"error or deferred");
72 WLog_VRB(TAG,
"(%d)=%d written=%" PRIu32
" duration=%" PRIu64, size, ret, written,
73 GetTickCount64() - start);
77 WLog_VRB(TAG,
"closed on write");
81 WINPR_ASSERT(written <= INT32_MAX);
85static BOOL treatReadResult(WINPR_BIO_NAMED* ptr, DWORD readBytes)
87 WLog_VRB(TAG,
"treatReadResult(readBytes=%" PRIu32
")", readBytes);
88 ptr->opInProgress = FALSE;
91 WLog_VRB(TAG,
"readBytes == 0");
95 if (!ringbuffer_write(&ptr->readBuffer, ptr->tmpReadBuffer, readBytes))
97 WLog_VRB(TAG,
"ringbuffer_write()");
101 return SetEvent(ptr->readEvent);
104static BOOL doReadOp(WINPR_BIO_NAMED* ptr)
108 if (!ResetEvent(ptr->readEvent))
111 ptr->opInProgress = TRUE;
112 if (!ReadFile(ptr->hFile, ptr->tmpReadBuffer,
sizeof(ptr->tmpReadBuffer), &readBytes,
113 &ptr->readOverlapped))
115 DWORD error = GetLastError();
119 WLog_VRB(TAG,
"No Data, unexpected");
121 case ERROR_IO_PENDING:
122 WLog_VRB(TAG,
"ERROR_IO_PENDING");
124 case ERROR_BROKEN_PIPE:
125 WLog_VRB(TAG,
"broken pipe");
126 ptr->lastOpClosed = TRUE;
133 return treatReadResult(ptr, readBytes);
136static int transport_bio_named_read(BIO* bio,
char* buf,
int size)
141 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
145 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ);
149 while (!ringbuffer_used(&ptr->readBuffer))
151 if (ptr->lastOpClosed)
154 if (ptr->opInProgress)
156 DWORD status = WaitForSingleObjectEx(ptr->readEvent, 500, TRUE);
160 case WAIT_IO_COMPLETION:
169 if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE))
171 WLog_ERR(TAG,
"GetOverlappedResult blocking(lastError=%" PRIu32
")",
176 if (!treatReadResult(ptr, readBytes))
178 WLog_ERR(TAG,
"treatReadResult blocking");
186 if (ptr->opInProgress)
188 DWORD status = WaitForSingleObject(ptr->readEvent, 0);
194 BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ));
197 WLog_ERR(TAG,
"error WaitForSingleObject(readEvent)=0x%" PRIx32
"", status);
202 if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE))
204 WLog_ERR(TAG,
"GetOverlappedResult non blocking(lastError=%" PRIu32
")",
209 if (!treatReadResult(ptr, readBytes))
211 WLog_ERR(TAG,
"error treatReadResult non blocking");
220 size_t rsize = ringbuffer_used(&ptr->readBuffer);
221 if (rsize <= SSIZE_MAX)
222 ret = MIN(size, (SSIZE_T)rsize);
224 if ((size >= 0) && ret)
226 DataChunk chunks[2] = WINPR_C_ARRAY_INIT;
228 ringbuffer_peek(&ptr->readBuffer, chunks, WINPR_ASSERTING_INT_CAST(
size_t, ret));
229 for (
int i = 0; i < nchunks; i++)
231 memcpy(buf, chunks[i].data, chunks[i].size);
232 buf += chunks[i].size;
235 ringbuffer_commit_read_bytes(&ptr->readBuffer, WINPR_ASSERTING_INT_CAST(
size_t, ret));
237 WLog_VRB(TAG,
"(%d)=%" PRIdz
" nchunks=%d", size, ret, nchunks);
240 if (!ringbuffer_used(&ptr->readBuffer))
242 if (!ptr->opInProgress && !doReadOp(ptr))
244 WLog_ERR(TAG,
"error rearming read");
250 BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ));
252 WINPR_ASSERT(ret <= INT32_MAX);
256static int transport_bio_named_puts(BIO* bio,
const char* str)
261 const size_t max = (INT_MAX > SIZE_MAX) ? SIZE_MAX : INT_MAX;
262 const size_t len = strnlen(str, max);
265 return transport_bio_named_write(bio, str, WINPR_ASSERTING_INT_CAST(
int, len));
268static int transport_bio_named_gets(BIO* bio,
char* str,
int size)
273 return transport_bio_named_read(bio, str, size);
276static long transport_bio_named_ctrl(BIO* bio,
int cmd,
long arg1,
void* arg2)
281 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
285 case BIO_C_SET_SOCKET:
286 case BIO_C_GET_SOCKET:
288 case BIO_C_GET_EVENT:
289 if (!BIO_get_init(bio) || !arg2)
292 *((HANDLE*)arg2) = ptr->readEvent;
294 case BIO_C_SET_HANDLE:
295 BIO_set_init(bio, 1);
296 if (!BIO_get_init(bio) || !arg2)
299 ptr->hFile = (HANDLE)arg2;
300 ptr->blocking = TRUE;
304 case BIO_C_SET_NONBLOCK:
306 WLog_DBG(TAG,
"BIO_C_SET_NONBLOCK");
307 ptr->blocking = FALSE;
310 case BIO_C_WAIT_READ:
312 WLog_DBG(TAG,
"BIO_C_WAIT_READ");
316 case BIO_C_WAIT_WRITE:
318 WLog_DBG(TAG,
"BIO_C_WAIT_WRITE");
328 case BIO_CTRL_GET_CLOSE:
329 status = BIO_get_shutdown(bio);
332 case BIO_CTRL_SET_CLOSE:
333 BIO_set_shutdown(bio, (
int)arg1);
353static void BIO_NAMED_free(WINPR_BIO_NAMED* ptr)
360 (void)CloseHandle(ptr->hFile);
361 ptr->hFile =
nullptr;
366 (void)CloseHandle(ptr->readEvent);
367 ptr->readEvent =
nullptr;
370 ringbuffer_destroy(&ptr->readBuffer);
374static int transport_bio_named_uninit(BIO* bio)
377 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
381 BIO_set_init(bio, 0);
382 BIO_set_flags(bio, 0);
386static int transport_bio_named_new(BIO* bio)
390 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)calloc(1,
sizeof(WINPR_BIO_NAMED));
394 if (!ringbuffer_init(&ptr->readBuffer, 0xfffff))
397 ptr->readEvent = CreateEventA(
nullptr, TRUE, FALSE,
nullptr);
398 if (!ptr->readEvent || ptr->readEvent == INVALID_HANDLE_VALUE)
401 ptr->readOverlapped.hEvent = ptr->readEvent;
403 BIO_set_data(bio, ptr);
404 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
412static int transport_bio_named_free(BIO* bio)
414 WINPR_BIO_NAMED* ptr =
nullptr;
419 transport_bio_named_uninit(bio);
421 ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
423 BIO_set_data(bio,
nullptr);
428static BIO_METHOD* BIO_s_namedpipe(
void)
430 static BIO_METHOD* bio_methods =
nullptr;
432 if (bio_methods ==
nullptr)
434 if (!(bio_methods = BIO_meth_new(BIO_TYPE_NAMEDPIPE,
"NamedPipe")))
437 BIO_meth_set_write(bio_methods, transport_bio_named_write);
438 BIO_meth_set_read(bio_methods, transport_bio_named_read);
439 BIO_meth_set_puts(bio_methods, transport_bio_named_puts);
440 BIO_meth_set_gets(bio_methods, transport_bio_named_gets);
441 BIO_meth_set_ctrl(bio_methods, transport_bio_named_ctrl);
442 BIO_meth_set_create(bio_methods, transport_bio_named_new);
443 BIO_meth_set_destroy(bio_methods, transport_bio_named_free);
449typedef NTSTATUS (*WinStationCreateChildSessionTransportFn)(WCHAR* path, DWORD len);
450static BOOL createChildSessionTransport(HANDLE* pFile)
454 HANDLE hModule =
nullptr;
456 *pFile = INVALID_HANDLE_VALUE;
458 BOOL childEnabled = 0;
459 if (!WTSIsChildSessionsEnabled(&childEnabled))
461 WLog_ERR(TAG,
"error when calling WTSIsChildSessionsEnabled");
467 WLog_INFO(TAG,
"child sessions aren't enabled");
468 if (!WTSEnableChildSessions(TRUE))
470 WLog_ERR(TAG,
"error when calling WTSEnableChildSessions");
473 WLog_INFO(TAG,
"successfully enabled child sessions");
476 hModule = LoadLibraryA(
"winsta.dll");
481 WCHAR pipePath[0x80] = WINPR_C_ARRAY_INIT;
482 char pipePathA[0x80] = WINPR_C_ARRAY_INIT;
485 WinStationCreateChildSessionTransportFn createChildSessionFn =
486 GetProcAddressAs(hModule,
"WinStationCreateChildSessionTransport",
487 WinStationCreateChildSessionTransportFn);
488 if (!createChildSessionFn)
490 WLog_ERR(TAG,
"unable to retrieve WinStationCreateChildSessionTransport function");
495 HRESULT hStatus = createChildSessionFn(pipePath, 0x80);
496 if (!SUCCEEDED(hStatus))
498 WLog_ERR(TAG,
"error 0x%08x when creating childSessionTransport",
499 WINPR_CXX_COMPAT_CAST(
unsigned, hStatus));
506 const BYTE startOfPath[] = {
'\\', 0,
'\\', 0,
'.', 0,
'\\', 0 };
507 if (_wcsncmp(pipePath, (
const WCHAR*)startOfPath, 4))
512 size_t len = _wcslen(pipePath);
513 if (len > 0x80 - (4 + 1))
515 WLog_ERR(TAG,
"pipePath is too long to be adjusted");
519 memmove(pipePath + 4, pipePath, (len + 1) *
sizeof(WCHAR));
520 memcpy(pipePath, startOfPath, 8);
524 (void)ConvertWCharNToUtf8(pipePath, 0x80, pipePathA,
sizeof(pipePathA));
525 WLog_DBG(TAG,
"child session is at '%s'", pipePathA);
528 HANDLE f = CreateFileW(pipePath, GENERIC_READ | GENERIC_WRITE, 0,
nullptr,
529 OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
nullptr);
530 if (f == INVALID_HANDLE_VALUE)
532 WLog_ERR(TAG,
"error when connecting to local named pipe");
543 FreeLibrary(hModule);
547BIO* createChildSessionBio(
void)
549 HANDLE f = INVALID_HANDLE_VALUE;
550 if (!createChildSessionTransport(&f))
553 BIO* lowLevelBio = BIO_new(BIO_s_namedpipe());
556 (void)CloseHandle(f);
560 BIO_set_handle(lowLevelBio, f);
561 BIO* bufferedBio = BIO_new(BIO_s_buffered_socket());
565 BIO_free_all(lowLevelBio);
569 bufferedBio = BIO_push(bufferedBio, lowLevelBio);
a piece of data in the ring buffer, exactly like a glibc iovec