Coverage Report

Created: 2024-09-08 06:20

/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
  return irp->Complete(irp);
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 irp->Complete(irp);
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)
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 = 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 irp->Complete(irp);
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, 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 irp->Complete(irp);
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(PARALLEL_DEVICE* parallel, IRP* irp)
256
0
{
257
0
  WINPR_ASSERT(parallel);
258
0
  WINPR_ASSERT(irp);
259
260
0
  Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
261
0
  return irp->Complete(irp);
262
0
}
263
264
/**
265
 * Function description
266
 *
267
 * @return 0 on success, otherwise a Win32 error code
268
 */
269
static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
270
0
{
271
0
  UINT error = ERROR_INTERNAL_ERROR;
272
273
0
  WINPR_ASSERT(parallel);
274
0
  WINPR_ASSERT(irp);
275
276
0
  switch (irp->MajorFunction)
277
0
  {
278
0
    case IRP_MJ_CREATE:
279
0
      error = parallel_process_irp_create(parallel, irp);
280
0
      break;
281
282
0
    case IRP_MJ_CLOSE:
283
0
      error = parallel_process_irp_close(parallel, irp);
284
0
      break;
285
286
0
    case IRP_MJ_READ:
287
0
      error = parallel_process_irp_read(parallel, irp);
288
0
      break;
289
290
0
    case IRP_MJ_WRITE:
291
0
      error = parallel_process_irp_write(parallel, irp);
292
0
      break;
293
294
0
    case IRP_MJ_DEVICE_CONTROL:
295
0
      error = parallel_process_irp_device_control(parallel, irp);
296
0
      break;
297
298
0
    default:
299
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
300
0
      error = irp->Complete(irp);
301
0
      break;
302
0
  }
303
304
0
  DWORD level = WLOG_TRACE;
305
0
  if (error)
306
0
    level = WLOG_WARN;
307
308
0
  WLog_Print(parallel->log, level,
309
0
             "[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
310
0
             "])",
311
0
             rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
312
0
             error, NtStatus2Tag(irp->IoStatus), irp->IoStatus);
313
314
0
  return error;
315
0
}
316
317
static DWORD WINAPI parallel_thread_func(LPVOID arg)
318
0
{
319
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
320
0
  UINT error = CHANNEL_RC_OK;
321
322
0
  WINPR_ASSERT(parallel);
323
0
  while (1)
324
0
  {
325
0
    if (!MessageQueue_Wait(parallel->queue))
326
0
    {
327
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
328
0
      error = ERROR_INTERNAL_ERROR;
329
0
      break;
330
0
    }
331
332
0
    wMessage message = { 0 };
333
0
    if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
334
0
    {
335
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
336
0
      error = ERROR_INTERNAL_ERROR;
337
0
      break;
338
0
    }
339
340
0
    if (message.id == WMQ_QUIT)
341
0
      break;
342
343
0
    IRP* irp = (IRP*)message.wParam;
344
345
0
    error = parallel_process_irp(parallel, irp);
346
0
    if (error)
347
0
    {
348
0
      WLog_Print(parallel->log, WLOG_ERROR,
349
0
                 "parallel_process_irp failed with error %" PRIu32 "!", error);
350
0
      break;
351
0
    }
352
0
  }
353
354
0
  if (error && parallel->rdpcontext)
355
0
    setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
356
357
0
  ExitThread(error);
358
0
  return error;
359
0
}
360
361
/**
362
 * Function description
363
 *
364
 * @return 0 on success, otherwise a Win32 error code
365
 */
366
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
367
0
{
368
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
369
370
0
  WINPR_ASSERT(parallel);
371
372
0
  if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
373
0
  {
374
0
    WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
375
0
    return ERROR_INTERNAL_ERROR;
376
0
  }
377
378
0
  return CHANNEL_RC_OK;
379
0
}
380
381
/**
382
 * Function description
383
 *
384
 * @return 0 on success, otherwise a Win32 error code
385
 */
386
static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
387
0
{
388
0
  if (parallel)
389
0
  {
390
0
    if (!MessageQueue_PostQuit(parallel->queue, 0) ||
391
0
        (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
392
0
    {
393
0
      const UINT error = GetLastError();
394
0
      WLog_Print(parallel->log, WLOG_ERROR,
395
0
                 "WaitForSingleObject failed with error %" PRIu32 "!", error);
396
0
    }
397
398
0
    CloseHandle(parallel->thread);
399
0
    Stream_Free(parallel->device.data, TRUE);
400
0
    MessageQueue_Free(parallel->queue);
401
0
  }
402
0
  free(parallel);
403
0
  return CHANNEL_RC_OK;
404
0
}
405
406
static UINT parallel_free(DEVICE* device)
407
0
{
408
0
  if (device)
409
0
    return parallel_free_int((PARALLEL_DEVICE*)device);
410
0
  return CHANNEL_RC_OK;
411
0
}
412
413
static void parallel_message_free(void* obj)
414
0
{
415
0
  wMessage* msg = obj;
416
0
  if (!msg)
417
0
    return;
418
0
  if (msg->id != 0)
419
0
    return;
420
421
0
  IRP* irp = (IRP*)msg->wParam;
422
0
  if (!irp)
423
0
    return;
424
0
  WINPR_ASSERT(irp->Discard);
425
0
  irp->Discard(irp);
426
0
}
427
428
/**
429
 * Function description
430
 *
431
 * @return 0 on success, otherwise a Win32 error code
432
 */
433
FREERDP_ENTRY_POINT(
434
    UINT VCAPITYPE parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
435
0
{
436
0
  PARALLEL_DEVICE* parallel = NULL;
437
0
  UINT error = 0;
438
439
0
  WINPR_ASSERT(pEntryPoints);
440
441
0
  RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
442
0
  WINPR_ASSERT(device);
443
444
0
  wLog* log = WLog_Get(TAG);
445
0
  WINPR_ASSERT(log);
446
447
0
  char* name = device->device.Name;
448
0
  char* path = device->Path;
449
450
0
  if (!name || (name[0] == '*') || !path)
451
0
  {
452
    /* TODO: implement auto detection of parallel ports */
453
0
    WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
454
0
    return CHANNEL_RC_INITIALIZATION_ERROR;
455
0
  }
456
457
0
  if (name[0] && path[0])
458
0
  {
459
0
    parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
460
461
0
    if (!parallel)
462
0
    {
463
0
      WLog_Print(log, WLOG_ERROR, "calloc failed!");
464
0
      return CHANNEL_RC_NO_MEMORY;
465
0
    }
466
467
0
    parallel->log = log;
468
0
    parallel->device.type = RDPDR_DTYP_PARALLEL;
469
0
    parallel->device.name = name;
470
0
    parallel->device.IRPRequest = parallel_irp_request;
471
0
    parallel->device.Free = parallel_free;
472
0
    parallel->rdpcontext = pEntryPoints->rdpcontext;
473
0
    const size_t length = strlen(name);
474
0
    parallel->device.data = Stream_New(NULL, length + 1);
475
476
0
    if (!parallel->device.data)
477
0
    {
478
0
      WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
479
0
      error = CHANNEL_RC_NO_MEMORY;
480
0
      goto error_out;
481
0
    }
482
483
0
    for (size_t i = 0; i <= length; i++)
484
0
      Stream_Write_UINT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
485
486
0
    parallel->path = path;
487
0
    parallel->queue = MessageQueue_New(NULL);
488
489
0
    if (!parallel->queue)
490
0
    {
491
0
      WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
492
0
      error = CHANNEL_RC_NO_MEMORY;
493
0
      goto error_out;
494
0
    }
495
496
0
    wObject* obj = MessageQueue_Object(parallel->queue);
497
0
    WINPR_ASSERT(obj);
498
0
    obj->fnObjectFree = parallel_message_free;
499
500
0
    error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &parallel->device);
501
0
    if (error)
502
0
    {
503
0
      WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
504
0
                 error);
505
0
      goto error_out;
506
0
    }
507
508
0
    parallel->thread = CreateThread(NULL, 0, parallel_thread_func, parallel, 0, NULL);
509
0
    if (!parallel->thread)
510
0
    {
511
0
      WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
512
0
      error = ERROR_INTERNAL_ERROR;
513
0
      goto error_out;
514
0
    }
515
0
  }
516
517
0
  return CHANNEL_RC_OK;
518
0
error_out:
519
0
  parallel_free_int(parallel);
520
0
  return error;
521
0
}