diff options
Diffstat (limited to 'channels/parallel/client/parallel_main.c')
-rw-r--r-- | channels/parallel/client/parallel_main.c | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/channels/parallel/client/parallel_main.c b/channels/parallel/client/parallel_main.c new file mode 100644 index 0000000..f0801e1 --- /dev/null +++ b/channels/parallel/client/parallel_main.c @@ -0,0 +1,503 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Redirected Parallel Port Device Service + * + * Copyright 2010 O.S. Systems Software Ltda. + * Copyright 2010 Eduardo Fiss Beloni <beloni@ossystems.com.br> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <fcntl.h> +#include <errno.h> + +#ifndef _WIN32 +#include <termios.h> +#include <strings.h> +#include <sys/ioctl.h> +#endif + +#ifdef __LINUX__ +#include <linux/ppdev.h> +#include <linux/parport.h> +#endif + +#include <winpr/crt.h> +#include <winpr/assert.h> +#include <winpr/synch.h> +#include <winpr/thread.h> +#include <winpr/stream.h> +#include <winpr/collections.h> +#include <winpr/interlocked.h> + +#include <freerdp/types.h> +#include <freerdp/freerdp.h> +#include <freerdp/constants.h> +#include <freerdp/channels/rdpdr.h> +#include <freerdp/channels/log.h> + +#define TAG CHANNELS_TAG("drive.client") + +typedef struct +{ + DEVICE device; + + int file; + char* path; + UINT32 id; + + HANDLE thread; + wMessageQueue* queue; + rdpContext* rdpcontext; +} PARALLEL_DEVICE; + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp_create(PARALLEL_DEVICE* parallel, IRP* irp) +{ + char* path = NULL; + UINT32 PathLength = 0; + if (!Stream_SafeSeek(irp->input, 28)) + return ERROR_INVALID_DATA; + /* DesiredAccess(4) AllocationSize(8), FileAttributes(4) */ + /* SharedAccess(4) CreateDisposition(4), CreateOptions(4) */ + if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 4)) + return ERROR_INVALID_DATA; + Stream_Read_UINT32(irp->input, PathLength); + if (PathLength < sizeof(WCHAR)) + return ERROR_INVALID_DATA; + const WCHAR* ptr = Stream_ConstPointer(irp->input); + if (!Stream_SafeSeek(irp->input, PathLength)) + return ERROR_INVALID_DATA; + path = ConvertWCharNToUtf8Alloc(ptr, PathLength / sizeof(WCHAR), NULL); + if (!path) + return CHANNEL_RC_NO_MEMORY; + + parallel->id = irp->devman->id_sequence++; + parallel->file = open(parallel->path, O_RDWR); + + if (parallel->file < 0) + { + irp->IoStatus = STATUS_ACCESS_DENIED; + parallel->id = 0; + } + else + { + /* all read and write operations should be non-blocking */ + if (fcntl(parallel->file, F_SETFL, O_NONBLOCK) == -1) + { + } + } + + Stream_Write_UINT32(irp->output, parallel->id); + Stream_Write_UINT8(irp->output, 0); + free(path); + return irp->Complete(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp_close(PARALLEL_DEVICE* parallel, IRP* irp) +{ + if (close(parallel->file) < 0) + { + } + else + { + } + + Stream_Zero(irp->output, 5); /* Padding(5) */ + return irp->Complete(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp) +{ + UINT32 Length = 0; + UINT64 Offset = 0; + ssize_t status = 0; + BYTE* buffer = NULL; + if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12)) + return ERROR_INVALID_DATA; + Stream_Read_UINT32(irp->input, Length); + Stream_Read_UINT64(irp->input, Offset); + buffer = (BYTE*)calloc(Length, sizeof(BYTE)); + + if (!buffer) + { + WLog_ERR(TAG, "malloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + status = read(parallel->file, buffer, Length); + + if (status < 0) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + free(buffer); + buffer = NULL; + Length = 0; + } + else + { + Length = status; + } + + Stream_Write_UINT32(irp->output, Length); + + if (Length > 0) + { + if (!Stream_EnsureRemainingCapacity(irp->output, Length)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + free(buffer); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write(irp->output, buffer, Length); + } + + free(buffer); + return irp->Complete(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp_write(PARALLEL_DEVICE* parallel, IRP* irp) +{ + UINT32 len = 0; + UINT32 Length = 0; + UINT64 Offset = 0; + ssize_t status = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, 12)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(irp->input, Length); + Stream_Read_UINT64(irp->input, Offset); + if (!Stream_SafeSeek(irp->input, 20)) /* Padding */ + return ERROR_INVALID_DATA; + const void* ptr = Stream_ConstPointer(irp->input); + if (!Stream_SafeSeek(irp->input, Length)) + return ERROR_INVALID_DATA; + len = Length; + + while (len > 0) + { + status = write(parallel->file, ptr, len); + + if (status < 0) + { + irp->IoStatus = STATUS_UNSUCCESSFUL; + Length = 0; + break; + } + + Stream_Seek(irp->input, status); + len -= status; + } + + Stream_Write_UINT32(irp->output, Length); + Stream_Write_UINT8(irp->output, 0); /* Padding */ + return irp->Complete(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp_device_control(PARALLEL_DEVICE* parallel, IRP* irp) +{ + Stream_Write_UINT32(irp->output, 0); /* OutputBufferLength */ + return irp->Complete(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_process_irp(PARALLEL_DEVICE* parallel, IRP* irp) +{ + UINT error = 0; + + switch (irp->MajorFunction) + { + case IRP_MJ_CREATE: + if ((error = parallel_process_irp_create(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp_create failed with error %" PRIu32 "!", error); + return error; + } + + break; + + case IRP_MJ_CLOSE: + if ((error = parallel_process_irp_close(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp_close failed with error %" PRIu32 "!", error); + return error; + } + + break; + + case IRP_MJ_READ: + if ((error = parallel_process_irp_read(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp_read failed with error %" PRIu32 "!", error); + return error; + } + + break; + + case IRP_MJ_WRITE: + if ((error = parallel_process_irp_write(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp_write failed with error %" PRIu32 "!", error); + return error; + } + + break; + + case IRP_MJ_DEVICE_CONTROL: + if ((error = parallel_process_irp_device_control(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp_device_control failed with error %" PRIu32 "!", + error); + return error; + } + + break; + + default: + irp->IoStatus = STATUS_NOT_SUPPORTED; + return irp->Complete(irp); + } + + return CHANNEL_RC_OK; +} + +static DWORD WINAPI parallel_thread_func(LPVOID arg) +{ + IRP* irp = NULL; + wMessage message = { 0 }; + PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)arg; + UINT error = CHANNEL_RC_OK; + + while (1) + { + if (!MessageQueue_Wait(parallel->queue)) + { + WLog_ERR(TAG, "MessageQueue_Wait failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + + if (!MessageQueue_Peek(parallel->queue, &message, TRUE)) + { + WLog_ERR(TAG, "MessageQueue_Peek failed!"); + error = ERROR_INTERNAL_ERROR; + break; + } + + if (message.id == WMQ_QUIT) + break; + + irp = (IRP*)message.wParam; + + if ((error = parallel_process_irp(parallel, irp))) + { + WLog_ERR(TAG, "parallel_process_irp failed with error %" PRIu32 "!", error); + break; + } + } + + if (error && parallel->rdpcontext) + setChannelError(parallel->rdpcontext, error, "parallel_thread_func reported an error"); + + ExitThread(error); + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_irp_request(DEVICE* device, IRP* irp) +{ + PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device; + + if (!MessageQueue_Post(parallel->queue, NULL, 0, (void*)irp, NULL)) + { + WLog_ERR(TAG, "MessageQueue_Post failed!"); + return ERROR_INTERNAL_ERROR; + } + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT parallel_free(DEVICE* device) +{ + UINT error = 0; + PARALLEL_DEVICE* parallel = (PARALLEL_DEVICE*)device; + + if (!MessageQueue_PostQuit(parallel->queue, 0) || + (WaitForSingleObject(parallel->thread, INFINITE) == WAIT_FAILED)) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error); + return error; + } + + CloseHandle(parallel->thread); + Stream_Free(parallel->device.data, TRUE); + MessageQueue_Free(parallel->queue); + free(parallel); + return CHANNEL_RC_OK; +} + +static void parallel_message_free(void* obj) +{ + wMessage* msg = obj; + if (!msg) + return; + if (msg->id != 0) + return; + + IRP* irp = (IRP*)msg->wParam; + if (!irp) + return; + WINPR_ASSERT(irp->Discard); + irp->Discard(irp); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +FREERDP_ENTRY_POINT(UINT parallel_DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)) +{ + char* name = NULL; + char* path = NULL; + size_t length = 0; + RDPDR_PARALLEL* device = NULL; + PARALLEL_DEVICE* parallel = NULL; + UINT error = 0; + + WINPR_ASSERT(pEntryPoints); + + device = (RDPDR_PARALLEL*)pEntryPoints->device; + WINPR_ASSERT(device); + + name = device->device.Name; + path = device->Path; + + if (!name || (name[0] == '*') || !path) + { + /* TODO: implement auto detection of parallel ports */ + return CHANNEL_RC_INITIALIZATION_ERROR; + } + + if (name[0] && path[0]) + { + parallel = (PARALLEL_DEVICE*)calloc(1, sizeof(PARALLEL_DEVICE)); + + if (!parallel) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + parallel->device.type = RDPDR_DTYP_PARALLEL; + parallel->device.name = name; + parallel->device.IRPRequest = parallel_irp_request; + parallel->device.Free = parallel_free; + parallel->rdpcontext = pEntryPoints->rdpcontext; + length = strlen(name); + parallel->device.data = Stream_New(NULL, length + 1); + + if (!parallel->device.data) + { + WLog_ERR(TAG, "Stream_New failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto error_out; + } + + for (size_t i = 0; i <= length; i++) + Stream_Write_UINT8(parallel->device.data, name[i] < 0 ? '_' : name[i]); + + parallel->path = path; + parallel->queue = MessageQueue_New(NULL); + + if (!parallel->queue) + { + WLog_ERR(TAG, "MessageQueue_New failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto error_out; + } + + wObject* obj = MessageQueue_Object(parallel->queue); + WINPR_ASSERT(obj); + obj->fnObjectFree = parallel_message_free; + + if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)parallel))) + { + WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error); + goto error_out; + } + + if (!(parallel->thread = + CreateThread(NULL, 0, parallel_thread_func, (void*)parallel, 0, NULL))) + { + WLog_ERR(TAG, "CreateThread failed!"); + error = ERROR_INTERNAL_ERROR; + goto error_out; + } + } + + return CHANNEL_RC_OK; +error_out: + MessageQueue_Free(parallel->queue); + Stream_Free(parallel->device.data, TRUE); + free(parallel); + return error; +} |