#include "ILibDuktape_HECI.h" #include "ILibDuktapeModSearch.h" #include "ILibDuktape_Helpers.h" #include "ILibDuktape_DuplexStream.h" #include "ILibDuktape_EventEmitter.h" #include "ILibDuktape_ChildProcess.h" #include "../microstack/ILibParsers.h" #include "../microstack/ILibProcessPipe.h" #include "../microstack/ILibRemoteLogging.h" #ifdef WIN32 #include #include #include #include DEFINE_GUID(GUID_DEVINTERFACE_HECI, 0xE2D1FF34, 0x3458, 0x49A9, 0x88, 0xDA, 0x8E, 0x69, 0x15, 0xCE, 0x9B, 0xE5); #elif defined(_POSIX) typedef struct HECI_client { unsigned int max_msg_length; unsigned char protocol_version; unsigned char reserved[3]; }HECI_client; typedef struct uuid_le { unsigned char b[16]; }uuid_le; struct HECI_CONNECT_client_data { union { uuid_le uuid; HECI_client properties; }; }; #endif #define ILibDuktape_HECI_ChainLink "\xFF_HECI_ChainLink" #define ILibDuktape_HECI_Descriptor "\xFF_HECI_Descriptor" #define ILibDuktape_HECI_ChildProcess "\xFF_HECI_ChildProcess" #define ILibDuktape_HECI_Q "\xFF_HECI_Q" #define ILibDuktape_HECI_IoctlWaitHandle "\xFF_HECI_IoctlWaitHandle" #define ILibDuktape_HECI_Child "\xFF_HECI_Child" #define ILibDuktape_HECI_Parent "\xFF_HECI_Parent" #define ILibDuktape_HECI_MaxBufferSize "\xFF_HECI_MaxBufSize" #define ILibDuktape_HECI_SessionMemPtr "\xFF_HECI_SessionMemPtr" #define ILibDuktape_HECI_Session_NoPipeline "\xFF_HECI_Session_NoPipeline" #ifdef __DOXY__ /*! \implements EventEmitter \brief JavaScript object interface for HECI calls. require('heci') to use; */ class Heci { public: /*! \brief Performs an Ioctl on the HECI device \param code Ioctl Code to invoke \param inBuffer \ Input data for Ioctl. Can be null \param outBuffer \ Optional. Output data from Ioctl. Must be specified if Ioctl code returns data \param callback Dispatched when a response is received from the HECI device\n status Success Code. 0 = Success, Error code on failure\n buffer \ Output Buffer\n args Optional parameters that were passed in\n \param args Optional arguments to pass when the callback is called */ void doIoctl(code, inBuffer[, outBuffer], callback[, ...args]); }; #endif typedef struct ILibDuktape_HECI_ioctl_data { duk_context *ctx; void *heciObject; void *data; void *Q; void *chain; ILibProcessPipe_Manager pipeManager; #ifdef WIN32 OVERLAPPED v; HANDLE device; DWORD bytesReceived; #elif defined(_POSIX) int device; #endif int code; char *outBuffer; void *outBuffer_obj; duk_size_t outBufferLen; duk_size_t bufferLen; char buffer[]; }ILibDuktape_HECI_ioctl_data; typedef struct ILibDuktape_HECI_Session { void *chain; int noPipelining; ILibDuktape_DuplexStream *stream; #ifdef WIN32 OVERLAPPED v; OVERLAPPED wv; ILibProcessPipe_Manager mgr; HANDLE descriptor; #else int descriptor; #endif ILibQueue PendingWrites; duk_size_t bufferSize; char buffer[]; }ILibDuktape_HECI_Session; typedef struct ILibDuktape_HECI_WriteState { ILibDuktape_HECI_Session *session; int returnIgnored; #ifndef WIN32 int bufferOffset; #endif int bufferLen; char buffer[]; }ILibDuktape_HECI_WriteState; typedef struct HECI_chainLink { ILibChain_Link link; duk_context *ctx; void *Q; ILibDuktape_HECI_Session *session; void *heciObject; int descriptor; int paused; }HECI_chainLink; void ILibDuktape_HECI_Push(duk_context *ctx, void *chain); ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session); #ifdef WIN32 HANDLE ILibDuktape_HECI_windowsInit() { PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetail = NULL; HDEVINFO hDeviceInfo; DWORD bufferSize; SP_DEVICE_INTERFACE_DATA interfaceData; LONG ii = 0; HANDLE retVal = NULL; // Find all devices that have our interface hDeviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_DEVINTERFACE_HECI, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { return(NULL); } // Setup the interface data struct interfaceData.cbSize = sizeof(interfaceData); for (ii = 0; SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, (LPGUID)&GUID_DEVINTERFACE_HECI, ii, &interfaceData); ++ii) { // Found our device instance if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, NULL, 0, &bufferSize, NULL)) { DWORD err = GetLastError(); if (err != ERROR_INSUFFICIENT_BUFFER) { continue; } } // Allocate a big enough buffer to get detail data deviceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)ILibMemory_AllocateA(bufferSize); if (deviceDetail == NULL) { continue; } // Setup the device interface struct deviceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); // Try again to get the device interface detail info if (!SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &interfaceData, deviceDetail, bufferSize, NULL, NULL)) { deviceDetail = NULL; continue; } break; } SetupDiDestroyDeviceInfoList(hDeviceInfo); if (deviceDetail == NULL) { return(NULL); } retVal = CreateFile(deviceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (retVal == INVALID_HANDLE_VALUE) { return(NULL); } return(retVal); } #else int ILibDuktape_HECI_linuxInit() { int fd, flags; if ((fd = open("/dev/mei", O_RDWR)) == -1 && (fd = open("/dev/mei0", O_RDWR)) == -1) { return(-1); } else { flags = fcntl(fd, F_GETFL, 0); if (fcntl(fd, F_SETFL, O_NONBLOCK | flags) == -1) { printf("Failed to set O_NONBLOCK\n"); close(fd); fd = -1; } return(fd); } } #endif duk_ret_t ILibDuktape_HECI_SessionFinalizer(duk_context *ctx) { return(0); } void ILibDuktape_HECI_Session_EmitErrorEvent(void *chain, void *session) { if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitErrorEvent, session); } ILibDuktape_HECI_Session *s = (ILibDuktape_HECI_Session*)session; duk_context *ctx = s->stream->readableStream->ctx; duk_push_heapptr(ctx, s->stream->ParentObject); // [session] duk_get_prop_string(ctx, -1, "emit"); // [session][emit] duk_swap_top(ctx, -2); // [emit][this] duk_push_string(ctx, "error"); // [emit][this][error] duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err] if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } duk_pop(ctx); // ... } void ILibDuktape_HECI_Session_EmitStreamReady(void *chain, void *session) { if (ILibIsRunningOnChainThread(chain) == 0) { ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_HECI_Session_EmitStreamReady, session); } ILibDuktape_DuplexStream_Ready(((ILibDuktape_HECI_Session*)session)->stream); } #ifdef WIN32 BOOL ILibDuktape_HECI_Session_WriteHandler_Ready(HANDLE event, void* user) { ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; DWORD bytesWritten; ILibProcessPipe_WaitHandle_Remove(session->mgr, session->wv.hEvent); if (session->noPipelining == 0) { ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites); free(state); } if (GetOverlappedResult(session->descriptor, &(session->wv), &bytesWritten, FALSE) == 0) { // Broken Connection ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); } else { if (session->noPipelining == 0) { // Write Completed ILibDuktape_HECI_Session_WriteHandler_Process(session); } } return(TRUE); } #endif ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler_Process(ILibDuktape_HECI_Session *session) { ILibTransport_DoneState retVal = ILibTransport_DoneState_ERROR; int returnIgnored = 0; #ifdef WIN32 DWORD bytesWritten; BOOL result = TRUE; #else size_t bytesWritten; #endif while (ILibQueue_GetCount(session->PendingWrites) > 0) { ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_PeekQueue(session->PendingWrites); returnIgnored = state->returnIgnored; #ifdef WIN32 if ((result = WriteFile(state->session->descriptor, state->buffer, state->bufferLen, &bytesWritten, &(state->session->wv))) == TRUE) { if (session->noPipelining == 0) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); } } else { break; } #elif defined(_POSIX) bytesWritten = write(state->session->descriptor, state->buffer + state->bufferOffset, state->bufferLen - state->bufferOffset); if (bytesWritten > 0) { state->bufferOffset += bytesWritten; if (state->bufferOffset == state->bufferLen) { ILibQueue_DeQueue(state->session->PendingWrites); free(state); retVal = session->noPipelining == 0 ? ILibTransport_DoneState_COMPLETE:ILibTransport_DoneState_INCOMPLETE; } } else { if (errno != EAGAIN) { // Error Occured retVal = ILibTransport_DoneState_ERROR; ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); } else { retVal = ILibTransport_DoneState_INCOMPLETE; } break; } #endif if (session->noPipelining != 0) { break; } } #ifdef WIN32 if (result == FALSE) { if (GetLastError() == ERROR_IO_PENDING) { // Not done writing retVal = ILibTransport_DoneState_INCOMPLETE; ILibProcessPipe_WaitHandle_Add(session->mgr, session->wv.hEvent, session, ILibDuktape_HECI_Session_WriteHandler_Ready); } else { // Error Occured retVal = ILibTransport_DoneState_ERROR; ILibDuktape_HECI_Session_EmitErrorEvent(session->chain, (void*)session); } } else { if (session->noPipelining == 0) { // No more Pending Writes retVal = ILibTransport_DoneState_COMPLETE; if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); } } else { retVal = ILibTransport_DoneState_INCOMPLETE; } } #else if (ILibQueue_GetCount(session->PendingWrites) == 0 && session->noPipelining == 0) { // No more Pending Writes retVal = ILibTransport_DoneState_COMPLETE; if (returnIgnored != 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, (void*)session); } } #endif return(retVal); } #ifdef WIN32 void __stdcall ILibDuktape_HECI_Session_WriteHandler(ULONG_PTR obj) { // This Method is always dispatched from the WindowsRunLoop APC Thread ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)obj; ILibQueue_EnQueue(state->session->PendingWrites, state); if (ILibQueue_GetCount(state->session->PendingWrites) == 1) { // No Pending Writes, so we can go ahead and send out the first block ILibDuktape_HECI_Session_WriteHandler_Process(state->session); } } #elif defined(_POSIX) ILibTransport_DoneState ILibDuktape_HECI_Session_WriteHandler(void *chain, ILibDuktape_HECI_WriteState* state) { // This Method is always dispatched from the Microstack Thread ILibQueue_EnQueue(state->session->PendingWrites, state); if (ILibQueue_GetCount(state->session->PendingWrites) == 1) { return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session)); } else { return(ILibTransport_DoneState_INCOMPLETE); } } #endif ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink_NoPipeline(void *chain, void *user) { // This is always called from the Microstack Thread ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)user; ILibQueue_EnQueue(state->session->PendingWrites, state); if (ILibQueue_GetCount(state->session->PendingWrites) == 1) { return(ILibDuktape_HECI_Session_WriteHandler_Process(state->session)); } else { return(ILibTransport_DoneState_INCOMPLETE); } } ILibTransport_DoneState ILibDuktape_HECI_Session_WriteSink(ILibDuktape_DuplexStream *stream, char *buffer, int bufferLen, void *user) { if ((duk_size_t)bufferLen > ((ILibDuktape_HECI_Session*)user)->bufferSize) { return(ILibTransport_DoneState_ERROR); } ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibMemory_Allocate(bufferLen + sizeof(ILibDuktape_HECI_WriteState), 0, NULL, NULL); state->session = session; state->bufferLen = bufferLen; memcpy_s(state->buffer, bufferLen, buffer, bufferLen); if (session->noPipelining == 0) { #if defined(WIN32) state->returnIgnored = 1; QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_WriteHandler, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)state); #elif defined(_POSIX) if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0) { return(ILibDuktape_HECI_Session_WriteHandler(NULL, state)); } else { state->returnIgnored = 1; ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteHandler, state); } #endif } else { // We can't pipeline write requests if (ILibIsRunningOnChainThread(stream->readableStream->chain) != 0) { return(ILibDuktape_HECI_Session_WriteSink_NoPipeline(stream->readableStream->chain, state)); } else { state->returnIgnored = 1; ILibChain_RunOnMicrostackThreadEx(stream->readableStream->chain, (ILibChain_StartEvent)ILibDuktape_HECI_Session_WriteSink_NoPipeline, state); } } return(ILibTransport_DoneState_INCOMPLETE); } void ILibDuktape_HECI_Session_EndSink(ILibDuktape_DuplexStream *stream, void *user) { } void ILibDuktape_HECI_Session_PauseSink(ILibDuktape_DuplexStream *sender, void *user) { #ifdef WIN32 ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; // To Pause, all we need to do is remove our handle ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent); #else UNREFERENCED_PARAMETER(sender); UNREFERENCED_PARAMETER(user); #endif } #ifdef WIN32 BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user); #endif void ILibDuktape_HECI_Session_ResumeSink_NoPipeline(void *chain, void *user) { // This is always called from the Microstack Thread ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; ILibDuktape_HECI_WriteState *state = (ILibDuktape_HECI_WriteState*)ILibQueue_DeQueue(session->PendingWrites); free(state); if (ILibQueue_GetCount(session->PendingWrites) == 0) { ILibDuktape_HECI_Session_EmitStreamReady(session->chain, session); } else { ILibDuktape_HECI_Session_WriteHandler_Process(session); } } void ILibDuktape_HECI_Session_ResumeSink(ILibDuktape_DuplexStream *sender, void *user) { ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; if (session->noPipelining != 0) { ILibChain_RunOnMicrostackThread(sender->readableStream->chain, ILibDuktape_HECI_Session_ResumeSink_NoPipeline, session); } #ifdef WIN32 DWORD bytesRead; // To Resume, we need to ReadFile, then re-add our waithandle BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)); if (result == TRUE || GetLastError() == ERROR_IO_PENDING) { ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink); } #endif } #ifdef WIN32 BOOL ILibDuktape_HECI_Session_ReceiveSink(HANDLE event, void* user) { ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)user; DWORD bytesRead; do { if (GetOverlappedResult(session->descriptor, &(session->v), &bytesRead, FALSE) == FALSE) { break; } ILibDuktape_DuplexStream_WriteData(session->stream, session->buffer, bytesRead); } while (session->stream->readableStream->paused == 0 && ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)) == TRUE); if (session->stream->readableStream->paused == 0 && GetLastError() != ERROR_IO_PENDING) { // Broken Connection ILibProcessPipe_WaitHandle_Remove(session->mgr, session->v.hEvent); // Remove ourselves from processing loop ILibDuktape_DuplexStream_WriteEnd(session->stream); } return(TRUE); } void __stdcall ILibDuktape_HECI_Session_Start(ULONG_PTR obj) { ILibDuktape_HECI_Session *session = (ILibDuktape_HECI_Session*)obj; DWORD bytesRead; BOOL result = ReadFile(session->descriptor, session->buffer, (DWORD)session->bufferSize, &bytesRead, &(session->v)); ILibProcessPipe_WaitHandle_Add(session->mgr, session->v.hEvent, session, ILibDuktape_HECI_Session_ReceiveSink); } #endif duk_ret_t ILibDuktape_HECI_create_OnClientConnect(duk_context *ctx) { int statusCode = duk_require_int(ctx, 0); ILibDuktape_HECI_Session *session = NULL; duk_dup(ctx, 2); // [Session] if (statusCode != 0) { duk_get_prop_string(ctx, -1, "emit"); // [session][emit] duk_swap_top(ctx, -2); // [emit][this] duk_push_string(ctx, "error"); // [emit][this][error] duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error [%d]", statusCode); // [emit][this][error][err] if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } duk_pop(ctx); // ... } else { duk_size_t bufferLen; char *buffer = (char*)Duktape_GetBuffer(ctx, 1, &bufferLen); if (bufferLen > 4) { duk_push_int(ctx, ((int*)buffer)[0]); duk_put_prop_string(ctx, -2, ILibDuktape_HECI_MaxBufferSize); // [session] duk_push_fixed_buffer(ctx, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); // [session][buffer] session = (ILibDuktape_HECI_Session*)Duktape_GetBuffer(ctx, -1, NULL); memset(session, 0, sizeof(ILibDuktape_HECI_Session) + ((int*)buffer)[0]); duk_put_prop_string(ctx, -2, ILibDuktape_HECI_SessionMemPtr); // [session] #ifdef WIN32 session->v.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); session->wv.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); #endif session->chain = Duktape_GetChain(ctx); session->bufferSize = (duk_size_t)((int*)buffer)[0]; session->stream = ILibDuktape_DuplexStream_Init(ctx, ILibDuktape_HECI_Session_WriteSink, ILibDuktape_HECI_Session_EndSink, ILibDuktape_HECI_Session_PauseSink, ILibDuktape_HECI_Session_ResumeSink, session); ILibDuktape_CreateReadonlyProperty_int(ctx, "maxBufferSize", (int)session->bufferSize); session->PendingWrites = ILibQueue_Create(); duk_push_current_function(ctx); session->noPipelining = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Session_NoPipeline, 0); duk_pop(ctx); #ifdef _POSIX //printf("Session: %p\n", session); duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [session][heci] duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChainLink); // [session][heci][link] HECI_chainLink *link = (HECI_chainLink*)duk_get_pointer(ctx, -1); link->session = session; duk_pop_2(ctx); // [session] #endif //printf("NoPipeline: %d\n", session->noPipelining); } else { // Even tho it was a success, the result buffer is invalid duk_get_prop_string(ctx, -1, "emit"); // [session][emit] duk_swap_top(ctx, -2); // [emit][this] duk_push_string(ctx, "error"); // [emit][this][error] duk_push_error_object(ctx, DUK_ERR_ERROR, "HECI Connection Error"); // [emit][this][error][err] if (duk_pcall_method(ctx, 2) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onError(): "); } duk_pop(ctx); // ... } } if (session != NULL) { // Hookup the Send/Receive logic #ifdef WIN32 duk_push_this(ctx); // [HECI] session->descriptor = (HANDLE)Duktape_GetPointerProperty(ctx, -1, ILibDuktape_HECI_Descriptor); duk_get_prop_string(ctx, -1, ILibDuktape_HECI_ChildProcess); // [HECI][childProcess] duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [HECI][childProcess][manager] session->mgr = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1); QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_Session_Start, ILibProcessPipe_Manager_GetWorkerThread(session->mgr), (ULONG_PTR)session); #else duk_push_this(ctx); // [HECI] session->descriptor = Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_HECI_Descriptor, -1); ILibForceUnBlockChain(session->chain); #endif duk_dup(ctx, 2); duk_get_prop_string(ctx, -1, "emit"); // [session][emit] duk_swap_top(ctx, -2); // [emit][this] duk_push_string(ctx, "connect"); // [emit][this][connect] if (duk_pcall_method(ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(ctx, "HECI.session.onConnect(): "); } duk_pop(ctx); // ... } return(0); } duk_ret_t ILibDuktape_HECI_Session_connect(duk_context *ctx) { int i; int nargs = duk_get_top(ctx); duk_push_this(ctx); // [Session] duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Child); // [Session][HECI] duk_remove(ctx, -2); // [HECI] duk_get_prop_string(ctx, -1, "doIoctl"); // [HECI][func] duk_swap_top(ctx, -2); // [doIoctl][this] duk_get_prop_string(ctx, -1, "IOCTL"); // [doIoctl][this][IOCTL] duk_get_prop_string(ctx, -1, "CLIENT_CONNECT"); // [doIoctl][this][IOCTL][CLIENT_CONNECT] duk_remove(ctx, -2); // [doIoctl][this][CLIENT_CONNECT] duk_dup(ctx, 0); // [doIoctl][this][CLIENT_CONNECT][guid] duk_push_fixed_buffer(ctx, 16); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer] duk_push_c_function(ctx, ILibDuktape_HECI_create_OnClientConnect, DUK_VARARGS); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback] duk_push_int(ctx, 0); duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline); for (i = 1; i < nargs; ++i) { if (duk_is_function(ctx, i)) { ILibDuktape_EventEmitter_AddOnce(ILibDuktape_EventEmitter_GetEmitter_fromThis(ctx), "connect", duk_require_heapptr(ctx, i)); } else if (duk_is_object(ctx, i)) { int noPipeline = Duktape_GetIntPropertyValue(ctx, i, "noPipeline", 0); duk_push_int(ctx, noPipeline); duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Session_NoPipeline); } } duk_push_this(ctx); // [doIoctl][this][CLIENT_CONNECT][guid][outBuffer][callback][Session] duk_call_method(ctx, 5); // [retVal] duk_pop(ctx); // ... return(0); } duk_ret_t ILibDuktape_HECI_create(duk_context *ctx) { duk_push_object(ctx); // [Session] ILibDuktape_HECI_Push(ctx, NULL); // [Session][HECI] duk_dup(ctx, -2); // [Session][HECI][Session] duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Parent); // [Session][HECI] duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Child); // [Session] ILibDuktape_EventEmitter *emitter = ILibDuktape_EventEmitter_Create(ctx); ILibDuktape_EventEmitter_CreateEventEx(emitter, "connect"); ILibDuktape_EventEmitter_CreateEventEx(emitter, "error"); ILibDuktape_CreateProperty_InstanceMethod(ctx, "connect", ILibDuktape_HECI_Session_connect, DUK_VARARGS); ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_SessionFinalizer); return(1); } void ILibDuktape_HECI_IoctlHandler_Dispatch(void *chain, void *user) { ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user; duk_size_t count; int i; duk_context *ctx = data->ctx; duk_push_heapptr(data->ctx, data->data); // [array] duk_push_heapptr(data->ctx, data->heciObject); // [array][heci] duk_get_prop_index(data->ctx, -2, 2); // [array][heci][callback] duk_swap_top(data->ctx, -2); // [array][callback][this] count = duk_get_length(data->ctx, -3); duk_push_int(data->ctx, data->code); // [array][callback][this][status] duk_get_prop_index(data->ctx, -4, 1); // [array][callback][this][status][buffer] for (i = 3; i < (int)count; ++i) { duk_get_prop_index(data->ctx, -i - 2, i); // [array][callback][this][status][buffer][...args...] } if (duk_pcall_method(data->ctx, (duk_idx_t)count - 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "heci.ioctlHandler_Dispatch.callback(): "); } duk_pop_2(data->ctx); // ... duk_push_heapptr(data->ctx, data->heciObject); // [heci] ILibDuktape_Push_ObjectStash(data->ctx); // [heci][stash] duk_del_prop_string(data->ctx, -1, Duktape_GetStashKey(data->data)); // (This will free data internally) duk_pop_2(ctx); // ... } #ifdef WIN32 void ILibDuktape_HECI_NextIoctl(ILibQueue q); BOOL ILibDuktape_HECI_IoctlHandler(HANDLE h, void *user) { ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)user; ILibQueue Q = data->Q; BOOL result = GetOverlappedResult(data->device, &(data->v), &(data->bytesReceived), FALSE); ILibQueue_DeQueue(data->Q); ILibProcessPipe_WaitHandle_Remove(data->pipeManager, h); data->code = result == TRUE ? 0 : (int)GetLastError(); ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_HECI_IoctlHandler_Dispatch, data); if (ILibQueue_GetCount(Q) > 0) { void ILibDuktape_HECI_NextIoctl(Q); } return(TRUE); } void ILibDuktape_HECI_NextIoctl(ILibQueue q) { ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_PeekQueue(q); int res; data->bytesReceived = 0; ResetEvent(data->v.hEvent); res = DeviceIoControl(data->device, (DWORD)data->code, data->buffer, (DWORD)data->bufferLen, data->outBuffer, (DWORD)data->outBufferLen, &(data->bytesReceived), &(data->v)); ILibProcessPipe_WaitHandle_Add(data->pipeManager, data->v.hEvent, data, ILibDuktape_HECI_IoctlHandler); } void __stdcall ILibDuktape_HECI_apc_AddIoctl(ULONG_PTR obj) { ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)obj; ILibQueue_EnQueue(data->Q, data); if (ILibQueue_GetCount(data->Q) == 1) { ILibDuktape_HECI_NextIoctl(data->Q); } } #endif #ifdef _POSIX void ILibDuktape_HECI_AddIoctl(ILibDuktape_HECI_ioctl_data *data) { ILibQueue_EnQueue(data->Q, data); if (ILibQueue_GetCount(data->Q) == 1) { ILibForceUnBlockChain(data->chain); } } #endif duk_ret_t ILibDuktape_HECI_doIoctl(duk_context *ctx) { int code = duk_require_int(ctx, 0); duk_size_t bufferLen = 0; char *buffer = duk_is_null(ctx, 1) ? NULL : (char*)Duktape_GetBuffer(ctx, 1, &bufferLen); int nargs = duk_get_top(ctx); int i; ILibQueue Q; duk_size_t outBufferLen; char *outBuffer; int cbx; if (duk_is_buffer(ctx, 2) || duk_is_buffer_data(ctx, 2)) { outBuffer = (char*)Duktape_GetBuffer(ctx, 2, &outBufferLen); cbx = 3; } else { outBuffer = NULL; outBufferLen = 0; cbx = 2; } #ifdef _POSIX if (outBuffer == NULL) { outBuffer = buffer; outBufferLen = bufferLen; } else { if (bufferLen < outBufferLen) { return(ILibDuktape_Error(ctx, "HECI.doIoctl(): Output Buffer too small")); } memcpy_s(outBuffer, outBufferLen, buffer, bufferLen); } #endif duk_require_function(ctx, cbx); duk_push_this(ctx); // [heci] duk_get_prop_string(ctx, -1, ILibDuktape_HECI_Q); // [heci][q] Q = (ILibQueue)duk_get_pointer(ctx, -1); duk_pop(ctx); // [heci] ILibDuktape_Push_ObjectStash(ctx); // [heci][stash] duk_push_array(ctx); // [heci][stash][array] ILibDuktape_HECI_ioctl_data *data; duk_push_fixed_buffer(ctx, bufferLen + sizeof(ILibDuktape_HECI_ioctl_data)); // [heci][stash][array][state] data = (ILibDuktape_HECI_ioctl_data*)Duktape_GetBuffer(ctx, -1, NULL); memset(data, 0, sizeof(ILibDuktape_HECI_ioctl_data)); duk_put_prop_index(ctx, -2, 0); // [heci][stash][array] if (outBufferLen > 0) { // [heci][stash][array][buffer] duk_dup(ctx, 2); } else { duk_push_null(ctx); // [heci][stash][array][buffer] } duk_put_prop_index(ctx, -2, 1); // [heci][stash][array] data->ctx = ctx; duk_dup(ctx, cbx); // [heci][stash][array][callback] duk_put_prop_index(ctx, -2, 2); // [heci][stash][array] #ifdef WIN32 duk_get_prop_string(ctx, -3, ILibDuktape_HECI_IoctlWaitHandle); // [heci][stash][array][handle] data->v.hEvent = (HANDLE)duk_get_pointer(ctx, -1); duk_pop(ctx); // [heci][stash][array] #endif duk_get_prop_string(ctx, -3, ILibDuktape_HECI_Descriptor); // [heci][stash][array][descriptor] #ifdef WIN32 data->device = (HANDLE)duk_get_pointer(ctx, -1); #elif defined(_POSIX) data->device = duk_get_int(ctx, -1); #endif duk_pop(ctx); // [heci][stash][array] data->chain = Duktape_GetChain(ctx); data->Q = Q; data->code = code; data->outBuffer = outBuffer; data->outBufferLen = outBufferLen; data->heciObject = duk_get_heapptr(ctx, -3); data->bufferLen = bufferLen; data->data = duk_get_heapptr(ctx, -1); memcpy_s(data->buffer, bufferLen, buffer, bufferLen); for (i = cbx + 1; i < nargs; ++i) { duk_dup(ctx, i); // [heci][stash][array][object] duk_put_prop_index(ctx, -2, i-1); // [heci][stash][array] } duk_put_prop_string(ctx, -2, Duktape_GetStashKey(duk_get_heapptr(ctx, -1))); // [heci][stash] #ifdef WIN32 duk_get_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [heci][stash][childProcess] duk_get_prop_string(ctx, -1, ILibDuktape_ChildProcess_Manager); // [heci][stash][childProcess][manager] data->pipeManager = (ILibProcessPipe_Manager)duk_get_pointer(ctx, -1); QueueUserAPC((PAPCFUNC)ILibDuktape_HECI_apc_AddIoctl, ILibProcessPipe_Manager_GetWorkerThread(data->pipeManager), (ULONG_PTR)data); #elif defined(_POSIX) ILibDuktape_HECI_AddIoctl(data); #endif return(0); } duk_ret_t ILibDuktape_HECI_Finalizer(duk_context *ctx) { #ifdef WIN32 HANDLE h = Duktape_GetPointerProperty(ctx, 0, ILibDuktape_HECI_IoctlWaitHandle); if (h != NULL) { CloseHandle(h); } #endif if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_Q)) { duk_get_prop_string(ctx, 0, ILibDuktape_HECI_Q); ILibQueue_Destroy((ILibQueue)duk_get_pointer(ctx, -1)); } #ifdef _POSIX if (duk_has_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink)) { duk_get_prop_string(ctx, 0, ILibDuktape_HECI_ChainLink); HECI_chainLink *h = (HECI_chainLink*)duk_get_pointer(ctx, -1); h->ctx = NULL; h->heciObject = NULL; ILibChain_SafeRemove(h->link.ParentChain, h); } #endif return(0); } #ifndef WIN32 void ILibDuktape_HECI_PreSelect(void* object, fd_set *readset, fd_set *writeset, fd_set *errorset, int* blocktime) { int result; HECI_chainLink *h = (HECI_chainLink*)object; //printf("h = %p, descriptor = %d, paused = %d, session = %p\n", (void*)h, h->descriptor, h->paused, (void*)h->session); if (h->descriptor <= 0) { return; } if (h->paused == 0 && h->session != NULL) { FD_SET(h->descriptor, readset); } if (h->session != NULL && ILibQueue_GetCount(h->session->PendingWrites) > 0) { FD_SET(h->descriptor, writeset); } while (ILibQueue_GetCount(h->Q) > 0 && h->paused == 0) { ILibDuktape_HECI_ioctl_data *data = (ILibDuktape_HECI_ioctl_data*)ILibQueue_DeQueue(h->Q); switch (data->code) { case 0x00: break; case 0x01: case 0x02: case 0x03: result = ioctl(h->descriptor, _IOC(_IOC_READ | _IOC_WRITE, 'H', data->code, data->outBufferLen), data->outBuffer); data->code = result ? errno : 0; ILibDuktape_HECI_IoctlHandler_Dispatch(NULL, data); break; default: break; } } } void ILibDuktape_HECI_PostSelect(void* object, int slct, fd_set *readset, fd_set *writeset, fd_set *errorset) { HECI_chainLink *h = (HECI_chainLink*)object; if (h->descriptor <= 0) { return; } if (FD_ISSET(h->descriptor, readset)) { //printf("session = %p\n", (void*)h->session); //printf("Attempting to read: %d bytes from %p\n", h->session->bufferSize, (void*)h->session->buffer); int bytesRead = read(h->descriptor, h->session->buffer, h->session->bufferSize); if (bytesRead >= 0) { ILibDuktape_DuplexStream_WriteData(h->session->stream, h->session->buffer, bytesRead); } else { ILibDuktape_DuplexStream_WriteEnd(h->session->stream); } } if (FD_ISSET(h->descriptor, writeset)) { printf("Writeset\n"); } } void ILibDuktape_HECI_Destroy(void *object) { HECI_chainLink *h = (HECI_chainLink*)object; if (h->ctx != NULL && h->heciObject != NULL) { duk_push_heapptr(h->ctx, h->heciObject); // [heci] duk_del_prop_string(h->ctx, -1, ILibDuktape_HECI_ChainLink); duk_pop(h->ctx); // ... } close(h->descriptor); } #endif void ILibDuktape_HECI_Push(duk_context *ctx, void *chain) { duk_push_object(ctx); // [HECI] ILibDuktape_CreateFinalizer(ctx, ILibDuktape_HECI_Finalizer); #ifdef WIN32 HANDLE h = ILibDuktape_HECI_windowsInit(); if (h == NULL) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); } duk_push_pointer(ctx, h); // [HECI][HANDLE] duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI] if (duk_peval_string(ctx, "require('child_process');") != 0) // [HECI][child_process] { duk_push_string(ctx, "Error instantiating dependency 'child_process'"); duk_throw(ctx); return; } duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChildProcess); // [HECI] duk_push_pointer(ctx, CreateEvent(NULL, TRUE, FALSE, NULL)); duk_put_prop_string(ctx, -2, ILibDuktape_HECI_IoctlWaitHandle); // [HECI] #elif defined(_POSIX) int h = ILibDuktape_HECI_linuxInit(); if (h < 0) { duk_push_string(ctx, "error initializing HECI"); duk_throw(ctx); } duk_push_int(ctx, h); // [HECI][descriptor] duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Descriptor); // [HECI] HECI_chainLink *hlink = ILibMemory_Allocate(sizeof(HECI_chainLink), 0, NULL, NULL); hlink->ctx = ctx; hlink->descriptor = h; hlink->link.PreSelectHandler = ILibDuktape_HECI_PreSelect; hlink->link.PostSelectHandler = ILibDuktape_HECI_PostSelect; hlink->link.DestroyHandler = ILibDuktape_HECI_Destroy; hlink->Q = ILibQueue_Create(); duk_push_pointer(ctx, hlink); // [HECI][link] duk_put_prop_string(ctx, -2, ILibDuktape_HECI_ChainLink); // [HECI] ILibChain_SafeAdd(Duktape_GetChain(ctx), hlink); #endif if (chain != NULL) { ILibDuktape_CreateInstanceMethod(ctx, "create", ILibDuktape_HECI_create, 0); } ILibDuktape_CreateInstanceMethod(ctx, "doIoctl", ILibDuktape_HECI_doIoctl, DUK_VARARGS); #ifdef _POSIX duk_push_pointer(ctx, hlink->Q); // [HECI][Q] #else duk_push_pointer(ctx, ILibQueue_Create()); // [HECI][Q] #endif duk_put_prop_string(ctx, -2, ILibDuktape_HECI_Q); // [HECI] duk_push_object(ctx); #ifdef WIN32 ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)(CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS))); ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)(CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS))); #elif defined(_POSIX) ILibDuktape_CreateReadonlyProperty_int(ctx, "HECI_VERSION", (int)0x00); ILibDuktape_CreateReadonlyProperty_int(ctx, "CLIENT_CONNECT", (int)0x01); #endif ILibDuktape_CreateReadonlyProperty(ctx, "IOCTL"); duk_push_object(ctx); duk_peval_string(ctx, "Buffer.from('DBA4336776047B4EB3AFBCFC29BEE7A7', 'hex');"); ILibDuktape_CreateReadonlyProperty(ctx, "LME"); duk_peval_string(ctx, "Buffer.from('2800F812B7B42D4BACA846E0FF65814C', 'hex');"); ILibDuktape_CreateReadonlyProperty(ctx, "AMT"); ILibDuktape_CreateReadonlyProperty(ctx, "GUIDS"); } // KLOCKWORK: We are not losing reference to created Event... It is freed in the object finalizer 'ILibDuktape_HECI_Finalizer' void ILibDuktape_HECI_Init(duk_context *ctx) { ILibDuktape_ModSearch_AddHandler(ctx, "heci", ILibDuktape_HECI_Push); }