Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/parallel/client/parallel_main.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Redirected Parallel Port Device Service
4
 *
5
 * Copyright 2010 O.S. Systems Software Ltda.
6
 * Copyright 2010 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7
 * Copyright 2015 Thincast Technologies GmbH
8
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 <freerdp/config.h>
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include <fcntl.h>
30
#include <errno.h>
31
32
#ifndef _WIN32
33
#include <termios.h>
34
#include <strings.h>
35
#include <sys/ioctl.h>
36
#endif
37
38
#ifdef __LINUX__
39
#include <linux/ppdev.h>
40
#include <linux/parport.h>
41
#endif
42
43
#include <winpr/crt.h>
44
#include <winpr/assert.h>
45
#include <winpr/synch.h>
46
#include <winpr/thread.h>
47
#include <winpr/stream.h>
48
#include <winpr/collections.h>
49
#include <winpr/interlocked.h>
50
51
#include <freerdp/types.h>
52
#include <freerdp/freerdp.h>
53
#include <freerdp/constants.h>
54
#include <freerdp/channels/rdpdr.h>
55
#include <freerdp/channels/log.h>
56
#include <freerdp/utils/rdpdr_utils.h>
57
58
0
#define TAG CHANNELS_TAG("drive.client")
59
60
typedef struct
61
{
62
  DEVICE device;
63
64
  int file;
65
  char* path;
66
  UINT32 id;
67
68
  HANDLE thread;
69
  wMessageQueue* queue;
70
  rdpContext* rdpcontext;
71
  wLog* log;
72
} PARALLEL_DEVICE;
73
74
/**
75
 * Function description
76
 *
77
 * @return 0 on success, otherwise a Win32 error code
78
 */
79
static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
80
0
{
81
0
  char* path = NULL;
82
0
  UINT32 PathLength = 0;
83
84
0
  WINPR_ASSERT(parallel);
85
0
  WINPR_ASSERT(irp);
86
87
0
  if (!Stream_SafeSeek(irp->input, 28))
88
0
    return ERROR_INVALID_DATA;
89
  /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
90
  /* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
91
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
92
0
    return ERROR_INVALID_DATA;
93
0
  Stream_Read_UINT32(irp->input, PathLength);
94
0
  if (PathLength < sizeof(WCHAR))
95
0
    return ERROR_INVALID_DATA;
96
0
  const WCHAR* ptr = Stream_ConstPointer(irp->input);
97
0
  if (!Stream_SafeSeek(irp->input, PathLength))
98
0
    return ERROR_INVALID_DATA;
99
0
  path = ConvertWCharNToUtf8Alloc(ptr, PathLength / sizeof(WCHAR), NULL);
100
0
  if (!path)
101
0
    return CHANNEL_RC_NO_MEMORY;
102
103
0
  parallel->id = irp->devman->id_sequence++;
104
0
  parallel->file = open(parallel->path, O_RDWR);
105
106
0
  if (parallel->file < 0)
107
0
  {
108
0
    irp->IoStatus = STATUS_ACCESS_DENIED;
109
0
    parallel->id = 0;
110
0
  }
111
0
  else
112
0
  {
113
    /* all read and write operations should be non-blocking */
114
0
    if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1)
115
0
    {
116
0
    }
117
0
  }
118
119
0
  Stream_Write_UINT32(irp->output, parallel->id);
120
0
  Stream_Write_UINT8(irp->output, 0);
121
0
  free(path);
122
0
  return CHANNEL_RC_OK;
123
0
}
124
125
/**
126
 * Function description
127
 *
128
 * @return 0 on success, otherwise a Win32 error code
129
 */
130
static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
131
0
{
132
0
  WINPR_ASSERT(parallel);
133
0
  WINPR_ASSERT(irp);
134
135
0
  (void)close(parallel->file);
136
137
0
  Stream_Zero(irp->output, 5); /* Padding(5) */
138
0
  return CHANNEL_RC_OK;
139
0
}
140
141
/**
142
 * Function description
143
 *
144
 * @return 0 on success, otherwise a Win32 error code
145
 */
146
static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
147
0
{
148
0
  UINT32 Length = 0;
149
0
  UINT64 Offset = 0;
150
0
  ssize_t status = 0;
151
0
  BYTE* buffer = NULL;
152
153
0
  WINPR_ASSERT(parallel);
154
0
  WINPR_ASSERT(irp);
155
156
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
157
0
    return ERROR_INVALID_DATA;
158
0
  Stream_Read_UINT32(irp->input, Length);
159
0
  Stream_Read_UINT64(irp->input, Offset);
160
0
  (void)Offset; /* [MS-RDPESP] 3.2.5.1.4 Processing a Server Read Request Message
161
                 * ignored */
162
0
  buffer = (BYTE*)calloc(Length, sizeof(BYTE));
163
164
0
  if (!buffer)
165
0
  {
166
0
    WLog_Print(parallel->log, WLOG_ERROR, "malloc failed!");
167
0
    return CHANNEL_RC_NO_MEMORY;
168
0
  }
169
170
0
  status = read(parallel->file, buffer, Length);
171
172
0
  if ((status < 0) || (status > UINT32_MAX))
173
0
  {
174
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
175
0
    free(buffer);
176
0
    buffer = NULL;
177
0
    Length = 0;
178
0
  }
179
0
  else
180
0
  {
181
0
    Length = (UINT32)status;
182
0
  }
183
184
0
  Stream_Write_UINT32(irp->output, Length);
185
186
0
  if (Length > 0)
187
0
  {
188
0
    if (!Stream_EnsureRemainingCapacity(irp->output, Length))
189
0
    {
190
0
      WLog_Print(parallel->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
191
0
      free(buffer);
192
0
      return CHANNEL_RC_NO_MEMORY;
193
0
    }
194
195
0
    Stream_Write(irp->output, buffer, Length);
196
0
  }
197
198
0
  free(buffer);
199
0
  return CHANNEL_RC_OK;
200
0
}
201
202
/**
203
 * Function description
204
 *
205
 * @return 0 on success, otherwise a Win32 error code
206
 */
207
static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
208
0
{
209
0
  UINT32 len = 0;
210
0
  UINT32 Length = 0;
211
0
  UINT64 Offset = 0;
212
213
0
  WINPR_ASSERT(parallel);
214
0
  WINPR_ASSERT(irp);
215
216
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
217
0
    return ERROR_INVALID_DATA;
218
219
0
  Stream_Read_UINT32(irp->input, Length);
220
0
  Stream_Read_UINT64(irp->input, Offset);
221
0
  (void)Offset; /* [MS-RDPESP] 3.2.5.1.5 Processing a Server Write Request Message
222
                 * ignore offset */
223
0
  if (!Stream_SafeSeek(irp->input, 20)) /* Padding */
224
0
    return ERROR_INVALID_DATA;
225
0
  const void* ptr = Stream_ConstPointer(irp->input);
226
0
  if (!Stream_SafeSeek(irp->input, Length))
227
0
    return ERROR_INVALID_DATA;
228
0
  len = Length;
229
230
0
  while (len > 0)
231
0
  {
232
0
    const ssize_t status = write(parallel->file, ptr, len);
233
234
0
    if ((status < 0) || (status > len))
235
0
    {
236
0
      irp->IoStatus = STATUS_UNSUCCESSFUL;
237
0
      Length = 0;
238
0
      break;
239
0
    }
240
241
0
    Stream_Seek(irp->input, WINPR_ASSERTING_INT_CAST(size_t, status));
242
0
    len -= status;
243
0
  }
244
245
0
  Stream_Write_UINT32(irp->output, Length);
246
0
  Stream_Write_UINT8(irp->output, 0); /* Padding */
247
0
  return CHANNEL_RC_OK;
248
0
}
249
250
/**
251
 * Function description
252
 *
253
 * @return 0 on success, otherwise a Win32 error code
254
 */
255
static UINT parallel_process_irp_device_control(WINPR_ATTR_UNUSED PARALLEL_DEVICE* parallel,
256
                                                IRP* irp)
257
0
{
258
0
  WINPR_ASSERT(parallel);
259
0
  WINPR_ASSERT(irp);
260
261
0
  Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
262
0
  return CHANNEL_RC_OK;
263
0
}
264
265
/**
266
 * Function description
267
 *
268
 * @return 0 on success, otherwise a Win32 error code
269
 */
270
271
static UINT parallel_eval(UINT error, IRP* irp)
272
0
{
273
0
  WINPR_ASSERT(irp);
274
0
  if (error == CHANNEL_RC_OK)
275
0
  {
276
0
    WINPR_ASSERT(irp->Complete);
277
0
    return irp->Complete(irp);
278
0
  }
279
280
0
  WLog_ERR(TAG, "IRP %s failed with %" PRIu32, rdpdr_irp_string(irp->MajorFunction), error);
281
0
  WINPR_ASSERT(irp->Discard);
282
0
  irp->Discard(irp);
283
0
  return error;
284
0
}
285
286
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
287
0
{
288
0
  UINT error = ERROR_INTERNAL_ERROR;
289
290
0
  WINPR_ASSERT(parallel);
291
0
  WINPR_ASSERT(irp);
292
293
0
  switch (irp->MajorFunction)
294
0
  {
295
0
    case IRP_MJ_CREATE:
296
0
      error = parallel_process_irp_create(parallel, irp);
297
0
      break;
298
299
0
    case IRP_MJ_CLOSE:
300
0
      error = parallel_process_irp_close(parallel, irp);
301
0
      break;
302
303
0
    case IRP_MJ_READ:
304
0
      error = parallel_process_irp_read(parallel, irp);
305
0
      break;
306
307
0
    case IRP_MJ_WRITE:
308
0
      error = parallel_process_irp_write(parallel, irp);
309
0
      break;
310
311
0
    case IRP_MJ_DEVICE_CONTROL:
312
0
      error = parallel_process_irp_device_control(parallel, irp);
313
0
      break;
314
315
0
    default:
316
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
317
0
      error = CHANNEL_RC_OK;
318
0
      break;
319
0
  }
320
321
0
  error = parallel_eval(error, irp);
322
323
0
  DWORD level = WLOG_TRACE;
324
0
  if (error)
325
0
    level = WLOG_WARN;
326
327
0
  WLog_Print(parallel->log, level,
328
0
             "[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
329
0
             "])",
330
0
             rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
331
0
             error, NtStatus2Tag(irp->IoStatus), WINPR_CXX_COMPAT_CAST(UINT32, irp->IoStatus));
332
333
0
  return error;
334
0
}
335
336
static DWORD WINAPI parallel_thread_func(LPVOID arg)
337
0
{
338
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
339
0
  UINT error = CHANNEL_RC_OK;
340
341
0
  WINPR_ASSERT(parallel);
342
0
  while (1)
343
0
  {
344
0
    if (!MessageQueue_Wait(parallel->queue))
345
0
    {
346
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
347
0
      error = ERROR_INTERNAL_ERROR;
348
0
      break;
349
0
    }
350
351
0
    wMessage message = WINPR_C_ARRAY_INIT;
352
0
    if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
353
0
    {
354
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
355
0
      error = ERROR_INTERNAL_ERROR;
356
0
      break;
357
0
    }
358
359
0
    if (message.id == WMQ_QUIT)
360
0
      break;
361
362
0
    IRP* irp = (IRP*)message.wParam;
363
364
0
    error = parallel_process_irp(parallel, irp);
365
0
    if (error)
366
0
    {
367
0
      WLog_Print(parallel->log, WLOG_ERROR,
368
0
                 "parallel_process_irp failed with error %" PRIu32 "!", error);
369
0
      break;
370
0
    }
371
0
  }
372
373
0
  if (error && parallel->rdpcontext)
374
0
    setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
375
376
0
  ExitThread(error);
377
0
  return error;
378
0
}
379
380
/**
381
 * Function description
382
 *
383
 * @return 0 on success, otherwise a Win32 error code
384
 */
385
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
386
0
{
387
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
388
389
0
  WINPR_ASSERT(parallel);
390
391
0
  if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
392
0
  {
393
0
    WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
394
0
    irp->Discard(irp);
395
0
    return ERROR_INTERNAL_ERROR;
396
0
  }
397
398
0
  return CHANNEL_RC_OK;
399
0
}
400
401
/**
402
 * Function description
403
 *
404
 * @return 0 on success, otherwise a Win32 error code
405
 */
406
static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
407
0
{
408
0
  if (parallel)
409
0
  {
410
0
    if (!MessageQueue_PostQuit(parallel->queue, 0) ||
411
0
        (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
412
0
    {
413
0
      const UINT error = GetLastError();
414
0
      WLog_Print(parallel->log, WLOG_ERROR,
415
0
                 "WaitForSingleObject failed with error %" PRIu32 "!", error);
416
0
    }
417
418
0
    (void)CloseHandle(parallel->thread);
419
0
    Stream_Free(parallel->device.data, TRUE);
420
0
    MessageQueue_Free(parallel->queue);
421
0
  }
422
0
  free(parallel);
423
0
  return CHANNEL_RC_OK;
424
0
}
425
426
static UINT parallel_free(DEVICE* device)
427
0
{
428
0
  if (device)
429
0
    return parallel_free_int((PARALLEL_DEVICE*)device);
430
0
  return CHANNEL_RC_OK;
431
0
}
432
433
static void parallel_message_free(void* obj)
434
0
{
435
0
  wMessage* msg = obj;
436
0
  if (!msg)
437
0
    return;
438
0
  if (msg->id != 0)
439
0
    return;
440
441
0
  IRP* irp = (IRP*)msg->wParam;
442
0
  if (!irp)
443
0
    return;
444
0
  WINPR_ASSERT(irp->Discard);
445
0
  irp->Discard(irp);
446
0
}
447
448
/**
449
 * Function description
450
 *
451
 * @return 0 on success, otherwise a Win32 error code
452
 */
453
FREERDP_ENTRY_POINT(
454
    UINT VCAPITYPE parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
455
0
{
456
0
  PARALLEL_DEVICE* parallel = NULL;
457
0
  UINT error = 0;
458
459
0
  WINPR_ASSERT(pEntryPoints);
460
461
0
  RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
462
0
  WINPR_ASSERT(device);
463
464
0
  wLog* log = WLog_Get(TAG);
465
0
  WINPR_ASSERT(log);
466
467
0
  char* name = device->device.Name;
468
0
  char* path = device->Path;
469
470
0
  if (!name || (name[0] == '*') || !path)
471
0
  {
472
    /* TODO: implement auto detection of parallel ports */
473
0
    WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
474
0
    return CHANNEL_RC_INITIALIZATION_ERROR;
475
0
  }
476
477
0
  if (name[0] && path[0])
478
0
  {
479
0
    parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
480
481
0
    if (!parallel)
482
0
    {
483
0
      WLog_Print(log, WLOG_ERROR, "calloc failed!");
484
0
      return CHANNEL_RC_NO_MEMORY;
485
0
    }
486
487
0
    parallel->log = log;
488
0
    parallel->device.type = RDPDR_DTYP_PARALLEL;
489
0
    parallel->device.name = name;
490
0
    parallel->device.IRPRequest = parallel_irp_request;
491
0
    parallel->device.Free = parallel_free;
492
0
    parallel->rdpcontext = pEntryPoints->rdpcontext;
493
0
    const size_t length = strlen(name);
494
0
    parallel->device.data = Stream_New(NULL, length + 1);
495
496
0
    if (!parallel->device.data)
497
0
    {
498
0
      WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
499
0
      error = CHANNEL_RC_NO_MEMORY;
500
0
      goto error_out;
501
0
    }
502
503
0
    for (size_t i = 0; i <= length; i++)
504
0
      Stream_Write_INT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
505
506
0
    parallel->path = path;
507
0
    parallel->queue = MessageQueue_New(NULL);
508
509
0
    if (!parallel->queue)
510
0
    {
511
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
512
0
      error = CHANNEL_RC_NO_MEMORY;
513
0
      goto error_out;
514
0
    }
515
516
0
    wObject* obj = MessageQueue_Object(parallel->queue);
517
0
    WINPR_ASSERT(obj);
518
0
    obj->fnObjectFree = parallel_message_free;
519
520
0
    error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &parallel->device);
521
0
    if (error)
522
0
    {
523
0
      WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
524
0
                 error);
525
0
      goto error_out;
526
0
    }
527
528
0
    parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
529
0
    if (!parallel->thread)
530
0
    {
531
0
      WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
532
0
      error = ERROR_INTERNAL_ERROR;
533
0
      goto error_out;
534
0
    }
535
0
  }
536
537
0
  return CHANNEL_RC_OK;
538
0
error_out:
539
0
  parallel_free_int(parallel);
540
0
  return error;
541
0
}