FreeRDP
Loading...
Searching...
No Matches
parallel_main.c
1
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#define TAG CHANNELS_TAG("drive.client")
59
60typedef 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
79static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp)
80{
81 char* path = nullptr;
82 UINT32 PathLength = 0;
83
84 WINPR_ASSERT(parallel);
85 WINPR_ASSERT(irp);
86
87 if (!Stream_SafeSeek(irp->input, 28))
88 return ERROR_INVALID_DATA;
89 /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */
90 /* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */
91 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4))
92 return ERROR_INVALID_DATA;
93 Stream_Read_UINT32(irp->input, PathLength);
94 if (PathLength < sizeof(WCHAR))
95 return ERROR_INVALID_DATA;
96 const WCHAR* ptr = Stream_ConstPointer(irp->input);
97 if (!Stream_SafeSeek(irp->input, PathLength))
98 return ERROR_INVALID_DATA;
99 path = ConvertWCharNToUtf8Alloc(ptr, PathLength / sizeof(WCHAR), nullptr);
100 if (!path)
101 return CHANNEL_RC_NO_MEMORY;
102
103 if (parallel->file >= 0)
104 {
105 WLog_ERR(TAG, "Duplicate create request for %s. Aborting.", path);
106 free(path);
107 return ERROR_INVALID_DATA;
108 }
109 parallel->id = irp->devman->id_sequence++;
110 parallel->file = open(parallel->path, O_RDWR);
111
112 if (parallel->file < 0)
113 {
114 irp->IoStatus = STATUS_ACCESS_DENIED;
115 parallel->id = 0;
116 }
117 else
118 {
119 /* all read and write operations should be non-blocking */
120 if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1)
121 {
122 }
123 }
124
125 Stream_Write_UINT32(irp->output, parallel->id);
126 Stream_Write_UINT8(irp->output, 0);
127 free(path);
128 return CHANNEL_RC_OK;
129}
130
136static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp)
137{
138 WINPR_ASSERT(parallel);
139 WINPR_ASSERT(irp);
140
141 (void)close(parallel->file);
142 parallel->file = -1;
143
144 Stream_Zero(irp->output, 5); /* Padding(5) */
145 return CHANNEL_RC_OK;
146}
147
153static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp)
154{
155 UINT32 Length = 0;
156 UINT64 Offset = 0;
157 ssize_t status = 0;
158 BYTE* buffer = nullptr;
159
160 WINPR_ASSERT(parallel);
161 WINPR_ASSERT(irp);
162
163 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
164 return ERROR_INVALID_DATA;
165 Stream_Read_UINT32(irp->input, Length);
166 Stream_Read_UINT64(irp->input, Offset);
167 (void)Offset; /* [MS-RDPESP] 3.2.5.1.4 Processing a Server Read Request Message
168 * ignored */
169
170 if (parallel->file < 0)
171 {
172 WLog_ERR(TAG, "Device not open, aborting");
173 return ERROR_DEVICE_NOT_AVAILABLE;
174 }
175
176 buffer = (BYTE*)calloc(Length, sizeof(BYTE));
177
178 if (!buffer)
179 {
180 WLog_Print(parallel->log, WLOG_ERROR, "malloc failed!");
181 return CHANNEL_RC_NO_MEMORY;
182 }
183
184 status = read(parallel->file, buffer, Length);
185
186 if ((status < 0) || (status > UINT32_MAX))
187 {
188 irp->IoStatus = STATUS_UNSUCCESSFUL;
189 free(buffer);
190 buffer = nullptr;
191 Length = 0;
192 }
193 else
194 {
195 Length = (UINT32)status;
196 }
197
198 Stream_Write_UINT32(irp->output, Length);
199
200 if (Length > 0)
201 {
202 if (!Stream_EnsureRemainingCapacity(irp->output, Length))
203 {
204 WLog_Print(parallel->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
205 free(buffer);
206 return CHANNEL_RC_NO_MEMORY;
207 }
208
209 Stream_Write(irp->output, buffer, Length);
210 }
211
212 free(buffer);
213 return CHANNEL_RC_OK;
214}
215
221static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp)
222{
223 UINT32 len = 0;
224 UINT32 Length = 0;
225 UINT64 Offset = 0;
226
227 WINPR_ASSERT(parallel);
228 WINPR_ASSERT(irp);
229
230 if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12))
231 return ERROR_INVALID_DATA;
232
233 Stream_Read_UINT32(irp->input, Length);
234 Stream_Read_UINT64(irp->input, Offset);
235 (void)Offset; /* [MS-RDPESP] 3.2.5.1.5 Processing a Server Write Request Message
236 * ignore offset */
237 if (!Stream_SafeSeek(irp->input, 20)) /* Padding */
238 return ERROR_INVALID_DATA;
239 const void* ptr = Stream_ConstPointer(irp->input);
240 if (!Stream_SafeSeek(irp->input, Length))
241 return ERROR_INVALID_DATA;
242 len = Length;
243
244 if (parallel->file < 0)
245 {
246 WLog_ERR(TAG, "Device not open, aborting");
247 return ERROR_DEVICE_NOT_AVAILABLE;
248 }
249 while (len > 0)
250 {
251 const ssize_t status = write(parallel->file, ptr, len);
252
253 if ((status < 0) || (status > len))
254 {
255 irp->IoStatus = STATUS_UNSUCCESSFUL;
256 Length = 0;
257 break;
258 }
259
260 Stream_Seek(irp->input, WINPR_ASSERTING_INT_CAST(size_t, status));
261 len -= status;
262 }
263
264 Stream_Write_UINT32(irp->output, Length);
265 Stream_Write_UINT8(irp->output, 0); /* Padding */
266 return CHANNEL_RC_OK;
267}
268
274static UINT parallel_process_irp_device_control(WINPR_ATTR_UNUSED PARALLEL_DEVICE* parallel,
275 IRP* irp)
276{
277 WINPR_ASSERT(parallel);
278 WINPR_ASSERT(irp);
279
280 Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */
281 return CHANNEL_RC_OK;
282}
283
290static UINT parallel_eval(UINT error, IRP* irp)
291{
292 WINPR_ASSERT(irp);
293 if (error == CHANNEL_RC_OK)
294 {
295 WINPR_ASSERT(irp->Complete);
296 return irp->Complete(irp);
297 }
298
299 WLog_ERR(TAG, "IRP %s failed with %" PRIu32, rdpdr_irp_string(irp->MajorFunction), error);
300 WINPR_ASSERT(irp->Discard);
301 irp->Discard(irp);
302 return error;
303}
304
305static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp)
306{
307 UINT error = ERROR_INTERNAL_ERROR;
308
309 WINPR_ASSERT(parallel);
310 WINPR_ASSERT(irp);
311
312 switch (irp->MajorFunction)
313 {
314 case IRP_MJ_CREATE:
315 error = parallel_process_irp_create(parallel, irp);
316 break;
317
318 case IRP_MJ_CLOSE:
319 error = parallel_process_irp_close(parallel, irp);
320 break;
321
322 case IRP_MJ_READ:
323 error = parallel_process_irp_read(parallel, irp);
324 break;
325
326 case IRP_MJ_WRITE:
327 error = parallel_process_irp_write(parallel, irp);
328 break;
329
330 case IRP_MJ_DEVICE_CONTROL:
331 error = parallel_process_irp_device_control(parallel, irp);
332 break;
333
334 default:
335 irp->IoStatus = STATUS_NOT_SUPPORTED;
336 error = CHANNEL_RC_OK;
337 break;
338 }
339
340 error = parallel_eval(error, irp);
341
342 DWORD level = WLOG_TRACE;
343 if (error)
344 level = WLOG_WARN;
345
346 WLog_Print(parallel->log, level,
347 "[%s|0x%08" PRIx32 "] completed with %s [0x%08" PRIx32 "] (IoStatus %s [0x%08" PRIx32
348 "])",
349 rdpdr_irp_string(irp->MajorFunction), irp->MajorFunction, WTSErrorToString(error),
350 error, NtStatus2Tag(irp->IoStatus), WINPR_CXX_COMPAT_CAST(UINT32, irp->IoStatus));
351
352 return error;
353}
354
355static DWORD WINAPI parallel_thread_func(LPVOID arg)
356{
357 PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg;
358 UINT error = CHANNEL_RC_OK;
359
360 WINPR_ASSERT(parallel);
361 while (1)
362 {
363 if (!MessageQueue_Wait(parallel->queue))
364 {
365 WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Wait failed!");
366 error = ERROR_INTERNAL_ERROR;
367 break;
368 }
369
370 wMessage message = WINPR_C_ARRAY_INIT;
371 if (!MessageQueue_Peek(parallel->queue, &message, TRUE))
372 {
373 WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Peek failed!");
374 error = ERROR_INTERNAL_ERROR;
375 break;
376 }
377
378 if (message.id == WMQ_QUIT)
379 break;
380
381 IRP* irp = (IRP*)message.wParam;
382
383 error = parallel_process_irp(parallel, irp);
384 if (error)
385 {
386 WLog_Print(parallel->log, WLOG_ERROR,
387 "parallel_process_irp failed with error %" PRIu32 "!", error);
388 break;
389 }
390 }
391
392 if (error && parallel->rdpcontext)
393 setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error");
394
395 ExitThread(error);
396 return error;
397}
398
404static UINT parallel_irp_request(DEVICE* device, IRP* irp)
405{
406 PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device;
407
408 WINPR_ASSERT(parallel);
409
410 if (!MessageQueue_Post(parallel->queue, nullptr, 0, (void*)irp, nullptr))
411 {
412 WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_Post failed!");
413 irp->Discard(irp);
414 return ERROR_INTERNAL_ERROR;
415 }
416
417 return CHANNEL_RC_OK;
418}
419
425static UINT parallel_free_int(PARALLEL_DEVICE* parallel)
426{
427 if (parallel)
428 {
429 if (!MessageQueue_PostQuit(parallel->queue, 0) ||
430 (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED))
431 {
432 const UINT error = GetLastError();
433 WLog_Print(parallel->log, WLOG_ERROR,
434 "WaitForSingleObject failed with error %" PRIu32 "!", error);
435 }
436
437 (void)CloseHandle(parallel->thread);
438 Stream_Free(parallel->device.data, TRUE);
439 MessageQueue_Free(parallel->queue);
440 }
441 free(parallel);
442 return CHANNEL_RC_OK;
443}
444
445static UINT parallel_free(DEVICE* device)
446{
447 if (device)
448 return parallel_free_int((PARALLEL_DEVICE*)device);
449 return CHANNEL_RC_OK;
450}
451
452static void parallel_message_free(void* obj)
453{
454 wMessage* msg = obj;
455 if (!msg)
456 return;
457 if (msg->id != 0)
458 return;
459
460 IRP* irp = (IRP*)msg->wParam;
461 if (!irp)
462 return;
463 WINPR_ASSERT(irp->Discard);
464 irp->Discard(irp);
465}
466
472FREERDP_ENTRY_POINT(
473 UINT VCAPITYPE parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints))
474{
475 PARALLEL_DEVICE* parallel = nullptr;
476 UINT error = 0;
477
478 WINPR_ASSERT(pEntryPoints);
479
480 RDPDR_PARALLEL* device = (RDPDR_PARALLEL*)pEntryPoints->device;
481 WINPR_ASSERT(device);
482
483 wLog* log = WLog_Get(TAG);
484 WINPR_ASSERT(log);
485
486 char* name = device->device.Name;
487 char* path = device->Path;
488
489 if (!name || (name[0] == '*') || !path)
490 {
491 /* TODO: implement auto detection of parallel ports */
492 WLog_Print(log, WLOG_WARN, "Autodetection not implemented, no ports will be redirected");
493 return CHANNEL_RC_INITIALIZATION_ERROR;
494 }
495
496 if (name[0] && path[0])
497 {
498 parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE));
499
500 if (!parallel)
501 {
502 WLog_Print(log, WLOG_ERROR, "calloc failed!");
503 return CHANNEL_RC_NO_MEMORY;
504 }
505
506 parallel->file = -1;
507 parallel->log = log;
508 parallel->device.type = RDPDR_DTYP_PARALLEL;
509 parallel->device.name = name;
510 parallel->device.IRPRequest = parallel_irp_request;
511 parallel->device.Free = parallel_free;
512 parallel->rdpcontext = pEntryPoints->rdpcontext;
513 const size_t length = strlen(name);
514 parallel->device.data = Stream_New(nullptr, length + 1);
515
516 if (!parallel->device.data)
517 {
518 WLog_Print(parallel->log, WLOG_ERROR, "Stream_New failed!");
519 error = CHANNEL_RC_NO_MEMORY;
520 goto error_out;
521 }
522
523 for (size_t i = 0; i <= length; i++)
524 Stream_Write_INT8(parallel->device.data, name[i] < 0 ? '_' : name[i]);
525
526 parallel->path = path;
527 parallel->queue = MessageQueue_New(nullptr);
528
529 if (!parallel->queue)
530 {
531 WLog_Print(parallel->log, WLOG_ERROR, "MessageQueue_New failed!");
532 error = CHANNEL_RC_NO_MEMORY;
533 goto error_out;
534 }
535
536 wObject* obj = MessageQueue_Object(parallel->queue);
537 WINPR_ASSERT(obj);
538 obj->fnObjectFree = parallel_message_free;
539
540 error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &parallel->device);
541 if (error)
542 {
543 WLog_Print(parallel->log, WLOG_ERROR, "RegisterDevice failed with error %" PRIu32 "!",
544 error);
545 goto error_out;
546 }
547
548 parallel->thread = CreateThread(nullptr, 0, parallel_thread_func, parallel, 0, nullptr);
549 if (!parallel->thread)
550 {
551 WLog_Print(parallel->log, WLOG_ERROR, "CreateThread failed!");
552 error = ERROR_INTERNAL_ERROR;
553 goto error_out;
554 }
555 }
556
557 return CHANNEL_RC_OK;
558error_out:
559 parallel_free_int(parallel);
560 return error;
561}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59