Coverage Report

Created: 2024-05-20 06:11

/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
57
#define TAG CHANNELS_TAG("drive.client")
58
59
typedef struct
60
{
61
  DEVICE device;
62
63
  int file;
64
  char* path;
65
  UINT32 id;
66
67
  HANDLE thread;
68
  wMessageQueue* queue;
69
  rdpContext* rdpcontext;
70
} PARALLEL_DEVICE;
71
72
/**
73
 * Function description
74
 *
75
 * @return 0 on success, otherwise a Win32 error code
76
 */
77
static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
78
0
{
79
0
  char* path = NULL;
80
0
  UINT32 PathLength = 0;
81
82
0
  WINPR_ASSERT(parallel);
83
0
  WINPR_ASSERT(irp);
84
85
0
  if (!Stream_SafeSeek(irp->input, 28))
86
0
    return ERROR_INVALID_DATA;
87
  /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
88
  /* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
89
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
90
0
    return ERROR_INVALID_DATA;
91
0
  Stream_Read_UINT32(irp->input, PathLength);
92
0
  if (PathLength < sizeof(WCHAR))
93
0
    return ERROR_INVALID_DATA;
94
0
  const WCHAR* ptr = Stream_ConstPointer(irp->input);
95
0
  if (!Stream_SafeSeek(irp->input, PathLength))
96
0
    return ERROR_INVALID_DATA;
97
0
  path = ConvertWCharNToUtf8Alloc(ptr, PathLength / sizeof(WCHAR), NULL);
98
0
  if (!path)
99
0
    return CHANNEL_RC_NO_MEMORY;
100
101
0
  parallel->id = irp->devman->id_sequence++;
102
0
  parallel->file = open(parallel->path, O_RDWR);
103
104
0
  if (parallel->file < 0)
105
0
  {
106
0
    irp->IoStatus = STATUS_ACCESS_DENIED;
107
0
    parallel->id = 0;
108
0
  }
109
0
  else
110
0
  {
111
    /* all read and write operations should be non-blocking */
112
0
    if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1)
113
0
    {
114
0
    }
115
0
  }
116
117
0
  Stream_Write_UINT32(irp->output, parallel->id);
118
0
  Stream_Write_UINT8(irp->output, 0);
119
0
  free(path);
120
0
  return irp->Complete(irp);
121
0
}
122
123
/**
124
 * Function description
125
 *
126
 * @return 0 on success, otherwise a Win32 error code
127
 */
128
static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
129
0
{
130
0
  WINPR_ASSERT(parallel);
131
0
  WINPR_ASSERT(irp);
132
133
0
  if (close(parallel->file) < 0)
134
0
  {
135
0
  }
136
0
  else
137
0
  {
138
0
  }
139
140
0
  Stream_Zero(irp->output, 5); /* Padding(5) */
141
0
  return irp->Complete(irp);
142
0
}
143
144
/**
145
 * Function description
146
 *
147
 * @return 0 on success, otherwise a Win32 error code
148
 */
149
static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
150
0
{
151
0
  UINT32 Length = 0;
152
0
  UINT64 Offset = 0;
153
0
  ssize_t status = 0;
154
0
  BYTE* buffer = NULL;
155
156
0
  WINPR_ASSERT(parallel);
157
0
  WINPR_ASSERT(irp);
158
159
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
160
0
    return ERROR_INVALID_DATA;
161
0
  Stream_Read_UINT32(irp->input, Length);
162
0
  Stream_Read_UINT64(irp->input, Offset);
163
0
  buffer = (BYTE*)calloc(Length, sizeof(BYTE));
164
165
0
  if (!buffer)
166
0
  {
167
0
    WLog_ERR(TAG, "malloc failed!");
168
0
    return CHANNEL_RC_NO_MEMORY;
169
0
  }
170
171
0
  status = read(parallel->file, buffer, Length);
172
173
0
  if (status < 0)
174
0
  {
175
0
    irp->IoStatus = STATUS_UNSUCCESSFUL;
176
0
    free(buffer);
177
0
    buffer = NULL;
178
0
    Length = 0;
179
0
  }
180
0
  else
181
0
  {
182
0
    Length = status;
183
0
  }
184
185
0
  Stream_Write_UINT32(irp->output, Length);
186
187
0
  if (Length > 0)
188
0
  {
189
0
    if (!Stream_EnsureRemainingCapacity(irp->output, Length))
190
0
    {
191
0
      WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
192
0
      free(buffer);
193
0
      return CHANNEL_RC_NO_MEMORY;
194
0
    }
195
196
0
    Stream_Write(irp->output, buffer, Length);
197
0
  }
198
199
0
  free(buffer);
200
0
  return irp->Complete(irp);
201
0
}
202
203
/**
204
 * Function description
205
 *
206
 * @return 0 on success, otherwise a Win32 error code
207
 */
208
static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
209
0
{
210
0
  UINT32 len = 0;
211
0
  UINT32 Length = 0;
212
0
  UINT64 Offset = 0;
213
0
  ssize_t status = 0;
214
215
0
  WINPR_ASSERT(parallel);
216
0
  WINPR_ASSERT(irp);
217
218
0
  if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
219
0
    return ERROR_INVALID_DATA;
220
221
0
  Stream_Read_UINT32(irp->input, Length);
222
0
  Stream_Read_UINT64(irp->input, 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
    status = write(parallel->file, ptr, len);
233
234
0
    if (status < 0)
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 = 0;
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
      if ((error = parallel_process_irp_create(parallel, irp)))
280
0
      {
281
0
        WLog_ERR(TAG, "parallel_process_irp_create failed with error %" PRIu32 "!", error);
282
0
        return error;
283
0
      }
284
285
0
      break;
286
287
0
    case IRP_MJ_CLOSE:
288
0
      if ((error = parallel_process_irp_close(parallel, irp)))
289
0
      {
290
0
        WLog_ERR(TAG, "parallel_process_irp_close failed with error %" PRIu32 "!", error);
291
0
        return error;
292
0
      }
293
294
0
      break;
295
296
0
    case IRP_MJ_READ:
297
0
      if ((error = parallel_process_irp_read(parallel, irp)))
298
0
      {
299
0
        WLog_ERR(TAG, "parallel_process_irp_read failed with error %" PRIu32 "!", error);
300
0
        return error;
301
0
      }
302
303
0
      break;
304
305
0
    case IRP_MJ_WRITE:
306
0
      if ((error = parallel_process_irp_write(parallel, irp)))
307
0
      {
308
0
        WLog_ERR(TAG, "parallel_process_irp_write failed with error %" PRIu32 "!", error);
309
0
        return error;
310
0
      }
311
312
0
      break;
313
314
0
    case IRP_MJ_DEVICE_CONTROL:
315
0
      if ((error = parallel_process_irp_device_control(parallel, irp)))
316
0
      {
317
0
        WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %" PRIu32 "!",
318
0
                 error);
319
0
        return error;
320
0
      }
321
322
0
      break;
323
324
0
    default:
325
0
      irp->IoStatus = STATUS_NOT_SUPPORTED;
326
0
      return irp->Complete(irp);
327
0
  }
328
329
0
  return CHANNEL_RC_OK;
330
0
}
331
332
static DWORD WINAPI parallel_thread_func(LPVOID arg)
333
0
{
334
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
335
0
  UINT error = CHANNEL_RC_OK;
336
337
0
  WINPR_ASSERT(parallel);
338
0
  while (1)
339
0
  {
340
0
    if (!MessageQueue_Wait(parallel->queue))
341
0
    {
342
0
      WLog_ERR(TAG, "MessageQueue_Wait failed!");
343
0
      error = ERROR_INTERNAL_ERROR;
344
0
      break;
345
0
    }
346
347
0
    wMessage message = { 0 };
348
0
    if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
349
0
    {
350
0
      WLog_ERR(TAG, "MessageQueue_Peek failed!");
351
0
      error = ERROR_INTERNAL_ERROR;
352
0
      break;
353
0
    }
354
355
0
    if (message.id == WMQ_QUIT)
356
0
      break;
357
358
0
    IRP* irp = (IRP*)message.wParam;
359
360
0
    if ((error = parallel_process_irp(parallel, irp)))
361
0
    {
362
0
      WLog_ERR(TAG, "parallel_process_irp failed with error %" PRIu32 "!", error);
363
0
      break;
364
0
    }
365
0
  }
366
367
0
  if (error && parallel->rdpcontext)
368
0
    setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
369
370
0
  ExitThread(error);
371
0
  return error;
372
0
}
373
374
/**
375
 * Function description
376
 *
377
 * @return 0 on success, otherwise a Win32 error code
378
 */
379
static UINT parallel_irp_request(DEVICE* device, IRP* irp)
380
0
{
381
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
382
383
0
  if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL))
384
0
  {
385
0
    WLog_ERR(TAG, "MessageQueue_Post failed!");
386
0
    return ERROR_INTERNAL_ERROR;
387
0
  }
388
389
0
  return CHANNEL_RC_OK;
390
0
}
391
392
/**
393
 * Function description
394
 *
395
 * @return 0 on success, otherwise a Win32 error code
396
 */
397
static UINT parallel_free(DEVICE* device)
398
0
{
399
0
  UINT error = 0;
400
0
  PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
401
402
0
  if (!MessageQueue_PostQuit(parallel->queue, 0) ||
403
0
      (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
404
0
  {
405
0
    error = GetLastError();
406
0
    WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error);
407
0
    return error;
408
0
  }
409
410
0
  CloseHandle(parallel->thread);
411
0
  Stream_Free(parallel->device.data, TRUE);
412
0
  MessageQueue_Free(parallel->queue);
413
0
  free(parallel);
414
0
  return CHANNEL_RC_OK;
415
0
}
416
417
static void parallel_message_free(void* obj)
418
0
{
419
0
  wMessage* msg = obj;
420
0
  if (!msg)
421
0
    return;
422
0
  if (msg->id != 0)
423
0
    return;
424
425
0
  IRP* irp = (IRP*)msg->wParam;
426
0
  if (!irp)
427
0
    return;
428
0
  WINPR_ASSERT(irp->Discard);
429
0
  irp->Discard(irp);
430
0
}
431
432
/**
433
 * Function description
434
 *
435
 * @return 0 on success, otherwise a Win32 error code
436
 */
437
FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
438
0
{
439
0
  char* name = NULL;
440
0
  char* path = NULL;
441
0
  size_t length = 0;
442
0
  RDPDR_PARALLEL* device = NULL;
443
0
  PARALLEL_DEVICE* parallel = NULL;
444
0
  UINT error = 0;
445
446
0
  WINPR_ASSERT(pEntryPoints);
447
448
0
  device = (RDPDR_PARALLEL*)pEntryPoints->device;
449
0
  WINPR_ASSERT(device);
450
451
0
  name = device->device.Name;
452
0
  path = device->Path;
453
454
0
  if (!name || (name[0] == '*') || !path)
455
0
  {
456
    /* TODO: implement auto detection of parallel ports */
457
0
    return CHANNEL_RC_INITIALIZATION_ERROR;
458
0
  }
459
460
0
  if (name[0] && path[0])
461
0
  {
462
0
    parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
463
464
0
    if (!parallel)
465
0
    {
466
0
      WLog_ERR(TAG, "calloc failed!");
467
0
      return CHANNEL_RC_NO_MEMORY;
468
0
    }
469
470
0
    parallel->device.type = RDPDR_DTYP_PARALLEL;
471
0
    parallel->device.name = name;
472
0
    parallel->device.IRPRequest = parallel_irp_request;
473
0
    parallel->device.Free = parallel_free;
474
0
    parallel->rdpcontext = pEntryPoints->rdpcontext;
475
0
    length = strlen(name);
476
0
    parallel->device.data = Stream_New(NULL, length + 1);
477
478
0
    if (!parallel->device.data)
479
0
    {
480
0
      WLog_ERR(TAG, "Stream_New failed!");
481
0
      error = CHANNEL_RC_NO_MEMORY;
482
0
      goto error_out;
483
0
    }
484
485
0
    for (size_t i = 0; i <= length; i++)
486
0
      Stream_Write_UINT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
487
488
0
    parallel->path = path;
489
0
    parallel->queue = MessageQueue_New(NULL);
490
491
0
    if (!parallel->queue)
492
0
    {
493
0
      WLog_ERR(TAG, "MessageQueue_New failed!");
494
0
      error = CHANNEL_RC_NO_MEMORY;
495
0
      goto error_out;
496
0
    }
497
498
0
    wObject* obj = MessageQueue_Object(parallel->queue);
499
0
    WINPR_ASSERT(obj);
500
0
    obj->fnObjectFree = parallel_message_free;
501
502
0
    if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel)))
503
0
    {
504
0
      WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
505
0
      goto error_out;
506
0
    }
507
508
0
    if (!(parallel->thread =
509
0
              CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL)))
510
0
    {
511
0
      WLog_ERR(TAG, "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
  MessageQueue_Free(parallel->queue);
520
0
  Stream_Free(parallel->device.data, TRUE);
521
0
  free(parallel);
522
0
  return error;
523
0
}