Coverage Report

Created: 2026-08-31 06:25

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
/**
533
 * Named pipe
534
 */
535
536
static void winpr_unref_named_pipe(WINPR_NAMED_PIPE* pNamedPipe)
537
0
{
538
0
  NamedPipeServerSocketEntry* baseSocket = nullptr;
539
540
0
  if (!pNamedPipe)
541
0
    return;
542
543
0
  WINPR_ASSERT(pNamedPipe->name);
544
0
  WINPR_ASSERT(g_NamedPipeServerSockets);
545
  // WLog_VRB(TAG, "%p (%s)", (void*) pNamedPipe, pNamedPipe->name);
546
0
  ArrayList_Lock(g_NamedPipeServerSockets);
547
548
0
  for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
549
0
  {
550
0
    baseSocket =
551
0
        (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
552
0
    WINPR_ASSERT(baseSocket->name);
553
554
0
    if (!strcmp(baseSocket->name, pNamedPipe->name))
555
0
    {
556
0
      WINPR_ASSERT(baseSocket->references > 0);
557
0
      WINPR_ASSERT(baseSocket->serverfd != -1);
558
559
0
      if (--baseSocket->references == 0)
560
0
      {
561
        // WLog_DBG(TAG, "removing shared server socked resource");
562
        // WLog_DBG(TAG, "closing shared serverfd %d", baseSocket->serverfd);
563
0
        ArrayList_Remove(g_NamedPipeServerSockets, baseSocket);
564
0
        close(baseSocket->serverfd);
565
0
        free(baseSocket->name);
566
0
        free(baseSocket);
567
0
      }
568
569
0
      break;
570
0
    }
571
0
  }
572
573
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
574
0
}
575
576
HANDLE CreateNamedPipeA(LPCSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances,
577
                        DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut,
578
                        LPSECURITY_ATTRIBUTES lpSecurityAttributes)
579
0
{
580
0
  char* lpPipePath = nullptr;
581
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
582
0
  int serverfd = -1;
583
0
  NamedPipeServerSocketEntry* baseSocket = nullptr;
584
585
0
  WINPR_UNUSED(lpSecurityAttributes);
586
587
0
  if (dwOpenMode & FILE_FLAG_OVERLAPPED)
588
0
  {
589
0
    WLog_ERR(TAG, "WinPR does not support the FILE_FLAG_OVERLAPPED flag");
590
0
    SetLastError(ERROR_NOT_SUPPORTED);
591
0
    return INVALID_HANDLE_VALUE;
592
0
  }
593
594
0
  if (!lpName)
595
0
    return INVALID_HANDLE_VALUE;
596
597
0
  if (!InitWinPRPipeModule())
598
0
    return INVALID_HANDLE_VALUE;
599
600
0
  pNamedPipe = (WINPR_NAMED_PIPE*)calloc(1, sizeof(WINPR_NAMED_PIPE));
601
602
0
  if (!pNamedPipe)
603
0
    return INVALID_HANDLE_VALUE;
604
605
0
  ArrayList_Lock(g_NamedPipeServerSockets);
606
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(pNamedPipe, HANDLE_TYPE_NAMED_PIPE, WINPR_FD_READ);
607
0
  pNamedPipe->serverfd = -1;
608
0
  pNamedPipe->clientfd = -1;
609
610
0
  if (!(pNamedPipe->name = _strdup(lpName)))
611
0
    goto out;
612
613
0
  if (!(pNamedPipe->lpFileName = GetNamedPipeNameWithoutPrefixA(lpName)))
614
0
    goto out;
615
616
0
  if (!(pNamedPipe->lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpName)))
617
0
    goto out;
618
619
0
  pNamedPipe->dwOpenMode = dwOpenMode;
620
0
  pNamedPipe->dwPipeMode = dwPipeMode;
621
0
  pNamedPipe->nMaxInstances = nMaxInstances;
622
0
  pNamedPipe->nOutBufferSize = nOutBufferSize;
623
0
  pNamedPipe->nInBufferSize = nInBufferSize;
624
0
  pNamedPipe->nDefaultTimeOut = nDefaultTimeOut;
625
0
  pNamedPipe->dwFlagsAndAttributes = dwOpenMode;
626
0
  pNamedPipe->clientfd = -1;
627
0
  pNamedPipe->ServerMode = TRUE;
628
0
  pNamedPipe->common.ops = &namedOps;
629
630
0
  for (size_t index = 0; index < ArrayList_Count(g_NamedPipeServerSockets); index++)
631
0
  {
632
0
    baseSocket =
633
0
        (NamedPipeServerSocketEntry*)ArrayList_GetItem(g_NamedPipeServerSockets, index);
634
635
0
    if (!strcmp(baseSocket->name, lpName))
636
0
    {
637
0
      serverfd = baseSocket->serverfd;
638
      // WLog_DBG(TAG, "using shared socked resource for pipe %p (%s)", (void*) pNamedPipe,
639
      // lpName);
640
0
      break;
641
0
    }
642
0
  }
643
644
  /* If this is the first instance of the named pipe... */
645
0
  if (serverfd == -1)
646
0
  {
647
0
    struct sockaddr_un s = WINPR_C_ARRAY_INIT;
648
    /* Create the UNIX domain socket and start listening. */
649
0
    if (!(lpPipePath = GetNamedPipeUnixDomainSocketBaseFilePathA()))
650
0
      goto out;
651
652
0
    if (!winpr_PathFileExists(lpPipePath))
653
0
    {
654
0
      if (!CreateDirectoryA(lpPipePath, nullptr))
655
0
      {
656
0
        free(lpPipePath);
657
0
        goto out;
658
0
      }
659
660
0
      UnixChangeFileMode(lpPipePath, 0xFFFF);
661
0
    }
662
663
0
    free(lpPipePath);
664
665
0
    if (winpr_PathFileExists(pNamedPipe->lpFilePath))
666
0
      winpr_DeleteFile(pNamedPipe->lpFilePath);
667
668
0
    if ((serverfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
669
0
    {
670
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
671
0
      WLog_ERR(TAG, "CreateNamedPipeA: socket error, %s",
672
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
673
0
      goto out;
674
0
    }
675
676
    /* this is the shared listening socket kept in g_NamedPipeServerSockets, never handed out
677
     * as a HANDLE itself (each CreateNamedPipeA call gets its own close-on-exec duplicate
678
     * below) - still, it's a real fd in this process, so keep it from leaking into children */
679
0
    if (!winpr_set_cloexec(serverfd, TRUE))
680
0
    {
681
0
      WLog_ERR(TAG, "CreateNamedPipeA: failed to set close-on-exec on listening socket");
682
0
      goto out;
683
0
    }
684
685
0
    s.sun_family = AF_UNIX;
686
0
    (void)sprintf_s(s.sun_path, ARRAYSIZE(s.sun_path), "%s", pNamedPipe->lpFilePath);
687
688
0
    if (bind(serverfd, (struct sockaddr*)&s, sizeof(struct sockaddr_un)) == -1)
689
0
    {
690
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
691
0
      WLog_ERR(TAG, "CreateNamedPipeA: bind error, %s",
692
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
693
0
      goto out;
694
0
    }
695
696
0
    if (listen(serverfd, 2) == -1)
697
0
    {
698
0
      char ebuffer[256] = WINPR_C_ARRAY_INIT;
699
0
      WLog_ERR(TAG, "CreateNamedPipeA: listen error, %s",
700
0
               winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
701
0
      goto out;
702
0
    }
703
704
0
    UnixChangeFileMode(pNamedPipe->lpFilePath, 0xFFFF);
705
706
0
    if (!(baseSocket = (NamedPipeServerSocketEntry*)malloc(sizeof(NamedPipeServerSocketEntry))))
707
0
      goto out;
708
709
0
    if (!(baseSocket->name = _strdup(lpName)))
710
0
    {
711
0
      free(baseSocket);
712
0
      goto out;
713
0
    }
714
715
0
    baseSocket->serverfd = serverfd;
716
0
    baseSocket->references = 0;
717
718
0
    if (!ArrayList_Append(g_NamedPipeServerSockets, baseSocket))
719
0
    {
720
0
      free(baseSocket->name);
721
0
      free(baseSocket);
722
0
      goto out;
723
0
    }
724
725
    // WLog_DBG(TAG, "created shared socked resource for pipe %p (%s). base serverfd = %d",
726
    // (void*) pNamedPipe, lpName, serverfd);
727
0
  }
728
729
  /* F_DUPFD_CLOEXEC rather than plain dup(): dup() always clears close-on-exec on the new fd
730
   * regardless of the source's own flags, so a plain dup() here would silently undo the
731
   * close-on-exec set on the shared listening socket above. */
732
0
  pNamedPipe->serverfd = fcntl(baseSocket->serverfd, F_DUPFD_CLOEXEC, 0);
733
  // WLog_DBG(TAG, "using serverfd %d (duplicated from %d)", pNamedPipe->serverfd,
734
  // baseSocket->serverfd);
735
0
  pNamedPipe->pfnUnrefNamedPipe = winpr_unref_named_pipe;
736
0
  baseSocket->references++;
737
738
0
  if (dwOpenMode & FILE_FLAG_OVERLAPPED)
739
0
  {
740
    // TODO: Implement
741
0
    WLog_ERR(TAG, "TODO: implement this");
742
0
  }
743
744
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append takes ownership of baseSocket
745
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
746
0
  return pNamedPipe;
747
0
out:
748
0
  NamedPipeCloseHandle(pNamedPipe);
749
0
  free(pNamedPipe);
750
751
0
  if (serverfd != -1)
752
0
    close(serverfd);
753
754
0
  ArrayList_Unlock(g_NamedPipeServerSockets);
755
0
  return INVALID_HANDLE_VALUE;
756
0
}
757
758
HANDLE CreateNamedPipeW(WINPR_ATTR_UNUSED LPCWSTR lpName, WINPR_ATTR_UNUSED DWORD dwOpenMode,
759
                        WINPR_ATTR_UNUSED DWORD dwPipeMode, WINPR_ATTR_UNUSED DWORD nMaxInstances,
760
                        WINPR_ATTR_UNUSED DWORD nOutBufferSize,
761
                        WINPR_ATTR_UNUSED DWORD nInBufferSize,
762
                        WINPR_ATTR_UNUSED DWORD nDefaultTimeOut,
763
                        WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSecurityAttributes)
764
0
{
765
0
  WLog_ERR(TAG, "is not implemented");
766
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
767
0
  return nullptr;
768
0
}
769
770
BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped)
771
0
{
772
0
  int status = 0;
773
0
  socklen_t length = 0;
774
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
775
776
0
  if (lpOverlapped)
777
0
  {
778
0
    WLog_ERR(TAG, "WinPR does not support the lpOverlapped parameter");
779
0
    SetLastError(ERROR_NOT_SUPPORTED);
780
0
    return FALSE;
781
0
  }
782
783
0
  if (!hNamedPipe)
784
0
    return FALSE;
785
786
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
787
788
0
  if (!(pNamedPipe->dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED))
789
0
  {
790
0
    struct sockaddr_un s = WINPR_C_ARRAY_INIT;
791
0
    length = sizeof(struct sockaddr_un);
792
0
#ifdef WINPR_HAVE_ACCEPT4
793
0
    status = accept4(pNamedPipe->serverfd, (struct sockaddr*)&s, &length, SOCK_CLOEXEC);
794
#else
795
    status = accept(pNamedPipe->serverfd, (struct sockaddr*)&s, &length);
796
#endif
797
798
0
    if (status < 0)
799
0
    {
800
0
      WLog_ERR(TAG, "ConnectNamedPipe: accept error");
801
0
      return FALSE;
802
0
    }
803
804
#ifndef WINPR_HAVE_ACCEPT4
805
    if (!winpr_set_cloexec(status, TRUE))
806
    {
807
      WLog_ERR(TAG, "ConnectNamedPipe: failed to set close-on-exec on accepted socket");
808
      close(status);
809
      return FALSE;
810
    }
811
#endif
812
813
0
    pNamedPipe->clientfd = status;
814
0
    pNamedPipe->ServerMode = FALSE;
815
0
  }
816
0
  else
817
0
  {
818
0
    if (!lpOverlapped)
819
0
      return FALSE;
820
821
0
    if (pNamedPipe->serverfd == -1)
822
0
      return FALSE;
823
824
0
    pNamedPipe->lpOverlapped = lpOverlapped;
825
    /* synchronous behavior */
826
0
    lpOverlapped->Internal = 2;
827
0
    lpOverlapped->InternalHigh = (ULONG_PTR)0;
828
0
    lpOverlapped->DUMMYUNIONNAME.Pointer = (PVOID) nullptr;
829
0
    (void)SetEvent(lpOverlapped->hEvent);
830
0
  }
831
832
0
  return TRUE;
833
0
}
834
835
BOOL DisconnectNamedPipe(HANDLE hNamedPipe)
836
0
{
837
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
838
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
839
840
0
  if (pNamedPipe->clientfd != -1)
841
0
  {
842
0
    close(pNamedPipe->clientfd);
843
0
    pNamedPipe->clientfd = -1;
844
0
  }
845
846
0
  return TRUE;
847
0
}
848
849
BOOL PeekNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpBuffer,
850
                   WINPR_ATTR_UNUSED DWORD nBufferSize, WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
851
                   WINPR_ATTR_UNUSED LPDWORD lpTotalBytesAvail,
852
                   WINPR_ATTR_UNUSED LPDWORD lpBytesLeftThisMessage)
853
0
{
854
0
  WLog_ERR(TAG, "Not implemented");
855
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
856
0
  return FALSE;
857
0
}
858
859
BOOL TransactNamedPipe(WINPR_ATTR_UNUSED HANDLE hNamedPipe, WINPR_ATTR_UNUSED LPVOID lpInBuffer,
860
                       WINPR_ATTR_UNUSED DWORD nInBufferSize, WINPR_ATTR_UNUSED LPVOID lpOutBuffer,
861
                       WINPR_ATTR_UNUSED DWORD nOutBufferSize,
862
                       WINPR_ATTR_UNUSED LPDWORD lpBytesRead,
863
                       WINPR_ATTR_UNUSED LPOVERLAPPED lpOverlapped)
864
0
{
865
0
  WLog_ERR(TAG, "Not implemented");
866
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
867
0
  return FALSE;
868
0
}
869
870
BOOL WaitNamedPipeA(LPCSTR lpNamedPipeName, DWORD nTimeOut)
871
0
{
872
0
  BOOL status = 0;
873
0
  DWORD nWaitTime = 0;
874
0
  char* lpFilePath = nullptr;
875
0
  DWORD dwSleepInterval = 0;
876
877
0
  if (!lpNamedPipeName)
878
0
    return FALSE;
879
880
0
  lpFilePath = GetNamedPipeUnixDomainSocketFilePathA(lpNamedPipeName);
881
882
0
  if (!lpFilePath)
883
0
    return FALSE;
884
885
0
  if (nTimeOut == NMPWAIT_USE_DEFAULT_WAIT)
886
0
    nTimeOut = 50;
887
888
0
  nWaitTime = 0;
889
0
  status = TRUE;
890
0
  dwSleepInterval = 10;
891
892
0
  while (!winpr_PathFileExists(lpFilePath))
893
0
  {
894
0
    Sleep(dwSleepInterval);
895
0
    nWaitTime += dwSleepInterval;
896
897
0
    if (nWaitTime >= nTimeOut)
898
0
    {
899
0
      status = FALSE;
900
0
      break;
901
0
    }
902
0
  }
903
904
0
  free(lpFilePath);
905
0
  return status;
906
0
}
907
908
BOOL WaitNamedPipeW(WINPR_ATTR_UNUSED LPCWSTR lpNamedPipeName, WINPR_ATTR_UNUSED DWORD nTimeOut)
909
0
{
910
0
  WLog_ERR(TAG, "Not implemented");
911
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
912
0
  return FALSE;
913
0
}
914
915
// NOLINTBEGIN(readability-non-const-parameter)
916
BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount,
917
                             LPDWORD lpCollectDataTimeout)
918
// NOLINTEND(readability-non-const-parameter)
919
0
{
920
0
  int fd = 0;
921
0
  int flags = 0;
922
0
  WINPR_NAMED_PIPE* pNamedPipe = nullptr;
923
0
  pNamedPipe = (WINPR_NAMED_PIPE*)hNamedPipe;
924
925
0
  if (lpMode)
926
0
  {
927
0
    pNamedPipe->dwPipeMode = *lpMode;
928
0
    fd = (pNamedPipe->ServerMode) ? pNamedPipe->serverfd : pNamedPipe->clientfd;
929
930
0
    if (fd == -1)
931
0
      return FALSE;
932
933
0
    flags = fcntl(fd, F_GETFL);
934
935
0
    if (flags < 0)
936
0
      return FALSE;
937
938
0
    if (pNamedPipe->dwPipeMode & PIPE_NOWAIT)
939
0
      flags = (flags | O_NONBLOCK);
940
0
    else
941
0
      flags = (flags & ~(O_NONBLOCK));
942
943
0
    if (fcntl(fd, F_SETFL, flags) < 0)
944
0
      return FALSE;
945
0
  }
946
947
0
  if (lpMaxCollectionCount)
948
0
  {
949
0
  }
950
951
0
  if (lpCollectDataTimeout)
952
0
  {
953
0
  }
954
955
0
  return TRUE;
956
0
}
957
958
BOOL ImpersonateNamedPipeClient(WINPR_ATTR_UNUSED HANDLE hNamedPipe)
959
0
{
960
0
  WLog_ERR(TAG, "Not implemented");
961
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
962
0
  return FALSE;
963
0
}
964
965
BOOL GetNamedPipeClientComputerNameA(WINPR_ATTR_UNUSED HANDLE Pipe,
966
                                     WINPR_ATTR_UNUSED LPCSTR ClientComputerName,
967
                                     WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
968
0
{
969
0
  WLog_ERR(TAG, "Not implemented");
970
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
971
0
  return FALSE;
972
0
}
973
974
BOOL GetNamedPipeClientComputerNameW(WINPR_ATTR_UNUSED HANDLE Pipe,
975
                                     WINPR_ATTR_UNUSED LPCWSTR ClientComputerName,
976
                                     WINPR_ATTR_UNUSED ULONG ClientComputerNameLength)
977
0
{
978
0
  WLog_ERR(TAG, "Not implemented");
979
0
  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
980
0
  return FALSE;
981
0
}
982
983
#endif