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