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