FreeRDP
Loading...
Searching...
No Matches
pipe.c
1
23#include <winpr/config.h>
24
25#include <winpr/crt.h>
26#include <winpr/path.h>
27#include <winpr/synch.h>
28#include <winpr/handle.h>
29
30#include <winpr/pipe.h>
31
32#ifdef WINPR_HAVE_UNISTD_H
33#include <unistd.h>
34#endif
35
36#ifndef _WIN32
37
38#include "../handle/handle.h"
39
40#include <fcntl.h>
41#include <errno.h>
42#include <sys/un.h>
43#include <sys/socket.h>
44#include <winpr/assert.h>
45
46#ifdef WINPR_HAVE_SYS_AIO_H
47#undef WINPR_HAVE_SYS_AIO_H /* disable for now, incomplete */
48#endif
49
50#ifdef WINPR_HAVE_SYS_AIO_H
51#include <aio.h>
52#endif
53
54#include "pipe.h"
55
56#include "../log.h"
57#define TAG WINPR_TAG("pipe")
58
59/*
60 * Since the WinPR implementation of named pipes makes use of UNIX domain
61 * sockets, it is not possible to bind the same name more than once (i.e.,
62 * SO_REUSEADDR does not work with UNIX domain sockets). As a result, the
63 * first call to CreateNamedPipe with name n creates a "shared" UNIX domain
64 * socket descriptor that gets duplicated via dup() for the first and all
65 * subsequent calls to CreateNamedPipe with name n.
66 *
67 * The following array keeps track of the references to the shared socked
68 * descriptors. If an entry's reference count is zero the base socket
69 * descriptor gets closed and the entry is removed from the list.
70 */
71
72static wArrayList* g_NamedPipeServerSockets = nullptr;
73
74typedef struct
75{
76 char* name;
77 int serverfd;
78 int references;
79} NamedPipeServerSocketEntry;
80
81static BOOL PipeIsHandled(HANDLE handle)
82{
83 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_ANONYMOUS_PIPE, FALSE);
84}
85
86static int PipeGetFd(HANDLE handle)
87{
88 WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
89
90 if (!PipeIsHandled(handle))
91 return -1;
92
93 return pipe->fd;
94}
95
96static BOOL PipeCloseHandle(HANDLE handle)
97{
98 WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
99
100 if (!PipeIsHandled(handle))
101 return FALSE;
102
103 if (pipe->fd != -1)
104 {
105 close(pipe->fd);
106 pipe->fd = -1;
107 }
108
109 return TRUE;
110}
111
112static BOOL PipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
113 LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
114{
115 SSIZE_T io_status = 0;
116 WINPR_PIPE* pipe = nullptr;
117 BOOL status = TRUE;
118
119 if (lpOverlapped)
120 {
121 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
122 SetLastError(ERROR_NOT_SUPPORTED);
123 return FALSE;
124 }
125
126 pipe = (WINPR_PIPE*)Object;
127
128 do
129 {
130 io_status = read(pipe->fd, lpBuffer, nNumberOfBytesToRead);
131 } while ((io_status < 0) && (errno == EINTR));
132
133 if (io_status < 0)
134 {
135 status = FALSE;
136
137 switch (errno)
138 {
139 case EWOULDBLOCK:
140 SetLastError(ERROR_NO_DATA);
141 break;
142 default:
143 break;
144 }
145 }
146
147 if (lpNumberOfBytesRead)
148 *lpNumberOfBytesRead = (DWORD)io_status;
149
150 return status;
151}
152
153static BOOL PipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
154 LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
155{
156 SSIZE_T io_status = 0;
157 WINPR_PIPE* pipe = nullptr;
158
159 if (lpOverlapped)
160 {
161 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
162 SetLastError(ERROR_NOT_SUPPORTED);
163 return FALSE;
164 }
165
166 pipe = (WINPR_PIPE*)Object;
167
168 do
169 {
170 io_status = write(pipe->fd, lpBuffer, nNumberOfBytesToWrite);
171 } while ((io_status < 0) && (errno == EINTR));
172
173 if ((io_status < 0) && (errno == EWOULDBLOCK))
174 io_status = 0;
175
176 *lpNumberOfBytesWritten = (DWORD)io_status;
177 return TRUE;
178}
179
180static HANDLE_OPS ops = { PipeIsHandled,
181 PipeCloseHandle,
182 PipeGetFd,
183 nullptr, /* CleanupHandle */
184 PipeRead,
185 nullptr, /* FileReadEx */
186 nullptr, /* FileReadScatter */
187 PipeWrite,
188 nullptr, /* FileWriteEx */
189 nullptr, /* FileWriteGather */
190 nullptr, /* FileGetFileSize */
191 nullptr, /* FlushFileBuffers */
192 nullptr, /* FileSetEndOfFile */
193 nullptr, /* FileSetFilePointer */
194 nullptr, /* SetFilePointerEx */
195 nullptr, /* FileLockFile */
196 nullptr, /* FileLockFileEx */
197 nullptr, /* FileUnlockFile */
198 nullptr, /* FileUnlockFileEx */
199 nullptr /* SetFileTime */
200 ,
201 nullptr };
202
203static BOOL NamedPipeIsHandled(HANDLE handle)
204{
205 return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE);
206}
207
208static int NamedPipeGetFd(HANDLE handle)
209{
210 WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)handle;
211
212 if (!NamedPipeIsHandled(handle))
213 return -1;
214
215 if (pipe->ServerMode)
216 return pipe->serverfd;
217
218 return pipe->clientfd;
219}
220
221static BOOL NamedPipeCloseHandle(HANDLE handle)
222{
223 WINPR_NAMED_PIPE* pNamedPipe = (WINPR_NAMED_PIPE*)handle;
224
225 /* This check confuses the analyzer. Since not all handle
226 * types are handled here, it guesses that the memory of a
227 * NamedPipeHandle may leak. */
228#ifndef __clang_analyzer__
229 if (!NamedPipeIsHandled(handle))
230 return FALSE;
231#endif
232
233 if (pNamedPipe->pfnUnrefNamedPipe)
234 pNamedPipe->pfnUnrefNamedPipe(pNamedPipe);
235
236 free(pNamedPipe->name);
237 pNamedPipe->name = nullptr;
238 free(pNamedPipe->lpFileName);
239 pNamedPipe->lpFileName = nullptr;
240 free(pNamedPipe->lpFilePath);
241 pNamedPipe->lpFilePath = nullptr;
242
243 if (pNamedPipe->serverfd != -1)
244 {
245 close(pNamedPipe->serverfd);
246 pNamedPipe->serverfd = -1;
247 }
248
249 if (pNamedPipe->clientfd != -1)
250 {
251 close(pNamedPipe->clientfd);
252 pNamedPipe->clientfd = -1;
253 }
254
255 return TRUE;
256}
257
258BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
259 LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
260{
261 SSIZE_T io_status = 0;
262 BOOL status = TRUE;
263
264 WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)Object;
265
266 if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
267 {
268 if (pipe->clientfd == -1)
269 return FALSE;
270
271 do
272 {
273 io_status = read(pipe->clientfd, lpBuffer, nNumberOfBytesToRead);
274 } while ((io_status < 0) && (errno == EINTR));
275
276 if (io_status == 0)
277 {
278 SetLastError(ERROR_BROKEN_PIPE);
279 status = FALSE;
280 }
281 else if (io_status < 0)
282 {
283 status = FALSE;
284
285 switch (errno)
286 {
287 case EWOULDBLOCK:
288 SetLastError(ERROR_NO_DATA);
289 break;
290
291 default:
292 SetLastError(ERROR_BROKEN_PIPE);
293 break;
294 }
295 }
296
297 if (lpNumberOfBytesRead)
298 *lpNumberOfBytesRead = (DWORD)io_status;
299 }
300 else
301 {
302 /* Overlapped I/O */
303 if (!lpOverlapped)
304 {
305 WLog_ERR(TAG, "requires lpOverlapped != nullptr as FILE_FLAG_OVERLAPPED is set");
306 SetLastError(ERROR_NOT_SUPPORTED);
307 return FALSE;
308 }
309
310 if (pipe->clientfd == -1)
311 return FALSE;
312
313 pipe->lpOverlapped = lpOverlapped;
314#ifdef WINPR_HAVE_SYS_AIO_H
315 {
316 int aio_status;
317 struct aiocb cb = WINPR_C_ARRAY_INIT;
318
319 cb.aio_fildes = pipe->clientfd;
320 cb.aio_buf = lpBuffer;
321 cb.aio_nbytes = nNumberOfBytesToRead;
322 cb.aio_offset = lpOverlapped->Offset;
323 cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
324 cb.aio_sigevent.sigev_signo = SIGIO;
325 cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped;
326 InstallAioSignalHandler();
327 aio_status = aio_read(&cb);
328 WLog_DBG(TAG, "aio_read status: %d", aio_status);
329
330 if (aio_status < 0)
331 status = FALSE;
332
333 return status;
334 }
335#else
336 /* synchronous behavior */
337 lpOverlapped->Internal = 0;
338 lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToRead;
339 lpOverlapped->DUMMYUNIONNAME.Pointer = (PVOID)lpBuffer;
340 (void)SetEvent(lpOverlapped->hEvent);
341#endif
342 }
343
344 return status;
345}
346
347BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
348 LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
349{
350 SSIZE_T io_status = 0;
351 WINPR_NAMED_PIPE* pipe = nullptr;
352 BOOL status = TRUE;
353
354 if (lpOverlapped)
355 {
356 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
357 SetLastError(ERROR_NOT_SUPPORTED);
358 return FALSE;
359 }
360
361 pipe = (WINPR_NAMED_PIPE*)Object;
362
363 if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
364 {
365 if (pipe->clientfd == -1)
366 return FALSE;
367
368 do
369 {
370 io_status = write(pipe->clientfd, lpBuffer, nNumberOfBytesToWrite);
371 } while ((io_status < 0) && (errno == EINTR));
372
373 if (io_status < 0)
374 {
375 *lpNumberOfBytesWritten = 0;
376
377 switch (errno)
378 {
379 case EWOULDBLOCK:
380 io_status = 0;
381 status = TRUE;
382 break;
383
384 default:
385 status = FALSE;
386 }
387 }
388
389 *lpNumberOfBytesWritten = (DWORD)io_status;
390 return status;
391 }
392 else
393 {
394 /* Overlapped I/O */
395 if (!lpOverlapped)
396 return FALSE;
397
398 if (pipe->clientfd == -1)
399 return FALSE;
400
401 pipe->lpOverlapped = lpOverlapped;
402#ifdef WINPR_HAVE_SYS_AIO_H
403 {
404 struct aiocb cb = WINPR_C_ARRAY_INIT;
405
406 cb.aio_fildes = pipe->clientfd;
407 cb.aio_buf = (void*)lpBuffer;
408 cb.aio_nbytes = nNumberOfBytesToWrite;
409 cb.aio_offset = lpOverlapped->Offset;
410 cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
411 cb.aio_sigevent.sigev_signo = SIGIO;
412 cb.aio_sigevent.sigev_value.sival_ptr = (void*)lpOverlapped;
413 InstallAioSignalHandler();
414 io_status = aio_write(&cb);
415 WLog_DBG("aio_write status: %" PRIdz, io_status);
416
417 if (io_status < 0)
418 status = FALSE;
419
420 return status;
421 }
422#else
423 /* synchronous behavior */
424 lpOverlapped->Internal = 1;
425 lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToWrite;
426 {
427 union
428 {
429 LPCVOID cpv;
430 PVOID pv;
431 } cnv;
432 cnv.cpv = lpBuffer;
433 lpOverlapped->DUMMYUNIONNAME.Pointer = cnv.pv;
434 }
435 (void)SetEvent(lpOverlapped->hEvent);
436#endif
437 }
438
439 return TRUE;
440}
441
442static HANDLE_OPS namedOps = { NamedPipeIsHandled,
443 NamedPipeCloseHandle,
444 NamedPipeGetFd,
445 nullptr, /* CleanupHandle */
446 NamedPipeRead,
447 nullptr,
448 nullptr,
449 NamedPipeWrite,
450 nullptr,
451 nullptr,
452 nullptr,
453 nullptr,
454 nullptr,
455 nullptr,
456 nullptr,
457 nullptr,
458 nullptr,
459 nullptr,
460 nullptr,
461 nullptr,
462 nullptr };
463
464static BOOL InitWinPRPipeModule(void)
465{
466 if (g_NamedPipeServerSockets)
467 return TRUE;
468
469 g_NamedPipeServerSockets = ArrayList_New(FALSE);
470 return g_NamedPipeServerSockets != nullptr;
471}
472
473/*
474 * Unnamed pipe
475 */
476BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes,
477 DWORD nSize)
478{
479 int pipe_fd[] = { -1, -1 };
480 WINPR_PIPE* pReadPipe = nullptr;
481 WINPR_PIPE* pWritePipe = nullptr;
482 const BOOL inherit = lpPipeAttributes && lpPipeAttributes->bInheritHandle;
483
484 WINPR_UNUSED(nSize);
485
486 /* match Windows semantics: handles are not inherited by a child process unless the creator
487 * asks for it via bInheritHandle. Prefer the atomic pipe2(O_CLOEXEC) where available so
488 * there's no window between pipe creation and marking it close-on-exec during which a
489 * concurrent fork() elsewhere in the process could leak the fd into an unrelated child. */
490#ifdef WINPR_HAVE_PIPE2
491 const int pipe2flags = inherit ? 0 : O_CLOEXEC;
492 if (pipe2(pipe_fd, pipe2flags) < 0)
493#else
494 if (pipe(pipe_fd) < 0 || !winpr_set_cloexec(pipe_fd[0], !inherit) ||
495 !winpr_set_cloexec(pipe_fd[1], !inherit))
496#endif
497 {
498 WLog_ERR(TAG, "failed to create and set cloexec pipe");
499 goto fail_close_fd;
500 }
501
502 pReadPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
503 pWritePipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
504
505 if (!pReadPipe || !pWritePipe)
506 {
507 WLog_ERR(TAG, "error allocating pipes");
508 goto fail_free_pipes;
509 }
510
511 pReadPipe->fd = pipe_fd[0];
512 pWritePipe->fd = pipe_fd[1];
513 WINPR_HANDLE_SET_TYPE_AND_MODE(pReadPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
514 pReadPipe->common.ops = &ops;
515 *((ULONG_PTR*)hReadPipe) = (ULONG_PTR)pReadPipe;
516 WINPR_HANDLE_SET_TYPE_AND_MODE(pWritePipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
517 pWritePipe->common.ops = &ops;
518 *((ULONG_PTR*)hWritePipe) = (ULONG_PTR)pWritePipe;
519 return TRUE;
520
521fail_free_pipes:
522 free(pReadPipe);
523 free(pWritePipe);
524fail_close_fd:
525 if (pipe_fd[0] >= 0)
526 close(pipe_fd[0]);
527 if (pipe_fd[1] >= 0)
528 close(pipe_fd[1]);
529 return FALSE;
530}
531
532/* wraps an already-open fd (typically inherited across exec() from a parent process, e.g. via
533 * winpr_importHandleFromString()) into a WINPR HANDLE with the same blocking read()/write()
534 * behavior CreatePipe() hands out - the fd does not actually have to be a pipe, anything
535 * read()/write()-able works (pipe, regular file, socket). */
536HANDLE winpr_Pipe_FromFd(int fd)
537{
538 if (fd < 0)
539 return INVALID_HANDLE_VALUE;
540
541 WINPR_PIPE* pPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
542 if (!pPipe)
543 {
544 WLog_ERR(TAG, "error allocating pipe");
545 return INVALID_HANDLE_VALUE;
546 }
547
548 pPipe->fd = fd;
549 WINPR_HANDLE_SET_TYPE_AND_MODE(pPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
550 pPipe->common.ops = &ops;
551 return &pPipe->common;
552}
553
558static void winpr_unref_named_pipe(WINPR_NAMED_PIPE* pNamedPipe)
559{
560 NamedPipeServerSocketEntry* baseSocket = nullptr;
561
562 if (!pNamedPipe)
563 return;
564
565 WINPR_ASSERT(pNamedPipe->name);
566 WINPR_ASSERT(g_NamedPipeServerSockets);
567 // WLog_VRB(TAG, "%p (%s)", (void*) pNamedPipe, pNamedPipe->name);
568 ArrayList_Lock(g_NamedPipeServerSockets);
569
570 for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
571 {
572 baseSocket =
573 (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
574 WINPR_ASSERT(baseSocket->name);
575
576 if (!strcmp(baseSocket->name, pNamedPipe->name))
577 {
578 WINPR_ASSERT(baseSocket->references > 0);
579 WINPR_ASSERT(baseSocket->serverfd != -1);
580
581 if (--baseSocket->references == 0)
582 {
583 // WLog_DBG(TAG, "removing shared server socked resource");
584 // WLog_DBG(TAG, "closing shared serverfd %d", baseSocket->serverfd);
585 ArrayList_Remove(g_NamedPipeServerSockets, baseSocket);
586 close(baseSocket->serverfd);
587 free(baseSocket->name);
588 free(baseSocket);
589 }
590
591 break;
592 }
593 }
594
595 ArrayList_Unlock(g_NamedPipeServerSockets);
596}
597
598HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
599 DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
600 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
601{
602 char* lpPipePath = nullptr;
603 WINPR_NAMED_PIPE* pNamedPipe = nullptr;
604 int serverfd = -1;
605 NamedPipeServerSocketEntry* baseSocket = nullptr;
606
607 WINPR_UNUSED(lpSecurityAttributes);
608
609 if (dwOpenMode & FILE_FLAG_OVERLAPPED)
610 {
611 WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
612 SetLastError(ERROR_NOT_SUPPORTED);
613 return INVALID_HANDLE_VALUE;
614 }
615
616 if (!lpName)
617 return INVALID_HANDLE_VALUE;
618
619 if (!InitWinPRPipeModule())
620 return INVALID_HANDLE_VALUE;
621
622 pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE));
623
624 if (!pNamedPipe)
625 return INVALID_HANDLE_VALUE;
626
627 ArrayList_Lock(g_NamedPipeServerSockets);
628 WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ);
629 pNamedPipe->serverfd = -1;
630 pNamedPipe->clientfd = -1;
631
632 if (!(pNamedPipe->name = _strdup(lpName)))
633 goto out;
634
635 if (!(pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName)))
636 goto out;
637
638 if (!(pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName)))
639 goto out;
640
641 pNamedPipe->dwOpenMode = dwOpenMode;
642 pNamedPipe->dwPipeMode = dwPipeMode;
643 pNamedPipe->nMaxInstances = nMaxInstances;
644 pNamedPipe->nOutBufferSize = nOutBufferSize;
645 pNamedPipe->nInBufferSize = nInBufferSize;
646 pNamedPipe->nDefaultTimeOut = nDefaultTimeOut;
647 pNamedPipe->dwFlagsAndAttributes = dwOpenMode;
648 pNamedPipe->clientfd = -1;
649 pNamedPipe->ServerMode = TRUE;
650 pNamedPipe->common.ops = &namedOps;
651
652 for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
653 {
654 baseSocket =
655 (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
656
657 if (!strcmp(baseSocket->name, lpName))
658 {
659 serverfd = baseSocket->serverfd;
660 // WLog_DBG(TAG, "using shared socked resource for pipe %p (%s)", (void*) pNamedPipe,
661 // lpName);
662 break;
663 }
664 }
665
666 /* If this is the first instance of the named pipe... */
667 if (serverfd == -1)
668 {
669 struct sockaddr_un s = WINPR_C_ARRAY_INIT;
670 /* Create the UNIX domain socket and start listening. */
671 if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA()))
672 goto out;
673
674 if (!winpr_PathFileExists(lpPipePath))
675 {
676 if (!CreateDirectoryA(lpPipePath, nullptr))
677 {
678 free(lpPipePath);
679 goto out;
680 }
681
682 UnixChangeFileMode(lpPipePath, 0xFFFF);
683 }
684
685 free(lpPipePath);
686
687 if (winpr_PathFileExists(pNamedPipe->lpFilePath))
688 winpr_DeleteFile(pNamedPipe->lpFilePath);
689
690 if ((serverfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
691 {
692 char ebuffer[256] = WINPR_C_ARRAY_INIT;
693 WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s",
694 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
695 goto out;
696 }
697
698 /* this is the shared listening socket kept in g_NamedPipeServerSockets, never handed out
699 * as a HANDLE itself (each CreateNamedPipeA call gets its own close-on-exec duplicate
700 * below) - still, it's a real fd in this process, so keep it from leaking into children */
701 if (!winpr_set_cloexec(serverfd, TRUE))
702 {
703 WLog_ERR(TAG, "CreateNamedPipeA: failed to set close-on-exec on listening socket");
704 goto out;
705 }
706
707 s.sun_family = AF_UNIX;
708 (void)sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath);
709
710 if (bind(serverfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)) == -1)
711 {
712 char ebuffer[256] = WINPR_C_ARRAY_INIT;
713 WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s",
714 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
715 goto out;
716 }
717
718 if (listen(serverfd, 2) == -1)
719 {
720 char ebuffer[256] = WINPR_C_ARRAY_INIT;
721 WLog_ERR(TAG, "CreateNamedPipeA: listen error, %s",
722 winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
723 goto out;
724 }
725
726 UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF);
727
728 if (!(baseSocket = (NamedPipeServerSocketEntry*)malloc(sizeof(NamedPipeServerSocketEntry))))
729 goto out;
730
731 if (!(baseSocket->name = _strdup(lpName)))
732 {
733 free(baseSocket);
734 goto out;
735 }
736
737 baseSocket->serverfd = serverfd;
738 baseSocket->references = 0;
739
740 if (!ArrayList_Append(g_NamedPipeServerSockets, baseSocket))
741 {
742 free(baseSocket->name);
743 free(baseSocket);
744 goto out;
745 }
746
747 // WLog_DBG(TAG, "created shared socked resource for pipe %p (%s). base serverfd = %d",
748 // (void*) pNamedPipe, lpName, serverfd);
749 }
750
751 /* F_DUPFD_CLOEXEC rather than plain dup(): dup() always clears close-on-exec on the new fd
752 * regardless of the source's own flags, so a plain dup() here would silently undo the
753 * close-on-exec set on the shared listening socket above. */
754 pNamedPipe->serverfd = fcntl(baseSocket->serverfd, F_DUPFD_CLOEXEC, 0);
755 // WLog_DBG(TAG, "using serverfd %d (duplicated from %d)", pNamedPipe->serverfd,
756 // baseSocket->serverfd);
757 pNamedPipe->pfnUnrefNamedPipe = winpr_unref_named_pipe;
758 baseSocket->references++;
759
760 if (dwOpenMode & FILE_FLAG_OVERLAPPED)
761 {
762 // TODO: Implement
763 WLog_ERR(TAG, "TODO: implement this");
764 }
765
766 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of baseSocket
767 ArrayList_Unlock(g_NamedPipeServerSockets);
768 return pNamedPipe;
769out:
770 NamedPipeCloseHandle(pNamedPipe);
771 free(pNamedPipe);
772
773 if (serverfd != -1)
774 close(serverfd);
775
776 ArrayList_Unlock(g_NamedPipeServerSockets);
777 return INVALID_HANDLE_VALUE;
778}
779
780HANDLE CreateNamedPipeW(WINPR_ATTR_UNUSED LPCWSTR lpName, WINPR_ATTR_UNUSED DWORD dwOpenMode,
781 WINPR_ATTR_UNUSED DWORD dwPipeMode, WINPR_ATTR_UNUSED DWORD nMaxInstances,
782 WINPR_ATTR_UNUSED DWORD nOutBufferSize,
783 WINPR_ATTR_UNUSED DWORD nInBufferSize,
784 WINPR_ATTR_UNUSED DWORD nDefaultTimeOut,
785 WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
786{
787 WLog_ERR(TAG, "is not implemented");
788 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
789 return nullptr;
790}
791
792BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped)
793{
794 int status = 0;
795 socklen_t length = 0;
796 WINPR_NAMED_PIPE* pNamedPipe = nullptr;
797
798 if (lpOverlapped)
799 {
800 WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
801 SetLastError(ERROR_NOT_SUPPORTED);
802 return FALSE;
803 }
804
805 if (!hNamedPipe)
806 return FALSE;
807
808 pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
809
810 if (!(pNamedPipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
811 {
812 struct sockaddr_un s = WINPR_C_ARRAY_INIT;
813 length = sizeof(struct sockaddr_un);
814#ifdef WINPR_HAVE_ACCEPT4
815 status = accept4(pNamedPipe->serverfd, (struct sockaddr*)&s, &length, SOCK_CLOEXEC);
816#else
817 status = accept(pNamedPipe->serverfd, (struct sockaddr*)&s, &length);
818#endif
819
820 if (status < 0)
821 {
822 WLog_ERR(TAG, "ConnectNamedPipe: accept error");
823 return FALSE;
824 }
825
826#ifndef WINPR_HAVE_ACCEPT4
827 if (!winpr_set_cloexec(status, TRUE))
828 {
829 WLog_ERR(TAG, "ConnectNamedPipe: failed to set close-on-exec on accepted socket");
830 close(status);
831 return FALSE;
832 }
833#endif
834
835 pNamedPipe->clientfd = status;
836 pNamedPipe->ServerMode = FALSE;
837 }
838 else
839 {
840 if (!lpOverlapped)
841 return FALSE;
842
843 if (pNamedPipe->serverfd == -1)
844 return FALSE;
845
846 pNamedPipe->lpOverlapped = lpOverlapped;
847 /* synchronous behavior */
848 lpOverlapped->Internal = 2;
849 lpOverlapped->InternalHigh = (ULONG_PTR)0;
850 lpOverlapped->DUMMYUNIONNAME.Pointer = (PVOID) nullptr;
851 (void)SetEvent(lpOverlapped->hEvent);
852 }
853
854 return TRUE;
855}
856
857BOOL DisconnectNamedPipe(HANDLE hNamedPipe)
858{
859 WINPR_NAMED_PIPE* pNamedPipe = nullptr;
860 pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
861
862 if (pNamedPipe->clientfd != -1)
863 {
864 close(pNamedPipe->clientfd);
865 pNamedPipe->clientfd = -1;
866 }
867
868 return TRUE;
869}
870
871BOOL PeekNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpBuffer,
872 WINPR_ATTR_UNUSED DWORD nBufferSize, WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
873 WINPR_ATTR_UNUSED LPDWORD lpTotalBytesAvail,
874 WINPR_ATTR_UNUSED LPDWORD lpBytesLeftThisMessage)
875{
876 WLog_ERR(TAG, "Not implemented");
877 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
878 return FALSE;
879}
880
881BOOL TransactNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpInBuffer,
882 WINPR_ATTR_UNUSED DWORD nInBufferSize, WINPR_ATTR_UNUSED LPVOID lpOutBuffer,
883 WINPR_ATTR_UNUSED DWORD nOutBufferSize,
884 WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
885 WINPR_ATTR_UNUSED LPOVERLAPPED lpOverlapped)
886{
887 WLog_ERR(TAG, "Not implemented");
888 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
889 return FALSE;
890}
891
892BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut)
893{
894 BOOL status = 0;
895 DWORD nWaitTime = 0;
896 char* lpFilePath = nullptr;
897 DWORD dwSleepInterval = 0;
898
899 if (!lpNamedPipeName)
900 return FALSE;
901
902 lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpNamedPipeName);
903
904 if (!lpFilePath)
905 return FALSE;
906
907 if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
908 nTimeOut = 50;
909
910 nWaitTime = 0;
911 status = TRUE;
912 dwSleepInterval = 10;
913
914 while (!winpr_PathFileExists(lpFilePath))
915 {
916 Sleep(dwSleepInterval);
917 nWaitTime += dwSleepInterval;
918
919 if (nWaitTime >= nTimeOut)
920 {
921 status = FALSE;
922 break;
923 }
924 }
925
926 free(lpFilePath);
927 return status;
928}
929
930BOOL WaitNamedPipeW(WINPR_ATTR_UNUSED LPCWSTR lpNamedPipeName, WINPR_ATTR_UNUSED DWORD nTimeOut)
931{
932 WLog_ERR(TAG, "Not implemented");
933 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
934 return FALSE;
935}
936
937// NOLINTBEGIN(readability-non-const-parameter)
938BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount,
939 LPDWORD lpCollectDataTimeout)
940// NOLINTEND(readability-non-const-parameter)
941{
942 int fd = 0;
943 int flags = 0;
944 WINPR_NAMED_PIPE* pNamedPipe = nullptr;
945 pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
946
947 if (lpMode)
948 {
949 pNamedPipe->dwPipeMode = *lpMode;
950 fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd;
951
952 if (fd == -1)
953 return FALSE;
954
955 flags = fcntl(fd, F_GETFL);
956
957 if (flags < 0)
958 return FALSE;
959
960 if (pNamedPipe->dwPipeMode & PIPE_NOWAIT)
961 flags = (flags | O_NONBLOCK);
962 else
963 flags = (flags & ~(O_NONBLOCK));
964
965 if (fcntl(fd, F_SETFL, flags) < 0)
966 return FALSE;
967 }
968
969 if (lpMaxCollectionCount)
970 {
971 }
972
973 if (lpCollectDataTimeout)
974 {
975 }
976
977 return TRUE;
978}
979
980BOOL ImpersonateNamedPipeClient(WINPR_ATTR_UNUSED HANDLE hNamedPipe)
981{
982 WLog_ERR(TAG, "Not implemented");
983 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
984 return FALSE;
985}
986
987BOOL GetNamedPipeClientComputerNameA(WINPR_ATTR_UNUSED HANDLE Pipe,
988 WINPR_ATTR_UNUSED LPCSTR ClientComputerName,
989 WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
990{
991 WLog_ERR(TAG, "Not implemented");
992 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
993 return FALSE;
994}
995
996BOOL GetNamedPipeClientComputerNameW(WINPR_ATTR_UNUSED HANDLE Pipe,
997 WINPR_ATTR_UNUSED LPCWSTR ClientComputerName,
998 WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
999{
1000 WLog_ERR(TAG, "Not implemented");
1001 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1002 return FALSE;
1003}
1004
1005#endif