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];
46 static int transport_bio_named_uninit(BIO* bio);
48 static 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 UINT64 start = GetTickCount64();
62 BOOL ret = WriteFile(ptr->hFile, buf, WINPR_ASSERTING_INT_CAST(uint32_t, size), &written, NULL);
67 WLog_VRB(TAG,
"error or deferred");
71 WLog_VRB(TAG,
"(%d)=%d written=%d duration=%d", size, ret, written, GetTickCount64() - start);
75 WLog_VRB(TAG,
"closed on write");
79 WINPR_ASSERT(written <= INT32_MAX);
83 static BOOL treatReadResult(WINPR_BIO_NAMED* ptr, DWORD readBytes)
85 WLog_VRB(TAG,
"treatReadResult(readBytes=%" PRIu32
")", readBytes);
86 ptr->opInProgress = FALSE;
89 WLog_VRB(TAG,
"readBytes == 0");
93 if (!ringbuffer_write(&ptr->readBuffer, ptr->tmpReadBuffer, readBytes))
95 WLog_VRB(TAG,
"ringbuffer_write()");
99 return SetEvent(ptr->readEvent);
102 static BOOL doReadOp(WINPR_BIO_NAMED* ptr)
106 if (!ResetEvent(ptr->readEvent))
109 ptr->opInProgress = TRUE;
110 if (!ReadFile(ptr->hFile, ptr->tmpReadBuffer,
sizeof(ptr->tmpReadBuffer), &readBytes,
111 &ptr->readOverlapped))
113 DWORD error = GetLastError();
117 WLog_VRB(TAG,
"No Data, unexpected");
119 case ERROR_IO_PENDING:
120 WLog_VRB(TAG,
"ERROR_IO_PENDING");
122 case ERROR_BROKEN_PIPE:
123 WLog_VRB(TAG,
"broken pipe");
124 ptr->lastOpClosed = TRUE;
131 return treatReadResult(ptr, readBytes);
134 static int transport_bio_named_read(BIO* bio,
char* buf,
int size)
139 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
143 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ);
147 while (!ringbuffer_used(&ptr->readBuffer))
149 if (ptr->lastOpClosed)
152 if (ptr->opInProgress)
154 DWORD status = WaitForSingleObjectEx(ptr->readEvent, 500, TRUE);
158 case WAIT_IO_COMPLETION:
167 if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE))
169 WLog_ERR(TAG,
"GetOverlappedResult blocking(lastError=%" PRIu32
")",
174 if (!treatReadResult(ptr, readBytes))
176 WLog_ERR(TAG,
"treatReadResult blocking");
184 if (ptr->opInProgress)
186 DWORD status = WaitForSingleObject(ptr->readEvent, 0);
192 BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ));
195 WLog_ERR(TAG,
"error WaitForSingleObject(readEvent)=0x%" PRIx32
"", status);
200 if (!GetOverlappedResult(ptr->hFile, &ptr->readOverlapped, &readBytes, FALSE))
202 WLog_ERR(TAG,
"GetOverlappedResult non blocking(lastError=%" PRIu32
")",
207 if (!treatReadResult(ptr, readBytes))
209 WLog_ERR(TAG,
"error treatReadResult non blocking");
218 size_t rsize = ringbuffer_used(&ptr->readBuffer);
219 if (rsize <= SSIZE_MAX)
220 ret = MIN(size, (SSIZE_T)rsize);
222 if ((size >= 0) && ret)
226 ringbuffer_peek(&ptr->readBuffer, chunks, WINPR_ASSERTING_INT_CAST(
size_t, ret));
227 for (
int i = 0; i < nchunks; i++)
229 memcpy(buf, chunks[i].data, chunks[i].size);
230 buf += chunks[i].size;
233 ringbuffer_commit_read_bytes(&ptr->readBuffer, WINPR_ASSERTING_INT_CAST(
size_t, ret));
235 WLog_VRB(TAG,
"(%d)=%" PRIdz
" nchunks=%d", size, ret, nchunks);
238 if (!ringbuffer_used(&ptr->readBuffer))
240 if (!ptr->opInProgress && !doReadOp(ptr))
242 WLog_ERR(TAG,
"error rearming read");
248 BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ));
250 WINPR_ASSERT(ret <= INT32_MAX);
254 static int transport_bio_named_puts(BIO* bio,
const char* str)
259 return transport_bio_named_write(bio, str, (
int)strnlen(str, INT32_MAX));
262 static int transport_bio_named_gets(BIO* bio,
char* str,
int size)
267 return transport_bio_named_read(bio, str, size);
270 static long transport_bio_named_ctrl(BIO* bio,
int cmd,
long arg1,
void* arg2)
275 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
279 case BIO_C_SET_SOCKET:
280 case BIO_C_GET_SOCKET:
282 case BIO_C_GET_EVENT:
283 if (!BIO_get_init(bio) || !arg2)
286 *((HANDLE*)arg2) = ptr->readEvent;
288 case BIO_C_SET_HANDLE:
289 BIO_set_init(bio, 1);
290 if (!BIO_get_init(bio) || !arg2)
293 ptr->hFile = (HANDLE)arg2;
294 ptr->blocking = TRUE;
298 case BIO_C_SET_NONBLOCK:
300 WLog_DBG(TAG,
"BIO_C_SET_NONBLOCK");
301 ptr->blocking = FALSE;
304 case BIO_C_WAIT_READ:
306 WLog_DBG(TAG,
"BIO_C_WAIT_READ");
310 case BIO_C_WAIT_WRITE:
312 WLog_DBG(TAG,
"BIO_C_WAIT_WRITE");
322 case BIO_CTRL_GET_CLOSE:
323 status = BIO_get_shutdown(bio);
326 case BIO_CTRL_SET_CLOSE:
327 BIO_set_shutdown(bio, (
int)arg1);
347 static void BIO_NAMED_free(WINPR_BIO_NAMED* ptr)
354 (void)CloseHandle(ptr->hFile);
360 (void)CloseHandle(ptr->readEvent);
361 ptr->readEvent = NULL;
364 ringbuffer_destroy(&ptr->readBuffer);
368 static int transport_bio_named_uninit(BIO* bio)
371 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
375 BIO_set_init(bio, 0);
376 BIO_set_flags(bio, 0);
380 static int transport_bio_named_new(BIO* bio)
384 WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)calloc(1,
sizeof(WINPR_BIO_NAMED));
388 if (!ringbuffer_init(&ptr->readBuffer, 0xfffff))
391 ptr->readEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
392 if (!ptr->readEvent || ptr->readEvent == INVALID_HANDLE_VALUE)
395 ptr->readOverlapped.hEvent = ptr->readEvent;
397 BIO_set_data(bio, ptr);
398 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY);
406 static int transport_bio_named_free(BIO* bio)
408 WINPR_BIO_NAMED* ptr = NULL;
413 transport_bio_named_uninit(bio);
415 ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio);
417 BIO_set_data(bio, NULL);
422 static BIO_METHOD* BIO_s_namedpipe(
void)
424 static BIO_METHOD* bio_methods = NULL;
426 if (bio_methods == NULL)
428 if (!(bio_methods = BIO_meth_new(BIO_TYPE_NAMEDPIPE,
"NamedPipe")))
431 BIO_meth_set_write(bio_methods, transport_bio_named_write);
432 BIO_meth_set_read(bio_methods, transport_bio_named_read);
433 BIO_meth_set_puts(bio_methods, transport_bio_named_puts);
434 BIO_meth_set_gets(bio_methods, transport_bio_named_gets);
435 BIO_meth_set_ctrl(bio_methods, transport_bio_named_ctrl);
436 BIO_meth_set_create(bio_methods, transport_bio_named_new);
437 BIO_meth_set_destroy(bio_methods, transport_bio_named_free);
443 typedef NTSTATUS (*WinStationCreateChildSessionTransportFn)(WCHAR* path, DWORD len);
444 static BOOL createChildSessionTransport(HANDLE* pFile)
448 HANDLE hModule = NULL;
450 *pFile = INVALID_HANDLE_VALUE;
452 BOOL childEnabled = 0;
453 if (!WTSIsChildSessionsEnabled(&childEnabled))
455 WLog_ERR(TAG,
"error when calling WTSIsChildSessionsEnabled");
461 WLog_INFO(TAG,
"child sessions aren't enabled");
462 if (!WTSEnableChildSessions(TRUE))
464 WLog_ERR(TAG,
"error when calling WTSEnableChildSessions");
467 WLog_INFO(TAG,
"successfully enabled child sessions");
470 hModule = LoadLibraryA(
"winsta.dll");
473 WCHAR pipePath[0x80] = { 0 };
474 char pipePathA[0x80] = { 0 };
476 WinStationCreateChildSessionTransportFn createChildSessionFn = GetProcAddressAs(
477 hModule,
"WinStationCreateChildSessionTransport", WinStationCreateChildSessionTransportFn);
478 if (!createChildSessionFn)
480 WLog_ERR(TAG,
"unable to retrieve WinStationCreateChildSessionTransport function");
484 HRESULT hStatus = createChildSessionFn(pipePath, 0x80);
485 if (!SUCCEEDED(hStatus))
487 WLog_ERR(TAG,
"error 0x%x when creating childSessionTransport", hStatus);
491 const BYTE startOfPath[] = {
'\\', 0,
'\\', 0,
'.', 0,
'\\', 0 };
492 if (_wcsncmp(pipePath, (
const WCHAR*)startOfPath, 4))
497 size_t len = _wcslen(pipePath);
498 if (len > 0x80 - (4 + 1))
500 WLog_ERR(TAG,
"pipePath is too long to be adjusted");
504 memmove(pipePath + 4, pipePath, (len + 1) *
sizeof(WCHAR));
505 memcpy(pipePath, startOfPath, 8);
508 (void)ConvertWCharNToUtf8(pipePath, 0x80, pipePathA,
sizeof(pipePathA));
509 WLog_DBG(TAG,
"child session is at '%s'", pipePathA);
511 HANDLE f = CreateFileW(pipePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
512 FILE_FLAG_OVERLAPPED, NULL);
513 if (f == INVALID_HANDLE_VALUE)
515 WLog_ERR(TAG,
"error when connecting to local named pipe");
523 FreeLibrary(hModule);
527 BIO* createChildSessionBio(
void)
529 HANDLE f = INVALID_HANDLE_VALUE;
530 if (!createChildSessionTransport(&f))
533 BIO* lowLevelBio = BIO_new(BIO_s_namedpipe());
536 (void)CloseHandle(f);
540 BIO_set_handle(lowLevelBio, &f);
541 BIO* bufferedBio = BIO_new(BIO_s_buffered_socket());
545 BIO_free_all(lowLevelBio);
549 bufferedBio = BIO_push(bufferedBio, lowLevelBio);
a piece of data in the ring buffer, exactly like a glibc iovec