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