From 701d03dcd905518cdae4a5bfe91c8700c2f7ca17 Mon Sep 17 00:00:00 2001 From: Brad Arant Date: Sat, 22 Aug 2026 19:51:08 -0700 Subject: [PATCH] Fix TLS read/write dispatch on tls branch TCPSocket inherits from both Socket and TLS, both declaring receiveData() as unrelated sibling methods - only Socket::receiveData was virtual, so TLS::receiveData (SSL_read) was never reached regardless of TLS config, and even when reached its result was never forwarded to onDataReceived (that call was commented out). Added an explicit TCPSocket::receiveData override dispatching to TLS::receiveData or Socket::receiveData based on tlsInfo, and completed TLS::receiveData to actually return/forward the decrypted bytes. Write path did not exist at all. Added Socket::sendData() as a new virtual hook mirroring receiveData, routed Socket::writeSocket() through it instead of a raw ::write(), added TLS::sendData() (SSL_write, same error-handling style as the existing TLS methods), and TCPSocket::sendData() dispatching the same way the read side does. Also fixed: a queued write in writeSocket() was being popped off the fifo as if sent even when shutDown was true and nothing was actually written; removed a dead empty if(tlsInfo){} stub in onDataReceived, no longer needed now dispatch happens one level up; replaced leftover debug std::cout lines with proper Log() calls matching the rest of the file. Verified via full local compile: all 20 .cpp files build clean, link into libServerCore.a, and nm confirms the new/fixed symbols are correctly defined in the right object files. --- Socket.cpp | 409 ++++++++++++++++++++++++----------------------- Socket.h | 433 ++++++++++++++++++++++++++------------------------ TCPSocket.cpp | 175 ++++++++++---------- TCPSocket.h | 263 ++++++++++++++++-------------- TLS.cpp | 368 ++++++++++++++++++++++-------------------- TLS.h | 135 ++++++++-------- 6 files changed, 932 insertions(+), 851 deletions(-) diff --git a/Socket.cpp b/Socket.cpp index 6cb32e5..f172875 100644 --- a/Socket.cpp +++ b/Socket.cpp @@ -1,202 +1,207 @@ -#include "Socket.h" -#include "EPoll.h" -#include "Exception.h" -#include "Log.h" -#include "ZString.h" - -namespace core -{ - - void sigpipe_handler(int unused) {} - - Socket::Socket(EPoll &ePoll, std::string text) : ePoll(ePoll), text(text) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "Socket object created [" << text << "]."; - buffer = (char *)malloc(4096); - length = 4096; - } - - Socket::~Socket() { - shutDown = true; - onUnregister(); - disableSocket(); - coreutils::Log(coreutils::LOG_DEBUG_4) << "Free on socket " << descriptor; - free(buffer); - if(descriptor == -1) - return; - close(descriptor); - coreutils::Log(coreutils::LOG_DEBUG_1) << text << " has ended (" << descriptor << ")."; - } - - void Socket::setDescriptor(int descriptor) - { - if ((descriptor == -1) && (errno == 24)) - { - shutdown("Too many files open"); - throw coreutils::Exception("Too many files open. Refusing connection."); - } - coreutils::Log(coreutils::LOG_DEBUG_1) << text << " has started (" << descriptor << ")."; - if(descriptor < 3) - throw coreutils::Exception("Descriptor out of range", __FILE__, __LINE__); - this->descriptor = descriptor; - onRegister(); - enableSocket(); - onRegistered(); - } - - int Socket::getDescriptor() - { - return descriptor; - } - - void Socket::setBufferSize(int length) - { - this->length = length; - buffer = (char *)realloc(buffer, length); - } - - int Socket::getBufferSize() - { - return length; - } - - void Socket::onRegister() {} - - void Socket::onRegistered() {} - - void Socket::onUnregister() {} - - void Socket::onUnregistered() {} - - bool Socket::eventReceived(struct epoll_event event, long long eventId) { - coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process beginning for socket " << getDescriptor(); - if(inHandler) -++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ coreutils::Log(coreutils::LOG_DEBUG_2) << "inHandler was already true."; - inHandler = true; - if(event.events & EPOLLRDHUP) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLRDHUP"; - readHangup = true; - shutdown("hangup received"); - } - if(event.events & EPOLLIN) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLIN"; - coreutils::ZString zbuffer(buffer, length); - lock.lock(); - receiveData(zbuffer); - if(!shutDown) { - inHandler = false; - lock.unlock(); - resetSocket(); - } - } - if(event.events & EPOLLWRNORM) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLWRNORM"; - writeSocket(); - inHandler = false; - resetSocket(); - } - inHandler = false; - coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process ending for socket " << getDescriptor(); - return !shutDown; - } - - void Socket::connectionRequest() {} - - void Socket::onDataReceived(std::string data) { - throw coreutils::Exception("Need to override onDataReceived.", __FILE__, __LINE__, -1); - } - - void Socket::onDataReceived(coreutils::ZString &data) { - onDataReceived(std::string(data.getData(), data.getLength())); - } - - void Socket::receiveData(coreutils::ZString &buffer) { - coreutils::ZString blank(""); - if(buffer.getLength() <= 0) - throw coreutils::Exception("Request to receive data with a zero buffer length.", __FILE__, __LINE__, -1); - int len; - int error = -1; - if((len = ::read(getDescriptor(), buffer.getData(), buffer.getLength())) >= 0) { - coreutils::ZString zbuffer(buffer.getData(), len); - // coreutils::Log(coreutils::LOG_DEBUG_1) << zbuffer; - onDataReceived(zbuffer); - } else { - error = errno; - switch (error) { - - // When a listening socket receives a connection - // request we get one of these. - - case ENOTCONN: - connectionRequest(); - break; - - case ECONNRESET: - break; - - default: - throw coreutils::Exception("Error in read of data from socket.", __FILE__, __LINE__, error); - } - } - } - - void Socket::writeSocket() { - outlock.lock(); - // coreutils::Log(coreutils::LOG_DEBUG_3) << "writing data to socket " << getDescriptor(); - if(fifo.size() > 0) { - if(!shutDown) - int rc = ::write(descriptor, fifo.front().c_str(), fifo.front().length()); - fifo.pop(); - } - outlock.unlock(); - } - - int Socket::write(std::string data) { - outlock.lock(); - fifo.emplace(data); - outlock.unlock(); - if(lock.try_lock()) { - resetSocket(); - lock.unlock(); - } - return 1; - } - - void Socket::output(std::stringstream &out) - { - out << "|" << descriptor << "|"; - } - - bool Socket::needsToWrite() - { - return fifo.size() > 0; - } - - void Socket::shutdown(std::string text) { - coreutils::Log(coreutils::LOG_DEBUG_2) << "Shutdown requested from " << this->text << " (" << descriptor << ") with reason: " << text << "."; - shutDown = true; - reset = false; - } - - void Socket::enableSocket() { - struct epoll_event event; - event.data.ptr = this; - event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET; - epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_ADD, getDescriptor(), &event); - } - - void Socket::disableSocket() { - epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_DEL, getDescriptor(), NULL); - } - - void Socket::resetSocket() { - struct epoll_event event; - event.data.ptr = this; - event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET; - if(fifo.size() > 0) - event.events |= EPOLLWRNORM; - if(!shutDown) - epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_MOD, getDescriptor(), &event); - } - - -} +#include "Socket.h" +#include "EPoll.h" +#include "Exception.h" +#include "Log.h" +#include "ZString.h" + +namespace core +{ + + void sigpipe_handler(int unused) {} + + Socket::Socket(EPoll &ePoll, std::string text) : ePoll(ePoll), text(text) { + coreutils::Log(coreutils::LOG_DEBUG_2) << "Socket object created [" << text << "]."; + buffer = (char *)malloc(4096); + length = 4096; + } + + Socket::~Socket() { + shutDown = true; + onUnregister(); + disableSocket(); + coreutils::Log(coreutils::LOG_DEBUG_4) << "Free on socket " << descriptor; + free(buffer); + if(descriptor == -1) + return; + close(descriptor); + coreutils::Log(coreutils::LOG_DEBUG_1) << text << " has ended (" << descriptor << ")."; + } + + void Socket::setDescriptor(int descriptor) + { + if ((descriptor == -1) && (errno == 24)) + { + shutdown("Too many files open"); + throw coreutils::Exception("Too many files open. Refusing connection."); + } + coreutils::Log(coreutils::LOG_DEBUG_1) << text << " has started (" << descriptor << ")."; + if(descriptor < 3) + throw coreutils::Exception("Descriptor out of range", __FILE__, __LINE__); + this->descriptor = descriptor; + onRegister(); + enableSocket(); + onRegistered(); + } + + int Socket::getDescriptor() + { + return descriptor; + } + + void Socket::setBufferSize(int length) + { + this->length = length; + buffer = (char *)realloc(buffer, length); + } + + int Socket::getBufferSize() + { + return length; + } + + void Socket::onRegister() {} + + void Socket::onRegistered() {} + + void Socket::onUnregister() {} + + void Socket::onUnregistered() {} + + bool Socket::eventReceived(struct epoll_event event, long long eventId) { + coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process beginning for socket " << getDescriptor(); + if(inHandler) + coreutils::Log(coreutils::LOG_DEBUG_2) << "inHandler was already true."; + inHandler = true; + if(event.events & EPOLLRDHUP) { + coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLRDHUP"; + readHangup = true; + shutdown("hangup received"); + } + if(event.events & EPOLLIN) { + coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLIN"; + coreutils::ZString zbuffer(buffer, length); + lock.lock(); + receiveData(zbuffer); + if(!shutDown) { + inHandler = false; + lock.unlock(); + resetSocket(); + } + } + if(event.events & EPOLLWRNORM) { + coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLWRNORM"; + writeSocket(); + inHandler = false; + resetSocket(); + } + inHandler = false; + coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process ending for socket " << getDescriptor(); + return !shutDown; + } + + void Socket::connectionRequest() {} + + void Socket::onDataReceived(std::string data) { + throw coreutils::Exception("Need to override onDataReceived.", __FILE__, __LINE__, -1); + } + + void Socket::onDataReceived(coreutils::ZString &data) { + onDataReceived(std::string(data.getData(), data.getLength())); + } + + void Socket::receiveData(coreutils::ZString &buffer) { + coreutils::ZString blank(""); + if(buffer.getLength() <= 0) + throw coreutils::Exception("Request to receive data with a zero buffer length.", __FILE__, __LINE__, -1); + int len; + int error = -1; + if((len = ::read(getDescriptor(), buffer.getData(), buffer.getLength())) >= 0) { + coreutils::ZString zbuffer(buffer.getData(), len); + // coreutils::Log(coreutils::LOG_DEBUG_1) << zbuffer; + onDataReceived(zbuffer); + } else { + error = errno; + switch (error) { + + // When a listening socket receives a connection + // request we get one of these. + + case ENOTCONN: + connectionRequest(); + break; + + case ECONNRESET: + break; + + default: + throw coreutils::Exception("Error in read of data from socket.", __FILE__, __LINE__, error); + } + } + } + + int Socket::sendData(const char *data, int length) { + return ::write(descriptor, data, length); + } + + void Socket::writeSocket() { + outlock.lock(); + // coreutils::Log(coreutils::LOG_DEBUG_3) << "writing data to socket " << getDescriptor(); + if(fifo.size() > 0) { + if(!shutDown) { + sendData(fifo.front().c_str(), fifo.front().length()); + fifo.pop(); + } + } + outlock.unlock(); + } + + int Socket::write(std::string data) { + outlock.lock(); + fifo.emplace(data); + outlock.unlock(); + if(lock.try_lock()) { + resetSocket(); + lock.unlock(); + } + return 1; + } + + void Socket::output(std::stringstream &out) + { + out << "|" << descriptor << "|"; + } + + bool Socket::needsToWrite() + { + return fifo.size() > 0; + } + + void Socket::shutdown(std::string text) { + coreutils::Log(coreutils::LOG_DEBUG_2) << "Shutdown requested from " << this->text << " (" << descriptor << ") with reason: " << text << "."; + shutDown = true; + reset = false; + } + + void Socket::enableSocket() { + struct epoll_event event; + event.data.ptr = this; + event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET; + epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_ADD, getDescriptor(), &event); + } + + void Socket::disableSocket() { + epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_DEL, getDescriptor(), NULL); + } + + void Socket::resetSocket() { + struct epoll_event event; + event.data.ptr = this; + event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET; + if(fifo.size() > 0) + event.events |= EPOLLWRNORM; + if(!shutDown) + epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_MOD, getDescriptor(), &event); + } + + +} diff --git a/Socket.h b/Socket.h index cc70c41..a1d84f4 100644 --- a/Socket.h +++ b/Socket.h @@ -1,212 +1,221 @@ -#ifndef __Socket_h__ -#define __Socket_h__ - -#include "ZString.h" -#include -#include -#include - -namespace core { - - class EPoll; - - /// - /// Socket - /// - /// The core component to managing a socket. - /// - /// Hooks into the EPoll through the registration and unregistration process and provides a - /// communication socket of the specified protocol type. This object provides for all receiving - /// data threading through use of the EPoll object and also provides buffering for output data - /// requests to the socket. - /// - /// A program using a socket object can request to open a socket (network or device) and - /// communicate through the streambuffer interface of the socket object. - /// - /// The socket side of the Socket accepts EPOLLIN event and will maintain the data in a buffer - /// for the stream readers to read. A onDataReceived event is then sent with the data received in - /// the buffer that can be read through the stream. Only sockets that send events to epoll can be - /// used with this object. - /// - /// When writing to the stream the data is written into a buffer and a EPOLLOUT is scheduled. Upon - /// receiving the EPOLLOUT event then the buffer is written to the socket output. - /// - - class Socket { - - public: - - /// - /// Constructor - /// - /// @param ePoll The EPoll socket descriptor. - /// @param text A title for this socket. - /// - - Socket(EPoll &ePoll, std::string text = ""); - - /// - /// Destructor - /// - - virtual ~Socket(); - - /// - /// Use the shutdown() method to terminate the socket connection and remove resources. - /// This method is provided to ensure that all destructors are called for all inherited - /// objects with a virtual destructor. - /// - - void shutdown(std::string text = "unknown"); - - /// - /// setDescriptor establishes the file descriptor for the socket and registers the socket - /// on the EPoll controller. setDescriptor will invoke the onRegister() event. - /// - - void setDescriptor(int descriptor); /// fifo; - - void resetSocket(); - - std::mutex lock; - - }; - -} - -#endif - +#ifndef __Socket_h__ +#define __Socket_h__ + +#include "ZString.h" +#include +#include +#include + +namespace core { + + class EPoll; + + /// + /// Socket + /// + /// The core component to managing a socket. + /// + /// Hooks into the EPoll through the registration and unregistration process and provides a + /// communication socket of the specified protocol type. This object provides for all receiving + /// data threading through use of the EPoll object and also provides buffering for output data + /// requests to the socket. + /// + /// A program using a socket object can request to open a socket (network or device) and + /// communicate through the streambuffer interface of the socket object. + /// + /// The socket side of the Socket accepts EPOLLIN event and will maintain the data in a buffer + /// for the stream readers to read. A onDataReceived event is then sent with the data received in + /// the buffer that can be read through the stream. Only sockets that send events to epoll can be + /// used with this object. + /// + /// When writing to the stream the data is written into a buffer and a EPOLLOUT is scheduled. Upon + /// receiving the EPOLLOUT event then the buffer is written to the socket output. + /// + + class Socket { + + public: + + /// + /// Constructor + /// + /// @param ePoll The EPoll socket descriptor. + /// @param text A title for this socket. + /// + + Socket(EPoll &ePoll, std::string text = ""); + + /// + /// Destructor + /// + + virtual ~Socket(); + + /// + /// Use the shutdown() method to terminate the socket connection and remove resources. + /// This method is provided to ensure that all destructors are called for all inherited + /// objects with a virtual destructor. + /// + + void shutdown(std::string text = "unknown"); + + /// + /// setDescriptor establishes the file descriptor for the socket and registers the socket + /// on the EPoll controller. setDescriptor will invoke the onRegister() event. + /// + + void setDescriptor(int descriptor); /// fifo; + + void resetSocket(); + + std::mutex lock; + + }; + +} + +#endif + diff --git a/TCPSocket.cpp b/TCPSocket.cpp index 59df2b8..b410d8e 100644 --- a/TCPSocket.cpp +++ b/TCPSocket.cpp @@ -1,80 +1,95 @@ -#include "TCPSocket.h" -#include "EPoll.h" -#include "Log.h" -#include "Exception.h" -#include "errno.h" - -namespace core { - - TCPSocket::TCPSocket(EPoll &ePoll) : Socket(ePoll) {} - - TCPSocket::TCPSocket(EPoll &ePoll, std::string text) : Socket(ePoll, text) {} - - TCPSocket::TCPSocket(EPoll &ePoll, TLSInfo *tlsInfo, std::string text) : Socket(ePoll, text), TLS(tlsInfo), tlsInfo(tlsInfo) {} - - TCPSocket::TCPSocket(EPoll &ePoll, SSL_CTX *ctx, std::string text) : Socket(ePoll, text), TLS(ctx) {} - - TCPSocket::~TCPSocket() {} - - void TCPSocket::connect(IPAddress &address) { - setDescriptor(socket(AF_INET, SOCK_STREAM, 0)); - if(::connect(getDescriptor(), (struct sockaddr *)&address.addr, address.addressLength) == -1) - throw coreutils::Exception("Error on connect to TCP socket." + errno); - coreutils::Log(coreutils::LOG_DEBUG_3) << "Connected to IP..." << address.getClientAddressAndPort(); - } - - void TCPSocket::output(std::stringstream &out) { - out << "|" << ipAddress.getClientAddressAndPort(); - } - - void TCPSocket::onDataReceived(coreutils::ZString &data) { - - if(tlsInfo) { - - } - - if (data.getLength() > 0) { - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize + data.getLength()); - memcpy(lineBuffer + lineBufferSize, data.getData(), data.getLength()); - lineBufferSize += data.getLength(); - while (lineBufferSize > 0) { - if (blockSize == 0) { - lineLength = strcspn(lineBuffer, "\r\n"); - if (lineLength == lineBufferSize) - break; - coreutils::ZString zLine(lineBuffer, lineLength); - onLineReceived(zLine); - if (lineBuffer[lineLength] == '\r') - ++lineLength; - if (lineBuffer[lineLength] == '\n') - ++lineLength; - lineBufferSize -= lineLength; - if (lineBufferSize > 0) - memmove(lineBuffer, lineBuffer + lineLength, lineBufferSize); - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); - } - else if (lineBufferSize >= blockLength) { - coreutils::ZString zBlock(lineBuffer, blockLength); - onBlockReceived(zBlock); - lineBufferSize -= blockLength; - if (lineBufferSize > 0) - memmove(lineBuffer, lineBuffer + blockLength, lineBufferSize); - lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); - } - } - } - } - - void TCPSocket::onLineReceived(coreutils::ZString &line) { - if (term) - shutdown("termination requested"); - } - - void TCPSocket::onBlockReceived(coreutils::ZString &block) {} - - void TCPSocket::setBlockSize(int blockSize) { - this->blockSize = blockSize; - } - -} - +#include "TCPSocket.h" +#include "EPoll.h" +#include "Log.h" +#include "Exception.h" +#include "errno.h" + +namespace core { + + TCPSocket::TCPSocket(EPoll &ePoll) : Socket(ePoll) {} + + TCPSocket::TCPSocket(EPoll &ePoll, std::string text) : Socket(ePoll, text) {} + + TCPSocket::TCPSocket(EPoll &ePoll, TLSInfo *tlsInfo, std::string text) : Socket(ePoll, text), TLS(tlsInfo), tlsInfo(tlsInfo) {} + + TCPSocket::TCPSocket(EPoll &ePoll, SSL_CTX *ctx, std::string text) : Socket(ePoll, text), TLS(ctx) {} + + TCPSocket::~TCPSocket() {} + + void TCPSocket::connect(IPAddress &address) { + setDescriptor(socket(AF_INET, SOCK_STREAM, 0)); + if(::connect(getDescriptor(), (struct sockaddr *)&address.addr, address.addressLength) == -1) + throw coreutils::Exception("Error on connect to TCP socket." + errno); + coreutils::Log(coreutils::LOG_DEBUG_3) << "Connected to IP..." << address.getClientAddressAndPort(); + } + + void TCPSocket::output(std::stringstream &out) { + out << "|" << ipAddress.getClientAddressAndPort(); + } + + void TCPSocket::receiveData(coreutils::ZString &buffer) { + if(tlsInfo) { + int len = TLS::receiveData(buffer); + if(len >= 0) { + coreutils::ZString zbuffer(buffer.getData(), len); + onDataReceived(zbuffer); + } + } else { + Socket::receiveData(buffer); + } + } + + int TCPSocket::sendData(const char *data, int length) { + if(tlsInfo) + return TLS::sendData(data, length); + else + return Socket::sendData(data, length); + } + + void TCPSocket::onDataReceived(coreutils::ZString &data) { + + if (data.getLength() > 0) { + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize + data.getLength()); + memcpy(lineBuffer + lineBufferSize, data.getData(), data.getLength()); + lineBufferSize += data.getLength(); + while (lineBufferSize > 0) { + if (blockSize == 0) { + lineLength = strcspn(lineBuffer, "\r\n"); + if (lineLength == lineBufferSize) + break; + coreutils::ZString zLine(lineBuffer, lineLength); + onLineReceived(zLine); + if (lineBuffer[lineLength] == '\r') + ++lineLength; + if (lineBuffer[lineLength] == '\n') + ++lineLength; + lineBufferSize -= lineLength; + if (lineBufferSize > 0) + memmove(lineBuffer, lineBuffer + lineLength, lineBufferSize); + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); + } + else if (lineBufferSize >= blockLength) { + coreutils::ZString zBlock(lineBuffer, blockLength); + onBlockReceived(zBlock); + lineBufferSize -= blockLength; + if (lineBufferSize > 0) + memmove(lineBuffer, lineBuffer + blockLength, lineBufferSize); + lineBuffer = (char *)realloc(lineBuffer, lineBufferSize); + } + } + } + } + + void TCPSocket::onLineReceived(coreutils::ZString &line) { + if (term) + shutdown("termination requested"); + } + + void TCPSocket::onBlockReceived(coreutils::ZString &block) {} + + void TCPSocket::setBlockSize(int blockSize) { + this->blockSize = blockSize; + } + +} + diff --git a/TCPSocket.h b/TCPSocket.h index 8f67cb0..883824c 100644 --- a/TCPSocket.h +++ b/TCPSocket.h @@ -1,122 +1,141 @@ - -#ifndef __TCPSocket_h__ -#define __TCPSocket_h__ - -#include "Socket.h" -#include "TLS.h" -#include "TLSInfo.h" -#include "IPAddress.h" - -namespace core { - - /// - /// TCPSocket - /// - /// Provides a network TCP socket. - /// - /// For accessing TCP network functions use this object. The connection oriented nature of TCP - /// provides a single client persistent connection with data error correction and a durable - /// synchronous data connection. - /// - - class TCPSocket : public Socket, public TLS { - - public: - - /// - /// - /// - - TCPSocket(EPoll &ePoll); - - /// - /// - /// - - TCPSocket(EPoll &ePoll, std::string text); - - /// - /// - /// - - TCPSocket(EPoll &ePoll, TLSInfo *tlsInfo, std::string text); - - /// - /// - /// - - TCPSocket(EPoll &ePoll, SSL_CTX *ctx, std::string text); - - /// - /// - /// - - virtual ~TCPSocket(); - - /// - /// - /// - - void connect(IPAddress &address); - - IPAddress ipAddress; - - /// - /// The output method is called by a socket session (TCPSession) and - /// will output the detail information for the client socket. When extending - /// TCPSocket or TCPSession you can override the method to add attributes - /// to the list. - /// - - virtual void output(std::stringstream &out); - - /// - /// Override this method to receive data directly from the socket as data is - /// received. If you need data split by line termination characters then - /// override the onLineReceived method instead. - /// - - virtual void onDataReceived(coreutils::ZString &data) override; - - /// - /// Override the onLineReceived method to receive a string of characters that - /// represents a single line of data terminated by a LF or CRLF. If onDataReceived - /// was overriden this method will not be called unless the onDataReceived calls - /// this method explicitly using the class and member name. - /// - - virtual void onLineReceived(coreutils::ZString &line); - - /// - /// Override the onBlockReceived method to receive a string of characters that - /// represents a single block of data of length determined by the block length value. If - /// onDataReceived was overriden this method will not be called unless the onDataReceived - /// calls this method explicitly using the class and member name. - /// - - virtual void onBlockReceived(coreutils::ZString &block); - - /// - /// Use setBlockSize to set the amount of data that should be read at once from the - /// session data buffer. - /// If this value is set to 0 then the data will be retrieved - /// - - void setBlockSize(int size = 0); - - protected: - bool term = false; - TLSInfo *tlsInfo; - - private: - char *lineBuffer = NULL; - int lineBufferSize = 0; - int lineLength = 0; - int blockLength = 0; - int blockSize = 0; - - }; - -} - -#endif + +#ifndef __TCPSocket_h__ +#define __TCPSocket_h__ + +#include "Socket.h" +#include "TLS.h" +#include "TLSInfo.h" +#include "IPAddress.h" + +namespace core { + + /// + /// TCPSocket + /// + /// Provides a network TCP socket. + /// + /// For accessing TCP network functions use this object. The connection oriented nature of TCP + /// provides a single client persistent connection with data error correction and a durable + /// synchronous data connection. + /// + + class TCPSocket : public Socket, public TLS { + + public: + + /// + /// + /// + + TCPSocket(EPoll &ePoll); + + /// + /// + /// + + TCPSocket(EPoll &ePoll, std::string text); + + /// + /// + /// + + TCPSocket(EPoll &ePoll, TLSInfo *tlsInfo, std::string text); + + /// + /// + /// + + TCPSocket(EPoll &ePoll, SSL_CTX *ctx, std::string text); + + /// + /// + /// + + virtual ~TCPSocket(); + + /// + /// + /// + + void connect(IPAddress &address); + + IPAddress ipAddress; + + /// + /// The output method is called by a socket session (TCPSession) and + /// will output the detail information for the client socket. When extending + /// TCPSocket or TCPSession you can override the method to add attributes + /// to the list. + /// + + virtual void output(std::stringstream &out); + + /// + /// Override this method to receive data directly from the socket as data is + /// received. If you need data split by line termination characters then + /// override the onLineReceived method instead. + /// + + virtual void onDataReceived(coreutils::ZString &data) override; + + /// + /// Override the onLineReceived method to receive a string of characters that + /// represents a single line of data terminated by a LF or CRLF. If onDataReceived + /// was overriden this method will not be called unless the onDataReceived calls + /// this method explicitly using the class and member name. + /// + + virtual void onLineReceived(coreutils::ZString &line); + + /// + /// Override the onBlockReceived method to receive a string of characters that + /// represents a single block of data of length determined by the block length value. If + /// onDataReceived was overriden this method will not be called unless the onDataReceived + /// calls this method explicitly using the class and member name. + /// + + virtual void onBlockReceived(coreutils::ZString &block); + + /// + /// Use setBlockSize to set the amount of data that should be read at once from the + /// session data buffer. + /// If this value is set to 0 then the data will be retrieved + /// + + void setBlockSize(int size = 0); + + /// + /// Dispatches to TLS::receiveData (SSL_read) when this socket was constructed with a + /// TLSInfo/SSL_CTX, otherwise falls back to Socket::receiveData (plain ::read). Needed + /// because Socket and TLS both declare receiveData as unrelated sibling methods - + /// Socket::receiveData is virtual but TLS::receiveData does not override it, so without + /// this explicit override TLS::receiveData would never be reached and encrypted bytes + /// would be handed to onDataReceived as if they were plaintext. + /// + + virtual void receiveData(coreutils::ZString &buffer) override; + + /// + /// Dispatches to TLS::sendData (SSL_write) when this socket was constructed with a + /// TLSInfo/SSL_CTX, otherwise falls back to Socket::sendData (plain ::write). Mirrors + /// the receiveData override above for the write path. + /// + + virtual int sendData(const char *data, int length) override; + + protected: + bool term = false; + TLSInfo *tlsInfo; + + private: + char *lineBuffer = NULL; + int lineBufferSize = 0; + int lineLength = 0; + int blockLength = 0; + int blockSize = 0; + + }; + +} + +#endif diff --git a/TLS.cpp b/TLS.cpp index a82d43d..b6a363c 100644 --- a/TLS.cpp +++ b/TLS.cpp @@ -1,169 +1,201 @@ -#include "TLS.h" -#include "Exception.h" -#include "Log.h" -#include -#include - -namespace core { - - static pthread_mutex_t *lockarray; - - static unsigned long thread_id(void) { - return ((unsigned long) pthread_self()); - } - - static void lock_callback(int mode, int type, const char *file, int line) { - if(mode & CRYPTO_LOCK) - pthread_mutex_lock(&(lockarray[type])); - else - pthread_mutex_unlock(&(lockarray[type])); - } - - static int generate_session_id(const SSL *ssl, unsigned char *id, unsigned int *id_len) { - char *session_id_prefix = (char *)"BARANT"; - unsigned int count = 0; - coreutils::Log(coreutils::LOG_DEBUG_3) << "Generating unique session id."; - do { - RAND_bytes(id, *id_len); - memcpy(id, session_id_prefix, (strlen(session_id_prefix) < *id_len)); - } while(SSL_has_matching_session_id(ssl, id, *id_len) && (++count < 10)); - return 1; - } - - void handshake_complete(const SSL *ssl, int where, int ret) { - coreutils::Log(coreutils::LOG_DEBUG_3) << "==>" << SSL_state_string_long(ssl) << "<==" << ret; - if(where & SSL_CB_HANDSHAKE_DONE) { - X509 *ssl_client_cert = SSL_get_peer_certificate(ssl); - if(!ssl_client_cert) - throw std::string("Unable to get peer certificate."); - X509_free(ssl_client_cert); - if(SSL_get_verify_result(ssl) != X509_V_OK) - throw std::string("Certificate verification failed."); - coreutils::Log(coreutils::LOG_DEBUG_3) << "Certificate verified successfully."; - } - else - coreutils::Log(coreutils::LOG_DEBUG_3) << "No client certificate."; - } - - TLS::TLS() {} - - TLS::TLS(TLSInfo *tlsInfo) { - createContext(tlsInfo); - } - - TLS::TLS(SSL_CTX *ctx) : ctx(ctx) {} - - TLS::~TLS() {} - - void TLS::createContext(TLSInfo *tlsInfo) { - - if(tlsInfo) { - SSL_library_init(); - SSL_load_error_strings(); - lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); - for(int i = 0; i < CRYPTO_num_locks(); ++i) - pthread_mutex_init(&(lockarray[i]), NULL); - CRYPTO_set_id_callback((unsigned long (*)())thread_id); - CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback); - SSLeay_add_ssl_algorithms(); - RAND_load_file("/dev/hwrng", 1024); - if(!(ctx = SSL_CTX_new(SSLv23_server_method()))) - throw coreutils::Exception("Error while setting server method SSLv23."); - SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); - // SSL_CTX_set_generate_session_id(ctx, generate_session_id); - SSL_CTX_set_cipher_list(ctx, "ECDH-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA"); - if(SSL_CTX_use_certificate_file(ctx, tlsInfo->certificate.c_str(), SSL_FILETYPE_PEM) <= 0) - throw coreutils::Exception("Error looking up certificate."); - if(SSL_CTX_use_PrivateKey_file(ctx, tlsInfo->key.c_str(), SSL_FILETYPE_PEM) < 0) - throw coreutils::Exception("Error with private key."); - if(SSL_CTX_check_private_key(ctx) != 1) - throw coreutils::Exception("Private key does not match certificate."); - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); - SSL_CTX_set_verify_depth(ctx, 1); - if(!SSL_CTX_load_verify_locations(ctx, tlsInfo->cACertificate.c_str(), NULL)) - throw coreutils::Exception("Cannot verify locations."); - SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(tlsInfo->cACertificate.c_str())); - coreutils::Log(coreutils::LOG_INFO) << "Server key authenticated."; - } - } - - void TLS::registerSocket(int fd) { - initialized = true; - int ret; - - coreutils::Log(coreutils::LOG_DEBUG_3) << "TLS socket initializing on socket " << fd << "..."; - - fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); - - ssl = SSL_new(ctx); -// if(ssl <= 0) -// throw std::string("Error creating new TLS socket."); - - SSL_set_info_callback(ssl, handshake_complete); - - if((ret = SSL_set_fd(ssl, fd)) == 0) - throw std::string("Error setting TLS socket descriptor."); - -// if(!SSL_set_generate_session_id(ssl, generate_session_id)) -// throw std::string("Error setting session identifier callback."); - - } - - void TLS::acceptSocket() { - - switch (SSL_get_error(ssl, SSL_accept(ssl))) { - case SSL_ERROR_SSL: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_accept. errno=" << errno; - break; - case SSL_ERROR_WANT_READ: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_accept."; - break; - case SSL_ERROR_WANT_WRITE: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_accept."; - break; - case SSL_ERROR_SYSCALL: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_accept. errno=" << errno; -// shutdown(); - break; - default: - coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_accept."; - break; - } - - } - - void TLS::receiveData(coreutils::ZString &buffer) { - - int len; - std::cout << "receiveData TLS" << std::endl; - - if((len = ::SSL_read(ssl, buffer.getData(), buffer.getLength())) >= 0) { - std::cout << "receiveData TLS...len=" << len << ":" << buffer << std::endl; -// onDataReceived(buffer); - } - else { - switch (SSL_get_error(ssl, len)) { - case SSL_ERROR_SSL: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_read. error=" << errno; - break; - case SSL_ERROR_WANT_READ: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_read."; - break; - case SSL_ERROR_WANT_WRITE: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_read."; - break; - case SSL_ERROR_SYSCALL: - coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_read. errno=" << errno; - break; - default: - coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_read."; - break; - } - } - - } - -} +#include "TLS.h" +#include "Exception.h" +#include "Log.h" +#include +#include + +namespace core { + + static pthread_mutex_t *lockarray; + + static unsigned long thread_id(void) { + return ((unsigned long) pthread_self()); + } + + static void lock_callback(int mode, int type, const char *file, int line) { + if(mode & CRYPTO_LOCK) + pthread_mutex_lock(&(lockarray[type])); + else + pthread_mutex_unlock(&(lockarray[type])); + } + + static int generate_session_id(const SSL *ssl, unsigned char *id, unsigned int *id_len) { + char *session_id_prefix = (char *)"BARANT"; + unsigned int count = 0; + coreutils::Log(coreutils::LOG_DEBUG_3) << "Generating unique session id."; + do { + RAND_bytes(id, *id_len); + memcpy(id, session_id_prefix, (strlen(session_id_prefix) < *id_len)); + } while(SSL_has_matching_session_id(ssl, id, *id_len) && (++count < 10)); + return 1; + } + + void handshake_complete(const SSL *ssl, int where, int ret) { + coreutils::Log(coreutils::LOG_DEBUG_3) << "==>" << SSL_state_string_long(ssl) << "<==" << ret; + if(where & SSL_CB_HANDSHAKE_DONE) { + X509 *ssl_client_cert = SSL_get_peer_certificate(ssl); + if(!ssl_client_cert) + throw std::string("Unable to get peer certificate."); + X509_free(ssl_client_cert); + if(SSL_get_verify_result(ssl) != X509_V_OK) + throw std::string("Certificate verification failed."); + coreutils::Log(coreutils::LOG_DEBUG_3) << "Certificate verified successfully."; + } + else + coreutils::Log(coreutils::LOG_DEBUG_3) << "No client certificate."; + } + + TLS::TLS() {} + + TLS::TLS(TLSInfo *tlsInfo) { + createContext(tlsInfo); + } + + TLS::TLS(SSL_CTX *ctx) : ctx(ctx) {} + + TLS::~TLS() {} + + void TLS::createContext(TLSInfo *tlsInfo) { + + if(tlsInfo) { + SSL_library_init(); + SSL_load_error_strings(); + lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); + for(int i = 0; i < CRYPTO_num_locks(); ++i) + pthread_mutex_init(&(lockarray[i]), NULL); + CRYPTO_set_id_callback((unsigned long (*)())thread_id); + CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback); + SSLeay_add_ssl_algorithms(); + RAND_load_file("/dev/hwrng", 1024); + if(!(ctx = SSL_CTX_new(SSLv23_server_method()))) + throw coreutils::Exception("Error while setting server method SSLv23."); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + // SSL_CTX_set_generate_session_id(ctx, generate_session_id); + SSL_CTX_set_cipher_list(ctx, "ECDH-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA"); + if(SSL_CTX_use_certificate_file(ctx, tlsInfo->certificate.c_str(), SSL_FILETYPE_PEM) <= 0) + throw coreutils::Exception("Error looking up certificate."); + if(SSL_CTX_use_PrivateKey_file(ctx, tlsInfo->key.c_str(), SSL_FILETYPE_PEM) < 0) + throw coreutils::Exception("Error with private key."); + if(SSL_CTX_check_private_key(ctx) != 1) + throw coreutils::Exception("Private key does not match certificate."); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_verify_depth(ctx, 1); + if(!SSL_CTX_load_verify_locations(ctx, tlsInfo->cACertificate.c_str(), NULL)) + throw coreutils::Exception("Cannot verify locations."); + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(tlsInfo->cACertificate.c_str())); + coreutils::Log(coreutils::LOG_INFO) << "Server key authenticated."; + } + } + + void TLS::registerSocket(int fd) { + initialized = true; + int ret; + + coreutils::Log(coreutils::LOG_DEBUG_3) << "TLS socket initializing on socket " << fd << "..."; + + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + + ssl = SSL_new(ctx); +// if(ssl <= 0) +// throw std::string("Error creating new TLS socket."); + + SSL_set_info_callback(ssl, handshake_complete); + + if((ret = SSL_set_fd(ssl, fd)) == 0) + throw std::string("Error setting TLS socket descriptor."); + +// if(!SSL_set_generate_session_id(ssl, generate_session_id)) +// throw std::string("Error setting session identifier callback."); + + } + + void TLS::acceptSocket() { + + switch (SSL_get_error(ssl, SSL_accept(ssl))) { + case SSL_ERROR_SSL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_accept. errno=" << errno; + break; + case SSL_ERROR_WANT_READ: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_accept."; + break; + case SSL_ERROR_WANT_WRITE: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_accept."; + break; + case SSL_ERROR_SYSCALL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_accept. errno=" << errno; +// shutdown(); + break; + default: + coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_accept."; + break; + } + + } + + int TLS::receiveData(coreutils::ZString &buffer) { + + int len; + coreutils::Log(coreutils::LOG_DEBUG_3) << "receiveData TLS on socket."; + + if((len = ::SSL_read(ssl, buffer.getData(), buffer.getLength())) >= 0) { + coreutils::Log(coreutils::LOG_DEBUG_3) << "receiveData TLS...len=" << len; + return len; + } + else { + switch (SSL_get_error(ssl, len)) { + case SSL_ERROR_SSL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_read. error=" << errno; + break; + case SSL_ERROR_WANT_READ: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_read."; + break; + case SSL_ERROR_WANT_WRITE: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_read."; + break; + case SSL_ERROR_SYSCALL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_read. errno=" << errno; + break; + default: + coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_read."; + break; + } + return -1; + } + + } + + int TLS::sendData(const char *data, int length) { + + int len; + + if((len = ::SSL_write(ssl, data, length)) >= 0) { + coreutils::Log(coreutils::LOG_DEBUG_3) << "sendData TLS...len=" << len; + return len; + } + else { + switch (SSL_get_error(ssl, len)) { + case SSL_ERROR_SSL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_write. error=" << errno; + break; + case SSL_ERROR_WANT_READ: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_write."; + break; + case SSL_ERROR_WANT_WRITE: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_write."; + break; + case SSL_ERROR_SYSCALL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_write. errno=" << errno; + break; + default: + coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_write."; + break; + } + return -1; + } + + } + +} \ No newline at end of file diff --git a/TLS.h b/TLS.h index 44ea5e1..d93cad4 100644 --- a/TLS.h +++ b/TLS.h @@ -1,67 +1,68 @@ -#ifndef __TLS_h__ -#define __TLS_h__ - -#include "ZString.h" -#include "TLSInfo.h" -#include - -namespace core { - - /// - /// TLS - /// - /// This object provides the support methods to handle TLS on the server core and - /// session environment. - /// - - class TLS { - - public: - - /// - /// - /// - - TLS(); - - /// - /// Use this constructor when the SSL context needs to be created as when opening - /// a server TCPSocket. - /// - - TLS(TLSInfo *tlsInfo); - - /// - /// Use this constructor on creation of a new TCPSocket that needs access to - /// the generating server's SSL context. - /// - - TLS(SSL_CTX *ctx); - - /// - /// The destructor for this object. - /// - - ~TLS(); - - void createContext(TLSInfo *tlsInfo); - - SSL_CTX *ctx; - - protected: - void receiveData(coreutils::ZString &buffer); - void registerSocket(int fd); - void acceptSocket(); - - private: - char *sip_cacert = (char *)"../testkeys/certs/pbxca.crt"; - char *sip_cert = (char *)"../testkeys/certs/pbxserver.crt"; - char *sip_key = (char *)"../testkeys/certs/pbxserver.key"; - bool initialized = false; - SSL *ssl; - - }; - -} - -#endif +#ifndef __TLS_h__ +#define __TLS_h__ + +#include "ZString.h" +#include "TLSInfo.h" +#include + +namespace core { + + /// + /// TLS + /// + /// This object provides the support methods to handle TLS on the server core and + /// session environment. + /// + + class TLS { + + public: + + /// + /// + /// + + TLS(); + + /// + /// Use this constructor when the SSL context needs to be created as when opening + /// a server TCPSocket. + /// + + TLS(TLSInfo *tlsInfo); + + /// + /// Use this constructor on creation of a new TCPSocket that needs access to + /// the generating server's SSL context. + /// + + TLS(SSL_CTX *ctx); + + /// + /// The destructor for this object. + /// + + ~TLS(); + + void createContext(TLSInfo *tlsInfo); + + SSL_CTX *ctx; + + protected: + int receiveData(coreutils::ZString &buffer); + int sendData(const char *data, int length); + void registerSocket(int fd); + void acceptSocket(); + + private: + char *sip_cacert = (char *)"../testkeys/certs/pbxca.crt"; + char *sip_cert = (char *)"../testkeys/certs/pbxserver.crt"; + char *sip_key = (char *)"../testkeys/certs/pbxserver.key"; + bool initialized = false; + SSL *ssl; + + }; + +} + +#endif