connectionRequest() had a literal "if(true) { registerSocket(...);
acceptSocket(); }" guarding the TLS handshake setup for every accepted
connection, regardless of whether this TCPServer was ever given a
TLSInfo. TLS::createContext() only sets `ctx` when tlsInfo is non-NULL
(correctly guarded there) - so for any plain, non-TLS TCPServer, `ctx`
is left uninitialized, and this unconditional call reached
TLS::registerSocket() -> SSL_new(ctx) with garbage, segfaulting the
whole process on the very first connection.
Found while building APIServer (a new non-TLS HTTP server on top of
TCPServer) - confirmed via gdb (SIGSEGV inside SSL_new, called from
TLS::registerSocket, called from this exact line) that this affects ANY
non-TLS ServerCore-based server's first accepted connection, not
anything specific to the new code being tested against it.
Fixed by checking `tlsInfo` (already a TCPSocket-protected member this
class inherits) instead of the unconditional `true` - matches the guard
TLS::createContext() already uses correctly for the same distinction.
Verified live: before the fix, a non-TLS ApiServer segfaulted on the
first curl request every time; after, GET/HEAD/POST requests across
several real routes complete correctly and the server stays up for
repeated requests, including one that deliberately throws (confirming
the crash is really gone, not just less frequent).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
132 lines
4.4 KiB
C++
132 lines
4.4 KiB
C++
#include "TCPServer.h"
|
|
#include "EPoll.h"
|
|
#include "Exception.h"
|
|
#include "Log.h"
|
|
#include "TCPSession.h"
|
|
|
|
namespace core {
|
|
|
|
TCPServer::TCPServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, std::string delimiter, int depth, std::string text)
|
|
: TCPSocket(ePoll, tlsInfo, text), commands(delimiter, depth) {
|
|
|
|
setDescriptor(socket(AF_INET, SOCK_STREAM, 0));
|
|
int yes = 1;
|
|
setsockopt(getDescriptor(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
|
|
|
if (bind(getDescriptor(), address.getPointer(), address.addressLength) < 0)
|
|
throw coreutils::Exception("Error on bind to socket: " + std::to_string(errno));
|
|
|
|
if (listen(getDescriptor(), 20) < 0)
|
|
throw coreutils::Exception("Error on listen to socket");
|
|
}
|
|
|
|
TCPServer::~TCPServer() {
|
|
coreutils::Log(coreutils::LOG_DEBUG_2) << "Closing server socket " << getDescriptor() << ".";
|
|
close(getDescriptor());
|
|
}
|
|
|
|
void TCPServer::connectionRequest() {
|
|
coreutils::Log(coreutils::LOG_DEBUG_2) << "Connection request is being received on socket " << getDescriptor() << ".";
|
|
lock.lock();
|
|
TCPSession *session = accept();
|
|
if (session)
|
|
sessions.push_back(session);
|
|
if (tlsInfo) {
|
|
registerSocket(session->getDescriptor());
|
|
acceptSocket();
|
|
}
|
|
lock.unlock();
|
|
}
|
|
|
|
TCPSession * TCPServer::accept() {
|
|
|
|
try {
|
|
|
|
TCPSession *session = getSocketAccept(ePoll);
|
|
session->setDescriptor(::accept(getDescriptor(), (struct sockaddr *)&session->ipAddress.addr, &session->ipAddress.addressLength));
|
|
// if(blackList && blackList->contains(session->ipAddress.getClientAddress())) {
|
|
// session->shutdown();
|
|
// Log(LOG_WARN) << "Client at IP address " << session->ipAddress.getClientAddress() << " is blacklisted and was denied a connection.";
|
|
// return NULL;
|
|
// }
|
|
// if(whiteList && !whiteList->contains(session->ipAddress.getClientAddress())) {
|
|
// session->shutdown();
|
|
// Log(LOG_WARN) << "Client at IP address " << session->ipAddress.getClientAddress() << " is not authorized and was denied a connection.";
|
|
// return NULL;
|
|
// }
|
|
return session;
|
|
}
|
|
catch (coreutils::Exception e) {
|
|
coreutils::Log(coreutils::LOG_EXCEPT) << "Major error on session initialization. Error is '" << e.text << "'.";
|
|
}
|
|
catch (...) {
|
|
coreutils::Log(coreutils::LOG_EXCEPT) << "Unnspecified error on session initialization.";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void TCPServer::removeFromSessionList(TCPSession *session) {
|
|
std::vector<TCPSession *>::iterator cursor;
|
|
lock.lock();
|
|
for(cursor = sessions.begin(); cursor < sessions.end(); ++cursor)
|
|
if(*cursor == session) {
|
|
sessions.erase(cursor);
|
|
break;
|
|
}
|
|
lock.unlock();
|
|
}
|
|
|
|
void TCPServer::sessionErrorHandler(std::string errorString, std::stringstream &out) {
|
|
throw coreutils::Exception(errorString);
|
|
}
|
|
|
|
TCPSession * TCPServer::getSocketAccept(EPoll &ePoll) {
|
|
return new TCPSession(ePoll, this);
|
|
}
|
|
|
|
void TCPServer::output(std::stringstream &out) {
|
|
out << "Use the 'help' command to list the commands for this server." << std::endl;
|
|
}
|
|
|
|
int TCPServer::processCommand(coreutils::ZString &request, TCPSession &session) {
|
|
int sequence = 0;
|
|
for (auto *sessionx : sessions)
|
|
{
|
|
session.out << "|" << ++sequence;
|
|
sessionx->output(session.out);
|
|
session.out << "|" << std::endl;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void TCPServer::sendToAll(std::stringstream &data) {
|
|
for (auto session : sessions)
|
|
session->write(data.str());
|
|
data.str("");
|
|
}
|
|
|
|
void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender) {
|
|
for (auto session : sessions)
|
|
if (session != &sender)
|
|
session->write(data.str());
|
|
data.str("");
|
|
}
|
|
|
|
void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender, SessionFilter filter) {
|
|
for (auto session : sessions)
|
|
if (filter.test(*session))
|
|
if (session != &sender)
|
|
session->write(data.str());
|
|
data.str("");
|
|
}
|
|
|
|
TCPSession *TCPServer::getSessionByAlias(void *alias) {
|
|
coreutils::Log(coreutils::LOG_DEBUG_1) << alias;
|
|
for (auto session : sessions)
|
|
if (session->compareAlias(alias))
|
|
return session;
|
|
return NULL;
|
|
}
|
|
|
|
}
|