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