/*********************************************************************** * * Copyright (C) 2006 Novell, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * * Author: Juan Carlos Luciani * ***********************************************************************/ //===[ Include files ]===================================================== #include "ipcint.h" #include "schannel.h" #include "serverreq.h" //===[ External data ]===================================================== //===[ External prototypes ]=============================================== extern int ServiceRequest( ServerReq *pServerReq); extern void RemoveFromSChannelList( SChannel *pSChannel); //===[ Manifest constants ]================================================ // // Socket Mapping definitions // #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define LINGER struct linger #define SOCKADDR_IN struct sockaddr_in #define closesocket close //===[ Type definitions ]================================================== //===[ Function prototypes ]=============================================== //===[ Global variables ]================================================== // // Object Counters // unsigned long numSChannelObjects = 0; //===[ Type definitions ]================================================== //===[ Function prototypes ]=============================================== //===[ Global variables ]================================================== //++======================================================================= SChannel::SChannel( int connSocket) : m_state (State_Connected), m_socket (connSocket) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "SChannel::SChannel- Start, Obj = %08X\n", this); // Initialize the mutex if (pthread_mutex_init(&m_mutex, NULL) != 0) { DbgTrace(0, "SChannel::SChannel- Mutex initialization failed\n", 0); // Throw exception throw bad_alloc(); } // Increment the object count InterlockedIncrement(&numSChannelObjects); DbgTrace(1, "SChannel::SChannel- End\n", 0); } /*-- SChannel::SChannel() --*/ //++======================================================================= SChannel::~SChannel(void) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "SChannel::~SChannel- Start, Obj = %08X\n", this); // Cleanup resources allocated for the object pthread_mutex_destroy(&m_mutex); // Free connection socket if necessary if (m_socket != INVALID_SOCKET) { shutdown(m_socket, SHUT_RDWR); struct linger linger_opt = {1, 15}; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)); closesocket(m_socket); } // Decrement the object count InterlockedDecrement(&numSChannelObjects); DbgTrace(1, "SChannel::~SChannel- End\n", 0); } /*-- SChannel::~SChannel() --*/ //++======================================================================= int SChannel::init(void) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { int retStatus = -1; SmartSChannel *pSmartSChannel = NULL; DbgTrace(1, "SChannel::init- Start, Obj = %08X\n", this); // Verify the state of the object if (m_state == State_Connected) { // Launch a thread to service the channel connection try { // Create a SmartSChannel object to make sure that the object // does not get deleted prematurely. pSmartSChannel = new SmartSChannel(this); // Create the channel connection thread pthread_t thread; int threadCreateStatus = pthread_create(&thread, NULL, (void*(*)(void*))SChannel::connectionThread, pSmartSChannel); if (threadCreateStatus == 0) { // We succeeded retStatus = 0; } else { DbgTrace(0, "SChannel::init- Unable to create channel connection thread, error = %08X\n", threadCreateStatus); } } catch (...) { DbgTrace(0, "SChannel::init- Exception caught creating smart pointer\n", 0); } } else { DbgTrace(0, "SChannel::init- invalid state, state = %d\n", m_state); } // Deal with initialization failures if (retStatus) { // Adjust the object state m_state = State_FailedInitialization; // Free SmartSChannel just in case delete pSmartSChannel; } DbgTrace(1, "SChannel::init- End, status = %08X\n", retStatus); return retStatus; } /*-- SChannel::init() --*/ //++======================================================================= void* SChannel::connectionThread( SmartPtr *pSmartSChannel) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { SChannel *pSChannel = *pSmartSChannel; bool doneReceivingData = false; unsigned long bytesReceived; unsigned long bytesSent; uint32_t reqId; int payloadLength; unsigned long totalPayloadBytesReceived = 0; char reqDataPktHdr[ReqDataPktHdrTemplate.length()]; char reqErrorPktHdr[ReqErrorPktHdrTemplate.length()]; char *pRecvBuff; ServerReq *pServerReq; DbgTrace(1, "SChannel::connectionThread- Start, Obj = %08X\n", pSChannel); // Set the thread in the detached state so that it is cleaned up when it exits pthread_detach(pthread_self()); // Check that we are still connected if (pSChannel->m_state == State_Connected) { // Receive and process channel data while (!doneReceivingData) { DbgTrace(2, "SChannel::connectionThread- Receive Loop, Obj = %08X\n", pSChannel); // Receive the ReqDataPktHdr. Note, if we add other packet types and if the // packet types have different header lengths, then we will need to modify // this code to first receive the packet type and then receive the rest // of the header based on type. while (1) { bytesReceived = recv(pSChannel->m_socket, reqDataPktHdr, ReqDataPktHdrTemplate.length(), MSG_WAITALL); if (bytesReceived != SOCKET_ERROR || errno != EINTR) { break; } } if (bytesReceived != SOCKET_ERROR) { // Check if the connection was terminated if (bytesReceived == ReqDataPktHdrTemplate.length()) { // Get the reqId and payload length if (ChannelProto::getReqIdAndPayloadLength(reqDataPktHdr, sizeof(reqDataPktHdr), &reqId, &payloadLength)) { // Procced based on the packet type switch (ChannelProto::getPktType(*reqDataPktHdr)) { case ChannelProto::ReqDataCarrierPacketType: DbgTrace(2, "SChannel::connectionThread- Processing Request Data Packet, Obj = %08X\n", pSChannel); // Allocate a buffer big enough to receive the payload. Allow space to NULL terminate. pRecvBuff = new char[payloadLength + 1]; if (pRecvBuff != NULL) { pRecvBuff[payloadLength] = '\0'; // Buffer allocated, receive the Req payload. while (1) { bytesReceived = recv(pSChannel->m_socket, pRecvBuff, payloadLength, MSG_WAITALL); if (bytesReceived != SOCKET_ERROR || errno != EINTR) { break; } } if (bytesReceived != SOCKET_ERROR) { // Verify that we received all of the payload if (bytesReceived == payloadLength) { // Received all of the payload data totalPayloadBytesReceived += bytesReceived; // Instantiate ServerReq object bool reqProcessingStartedSuccessfully = false; try { pServerReq = new ServerReq(pSChannel, reqId, pRecvBuff, bytesReceived); } catch (...) { DbgTrace(0, "SChannel::connectionThread- Exception caught creating ServerReq obj\n", 0); } // Acquire exclusive access to the SChannel object pthread_mutex_lock(&pSChannel->m_mutex); if (pServerReq) { // Forget about the receive buffer pRecvBuff = NULL; // Start processing the Request if (ServiceRequest(pServerReq) != 0) { // Failed to start processing of the Request, delete the ServerReq object. DbgTrace(0, "SChannel::connectionThread- StartRequest failed, Obj = %08X\n", pSChannel); delete pServerReq; } else { reqProcessingStartedSuccessfully = true; } } else { //DbgTrace(1, "SChannel::connectionThread- Failed to obtain idle ServerReq, Obj = %08X\n", pSChannel); DbgTrace(0, "SChannel::connectionThread- Failed to obtain idle ServerReq, Obj = %08X\n", pSChannel); } // Check if we must send an Request Error packet back to the client if (reqProcessingStartedSuccessfully == false) { // Build ReqErrorHeader if (ChannelProto::buildReqErrorPktHdr(reqId, 0, reqErrorPktHdr) == 0) { // Packet header was built, now sent it to the client. bytesSent = send(pSChannel->m_socket, reqErrorPktHdr, sizeof(reqErrorPktHdr), MSG_NOSIGNAL); if (bytesSent != sizeof(reqErrorPktHdr)) { DbgTrace(1, "SChannel::connectionThread- Connection aborted prematurely, Obj = %08X\n", pSChannel); //printf("SChannel::connectionThread- 1Connection aborted prematurely, Obj = %08X\n", pSChannel); doneReceivingData = true; } } else { DbgTrace(0, "SChannel::connectionThread- Error building Req End Pkt Header, Obj = %08X\n", pSChannel); } } // Release exclusive access to the SChannel object pthread_mutex_unlock(&pSChannel->m_mutex); } else { DbgTrace(1, "SChannel::connectionThread- Connection aborted prematurely, Obj = %08X\n", pSChannel); //printf("bytesReceived = %d, payloadLength = %d\n", bytesReceived, payloadLength); //printf("SChannel::connectionThread- 2Connection aborted prematurely, Obj = %08X\n", pSChannel); doneReceivingData = true; } } else { DbgTrace(1, "SChannel::connectionThread- Connection aborted prematurely, Obj = %08X\n", pSChannel); //printf("Socket error = %d\n", errno); //printf("SChannel::connectionThread- 3Connection aborted prematurely, Obj = %08X\n", pSChannel); doneReceivingData = true; } // Free receive buffer if necessary if (pRecvBuff) delete[] pRecvBuff; } else { DbgTrace(0, "SChannel::connectionThread- Unable to allocate receive buffer, Obj = %08X\n", pSChannel); doneReceivingData = true; } break; default: DbgTrace(0, "SChannel::connectionThread- Unknown Packet Type, Obj = %08X\n", pSChannel); doneReceivingData = true; break; } } else { DbgTrace(1, "SChannel::connectionThread- Unable to obtain payload length, Obj = %08X\n", pSChannel); doneReceivingData = true; } } else { DbgTrace(1, "SChannel::connectionThread- The channel connection was terminated, Obj = %08X\n", pSChannel); //printf("bytesReceived = %d, expected = %d\n", bytesReceived, ReqDataPktHdrTemplate.length()); //printf("SChannel::connectionThread- 4The channel connection was terminated, Obj = %08X\n", pSChannel); doneReceivingData = true; } } else { DbgTrace(1, "SChannel::connectionThread- The channel connection was aborted, Obj = %08X\n", pSChannel); //printf("Socket error = %d\n", errno); //printf("SChannel::connectionThread- 5The channel connection was aborted, Obj = %08X\n", pSChannel); doneReceivingData = true; } } } // Acquire exclusive access to the SChannel object pthread_mutex_lock(&pSChannel->m_mutex); // Try to change the SChannel state to disconnected if (pSChannel->m_state == State_Connected) pSChannel->m_state = State_Disconnected; // Release exclusive access to the SChannel object pthread_mutex_unlock(&pSChannel->m_mutex); // Remove ourselves from the SChannel list RemoveFromSChannelList(pSChannel); // Free SmartSChannel delete pSmartSChannel; DbgTrace(1, "SChannel::connectionThread- End\n", 0); // Exit pthread_exit(NULL); return 0; // never-reached! } /*-- SChannel::connectionThread() --*/ //++======================================================================= void SChannel::closeChannel(void) // // Arguments: // // Returns: // // Abstract: // // Notes: // // L2 //=======================================================================-- { DbgTrace(1, "SChannel::closeChannel- Start, Obj = %08X\n", this); // Acquire SChannel mutex pthread_mutex_lock(&m_mutex); // Switch the socket state to closed m_state = State_Closed; // Check if we must close the socket if (m_socket != INVALID_SOCKET) { // Socket needs to be closed, this will // release the channel connection thread // if it is active. shutdown(m_socket, SHUT_RDWR); struct linger linger_opt = {1, 15}; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)); closesocket(m_socket); m_socket = INVALID_SOCKET; } // Release SChannel mutex pthread_mutex_unlock(&m_mutex); DbgTrace(1, "SChannel::closeChannel- End\n", 0); } /*-- SChannel::closeChannel() --*/ //++======================================================================= int SChannel::sendReplyData( uint32_t reqId, char *pServerData, int32_t serverDataLen) // // Arguments: // // Returns: // // Abstract: // // Notes: // // Environment: // // L2 //=======================================================================-- { int retStatus = -1; char reqDataPktHdr[ReqDataPktHdrTemplate.length()]; struct msghdr sendmsgHdr = {0}; struct iovec ioVectors[2]; unsigned long bytesSent; unsigned long totalBytesSent = 0; unsigned long bytesToSend = sizeof(reqDataPktHdr) + serverDataLen; DbgTrace(1, "SChannel::sendReplyData- Start, Obj = %08X\n", this); // Acquire exclusive access to the channel object pthread_mutex_lock(&m_mutex); // Verify that the channel is connected if (m_state == State_Connected) { // Build ReqDataHeader if (ChannelProto::buildReqDataPktHdr(reqId, serverDataLen, reqDataPktHdr) == 0) { // Packet header was built, now sent it along with the client data to // the server. ioVectors[0].iov_base = reqDataPktHdr; ioVectors[0].iov_len = sizeof(reqDataPktHdr); ioVectors[1].iov_base = (char*) pServerData; ioVectors[1].iov_len = serverDataLen; sendmsgHdr.msg_iov = ioVectors; sendmsgHdr.msg_iovlen = 2; while (1) { bytesSent = sendmsg(m_socket, &sendmsgHdr, MSG_NOSIGNAL); if (bytesSent == SOCKET_ERROR) { // Check if we were interrupted during the transfer if (errno == EINTR) { // Just try again continue; } // An unrecoverable error was encountered during the send operation, // assume there was a communication failure. Close the socket to make // sure that the connectionThread cleans up. //printf("SChannel::sendReplyData- sendmsgn error, totalBytesSent = %d, bytesToSend = %d, errno = %d\n", totalBytesSent, bytesToSend, errno); DbgTrace(0, "SChannel::sendReplyData- sendmsgn error, errno = %d\n", errno); m_state = State_Disconnected; shutdown(m_socket, SHUT_RDWR); struct linger linger_opt = {1, 15}; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)); closesocket(m_socket); m_socket = INVALID_SOCKET; break; } else { // Account for the bytes sent totalBytesSent += bytesSent; // Check if we are done sending all of the data if (totalBytesSent >= bytesToSend) { // We are done break; } else { // Adjust the ioVector structure to send data not yet sent if (totalBytesSent >= sizeof(reqDataPktHdr)) { // The packet header was sent, use only one ioVector. int serverDataAlreadySent = totalBytesSent - sizeof(reqDataPktHdr); ioVectors[0].iov_base = (char*) pServerData + serverDataAlreadySent; ioVectors[0].iov_len = serverDataLen - serverDataAlreadySent; sendmsgHdr.msg_iov = ioVectors; sendmsgHdr.msg_iovlen = 1; } else { // Not all of the packet header was sent, use two ioVectors. ioVectors[0].iov_base = (char*) reqDataPktHdr + totalBytesSent; ioVectors[0].iov_len = sizeof(reqDataPktHdr) - totalBytesSent; ioVectors[1].iov_base = (char*) pServerData; ioVectors[1].iov_len = serverDataLen; sendmsgHdr.msg_iov = ioVectors; sendmsgHdr.msg_iovlen = 2; } } } } // Return success even if the send failed to allow things to be cleaned up // by the connectionThread routine. retStatus = 0; } else { DbgTrace(0, "SChannel::sendReplyData- Error building Req Data Pkt Header, Obj = %08X\n", this); } } else { DbgTrace(1, "SChannel::sendReplyData- Channel not connected, state = %08X\n", m_state); } // Release exclusive access to the channel object pthread_mutex_unlock(&m_mutex); DbgTrace(1, "SChannel::sendReplyData- End, retStatus = %08X\n", retStatus); return retStatus; } /*-- SChannel::sendReplyData() --*/ //++======================================================================= int SChannel::sendReplyError( uint32_t reqId) // // Arguments: // // Returns: // // Abstract: // // Notes: // // Environment: // // L2 //=======================================================================-- { int retStatus = -1; char reqErrorPktHdr[ReqErrorPktHdrTemplate.length()]; struct msghdr sendmsgHdr = {0}; struct iovec ioVectors[1]; unsigned long bytesSent; unsigned long totalBytesSent = 0; unsigned long bytesToSend = sizeof(reqErrorPktHdr); DbgTrace(1, "SChannel::sendReplyError- Start, Obj = %08X\n", this); // Acquire exclusive access to the channel object pthread_mutex_lock(&m_mutex); // Verify that the channel is connected if (m_state == State_Connected) { // Build ReqErrorHeader if (ChannelProto::buildReqErrorPktHdr(reqId, 0, reqErrorPktHdr) == 0) { // Packet header was built, now sent it along with the client data to // the server. ioVectors[0].iov_base = reqErrorPktHdr; ioVectors[0].iov_len = sizeof(reqErrorPktHdr); sendmsgHdr.msg_iov = ioVectors; sendmsgHdr.msg_iovlen = 1; while (1) { bytesSent = sendmsg(m_socket, &sendmsgHdr, MSG_NOSIGNAL); if (bytesSent == SOCKET_ERROR) { // Check if we were interrupted during the transfer if (errno == EINTR) { // Just try again continue; } // An unrecoverable error was encountered during the send operation, // assume there was a communication failure. Close the socket to make // sure that the connectionThread cleans up. //printf("SChannel::sendReplyError- sendmsgn error, totalBytesSent = %d, bytesToSend = %d, errno = %d\n", totalBytesSent, bytesToSend, errno); DbgTrace(0, "SChannel::sendReplyError- sendmsgn error, errno = %d\n", errno); m_state = State_Disconnected; shutdown(m_socket, SHUT_RDWR); struct linger linger_opt = {1, 15}; setsockopt(m_socket, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)); closesocket(m_socket); m_socket = INVALID_SOCKET; break; } else { // Account for the bytes sent totalBytesSent += bytesSent; // Check if we are done sending all of the data if (totalBytesSent >= bytesToSend) { // We are done break; } else { // Adjust the ioVector structure to send data not yet sent ioVectors[0].iov_base = (char*) reqErrorPktHdr + totalBytesSent; ioVectors[0].iov_len = sizeof(reqErrorPktHdr) - totalBytesSent; sendmsgHdr.msg_iov = ioVectors; sendmsgHdr.msg_iovlen = 1; } } } // Return success even if the send failed to allow things to be cleaned up // by the connectionThread routine. retStatus = 0; } else { DbgTrace(0, "SChannel::sendReplyError- Error building Req Data Pkt Header, Obj = %08X\n", this); } } else { DbgTrace(1, "SChannel::sendReplyError- Channel not connected, state = %08X\n", m_state); } // Release exclusive access to the channel object pthread_mutex_unlock(&m_mutex); DbgTrace(1, "SChannel::sendReplyError- End, retStatus = %08X\n", retStatus); return retStatus; } /*-- SChannel::sendReplyError() --*/ //========================================================================= //=========================================================================