/* INTEL CONFIDENTIAL * Copyright 2011 - 2019 Intel Corporation. * This software and the related documents are Intel copyrighted materials, and your use of them is governed by the express license under which they were provided to you ("License"). Unless the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose or transmit this software or the related documents without Intel's prior written permission. * This software and the related documents are provided as is, with no express or implied warranties, other than those that are expressly stated in the License. */ /* Real LMS code can be found here: http://software.intel.com/en-us/articles/download-the-latest-intel-amt-open-source-drivers */ #if !defined(_NOHECI) #if defined(WIN32) && !defined(_MINCORE) #define _CRTDBG_MAP_ALLOC #include #include #else #include #endif #if defined(WINSOCK2) #include #include #elif defined(WINSOCK1) #include #include #endif #include "ILibParsers.h" #include "ILibAsyncSocket.h" #include "ILibAsyncServerSocket.h" #ifdef WIN32 #include "../heci/heciwin.h" #include "WinBase.h" #endif #ifdef _POSIX #include "../heci/HECILinux.h" #define UNREFERENCED_PARAMETER(P) (P) #endif #include "../heci/PTHICommand.h" #include "../heci/LMEConnection.h" #define INET_SOCKADDR_LENGTH(x) ((x==AF_INET6?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in))) #define LMS_MAX_CONNECTIONS 16 // Maximum is 32 #define LMS_MAX_SESSIONS 32 // #define _DEBUGLMS #ifdef _DEBUGLMS #define LMSDEBUG(...) printf(__VA_ARGS__); #else #define LMSDEBUG(...) #endif char * g_SelfLMSDir=NULL; int LastId = 2; struct ILibLMS_StateModule* IlibExternLMS = NULL; // Glocal pointer to the LMS module. Since we can only have one of these modules running, a global pointer is sometimes useful. void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i); void ILibLMS_LaunchHoldingSessions(struct ILibLMS_StateModule* module); // Each LMS session can be in one of these states. enum LME_CHANNEL_STATUS { LME_CS_FREE = 0, // The session slot is free, can be used at any time. LME_CS_PENDING_CONNECT = 1, // A connection as been made to LMS and a notice has been send to Intel AMT asking for a CHANNEL OPEN. LME_CS_CONNECTED = 2, // Intel AMT confirmed the connection and data can flow in both directions. LME_CS_PENDING_LMS_DISCONNECT = 3, // The connection to LMS was disconnected, Intel AMT has been notified and we are waitting for Intel AMT confirmation. LME_CS_PENDING_AMT_DISCONNECT = 4, // Intel AMT decided to close the connection. We are disconnecting the LMS TCP connection. LME_CS_HOLDING = 5, // We got too much data from the LMS TCP connection, more than Intel AMT can handle. We are holding the connection until AMT can handle more. LME_CS_CONNECTION_WAIT = 6 // A TCP connection to LMS was made, but there all LMS connections are currently busy. Waitting for one to free up. }; // This is the structure for a session struct LMEChannel { int ourid; // The iddentifier of this channel on our side, this is the same as the index in the "Channel Sessions[LMS_MAX_SESSIONS];" array below. int amtid; // The Intel AMT identifier for this channel. enum LME_CHANNEL_STATUS status; // Current status of the channel. int sockettype; // Type of connected socket: 0 = TCP, 1 = WebSocket void* socketmodule; // The microstack associated with the LMS connection. int txwindow; // Transmit window. int rxwindow; // Receive window. unsigned short localport; // The Intel AMT connection port. int errorcount; // Open channel error count. char* pending; // Buffer pointing to pending data that needs to be sent to Intel AMT, used for websocket only. int pendingcount; // Buffer pointing to pending data size that needs to be sent to Intel AMT, used for websocket only. int pendingptr; // Pointer to start of the pending buffer. }; enum LME_VERSION_HANDSHAKING { LME_NOT_INITIATED, LME_INITIATED, LME_AGREED }; enum LME_SERVICE_STATUS { LME_NOT_STARTED, LME_STARTED }; // This is the Intel AMT LMS chain module structure struct ILibLMS_StateModule { ILibChain_Link ChainLink; void *Server1; // Microstack TCP server for port 16992 void *Server2; // Microstack TCP server for port 16993 void *ControlSocket; // Pointer to the LMS control web socket, NULL if not connected. struct LMEConnection MeConnection; // The MEI connection structure struct LMEChannel Sessions[LMS_MAX_SESSIONS]; // List of sessions //ILibWebServer_ServerToken WebServer; // LMS Web Server enum LME_VERSION_HANDSHAKING handshakingStatus; enum LME_SERVICE_STATUS pfwdService; unsigned int AmtProtVersionMajor; // Intel AMT MEI major version unsigned int AmtProtVersionMinor; // Intel AMT MEI minor version sem_t Lock; // Global lock, this is needed because MEI listener is a different thread from the microstack thread }; /* // Convert a block of data to HEX // The "out" must have (len*2)+1 free space. char utils_HexTable[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; char* __fastcall util_tohex(char* data, int len, char* out) { int i; char *p = out; if (data == NULL || len == 0) { *p = 0; return NULL; } for (i = 0; i < len; i++) { *(p++) = utils_HexTable[((unsigned char)data[i]) >> 4]; *(p++) = utils_HexTable[((unsigned char)data[i]) & 0x0F]; } *p = 0; return out; } */ int GetFreeSlot(struct ILibLMS_StateModule* module){ int i; int cur = LastId; // Look for an empty session for (i = 0; i < LMS_MAX_SESSIONS-3; i++) { cur++; if (cur >= LMS_MAX_SESSIONS){ cur = 3; } if (module->Sessions[cur].status == LME_CS_FREE) { LastId = cur; return cur; } } return -1; } //int GetFreeSlot(struct ILibLMS_StateModule* module){ // int i; // for (i = 0; i < LMS_MAX_SESSIONS - 3; i++) { // if (cur >= LMS_MAX_SESSIONS){ // cur = 3; // } // if (module->Sessions[cur].status == LME_CS_FREE) { // LastId = cur; // return cur; // } // } // return -1; //} // This function is called each time a MEI message is received from Intel AMT void ILibLMS_MEICallback(struct LMEConnection* lmemodule, void *param, void *rxBuffer, unsigned int len) { struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)param; int disconnects = 0; // This is a counter of how many sessions are freed up. We use this at the end to place HOLDING sessions into free slots. //ILibAsyncServerSocket_ServerModule newModule; //ILibLinkedList list; //void* node; //ILibTransport* curModule; // Happens when the chain is being destroyed, don't call anything chain related. if (rxBuffer == NULL) return; sem_wait(&(module->Lock)); LMSDEBUG("ILibLMS_MEICallback %d\r\n", ((unsigned char*)rxBuffer)[0]); switch (((unsigned char*)rxBuffer)[0]) { case APF_GLOBAL_REQUEST: // 80 { int request = 0; unsigned char *pCurrent; APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; pHeader->StringLength = ntohl(pHeader->StringLength); if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST)) == 0) { request = 1; } else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST)) == 0) { request = 2; } else if (pHeader->StringLength == APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO) && memcmp(pHeader->String, APF_GLOBAL_REQUEST_STR_UDP_SEND_TO, APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_UDP_SEND_TO)) == 0) { request = 3; } if (request == 1 || request == 2) { int port = 0; unsigned int len2; unsigned int bytesRead = len; unsigned int hsize=0; if (request==1) hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_REQUEST) + sizeof(UINT8); else if (request==2) hsize = sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_GLOBAL_REQUEST_STR_TCP_FORWARD_CANCEL_REQUEST) + sizeof(UINT8); pCurrent = (unsigned char*)rxBuffer + hsize; bytesRead -= hsize; if (bytesRead < sizeof(unsigned int)) { LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } len2 = ntohl(*((unsigned int *)pCurrent)); pCurrent += sizeof(unsigned int); if (bytesRead < (sizeof(unsigned int) + len2 + sizeof(unsigned int))) { LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } // addr = (char*)pCurrent; pCurrent += len2; port = ntohl(*((unsigned int *)pCurrent)); if (request == 1) { //newModule = ILibCreateAsyncServerSocketModule(module->Chain, LMS_MAX_CONNECTIONS, port, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); //if (newModule){ // ILibAsyncServerSocket_SetTag(newModule, module); // LME_TcpForwardReplySuccess(lmemodule, port); //} //else{ // LME_TcpForwardReplyFailure(lmemodule); //} // New forwarding request if (port == 16992 || port == 16993) LME_TcpForwardReplySuccess(lmemodule, port); else LME_TcpForwardReplyFailure(lmemodule); } else { // Cancel a forwarding request //list = ILibGetModuleList(module->Chain); //node = ILibLinkedList_GetNode_Head(list); //while (node != NULL){ // curModule = (ILibTransport*)ILibLinkedList_GetDataFromNode(node); // if (curModule->IdentifierFlags == ILibTransports_ILibAsyncServerSocket){ // if (ILibAsyncServerSocket_GetPortNumber(curModule)){ // ILibAsyncServerSocket_Close(curModule); // } // } // node = ILibLinkedList_GetNextNode(node); //} LME_TcpForwardCancelReplySuccess(lmemodule); } } else if (request == 3) { // Send a UDP packet // TODO: Send UDP } else{ // do nothing } } break; case APF_CHANNEL_OPEN: // (90) Sent by Intel AMT when a channel needs to be open from Intel AMT. This is not common, but WSMAN events are a good example of channel coming from AMT. { APF_GENERIC_HEADER *pHeader = (APF_GENERIC_HEADER *)rxBuffer; if (pHeader->StringLength == APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT) && memcmp((char*)pHeader->String, APF_OPEN_CHANNEL_REQUEST_DIRECT, APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT)) == 0) { unsigned int len2; unsigned char *pCurrent; struct LMEChannelOpenRequestMessage channelOpenRequest; if (len < sizeof(APF_GENERIC_HEADER) + ntohl(pHeader->StringLength) + 7 + (5 * sizeof(UINT32))) { LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } pCurrent = (unsigned char*)rxBuffer + sizeof(APF_GENERIC_HEADER) + APF_STR_SIZE_OF(APF_OPEN_CHANNEL_REQUEST_DIRECT); channelOpenRequest.ChannelType = APF_CHANNEL_DIRECT; channelOpenRequest.SenderChannel = ntohl(*((UINT32 *)pCurrent)); pCurrent += sizeof(UINT32); channelOpenRequest.InitialWindow = ntohl(*((UINT32 *)pCurrent)); pCurrent += 2 * sizeof(UINT32); len2 = ntohl(*((UINT32 *)pCurrent)); pCurrent += sizeof(UINT32); channelOpenRequest.Address = (char*)pCurrent; pCurrent += len2; channelOpenRequest.Port = ntohl(*((UINT32 *)pCurrent)); pCurrent += sizeof(UINT32); LME_ChannelOpenReplyFailure(lmemodule, channelOpenRequest.SenderChannel, OPEN_FAILURE_REASON_CONNECT_FAILED); } } break; case APF_DISCONNECT: // (1) Intel AMT wants to completely disconnect. Not sure when this happens. { // First, we decode the message. struct LMEDisconnectMessage disconnectMessage; disconnectMessage.MessageType = APF_DISCONNECT; disconnectMessage.ReasonCode = ((APF_DISCONNECT_REASON_CODE)ntohl(((APF_DISCONNECT_MESSAGE *)rxBuffer)->ReasonCode)); LMSDEBUG("LME requested to disconnect with reason code 0x%08x\r\n", disconnectMessage.ReasonCode); LME_Deinit(lmemodule); } break; case APF_SERVICE_REQUEST: // (5) { int service = 0; APF_SERVICE_REQUEST_MESSAGE *pMessage = (APF_SERVICE_REQUEST_MESSAGE *)rxBuffer; pMessage->ServiceNameLength = ntohl(pMessage->ServiceNameLength); if (pMessage->ServiceNameLength == 18) { if (memcmp(pMessage->ServiceName, "pfwd@amt.intel.com", 18) == 0) service = 1; else if (memcmp(pMessage->ServiceName, "auth@amt.intel.com", 18) == 0) service = 2; } if (service > 0) { if (service == 1) { LME_ServiceAccept(lmemodule, "pfwd@amt.intel.com"); module->pfwdService = LME_STARTED; } else if (service == 2) { LME_ServiceAccept(lmemodule, "auth@amt.intel.com"); } } else { LMSDEBUG("APF_SERVICE_REQUEST - APF_DISCONNECT_SERVICE_NOT_AVAILABLE\r\n"); LME_Disconnect(lmemodule, APF_DISCONNECT_SERVICE_NOT_AVAILABLE); LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } } break; case APF_CHANNEL_OPEN_CONFIRMATION: // (91) Intel AMT confirmation to an APF_CHANNEL_OPEN request. { // First, we decode the message. struct LMEChannel* channel; APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *pMessage = (APF_CHANNEL_OPEN_CONFIRMATION_MESSAGE *)rxBuffer; struct LMEChannelOpenReplySuccessMessage channelOpenReply; channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. channelOpenReply.SenderChannel = ntohl(pMessage->SenderChannel); // This is the identifier on the Intel AMT side. channelOpenReply.InitialWindow = ntohl(pMessage->InitialWindowSize); // This is the starting window size for flow control. channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. if (channel == NULL) break; // This should never happen. LMSDEBUG("MEI OPEN OK OUR:%d AMT:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.SenderChannel); if (channel->status == LME_CS_PENDING_CONNECT) // If the channel is in PENDING_CONNECT mode, move the session to connected state. { channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. channel->txwindow = channelOpenReply.InitialWindow; // Set the session txwindow. channel->status = LME_CS_CONNECTED; // Now set the session as CONNECTED. LMSDEBUG("Channel %d now CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. { ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket resume } } else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) // If the channel is in PENDING_DISCONNECT, we have to disconnect the session now. Happens when we disconnect while connection is pending. We don't want to stop a channel during connection, that is bad. { channel->amtid = channelOpenReply.SenderChannel; // We have to set the Intel AMT identifier for this session. LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid); // Send the Intel AMT close. We keep the channel in LME_CS_PENDING_LMS_DISCONNECT state until the close is confirmed. LMSDEBUG("Channel %d now CONNECTED by AMT %d, but CLOSING it now\r\n", channel->ourid, channel->socketmodule); } else { // Here, we get an APF_CHANNEL_OPEN in an unexpected state, this should never happen. LMSDEBUG("Channel %d, unexpected CONNECTED by AMT %d\r\n", channel->ourid, channel->socketmodule); } } break; case APF_CHANNEL_OPEN_FAILURE: // (92) Intel AMT rejected our connection attempt. { // First, we decode the message. struct LMEChannel* channel; APF_CHANNEL_OPEN_FAILURE_MESSAGE *pMessage = (APF_CHANNEL_OPEN_FAILURE_MESSAGE *)rxBuffer; struct LMEChannelOpenReplyFailureMessage channelOpenReply; channelOpenReply.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. channelOpenReply.ReasonCode = (OPEN_FAILURE_REASON)(ntohl(pMessage->ReasonCode)); // Get the error reason code. channel = &(module->Sessions[channelOpenReply.RecipientChannel]); // Get the current session for this message. if (channel == NULL) break; // This should never happen. LMSDEBUG("**OPEN FAIL OUR:%d ERR:%d\r\n", channelOpenReply.RecipientChannel, channelOpenReply.ReasonCode); if (channel->errorcount++ >= 0 || channel->status == LME_CS_PENDING_LMS_DISCONNECT) { // Fail connection channel->status = LME_CS_FREE; LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); sem_post(&(module->Lock)); ILibAsyncSocket_Disconnect(channel->socketmodule); //ILibLMS_LaunchHoldingSessions(module); return; } else { // Try again //ILibLMS_SetupConnection(module, channel->ourid); } } break; case APF_CHANNEL_CLOSE: // (97) Intel AMT is closing this channel, we need to disconnect the LMS TCP connection { // First, we decode the message. struct LMEChannel* channel; APF_CHANNEL_CLOSE_MESSAGE *pMessage = (APF_CHANNEL_CLOSE_MESSAGE *)rxBuffer; struct LMEChannelCloseMessage channelClose; channelClose.RecipientChannel = ntohl(pMessage->RecipientChannel); // This is the identifier on our side. channel = &(module->Sessions[channelClose.RecipientChannel]); // Get the current session for this message. if (channel == NULL) break; // This should never happen. //LMSDEBUG("CLOSE OUR:%d\r\n", channelClose.RecipientChannel); if (channel->status == LME_CS_CONNECTED) { if (ILibAsyncSocket_IsConnected(channel->socketmodule)) { channel->status = LME_CS_PENDING_AMT_DISCONNECT; LMSDEBUG("Channel %d now PENDING_AMT_DISCONNECT by AMT, calling microstack disconnect %d\r\n", channel->ourid, channel->socketmodule); LME_ChannelClose(lmemodule, channel->amtid, channel->ourid); sem_post(&(module->Lock)); if (channel->sockettype == 0) // If the current socket is PAUSED, lets resume it so data can start flowing again. { ILibAsyncSocket_Disconnect(channel->socketmodule); // TCP socket close } sem_wait(&(module->Lock)); channel->status = LME_CS_FREE; disconnects++; LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); } else { channel->status = LME_CS_FREE; disconnects++; LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); } } else if (channel->status == LME_CS_PENDING_LMS_DISCONNECT) { channel->status = LME_CS_FREE; disconnects++; LMSDEBUG("Channel %d now FREE by AMT\r\n", channel->ourid); } else { LMSDEBUG("Channel %d CLOSE, UNEXPECTED STATE %d\r\n", channel->ourid, channel->status); } } break; case APF_CHANNEL_DATA: // (94) Intel AMT is sending data that we must relay into an LMS TCP connection. { //sem_wait(&(module->Lock)); struct LMEChannel* channel; APF_CHANNEL_DATA_MESSAGE *pMessage = (APF_CHANNEL_DATA_MESSAGE *)rxBuffer; struct LMEChannelDataMessage channelData; enum ILibAsyncSocket_SendStatus r; channelData.MessageType = APF_CHANNEL_DATA; channelData.RecipientChannel = ntohl(pMessage->RecipientChannel); channelData.DataLength = ntohl(pMessage->DataLength); channelData.Data = (unsigned char*)rxBuffer + sizeof(APF_CHANNEL_DATA_MESSAGE); channel = &(module->Sessions[channelData.RecipientChannel]); if (channel == NULL || channel->socketmodule == NULL){ sem_post(&(module->Lock)); break; } if (channel->sockettype == 0) { r = ILibAsyncSocket_Send(channel->socketmodule, (char*)(channelData.Data), channelData.DataLength, ILibAsyncSocket_MemoryOwnership_USER); // TCP socket } channel->rxwindow += channelData.DataLength; if (r == ILibAsyncSocket_ALL_DATA_SENT && channel->rxwindow > 1024) { LME_ChannelWindowAdjust(lmemodule, channel->amtid, channel->rxwindow); channel->rxwindow = 0; } //sem_post(&(module->Lock)); } break; case APF_CHANNEL_WINDOW_ADJUST: // 93 { //sem_wait(&(module->Lock)); struct LMEChannel* channel; APF_WINDOW_ADJUST_MESSAGE *pMessage = (APF_WINDOW_ADJUST_MESSAGE *)rxBuffer; struct LMEChannelWindowAdjustMessage channelWindowAdjust; channelWindowAdjust.MessageType = APF_CHANNEL_WINDOW_ADJUST; channelWindowAdjust.RecipientChannel = ntohl(pMessage->RecipientChannel); channelWindowAdjust.BytesToAdd = ntohl(pMessage->BytesToAdd); channel = &(module->Sessions[channelWindowAdjust.RecipientChannel]); if (channel == NULL || channel->status == LME_CS_FREE){ sem_post(&(module->Lock)); break; } channel->txwindow += channelWindowAdjust.BytesToAdd; if (channel->sockettype == 0) { ILibAsyncSocket_Resume(channel->socketmodule); // TCP socket } //sem_post(&(module->Lock)); } break; case APF_PROTOCOLVERSION: // 192 { APF_PROTOCOL_VERSION_MESSAGE *pMessage = (APF_PROTOCOL_VERSION_MESSAGE *)rxBuffer; struct LMEProtocolVersionMessage protVersion; protVersion.MajorVersion = ntohl(pMessage->MajorVersion); protVersion.MinorVersion = ntohl(pMessage->MinorVersion); protVersion.TriggerReason = (APF_TRIGGER_REASON)ntohl(pMessage->TriggerReason); switch (module->handshakingStatus) { case LME_AGREED: case LME_NOT_INITIATED: { LME_ProtocolVersion(lmemodule, 1, 0, protVersion.TriggerReason); } case LME_INITIATED: if (protVersion.MajorVersion != 1 || protVersion.MinorVersion != 0) { LMSDEBUG("LME Version %d.%d is not supported.\r\n", protVersion.MajorVersion, protVersion.MinorVersion); LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED); LME_Deinit(lmemodule); sem_post(&(module->Lock)); return; } module->AmtProtVersionMajor = protVersion.MajorVersion; module->AmtProtVersionMinor = protVersion.MinorVersion; module->handshakingStatus = LME_AGREED; break; default: LME_Disconnect(lmemodule, APF_DISCONNECT_BY_APPLICATION); LME_Deinit(lmemodule); break; } } break; case APF_USERAUTH_REQUEST: // 50 { // _lme.UserAuthSuccess(); } break; default: // Unknown request. LMSDEBUG("**Unknown LME command: %d\r\n", ((unsigned char*)rxBuffer)[0]); LME_Disconnect(lmemodule, APF_DISCONNECT_PROTOCOL_ERROR); LME_Deinit(lmemodule); break; } sem_post(&(module->Lock)); //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); // If disconnects is set to anything, we have free session slots we can fill up. } void ILibLMS_OnReceive(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, char* buffer, int *p_beginPointer, int endPointer, ILibAsyncServerSocket_OnInterrupt *OnInterrupt, void **user, int *PAUSE) { int r, maxread = endPointer; struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); struct LMEChannel* channel = (struct LMEChannel*)*user; UNREFERENCED_PARAMETER( AsyncServerSocketModule ); UNREFERENCED_PARAMETER( ConnectionToken ); UNREFERENCED_PARAMETER( OnInterrupt ); UNREFERENCED_PARAMETER( PAUSE ); if (channel == NULL) return; sem_wait(&(module->Lock)); if (channel->socketmodule != ConnectionToken) { sem_post(&(module->Lock)); ILibAsyncSocket_Disconnect(ConnectionToken); return; } if (channel->txwindow < endPointer) maxread = channel->txwindow; if (channel->status != LME_CS_CONNECTED || maxread == 0) { *PAUSE = 1; sem_post(&(module->Lock)); return; } //printf("%.*s", maxread, buffer); r = LME_ChannelData(&(module->MeConnection), channel->amtid, maxread, (unsigned char*)buffer); //LMSDEBUG("ILibLMS_OnReceive, status = %d, txwindow = %d, endPointer = %d, r = %d\r\n", channel->status, channel->txwindow, endPointer, r); if (r != maxread) { //LMSDEBUG("ILibLMS_OnReceive, DISCONNECT %d\r\n", channel->ourid); sem_post(&(module->Lock)); ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection return; } channel->txwindow -= maxread; *p_beginPointer = maxread; sem_post(&(module->Lock)); } void ILibLMS_SetupConnection(struct ILibLMS_StateModule* module, int i) { int rport = 0; char* laddr = NULL; struct sockaddr_in6 remoteAddress; if (module->Sessions[i].sockettype == 0) { // Fetch the socket remote TCP address ILibAsyncSocket_GetRemoteInterface(module->Sessions[i].socketmodule, (struct sockaddr*)&remoteAddress); if (remoteAddress.sin6_family == AF_INET6) { laddr = "::1"; // TODO: decode this properly into a string rport = ntohs(remoteAddress.sin6_port); } else { laddr = "127.0.0.1"; // TODO: decode this properly into a string rport = ntohs(((struct sockaddr_in*)(&remoteAddress))->sin_port); } } else { // Fetch the socket remote web socket address laddr = "127.0.0.1"; // TODO: decode this properly into a string rport = 123; // TODO: decode the remote port } // Setup a new LME session LME_ChannelOpenForwardedRequest(&(module->MeConnection), (unsigned int)i, laddr, module->Sessions[i].localport, laddr, rport); //LMSDEBUG("ILibLMS_OnReceive, CONNECT %d\r\n", i); } int OnConnect = 0; void ILibLMS_OnConnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void **user) { OnConnect++; LMSDEBUG("OnCOnnect:%d", OnConnect); int i, sessionid = -1, activecount = 0; struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); sem_wait(&(module->Lock)); // Count the number of active sessions activecount = LMS_MAX_SESSIONS; for (i = 0; i < LMS_MAX_SESSIONS; i++) { if (module->Sessions[i].status == LME_CS_FREE || module->Sessions[i].status == LME_CS_CONNECTION_WAIT) { activecount--; } } sessionid=GetFreeSlot(module); // Look for an empty session //for (i = 1; i < LMS_MAX_SESSIONS; i++) { // if (module->Sessions[i].status == LME_CS_FREE) { // sessionid = i; // break; // } //} if (sessionid == -1) { sem_post(&(module->Lock)); LMSDEBUG("ILibLMS_OnConnect NO SESSION SLOTS AVAILABLE\r\n"); ILibAsyncSocket_Disconnect(ConnectionToken); // Drop the connection return; } i = sessionid; // LMSDEBUG("USING SLOT OUR:%d\r\n", i); module->Sessions[i].amtid = -1; module->Sessions[i].ourid = i; module->Sessions[i].sockettype = 0; // TCP Type module->Sessions[i].socketmodule = ConnectionToken; module->Sessions[i].rxwindow = 0; module->Sessions[i].txwindow = 0; module->Sessions[i].localport = ILibAsyncServerSocket_GetPortNumber(AsyncServerSocketModule); module->Sessions[i].errorcount = 0; *user = &(module->Sessions[i]); /* if (activecount >= LMS_MAX_CONNECTIONS) { module->Sessions[i].status = LME_CS_CONNECTION_WAIT; LMSDEBUG("ILibLMS_OnConnect %d (HOLDING)\r\n", i); } else */ { module->Sessions[i].status = LME_CS_PENDING_CONNECT; LMSDEBUG("ILibLMS_OnConnect %d\r\n", i); ILibLMS_SetupConnection(module, i); } sem_post(&(module->Lock)); } void ILibLMS_OnDisconnect(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) { int disconnects = 0; struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); struct LMEChannel* channel = (struct LMEChannel*)user; UNREFERENCED_PARAMETER( ConnectionToken ); sem_wait(&(module->Lock)); if (channel == NULL || channel->socketmodule != ConnectionToken) { LMSDEBUG("****ILibLMS_OnDisconnect EXIT\r\n"); sem_post(&(module->Lock)); return; } LMSDEBUG("ILibLMS_OnDisconnect, Channel %d, %d\r\n", channel->ourid, ConnectionToken); if (channel->status == LME_CS_CONNECTED) { channel->status = LME_CS_PENDING_LMS_DISCONNECT; if (channel->amtid!=-1 && !LME_ChannelClose(&(module->MeConnection), channel->amtid, channel->ourid)) { channel->status = LME_CS_FREE; disconnects++; LMSDEBUG("Channel %d now FREE by LMS because of failed close\r\n", channel->ourid); } else { LMSDEBUG("Channel %d now PENDING_LMS_DISCONNECT by LMS\r\n", channel->ourid); //LMSDEBUG("LME_ChannelClose OK\r\n"); } } else if (channel->status == LME_CS_PENDING_CONNECT) { channel->status = LME_CS_PENDING_LMS_DISCONNECT; LMSDEBUG("Channel %d now PENDING_DISCONNECT by LMS\r\n", channel->ourid); } else if (channel->status == LME_CS_CONNECTION_WAIT) { channel->status = LME_CS_FREE; disconnects++; LMSDEBUG("Channel %d now FREE by LMS\r\n", channel->ourid); } //LMSDEBUG("ILibLMS_OnDisconnect, DISCONNECT %d, status = %d\r\n", channel->ourid, channel->status); sem_post(&(module->Lock)); //if (disconnects > 0) ILibLMS_LaunchHoldingSessions(module); } void ILibLMS_OnSendOK(ILibAsyncServerSocket_ServerModule AsyncServerSocketModule, ILibAsyncServerSocket_ConnectionToken ConnectionToken, void *user) { struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)ILibAsyncServerSocket_GetTag(AsyncServerSocketModule); struct LMEChannel* channel = (struct LMEChannel*)user; UNREFERENCED_PARAMETER( ConnectionToken ); // Ok to send more on this socket, adjust the window sem_wait(&(module->Lock)); if (channel->rxwindow != 0) { //LMSDEBUG("LME_ChannelWindowAdjust id=%d, rxwindow=%d\r\n", channel->amtid, channel->rxwindow); LME_ChannelWindowAdjust(&(module->MeConnection), channel->amtid, channel->rxwindow); channel->rxwindow = 0; } sem_post(&(module->Lock)); } // Private method called when the chain is destroyed, we want to do our cleanup here void ILibLMS_Destroy(void *object) { struct ILibLMS_StateModule* module = (struct ILibLMS_StateModule*)object; UNREFERENCED_PARAMETER( object ); sem_wait(&(module->Lock)); LME_Disconnect(&(module->MeConnection), APF_DISCONNECT_BY_APPLICATION); LME_Exit(&(module->MeConnection)); sem_destroy(&(module->Lock)); if (IlibExternLMS == module) { IlibExternLMS = NULL; } // Clear the global reference to the the LMS module. } // Create a new Reflector module. struct ILibLMS_StateModule *ILibLMS_Create(void *Chain) { struct ILibLMS_StateModule *module; // Allocate the new module module = (struct ILibLMS_StateModule*)malloc(sizeof(struct ILibLMS_StateModule)); if (module == NULL) { ILIBCRITICALEXIT(254); } memset(module, 0, sizeof(struct ILibLMS_StateModule)); // Setup MEI with LMS interface, if we can't return null if (LME_Init(&(module->MeConnection), &ILibLMS_MEICallback, module) == 0) { free(module); return NULL; } // Setup the module module->ChainLink.DestroyHandler = &ILibLMS_Destroy; module->ChainLink.ParentChain = Chain; sem_init(&(module->Lock), 0, 1); // TCP servers module->Server1 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16992, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); if (module->Server1 == NULL) { sem_destroy(&(module->Lock)); free(module); return NULL; } ILibAsyncServerSocket_SetTag(module->Server1, module); module->Server2 = ILibCreateAsyncServerSocketModule(Chain, LMS_MAX_CONNECTIONS, 16993, 4096, 0, &ILibLMS_OnConnect, &ILibLMS_OnDisconnect, &ILibLMS_OnReceive, NULL, &ILibLMS_OnSendOK); if (module->Server2 == NULL) { sem_destroy(&(module->Lock)); free(module->Server1); free(module); return NULL; } ILibAsyncServerSocket_SetTag(module->Server2, module); // TODO: US6986 - The meshcommander site should not be served on localhost:16994. // This code should be commented out - in the future(late 2018) IMC could be put there instead. // Web Server //module->WebServer = ILibWebServer_CreateEx(Chain, 8, 16994, 2, &ILibLMS_WebServer_OnSession, module); IlibExternLMS = module; // Set the global reference to the LMS module. ILibAddToChain(Chain, module); // Add this module to the chain. return module; } #endif