Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/parallel/client/parallel_main.c
Line
Count
Source (jump to first uncovered line)
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
  WINPR_ASSERT(irp->Complete);
123
0
  return irp->Complete(irp);
124
0
}
125
126
/**
127
 * Function description
128
 *
129
 * @return 0 on success, otherwise a Win32 error code
130
 */
131
static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
132
0
{
133
0
  WINPR_ASSERT(parallel);
134
0
  WINPR_ASSERT(irp);
135
136
0
  (void)close(parallel->file);
137
138
0
  Stream_Zero(irp->output, 5); /* Padding(5) */
139
0
  WINPR_ASSERT(irp->Complete);
140
0
  return irp->Complete(irp);
141
0
}
142
143
/**
144
 * Function description
145
 *
146
 * @return 0 on success, otherwise a Win32 error code
147
 */
148
static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
149
0
{
150
0
  UINT32 Length = 0;
151
0
  UINT64 Offset = 0;
152
0
  ssize_t status = 0;
153
0
  BYTE* buffer = NULL;
154
155
0
  WINPR_ASSERT(parallel);
156
0
  WINPR_ASSERT(irp);
157
158
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
159
0
    return ERROR_INVALID_DATA;
160
0
  Stream_Read_UINT32(irp->input, Length);
161
0
  Stream_Read_UINT64(irp->input, Offset);
162
0
  (void)Offset; /* [MS-RDPESP] 3.2.5.1.4 Processing a Server Read Request Message
163
                 * ignored */
164
0
  buffer = (BYTE*)calloc(Length, sizeof(BYTE));
165
166
0
  if (!buffer)
167
0
  {
168
0
    WLog_Print(parallel->log, WLOG_ERROR, "malloc failed!");
169
0
    return CHANNEL_RC_NO_MEMORY;
170
0
  }
171
172
0
  status = read(parallel->file, buffer, Length);
173
174
0
  if ((status < 0) || (status > UINT32_MAX))
175
0
  {
176
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
177
0
    free(buffer);
178
0
    buffer = NULL;
179
0
    Length = 0;
180
0
  }
181
0
  else
182
0
  {
183
0
    Length = (UINT32)status;
184
0
  }
185
186
0
  Stream_Write_UINT32(irp->output, Length);
187
188
0
  if (Length > 0)
189
0
  {
190
0
    if (!Stream_EnsureRemainingCapacity(irp->output, Length))
191
0
    {
192
0
      WLog_Print(parallel->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
193
0
      free(buffer);
194
0
      return CHANNEL_RC_NO_MEMORY;
195
0
    }
196
197
0
    Stream_Write(irp->output, buffer, Length);
198
0
  }
199
200
0
  free(buffer);
201
0
  WINPR_ASSERT(irp->Complete);
202
0
  return irp->Complete(irp);
203
0
}
204
205
/**
206
 * Function description
207
 *
208
 * @return 0 on success, otherwise a Win32 error code
209
 */
210
static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
211
0
{
212
0
  UINT32 len = 0;
213
0
  UINT32 Length = 0;
214
0
  UINT64 Offset = 0;
215
216
0
  WINPR_ASSERT(parallel);
217
0
  WINPR_ASSERT(irp);
218
219
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
220
0
    return ERROR_INVALID_DATA;
221
222
0
  Stream_Read_UINT32(irp->input, Length);
223
0
  Stream_Read_UINT64(irp->input, Offset);
224
0
  (void)Offset; /* [MS-RDPESP] 3.2.5.1.5 Processing a Server Write Request Message
225
                 * ignore offset */
226
0
  if (!Stream_SafeSeek(irp->input, 20)) /* Padding */
227
0
    return ERROR_INVALID_DATA;
228
0
  const void* ptr = Stream_ConstPointer(irp->input);
229
0
  if (!Stream_SafeSeek(irp->input, Length))
230
0
    return ERROR_INVALID_DATA;
231
0
  len = Length;
232
233
0
  while (len > 0)
234
0
  {
235
0
    const ssize_t status = write(parallel->file, ptr, len);
236
237
0
    if ((status < 0) || (status > len))
238
0
    {
239
0
      irp->IoStatus = STATUS_UNSUCCESSFUL;
240
0
      Length = 0;
241
0
      break;
242
0
    }
243
244
0
    Stream_Seek(irp->input, WINPR_ASSERTING_INT_CAST(size_t, status));
245
0
    len -= status;
246
0
  }
247
248
0
  Stream_Write_UINT32(irp->output, Length);
249
0
  Stream_Write_UINT8(irp->output, 0); /* Padding */
250
0
  WINPR_ASSERT(irp->Complete);
251
0
  return irp->Complete(irp);
252
0
}
253
254
/**
255
 * Function description
256
 *
257
 * @return 0 on success, otherwise a Win32 error code
258
 */
259
static UINT parallel_process_irp_device_control(WINPR_ATTR_UNUSED PARALLEL_DEVICE* parallel,
260
                                                IRP* irp)
261
0
{
262
0
  WINPR_ASSERT(parallel);
263
0
  WINPR_ASSERT(irp);
264
265
0
  Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
266
0
  WINPR_ASSERT(irp->Complete);
267
0
  return irp->Complete(irp);
268
0
}
269
270
/**
271
 * Function description
272
 *
273
 * @return 0 on success, otherwise a Win32 error code
274
 */
275
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
276
0
{
277
0
  UINT error = ERROR_INTERNAL_ERROR;
278
279
0
  WINPR_ASSERT(parallel);
280
0
  WINPR_ASSERT(irp);
281
282
0
  switch (irp->MajorFunction)
283
0
  {
284
0
    case IRP_MJ_CREATE:
285
0
      error = parallel_process_irp_create(parallel, irp);
286
0
      break;
287
288
0
    case IRP_MJ_CLOSE:
289
0
      error = parallel_process_irp_close(parallel, irp);
290
0
      break;
291
292
0
    case IRP_MJ_READ:
293
0
      error = parallel_process_irp_read(parallel, irp);
294
0
      break;
295
296
0
    case IRP_MJ_WRITE:
297
0
      error = parallel_process_irp_write(parallel, irp);
298
0
      break;
299
300
0
    case IRP_MJ_DEVICE_CONTROL:
301
0
      error = parallel_process_irp_device_control(parallel, irp);
302
0
      break;
303
304
0
    default:
305
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
306
0
      WINPR_ASSERT(irp->Complete);
307
0
      error = irp->Complete(irp);
308
0
      break;
309
0
  }
310
311
0
  DWORD level = WLOG_TRACE;
312
0
  if (error)
313
0
    level = WLOG_WARN;
314
315
0
  WLog_Print(parallel->log, level,
316
0
             "[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
317
0
             "])",
318
0
             rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
319
0
             error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
320
321
0
  return error;
322
0
}
323
324
static DWORD WINAPI parallel_thread_func(LPVOID arg)
325
0
{
326
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
327
0
  UINT error = CHANNEL_RC_OK;
328
329
0
  WINPR_ASSERT(parallel);
330
0
  while (1)
331
0
  {
332
0
    if (!MessageQueue_Wait(parallel->queue))
333
0
    {
334
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
335
0
      error = ERROR_INTERNAL_ERROR;
336
0
      break;
337
0
    }
338
339
0
    wMessage message = { 0 };
340
0
    if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
341
0
    {
342
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
343
0
      error = ERROR_INTERNAL_ERROR;
344
0
      break;
345
0
    }
346
347
0
    if (message.id == WMQ_QUIT)
348
0
      break;
349
350
0
    IRP* irp = (IRP*)message.wParam;
351
352
0
    error = parallel_process_irp(parallel, irp);
353
0
    if (error)
354
0
    {
355
0
      WLog_Print(parallel->log, WLOG_ERROR,
356
0
                 "parallel_process_irp failed with error %" PRIu32 "!", error);
357
0
      break;
358
0
    }
359
0
  }
360
361
0
  if (error && parallel->rdpcontext)
362
0
    setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
363
364
0
  ExitThread(error);
365
0
  return error;
366
0
}
367
368
/**
369
 * Function description
370
 *
371
 * @return 0 on success, otherwise a Win32 error code
372
 */
373
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
374
0
{
375
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
376
377
0
  WINPR_ASSERT(parallel);
378
379
0
  if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
380
0
  {
381
0
    WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
382
0
    return ERROR_INTERNAL_ERROR;
383
0
  }
384
385
0
  return CHANNEL_RC_OK;
386
0
}
387
388
/**
389
 * Function description
390
 *
391
 * @return 0 on success, otherwise a Win32 error code
392
 */
393
static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
394
0
{
395
0
  if (parallel)
396
0
  {
397
0
    if (!MessageQueue_PostQuit(parallel->queue, 0) ||
398
0
        (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
399
0
    {
400
0
      const UINT error = GetLastError();
401
0
      WLog_Print(parallel->log, WLOG_ERROR,
402
0
                 "WaitForSingleObject failed with error %" PRIu32 "!", error);
403
0
    }
404
405
0
    (void)CloseHandle(parallel->thread);
406
0
    Stream_Free(parallel->device.data, TRUE);
407
0
    MessageQueue_Free(parallel->queue);
408
0
  }
409
0
  free(parallel);
410
0
  return CHANNEL_RC_OK;
411
0
}
412
413
static UINT parallel_free(DEVICE* device)
414
0
{
415
0
  if (device)
416
0
    return parallel_free_int((PARALLEL_DEVICE*)device);
417
0
  return CHANNEL_RC_OK;
418
0
}
419
420
static void parallel_message_free(void* obj)
421
0
{
422
0
  wMessage* msg = obj;
423
0
  if (!msg)
424
0
    return;
425
0
  if (msg->id != 0)
426
0
    return;
427
428
0
  IRP* irp = (IRP*)msg->wParam;
429
0
  if (!irp)
430
0
    return;
431
0
  WINPR_ASSERT(irp->Discard);
432
0
  irp->Discard(irp);
433
0
}
434
435
/**
436
 * Function description
437
 *
438
 * @return 0 on success, otherwise a Win32 error code
439
 */
440
FREERDP_ENTRY_POINT(
441
    UINT VCAPITYPE parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
442
0
{
443
0
  PARALLEL_DEVICE* parallel = NULL;
444
0
  UINT error = 0;
445
446
0
  WINPR_ASSERT(pEntryPoints);
447
448
0
  RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
449
0
  WINPR_ASSERT(device);
450
451
0
  wLog* log = WLog_Get(TAG);
452
0
  WINPR_ASSERT(log);
453
454
0
  char* name = device->device.Name;
455
0
  char* path = device->Path;
456
457
0
  if (!name || (name[0] == '*') || !path)
458
0
  {
459
    /* TODO: implement auto detection of parallel ports */
460
0
    WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
461
0
    return CHANNEL_RC_INITIALIZATION_ERROR;
462
0
  }
463
464
0
  if (name[0] && path[0])
465
0
  {
466
0
    parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
467
468
0
    if (!parallel)
469
0
    {
470
0
      WLog_Print(log, WLOG_ERROR, "calloc failed!");
471
0
      return CHANNEL_RC_NO_MEMORY;
472
0
    }
473
474
0
    parallel->log = log;
475
0
    parallel->device.type = RDPDR_DTYP_PARALLEL;
476
0
    parallel->device.name = name;
477
0
    parallel->device.IRPRequest = parallel_irp_request;
478
0
    parallel->device.Free = parallel_free;
479
0
    parallel->rdpcontext = pEntryPoints->rdpcontext;
480
0
    const size_t length = strlen(name);
481
0
    parallel->device.data = Stream_New(NULL, length + 1);
482
483
0
    if (!parallel->device.data)
484
0
    {
485
0
      WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
486
0
      error = CHANNEL_RC_NO_MEMORY;
487
0
      goto error_out;
488
0
    }
489
490
0
    for (size_t i = 0; i <= length; i++)
491
0
      Stream_Write_INT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
492
493
0
    parallel->path = path;
494
0
    parallel->queue = MessageQueue_New(NULL);
495
496
0
    if (!parallel->queue)
497
0
    {
498
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
499
0
      error = CHANNEL_RC_NO_MEMORY;
500
0
      goto error_out;
501
0
    }
502
503
0
    wObject* obj = MessageQueue_Object(parallel->queue);
504
0
    WINPR_ASSERT(obj);
505
0
    obj->fnObjectFree = parallel_message_free;
506
507
0
    error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &parallel->device);
508
0
    if (error)
509
0
    {
510
0
      WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
511
0
                 error);
512
0
      goto error_out;
513
0
    }
514
515
0
    parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
516
0
    if (!parallel->thread)
517
0
    {
518
0
      WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
519
0
      error = ERROR_INTERNAL_ERROR;
520
0
      goto error_out;
521
0
    }
522
0
  }
523
524
0
  return CHANNEL_RC_OK;
525
0
error_out:
526
0
  parallel_free_int(parallel);
527
0
  return error;
528
0
}