/********************************************************************* * Copyright (c) Intel Corporation 2011 - 2020 * SPDX-License-Identifier: Apache-2.0 **********************************************************************/ /* 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