Coverage Report

Created: 2026-09-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/pipe/pipe.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Pipe Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2017 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2017 Thincast Technologies GmbH
8
 * Copyright 2026 David Fort <contact@hardening-consulting.com>
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *     http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
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
72
static wArrayList* g_NamedPipeServerSockets = nullptr;
73
74
typedef struct
75
{
76
  char* name;
77
  int serverfd;
78
  int references;
79
} NamedPipeServerSocketEntry;
80
81
static BOOL PipeIsHandled(HANDLE handle)
82
0
{
83
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_ANONYMOUS_PIPE, FALSE);
84
0
}
85
86
static int PipeGetFd(HANDLE handle)
87
0
{
88
0
  WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
89
90
0
  if (!PipeIsHandled(handle))
91
0
    return -1;
92
93
0
  return pipe->fd;
94
0
}
95
96
static BOOL PipeCloseHandle(HANDLE handle)
97
0
{
98
0
  WINPR_PIPE* pipe = (WINPR_PIPE*)handle;
99
100
0
  if (!PipeIsHandled(handle))
101
0
    return FALSE;
102
103
0
  if (pipe->fd != -1)
104
0
  {
105
0
    close(pipe->fd);
106
0
    pipe->fd = -1;
107
0
  }
108
109
0
  return TRUE;
110
0
}
111
112
static BOOL PipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
113
                     LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
114
0
{
115
0
  SSIZE_T io_status = 0;
116
0
  WINPR_PIPE* pipe = nullptr;
117
0
  BOOL status = TRUE;
118
119
0
  if (lpOverlapped)
120
0
  {
121
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
122
0
    SetLastError(ERROR_NOT_SUPPORTED);
123
0
    return FALSE;
124
0
  }
125
126
0
  pipe = (WINPR_PIPE*)Object;
127
128
0
  do
129
0
  {
130
0
    io_status = read(pipe->fd, lpBuffer, nNumberOfBytesToRead);
131
0
  } while ((io_status < 0) && (errno == EINTR));
132
133
0
  if (io_status < 0)
134
0
  {
135
0
    status = FALSE;
136
137
0
    switch (errno)
138
0
    {
139
0
      case EWOULDBLOCK:
140
0
        SetLastError(ERROR_NO_DATA);
141
0
        break;
142
0
      default:
143
0
        break;
144
0
    }
145
0
  }
146
147
0
  if (lpNumberOfBytesRead)
148
0
    *lpNumberOfBytesRead = (DWORD)io_status;
149
150
0
  return status;
151
0
}
152
153
static BOOL PipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
154
                      LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
155
0
{
156
0
  SSIZE_T io_status = 0;
157
0
  WINPR_PIPE* pipe = nullptr;
158
159
0
  if (lpOverlapped)
160
0
  {
161
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
162
0
    SetLastError(ERROR_NOT_SUPPORTED);
163
0
    return FALSE;
164
0
  }
165
166
0
  pipe = (WINPR_PIPE*)Object;
167
168
0
  do
169
0
  {
170
0
    io_status = write(pipe->fd, lpBuffer, nNumberOfBytesToWrite);
171
0
  } while ((io_status < 0) && (errno == EINTR));
172
173
0
  if ((io_status < 0) && (errno == EWOULDBLOCK))
174
0
    io_status = 0;
175
176
0
  *lpNumberOfBytesWritten = (DWORD)io_status;
177
0
  return TRUE;
178
0
}
179
180
static 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
203
static BOOL NamedPipeIsHandled(HANDLE handle)
204
0
{
205
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_NAMED_PIPE, TRUE);
206
0
}
207
208
static int NamedPipeGetFd(HANDLE handle)
209
0
{
210
0
  WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)handle;
211
212
0
  if (!NamedPipeIsHandled(handle))
213
0
    return -1;
214
215
0
  if (pipe->ServerMode)
216
0
    return pipe->serverfd;
217
218
0
  return pipe->clientfd;
219
0
}
220
221
static BOOL NamedPipeCloseHandle(HANDLE handle)
222
0
{
223
0
  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
0
#ifndef __clang_analyzer__
229
0
  if (!NamedPipeIsHandled(handle))
230
0
    return FALSE;
231
0
#endif
232
233
0
  if (pNamedPipe->pfnUnrefNamedPipe)
234
0
    pNamedPipe->pfnUnrefNamedPipe(pNamedPipe);
235
236
0
  free(pNamedPipe->name);
237
0
  pNamedPipe->name = nullptr;
238
0
  free(pNamedPipe->lpFileName);
239
0
  pNamedPipe->lpFileName = nullptr;
240
0
  free(pNamedPipe->lpFilePath);
241
0
  pNamedPipe->lpFilePath = nullptr;
242
243
0
  if (pNamedPipe->serverfd != -1)
244
0
  {
245
0
    close(pNamedPipe->serverfd);
246
0
    pNamedPipe->serverfd = -1;
247
0
  }
248
249
0
  if (pNamedPipe->clientfd != -1)
250
0
  {
251
0
    close(pNamedPipe->clientfd);
252
0
    pNamedPipe->clientfd = -1;
253
0
  }
254
255
0
  return TRUE;
256
0
}
257
258
BOOL NamedPipeRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
259
                   LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
260
0
{
261
0
  SSIZE_T io_status = 0;
262
0
  BOOL status = TRUE;
263
264
0
  WINPR_NAMED_PIPE* pipe = (WINPR_NAMED_PIPE*)Object;
265
266
0
  if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
267
0
  {
268
0
    if (pipe->clientfd == -1)
269
0
      return FALSE;
270
271
0
    do
272
0
    {
273
0
      io_status = read(pipe->clientfd, lpBuffer, nNumberOfBytesToRead);
274
0
    } while ((io_status < 0) && (errno == EINTR));
275
276
0
    if (io_status == 0)
277
0
    {
278
0
      SetLastError(ERROR_BROKEN_PIPE);
279
0
      status = FALSE;
280
0
    }
281
0
    else if (io_status < 0)
282
0
    {
283
0
      status = FALSE;
284
285
0
      switch (errno)
286
0
      {
287
0
        case EWOULDBLOCK:
288
0
          SetLastError(ERROR_NO_DATA);
289
0
          break;
290
291
0
        default:
292
0
          SetLastError(ERROR_BROKEN_PIPE);
293
0
          break;
294
0
      }
295
0
    }
296
297
0
    if (lpNumberOfBytesRead)
298
0
      *lpNumberOfBytesRead = (DWORD)io_status;
299
0
  }
300
0
  else
301
0
  {
302
    /* Overlapped I/O */
303
0
    if (!lpOverlapped)
304
0
    {
305
0
      WLog_ERR(TAG, "requires lpOverlapped != nullptr as FILE_FLAG_OVERLAPPED is set");
306
0
      SetLastError(ERROR_NOT_SUPPORTED);
307
0
      return FALSE;
308
0
    }
309
310
0
    if (pipe->clientfd == -1)
311
0
      return FALSE;
312
313
0
    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
0
    lpOverlapped->Internal = 0;
338
0
    lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToRead;
339
0
    lpOverlapped->DUMMYUNIONNAME.Pointer = (PVOID)lpBuffer;
340
0
    (void)SetEvent(lpOverlapped->hEvent);
341
0
#endif
342
0
  }
343
344
0
  return status;
345
0
}
346
347
BOOL NamedPipeWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
348
                    LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
349
0
{
350
0
  SSIZE_T io_status = 0;
351
0
  WINPR_NAMED_PIPE* pipe = nullptr;
352
0
  BOOL status = TRUE;
353
354
0
  if (lpOverlapped)
355
0
  {
356
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
357
0
    SetLastError(ERROR_NOT_SUPPORTED);
358
0
    return FALSE;
359
0
  }
360
361
0
  pipe = (WINPR_NAMED_PIPE*)Object;
362
363
0
  if (!(pipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
364
0
  {
365
0
    if (pipe->clientfd == -1)
366
0
      return FALSE;
367
368
0
    do
369
0
    {
370
0
      io_status = write(pipe->clientfd, lpBuffer, nNumberOfBytesToWrite);
371
0
    } while ((io_status < 0) && (errno == EINTR));
372
373
0
    if (io_status < 0)
374
0
    {
375
0
      *lpNumberOfBytesWritten = 0;
376
377
0
      switch (errno)
378
0
      {
379
0
        case EWOULDBLOCK:
380
0
          io_status = 0;
381
0
          status = TRUE;
382
0
          break;
383
384
0
        default:
385
0
          status = FALSE;
386
0
      }
387
0
    }
388
389
0
    *lpNumberOfBytesWritten = (DWORD)io_status;
390
0
    return status;
391
0
  }
392
0
  else
393
0
  {
394
    /* Overlapped I/O */
395
0
    if (!lpOverlapped)
396
0
      return FALSE;
397
398
0
    if (pipe->clientfd == -1)
399
0
      return FALSE;
400
401
0
    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
0
    lpOverlapped->Internal = 1;
425
0
    lpOverlapped->InternalHigh = (ULONG_PTR)nNumberOfBytesToWrite;
426
0
    {
427
0
      union
428
0
      {
429
0
        LPCVOID cpv;
430
0
        PVOID pv;
431
0
      } cnv;
432
0
      cnv.cpv = lpBuffer;
433
0
      lpOverlapped->DUMMYUNIONNAME.Pointer = cnv.pv;
434
0
    }
435
0
    (void)SetEvent(lpOverlapped->hEvent);
436
0
#endif
437
0
  }
438
439
0
  return TRUE;
440
0
}
441
442
static 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
464
static BOOL InitWinPRPipeModule(void)
465
0
{
466
0
  if (g_NamedPipeServerSockets)
467
0
    return TRUE;
468
469
0
  g_NamedPipeServerSockets = ArrayList_New(FALSE);
470
0
  return g_NamedPipeServerSockets != nullptr;
471
0
}
472
473
/*
474
 * Unnamed pipe
475
 */
476
BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes,
477
                DWORD nSize)
478
0
{
479
0
  int pipe_fd[] = { -1, -1 };
480
0
  WINPR_PIPE* pReadPipe = nullptr;
481
0
  WINPR_PIPE* pWritePipe = nullptr;
482
0
  const BOOL inherit = lpPipeAttributes && lpPipeAttributes->bInheritHandle;
483
484
0
  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
0
#ifdef WINPR_HAVE_PIPE2
491
0
  const int pipe2flags = inherit ? 0 : O_CLOEXEC;
492
0
  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
0
  {
498
0
    WLog_ERR(TAG, "failed to create and set cloexec pipe");
499
0
    goto fail_close_fd;
500
0
  }
501
502
0
  pReadPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
503
0
  pWritePipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
504
505
0
  if (!pReadPipe || !pWritePipe)
506
0
  {
507
0
    WLog_ERR(TAG, "error allocating pipes");
508
0
    goto fail_free_pipes;
509
0
  }
510
511
0
  pReadPipe->fd = pipe_fd[0];
512
0
  pWritePipe->fd = pipe_fd[1];
513
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pReadPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
514
0
  pReadPipe->common.ops = &ops;
515
0
  *((ULONG_PTR*)hReadPipe) = (ULONG_PTR)pReadPipe;
516
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pWritePipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
517
0
  pWritePipe->common.ops = &ops;
518
0
  *((ULONG_PTR*)hWritePipe) = (ULONG_PTR)pWritePipe;
519
0
  return TRUE;
520
521
0
fail_free_pipes:
522
0
  free(pReadPipe);
523
0
  free(pWritePipe);
524
0
fail_close_fd:
525
0
  if (pipe_fd[0] >= 0)
526
0
    close(pipe_fd[0]);
527
0
  if (pipe_fd[1] >= 0)
528
0
    close(pipe_fd[1]);
529
0
  return FALSE;
530
0
}
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). */
536
HANDLE winpr_Pipe_FromFd(int fd)
537
0
{
538
0
  if (fd < 0)
539
0
    return INVALID_HANDLE_VALUE;
540
541
0
  WINPR_PIPE* pPipe = (WINPR_PIPE*)calloc(1, sizeof(WINPR_PIPE));
542
0
  if (!pPipe)
543
0
  {
544
0
    WLog_ERR(TAG, "error allocating pipe");
545
0
    return INVALID_HANDLE_VALUE;
546
0
  }
547
548
0
  pPipe->fd = fd;
549
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pPipe, HANDLE_TYPE_ANONYMOUS_PIPE, WINPR_FD_READ);
550
0
  pPipe->common.ops = &ops;
551
0
  return &pPipe->common;
552
0
}
553
554
/**
555
 * Named pipe
556
 */
557
558
static void winpr_unref_named_pipe(WINPR_NAMED_PIPE* pNamedPipe)
559
0
{
560
0
  NamedPipeServerSocketEntry* baseSocket = nullptr;
561
562
0
  if (!pNamedPipe)
563
0
    return;
564
565
0
  WINPR_ASSERT(pNamedPipe->name);
566
0
  WINPR_ASSERT(g_NamedPipeServerSockets);
567
  // WLog_VRB(TAG, "%p (%s)", (void*) pNamedPipe, pNamedPipe->name);
568
0
  ArrayList_Lock(g_NamedPipeServerSockets);
569
570
0
  for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
571
0
  {
572
0
    baseSocket =
573
0
        (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
574
0
    WINPR_ASSERT(baseSocket->name);
575
576
0
    if (!strcmp(baseSocket->name, pNamedPipe->name))
577
0
    {
578
0
      WINPR_ASSERT(baseSocket->references > 0);
579
0
      WINPR_ASSERT(baseSocket->serverfd != -1);
580
581
0
      if (--baseSocket->references == 0)
582
0
      {
583
        // WLog_DBG(TAG, "removing shared server socked resource");
584
        // WLog_DBG(TAG, "closing shared serverfd %d", baseSocket->serverfd);
585
0
        ArrayList_Remove(g_NamedPipeServerSockets, baseSocket);
586
0
        close(baseSocket->serverfd);
587
0
        free(baseSocket->name);
588
0
        free(baseSocket);
589
0
      }
590
591
0
      break;
592
0
    }
593
0
  }
594
595
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
596
0
}
597
598
HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
599
                        DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
600
                        LPSECURITY_ATTRIBUTES lpSecurityAttributes)
601
0
{
602
0
  char* lpPipePath = nullptr;
603
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
604
0
  int serverfd = -1;
605
0
  NamedPipeServerSocketEntry* baseSocket = nullptr;
606
607
0
  WINPR_UNUSED(lpSecurityAttributes);
608
609
0
  if (dwOpenMode & FILE_FLAG_OVERLAPPED)
610
0
  {
611
0
    WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
612
0
    SetLastError(ERROR_NOT_SUPPORTED);
613
0
    return INVALID_HANDLE_VALUE;
614
0
  }
615
616
0
  if (!lpName)
617
0
    return INVALID_HANDLE_VALUE;
618
619
0
  if (!InitWinPRPipeModule())
620
0
    return INVALID_HANDLE_VALUE;
621
622
0
  pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE));
623
624
0
  if (!pNamedPipe)
625
0
    return INVALID_HANDLE_VALUE;
626
627
0
  ArrayList_Lock(g_NamedPipeServerSockets);
628
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ);
629
0
  pNamedPipe->serverfd = -1;
630
0
  pNamedPipe->clientfd = -1;
631
632
0
  if (!(pNamedPipe->name = _strdup(lpName)))
633
0
    goto out;
634
635
0
  if (!(pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName)))
636
0
    goto out;
637
638
0
  if (!(pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName)))
639
0
    goto out;
640
641
0
  pNamedPipe->dwOpenMode = dwOpenMode;
642
0
  pNamedPipe->dwPipeMode = dwPipeMode;
643
0
  pNamedPipe->nMaxInstances = nMaxInstances;
644
0
  pNamedPipe->nOutBufferSize = nOutBufferSize;
645
0
  pNamedPipe->nInBufferSize = nInBufferSize;
646
0
  pNamedPipe->nDefaultTimeOut = nDefaultTimeOut;
647
0
  pNamedPipe->dwFlagsAndAttributes = dwOpenMode;
648
0
  pNamedPipe->clientfd = -1;
649
0
  pNamedPipe->ServerMode = TRUE;
650
0
  pNamedPipe->common.ops = &namedOps;
651
652
0
  for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
653
0
  {
654
0
    baseSocket =
655
0
        (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
656
657
0
    if (!strcmp(baseSocket->name, lpName))
658
0
    {
659
0
      serverfd = baseSocket->serverfd;
660
      // WLog_DBG(TAG, "using shared socked resource for pipe %p (%s)", (void*) pNamedPipe,
661
      // lpName);
662
0
      break;
663
0
    }
664
0
  }
665
666
  /* If this is the first instance of the named pipe... */
667
0
  if (serverfd == -1)
668
0
  {
669
0
    struct sockaddr_un s = WINPR_C_ARRAY_INIT;
670
    /* Create the UNIX domain socket and start listening. */
671
0
    if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA()))
672
0
      goto out;
673
674
0
    if (!winpr_PathFileExists(lpPipePath))
675
0
    {
676
0
      if (!CreateDirectoryA(lpPipePath, nullptr))
677
0
      {
678
0
        free(lpPipePath);
679
0
        goto out;
680
0
      }
681
682
0
      UnixChangeFileMode(lpPipePath, 0xFFFF);
683
0
    }
684
685
0
    free(lpPipePath);
686
687
0
    if (winpr_PathFileExists(pNamedPipe->lpFilePath))
688
0
      winpr_DeleteFile(pNamedPipe->lpFilePath);
689
690
0
    if ((serverfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
691
0
    {
692
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
693
0
      WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s",
694
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
695
0
      goto out;
696
0
    }
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
0
    if (!winpr_set_cloexec(serverfd, TRUE))
702
0
    {
703
0
      WLog_ERR(TAG, "CreateNamedPipeA: failed to set close-on-exec on listening socket");
704
0
      goto out;
705
0
    }
706
707
0
    s.sun_family = AF_UNIX;
708
0
    (void)sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath);
709
710
0
    if (bind(serverfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)) == -1)
711
0
    {
712
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
713
0
      WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s",
714
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
715
0
      goto out;
716
0
    }
717
718
0
    if (listen(serverfd, 2) == -1)
719
0
    {
720
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
721
0
      WLog_ERR(TAG, "CreateNamedPipeA: listen error, %s",
722
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
723
0
      goto out;
724
0
    }
725
726
0
    UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF);
727
728
0
    if (!(baseSocket = (NamedPipeServerSocketEntry*)malloc(sizeof(NamedPipeServerSocketEntry))))
729
0
      goto out;
730
731
0
    if (!(baseSocket->name = _strdup(lpName)))
732
0
    {
733
0
      free(baseSocket);
734
0
      goto out;
735
0
    }
736
737
0
    baseSocket->serverfd = serverfd;
738
0
    baseSocket->references = 0;
739
740
0
    if (!ArrayList_Append(g_NamedPipeServerSockets, baseSocket))
741
0
    {
742
0
      free(baseSocket->name);
743
0
      free(baseSocket);
744
0
      goto out;
745
0
    }
746
747
    // WLog_DBG(TAG, "created shared socked resource for pipe %p (%s). base serverfd = %d",
748
    // (void*) pNamedPipe, lpName, serverfd);
749
0
  }
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
0
  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
0
  pNamedPipe->pfnUnrefNamedPipe = winpr_unref_named_pipe;
758
0
  baseSocket->references++;
759
760
0
  if (dwOpenMode & FILE_FLAG_OVERLAPPED)
761
0
  {
762
    // TODO: Implement
763
0
    WLog_ERR(TAG, "TODO: implement this");
764
0
  }
765
766
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of baseSocket
767
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
768
0
  return pNamedPipe;
769
0
out:
770
0
  NamedPipeCloseHandle(pNamedPipe);
771
0
  free(pNamedPipe);
772
773
0
  if (serverfd != -1)
774
0
    close(serverfd);
775
776
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
777
0
  return INVALID_HANDLE_VALUE;
778
0
}
779
780
HANDLE 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
0
{
787
0
  WLog_ERR(TAG, "is not implemented");
788
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
789
0
  return nullptr;
790
0
}
791
792
BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped)
793
0
{
794
0
  int status = 0;
795
0
  socklen_t length = 0;
796
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
797
798
0
  if (lpOverlapped)
799
0
  {
800
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
801
0
    SetLastError(ERROR_NOT_SUPPORTED);
802
0
    return FALSE;
803
0
  }
804
805
0
  if (!hNamedPipe)
806
0
    return FALSE;
807
808
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
809
810
0
  if (!(pNamedPipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
811
0
  {
812
0
    struct sockaddr_un s = WINPR_C_ARRAY_INIT;
813
0
    length = sizeof(struct sockaddr_un);
814
0
#ifdef WINPR_HAVE_ACCEPT4
815
0
    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
0
    if (status < 0)
821
0
    {
822
0
      WLog_ERR(TAG, "ConnectNamedPipe: accept error");
823
0
      return FALSE;
824
0
    }
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
0
    pNamedPipe->clientfd = status;
836
0
    pNamedPipe->ServerMode = FALSE;
837
0
  }
838
0
  else
839
0
  {
840
0
    if (!lpOverlapped)
841
0
      return FALSE;
842
843
0
    if (pNamedPipe->serverfd == -1)
844
0
      return FALSE;
845
846
0
    pNamedPipe->lpOverlapped = lpOverlapped;
847
    /* synchronous behavior */
848
0
    lpOverlapped->Internal = 2;
849
0
    lpOverlapped->InternalHigh = (ULONG_PTR)0;
850
0
    lpOverlapped->DUMMYUNIONNAME.Pointer = (PVOID) nullptr;
851
0
    (void)SetEvent(lpOverlapped->hEvent);
852
0
  }
853
854
0
  return TRUE;
855
0
}
856
857
BOOL DisconnectNamedPipe(HANDLE hNamedPipe)
858
0
{
859
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
860
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
861
862
0
  if (pNamedPipe->clientfd != -1)
863
0
  {
864
0
    close(pNamedPipe->clientfd);
865
0
    pNamedPipe->clientfd = -1;
866
0
  }
867
868
0
  return TRUE;
869
0
}
870
871
BOOL 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
0
{
876
0
  WLog_ERR(TAG, "Not implemented");
877
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
878
0
  return FALSE;
879
0
}
880
881
BOOL 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
0
{
887
0
  WLog_ERR(TAG, "Not implemented");
888
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
889
0
  return FALSE;
890
0
}
891
892
BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut)
893
0
{
894
0
  BOOL status = 0;
895
0
  DWORD nWaitTime = 0;
896
0
  char* lpFilePath = nullptr;
897
0
  DWORD dwSleepInterval = 0;
898
899
0
  if (!lpNamedPipeName)
900
0
    return FALSE;
901
902
0
  lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpNamedPipeName);
903
904
0
  if (!lpFilePath)
905
0
    return FALSE;
906
907
0
  if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
908
0
    nTimeOut = 50;
909
910
0
  nWaitTime = 0;
911
0
  status = TRUE;
912
0
  dwSleepInterval = 10;
913
914
0
  while (!winpr_PathFileExists(lpFilePath))
915
0
  {
916
0
    Sleep(dwSleepInterval);
917
0
    nWaitTime += dwSleepInterval;
918
919
0
    if (nWaitTime >= nTimeOut)
920
0
    {
921
0
      status = FALSE;
922
0
      break;
923
0
    }
924
0
  }
925
926
0
  free(lpFilePath);
927
0
  return status;
928
0
}
929
930
BOOL WaitNamedPipeW(WINPR_ATTR_UNUSED LPCWSTR lpNamedPipeName, WINPR_ATTR_UNUSED DWORD nTimeOut)
931
0
{
932
0
  WLog_ERR(TAG, "Not implemented");
933
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
934
0
  return FALSE;
935
0
}
936
937
// NOLINTBEGIN(readability-non-const-parameter)
938
BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount,
939
                             LPDWORD lpCollectDataTimeout)
940
// NOLINTEND(readability-non-const-parameter)
941
0
{
942
0
  int fd = 0;
943
0
  int flags = 0;
944
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
945
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
946
947
0
  if (lpMode)
948
0
  {
949
0
    pNamedPipe->dwPipeMode = *lpMode;
950
0
    fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd;
951
952
0
    if (fd == -1)
953
0
      return FALSE;
954
955
0
    flags = fcntl(fd, F_GETFL);
956
957
0
    if (flags < 0)
958
0
      return FALSE;
959
960
0
    if (pNamedPipe->dwPipeMode & PIPE_NOWAIT)
961
0
      flags = (flags | O_NONBLOCK);
962
0
    else
963
0
      flags = (flags & ~(O_NONBLOCK));
964
965
0
    if (fcntl(fd, F_SETFL, flags) < 0)
966
0
      return FALSE;
967
0
  }
968
969
0
  if (lpMaxCollectionCount)
970
0
  {
971
0
  }
972
973
0
  if (lpCollectDataTimeout)
974
0
  {
975
0
  }
976
977
0
  return TRUE;
978
0
}
979
980
BOOL ImpersonateNamedPipeClient(WINPR_ATTR_UNUSED HANDLE hNamedPipe)
981
0
{
982
0
  WLog_ERR(TAG, "Not implemented");
983
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
984
0
  return FALSE;
985
0
}
986
987
BOOL GetNamedPipeClientComputerNameA(WINPR_ATTR_UNUSED HANDLE Pipe,
988
                                     WINPR_ATTR_UNUSED LPCSTR ClientComputerName,
989
                                     WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
990
0
{
991
0
  WLog_ERR(TAG, "Not implemented");
992
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
993
0
  return FALSE;
994
0
}
995
996
BOOL GetNamedPipeClientComputerNameW(WINPR_ATTR_UNUSED HANDLE Pipe,
997
                                     WINPR_ATTR_UNUSED LPCWSTR ClientComputerName,
998
                                     WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
999
0
{
1000
0
  WLog_ERR(TAG, "Not implemented");
1001
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1002
0
  return FALSE;
1003
0
}
1004
1005
#endif