Compare commits

..
1 Commits
Author SHA1 Message Date
Brad Arant 275c7298ee Adding LineParser layer. 2025-06-20 01:22:06 +00:00
2863 changed files with 84220 additions and 2340 deletions

No files matched your search

+1 -1
View File
@@ -11,4 +11,4 @@ docs/html
*/mmap_address.bin */mmap_address.bin
.history/* .history/*
html html
latex*.pre-* latex
+6 -4
View File
@@ -1,10 +1,13 @@
#ifndef __Command_h__ #ifndef __Command_h__
#define __Command_h__ #define __Command_h__
#include "Object.h"
#include "TCPSession.h" #include "TCPSession.h"
#include "ZString.h" #include "ZString.h"
#include "includes"
namespace core { namespace core
{
class CommandList; class CommandList;
@@ -17,10 +20,10 @@ namespace core {
/// a list of functions that can be invoked as a result of processing a request. /// a list of functions that can be invoked as a result of processing a request.
/// ///
class Command { class Command
{
public: public:
/// ///
/// This method is used to implement the functionality of the requested command. /// This method is used to implement the functionality of the requested command.
/// This pure virtual function must be implemented in your inheriting object. /// This pure virtual function must be implemented in your inheriting object.
@@ -41,7 +44,6 @@ namespace core {
/// ///
virtual void output(std::stringstream &out); virtual void output(std::stringstream &out);
}; };
} }
+3 -42
View File
@@ -1,30 +1,11 @@
#include "CommandList.h" #include "CommandList.h"
#include "Log.h" #include "Log.h"
#include <algorithm>
#include <cctype>
namespace core { namespace core {
CommandList::CommandList(std::string delimiter, int depth) : delimiter(delimiter), depth(depth) {} CommandList::CommandList(std::string delimiter, int depth) : delimiter(delimiter), depth(depth) {}
void CommandList::add(Command &command, std::string name) { void CommandList::add(Command &command, std::string name) {
// processRequest() below upper-cases the incoming keyword before
// looking it up, but this previously stored whatever case the
// caller passed in verbatim - any registration that wasn't
// already uppercase (nearly everything outside SMTP/POP3/IMAP's
// own RFC-driven uppercase convention) could then NEVER be
// matched by any input, since upper-casing a non-uppercase string
// can't produce a match against a non-uppercase key. Confirmed:
// this alone made every Zephora game command unreachable, along
// with this class's own base-level "publish"/"subscribe"/"event"/
// etc. registrations (TCPServer's constructor). Folding the same
// way here keeps registration and lookup permanently in sync
// regardless of what case any given add() call happens to use -
// already-uppercase callers (SMTP, the BARANTMail admin console)
// are unaffected, since upper-casing an uppercase string is a
// no-op.
std::transform(name.begin(), name.end(), name.begin(),
[](unsigned char c) { return std::toupper(c); });
commands.insert(std::make_pair(name, &command)); commands.insert(std::make_pair(name, &command));
} }
@@ -40,20 +21,7 @@ namespace core {
request.split(delimiter, depth); request.split(delimiter, depth);
request.reset(); request.reset();
try { try {
// Every protocol built on this (SMTP, POP3, IMAP - RFC 5321 auto command = commands.at(request[0].str());
// 2.4, RFC 1939 3, RFC 3501 2.2.1) specifies that command
// keywords are case-insensitive, but commands are always
// registered here in uppercase and this was an exact-match
// lookup - "mail from:" or "Mail From:" (both legal on the
// wire, and not unusual from real clients) silently failed
// to dispatch at all. Only the lookup key is case-folded;
// the token instances handlers actually receive are
// untouched, so any handler doing its own case-sensitive
// parsing on arguments is unaffected.
std::string key = request[0].str();
std::transform(key.begin(), key.end(), key.begin(),
[](unsigned char c) { return std::toupper(c); });
auto command = commands.at(key);
return command->processCommand(request, session); return command->processCommand(request, session);
} }
catch(...) { catch(...) {
@@ -73,15 +41,8 @@ namespace core {
} }
int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) { int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) {
// The intent was always "typing help lists the available // for(Command *command : commands)
// commands" - this was left as a no-op stub (the commented-out // session.out << command->getName() << std::endl;
// body even called a Command::getName() that was never added to
// the base class) so every registered CommandList - including
// the top-level one behind the "help" command itself - silently
// did nothing. The map's own key IS the registered name (see
// add()), so no getName() is needed at all - just walk it directly.
for (auto &pair : commands)
session.out << pair.first << std::string("\r\n");
return true; return true;
} }
-1
View File
@@ -4,7 +4,6 @@
#include "TCPSession.h" #include "TCPSession.h"
#include "Command.h" #include "Command.h"
#include "Log.h" #include "Log.h"
#include <map>
namespace core { namespace core {
+4 -15
View File
@@ -5,28 +5,17 @@
namespace core { namespace core {
ConsoleServer::ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, ConsoleServer::ConsoleServer(EPoll &ePoll, IPAddress address) : TCPServer(ePoll, address, " ", 10, "Console Server") {
std::function<bool(std::string, std::string)> credentialChecker)
: TCPServer(ePoll, address, tlsInfo, " ", 10, "Console Server"), credentialChecker(credentialChecker) {
coreutils::Log(this); coreutils::Log(this);
} }
void ConsoleServer::logSend(std::string out) { void ConsoleServer::logSend(std::string out) {
// Was unlocked entirely (and iterated raw pointers before for(auto *session : sessions)
// TCPServer::sessions became shared_ptr-owned - see its own doc ((ConsoleSession *)session)->writeLog(out);
// comment) despite connectionRequest()/removeFromSessionList()
// both mutating this same vector under `lock` from other threads.
std::lock_guard guard(lock);
for(auto &session : sessions)
((ConsoleSession *)session.get())->writeLog(out);
} }
TCPSession * ConsoleServer::getSocketAccept(EPoll &ePoll) { TCPSession * ConsoleServer::getSocketAccept(EPoll &ePoll) {
return new ConsoleSession(ePoll, this); return new ConsoleSession(ePoll, *this);
}
bool ConsoleServer::checkCredentials(std::string user, std::string password) {
return credentialChecker ? credentialChecker(user, password) : true;
} }
} }
+4 -17
View File
@@ -1,13 +1,11 @@
#ifndef __ConsoleServer_h__ #ifndef __ConsoleServer_h__
#define __ConsoleServer_h__ #define __ConsoleServer_h__
# include "TCPServer.h" #include "includes"
#include "TLSServer.h"
#include "Command.h" #include "Command.h"
#include "EPoll.h" #include "EPoll.h"
#include "LogListener.h" #include "LogListener.h"
# include "TLSInfo.h"
# include <functional>
# include <string>
namespace core { namespace core {
@@ -23,16 +21,10 @@ namespace core {
public: public:
// //
// credentialChecker, when provided, is consulted by //
// ConsoleSession's login flow (see its own doc comment) instead
// of the previous unconditional accept-anything behavior. Left
// defaulting to nullptr so any other, unrelated use of
// ConsoleServer that never cared about authentication is
// unaffected.
// //
ConsoleServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, ConsoleServer(EPoll &ePoll, IPAddress address);
std::function<bool(std::string, std::string)> credentialChecker = nullptr);
// //
// //
@@ -42,11 +34,6 @@ namespace core {
TCPSession * getSocketAccept(EPoll &ePoll) override; TCPSession * getSocketAccept(EPoll &ePoll) override;
bool checkCredentials(std::string user, std::string password);
private:
std::function<bool(std::string, std::string)> credentialChecker;
}; };
} }
+2 -25
View File
@@ -1,11 +1,10 @@
#include "ConsoleSession.h" #include "ConsoleSession.h"
#include "ConsoleServer.h"
#include "TCPServer.h" #include "TCPServer.h"
#include "Log.h" #include "Log.h"
namespace core { namespace core {
ConsoleSession::ConsoleSession(EPoll &ePoll, TCPServer *server) : TerminalSession(ePoll, server) {} ConsoleSession::ConsoleSession(EPoll &ePoll, TCPServer &server) : TerminalSession(ePoll, server) {}
ConsoleSession::~ConsoleSession() {} ConsoleSession::~ConsoleSession() {}
@@ -28,10 +27,6 @@ namespace core {
break; break;
case WAIT_USER_PROFILE: case WAIT_USER_PROFILE:
// Previously discarded entirely - nothing captured what was
// actually typed here, so a real credential check further
// down could never have anything to compare against.
enteredUser = data.str();
status = PASSWORD; status = PASSWORD;
protocol(blank); protocol(blank);
break; break;
@@ -42,21 +37,8 @@ namespace core {
break; break;
case WAIT_PASSWORD: case WAIT_PASSWORD:
// The actual check: previously this state unconditionally
// advanced to PROMPT regardless of what (if anything) was
// entered at either step - any username/password, including
// blank, reached the command prompt. checkCredentials()
// returns true unconditionally when the owning ConsoleServer
// was built without a credentialChecker, preserving that
// exact behavior for any other use of this class.
if (((ConsoleServer *)server)->checkCredentials(enteredUser, data.str())) {
status = PROMPT; status = PROMPT;
protocol(blank); protocol(blank);
} else {
out << "Login failed." << "\r\n";
status = DONE;
protocol(blank);
}
break; break;
case PROMPT: case PROMPT:
@@ -86,11 +68,6 @@ namespace core {
} }
} }
void ConsoleSession::onConnected() {
coreutils::ZString blank("");
protocol(blank);
}
void ConsoleSession::writeLog(std::string data) { void ConsoleSession::writeLog(std::string data) {
// saveCursor(); // saveCursor();
// setCursorLocation(16, 1); // setCursorLocation(16, 1);
@@ -98,7 +75,7 @@ namespace core {
} }
void ConsoleSession::doCommand(coreutils::ZString &request) { void ConsoleSession::doCommand(coreutils::ZString &request) {
server->commands.processRequest(request, *this); server.commands.processRequest(request, *this);
} }
} }
+1 -25
View File
@@ -16,19 +16,11 @@ namespace core {
/// Extend the protocol() method in order to define the behavior and /// Extend the protocol() method in order to define the behavior and
/// protocol interaction for this socket which is a console session. /// protocol interaction for this socket which is a console session.
/// ///
/// The User:/Password: exchange is a real login when the owning
/// ConsoleServer was constructed with a credentialChecker (see its
/// own doc comment) - previously the entered username was never
/// even stored and the password was never compared against
/// anything, so any input (including blank) reached the command
/// prompt. A ConsoleServer built without a credentialChecker still
/// accepts anything, matching that original behavior exactly.
///
class ConsoleSession : public TerminalSession { class ConsoleSession : public TerminalSession {
public: public:
ConsoleSession(EPoll &ePoll, TCPServer *server); ConsoleSession(EPoll &ePoll, TCPServer &server);
~ConsoleSession(); ~ConsoleSession();
void writeLog(std::string data); void writeLog(std::string data);
@@ -36,27 +28,11 @@ namespace core {
protected: protected:
void protocol(coreutils::ZString &data) override; void protocol(coreutils::ZString &data) override;
// Previously absent entirely, so the state machine only ever
// advanced on the CLIENT's first line of input - that line was
// consumed just to drive WELCOME->LOGIN and was never used as
// real content, meaning every subsequent line was shifted one
// slot early (what the user typed as their *username* actually
// got compared as nothing, their *password* got captured as
// enteredUser, and their first real command got compared as the
// password). Harmless before real credential checking existed -
// any input reached the prompt regardless - but it broke login
// outright once checkCredentials() started actually comparing
// values. Kicking the state machine off here instead means
// "User: " is sent immediately on connect, before the client
// has sent anything at all.
void onConnected() override;
private: private:
enum Status {WELCOME, LOGIN, WAIT_USER_PROFILE, PASSWORD, WAIT_PASSWORD, PROMPT, INPUT, PROCESS, DONE}; enum Status {WELCOME, LOGIN, WAIT_USER_PROFILE, PASSWORD, WAIT_PASSWORD, PROMPT, INPUT, PROCESS, DONE};
Status status = WELCOME; Status status = WELCOME;
void doCommand(coreutils::ZString &request); void doCommand(coreutils::ZString &request);
coreutils::ZString command; coreutils::ZString command;
std::string enteredUser;
}; };
+5 -36
View File
@@ -3,7 +3,6 @@
#include "Command.h" #include "Command.h"
#include "Exception.h" #include "Exception.h"
#include <signal.h> #include <signal.h>
#include <sys/epoll.h>
namespace core { namespace core {
@@ -14,27 +13,11 @@ namespace core {
maxSockets = 1000; maxSockets = 1000;
epfd = epoll_create1(0); epfd = epoll_create1(0);
terminateThreads = false; terminateThreads = false;
started = false;
} }
// Was empty - if this EPoll's worker pool was started but stop() was EPoll::~EPoll() {}
// never explicitly called (e.g. an exception unwinding the stack
// during startup, before the caller's own normal shutdown path could
// run), the pool just kept running against this object's - and every
// Socket registered with it's - soon-to-be-freed memory. stop() itself
// is what actually joins every worker thread before anything gets
// torn down; see Thread::~Thread()'s doc comment for the crash this
// was confirmed to cause. Guarded by `started` so a never-started
// EPoll (numberOfThreads/threads still default) doesn't call stop()
// over a garbage thread count, and by `!terminateThreads` so an
// EPoll that already went through a normal stop() doesn't do it
// (and close an already-closed epfd) twice.
EPoll::~EPoll() {
if (started && !terminateThreads)
stop();
}
bool EPoll::start(int numberOfThreads, int maxSockets, ThreadScope *scope) { bool EPoll::start(int numberOfThreads, int maxSockets) {
coreutils::Log(coreutils::LOG_DEBUG_2) << "Starting epoll event processing."; coreutils::Log(coreutils::LOG_DEBUG_2) << "Starting epoll event processing.";
@@ -48,20 +31,15 @@ namespace core {
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Create thread objects into vector for number of threads requested. // Create thread objects into vector for number of threads requested.
// Hand all the threads a pointer to the EPoll object so they can run // Hand all the threads a pointer to the EPoll object so they can run
// the socket handlers - and, if given, the same shared ThreadScope // the socket handlers.
// so each one runs its onThreadStart()/onThreadEnd() hooks (see
// ThreadScope's own doc comment for why - e.g. MySQL client library
// per-thread init).
//---------------------------------------------------------------------- //----------------------------------------------------------------------
for(int ix = 0; ix < numberOfThreads; ++ix) for(int ix = 0; ix < numberOfThreads; ++ix)
threads.emplace_back(*this, scope); threads.emplace_back(*this);
for(int ix = 0; ix < numberOfThreads; ++ix) for(int ix = 0; ix < numberOfThreads; ++ix)
threads[ix].start(); threads[ix].start();
started = true;
return true; return true;
} }
@@ -95,16 +73,7 @@ namespace core {
int EPoll::processCommand(coreutils::ZString &request, TCPSession &session) { int EPoll::processCommand(coreutils::ZString &request, TCPSession &session) {
int sequence = 0; int sequence = 0;
// Was `for(auto threadx : threads)` - copied each live, running for(auto threadx : threads) {
// Thread by value. Harmless while ~Thread() was empty, but now
// that it actually joins and deletes the underlying std::thread*
// (see its own doc comment), a copy going out of scope at the end
// of each iteration would join and free the SAME thread the real
// vector entry still owns and expects to keep running - a
// double-free the next time anything lists threads on a live
// server (this method is reachable via the console's own command
// dispatch).
for(auto &threadx : threads) {
session.out << "|" << ++sequence; session.out << "|" << ++sequence;
threadx.output(session.out); threadx.output(session.out);
session.out << "|" << std::endl; session.out << "|" << std::endl;
+1 -11
View File
@@ -51,13 +51,9 @@ namespace core {
/// ///
/// @param numberOfThreads the number of threads to start for processing epoll entries. /// @param numberOfThreads the number of threads to start for processing epoll entries.
/// @param maxSockets the maximum number of open sockets that epoll will manage. /// @param maxSockets the maximum number of open sockets that epoll will manage.
/// @param scope optional - if given, its onThreadStart()/onThreadEnd() run once on
/// each worker thread, at the top/bottom of that thread's own lifetime. The SAME
/// scope instance is shared across every worker thread - see ThreadScope's own
/// doc comment. Not owned by EPoll; caller keeps it alive for the server's lifetime.
/// ///
bool start(int numberOfThreads, int maxSockets, ThreadScope *scope = NULL); ///< Start the BMAEPoll processing. bool start(int numberOfThreads, int maxSockets); ///< Start the BMAEPoll processing.
/// ///
/// Use the stop() method to initiate the shutdown process for the epoll socket management. /// Use the stop() method to initiate the shutdown process for the epoll socket management.
@@ -107,12 +103,6 @@ namespace core {
int numberOfThreads; int numberOfThreads;
std::vector<Thread> threads; std::vector<Thread> threads;
volatile bool terminateThreads; volatile bool terminateThreads;
// Guards ~EPoll()'s own call to stop() - false until start()
// actually runs, so a never-started EPoll (numberOfThreads and
// threads both still default/empty) doesn't have its destructor
// iterate a garbage thread count. See ~EPoll()'s own doc comment
// in EPoll.cpp for what this is actually protecting against.
bool started;
}; };
+11 -39
View File
@@ -1,22 +1,15 @@
#include "INotify.h" #include "INotify.h"
#include "Log.h" #include "Log.h"
#include "ZString.h" #include "ZString.h"
#include <sys/inotify.h>
namespace core { namespace core {
INotify::INotify(EPoll &ePoll) : Socket(ePoll, "INotify") { INotify::INotify(EPoll &ePoll) : Socket(ePoll, "INotify") {
setDescriptor(inotify_init()); setDescriptor(inotify_init());
// See isKnownINotify()'s own doc comment - registers this object
// as safe for Thread::run() to dispatch epoll events to directly.
std::lock_guard<std::mutex> guard(knownINotifiesMutex());
knownINotifies().insert(this);
} }
INotify::~INotify() { INotify::~INotify() {
shutdown(); shutdown();
std::lock_guard<std::mutex> guard(knownINotifiesMutex());
knownINotifies().erase(this);
} }
int INotify::addWatch(coreutils::ZString &watch) { int INotify::addWatch(coreutils::ZString &watch) {
@@ -34,53 +27,32 @@ namespace core {
ptr < buffer.getData() + buffer.getLength(); ptr < buffer.getData() + buffer.getLength();
ptr += sizeof(struct inotify_event) + event->len) { ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr; event = (const struct inotify_event *) ptr;
coreutils::ZString name(event->name);
// event->name only exists - and is only NUL-terminated - when
// event->len > 0 (inotify(7): "This field is present only when
// the event relates to a file inside a watched directory ...
// otherwise ... this field is absent."). For events with
// len == 0 (e.g. IN_UNMOUNT, IN_Q_OVERFLOW, IN_IGNORED, or
// self-watch events), event->name is not a valid C-string at
// all - calling strlen() on it (as the old ZString(const char*)
// one-arg constructor did unconditionally here) reads past
// whatever garbage follows in the socket's 4096-byte receive
// buffer looking for a NUL, a real heap-buffer-overflow read
// confirmed via AddressSanitizer under concurrent load (the
// eventual heap corruption this caused was previously
// misdiagnosed as a MySQL client thread-safety issue, since it
// surfaces on whichever EPoll worker thread happens to service
// this watcher's socket and corrupts unrelated heap state).
// strnlen() bounded by event->len (rather than the one-arg
// ZString ctor's unbounded strlen) matches the kernel's actual
// guarantee: name is NUL-padded to exactly event->len bytes.
coreutils::ZString name = event->len > 0
? coreutils::ZString(event->name, strnlen(event->name, event->len))
: coreutils::ZString("");
if(event->mask & IN_ACCESS) if(event->mask & IN_ACCESS)
inAccess(name); inAccess(name);
if(event->mask & IN_ATTRIB) if(event->mask & IN_ATTRIB)
inAttrib(name.str()); inAttrib(std::string(event->name));
if(event->mask & IN_CLOSE_WRITE) if(event->mask & IN_CLOSE_WRITE)
inCloseWrite(name.str()); inCloseWrite(std::string(event->name));
if(event->mask & IN_CLOSE_NOWRITE) if(event->mask & IN_CLOSE_NOWRITE)
inCloseNoWrite(name.str()); inCloseNoWrite(std::string(event->name));
if(event->mask & IN_CREATE) if(event->mask & IN_CREATE)
inCreate(name); inCreate(name);
if(event->mask & IN_DELETE) if(event->mask & IN_DELETE)
inDelete(name.str()); inDelete(std::string(event->name));
if(event->mask & IN_DELETE_SELF) if(event->mask & IN_DELETE_SELF)
inDeleteSelf(name.str()); inDeleteSelf(std::string(event->name));
if(event->mask & IN_MODIFY) if(event->mask & IN_MODIFY)
inModify(name.str()); inModify(std::string(event->name));
if(event->mask & IN_MOVE_SELF) if(event->mask & IN_MOVE_SELF)
inMoveSelf(name.str()); inMoveSelf(std::string(event->name));
if(event->mask & IN_MOVED_FROM) if(event->mask & IN_MOVED_FROM)
inMovedFrom(name.str()); inMovedFrom(std::string(event->name));
if(event->mask & IN_MOVED_TO) if(event->mask & IN_MOVED_TO)
inMovedTo(name.str()); inMovedTo(std::string(event->name));
if(event->mask & IN_OPEN) if(event->mask & IN_OPEN)
inOpen(name.str()); inOpen(std::string(event->name));
} }
} }
+1 -37
View File
@@ -1,9 +1,8 @@
#ifndef __INotify_h__ #ifndef __INotify_h__
# define __INotify_h__ # define __INotify_h__
#include "includes"
#include "Socket.h" #include "Socket.h"
#include <set>
#include <mutex>
namespace core { namespace core {
@@ -31,41 +30,6 @@ namespace core {
virtual void inMovedTo(std::string name) {} virtual void inMovedTo(std::string name) {}
virtual void inOpen(std::string name) {} virtual void inOpen(std::string name) {}
/// core::INotify is a long-lived Socket subclass that is neither
/// a TCPSession (shared_ptr/pin()-owned) nor a listening
/// TCPServer - it is normally embedded as a base subobject of a
/// server object constructed once in main() and living for the
/// whole process (e.g. mail::SMTPServer's queue-directory watch).
/// Thread::run()'s epoll dispatch only ever recognized those two
/// categories of raw pointer as safe to call directly (see
/// TCPServer::isKnownListeningSocket()'s own doc comment) -
/// anything else fell through to the "stale/unrecognized epoll
/// event" branch and was silently dropped forever. Confirmed
/// live: a real IN_CREATE event on a mail queue directory logged
/// exactly that "Ignoring stale/unrecognized epoll event" line
/// and the newly queued message was never delivered - it just sat
/// in the queue until the next process restart, whose
/// processExisting() directory scan (a plain synchronous loop,
/// entirely independent of epoll) happened to flush the backlog
/// and made the bug look intermittent. Registering the instance here,
/// mirroring listeningSockets() exactly, gives Thread::run() the
/// same kind of safe "yes, dispatch to this raw pointer directly"
/// signal for INotify objects too.
static bool isKnownINotify(void *ptr) {
std::lock_guard<std::mutex> guard(knownINotifiesMutex());
return knownINotifies().count(ptr) > 0;
}
private:
static std::mutex &knownINotifiesMutex() {
static std::mutex m;
return m;
}
static std::set<void *> &knownINotifies() {
static std::set<void *> s;
return s;
}
}; };
} }
-6
View File
@@ -1,10 +1,4 @@
#include "IPAddress.h" #include "IPAddress.h"
#include <cstring>
#include <sstream>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
namespace core { namespace core {
+3 -4
View File
@@ -1,13 +1,12 @@
#ifndef __IPAddress_h__ #ifndef __IPAddress_h__
#define __IPAddress_h__ #define __IPAddress_h__
#include <sys/socket.h> #include "includes"
#include <netinet/in.h> #include "Object.h"
#include <string>
namespace core { namespace core {
class IPAddress { class IPAddress : public Object {
public: public:
IPAddress(); IPAddress();
+1 -2
View File
@@ -1,9 +1,8 @@
#ifndef __IPAddressList_h__ #ifndef __IPAddressList_h__
#define __IPAddressList_h__ #define __IPAddressList_h__
#include "includes"
#include "IPAddress.h" #include "IPAddress.h"
#include <map>
#include <string>
namespace core { namespace core {
+91
View File
@@ -0,0 +1,91 @@
#
# 'make' build executable file 'main'
# 'make clean' removes all .o and executable files
#
# define the Cpp compiler to use
CXX = g++
# define any compile-time flags
CXXFLAGS := -std=c++17 -Wall -Wextra -g
# define library paths in addition to /usr/lib
# if I wanted to include libraries not in /usr/lib I'd specify
# their path using -Lpath, something like:
LFLAGS =
# define output directory
OUTPUT := output
# define source directory
SRC := src
# define include directory
INCLUDE := include
# define lib directory
LIB := lib
ifeq ($(OS),Windows_NT)
MAIN := main.exe
SOURCEDIRS := $(SRC)
INCLUDEDIRS := $(INCLUDE)
LIBDIRS := $(LIB)
FIXPATH = $(subst /,\,$1)
RM := del /q /f
MD := mkdir
else
MAIN := main
SOURCEDIRS := $(shell find $(SRC) -type d)
INCLUDEDIRS := $(shell find $(INCLUDE) -type d)
LIBDIRS := $(shell find $(LIB) -type d)
FIXPATH = $1
RM = rm -f
MD := mkdir -p
endif
# define any directories containing header files other than /usr/include
INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%))
# define the C libs
LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%))
# define the C source files
SOURCES := $(wildcard $(patsubst %,%/*.cpp, $(SOURCEDIRS)))
# define the C object files
OBJECTS := $(SOURCES:.cpp=.o)
#
# The following part of the makefile is generic; it can be used to
# build any executable just by changing the definitions above and by
# deleting dependencies appended to the file from 'make depend'
#
OUTPUTMAIN := $(call FIXPATH,$(OUTPUT)/$(MAIN))
all: $(OUTPUT) $(MAIN)
@echo Executing 'all' complete!
$(OUTPUT):
$(MD) $(OUTPUT)
$(MAIN): $(OBJECTS)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(OUTPUTMAIN) $(OBJECTS) $(LFLAGS) $(LIBS)
# this is a suffix replacement rule for building .o's from .c's
# it uses automatic variables $<: the name of the prerequisite of
# the rule(a .c file) and $@: the name of the target of the rule (a .o file)
# (see the gnu make manual section about automatic variables)
.cpp.o:
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
.PHONY: clean
clean:
$(RM) $(OUTPUTMAIN)
$(RM) $(call FIXPATH,$(OBJECTS))
@echo Cleanup complete!
run: all
./$(OUTPUTMAIN)
@echo Executing 'run: all' complete!
+19
View File
@@ -0,0 +1,19 @@
#ifndef __Object_h__
#define __Object_h__
#include "includes"
namespace core {
class Object {
public:
std::string name;
std::string tag;
};
}
#endif
+9 -3
View File
@@ -1,14 +1,20 @@
#ifndef __SessionFilter_h__ #ifndef __SessionFilter_h__
#define __SessionFilter_h__ #define __SessionFilter_h__
namespace core { //#include "Session.h"
#include "Object.h"
namespace core
{
class TCPSession; class TCPSession;
class SessionFilter { class SessionFilter : public Object
{
public: public:
virtual bool test(TCPSession &session) { virtual bool test(TCPSession &session)
{
return true; return true;
} }
}; };
+31 -258
View File
@@ -3,7 +3,6 @@
#include "Exception.h" #include "Exception.h"
#include "Log.h" #include "Log.h"
#include "ZString.h" #include "ZString.h"
#include <iostream>
namespace core namespace core
{ {
@@ -49,17 +48,6 @@ namespace core
return descriptor; 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::onRegister() {}
void Socket::onRegistered() {} void Socket::onRegistered() {}
@@ -69,142 +57,36 @@ namespace core
void Socket::onUnregistered() {} void Socket::onUnregistered() {}
bool Socket::eventReceived(struct epoll_event event, long long eventId) { bool Socket::eventReceived(struct epoll_event event, long long eventId) {
coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process beginning for socket " << getDescriptor(); // coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process beginning for socket " << getDescriptor();
if(inHandler) if(inHandler)
coreutils::Log(coreutils::LOG_DEBUG_2) << "inHandler was already true."; // coreutils::Log(coreutils::LOG_DEBUG_2) << "inHandler was already true.";
inHandler = true; inHandler = true;
if(event.events & EPOLLRDHUP) { if(event.events & EPOLLRDHUP) {
coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLRDHUP"; // coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLRDHUP";
readHangup = true; readHangup = true;
shutdown("hangup received"); shutdown("hangup received");
} }
if(event.events & EPOLLIN) { if(event.events & EPOLLIN) {
coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLIN"; // coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLIN";
// A read just happened (or at least an opportunity for OpenSSL lock.lock();
// to make progress on whatever it needed a read for) - give any receiveDataNotification();
// write that was waiting on that a fresh attempt via this same if(!shutDown) {
// call's own trailing resetSocket(), rather than leaving inHandler = false;
// EPOLLWRNORM permanently un-armed from a single earlier stall. lock.unlock();
waitingOnReadToWrite = false; resetSocket();
coreutils::ZString zbuffer(buffer, length);
// receiveData() calls all the way down through
// onDataReceived()/HTTPRequest::doParse()/application request
// handling with NO exception protection anywhere in that chain -
// confirmed live: a single thrown coreutils::Exception (e.g. a
// malformed request hitting a parse-time throw, well before
// ApiSession::handleComplete()'s own try/catch around
// match.render() ever runs) reached std::terminate() and killed
// the entire server process, not just this one connection, since
// this runs on an EPoll worker thread and C++ terminates the
// whole program on an exception escaping a thread. The old
// lock.lock()/lock.unlock() pair (no RAII) also leaked the lock
// permanently on this same path - std::lock_guard fixes that as
// a side effect of adding the try/catch here.
try {
std::lock_guard<std::mutex> guard(lock);
receiveData(zbuffer);
} catch(coreutils::Exception &e) {
// Also to stderr, not just Log() - this process's Log class
// writes through a static listener/file neither this server
// nor its main() ever configures (its own startup banner
// uses plain std::cout, not Log at all), so a Log()-only
// message here would be silently dropped. stderr reliably
// lands in the log file given how this process is started
// (both stdout and stderr redirected to the same file).
std::cerr << "Exception processing socket " << getDescriptor() << ": " << e.text << std::endl;
coreutils::Log(coreutils::LOG_EXCEPT) << "Exception processing socket " << getDescriptor() << ": " << e.text;
shutdown("exception during receiveData: " + e.text);
} catch(std::exception &e) {
std::cerr << "std::exception processing socket " << getDescriptor() << ": " << e.what() << std::endl;
coreutils::Log(coreutils::LOG_EXCEPT) << "std::exception processing socket " << getDescriptor() << ": " << e.what();
shutdown(std::string("exception during receiveData: ") + e.what());
} catch(...) {
std::cerr << "Unknown exception processing socket " << getDescriptor() << std::endl;
coreutils::Log(coreutils::LOG_EXCEPT) << "Unknown exception processing socket " << getDescriptor();
shutdown("unknown exception during receiveData");
} }
} }
if(event.events & EPOLLWRNORM) { if(event.events & EPOLLWRNORM) {
coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLWRNORM"; // coreutils::Log(coreutils::LOG_DEBUG_2) << "EPOLLWRNORM";
writeSocket(); writeSocket();
}
// A graceful shutdown() (session.terminate() -> "goodbye" reply
// queued via write(), then shutdown() called - see e.g. IMAP's
// LOGOUT/POP3's QUIT/SMTP's QUIT) used to make keepAlive false
// the moment shutDown was set, in this SAME event cycle that had
// just queued that reply - Thread::run() then deletes this object
// immediately on a false return, before the reply was ever
// written to the actual socket (write() only queues into fifo;
// the real send() happens in writeSocket(), only reachable via a
// LATER EPOLLWRNORM event this object never lived to receive).
// Confirmed live: LOGOUT/QUIT connections closed correctly but
// their own final reply line never reached the client. Staying
// alive while shutDown is set but fifo still has data - as long
// as the peer hasn't already hung up (readHangup - trying to
// drain to a peer that's already gone is pointless, not graceful)
// - gives that queued reply a real chance to go out before this
// socket actually closes.
bool keepAlive = preventSelfDelete || !shutDown || (fifo.size() > 0 && !readHangup);
// resetSocket() re-arms this fd via EPOLL_CTL_MOD, which makes it
// immediately eligible for delivery to ANY worker thread's
// epoll_wait() - including one already blocked and about to wake
// up right away. The old code called resetSocket() from inside
// the branches above while several more statements (an
// unconditional inHandler write, this Log call, and `return
// !shutDown`) still touched `this` afterward - a second thread
// could legitimately receive the re-armed event, finish handling
// it, and delete this object (Thread::run() does exactly that
// when eventReceived() returns false) before this thread's own
// remaining statements ran, a heap-use-after-free confirmed via
// AddressSanitizer under concurrent load. resetSocket() must be
// the last thing that ever touches `this`, with the return value
// already captured in a local (`keepAlive`) beforehand.
coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process ending for socket " << getDescriptor();
// Was cleared several statements above (right after the EPOLLIN/
// EPOLLWRNORM handling), with no lock held around clearing it or
// calling resetSocket() below - a genuinely separate race from the
// one documented above: Socket::write() re-arms via this same
// resetSocket() (see its own doc comment there) whenever it can
// grab `lock`, from whatever thread called it (e.g. a broadcast
// hitting this session while ITS OWN eventReceived() call is still
// running, past receiveData()'s lock_guard but before reaching
// here). A first attempt narrowed this to just checking `inHandler`
// in both places, without holding `lock` across the check-and-act
// in either one - still a real TOCTOU window (inHandler read as
// false here, then write() on another thread sees false too and
// wins the race to resetSocket() first), confirmed live via
// AddressSanitizer as a genuine double-delete of the same session
// object (two threads both reaching ~TCPSession() on it) even
// after that narrowing. Holding `lock` for the whole
// clear-then-reset sequence - the exact same mutex write() already
// takes before its own resetSocket() call - makes the two
// mutually exclusive for real: whichever thread gets there first
// fully finishes (clearing inHandler and re-arming, or observing
// inHandler still true and skipping) before the other can act at
// all, closing the window rather than just narrowing it.
if(keepAlive) {
lock.lock();
inHandler = false; inHandler = false;
resetSocket(); resetSocket();
lock.unlock();
} }
// else: this object is about to be delete()'d by Thread::run() the inHandler = false;
// moment this call returns, unconditionally, regardless of // coreutils::Log(coreutils::LOG_DEBUG_1) << "Event process ending for socket " << getDescriptor();
// anything write() does. Leaving inHandler true forever (never return !shutDown;
// clearing it here) means no other thread's write() can ever
// resetSocket() this dying object in the meantime - it has no
// future to reset it FOR, and letting write() re-arm it here, even
// briefly, was exactly the remaining gap: a third thread could
// pick up that re-armed fd and touch/delete an object Thread::run()
// was already about to free, a genuine double-delete confirmed
// live via AddressSanitizer even after write()/resetSocket() were
// otherwise fully serialized against each other.
return keepAlive;
} }
void Socket::connectionRequest() {}
void Socket::onDataReceived(std::string data) { void Socket::onDataReceived(std::string data) {
throw coreutils::Exception("Need to override onDataReceived.", __FILE__, __LINE__, -1); throw coreutils::Exception("Need to override onDataReceived.", __FILE__, __LINE__, -1);
} }
@@ -213,7 +95,10 @@ namespace core
onDataReceived(std::string(data.getData(), data.getLength())); onDataReceived(std::string(data.getData(), data.getLength()));
} }
void Socket::receiveData(coreutils::ZString &buffer) { void Socket::receiveDataNotification() {}
coreutils::ZString blank(""); coreutils::ZString blank("");
if(buffer.getLength() <= 0) if(buffer.getLength() <= 0)
throw coreutils::Exception("Request to receive data with a zero buffer length.", __FILE__, __LINE__, -1); throw coreutils::Exception("Request to receive data with a zero buffer length.", __FILE__, __LINE__, -1);
@@ -223,15 +108,20 @@ namespace core
coreutils::ZString zbuffer(buffer.getData(), len); coreutils::ZString zbuffer(buffer.getData(), len);
// coreutils::Log(coreutils::LOG_DEBUG_1) << zbuffer; // coreutils::Log(coreutils::LOG_DEBUG_1) << zbuffer;
onDataReceived(zbuffer); onDataReceived(zbuffer);
} else { }
else
{
error = errno; error = errno;
switch (error) {
switch (error)
{
// When a listening socket receives a connection // When a listening socket receives a connection
// request we get one of these. // request we get one of these.
//
case ENOTCONN: case ENOTCONN:
connectionRequest(); onDataReceived(blank);
break; break;
case ECONNRESET: case ECONNRESET:
@@ -243,94 +133,13 @@ namespace core
} }
} }
int Socket::sendData(const char *data, int length) {
return ::write(descriptor, data, length);
}
void Socket::writeSocket() { void Socket::writeSocket() {
outlock.lock(); outlock.lock();
// coreutils::Log(coreutils::LOG_DEBUG_3) << "writing data to socket " << getDescriptor(); // coreutils::Log(coreutils::LOG_DEBUG_3) << "writing data to socket " << getDescriptor();
if(fifo.size() > 0) { if(fifo.size() > 0) {
// No longer gated on !shutDown: writeSocket() only ever runs in if(!shutDown)
// response to an EPOLLWRNORM event this object explicitly int rc = ::write(descriptor, fifo.front().c_str(), fifo.front().length());
// re-armed for (see resetSocket()'s own doc comment) - refusing
// to actually perform that queued write once shutDown was set
// was exactly what left a graceful QUIT/LOGOUT's own reply
// stuck in the fifo forever, never reaching the client.
{
// sendData()'s return value was previously discarded and the
// fifo entry popped unconditionally after ONE call - correct
// only by accident, since a single write()/SSL_write() call is
// never guaranteed to accept an entire buffer. A response
// small enough to fit the kernel's socket send buffer in one
// shot (true for almost everything on this site, and always
// true testing over loopback, where that buffer is
// effectively unlimited) went out fine either way - but a
// large one (confirmed with a real 208KB response, mostly an
// embedded base64 font: fine over loopback, but only ~65KB of
// it ever reached a real remote client before the rest was
// silently dropped here) left the client waiting forever for
// bytes matching its own Content-Length that were never
// coming. Now: pop only once the FULL entry has gone out;
// otherwise trim off just what was actually sent and leave
// the remainder queued - resetSocket() (called at the end of
// every eventReceived(), see its own doc comment) keeps
// re-arming EPOLLWRNORM as long as the fifo isn't empty, so
// the next write-ready event retries this same entry
// automatically. A return <= 0 (EAGAIN or a transient error)
// sends nothing this round and is handled identically - just
// leave the entry queued for the next attempt.
int sent = sendData(fifo.front().c_str(), fifo.front().length());
if(sent > 0) {
consecutiveWriteStalls = 0;
waitingOnReadToWrite = false;
if((size_t)sent >= fifo.front().length())
fifo.pop(); fifo.pop();
else
fifo.front().erase(0, sent);
} else if(sendNeedsRead()) {
// Not a stall in the CPU-spinning sense consecutiveWriteStalls
// exists to catch - resetSocket() below simply won't re-arm
// EPOLLWRNORM while this is true, so there's nothing to busy-
// retry until a real EPOLLIN event (already always watched
// for) brings in whatever OpenSSL is actually waiting to read.
waitingOnReadToWrite = true;
} else if(++consecutiveWriteStalls > 50) {
// sendData() keeps failing for a reason that has nothing to
// do with the socket's own write-readiness (confirmed real
// case: TLS::sendData() returning SSL_ERROR_WANT_READ,
// which needs a READ to make progress, not another write
// attempt) - resetSocket() re-arms EPOLLWRNORM regardless,
// and an already-writable TCP socket makes it fire again
// immediately, so this cycle would otherwise spin forever
// doing real work at the CPU's own top speed. See
// consecutiveWriteStalls' own doc comment in Socket.h for
// the confirmed real damage (a 55GB, then 12.9GB debug log)
// this caused before being noticed.
coreutils::Log(coreutils::LOG_EXCEPT) << "Socket " << getDescriptor() << ": " << consecutiveWriteStalls << " consecutive writes made zero progress - giving up and shutting down.";
// shutdown() alone only sets shutDown=true and expects the
// fifo to drain normally before the connection actually
// closes (see eventReceived()'s keepAlive: "!shutDown ||
// (fifo.size() > 0 && !readHangup)") - exactly backwards
// for THIS path, since the whole reason we're here is that
// the front of the fifo can never be sent. Leaving it
// queued meant keepAlive stayed true forever, resetSocket()
// kept re-arming EPOLLWRNORM because fifo.size() > 0, the
// very next write attempt hit this identical stall
// immediately, and shutdown() got called again - forever,
// at whatever rate the kernel could fire EPOLLWRNORM.
// Confirmed live: 2.4 million+ repeats of this exact log
// line for one socket in under 4 milliseconds, pegging a
// worker thread at 100% CPU indefinitely (a real game
// server, brought down by nothing more than an ordinary
// port-scanner connection). Clearing the undeliverable data
// here lets keepAlive correctly evaluate to just !shutDown
// (now false) on the very next cycle, so the connection
// actually tears down instead of spinning forever.
std::queue<std::string>().swap(fifo);
shutdown("stuck write retry loop - no progress after repeated attempts");
}
}
} }
outlock.unlock(); outlock.unlock();
} }
@@ -339,35 +148,7 @@ namespace core
outlock.lock(); outlock.lock();
fifo.emplace(data); fifo.emplace(data);
outlock.unlock(); outlock.unlock();
// Was unconditional - re-arming here (resetSocket() -> EPOLL_CTL_MOD)
// while this socket's OWN eventReceived() call is still mid-flight
// on another thread (inHandler true - see eventReceived()'s own
// comment on exactly this race) makes the fd immediately eligible
// for a THIRD thread to pick up a fresh event and potentially
// delete this object (Thread::run() does exactly that on a false
// return) while the first thread is still touching it - a genuine
// use-after-free, matching a real heap-corruption crash confirmed
// live via dmesg/AddressSanitizer under concurrent load.
// eventReceived() already re-arms itself once it's actually done
// (checking the current fifo, which already includes whatever was
// just queued above); skip doing it a second, early time from here
// while that's still in progress - the data isn't lost, just
// picked up on that call's own re-arm instead of this one's.
//
// inHandler must be re-checked AFTER acquiring `lock`, not just
// before try_lock() - checking it first and then separately
// acquiring the lock leaves a real TOCTOU gap (this thread reads
// inHandler==false, eventReceived() flips it back and forth on
// another thread, both end up calling resetSocket()); a first
// attempt at this fix only checked before, and a genuine
// double-delete of the same session object still reproduced live
// under AddressSanitizer despite it. eventReceived() holds this
// exact same `lock` across its own inHandler-clear-then-reset
// sequence now (see its own comment), so re-checking inHandler
// only after try_lock() succeeds makes the two truly mutually
// exclusive.
if(lock.try_lock()) { if(lock.try_lock()) {
if(!inHandler)
resetSocket(); resetSocket();
lock.unlock(); lock.unlock();
} }
@@ -405,17 +186,9 @@ namespace core
struct epoll_event event; struct epoll_event event;
event.data.ptr = this; event.data.ptr = this;
event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET; event.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLET;
if(fifo.size() > 0 && !waitingOnReadToWrite) if(fifo.size() > 0)
event.events |= EPOLLWRNORM; event.events |= EPOLLWRNORM;
// Was `if(!shutDown)` - refused to re-arm at all once shutting if(!shutDown)
// down, even with data still queued to send (e.g. a graceful
// QUIT/LOGOUT's own final reply - see eventReceived()'s own doc
// comment on keepAlive for the full chain this broke). Re-arming
// for just long enough to drain the fifo, when the peer is still
// there to receive it (!readHangup - draining to an already-gone
// peer is pointless), is what actually lets that reply go out
// before this socket closes.
if(!shutDown || (fifo.size() > 0 && !readHangup))
epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_MOD, getDescriptor(), &event); epoll_ctl(ePoll.getDescriptor(), EPOLL_CTL_MOD, getDescriptor(), &event);
} }
+5 -109
View File
@@ -1,11 +1,9 @@
#ifndef __Socket_h__ #ifndef __Socket_h__
#define __Socket_h__ #define __Socket_h__
#include "includes"
#include "Object.h"
#include "ZString.h" #include "ZString.h"
#include <queue>
#include <mutex>
#include <atomic>
#include <sys/epoll.h>
namespace core { namespace core {
@@ -124,29 +122,8 @@ namespace core {
protected: protected:
// Set by Timer's own constructor (see its own doc comment) - a
// Timer is always a Socket subobject privately/multiply inherited
// alongside a session's own real network Socket (e.g. ZephoraSession
// : TCPSession, Timer), never a standalone connection with its own
// independent lifetime. eventReceived()'s ordinary keepAlive
// contract - returning false means "delete me" (see Thread::run(),
// which then does exactly that on this object's own Socket* base) -
// is correct for a real connection but catastrophic here: deleting
// through a pointer to just the Timer subobject deletes the entire
// composite object at the WRONG size (Timer/Socket's own smaller
// size, not the true derived object's), a new-delete-type-mismatch
// confirmed live via AddressSanitizer under concurrent load. A
// Timer's own fd is already correctly torn down as part of its
// containing object's own destructor chain; it never needs (and
// must never trigger) independent deletion through this path.
bool preventSelfDelete = false;
EPoll &ePoll; // The EPoll control object. EPoll &ePoll; // The EPoll control object.
void setBufferSize(int length);
int getBufferSize();
/// ///
/// The onConnected method is called when the socket is ready to communicate. /// The onConnected method is called when the socket is ready to communicate.
/// Writing to the socket can begin on this call to initiate a contact with the /// Writing to the socket can begin on this call to initiate a contact with the
@@ -155,13 +132,6 @@ namespace core {
// virtual void onConnected(); ///< Called when socket is open and ready to communicate. // virtual void onConnected(); ///< Called when socket is open and ready to communicate.
///
/// The connectionRequest method is called to instantiate a new session handler object
/// and form a new connection to a listening server.
///
virtual void connectionRequest();
/// ///
/// ///
/// ///
@@ -169,47 +139,10 @@ namespace core {
// virtual void onDisconnected(); ///< Called when socket is closing and no longer ready to communicate. // virtual void onDisconnected(); ///< Called when socket is closing and no longer ready to communicate.
/// ///
/// The onDataReceived method is called when the socket has received an event from /// This method is called when data is ready to be read from the socket.
/// epoll and there is data ready to be read from the socket. The default handler
/// will pull the data and put it into the streambuf for the socket. EPOLLIN
///
/// @param data the data that has been received from the socket.
/// ///
virtual void onDataReceived(std::string data); ///< Called when data is received from the socket. virtual void readDataNotification();
///
///
///
virtual void onDataReceived(coreutils::ZString &data);
///
/// receiveData will read the data from the socket and place it in the socket buffer.
/// TLS layer overrides this to be able to read from SSL.
///
virtual void receiveData(coreutils::ZString &buffer);
///
/// sendData writes data to the socket descriptor. TLS layer overrides this to
/// write through SSL instead of a raw socket write.
///
/// @return The number of bytes written, or -1 on error.
///
virtual int sendData(const char *data, int length);
///
/// Overridden by TCPSocket to report whether the last sendData()
/// call failed specifically because the TLS layer needs a READ to
/// make progress (SSL_ERROR_WANT_READ), as opposed to any other
/// reason - see waitingOnReadToWrite's own doc comment for why
/// writeSocket()/resetSocket() need to know this. Always false for
/// a plain (non-TLS) socket, which has no such distinction.
///
virtual bool sendNeedsRead() { return false; }
private: private:
@@ -217,11 +150,7 @@ namespace core {
int descriptor = -1; int descriptor = -1;
std::mutex outlock; std::mutex outlock;
bool readHangup = false; bool readHangup = false;
// Was `volatile bool` - volatile gives no actual thread-safety volatile bool inHandler = false;
// (no atomicity, no cross-thread visibility guarantee), and this
// flag is now load-bearing (see Socket::write()'s own check of it)
// rather than just feeding a diagnostic log line.
std::atomic<bool> inHandler{false};
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// the writeSocket is called when epoll has received a write request for a socket. // the writeSocket is called when epoll has received a write request for a socket.
@@ -237,41 +166,8 @@ namespace core {
// int_type pbackfail(int_type ch); // int_type pbackfail(int_type ch);
// streamsize showmanyc(); // streamsize showmanyc();
char *buffer; // This is a pointer to the managed buffer space.
int length; // This is the length of the buffer.
std::queue<std::string> fifo; std::queue<std::string> fifo;
// Counts consecutive writeSocket() calls that sent zero bytes for
// the SAME queued fifo entry (SSL_ERROR_WANT_READ from
// TLS::sendData() is the confirmed real case - see writeSocket()'s
// own doc comment). resetSocket() re-arms EPOLLWRNORM whenever the
// fifo is non-empty regardless of WHY the last send failed, and an
// already-writable TCP socket (the common case) makes EPOLLWRNORM
// fire again essentially immediately - so a send that keeps
// failing for a reason unrelated to socket writability (like SSL
// needing a read first) spins this cycle as fast as the CPU can
// run it, forever, with zero actual progress. Confirmed live: one
// stuck connection generated a 55GB, then a 12.9GB debug log
// before being noticed, at roughly 20 log lines per millisecond.
// Reset to 0 on any successful send (even a partial one - real
// progress happened); past the threshold, the connection is
// clearly never going to un-stick itself and gets shut down.
int consecutiveWriteStalls = 0;
// Set by writeSocket() when the last failed send specifically
// needed a read to proceed (sendNeedsRead() true) rather than
// being genuinely stalled on write-readiness. resetSocket() skips
// adding EPOLLWRNORM to the re-armed event mask while this is
// true - re-arming it anyway just spins the CPU as fast as it can
// loop, since an already-writable TCP socket keeps firing
// EPOLLWRNORM immediately while the actual blocker (more bytes
// from the peer) has nothing to do with write-readiness at all.
// Cleared on any EPOLLIN event (see eventReceived()) so the write
// gets a fresh attempt via that same event's own trailing
// resetSocket() call, once there's real reason to expect progress.
bool waitingOnReadToWrite = false;
void resetSocket(); void resetSocket();
std::mutex lock; std::mutex lock;
+3 -17
View File
@@ -25,15 +25,8 @@ namespace core {
std::stringstream out; std::stringstream out;
out << "cancel:" << id << std::endl; out << "cancel:" << id << std::endl;
// subscribers holds plain observer pointers, not ownership (see
// TCPSession::pin()'s own doc comment) - a subscriber's session may
// have already started tearing down on another thread by the time
// this runs. pin() safely tells the two cases apart instead of
// blindly dereferencing a pointer that might already be mid-
// destruction or fully freed.
for (auto subscriber : subscribers) { for (auto subscriber : subscribers) {
if (auto safe = subscriber->pin()) subscriber->write(out.str());
safe->write(out.str());
} }
} }
@@ -72,14 +65,8 @@ namespace core {
} }
int Subscription::event(std::stringstream &out) { int Subscription::event(std::stringstream &out) {
// See ~Subscription()'s comment above - same pin()-before-use
// reasoning applies to every subscriber write here. This is the
// broadcast path for every *SUBSCRIBERS-mode topic (chat, item
// database updates, etc.) - the exact crash pin() closes here was
// confirmed live under concurrent load this session.
for (auto subscriber = subscribers.begin(); subscriber < subscribers.end(); ++subscriber) for (auto subscriber = subscribers.begin(); subscriber < subscribers.end(); ++subscriber)
if (auto safe = (*subscriber)->pin()) (*subscriber)->write(out.str());
safe->write(out.str());
return 1; return 1;
} }
@@ -98,8 +85,7 @@ namespace core {
void Subscription::sendToAll(std::stringstream &data, TCPSession &sender) { void Subscription::sendToAll(std::stringstream &data, TCPSession &sender) {
for (auto session : subscribers) for (auto session : subscribers)
if (session != &sender) if (session != &sender)
if (auto safe = session->pin()) session->write(data.str());
safe->write(data.str());
data.str(""); data.str("");
} }
-1
View File
@@ -7,7 +7,6 @@
#include "ZString.h" #include "ZString.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
namespace core { namespace core {
+33 -129
View File
@@ -4,82 +4,61 @@
#include "SubscriptionHandlerFactory.h" #include "SubscriptionHandlerFactory.h"
#include "TCPServer.h" #include "TCPServer.h"
#include <algorithm> #include <algorithm>
#include <mutex>
namespace core namespace core {
{
int SubscriptionManager::add(Subscription &subscription) int SubscriptionManager::add(Subscription &subscription) {
{
lock.lock(); lock.lock();
subscriptions.insert(std::make_pair(subscription.id, &subscription)); subscriptions.insert(std::make_pair(subscription.id, &subscription));
lock.unlock(); lock.unlock();
return 1; return 1;
} }
bool SubscriptionManager::onClearSubscription(coreutils::MString temp, coreutils::MString key) bool SubscriptionManager::onClearSubscription(coreutils::MString temp, coreutils::MString key) {
{
temp = ""; temp = "";
key = ""; key = "";
return true; return true;
} }
int SubscriptionManager::removeSessionSubscriptions(TCPSession &session) int SubscriptionManager::removeSessionSubscriptions(TCPSession &session) {
{
int countSubscribed = 0; int countSubscribed = 0;
int countPublished = 0; int countPublished = 0;
int count = 0; int count = 0;
lock.lock(); lock.lock();
coreutils::MString temp = ""; coreutils::MString temp = "";
for (auto [key, subscription] : subscriptions) for (auto [key, subscription] : subscriptions) {
{ if (temp != "") {
if (temp != "")
{
subscriptions.erase(temp); subscriptions.erase(temp);
temp = ""; temp = "";
} }
countSubscribed += subscription->unsubscribe(session); countSubscribed += subscription->unsubscribe(session);
if (subscription->subscriptionOwners == coreutils::MString(session.alias)) if (subscription->subscriptionOwners == session.alias) {
{ if (subscription->selectionOfOwnerForSubscription == "*DIE") {
if (subscription->selectionOfOwnerForSubscription == "*DIE")
{
onClearSubscription(temp, key); onClearSubscription(temp, key);
} }
else if (subscription->will == "*NEXT") else if (subscription->will == "*NEXT") {
{ if (subscription->subscribers.size() < 1) {
if (subscription->subscribers.size() < 1)
{
coreutils::Log(coreutils::LOG_DEBUG_1) << "There is not enough people to move to the next"; coreutils::Log(coreutils::LOG_DEBUG_1) << "There is not enough people to move to the next";
onClearSubscription(temp, key); onClearSubscription(temp, key);
} }
else else {
{ for (auto subscribed = subscription->subscribers.begin(); subscribed < subscription->subscribers.end(); subscribed++) {
for (auto subscribed = subscription->subscribers.begin(); subscribed < subscription->subscribers.end(); subscribed++) if ((*subscribed)->alias == session.alias) {
{
// pin() before reading .alias - a raw subscriber
// pointer here could already be mid-destruction or
// fully freed on another thread (see
// TCPSession::pin()'s own doc comment).
if (auto safe = (*subscribed)->pin())
if (safe->alias == session.alias)
{
subscription->subscribers.erase(subscribed++); subscription->subscribers.erase(subscribed++);
} }
subscription->subscriptionOwner(session.alias, "*NEXT"); subscription->subscriptionOwner(session.alias, "*NEXT");
} }
} }
} }
else else {
{
coreutils::Log(coreutils::LOG_DEBUG_1) << "Subscription doesn't exist"; coreutils::Log(coreutils::LOG_DEBUG_1) << "Subscription doesn't exist";
} }
++countPublished; ++countPublished;
} }
} }
if (temp != "") if (temp != "") {
{
subscriptions.erase(temp); subscriptions.erase(temp);
temp = ""; temp = "";
} }
@@ -89,35 +68,13 @@ namespace core
return countSubscribed; return countSubscribed;
} }
int SubscriptionManager::processCommand(coreutils::ZString &request, TCPSession &session) int SubscriptionManager::processCommand(coreutils::ZString &request, TCPSession &session) {
{ if (request[0] == "publish") {
// Every branch below reads or mutates either the `subscriptions` map
// or some Subscription's own `subscribers` vector - both of which
// removeSessionSubscriptions() (called from ~TCPSession(), on
// whatever thread happens to be tearing down a *different*
// connection) also reads/mutates, under `lock`. This method used to
// take no lock at all, so a session disconnecting on one thread
// could run subscribers.erase() concurrently with subscribe()/
// event() iterating or push_back()-ing the same vector on another -
// an unsynchronized data race. Confirmed live: rapid connect/
// subscribe/disconnect cycles against ItemDataBase's "createItem"
// subscription produced a real segfault (gdb: Socket::write() ->
// resetSocket() dereferencing a corrupted EPoll reference) while
// broadcasting to *SUBSCRIBERS. `lock` is a plain, non-recursive
// std::mutex, and nothing reachable from this function re-enters
// processCommand()/add()/removeSessionSubscriptions() on the same
// thread, so holding it for the whole call is safe.
std::lock_guard<std::mutex> guard(lock);
if (request[0] == "publish")
{
SubscriptionHandler *handler = NULL; SubscriptionHandler *handler = NULL;
if (request.getList().size() > 3) if (request.getList().size() > 3) {
{
handler = factory->getSubscriptionHandler(request[4].str()); handler = factory->getSubscriptionHandler(request[4].str());
} }
if (request[2] == "*OWNER") if (request[2] == "*OWNER") {
{
newSubscription = new Subscription(request[1].str(), session.alias, request[2].str(), request[3].str(), handler, request[5].str(), request[6].str()); newSubscription = new Subscription(request[1].str(), session.alias, request[2].str(), request[3].str(), handler, request[5].str(), request[6].str());
subscriptions.insert(std::make_pair(request[1].str(), newSubscription)); subscriptions.insert(std::make_pair(request[1].str(), newSubscription));
return 1; return 1;
@@ -125,101 +82,48 @@ namespace core
return 1; return 1;
} }
else if (request[0] == "catalog") else if (request[0] == "catalog") {
{
session.out << ":catalog:"; session.out << ":catalog:";
for (auto const &[key, subscription] : subscriptions) for (auto const &[key, subscription] : subscriptions) {
{
session.out << subscription->id << ";"; session.out << subscription->id << ";";
} }
session.out << std::endl; session.out << std::endl;
return 1; return 1;
} }
else if (request[0] == "invite") else if (request[0] == "invite") {
{
std::stringstream out; std::stringstream out;
coreutils::Log(coreutils::LOG_DEBUG_1) << request[2]; coreutils::Log(coreutils::LOG_DEBUG_1) << request[2];
std::string invitee = request[2].str(); invitee = (coreutils::MString *)&request[2];
// Was getSessionByAlias(&invitee) - getSessionByAlias(void*) TCPSession *tempSession = session.server.getSessionByAlias(invitee);
// compares by POINTER IDENTITY against each session's own
// alias, but &invitee is the address of a local variable that
// was just created in this function call - it can never equal
// any real session's alias, so this lookup was guaranteed to
// return NULL every time, and the result was dereferenced
// unconditionally below. Any client sending a bare "invite:"
// crashed the entire process, not just its own connection -
// same class of bug as the two GuildManager crashes fixed
// this session. This generic ServerCore layer has no
// name-based session lookup available (that only exists as a
// Zephora-specific addition on top of TCPServer), so a real
// fix for "find a session by the player name they typed"
// isn't possible at this layer without a larger change; this
// stops the crash without pretending to fix that gap.
// getSessionByAlias() now returns an already-pin()'d
// shared_ptr - see its own doc comment - so tempSession itself
// guarantees the session stays alive for this write() call
// below, no separate pin() needed.
auto tempSession = session.server->getSessionByAlias(&invitee);
if (!tempSession)
{
coreutils::Log(coreutils::LOG_DEBUG_1) << "invite: could not find session for " << invitee;
return 0;
}
std::stringstream temp; std::stringstream temp;
temp << "invite:" << request[1] << ":" << invitee; temp << "invite:" << request[1] << ":" << invitee;
tempSession->write(temp.str()); tempSession->write(temp.str());
return 1; return 1;
} }
// Was subscriptions[request[1].str()] - map::operator[] default- auto subscription = subscriptions[request[1].str()];
// constructs (here: NULL) and INSERTS an entry for any id that
// isn't already registered, and subscription->id below was then
// dereferenced completely unconditionally. Any subscribe:/
// unsubscribe:/event: naming an id nobody ever created (confirmed
// live: "subscribe:invite", sent by the client on every scene
// load, referencing an "invite" subscription that plain doesn't
// exist anywhere - real invites are Publish()'d under a
// per-group-guid id, not a shared "invite" channel) null-derefs
// and takes down the ENTIRE process, every connection included,
// not just the one that sent it. find() avoids polluting the map
// with the bogus NULL entry too.
auto it = subscriptions.find(request[1].str());
if (it == subscriptions.end())
{
coreutils::Log(coreutils::LOG_DEBUG_1) << "Unknown subscription id: " << request[1].str();
return 0;
}
auto subscription = it->second;
if (request[1].equals(subscription->id)) if (request[1].equals(subscription->id)) {
{ if (request[0] == "unpublish") {
if (request[0] == "unpublish")
{
subscriptions.erase(request[1].str()); subscriptions.erase(request[1].str());
} }
else if (request[0] == "subscribe") else if (request[0] == "subscribe") {
{
subscription->subscribe(session); subscription->subscribe(session);
return 1; return 1;
} }
else if (request[0] == "unsubscribe") else if (request[0] == "unsubscribe") {
{
subscription->unsubscribe(session); subscription->unsubscribe(session);
return 1; return 1;
} }
else if (request[0] == "event") else if (request[0] == "event") {
{
std::stringstream out; std::stringstream out;
subscription->process(request, out, session); subscription->process(request, out, session);
if (subscription->mode == "*ANYONE") if (subscription->mode == "*ANYONE") {
{
subscription->event(out); subscription->event(out);
return 1; return 1;
} }
else if (subscription->mode == "*SUBSCRIBERS") else if (subscription->mode == "*SUBSCRIBERS") {
{ if (subscription->ifSubscriber(session)) {
if (subscription->ifSubscriber(session))
{
subscription->event(out); subscription->event(out);
return 1; return 1;
} }
+1
View File
@@ -33,6 +33,7 @@ namespace core {
std::map<coreutils::MString, Subscription *> subscriptions; std::map<coreutils::MString, Subscription *> subscriptions;
Subscription *newSubscription; Subscription *newSubscription;
std::mutex lock; std::mutex lock;
coreutils::MString invitee;
}; };
+29
View File
@@ -0,0 +1,29 @@
#include "TCPLineBufferParser.h"
namespace core {
TCPLineBufferParser::TCPLineBufferParser() {
buffer = nullptr;
bufferSize = 4096;
buffer = realloc(buffer, bufferSize);
cursor = buffer;
}
void TCPLineBufferParser::readDataNotification() {
len = ::read(getDescriptor(), buffer, bufferSize);
for(unsigned int index = 0; index < bufferSize; ++index) {
if(cursor[index] == '\n') {
coreutils::ZString data(cursor, index);
dataReceived(data);
}
}
}
void TCPLineBufferParser::onDataReceived(coreutils::ZString data) {}
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef __TCPLineBufferParser_h__
#define __TCPLineBufferParser_h__
#include "TCPSocket.h"
namespace core {
///
/// TCPLineBufferParser
///
/// This object will read lines of data from the source one line at a time.
///
///
class TCPLineBufferParser : public TCPSocket {
protected:
///
/// This method is called when a line is ready to be processed.
///
virtual void onDataReceived();
private:
///
/// Read the data for the socket and split into lines and call dataReceived
/// for each line parsed.
///
void readDataNotification() override;
char *buffer;
size_t bufferSize;
char *cursor;
};
}
#endif
+43 -154
View File
@@ -3,20 +3,14 @@
#include "Exception.h" #include "Exception.h"
#include "Log.h" #include "Log.h"
#include "TCPSession.h" #include "TCPSession.h"
#include <chrono>
#include <vector>
namespace core { namespace core
{
TCPServer::TCPServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, std::string delimiter, int depth, std::string text) TCPServer::TCPServer(EPoll &ePoll, IPAddress address, std::string delimiter, int depth, std::string text)
: TCPSocket(ePoll, tlsInfo, text), commands(delimiter, depth) { : TCPSocket(ePoll, text), commands(delimiter, depth)
{
// Wires the subscription (pub/sub) command set into this server's
// own command dispatch - previously missing entirely here (the
// subscriptions member existed but nothing ever routed these verbs
// to it), so publish/subscribe/catalog/etc were unreachable on any
// TCPServer built after the TLS work, even though SubscriptionManager
// itself was still fully implemented.
commands.add(subscriptions, "publish"); commands.add(subscriptions, "publish");
commands.add(subscriptions, "unpublish"); commands.add(subscriptions, "unpublish");
commands.add(subscriptions, "subscribe"); commands.add(subscriptions, "subscribe");
@@ -32,103 +26,27 @@ namespace core {
if (bind(getDescriptor(), address.getPointer(), address.addressLength) < 0) if (bind(getDescriptor(), address.getPointer(), address.addressLength) < 0)
throw coreutils::Exception("Error on bind to socket: " + std::to_string(errno)); throw coreutils::Exception("Error on bind to socket: " + std::to_string(errno));
// Was 20 - small enough that a burst of connection attempts if (listen(getDescriptor(), 20) < 0)
// arriving faster than this process calls accept() (a port scan,
// or just several real clients reconnecting around the same
// moment) can fill the kernel's own backlog before the
// application layer gets a chance to drain it, silently dropping
// the overflow SYNs rather than queuing them. 128 gives real
// bursts meaningfully more headroom; still bounded, not unlimited.
if (listen(getDescriptor(), 128) < 0)
throw coreutils::Exception("Error on listen to socket"); throw coreutils::Exception("Error on listen to socket");
}
// See isKnownListeningSocket()'s own doc comment - registers this TCPServer::~TCPServer()
// listening socket as safe for Thread::run() to dispatch to
// directly, distinct from the shared_ptr-owned TCPSession registry.
{ {
std::lock_guard<std::mutex> guard(listeningSocketsMutex());
listeningSockets().insert(this);
}
}
TCPServer::~TCPServer() {
coreutils::Log(coreutils::LOG_DEBUG_2) << "Closing server socket " << getDescriptor() << "."; coreutils::Log(coreutils::LOG_DEBUG_2) << "Closing server socket " << getDescriptor() << ".";
close(getDescriptor()); close(getDescriptor());
} }
void TCPServer::connectionRequest() { void TCPServer::onDataReceived(std::string data)
coreutils::Log(coreutils::LOG_DEBUG_2) << "Connection request is being received on socket " << getDescriptor() << "."; {
// Outside the lock.lock()/unlock() below, not nested inside it -
// sweepStaleSessions() takes the same `lock` itself (see its own
// comment), and this mutex isn't recursive.
sweepStaleSessions();
lock.lock(); lock.lock();
TCPSession *session = accept(); TCPSession *session = accept();
// The shared_ptr constructed here IS the session's real, strong if (session)
// ownership from now on (see sessions' own doc comment). A second sessions.push_back(session);
// reference to the SAME shared_ptr (never a second, independent
// std::shared_ptr<TCPSession>(session) call on the same raw
// pointer - that would create a second, competing control block
// and a guaranteed double-free) also goes into the tryGet()
// registry, which is what Thread::run()'s epoll dispatch actually
// uses to safely reach this object (see TCPSession::tryGet()'s
// own comment on why).
if (session) {
auto sessionPtr = std::shared_ptr<TCPSession>(session);
sessions.push_back(sessionPtr);
TCPSession::registerLive(session, sessionPtr);
}
// Must run ON THE SESSION, not `this` (the listening server) - the
// unqualified registerSocket()/acceptSocket() calls this replaced
// built the SSL* handshake state on the SERVER's own TLS-base
// member instead of the newly accepted session's, leaving every
// session's own `ssl` uninitialized and segfaulting the first time
// real data arrived on it (SSL_read() against a never-SSL_new()'d
// pointer - confirmed via gdb). Sessions already correctly inherit
// this server's SSL_CTX at construction (TCPSession's own
// TLS(SSL_CTX*)-taking constructor) - performTLSHandshake() just
// needed to actually be invoked on that object.
if (session && tlsInfo)
session->performTLSHandshake();
lock.unlock(); lock.unlock();
} }
void TCPServer::sweepStaleSessions() { TCPSession *TCPServer::accept()
// 30 seconds is generous for completing a TCP handshake plus a
// TLS handshake plus sending a first real message over any real
// network - deliberately not aimed at catching anything borderline,
// only connections that never progressed at all.
static const std::chrono::seconds GRACE_PERIOD(30);
auto now = std::chrono::steady_clock::now();
// Collect under `lock` (sessions is mutated elsewhere under the
// same mutex - see its own doc comment), then drop the lock before
// actually removing anything. removeFromSessionList() takes `lock`
// itself for each call; calling it while still holding the guard
// below would deadlock on this non-recursive mutex.
std::vector<TCPSession *> stale;
{ {
std::lock_guard guard(lock);
for (auto &s : sessions)
if (!s->hasReceivedData && (now - s->acceptedAt) > GRACE_PERIOD)
stale.push_back(s.get());
}
for (auto *s : stale) {
coreutils::Log(coreutils::LOG_DEBUG_2) << "Reaping stale, never-active session " << s->getDescriptor() << ".";
removeFromSessionList(s);
}
}
void TCPServer::receiveData(coreutils::ZString &buffer) {
// See this override's own doc comment in TCPServer.h - the
// listening socket always uses the plain read()/ENOTCONN detection
// path, never TLS::receiveData(), regardless of whether this server
// is TLS-configured.
Socket::receiveData(buffer);
}
TCPSession * TCPServer::accept() {
try try
{ {
@@ -147,50 +65,38 @@ namespace core {
// } // }
return session; return session;
} }
catch (coreutils::Exception e) { catch (coreutils::Exception e)
{
coreutils::Log(coreutils::LOG_EXCEPT) << "Major error on session initialization. Error is '" << e.text << "'."; coreutils::Log(coreutils::LOG_EXCEPT) << "Major error on session initialization. Error is '" << e.text << "'.";
} }
catch (...) { catch (...)
{
coreutils::Log(coreutils::LOG_EXCEPT) << "Unnspecified error on session initialization."; coreutils::Log(coreutils::LOG_EXCEPT) << "Unnspecified error on session initialization.";
} }
return NULL; return NULL;
} }
void TCPServer::removeFromSessionList(TCPSession *session) { void TCPServer::removeFromSessionList(TCPSession *session)
lock.lock(); {
for (auto cursor = sessions.begin(); cursor < sessions.end(); ++cursor) std::vector<TCPSession *>::iterator cursor;
if (cursor->get() == session) lock.lock();
for (cursor = sessions.begin(); cursor < sessions.end(); ++cursor)
if (*cursor == session)
{ {
// Drops the one strong-owning reference. If nothing else is
// currently pin()'d to this session, the object is actually
// destroyed right here, synchronously - same timing as
// before this change for the common (no-contention) case.
// If some other thread IS mid-call via its own pin()'d
// shared_ptr right now, destruction is safely deferred until
// that reference is released too, instead of the crash
// (double-delete / use-after-free, confirmed live via
// AddressSanitizer under concurrent load) that raw-pointer
// ownership produced here before.
sessions.erase(cursor); sessions.erase(cursor);
break; break;
} }
lock.unlock(); lock.unlock();
// Removes this pointer from the tryGet() lookup registry too (see
// TCPSession::tryGet()'s own comment). Safe to do after releasing
// `lock` and regardless of whether the erase() above was actually
// the last reference: tryGet() correctly reports "gone" the moment
// a session's real refcount hits zero, via the weak_ptr's own
// expiration - this call is just tidying the registry map itself,
// not something later correctness depends on.
TCPSession::unregisterLive(session);
} }
void TCPServer::sessionErrorHandler(std::string errorString, std::stringstream &out) { void TCPServer::sessionErrorHandler(std::string errorString, std::stringstream &out)
{
throw coreutils::Exception(errorString); throw coreutils::Exception(errorString);
} }
TCPSession * TCPServer::getSocketAccept(EPoll &ePoll) { TCPSession *TCPServer::getSocketAccept(EPoll &ePoll)
return new TCPSession(ePoll, this); {
return new TCPSession(ePoll, *this);
} }
void TCPServer::output(std::stringstream &out) void TCPServer::output(std::stringstream &out)
@@ -200,23 +106,8 @@ namespace core {
int TCPServer::processCommand(coreutils::ZString &request, TCPSession &session) int TCPServer::processCommand(coreutils::ZString &request, TCPSession &session)
{ {
// Was unguarded, like every other method below - `sessions` is
// mutated under `lock` by connectionRequest()/removeFromSessionList()
// (a session accepting or disconnecting on another thread), but
// every READ of it here had no lock at all. Confirmed live via
// dmesg: a real segfault at a stable, deterministic instruction
// offset (same crash signature as the Subscription::subscribers
// race fixed earlier this session, in SubscriptionManager.cpp) -
// Socket::write() -> resetSocket() dereferencing a null EPoll
// reference, consistent with iterating `sessions` while another
// thread concurrently erased and freed one of its entries.
// sendToAll() in particular is what most real gameplay broadcasts
// (combat, position, chat) go through, so this is very likely the
// actual dominant cause of that crash, not just the ItemDataBase
// subscriber list this session's earlier fix addressed.
std::lock_guard guard(lock);
int sequence = 0; int sequence = 0;
for (auto &sessionx : sessions) for (auto *sessionx : sessions)
{ {
session.out << "|" << ++sequence; session.out << "|" << ++sequence;
sessionx->output(session.out); sessionx->output(session.out);
@@ -225,37 +116,35 @@ namespace core {
return 1; return 1;
} }
void TCPServer::sendToAll(std::stringstream &data) { void TCPServer::sendToAll(std::stringstream &data)
std::lock_guard guard(lock); {
for (auto &session : sessions) for (auto session : sessions)
session->write(data.str()); session->write(data.str());
data.str(""); data.str("");
} }
void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender) { void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender)
std::lock_guard guard(lock); {
for (auto &session : sessions) for (auto session : sessions)
if (session.get() != &sender) if (session != &sender)
session->write(data.str()); session->write(data.str());
data.str(""); data.str("");
} }
void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender, SessionFilter filter) { void TCPServer::sendToAll(std::stringstream &data, TCPSession &sender, SessionFilter filter)
std::lock_guard guard(lock); {
for (auto &session : sessions) for (auto session : sessions)
if (filter.test(*session)) if (filter.test(*session))
if (session.get() != &sender) if (session != &sender)
session->write(data.str()); session->write(data.str());
data.str(""); data.str("");
} }
std::shared_ptr<TCPSession> TCPServer::getSessionByAlias(void *alias) { TCPSession *TCPServer::getSessionByAlias(coreutils::MString alias) {
coreutils::Log(coreutils::LOG_DEBUG_1) << alias; for (auto session : sessions)
std::lock_guard guard(lock);
for (auto &session : sessions)
if (session->alias == alias) if (session->alias == alias)
return session; return session;
return nullptr; return NULL;
} }
} }
+18 -138
View File
@@ -8,11 +8,9 @@
#include "SubscriptionManager.h" #include "SubscriptionManager.h"
#include "TCPSession.h" #include "TCPSession.h"
#include "TCPSocket.h" #include "TCPSocket.h"
# include "TLSInfo.h"
# include <mutex>
# include <set>
namespace core { namespace core
{
/// ///
/// TCPServer /// TCPServer
@@ -22,14 +20,14 @@ namespace core {
/// ///
/// A list of connections is maintained in a vector object. /// A list of connections is maintained in a vector object.
/// ///
/// This object extends the Command object as well so it can be added to a Console object and /// This object extends the BMACommand object as well so it can be added to a Console object and
/// process commands to display status information. /// process commands to display status information.
/// ///
class TCPServer : public TCPSocket, public Command { class TCPServer : public TCPSocket, public Command
{
public: public:
/// ///
/// The constructor for the TCPServer object. /// The constructor for the TCPServer object.
/// ///
@@ -39,7 +37,7 @@ namespace core {
/// @param commandName the name of the command used to invoke the status display for this object. /// @param commandName the name of the command used to invoke the status display for this object.
/// ///
TCPServer(EPoll &ePoll, IPAddress address, TLSInfo *tlsInfo, std::string delimiter = " ", int depth = 10, std::string text = ""); TCPServer(EPoll &ePoll, IPAddress address, std::string delimiter = " ", int depth = 10, std::string text = "");
/// ///
/// The destructor for this object. /// The destructor for this object.
@@ -62,20 +60,8 @@ namespace core {
/// ///
/// The list of sessions that are currently open and being maintained by this object. /// The list of sessions that are currently open and being maintained by this object.
/// ///
/// This is the one real, strong-owning reference per live session -
/// see TCPSession::pin()'s own doc comment for the full reasoning.
/// Every other place that keeps a TCPSession* around (Subscription::
/// subscribers, a Zephora-specific sessionsByPlayerName map, etc.)
/// is just a non-owning observer and must pin() before touching the
/// object. A session stops existing, for real, the instant its
/// entry here is erased and every other outstanding pin() (if any
/// thread happened to be mid-call) has released its own reference -
/// removeFromSessionList() is the only place that ever removes an
/// entry, and Thread::run() calls it instead of ever calling
/// `delete` on a TCPSession directly (see its own comment).
///
std::vector<std::shared_ptr<TCPSession>> sessions; std::vector<TCPSession *> sessions;
/// ///
/// The commands object is a CommandList and is used to store Command objects to be /// The commands object is a CommandList and is used to store Command objects to be
@@ -133,64 +119,23 @@ namespace core {
/// ///
/// Use the getSessionByAlias to retrieve a session pointer by the value /// Use the getSessionByAlias to retrieve a session pointer by the value
/// of the alias pointer. Returns an already-pin()'d shared_ptr (null /// of the alias pointer.
/// if not found) rather than a raw pointer, so callers can't
/// accidentally hold onto - or use - a reference that isn't actually
/// keeping the session alive.
/// ///
std::shared_ptr<TCPSession> getSessionByAlias(void *alias); TCPSession *getSessionByAlias(coreutils::MString alias);
protected: protected:
/// ///
/// This method is called by the lower socket when a connection request comes into /// Override the virtual dataReceived since for the server these
/// the listening and bound server port. /// are requests to accept the new connection socket.
/// No data is to be read or written when this method is called. It is the response to
/// the fact that a new connection is coming into the system
///
/// @param data the pointer to the buffer containing the received data.
/// @param length the length of the associated data buffer.
/// ///
void connectionRequest() override; void onDataReceived(std::string data) override;
///
/// Drops any session that completed a TCP accept() but has never
/// had a single byte of real data pass through onDataReceived() -
/// covers both a TLS handshake that never finishes and a plain
/// connection that never sends anything - once it's been open
/// longer than a short grace period. Nothing else in this class
/// ever revisits an already-accepted session once it stops
/// generating epoll events (Thread::run()'s dispatch loop only
/// acts on a session when a real event for it arrives), so a
/// connection that completes the handshake and then goes
/// permanently silent - exactly what a port scan/prober looks
/// like from here, as opposed to a normal scan that just connects
/// and immediately closes again, which the existing read-hangup
/// path already cleans up fine - would otherwise sit in `sessions`
/// forever, one TCPSession + one fd each. Called from
/// connectionRequest() (see its own call site) so the sweep keeps
/// pace with however fast new connections are actually arriving,
/// without needing a dedicated timer/thread of its own; a
/// legitimate, already-talking client is never at risk, since
/// hasReceivedData latches true permanently on its first real byte
/// and is never consulted again after that.
///
void sweepStaleSessions();
///
/// The listening socket itself is never a TLS peer - accepting a
/// connection on it surfaces as a plain read() failing with ENOTCONN
/// (see Socket::receiveData(), which is what actually detects and
/// dispatches to connectionRequest() above). When this server is
/// TLS-configured, TCPSocket::receiveData()'s normal ctx-based
/// dispatch would instead route that same event through
/// TLS::receiveData()/SSL_read() against a listening fd that was
/// never SSL_accept()-ed at all (real per-connection sessions are
/// the only sockets TLS applies to) - confirmed via a real crash
/// (SIGSEGV inside SSL_read, called with `this` pointing at this
/// server object, not a session). Always using the plain path here,
/// regardless of whether tlsInfo is set, keeps connection-detection
/// working the same way with or without TLS configured.
///
void receiveData(coreutils::ZString &buffer) override;
/// ///
/// This method is called when the Command associated with this object is requested /// This method is called when the Command associated with this object is requested
@@ -201,74 +146,9 @@ namespace core {
int processCommand(coreutils::ZString &request, TCPSession &session) override; int processCommand(coreutils::ZString &request, TCPSession &session) override;
protected:
// Was private - ConsoleServer::logSend() (and any other subclass
// that iterates `sessions`, itself public further up) needs to be
// able to synchronize against the exact same mutex
// connectionRequest()/removeFromSessionList() already use, rather
// than reading a shared_ptr vector with no locking of its own.
//
// Was a plain std::mutex - self-deadlocked the accepting thread on
// EVERY single connection to ConsoleServer (confirmed live via a
// gdb thread dump caught mid-stall): connectionRequest() locks
// `lock`, then calls accept(), which constructs the new session -
// whose base Socket constructor logs its own creation via
// coreutils::Log, whose destructor calls back into
// ConsoleServer::logSend() (exactly the synchronization this was
// designed for, per the comment above) - which tries to lock this
// SAME mutex again, on the SAME thread that already holds it.
// std::mutex is not reentrant, so that thread deadlocked
// permanently, every time, without ever completing the accept -
// this is why the admin console never responded to anything.
// Since all of a server's listening sockets share one fixed-size
// EPoll worker-thread pool, each such deadlocked thread is gone
// for good - enough accumulated connection attempts (even
// sporadic ones, e.g. internet-wide port scanners probing every
// port) eventually exhausts every worker thread, at which point
// the WHOLE server - every listening socket, not just the
// console - stops accepting anything new. This is the actual
// root cause of BARANTMail's own multi-day-recurring "all ports
// simultaneously stop accepting connections" incidents - nothing
// to do with DNS/SPF lookup timing, which was a real, separate,
// worthwhile fix but not this bug. recursive_mutex lets the same
// thread re-enter safely (identical behavior to std::mutex for
// every other thread) without changing the locking design at all.
std::recursive_mutex lock;
public:
///
/// A listening TCPServer/ConsoleServer (as opposed to an accepted
/// TCPSession) is constructed once, directly by main(), and lives
/// for the whole process - never through the shared_ptr-based
/// ownership TCPSession::registerLive()/pin() provide (see their
/// own comments), since nothing ever calls `new` on one of these.
/// Thread::run()'s epoll dispatch still needs a SAFE way to tell
/// "this raw pointer is one of those known-immortal listening
/// sockets, fine to call methods on directly" apart from "this is
/// an unrecognized/stale pointer that must not be touched at all"
/// (see TCPSession::tryGet()'s own comment on why dynamic_cast
/// can't make that distinction safely). Registering `this` once,
/// in the constructor, into this tiny set is that signal - it's
/// checked by address only, never dereferenced for the check
/// itself.
///
static bool isKnownListeningSocket(void *ptr) {
std::lock_guard<std::mutex> guard(listeningSocketsMutex());
return listeningSockets().count(ptr) > 0;
}
private: private:
TCPSession *accept(); TCPSession *accept();
std::mutex lock;
static std::mutex &listeningSocketsMutex() {
static std::mutex m;
return m;
}
static std::set<void *> &listeningSockets() {
static std::set<void *> s;
return s;
}
}; };
} }
+54 -54
View File
@@ -6,38 +6,15 @@
namespace core { namespace core {
TCPSession::TCPSession(EPoll &ePoll, std::string text) : TCPSocket(ePoll, text) { TCPSession::TCPSession(EPoll &ePoll, TCPServer &server, std::string text) : TCPSocket(ePoll, text), server(server) {
// uuid_t is 16 raw binary bytes, not a NUL-terminated string - uuid_t uuid;
// streaming it directly (the old code did `<< uuidObject`) hits uuid_generate(uuid);
// libstdc++'s unsigned-char* operator<< overload, which treats its alias = coreutils::MString((char *)uuid, 26);
// argument as a C-string and calls strlen() on it: a real
// stack-buffer-overflow read past the 16-byte local, confirmed via
// AddressSanitizer under concurrent load. Separately, `alias` was
// being pointed at `uuidObject` itself - a stack-local array that is
// destroyed the instant this constructor returns, leaving `alias` a
// dangling pointer for the rest of the session's life (used by
// compareAlias() for session identity). uuid_unparse() into the
// `uuid` member (declared for exactly this - see its doc comment)
// fixes both: a real NUL-terminated string safe to log, backed by
// storage that lives as long as the TCPSession does.
uuid_t uuidObject;
uuid_generate(uuidObject);
uuid_unparse(uuidObject, uuid);
coreutils::Log(coreutils::LOG_DEBUG_1) << uuid;
alias = (void *)uuid;
}
TCPSession::TCPSession(EPoll &ePoll, TCPServer *server, std::string text) : TCPSocket(ePoll, server->ctx, text), server(server) {
uuid_t uuidObject;
uuid_generate(uuidObject);
uuid_unparse(uuidObject, uuid);
coreutils::Log(coreutils::LOG_DEBUG_1) << uuid;
alias = (void *)uuid;
} }
TCPSession::~TCPSession() { TCPSession::~TCPSession() {
server->removeFromSessionList(this); server.removeFromSessionList(this);
server->subscriptions.removeSessionSubscriptions(*this); server.subscriptions.removeSessionSubscriptions(*this);
} }
void TCPSession::output(std::stringstream &data) { void TCPSession::output(std::stringstream &data) {
@@ -45,34 +22,14 @@ namespace core {
} }
void TCPSession::protocol(coreutils::ZString &data) { void TCPSession::protocol(coreutils::ZString &data) {
// The data.getLength() != 0 guard alone silently swallowed every if (data.getLength() != 0)
// blank line a client sent, before it ever reached if (!server.commands.processRequest(data, *this))
// CommandList::processRequest() - fine for ordinary command
// dispatch (a stray blank line between commands isn't a command),
// but CommandList::processRequest() already special-cases
// session.grab != NULL to hand the line to the grabbed handler
// unconditionally, blank or not - a handler mid-grabInput() (e.g.
// __SMTP_DATA reading a message body, or an IMAP APPEND literal)
// depends on seeing blank lines as real content, not commands.
// With the outer guard unconditional, every message body's
// header/body separator - and every blank line within the body
// itself - was getting dropped before that handler ever saw it.
// Confirmed via a delivered test message with zero CRLFCRLF
// sequences anywhere in it, which is what first exposed this.
if (grab != NULL || data.getLength() != 0) {
if (!server->commands.processRequest(data, *this)) {
coreutils::Log(coreutils::LOG_DEBUG_1) << "Received data could not be parsed: " << data.str(); coreutils::Log(coreutils::LOG_DEBUG_1) << "Received data could not be parsed: " << data.str();
} }
}
}
bool TCPSession::compareAlias(void *alias) { // bool TCPSession::compareAlias(coreutils::MString alias) {
return this->alias = alias; // return this->alias == alias;
} // }
void TCPSession::outputAlias(std::stringstream &out) {
out << alias;
}
void TCPSession::onRegistered() { void TCPSession::onRegistered() {
onConnected(); onConnected();
@@ -83,6 +40,43 @@ namespace core {
void TCPSession::onConnected() {} void TCPSession::onConnected() {}
void TCPSession::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 TCPSession::setBlockSize(int blockSize) {
this->blockSize = blockSize;
}
void TCPSession::onLineReceived(coreutils::ZString &line) { void TCPSession::onLineReceived(coreutils::ZString &line) {
protocol(line); protocol(line);
send(); send();
@@ -90,6 +84,12 @@ namespace core {
shutdown("termination requested"); shutdown("termination requested");
} }
void TCPSession::onBlockReceived(coreutils::ZString &block) {
coreutils::Log(coreutils::LOG_DEBUG_3) << "[" << block.getLength() << "]";
if (term)
shutdown("termination requested");
}
void TCPSession::send() { void TCPSession::send() {
if (out.tellp() > 0) if (out.tellp() > 0)
write(out.str()); write(out.str());
+36 -146
View File
@@ -1,12 +1,10 @@
#ifndef __Session_h__ #ifndef __Session_h__
#define __Session_h__ #define __Session_h__
#include "TCPLineBufferParser.h"
#include "MString.h"
#include "SessionFilter.h" #include "SessionFilter.h"
#include "TCPSocket.h" #include "TCPSocket.h"
# include <map>
# include <memory>
# include <mutex>
# include <sstream>
namespace core { namespace core {
@@ -25,134 +23,14 @@ namespace core {
/// ///
/// ///
class TCPSession : public TCPSocket, public std::enable_shared_from_this<TCPSession> { class TCPSession : public TCPLineBufferParser {
public: public:
///
/// pin() safely upgrades a raw TCPSession* (as stored in
/// Subscription::subscribers, ZephoraServer's own
/// sessionsByPlayerName, GroupManager's membership lists, etc.)
/// into a real, ownership-extending shared_ptr before it's used.
///
/// The core problem this solves: every crash found and root-caused
/// this session (SubscriptionManager.cpp/TCPServer.cpp/Socket.cpp's
/// own fixes) was the same shape - one thread calling a method
/// (write(), a destructor) on a TCPSession* while another thread
/// concurrently deleted that same object, because nothing actually
/// guaranteed the object stayed alive for the duration of the
/// call. Mutex-based fixes at each individual call site kept
/// narrowing the window without fully closing it, and a mutex
/// around every write() call also isn't what the fifo-based
/// design (see Socket::write()) was going for in the first place -
/// no thread should have to block just to hand off a message.
///
/// TCPServer::sessions now holds the one real, strong-owning
/// shared_ptr<TCPSession> per live session (see its own comment).
/// Every OTHER place that stores a TCPSession* for later use is a
/// plain observer, not an owner - it must call pin() and check for
/// null immediately before touching the object, rather than
/// dereferencing the raw pointer directly. pin() returns nullptr
/// (instead of throwing, unlike raw shared_from_this()) once the
/// object's last real owner has let go and it's on its way out -
/// at that point there is nothing left to safely do with it, and
/// the caller should just skip it as if it were never there,
/// exactly like a stale/already-unsubscribed entry.
///
/// This adds no blocking anywhere: shared_ptr's own refcounting is
/// a couple of atomic increments, not a mutex - the actual
/// deletion still only ever happens once, whenever the last
/// outstanding shared_ptr (held by TCPServer::sessions, or
/// briefly by whichever thread(s) are mid-call via their own
/// pin()'d reference) goes out of scope.
///
std::shared_ptr<TCPSession> pin() {
try { return shared_from_this(); }
catch (const std::bad_weak_ptr &) { return nullptr; }
}
///
/// A second, distinct safety problem pin()/shared_from_this()
/// alone cannot solve: Thread::run()'s epoll dispatch loop starts
/// from a raw `void*` (epoll_event.data.ptr) whose TYPE isn't known
/// yet - the original code cast it straight to `Socket*` and
/// called virtual methods on it; an early version of this fix used
/// dynamic_cast<TCPSession*> to identify session objects before
/// calling pin(). Both of those REQUIRE reading the pointed-to
/// object's own memory (its vtable, at minimum) to work at all -
/// confirmed live via AddressSanitizer that dynamic_cast on a
/// stale pointer (one epoll handed out for a TCPSession that
/// something else had already fully destroyed and freed on another
/// thread) segfaults outright, before pin()'s own safety logic
/// ever gets a chance to run.
///
/// tryGet() sidesteps this entirely: it looks the raw pointer VALUE
/// up as a key in a plain map, and only ever touches a weak_ptr's
/// own control block (a separate heap allocation that outlives the
/// TCPSession object itself for as long as any weak_ptr or
/// shared_ptr referencing it exists) - never the TCPSession's own
/// memory - to decide whether it's still valid. That makes it safe
/// to call on ANY raw pointer value, including ones that turn out
/// to be completely unrelated to this registry (never a
/// TCPSession, e.g. a listening TCPServer/ConsoleServer - see
/// TCPServer::isKnownListeningSocket() for how Thread::run()
/// distinguishes that case) or ones pointing at freed memory.
///
/// registerLive()/unregisterLive() are called exactly once each,
/// by TCPServer::connectionRequest() and removeFromSessionList()
/// respectively - see their own comments for why those are the
/// right, and only, call sites.
///
static void registerLive(TCPSession *raw, std::shared_ptr<TCPSession> owner) {
std::lock_guard<std::mutex> guard(registryMutex());
registry()[raw] = owner;
}
static void unregisterLive(TCPSession *raw) {
std::lock_guard<std::mutex> guard(registryMutex());
registry().erase(raw);
}
static std::shared_ptr<TCPSession> tryGet(void *raw) {
std::lock_guard<std::mutex> guard(registryMutex());
auto it = registry().find((TCPSession *)raw);
if (it == registry().end())
return nullptr;
return it->second.lock();
}
private:
// Function-local statics (constructed on first use, in whichever
// translation unit/thread reaches them first, thread-safely per
// C++11's own guarantee for static local initialization) instead
// of ordinary static data members - avoids needing a .cpp
// definition for these two and any static-initialization-order
// concerns with other translation units, since TCPSession.h is
// included widely.
static std::mutex &registryMutex() {
static std::mutex m;
return m;
}
static std::map<TCPSession *, std::weak_ptr<TCPSession>> &registry() {
static std::map<TCPSession *, std::weak_ptr<TCPSession>> r;
return r;
}
public:
/// ///
/// ///
/// ///
TCPSession(EPoll &ePoll, std::string text = ""); TCPSession(EPoll &ePoll, TCPServer &server, std::string text = "");
///
///
///
TCPSession(EPoll &ePoll, TCPServer *server, std::string text = "");
/// ///
/// ///
@@ -178,11 +56,10 @@ namespace core {
void terminate(); void terminate();
/// ///
/// This is a pointer to the server that was used to 'spawn' the server connection. ///
/// If the session is just client side connection this pointer should be NULL;
/// ///
TCPServer *server; TCPServer &server;
/// ///
/// Use out to send data to the session socket or other session sockets. /// Use out to send data to the session socket or other session sockets.
@@ -199,20 +76,20 @@ namespace core {
char uuid[37]; char uuid[37];
/// ///
/// alias is a void pointer that can be set to point to any object that identifies /// alias is a MString that can be set to any string of characters that identifies
/// this session uniquely. Using this approach, inheriting objects can determine /// this session uniquely. Alias's should be unique between sessions.
/// how it knows the contacts that this server manages.
/// ///
void *alias; coreutils::MString alias;
///
///
///
/// ///
/// ///
/// ///
virtual bool compareAlias(void *alias); // bool compareAlias(coreutils::MString alias);
virtual void outputAlias(std::stringstream &out);
protected: protected:
/// ///
@@ -221,6 +98,14 @@ namespace core {
virtual void onRegistered() override; virtual void onRegistered() override;
///
/// 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.
///
void onDataReceived(coreutils::ZString &data) override;
/// ///
/// Override the onLineReceived method to receive a string of characters that /// 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 /// represents a single line of data terminated by a LF or CRLF. If onDataReceived
@@ -228,14 +113,6 @@ namespace core {
/// this method explicitly using the class and member name. /// this method explicitly using the class and member name.
/// ///
virtual void onLineReceived(coreutils::ZString &line) override;
///
/// This method is called from within the protocol method when protocol is called
/// on the initial connection where the data is an empty string. Use this method
/// to deliver a message to the connection upon connection.
///
virtual void onConnected(); virtual void onConnected();
/// ///
@@ -250,9 +127,22 @@ namespace core {
virtual void protocol(coreutils::ZString &data); virtual void protocol(coreutils::ZString &data);
private: ///
std::mutex mtx; /// 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);
private:
char *lineBuffer = NULL;
int lineBufferSize = 0;
int lineLength = 0;
int blockLength = 0;
std::mutex mtx;
bool term = false;
int blockSize = 0;
}; };
} }
+85
View File
@@ -0,0 +1,85 @@
#include "TCPSession2.h"
#include "Exception.h"
#include "Log.h"
namespace core {
TCPSession2::TCPSession2(EPoll &ePoll, std::string text) : TCPSocket(ePoll, text) {}
TCPSession2::~TCPSession2() {}
void TCPSession2::output(std::stringstream &data) {
data << "|" << ipAddress.getClientAddressAndPort();
}
void TCPSession2::protocol(coreutils::ZString &data) {}
void TCPSession2::onRegistered() {
onConnected();
send();
if(term)
TCPSocket::shutdown("termination requested");
}
void TCPSession2::onConnected() {}
void TCPSession2::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 TCPSession2::setBlockSize(int blockSize) {
this->blockSize = blockSize;
}
void TCPSession2::onLineReceived(coreutils::ZString &line) {
protocol(line);
send();
if(term)
TCPSocket::shutdown("termination requested");
}
void TCPSession2::onBlockReceived(coreutils::ZString &block) {
coreutils::Log(coreutils::LOG_DEBUG_3) << "[" << block.getLength() << "]";
if(term)
TCPSocket::shutdown("termination requested");
}
void TCPSession2::send() {
if(out.tellp() > 0)
TCPSocket::write(out.str());
out.str("");
}
void TCPSession2::terminate() {
term = true;
}
}
+139
View File
@@ -0,0 +1,139 @@
#ifndef __TCPSession2_h__
# define __TCPSession2_h__
#include "TCPSocket.h"
#include "Timer.h"
#include "SessionFilter.h"
namespace core {
class Command;
class TCPServer;
///
/// TCPSession2
///
/// TCPSession defines the nature of the interaction with the client
/// and stores persistent data for a defined session. TCPSession objects
/// are not sockets but instead provide a communications control
/// mechanism. Protocol conversations are provided through extensions
/// from this object.
///
/// TCPSession2 is designed to be 'connected' instead of being served
/// by a server.
///
class TCPSession2 : public TCPSocket {
public:
///
///
///
TCPSession2(EPoll &ePoll, std::string text = "");
///
///
///
virtual ~TCPSession2();
Command *grab = NULL;
virtual void output(std::stringstream &data);
///
/// The send method is used to output the contents of the out stream
/// to the session containing the stream.
///
void send();
///
/// Use this method to terminate this TCPSession.
///
void terminate();
///
/// Use out to send data to the session socket or other session sockets.
///
std::stringstream out;
protected:
///
///
///
virtual void onRegistered() override;
///
/// 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);
///
/// This method is called from within the protocol method when protocol is called
/// on the initial connection where the data is an empty string. Use this method
/// to deliver a message to the connection upon connection.
///
virtual void onConnected();
///
/// Override the protocol method to manage and control the session communications
/// in your inherited session. If you do not override this method then the Session
/// default will process the 'commands' added to the server object using the
/// processRequest method on the session input.
///
/// When data is received within the session two modes are available to pass the
/// data through the protocol method: LINE or BLOCK.
///
virtual void protocol(coreutils::ZString &data);
///
/// 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);
private:
char *lineBuffer = NULL;
int lineBufferSize = 0;
int lineLength = 0;
int blockLength = 0;
std::mutex mtx;
bool term = false;
int blockSize = 0;
};
}
#endif
-163
View File
@@ -3,8 +3,6 @@
#include "Log.h" #include "Log.h"
#include "Exception.h" #include "Exception.h"
#include "errno.h" #include "errno.h"
#include <algorithm>
#include <cstring>
namespace core { namespace core {
@@ -12,10 +10,6 @@ namespace core {
TCPSocket::TCPSocket(EPoll &ePoll, std::string text) : Socket(ePoll, text) {} 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() {} TCPSocket::~TCPSocket() {}
void TCPSocket::connect(IPAddress &address) { void TCPSocket::connect(IPAddress &address) {
@@ -29,162 +23,5 @@ namespace core {
out << "|" << ipAddress.getClientAddressAndPort(); out << "|" << ipAddress.getClientAddressAndPort();
} }
void TCPSocket::receiveData(coreutils::ZString &buffer) {
// Dispatch on `ctx`, not `tlsInfo` - the SSL_CTX*-taking constructor
// (used for every accepted TCPSession) never sets `tlsInfo` at all,
// leaving it as uninitialized garbage that intermittently (whenever
// that garbage happened to be non-zero) routed a plain, non-TLS
// session's data through SSL_read()/SSL_write() against an
// uninitialized SSL*, segfaulting inside libssl. `ctx` is the
// reliable signal: real and non-NULL for an actual TLS server/
// session (set in TLS::createContext(), inherited by sessions via
// TLS(SSL_CTX*)), safely NULL by default otherwise (see TLS.h).
if(ctx) {
int len;
{
// Held only around the raw SSL_read call - released before
// onDataReceived() so the request-handling chain it triggers
// (which reaches Socket::write()/outlock) never runs while
// ioLock is held. See ioLock's own doc comment in TCPSocket.h.
std::lock_guard<std::mutex> ioGuard(ioLock);
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(ctx) {
std::lock_guard<std::mutex> ioGuard(ioLock);
return TLS::sendData(data, length);
} else
return Socket::sendData(data, length);
}
bool TCPSocket::sendNeedsRead() {
return ctx && lastSendWantedRead;
}
void TCPSocket::performTLSHandshake() {
// registerSocket() makes this fd eligible for epoll dispatch to ANY
// worker thread immediately. ioLock MUST be acquired BEFORE that
// call, not after - a first attempt here took the lock AFTER
// registerSocket(), which still left a real window: another thread
// could win the race for ioLock (via a read/write event on this
// same brand-new connection) and have SSL_read/SSL_write silently
// complete the handshake on OpenSSL's own initiative, and only
// THEN would this thread acquire ioLock and run its own, now
// redundant, acceptSocket() (SSL_accept) against an already-
// negotiated SSL object - confirmed via gdb `thread apply all bt`
// during a live crash: one thread mid-SSL_read, a second thread
// simultaneously inside performTLSHandshake, both on the identical
// object address. Acquiring ioLock first closes that window
// entirely: no other thread can even begin touching this
// connection's SSL object until this thread's own handshake
// attempt has released the lock. acceptSocket() only attempts
// SSL_accept() once and may leave the handshake incomplete
// (WANT_READ/WANT_WRITE) - that's fine, OpenSSL transparently
// finishes a pending handshake on the next SSL_read/SSL_write,
// which ioLock already serializes against everything else.
std::lock_guard<std::mutex> ioGuard(ioLock);
registerSocket(getDescriptor());
acceptSocket();
}
void TCPSocket::onDataReceived(coreutils::ZString &data) {
if (data.getLength() > 0) {
hasReceivedData = true;
lineBuffer = (char *)realloc(lineBuffer, lineBufferSize + data.getLength());
memcpy(lineBuffer + lineBufferSize, data.getData(), data.getLength());
lineBufferSize += data.getLength();
while (lineBufferSize > 0) {
if (blockSize == 0) {
// strcspn() treats its argument as a NUL-terminated C
// string and keeps scanning past the end looking for one -
// lineBuffer is a raw byte buffer sized via realloc() to
// exactly lineBufferSize bytes, with no NUL terminator
// appended anywhere. Whenever the buffered data doesn't yet
// contain a CRLF (an ordinary, frequent case - a line split
// across TCP reads, or mid-way through a long one, e.g. a
// large email body), strcspn() read straight past
// lineBufferSize into whatever heap memory happened to
// follow, hunting for a byte that happened to match '\r',
// '\n', or an accidental '\0' - producing a lineLength
// larger than the buffer actually holds. Confirmed live via
// gdb: SIGSEGV inside a LATER, unrelated realloc() call on
// this same lineBuffer, well downstream of where the real
// corruption happened (lineBufferSize -= lineLength
// underflows when lineLength > lineBufferSize, and that
// negative value gets handed to realloc() as a size_t -
// realloc(ptr, SIZE_MAX-ish) - during a real inbound SMTP
// message with a long quoted-printable line). memchr() is
// the bounded equivalent - it never looks past the length
// given, so "no CRLF yet" correctly comes back as "not
// found" instead of reading off the end of the allocation.
char *cr = (char *)memchr(lineBuffer, '\r', lineBufferSize);
char *nl = (char *)memchr(lineBuffer, '\n', lineBufferSize);
char *found = (cr && nl) ? std::min(cr, nl) : (cr ? cr : nl);
lineLength = found ? (int)(found - lineBuffer) : lineBufferSize;
if (lineLength == lineBufferSize)
break;
coreutils::ZString zLine(lineBuffer, lineLength);
onLineReceived(zLine);
// lineLength < lineBufferSize is guaranteed by the break
// above, so this first index is always in bounds; bump
// past a lone '\r' only if there's still at least one more
// buffered byte to safely check for a following '\n' -
// otherwise (a bare trailing '\r' with nothing buffered
// after it yet) leave it for the next read to resolve,
// rather than reading lineBuffer[lineBufferSize], one past
// the allocation.
if (lineBuffer[lineLength] == '\r')
++lineLength;
if (lineLength < lineBufferSize && lineBuffer[lineLength] == '\n')
++lineLength;
lineBufferSize -= lineLength;
if (lineBufferSize > 0)
memmove(lineBuffer, lineBuffer + lineLength, lineBufferSize);
lineBuffer = (char *)realloc(lineBuffer, lineBufferSize);
}
else if (lineBufferSize >= blockSize) {
coreutils::ZString zBlock(lineBuffer, blockSize);
onBlockReceived(zBlock);
lineBufferSize -= blockSize;
if (lineBufferSize > 0)
memmove(lineBuffer, lineBuffer + blockSize, lineBufferSize);
lineBuffer = (char *)realloc(lineBuffer, lineBufferSize);
}
else
// Not enough buffered data yet for a full block - stop and
// wait for the next read, exactly like the line-mode
// branch's own "no complete line yet" break above.
// Without this, a block spanning more than one TCP read
// (any IMAP APPEND literal bigger than a single read()'s
// worth of socket buffer) sends this EPoll worker thread
// into an unconditional busy-spin - lineBufferSize never
// changes, so the while(lineBufferSize > 0) loop above
// never yields back to let more data arrive.
break;
}
}
}
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;
}
} }
+4 -184
View File
@@ -1,12 +1,9 @@
#ifndef __TCPSocket_h__ #ifndef __TCPSocket_h__
#define __TCPSocket_h__ #define __TCPSocket_h__
#include "Socket.h"
#include "TLS.h"
#include "TLSInfo.h"
#include "IPAddress.h" #include "IPAddress.h"
#include <chrono> #include "Socket.h"
#include "includes"
namespace core { namespace core {
@@ -20,44 +17,13 @@ namespace core {
/// synchronous data connection. /// synchronous data connection.
/// ///
class TCPSocket : public Socket, public TLS { class TCPSocket : public Socket {
public: public:
///
///
///
TCPSocket(EPoll &ePoll); TCPSocket(EPoll &ePoll);
///
///
///
TCPSocket(EPoll &ePoll, std::string text); 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(); virtual ~TCPSocket();
///
///
///
void connect(IPAddress &address); void connect(IPAddress &address);
IPAddress ipAddress; IPAddress ipAddress;
@@ -65,157 +31,11 @@ namespace core {
/// ///
/// The output method is called by a socket session (TCPSession) and /// The output method is called by a socket session (TCPSession) and
/// will output the detail information for the client socket. When extending /// will output the detail information for the client socket. When extending
/// TCPSocket or TCPSession you can override the method to add attributes /// BMATCPSocket or BMASession you can override the method to add attributes
/// to the list. /// to the list.
/// ///
virtual void output(std::stringstream &out); 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;
///
/// True only when this socket is actually TLS (ctx set) and the
/// last sendData() call failed specifically with
/// SSL_ERROR_WANT_READ - see TLS::lastSendWantedRead's own doc
/// comment for why Socket::writeSocket()/resetSocket() need this.
///
virtual bool sendNeedsRead() override;
///
/// Completes the TLS handshake (SSL_new/SSL_set_fd/SSL_accept) for
/// THIS socket's own ctx/descriptor. registerSocket()/acceptSocket()
/// are protected TLS members, reachable here via an ordinary
/// same-class call - the point of exposing this as a public wrapper
/// is so a sibling class (TCPServer, which does not inherit
/// TCPSocket-the-session's-particular-instance and so cannot reach
/// another object's protected members directly) can still trigger
/// the handshake on a specific accepted session from
/// TCPServer::connectionRequest(), rather than accidentally running
/// it against the listening server socket itself (see that call
/// site's own comment for the real, confirmed crash this replaces -
/// SSL_read() on an accepted session's never-initialized ssl
/// member).
///
void performTLSHandshake();
///
/// Set once, at construction, and never touched again - how long
/// this specific socket has been open since accept()/connect(),
/// regardless of whether anything has actually happened on it
/// since. TCPServer::sweepStaleSessions() reads this (alongside
/// hasReceivedData below) to find connections that completed a
/// TCP handshake but then never progressed - a scanner/prober
/// probing an open port, or a TLS handshake that never finished -
/// which otherwise sit in TCPServer::sessions forever: nothing in
/// the existing design ever revisits a socket that stops
/// generating epoll events (see sweepStaleSessions()'s own doc
/// comment for the full reasoning).
///
std::chrono::steady_clock::time_point acceptedAt = std::chrono::steady_clock::now();
///
/// Flips to true the first time onDataReceived() sees a non-empty
/// read - true for both a plaintext socket's raw bytes and a TLS
/// session's already-decrypted ones, since both paths funnel
/// through the same onDataReceived() override below. A session
/// that reaches sweepStaleSessions()'s grace period with this
/// still false has never had a real exchange of any kind, TLS
/// handshake included (a stuck/incomplete SSL_accept() never
/// surfaces anything to onDataReceived) - the specific, narrow
/// condition that sweep treats as abandoned. Deliberately NOT
/// reset afterward and never consulted again once true, so a
/// legitimate client that sends something once and then goes
/// idle for a long time (an ordinary connected-but-idle game
/// session) is never at risk from this - only a connection that
/// NEVER said anything at all within the grace window is.
///
bool hasReceivedData = false;
protected:
bool term = false;
TLSInfo *tlsInfo;
private:
char *lineBuffer = NULL;
int lineBufferSize = 0;
int lineLength = 0;
int blockSize = 0;
// Socket's own `lock` (guards receiveData) and `outlock` (guards
// writeSocket/the fifo) are two DIFFERENT mutexes - a thread
// handling EPOLLIN and a different thread handling EPOLLWRNORM for
// the SAME connection can therefore call SSL_read and SSL_write on
// the SAME `ssl*` concurrently. OpenSSL does not support that: an
// SSL object must not be used by more than one thread at a time
// (even in 1.1.0+/3.x, which dropped the old app-supplied
// CRYPTO_set_locking_callback global-locking API entirely - that
// API never protected concurrent use of one SSL object anyway,
// only OpenSSL's own internal global state). Confirmed live via
// dmesg: "traps: apiserver[...] general protection fault ... in
// libssl.so.3" during ordinary interactive use. ioLock serializes
// just the raw SSL_read/SSL_write calls themselves (see
// receiveData/sendData below) - deliberately NOT the surrounding
// request-handling, which already nests `lock` before `outlock`
// (receiveData's call chain reaches Socket::write(), which takes
// outlock) - wrapping any larger scope in ioLock+lock+outlock
// together risks recreating that same ordering as a new cycle.
std::mutex ioLock;
}; };
} }
-322
View File
@@ -1,322 +0,0 @@
#include "TLS.h"
#include "Exception.h"
#include "Log.h"
#include <iostream>
#include <openssl/rand.h>
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) {
// Only a context configured with SSL_VERIFY_PEER (see
// createContext()'s own cACertificate-gated block) ever sends a
// client a CertificateRequest in the first place - ordinary
// one-way TLS (a public site's normal case, no caCertificate
// configured) never asks the client for one, so
// SSL_get_peer_certificate() is unconditionally NULL there and is
// not a failure. Checking the mode actually negotiated on THIS
// connection (inherited from the context) rather than assuming
// every handshake is mutual-TLS is what makes plain
// browser-facing HTTPS work at all under this callback.
if(SSL_get_verify_mode(ssl) & SSL_VERIFY_PEER) {
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);
ctx = buildContext(tlsInfo);
}
}
// The per-context cert/key/cipher/verify setup factored out of
// createContext() above so addSNIContext() (below) can build
// additional contexts - one per extra hostname - without repeating
// the ONE-TIME, process-wide OpenSSL library init that stays in
// createContext() itself (SSL_library_init(), the lockarray, RAND
// seeding): that part must only ever run once per process, not once
// per hostname.
SSL_CTX *TLS::buildContext(TLSInfo *tlsInfo) {
SSL_CTX *newCtx;
if(!(newCtx = SSL_CTX_new(SSLv23_server_method())))
throw coreutils::Exception("Error while setting server method SSLv23.");
SSL_CTX_set_mode(newCtx, SSL_MODE_RELEASE_BUFFERS | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_options(newCtx, SSL_OP_NO_TICKET);
SSL_CTX_set_session_cache_mode(newCtx, SSL_SESS_CACHE_SERVER);
SSL_CTX_set_cipher_list(newCtx, "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");
// _chain_file (not _file) - loads every certificate in the file,
// not just the first. A real Let's Encrypt fullchain.pem is the
// leaf cert followed by the intermediate CA cert concatenated in
// one file specifically so a server can hand both to the client
// in one shot; SSL_CTX_use_certificate_file() only ever reads the
// first PEM block, silently dropping the intermediate - confirmed
// via a real handshake against this exact fullchain.pem: clients
// got "unable to get local issuer certificate" because the server
// only ever sent the leaf, never the intermediate needed to chain
// up to a root the client already trusts.
if(SSL_CTX_use_certificate_chain_file(newCtx, tlsInfo->certificate.c_str()) <= 0)
throw coreutils::Exception("Error looking up certificate.");
if(SSL_CTX_use_PrivateKey_file(newCtx, tlsInfo->key.c_str(), SSL_FILETYPE_PEM) < 0)
throw coreutils::Exception("Error with private key.");
if(SSL_CTX_check_private_key(newCtx) != 1)
throw coreutils::Exception("Private key does not match certificate.");
// Client-certificate verification (mutual TLS) is opt-in, not the
// default - a public-facing site (e.g. a real Let's Encrypt
// server cert with no corresponding client CA of its own) has no
// client certificate to hand out to ordinary visitors, so
// requesting one via SSL_VERIFY_PEER would surface a "select a
// certificate" prompt real browsers can't meaningfully answer.
// Only enable it when a caCertificate is actually configured -
// existing internal/mTLS use of this class (any config that DOES
// set tls.caCertificate) keeps behaving exactly as before.
if(tlsInfo->cACertificate.getLength() > 0) {
SSL_CTX_set_verify(newCtx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(newCtx, 1);
if(!SSL_CTX_load_verify_locations(newCtx, tlsInfo->cACertificate.c_str(), NULL))
throw coreutils::Exception("Cannot verify locations.");
SSL_CTX_set_client_CA_list(newCtx, SSL_load_client_CA_file(tlsInfo->cACertificate.c_str()));
coreutils::Log(coreutils::LOG_INFO) << "Server key authenticated (client certificate required).";
} else {
coreutils::Log(coreutils::LOG_INFO) << "Server key authenticated (no client certificate required).";
}
return newCtx;
}
int TLS::sniCallback(SSL *ssl, int *ad, void *arg) {
TLS *self = (TLS *)arg;
const char *name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if(name) {
auto it = self->sniContexts.find(name);
if(it != self->sniContexts.end())
SSL_set_SSL_CTX(ssl, it->second);
// No match (an unconfigured hostname, or a typo'd Host that still
// resolves here via DNS) - fall through and keep whatever SSL_CTX
// this SSL* already has, which is the base `ctx` SSL_new() was
// constructed from. Exactly the same cert a pre-SNI client (or
// one sending no SNI at all) already gets today.
}
return SSL_TLSEXT_ERR_OK;
}
void TLS::addSNIContext(const std::string &hostname, TLSInfo *tlsInfo) {
SSL_CTX *newCtx = buildContext(tlsInfo);
sniContexts[hostname] = newCtx;
if(!sniCallbackRegistered) {
SSL_CTX_set_tlsext_servername_callback(ctx, sniCallback);
SSL_CTX_set_tlsext_servername_arg(ctx, this);
sniCallbackRegistered = true;
}
}
void TLS::registerSocket(int fd) {
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.");
// Was set unconditionally as the FIRST statement in this
// function (before `ssl` was even assigned) - a flag meant to
// mean "ssl is a valid, ready-to-use SSL*" needs to become true
// only once that's actually true, at the end, not the start.
// Previously set but never actually READ anywhere either -
// sendData()/receiveData() below now check it before touching
// `ssl` at all. See their own doc comments for the real,
// confirmed crash this fixes: a brand-new TLS session's own
// greeting (queued in onConnected(), see e.g. IMAPSession.h)
// gets written via Socket::write() -> resetSocket(), which - deep
// inside TCPServer::accept(), BEFORE TCPServer::connectionRequest()
// ever calls this session's own performTLSHandshake() - makes the
// fd immediately eligible for an EPOLLWRNORM event on a SEPARATE
// epoll worker thread. That thread's TCPSocket::sendData() takes
// ioLock (see its own doc comment) and calls straight into
// SSL_write() on `ssl`, which is genuinely uninitialized garbage
// (TLS.h explicitly documents `ssl` has no default member
// initializer) until THIS function runs - ioLock alone doesn't
// prevent it, since performTLSHandshake() (the only other place
// that takes ioLock) hasn't even been called yet at that point.
// Confirmed live via gdb under a mixed-protocol connection-churn
// stress test: SIGSEGV inside libssl.so's SSL_write(), called from
// TCPSocket::sendData() with ioGuard already held, while
// registerSocket()/acceptSocket() for that same session were still
// pending on the accepting thread.
initialized = true;
}
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) {
// `ssl` is genuinely uninitialized until registerSocket() has
// fully run (see its own doc comment on `initialized` for the
// real crash this prevents) - a racing thread landing here first
// just gets "nothing yet", exactly like a real SSL_ERROR_WANT_READ
// would report, rather than touching a garbage pointer.
if (!initialized)
return -1;
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) {
// Same "ssl isn't ready yet" guard as receiveData() - see
// registerSocket()'s doc comment on `initialized` for the real,
// confirmed crash this prevents (SIGSEGV in SSL_write() on a
// still-uninitialized `ssl`, reached via a brand-new session's own
// greeting racing ahead of its own TLS handshake). Returning <= 0
// here is already the exact case Socket::writeSocket() treats as
// "nothing sent this round, leave it queued and retry on the next
// EPOLLWRNORM" - no special-casing needed at any call site.
if (!initialized)
return -1;
int len;
if((len = ::SSL_write(ssl, data, length)) >= 0) {
coreutils::Log(coreutils::LOG_DEBUG_3) << "sendData TLS...len=" << len;
lastSendWantedRead = false;
return len;
}
else {
lastSendWantedRead = false;
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.";
lastSendWantedRead = true;
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;
}
}
}
-130
View File
@@ -1,130 +0,0 @@
#ifndef __TLS_h__
#define __TLS_h__
#include "ZString.h"
#include "TLSInfo.h"
#include <openssl/ssl.h>
#include <map>
#include <string>
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);
///
/// Registers a second (or third, etc.) certificate/key pair for a
/// specific hostname on THIS SAME listening socket, via TLS's
/// Server Name Indication (SNI) extension - lets one TCPServer
/// terminate TLS for multiple unrelated domains, each with its own
/// real certificate, without needing a separate listening port or
/// process per domain. Must be called after createContext() (or the
/// TLSInfo* constructor, which calls it) has already established the
/// base `ctx` - that base context keeps serving as both the
/// fallback for hostnames not registered here AND the cert offered
/// to any client that sends no SNI hostname at all (a bare-IP
/// request, or any TLS client old enough to predate SNI). Callers
/// that never call this at all (every existing single-site
/// TLSInfo* user - BARANTMail, Zephora, ClockPlanet, and a
/// single-site APIServer config) see zero behavior change: the SNI
/// callback is only ever registered on `ctx` the first time this is
/// called, not unconditionally in createContext().
///
void addSNIContext(const std::string &hostname, TLSInfo *tlsInfo);
// A never-configured TLS object (either the listening TCPServer
// itself when constructed with tlsInfo=NULL, or a TCPSession spawned
// from one via TLS(SSL_CTX*)) must default to a real NULL here, not
// garbage - TCPSocket::receiveData()/sendData() key their TLS-vs-
// plain dispatch on this exact value (see TCPSocket.cpp).
SSL_CTX *ctx = NULL;
protected:
int receiveData(coreutils::ZString &buffer);
int sendData(const char *data, int length);
void registerSocket(int fd);
void acceptSocket();
protected:
// Set by sendData() when the last SSL_write() failed specifically
// with SSL_ERROR_WANT_READ (needs a READ to make progress - e.g.
// mid-handshake application data, or a post-handshake session
// ticket exchange) rather than any other reason. TCPSocket::
// sendNeedsRead() reads this (protected, so a subclass can reach
// it directly) to tell Socket::writeSocket()/resetSocket() not to
// busy-spin retrying the write via EPOLLWRNORM - see their own
// doc comments for the confirmed real damage (a 15GB+ debug log,
// and every affected connection dropped before ever reading the
// client's first command) this caused when unaddressed.
bool lastSendWantedRead = false;
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;
// The actual cert/key-loading + cipher/session-cache setup shared
// by both the base context (createContext(), unchanged) and every
// additional per-hostname context addSNIContext() builds - factored
// out so there's exactly one place that does this, not two copies
// drifting apart.
SSL_CTX *buildContext(TLSInfo *tlsInfo);
// Owns every SSL_CTX* built by addSNIContext() (the base `ctx` is
// owned/freed the same way it always was, untouched by this map).
// Keyed by hostname exactly as configured - matched case-
// insensitively against the ClientHello's SNI value in the
// callback (see TLS.cpp).
std::map<std::string, SSL_CTX *> sniContexts;
bool sniCallbackRegistered = false;
// OpenSSL's own SSL_CTX_set_tlsext_servername_callback signature -
// a static member (not a free function) so it can reach the owning
// TLS instance's private sniContexts via the `arg` parameter
// (SSL_CTX_set_tlsext_servername_arg(ctx, this), set alongside the
// callback itself in addSNIContext()).
static int sniCallback(SSL *ssl, int *ad, void *arg);
};
}
#endif
-27
View File
@@ -1,27 +0,0 @@
#ifndef __TLSInfo_h__
#define __TLSInfo_h__
#include "ZString.h"
namespace core {
///
/// TLSInfo
///
/// This object provides the support data to handle TLS on the server core and
/// session environment.
///
class TLSInfo {
public:
coreutils::ZString cACertificate;
coreutils::ZString certificate;
coreutils::ZString key;
};
}
#endif
+66
View File
@@ -0,0 +1,66 @@
#include "TLSServer.h"
#include "TLSSession.h"
#include "EPoll.h"
#include "TCPSession.h"
#include "Exception.h"
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]));
}
TLSServer::TLSServer(EPoll &ePoll, IPAddress address) : TCPServer(ePoll, address) {
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, sip_cert, SSL_FILETYPE_PEM) <= 0)
throw coreutils::Exception("Error looking up certificate.");
if(SSL_CTX_use_PrivateKey_file(ctx, sip_key, 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, sip_cacert, NULL))
throw coreutils::Exception("Cannot verify locations.");
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(sip_cacert));
coreutils::Log(coreutils::LOG_DEBUG_1) << "Server key authenticated.";
}
TLSServer::~TLSServer() {
}
TCPSession * TLSServer::getSocketAccept() {
return new TLSSession(ePoll, *this);
}
}
+54
View File
@@ -0,0 +1,54 @@
#ifndef TLSServerSocket_h__
#define TLSServerSocket_h__
#include "Socket.h"
#include "TCPServer.h"
#include "Command.h"
#include "TCPSession.h"
#include "IPAddress.h"
namespace core {
///
/// TLSServer
///
/// Manage a socket connection as a TLS server type. Connections to the socket are processed through
/// the accept functionality.
///
class TLSServer : public TCPServer {
public:
///
/// The constructor.
///
/// @param ePoll the BMAEPoll instance that manages the socket.
/// @param url the IP address for the socket to receive connection requests.
/// @param port the port number that the socket will listen on.
/// @param commandName the name of the command used to invoke the status display for this object.
/// @return the instance of the BMATLSServerSocket.
TLSServer(EPoll &ePoll, IPAddress address);
///
/// The destructor for this object.
///
~TLSServer();
TCPSession * getSocketAccept();
SSL_CTX *ctx;
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";
};
}
#endif
+123
View File
@@ -0,0 +1,123 @@
#include "TLSSession.h"
#include "EPoll.h"
#include "Log.h"
#include "Exception.h"
namespace core {
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.";
}
TLSSession::TLSSession(EPoll &ePoll, TCPServer &server) : TCPSession(ePoll, server) {}
void TLSSession::onRegister() {
initialized = true;
int ret;
coreutils::Log(coreutils::LOG_DEBUG_3) << "TLS socket initializing on socket " << getDescriptor() << "...";
fcntl(getDescriptor(), F_SETFL, fcntl(getDescriptor(), F_GETFL, 0) | O_NONBLOCK);
ssl = SSL_new(static_cast<TLSServer &>(server).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, getDescriptor())) == 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 TLSSession::onRegistered() {
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;
}
}
TLSSession::~TLSSession() {}
void TLSSession::protocol(coreutils::ZString &data) {}
void TLSSession::receiveData(coreutils::ZString &buffer) {
int len;
// int error = -1;
//
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;
}
}
}
void TLSSession::output(std::stringstream &out) {
out << "|" << ipAddress.getClientAddressAndPort();
}
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef __TLSSession_h__
#define __TLSSession_h__
#include "includes"
#include "TCPSession.h"
#include "TLSServer.h"
#include <openssl/ssl.h>
namespace core {
class TLSServer;
///
/// TLSSession
///
/// Provides a network TLS socket.
///
/// For accessing TLS network functions use this object. The connection oriented nature of TLS
/// provides a single client persistent connection with data error correction and a durable
/// synchronous data connection.
///
class TLSSession : public TCPSession {
public:
TLSSession(EPoll &ePoll, TCPServer &server);
~TLSSession();
///
/// The output method is called by a socket session (Session) and
/// will output the detail information for the client socket. When extending
/// TLSSocket or Session you can override the method to add attributes
/// to the list.
///
virtual void output(std::stringstream &out);
virtual void protocol(coreutils::ZString &data) override;
protected:
void receiveData(coreutils::ZString &buffer) override;
void onRegister();
void onRegistered();
private:
bool initialized = false;
SSL *ssl;
};
}
#endif
+4 -3
View File
@@ -1,11 +1,12 @@
#include "TerminalSession.h" #include "TerminalSession.h"
#include <sys/ioctl.h>
namespace core { namespace core {
TerminalSession::TerminalSession(EPoll &ePoll, TCPServer *server) : TCPSession(ePoll, server) {} TerminalSession::TerminalSession(EPoll &ePoll, TCPServer &server) : TCPSession(ePoll, server) {
}
TerminalSession::~TerminalSession() {} TerminalSession::~TerminalSession() {
}
int TerminalSession::getLines() { int TerminalSession::getLines() {
struct winsize size; struct winsize size;
+2 -1
View File
@@ -1,6 +1,7 @@
#ifndef __Terminal_h__ #ifndef __Terminal_h__
#define __Terminal_h__ #define __Terminal_h__
#include "includes"
#include "TCPSession.h" #include "TCPSession.h"
#include "TCPServer.h" #include "TCPServer.h"
@@ -29,7 +30,7 @@ namespace core {
class TerminalSession : public TCPSession { class TerminalSession : public TCPSession {
public: public:
TerminalSession(EPoll &ePoll, TCPServer *server); TerminalSession(EPoll &ePoll, TCPServer &server);
~TerminalSession(); ~TerminalSession();
int getLines(); int getLines();
+11 -123
View File
@@ -1,40 +1,12 @@
#include "Thread.h" #include "Thread.h"
#include "EPoll.h" #include "EPoll.h"
#include "TCPServer.h"
#include "INotify.h"
#include <sys/epoll.h>
namespace core { namespace core {
Thread::Thread(EPoll &ePoll) : ePoll(ePoll), _thread(NULL), thread(NULL) {} Thread::Thread(EPoll &ePoll) : ePoll(ePoll) {}
// Previously discarded `scope` entirely (never assigned to the `thread` Thread::Thread(EPoll &ePoll, ThreadScope *scope) : ePoll(ePoll) {}
// member) - a real, live bug found while adding the first actual caller
// of this constructor (APIServer's MysqlThreadScope): the whole point
// of passing a scope in was to have it invoked from run() below, which
// silently never happened.
Thread::Thread(EPoll &ePoll, ThreadScope *scope) : ePoll(ePoll), _thread(NULL), thread(scope) {}
// Was empty - _thread (heap-allocated in start()) was never joined or Thread::~Thread() {}
// freed here. A joinable std::thread destroyed without join()/detach()
// calls std::terminate() immediately, which this leaked-pointer
// approach silently sidestepped: the wrapped OS thread just kept
// running - polling this Thread's EPoll reference, touching Socket
// objects registered with it - even after this object, its EPoll, and
// every one of those sockets had already been destroyed out from under
// it (confirmed live: an exception thrown during BARANTMail startup,
// after EPoll::start() had already spun up its worker pool, unwound
// the stack destructing EPoll and its registered sockets while those
// still-running threads kept dereferencing them, producing a real
// "double free or corruption" crash). join() first (skipped if already
// joined via EPoll::stop() having run normally) so the OS thread is
// actually gone before anything it might touch gets freed.
Thread::~Thread() {
if (_thread) {
if (_thread->joinable())
_thread->join();
delete _thread;
}
}
void Thread::start() { void Thread::start() {
_thread = new std::thread(&Thread::run, this); _thread = new std::thread(&Thread::run, this);
@@ -68,9 +40,6 @@ namespace core {
coreutils::Log(coreutils::LOG_DEBUG_1) << "Thread started with thread id " << threadId << "."; coreutils::Log(coreutils::LOG_DEBUG_1) << "Thread started with thread id " << threadId << ".";
if(thread)
thread->onThreadStart();
count = 0; count = 0;
struct epoll_event events[50]; struct epoll_event events[50];
@@ -81,108 +50,27 @@ namespace core {
break; break;
status = "WAITING"; status = "WAITING";
// A finite timeout, not the previous -1 (wait forever), is what int rc = epoll_wait(ePoll.getDescriptor(), events, 50, -1);
// makes EPoll::stop() able to return at all: a thread idly
// blocked here with no socket activity would otherwise never
// re-check isStopping() above, so join()ing it (both
// EPoll::stop() and, now, ~Thread() itself) would hang forever.
// Costs up to a one-second shutdown latency.
int rc = epoll_wait(ePoll.getDescriptor(), events, 50, 1000);
status = "RUNNING"; status = "RUNNING";
if(rc < 0) { if(rc < 0) {
// TODO: Make log entry indicating status received and ignore for now. // TODO: Make log entry indicating status received and ignore for now.
} else if(rc == 0) { } else if(rc == 0) {
// Plain timeout, no socket activity - not itself a signal to break;
// stop (isStopping() above already decides that). Previously
// broke out of the loop unconditionally, which the old
// infinite wait never actually triggered in practice, but
// would have silently ended this thread after any single
// idle second once the wait became finite.
} else if(rc > 0) { } else if(rc > 0) {
for(int ix = 0; ix < rc; ++ix) { for(int ix = 0; ix < rc; ++ix) {
++count; ++count;
// events[ix].data.ptr is a raw pointer of UNKNOWN validity at if(((Socket *)events[ix].data.ptr)->eventReceived(events[ix], ++ePoll.eventId)) {
// this point - it could be a live TCPSession, a listening // coreutils::Log(coreutils::LOG_DEBUG_4) << "return true";
// TCPServer/ConsoleServer (immortal, never shared_ptr-owned), // ePoll.resetSocket((Socket *)events[ix].data.ptr);
// or - the case this whole block exists to handle safely - a
// stale/duplicate event for a TCPSession something else
// already fully destroyed and freed on another thread.
//
// A first version of this fix used dynamic_cast<TCPSession*>
// to tell these apart, and a version before that cast
// straight to Socket* unconditionally (the original code).
// BOTH require reading the pointed-to object's own memory
// (its vtable, at minimum) to work - confirmed live via
// AddressSanitizer that dynamic_cast itself segfaults on a
// stale pointer, before any pin()-based safety logic even
// runs. TCPSession::tryGet() and TCPServer::isKnownListening
// Socket() below never dereference this raw pointer for their
// own check - only compare its address against a registry -
// so they're safe to call on literally any value here,
// known-good or not.
void *rawPtr = events[ix].data.ptr;
std::shared_ptr<TCPSession> pinned = TCPSession::tryGet(rawPtr);
if(pinned) {
if(pinned->eventReceived(events[ix], ++ePoll.eventId)) {
// keep alive - pinned goes out of scope at the end of
// this iteration, releasing this thread's temporary
// reference; real ownership (TCPServer::sessions) is
// unaffected.
} else { } else {
pinned->shutDown = true; ((Socket *)events[ix].data.ptr)->shutDown = true;
// Was `delete (Socket *)events[ix].data.ptr;` // coreutils::Log(coreutils::LOG_DEBUG_4) << "return false";
// unconditionally, for every socket type, including delete (Socket *)events[ix].data.ptr;
// real sessions another thread might be mid-call on
// right now via its own pin()'d reference. Dropping
// the one real owning reference is what actually
// frees the object - immediately, if this thread's
// own `pinned` (about to go out of scope) was the
// last one outstanding; safely deferred otherwise.
pinned->server->removeFromSessionList(pinned.get());
}
} else if(TCPServer::isKnownListeningSocket(rawPtr)) {
// Known-immortal (see its own doc comment) - safe to call
// directly, unchanged from the original behavior. A
// listening socket's own eventReceived() returning false
// isn't something that happens in normal operation, but
// preserved as-is for parity with the pre-existing code.
Socket *rawSocket = (Socket *)rawPtr;
if(rawSocket->eventReceived(events[ix], ++ePoll.eventId)) {
// keep alive
} else {
rawSocket->shutDown = true;
delete rawSocket;
}
} else if(INotify::isKnownINotify(rawPtr)) {
// A third category of long-lived, non-TCPSession Socket -
// see INotify::isKnownINotify()'s own doc comment for the
// real, confirmed-live bug this fixes: every INotify event
// (e.g. BARANTMail's mail-queue delivery watch) previously
// fell all the way through to the "unrecognized" branch
// below and was silently dropped forever.
Socket *rawSocket = (Socket *)rawPtr;
if(rawSocket->eventReceived(events[ix], ++ePoll.eventId)) {
// keep alive
} else {
rawSocket->shutDown = true;
delete rawSocket;
}
} else {
// Neither registry recognizes this pointer - almost
// certainly a stale/duplicate epoll event for a
// TCPSession that has already been fully destroyed and
// unregistered elsewhere. There is nothing safe to do
// with it except leave it alone entirely.
coreutils::Log(coreutils::LOG_DEBUG_1) << "Ignoring stale/unrecognized epoll event (pointer " << rawPtr << ")";
} }
} }
} }
} }
if(thread)
thread->onThreadEnd();
coreutils::Log(coreutils::LOG_DEBUG_1) << "Thread ending with thread id " << threadId << "."; coreutils::Log(coreutils::LOG_DEBUG_1) << "Thread ending with thread id " << threadId << ".";
} }
+3 -2
View File
@@ -1,10 +1,11 @@
#ifndef __Thread_h__ #ifndef __Thread_h__
#define __Thread_h__ #define __Thread_h__
#include "includes"
#include "Log.h" #include "Log.h"
#include "Object.h"
#include "TCPSession.h" #include "TCPSession.h"
#include "ThreadScope.h" #include "ThreadScope.h"
#include <thread>
namespace core { namespace core {
@@ -18,7 +19,7 @@ namespace core {
/// a Thread object for each thread specified in the EPoll's start method. /// a Thread object for each thread specified in the EPoll's start method.
/// ///
class Thread { class Thread : public Object {
public: public:
Thread(EPoll &ePoll); Thread(EPoll &ePoll);
-24
View File
@@ -3,34 +3,10 @@
namespace core { namespace core {
///
/// ThreadScope
///
/// Extension point for per-OS-thread setup/teardown in an EPoll worker
/// pool. EPoll's threads are long-lived (one persistent event loop per
/// thread, for the process's whole lifetime, not spawned per-request) -
/// onThreadStart() runs once at the top of each thread, before it
/// enters its event loop; onThreadEnd() runs once as that thread is
/// shutting down. A single ThreadScope instance is shared across every
/// worker thread (passed to EPoll::start()), so overrides should only
/// touch thread-LOCAL state from these hooks, never the ThreadScope
/// object's own fields, without their own synchronization.
///
/// Concrete motivating case: a C library that requires explicit
/// per-thread initialization before use from a multi-threaded program
/// (e.g. the MySQL client library's mysql_thread_init()/
/// mysql_thread_end() - see APIServer's MysqlThreadScope) has no other
/// clean way to hook into an EPoll worker thread's lifetime.
///
class ThreadScope { class ThreadScope {
public: public:
ThreadScope() {} ThreadScope() {}
virtual ~ThreadScope() {}
virtual void onThreadStart() {}
virtual void onThreadEnd() {}
}; };
-8
View File
@@ -1,18 +1,10 @@
#include "Timer.h" #include "Timer.h"
#include <sys/timerfd.h>
#include <math.h>
namespace core namespace core
{ {
Timer::Timer(EPoll &ePoll, double delay = 0.0f) : Socket(ePoll, "Timer") Timer::Timer(EPoll &ePoll, double delay = 0.0f) : Socket(ePoll, "Timer")
{ {
// See preventSelfDelete's own doc comment on Socket - a Timer is
// always a subobject of some larger composite (e.g. ZephoraSession
// : TCPSession, Timer), never a standalone connection, so its own
// eventReceived() must never be allowed to signal "delete me" to
// Thread::run()'s generic dispatch.
preventSelfDelete = true;
setDescriptor(timerfd_create(CLOCK_REALTIME, 0)); setDescriptor(timerfd_create(CLOCK_REALTIME, 0));
setTimer(delay); setTimer(delay);
} }
-2
View File
@@ -1,8 +1,6 @@
#include "UDPServerSocket.h" #include "UDPServerSocket.h"
#include "EPoll.h" #include "EPoll.h"
#include "TCPSession.h" #include "TCPSession.h"
#include <sys/socket.h>
#include <netdb.h>
namespace core { namespace core {
+1 -1
View File
@@ -28,7 +28,7 @@ else
fi fi
echo -n "Building library documentation manual..." echo -n "Building library documentation manual..."
doxygen doxygen.sty >/dev/null 2>/dev/null doxygen docs/latex/doxygen.sty >/dev/null 2>/dev/null
echo "OK" echo "OK"
rm *.o rm *.o
+75
View File
@@ -0,0 +1,75 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAAccount.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAAccount.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef __BMAAccount_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define __BMAAccount_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"><a class="line" href="class_b_m_a_account.html"> 4</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_account.html">BMAAccount</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_object.html">BMAObject</a> {</div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;</div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span>&#160; <a class="code" href="class_b_m_a_account.html">BMAAccount</a>();</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160; ~<a class="code" href="class_b_m_a_account.html">BMAAccount</a>();</div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span>&#160; </div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; <span class="keywordtype">string</span> getName();</div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; <span class="keywordtype">void</span> setName(<span class="keywordtype">string</span> name);</div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160; <span class="keywordtype">string</span> getAlias();</div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160; <span class="keywordtype">void</span> setAlias(<span class="keywordtype">string</span> name);</div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160; vector&lt;string&gt; contacts;</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; ```````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````` </div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160; </div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160;};</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_account_html"><div class="ttname"><a href="class_b_m_a_account.html">BMAAccount</a></div><div class="ttdef"><b>Definition:</b> BMAAccount.h:4</div></div>
<div class="ttc" id="class_b_m_a_object_html"><div class="ttname"><a href="class_b_m_a_object.html">BMAObject</a></div><div class="ttdef"><b>Definition:</b> BMAObject.h:6</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
+110
View File
@@ -0,0 +1,110 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAAuthenticate.cpp File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAAuthenticate.cpp File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_authenticate_8h_source.html">BMAAuthenticate.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAAuthenticate.cpp:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_authenticate_8cpp__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8cpp" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8cpp" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8cpp">
<area shape="rect" id="node2" href="_b_m_a_authenticate_8h.html" title="BMAAuthenticate.h" alt="" coords="227,109,362,136"/>
<area shape="rect" id="node3" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="210,184,378,211"/>
<area shape="rect" id="node4" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="140,333,243,360"/>
<area shape="rect" id="node8" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="229,259,360,285"/>
<area shape="rect" id="node9" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="332,333,502,360"/>
<area shape="rect" id="node6" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="25,408,121,435"/>
<area shape="rect" id="node7" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="146,408,256,435"/>
</map>
</div>
</div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,9 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.cpp" name="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.cpp">
<area shape="rect" id="node2" href="$_b_m_a_authenticate_8h.html" title="BMAAuthenticate.h" alt="" coords="227,109,362,136"/>
<area shape="rect" id="node3" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="210,184,378,211"/>
<area shape="rect" id="node4" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="140,333,243,360"/>
<area shape="rect" id="node8" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="229,259,360,285"/>
<area shape="rect" id="node9" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="332,333,502,360"/>
<area shape="rect" id="node6" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="25,408,121,435"/>
<area shape="rect" id="node7" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="146,408,256,435"/>
</map>
@@ -0,0 +1 @@
4516a389b39c60e140f783cc3824bd1d
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+127
View File
@@ -0,0 +1,127 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAAuthenticate.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAAuthenticate.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_t_c_p_server_socket_8h_source.html">BMATCPServerSocket.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAAuthenticate.h:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_authenticate_8h__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8h" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8h" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8h">
<area shape="rect" id="node2" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="210,109,378,136"/>
<area shape="rect" id="node3" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="140,259,243,285"/>
<area shape="rect" id="node7" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="229,184,360,211"/>
<area shape="rect" id="node8" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="332,259,502,285"/>
<area shape="rect" id="node5" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="25,333,121,360"/>
<area shape="rect" id="node6" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="146,333,256,360"/>
</map>
</div>
</div><div class="textblock"><div class="dynheader">
This graph shows which files directly or indirectly include this file:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_authenticate_8h__dep__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8hdep" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8hdep" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_authenticate_8hdep">
<area shape="rect" id="node2" href="_b_m_a_authenticate_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.cpp" alt="" coords="5,109,189,165"/>
</map>
</div>
</div>
<p><a href="_b_m_a_authenticate_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_b_m_a_authenticate.html">BMAAuthenticate</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,3 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.h" name="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.h">
<area shape="rect" id="node2" href="$_b_m_a_authenticate_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.cpp" alt="" coords="5,109,189,165"/>
</map>
@@ -0,0 +1 @@
6990c5da0ea71ff0fb49e734e508f2ce
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

@@ -0,0 +1,8 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.h" name="/home/barant/Documents/Development/BMASockets/BMAAuthenticate.h">
<area shape="rect" id="node2" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="210,109,378,136"/>
<area shape="rect" id="node3" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="140,259,243,285"/>
<area shape="rect" id="node7" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="229,184,360,211"/>
<area shape="rect" id="node8" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="332,259,502,285"/>
<area shape="rect" id="node5" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="25,333,121,360"/>
<area shape="rect" id="node6" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="146,333,256,360"/>
</map>
@@ -0,0 +1 @@
47e2cc4566474eae16b06bb68e488e3b
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@@ -0,0 +1,76 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Development/BMA/server_core/ServerCore/BMAAuthenticate.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Development/BMA/server_core/ServerCore/BMAAuthenticate.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef __BMAAuthenticate_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define __BMAAuthenticate_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;includes&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="preprocessor">#include &quot;BMASession.h&quot;</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;</div><div class="line"><a name="l00007"></a><span class="lineno"><a class="line" href="class_b_m_a_authenticate.html"> 7</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_authenticate.html">BMAAuthenticate</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_object.html">BMAObject</a> {</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160;</div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; <a class="code" href="class_b_m_a_authenticate.html">BMAAuthenticate</a>(<a class="code" href="class_b_m_a_session.html">BMASession</a> *session);</div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; ~<a class="code" href="class_b_m_a_authenticate.html">BMAAuthenticate</a>();</div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160;</div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160; <span class="keywordtype">void</span> onStart();</div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160; <span class="keywordtype">void</span> onDataReceived(<span class="keywordtype">char</span> *data, <span class="keywordtype">int</span> length);</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; <span class="keywordtype">void</span> onEnd();</div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160; </div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160;};</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_session_html"><div class="ttname"><a href="class_b_m_a_session.html">BMASession</a></div><div class="ttdef"><b>Definition:</b> BMASession.h:18</div></div>
<div class="ttc" id="class_b_m_a_object_html"><div class="ttname"><a href="class_b_m_a_object.html">BMAObject</a></div><div class="ttdef"><b>Definition:</b> BMAObject.h:6</div></div>
<div class="ttc" id="class_b_m_a_authenticate_html"><div class="ttname"><a href="class_b_m_a_authenticate.html">BMAAuthenticate</a></div><div class="ttdef"><b>Definition:</b> BMAAuthenticate.h:7</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Development/BMA/server_core/ServerCore/BMACommand.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Development/BMA/server_core/ServerCore/BMACommand.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef BMACommand_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define BMACommand_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;includes&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="preprocessor">#include &quot;BMAObject.h&quot;</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_session.html">BMASession</a>;</div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span>&#160;</div><div class="line"><a name="l00008"></a><span class="lineno"><a class="line" href="class_b_m_a_command.html"> 8</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_command.html">BMACommand</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_object.html">BMAObject</a> {</div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span>&#160; </div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; <a class="code" href="class_b_m_a_command.html">BMACommand</a>(std::string commandName);</div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160; ~<a class="code" href="class_b_m_a_command.html">BMACommand</a>(); </div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160; </div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160; std::string commandName;</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160;</div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160; <span class="keyword">virtual</span> <span class="keywordtype">void</span> processCommand(std::string command, <a class="code" href="class_b_m_a_session.html">BMASession</a> *session) = 0;</div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160; <span class="keyword">virtual</span> <span class="keywordtype">void</span> output(<a class="code" href="class_b_m_a_session.html">BMASession</a> *session);</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160;};</div><div class="line"><a name="l00020"></a><span class="lineno"> 20</span>&#160;</div><div class="line"><a name="l00021"></a><span class="lineno"> 21</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_command_html"><div class="ttname"><a href="class_b_m_a_command.html">BMACommand</a></div><div class="ttdef"><b>Definition:</b> BMACommand.h:8</div></div>
<div class="ttc" id="class_b_m_a_session_html"><div class="ttname"><a href="class_b_m_a_session.html">BMASession</a></div><div class="ttdef"><b>Definition:</b> BMASession.h:18</div></div>
<div class="ttc" id="class_b_m_a_object_html"><div class="ttname"><a href="class_b_m_a_object.html">BMAObject</a></div><div class="ttdef"><b>Definition:</b> BMAObject.h:6</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
@@ -0,0 +1,77 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMACommandSession.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMACommandSession.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef __BMACommandSession_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define __BMACommandSession_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;BMASession.h&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;</div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160;</div><div class="line"><a name="l00014"></a><span class="lineno"><a class="line" href="class_b_m_a_command_session.html"> 14</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_command_session.html">BMACommandSession</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_session.html">BMASession</a> {</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; </div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160; <a class="code" href="class_b_m_a_command_session.html">BMACommandSession</a>(<a class="code" href="class_b_m_a_e_poll.html">BMAEPoll</a> &amp;ePoll);</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160; ~<a class="code" href="class_b_m_a_command_session.html">BMACommandSession</a>(); </div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160; </div><div class="line"><a name="l00020"></a><span class="lineno"> 20</span>&#160;<span class="comment">// string command;</span></div><div class="line"><a name="l00021"></a><span class="lineno"> 21</span>&#160; </div><div class="line"><a name="l00022"></a><span class="lineno"> 22</span>&#160; <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a class="code" href="class_b_m_a_command_session.html#a72e11ea685f9bbc32f66d42ab5a1627d">output</a>(stringstream &amp;out);</div><div class="line"><a name="l00023"></a><span class="lineno"> 23</span>&#160;</div><div class="line"><a name="l00024"></a><span class="lineno"> 24</span>&#160; <span class="keyword">protected</span>:</div><div class="line"><a name="l00025"></a><span class="lineno"> 25</span>&#160;<span class="comment">// virtual void onConnected(); </span></div><div class="line"><a name="l00026"></a><span class="lineno"> 26</span>&#160;<span class="comment">// virtual void onDataReceived(char *data, int length); </span></div><div class="line"><a name="l00027"></a><span class="lineno"> 27</span>&#160; <span class="keywordtype">void</span> protocol(<span class="keywordtype">char</span> *data, <span class="keywordtype">int</span> length) <span class="keyword">override</span>; </div><div class="line"><a name="l00028"></a><span class="lineno"> 28</span>&#160; </div><div class="line"><a name="l00029"></a><span class="lineno"> 29</span>&#160; <span class="keyword">private</span>:</div><div class="line"><a name="l00030"></a><span class="lineno"> 30</span>&#160; <span class="keyword">enum</span> Status {WELCOME, PROMPT, INPUT, PROCESS, DONE};</div><div class="line"><a name="l00031"></a><span class="lineno"> 31</span>&#160; Status status = WELCOME; </div><div class="line"><a name="l00032"></a><span class="lineno"> 32</span>&#160; </div><div class="line"><a name="l00033"></a><span class="lineno"> 33</span>&#160;};</div><div class="line"><a name="l00034"></a><span class="lineno"> 34</span>&#160;</div><div class="line"><a name="l00035"></a><span class="lineno"> 35</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_e_poll_html"><div class="ttname"><a href="class_b_m_a_e_poll.html">BMAEPoll</a></div><div class="ttdef"><b>Definition:</b> BMAEPoll.h:29</div></div>
<div class="ttc" id="class_b_m_a_command_session_html_a72e11ea685f9bbc32f66d42ab5a1627d"><div class="ttname"><a href="class_b_m_a_command_session.html#a72e11ea685f9bbc32f66d42ab5a1627d">BMACommandSession::output</a></div><div class="ttdeci">virtual void output(stringstream &amp;out)</div><div class="ttdef"><b>Definition:</b> BMACommandSession.cpp:10</div></div>
<div class="ttc" id="class_b_m_a_command_session_html"><div class="ttname"><a href="class_b_m_a_command_session.html">BMACommandSession</a></div><div class="ttdef"><b>Definition:</b> BMACommandSession.h:14</div></div>
<div class="ttc" id="class_b_m_a_session_html"><div class="ttname"><a href="class_b_m_a_session.html">BMASession</a></div><div class="ttdef"><b>Definition:</b> BMASession.h:16</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsole.cpp File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsole.cpp File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_e_poll_8h_source.html">BMAEPoll.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_console_8h_source.html">BMAConsole.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_console_session_8h_source.html">BMAConsoleSession.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsole.cpp:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_8cpp__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8cpp" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8cpp" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8cpp">
<area shape="rect" id="node2" href="_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="205,184,299,211"/>
<area shape="rect" id="node9" href="_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="198,408,309,435"/>
<area shape="rect" id="node13" href="_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="111,109,268,136"/>
<area shape="rect" id="node3" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="67,632,170,659"/>
<area shape="rect" id="node7" href="_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="290,259,393,285"/>
<area shape="rect" id="node5" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="5,707,101,733"/>
<area shape="rect" id="node6" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="177,707,287,733"/>
<area shape="rect" id="node8" href="_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="295,333,377,360"/>
<area shape="rect" id="node10" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="153,483,321,509"/>
<area shape="rect" id="node12" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="291,557,461,584"/>
<area shape="rect" id="node11" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="136,557,267,584"/>
</map>
</div>
</div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
+13
View File
@@ -0,0 +1,13 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsole.cpp" name="/home/barant/Documents/Development/BMASockets/BMAConsole.cpp">
<area shape="rect" id="node2" href="$_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="205,184,299,211"/>
<area shape="rect" id="node9" href="$_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="198,408,309,435"/>
<area shape="rect" id="node13" href="$_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="111,109,268,136"/>
<area shape="rect" id="node3" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="67,632,170,659"/>
<area shape="rect" id="node7" href="$_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="290,259,393,285"/>
<area shape="rect" id="node5" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="5,707,101,733"/>
<area shape="rect" id="node6" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="177,707,287,733"/>
<area shape="rect" id="node8" href="$_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="295,333,377,360"/>
<area shape="rect" id="node10" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="153,483,321,509"/>
<area shape="rect" id="node12" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="291,557,461,584"/>
<area shape="rect" id="node11" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="136,557,267,584"/>
</map>
+1
View File
@@ -0,0 +1 @@
6068b19be17a126ec437ed1ad655c961
Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

+150
View File
@@ -0,0 +1,150 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsole.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsole.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_t_c_p_server_socket_8h_source.html">BMATCPServerSocket.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_console_command_8h_source.html">BMAConsoleCommand.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_log_8h_source.html">BMALog.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsole.h:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_8h__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8h" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8h" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8h">
<area shape="rect" id="node2" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="111,109,279,136"/>
<area shape="rect" id="node8" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="258,184,429,211"/>
<area shape="rect" id="node9" href="_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="381,109,463,136"/>
<area shape="rect" id="node3" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="20,259,123,285"/>
<area shape="rect" id="node7" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="103,184,234,211"/>
<area shape="rect" id="node5" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="23,333,119,360"/>
<area shape="rect" id="node6" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="144,333,255,360"/>
</map>
</div>
</div><div class="textblock"><div class="dynheader">
This graph shows which files directly or indirectly include this file:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_8h__dep__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8hdep" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8hdep" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_8hdep">
<area shape="rect" id="node2" href="_b_m_a_log_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.h" alt="" coords="1285,109,1469,165"/>
<area shape="rect" id="node4" href="_b_m_a_e_poll_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.h" alt="" coords="1221,317,1405,373"/>
<area shape="rect" id="node5" href="_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="5,525,189,581"/>
<area shape="rect" id="node21" href="main_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/main.cpp" alt="" coords="2312,421,2496,477"/>
<area shape="rect" id="node3" href="_b_m_a_thread_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.h" alt="" coords="1285,213,1469,269"/>
<area shape="rect" id="node22" href="_b_m_a_log_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.cpp" alt="" coords="1493,213,1677,269"/>
<area shape="rect" id="node9" href="_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="635,525,819,581"/>
<area shape="rect" id="node19" href="_b_m_a_thread_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.cpp" alt="" coords="2104,421,2288,477"/>
<area shape="rect" id="node6" href="_b_m_a_console_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.h" alt="" coords="515,421,699,477"/>
<area shape="rect" id="node10" href="_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="843,525,1029,581"/>
<area shape="rect" id="node11" href="_b_m_a_h_t_t_p_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.cpp" alt="" coords="1053,525,1237,581"/>
<area shape="rect" id="node12" href="_b_m_a_h_t_t_p_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.h" alt="" coords="1013,421,1197,477"/>
<area shape="rect" id="node14" href="_b_m_a_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMASocket.cpp" alt="" coords="1221,421,1405,477"/>
<area shape="rect" id="node15" href="_b_m_a_stream_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.cpp" alt="" coords="1469,525,1653,581"/>
<area shape="rect" id="node16" href="_b_m_a_stream_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.h" alt="" coords="1480,421,1664,477"/>
<area shape="rect" id="node18" href="_b_m_a_t_c_p_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPSocket.cpp" alt="" coords="1688,421,1872,477"/>
<area shape="rect" id="node20" href="_b_m_a_timer_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATimer.cpp" alt="" coords="1896,421,2080,477"/>
<area shape="rect" id="node7" href="_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="214,525,402,581"/>
<area shape="rect" id="node8" href="_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="427,525,611,581"/>
<area shape="rect" id="node13" href="_b_m_a_h_t_t_p_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.cpp" alt="" coords="1261,525,1445,581"/>
<area shape="rect" id="node17" href="_b_m_a_stream_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.cpp" alt="" coords="1677,525,1861,581"/>
</map>
</div>
</div>
<p><a href="_b_m_a_console_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_b_m_a_console.html">BMAConsole</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,23 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsole.h" name="/home/barant/Documents/Development/BMASockets/BMAConsole.h">
<area shape="rect" id="node2" href="$_b_m_a_log_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.h" alt="" coords="1285,109,1469,165"/>
<area shape="rect" id="node4" href="$_b_m_a_e_poll_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.h" alt="" coords="1221,317,1405,373"/>
<area shape="rect" id="node5" href="$_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="5,525,189,581"/>
<area shape="rect" id="node21" href="$main_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/main.cpp" alt="" coords="2312,421,2496,477"/>
<area shape="rect" id="node3" href="$_b_m_a_thread_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.h" alt="" coords="1285,213,1469,269"/>
<area shape="rect" id="node22" href="$_b_m_a_log_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.cpp" alt="" coords="1493,213,1677,269"/>
<area shape="rect" id="node9" href="$_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="635,525,819,581"/>
<area shape="rect" id="node19" href="$_b_m_a_thread_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.cpp" alt="" coords="2104,421,2288,477"/>
<area shape="rect" id="node6" href="$_b_m_a_console_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.h" alt="" coords="515,421,699,477"/>
<area shape="rect" id="node10" href="$_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="843,525,1029,581"/>
<area shape="rect" id="node11" href="$_b_m_a_h_t_t_p_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.cpp" alt="" coords="1053,525,1237,581"/>
<area shape="rect" id="node12" href="$_b_m_a_h_t_t_p_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.h" alt="" coords="1013,421,1197,477"/>
<area shape="rect" id="node14" href="$_b_m_a_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMASocket.cpp" alt="" coords="1221,421,1405,477"/>
<area shape="rect" id="node15" href="$_b_m_a_stream_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.cpp" alt="" coords="1469,525,1653,581"/>
<area shape="rect" id="node16" href="$_b_m_a_stream_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.h" alt="" coords="1480,421,1664,477"/>
<area shape="rect" id="node18" href="$_b_m_a_t_c_p_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPSocket.cpp" alt="" coords="1688,421,1872,477"/>
<area shape="rect" id="node20" href="$_b_m_a_timer_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATimer.cpp" alt="" coords="1896,421,2080,477"/>
<area shape="rect" id="node7" href="$_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="214,525,402,581"/>
<area shape="rect" id="node8" href="$_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="427,525,611,581"/>
<area shape="rect" id="node13" href="$_b_m_a_h_t_t_p_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.cpp" alt="" coords="1261,525,1445,581"/>
<area shape="rect" id="node17" href="$_b_m_a_stream_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.cpp" alt="" coords="1677,525,1861,581"/>
</map>
@@ -0,0 +1 @@
11c30597ee7e0116a48afaee64e1854c
Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

+9
View File
@@ -0,0 +1,9 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsole.h" name="/home/barant/Documents/Development/BMASockets/BMAConsole.h">
<area shape="rect" id="node2" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="111,109,279,136"/>
<area shape="rect" id="node8" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="258,184,429,211"/>
<area shape="rect" id="node9" href="$_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="381,109,463,136"/>
<area shape="rect" id="node3" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="20,259,123,285"/>
<area shape="rect" id="node7" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="103,184,234,211"/>
<area shape="rect" id="node5" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="23,333,119,360"/>
<area shape="rect" id="node6" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="144,333,255,360"/>
</map>
+1
View File
@@ -0,0 +1 @@
a75b2e10971093e0c0adfe3aadf8dcbd
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+78
View File
@@ -0,0 +1,78 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsole.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsole.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef BMAConsole_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define BMAConsole_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;includes&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="preprocessor">#include &quot;BMATCPServerSocket.h&quot;</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;<span class="comment">//#include &quot;BMACommand.h&quot;</span></div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_t_c_p_socket.html">BMATCPSocket</a>;</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160;</div><div class="line"><a name="l00009"></a><span class="lineno"><a class="line" href="class_b_m_a_console.html"> 9</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_console.html">BMAConsole</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_t_c_p_server_socket.html">BMATCPServerSocket</a> {</div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; </div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160; <a class="code" href="class_b_m_a_console.html">BMAConsole</a>(<a class="code" href="class_b_m_a_e_poll.html">BMAEPoll</a> &amp;ePoll, <span class="keywordtype">string</span> url, <span class="keywordtype">short</span> <span class="keywordtype">int</span> port);</div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160; ~<a class="code" href="class_b_m_a_console.html">BMAConsole</a>(); </div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160;</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; <a class="code" href="class_b_m_a_session.html">BMASession</a> * getSocketAccept();</div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160;</div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160;<span class="comment">// void registerCommand(BMACommand &amp;command);</span></div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160;<span class="comment">// vector&lt;BMACommand *&gt; commands; </span></div><div class="line"><a name="l00020"></a><span class="lineno"> 20</span>&#160; </div><div class="line"><a name="l00021"></a><span class="lineno"> 21</span>&#160;<span class="comment">// void protocolHandler() {</span></div><div class="line"><a name="l00022"></a><span class="lineno"> 22</span>&#160; </div><div class="line"><a name="l00023"></a><span class="lineno"> 23</span>&#160;<span class="comment">// BMAAnnouncer announcer(&quot;Welcome to BMA Server Framework console.\n&quot;); </span></div><div class="line"><a name="l00024"></a><span class="lineno"> 24</span>&#160;<span class="comment">// addProtocol(announcer);</span></div><div class="line"><a name="l00025"></a><span class="lineno"> 25</span>&#160;<span class="comment">// BMACommandProcessor commandProcess();</span></div><div class="line"><a name="l00026"></a><span class="lineno"> 26</span>&#160; </div><div class="line"><a name="l00027"></a><span class="lineno"> 27</span>&#160;<span class="comment">// }</span></div><div class="line"><a name="l00028"></a><span class="lineno"> 28</span>&#160;</div><div class="line"><a name="l00029"></a><span class="lineno"> 29</span>&#160;};</div><div class="line"><a name="l00030"></a><span class="lineno"> 30</span>&#160;</div><div class="line"><a name="l00031"></a><span class="lineno"> 31</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_t_c_p_socket_html"><div class="ttname"><a href="class_b_m_a_t_c_p_socket.html">BMATCPSocket</a></div><div class="ttdef"><b>Definition:</b> BMATCPSocket.h:17</div></div>
<div class="ttc" id="class_b_m_a_console_html"><div class="ttname"><a href="class_b_m_a_console.html">BMAConsole</a></div><div class="ttdef"><b>Definition:</b> BMAConsole.h:9</div></div>
<div class="ttc" id="class_b_m_a_t_c_p_server_socket_html"><div class="ttname"><a href="class_b_m_a_t_c_p_server_socket.html">BMATCPServerSocket</a></div><div class="ttdef"><b>Definition:</b> BMATCPServerSocket.h:15</div></div>
<div class="ttc" id="class_b_m_a_e_poll_html"><div class="ttname"><a href="class_b_m_a_e_poll.html">BMAEPoll</a></div><div class="ttdef"><b>Definition:</b> BMAEPoll.h:29</div></div>
<div class="ttc" id="class_b_m_a_session_html"><div class="ttname"><a href="class_b_m_a_session.html">BMASession</a></div><div class="ttdef"><b>Definition:</b> BMASession.h:16</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
+115
View File
@@ -0,0 +1,115 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsoleCommand.cpp File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.cpp File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_console_command_8h_source.html">BMAConsoleCommand.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_console_session_8h_source.html">BMAConsoleSession.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsoleCommand.cpp:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_command_8cpp__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8cpp" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8cpp" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8cpp">
<area shape="rect" id="node2" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="144,557,315,584"/>
<area shape="rect" id="node4" href="_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="173,109,331,136"/>
<area shape="rect" id="node5" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="440,557,571,584"/>
<area shape="rect" id="node9" href="_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="205,184,299,211"/>
<area shape="rect" id="node6" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="326,632,429,659"/>
<area shape="rect" id="node7" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="245,707,341,733"/>
<area shape="rect" id="node8" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="366,707,477,733"/>
<area shape="rect" id="node10" href="_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="111,259,214,285"/>
<area shape="rect" id="node12" href="_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="145,408,255,435"/>
<area shape="rect" id="node11" href="_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="127,333,209,360"/>
<area shape="rect" id="node13" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="197,483,365,509"/>
</map>
</div>
</div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,13 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.cpp" name="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.cpp">
<area shape="rect" id="node2" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="144,557,315,584"/>
<area shape="rect" id="node4" href="$_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="173,109,331,136"/>
<area shape="rect" id="node5" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="440,557,571,584"/>
<area shape="rect" id="node9" href="$_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="205,184,299,211"/>
<area shape="rect" id="node6" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="326,632,429,659"/>
<area shape="rect" id="node7" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="245,707,341,733"/>
<area shape="rect" id="node8" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="366,707,477,733"/>
<area shape="rect" id="node10" href="$_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="111,259,214,285"/>
<area shape="rect" id="node12" href="$_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="145,408,255,435"/>
<area shape="rect" id="node11" href="$_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="127,333,209,360"/>
<area shape="rect" id="node13" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="197,483,365,509"/>
</map>
@@ -0,0 +1 @@
12c6b7c7bbfaeec186638b4c66cc0276
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

+151
View File
@@ -0,0 +1,151 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;includes&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsoleCommand.h:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_command_8h__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8h" alt=""/></div>
</div>
</div><div class="textblock"><div class="dynheader">
This graph shows which files directly or indirectly include this file:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_command_8h__dep__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8hdep" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8hdep" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_command_8hdep">
<area shape="rect" id="node2" href="_b_m_a_t_c_p_server_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.h" alt="" coords="2018,109,2202,165"/>
<area shape="rect" id="node5" href="_b_m_a_console_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.h" alt="" coords="962,213,1146,269"/>
<area shape="rect" id="node11" href="_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="5,733,193,789"/>
<area shape="rect" id="node3" href="_b_m_a_authenticate_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.h" alt="" coords="2147,213,2331,269"/>
<area shape="rect" id="node14" href="_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="1091,733,1278,789"/>
<area shape="rect" id="node25" href="main_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/main.cpp" alt="" coords="2385,629,2569,685"/>
<area shape="rect" id="node27" href="_b_m_a_h_t_t_p_server_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.h" alt="" coords="2046,421,2230,477"/>
<area shape="rect" id="node30" href="_b_m_a_stream_server_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.h" alt="" coords="2502,317,2686,373"/>
<area shape="rect" id="node4" href="_b_m_a_authenticate_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.cpp" alt="" coords="2147,317,2331,373"/>
<area shape="rect" id="node6" href="_b_m_a_log_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.h" alt="" coords="898,317,1082,373"/>
<area shape="rect" id="node8" href="_b_m_a_e_poll_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.h" alt="" coords="1027,525,1211,581"/>
<area shape="rect" id="node9" href="_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="218,733,402,789"/>
<area shape="rect" id="node7" href="_b_m_a_thread_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.h" alt="" coords="898,421,1082,477"/>
<area shape="rect" id="node26" href="_b_m_a_log_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.cpp" alt="" coords="690,421,874,477"/>
<area shape="rect" id="node13" href="_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="634,733,818,789"/>
<area shape="rect" id="node23" href="_b_m_a_thread_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.cpp" alt="" coords="187,629,371,685"/>
<area shape="rect" id="node10" href="_b_m_a_console_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.h" alt="" coords="446,629,630,685"/>
<area shape="rect" id="node15" href="_b_m_a_h_t_t_p_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.cpp" alt="" coords="1998,733,2182,789"/>
<area shape="rect" id="node16" href="_b_m_a_h_t_t_p_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.h" alt="" coords="1790,629,1974,685"/>
<area shape="rect" id="node18" href="_b_m_a_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMASocket.cpp" alt="" coords="1222,629,1406,685"/>
<area shape="rect" id="node19" href="_b_m_a_stream_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.cpp" alt="" coords="1582,733,1766,789"/>
<area shape="rect" id="node20" href="_b_m_a_stream_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.h" alt="" coords="1481,629,1665,685"/>
<area shape="rect" id="node22" href="_b_m_a_t_c_p_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPSocket.cpp" alt="" coords="755,629,939,685"/>
<area shape="rect" id="node24" href="_b_m_a_timer_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATimer.cpp" alt="" coords="963,629,1147,685"/>
<area shape="rect" id="node12" href="_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="426,733,610,789"/>
<area shape="rect" id="node17" href="_b_m_a_h_t_t_p_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.cpp" alt="" coords="1790,733,1974,789"/>
<area shape="rect" id="node21" href="_b_m_a_stream_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.cpp" alt="" coords="1374,733,1558,789"/>
<area shape="rect" id="node28" href="_b_m_a_h_t_t_p_request_handler_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPRequestHandler.h" alt="" coords="2095,525,2290,581"/>
<area shape="rect" id="node29" href="_b_m_a_h_t_t_p_request_handler_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPRequestHandler.cpp" alt="" coords="2100,629,2309,685"/>
<area shape="rect" id="node31" href="_b_m_a_m_p3_stream_content_provider_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3StreamContentProvider.h" alt="" coords="2517,421,2746,477"/>
<area shape="rect" id="node32" href="_b_m_a_m_p3_file_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3File.h" alt="" coords="2587,525,2771,581"/>
<area shape="rect" id="node34" href="_b_m_a_stream_content_provider_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamContentProvider.cpp" alt="" coords="2770,421,2986,477"/>
<area shape="rect" id="node33" href="_b_m_a_m_p3_file_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3File.cpp" alt="" coords="2593,629,2777,685"/>
</map>
</div>
</div>
<p><a href="_b_m_a_console_command_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_b_m_a_console_command.html">BMAConsoleCommand</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,35 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h" name="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h">
<area shape="rect" id="node2" href="$_b_m_a_t_c_p_server_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.h" alt="" coords="2018,109,2202,165"/>
<area shape="rect" id="node5" href="$_b_m_a_console_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.h" alt="" coords="962,213,1146,269"/>
<area shape="rect" id="node11" href="$_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="5,733,193,789"/>
<area shape="rect" id="node3" href="$_b_m_a_authenticate_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.h" alt="" coords="2147,213,2331,269"/>
<area shape="rect" id="node14" href="$_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="1091,733,1278,789"/>
<area shape="rect" id="node25" href="$main_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/main.cpp" alt="" coords="2385,629,2569,685"/>
<area shape="rect" id="node27" href="$_b_m_a_h_t_t_p_server_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.h" alt="" coords="2046,421,2230,477"/>
<area shape="rect" id="node30" href="$_b_m_a_stream_server_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.h" alt="" coords="2502,317,2686,373"/>
<area shape="rect" id="node4" href="$_b_m_a_authenticate_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAAuthenticate.cpp" alt="" coords="2147,317,2331,373"/>
<area shape="rect" id="node6" href="$_b_m_a_log_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.h" alt="" coords="898,317,1082,373"/>
<area shape="rect" id="node8" href="$_b_m_a_e_poll_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.h" alt="" coords="1027,525,1211,581"/>
<area shape="rect" id="node9" href="$_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="218,733,402,789"/>
<area shape="rect" id="node7" href="$_b_m_a_thread_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.h" alt="" coords="898,421,1082,477"/>
<area shape="rect" id="node26" href="$_b_m_a_log_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMALog.cpp" alt="" coords="690,421,874,477"/>
<area shape="rect" id="node13" href="$_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="634,733,818,789"/>
<area shape="rect" id="node23" href="$_b_m_a_thread_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAThread.cpp" alt="" coords="187,629,371,685"/>
<area shape="rect" id="node10" href="$_b_m_a_console_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.h" alt="" coords="446,629,630,685"/>
<area shape="rect" id="node15" href="$_b_m_a_h_t_t_p_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPServer.cpp" alt="" coords="1998,733,2182,789"/>
<area shape="rect" id="node16" href="$_b_m_a_h_t_t_p_session_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.h" alt="" coords="1790,629,1974,685"/>
<area shape="rect" id="node18" href="$_b_m_a_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMASocket.cpp" alt="" coords="1222,629,1406,685"/>
<area shape="rect" id="node19" href="$_b_m_a_stream_server_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamServer.cpp" alt="" coords="1582,733,1766,789"/>
<area shape="rect" id="node20" href="$_b_m_a_stream_socket_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.h" alt="" coords="1481,629,1665,685"/>
<area shape="rect" id="node22" href="$_b_m_a_t_c_p_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPSocket.cpp" alt="" coords="755,629,939,685"/>
<area shape="rect" id="node24" href="$_b_m_a_timer_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATimer.cpp" alt="" coords="963,629,1147,685"/>
<area shape="rect" id="node12" href="$_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="426,733,610,789"/>
<area shape="rect" id="node17" href="$_b_m_a_h_t_t_p_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPSession.cpp" alt="" coords="1790,733,1974,789"/>
<area shape="rect" id="node21" href="$_b_m_a_stream_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamSocket.cpp" alt="" coords="1374,733,1558,789"/>
<area shape="rect" id="node28" href="$_b_m_a_h_t_t_p_request_handler_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPRequestHandler.h" alt="" coords="2095,525,2290,581"/>
<area shape="rect" id="node29" href="$_b_m_a_h_t_t_p_request_handler_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAHTTPRequestHandler.cpp" alt="" coords="2100,629,2309,685"/>
<area shape="rect" id="node31" href="$_b_m_a_m_p3_stream_content_provider_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3StreamContentProvider.h" alt="" coords="2517,421,2746,477"/>
<area shape="rect" id="node32" href="$_b_m_a_m_p3_file_8h.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3File.h" alt="" coords="2587,525,2771,581"/>
<area shape="rect" id="node34" href="$_b_m_a_stream_content_provider_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAStreamContentProvider.cpp" alt="" coords="2770,421,2986,477"/>
<area shape="rect" id="node33" href="$_b_m_a_m_p3_file_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAMP3File.cpp" alt="" coords="2593,629,2777,685"/>
</map>
@@ -0,0 +1 @@
b7aac9a8dc5b060765a217d42acff0c3
Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

@@ -0,0 +1,2 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h" name="/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h">
</map>
@@ -0,0 +1 @@
10344f63fcd5b31d13e617caa4baf2c3
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,101 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsoleCommand.h</div> </div>
</div><!--header-->
<div class="contents">
<a href="_b_m_a_console_command_8h.html">Go to the documentation of this file.</a><div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef BMAConsoleCommand_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define BMAConsoleCommand_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;includes&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_console_session.html">BMAConsoleSession</a>;</div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;</div><div class="line"><a name="l00007"></a><span class="lineno"><a class="line" href="class_b_m_a_console_command.html"> 7</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_console_command.html">BMAConsoleCommand</a> {</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160; </div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; <a class="code" href="class_b_m_a_console_command.html#a318938fc38946e3f2e5d606e84784754">BMAConsoleCommand</a>(<span class="keywordtype">string</span> <a class="code" href="class_b_m_a_console_command.html#a5c003e975e6d63f83e5131e5b8df758f">commandName</a>);</div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; <a class="code" href="class_b_m_a_console_command.html#a909dee1bc6659e50d95fa3d43526557a">~BMAConsoleCommand</a>(); </div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160; </div><div class="line"><a name="l00013"></a><span class="lineno"><a class="line" href="class_b_m_a_console_command.html#a5c003e975e6d63f83e5131e5b8df758f"> 13</a></span>&#160; <span class="keywordtype">string</span> <a class="code" href="class_b_m_a_console_command.html#a5c003e975e6d63f83e5131e5b8df758f">commandName</a>;</div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160;</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; <span class="keyword">virtual</span> <span class="keywordtype">int</span> <a class="code" href="class_b_m_a_console_command.html#a0822dab9f44e4a046ff7bb4ab997170e">processCommand</a>(<a class="code" href="class_b_m_a_console_session.html">BMAConsoleSession</a> *session);</div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160;</div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160;};</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_console_command_html_a318938fc38946e3f2e5d606e84784754"><div class="ttname"><a href="class_b_m_a_console_command.html#a318938fc38946e3f2e5d606e84784754">BMAConsoleCommand::BMAConsoleCommand</a></div><div class="ttdeci">BMAConsoleCommand(string commandName)</div><div class="ttdef"><b>Definition:</b> BMAConsoleCommand.cpp:4</div></div>
<div class="ttc" id="class_b_m_a_console_command_html_a909dee1bc6659e50d95fa3d43526557a"><div class="ttname"><a href="class_b_m_a_console_command.html#a909dee1bc6659e50d95fa3d43526557a">BMAConsoleCommand::~BMAConsoleCommand</a></div><div class="ttdeci">~BMAConsoleCommand()</div><div class="ttdef"><b>Definition:</b> BMAConsoleCommand.cpp:8</div></div>
<div class="ttc" id="class_b_m_a_console_command_html"><div class="ttname"><a href="class_b_m_a_console_command.html">BMAConsoleCommand</a></div><div class="ttdef"><b>Definition:</b> BMAConsoleCommand.h:7</div></div>
<div class="ttc" id="class_b_m_a_console_session_html"><div class="ttname"><a href="class_b_m_a_console_session.html">BMAConsoleSession</a></div><div class="ttdef"><b>Definition:</b> BMAConsoleSession.h:7</div></div>
<div class="ttc" id="class_b_m_a_console_command_html_a0822dab9f44e4a046ff7bb4ab997170e"><div class="ttname"><a href="class_b_m_a_console_command.html#a0822dab9f44e4a046ff7bb4ab997170e">BMAConsoleCommand::processCommand</a></div><div class="ttdeci">virtual int processCommand(BMAConsoleSession *session)</div><div class="ttdef"><b>Definition:</b> BMAConsoleCommand.cpp:12</div></div>
<div class="ttc" id="class_b_m_a_console_command_html_a5c003e975e6d63f83e5131e5b8df758f"><div class="ttname"><a href="class_b_m_a_console_command.html#a5c003e975e6d63f83e5131e5b8df758f">BMAConsoleCommand::commandName</a></div><div class="ttdeci">string commandName</div><div class="ttdef"><b>Definition:</b> BMAConsoleCommand.h:13</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,81 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.13"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>BMA Server Framework: /home/barant/Development/BMA/server_core/ServerCore/BMAConsoleServer.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
$(function() {
initMenu('',true,false,'search.php','Search');
$(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
</div><!-- top -->
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Development/BMA/server_core/ServerCore/BMAConsoleServer.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="preprocessor">#ifndef BMAConsoleServer_h__</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="preprocessor">#define BMAConsoleServer_h__</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;</div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="preprocessor">#include &quot;includes&quot;</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="preprocessor">#include &quot;BMATCPServerSocket.h&quot;</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;<span class="preprocessor">#include &quot;BMACommand.h&quot;</span></div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_t_c_p_socket.html">BMATCPSocket</a>;</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160;</div><div class="line"><a name="l00009"></a><span class="lineno"><a class="line" href="class_b_m_a_console_server.html"> 9</a></span>&#160;<span class="keyword">class </span><a class="code" href="class_b_m_a_console_server.html">BMAConsoleServer</a> : <span class="keyword">public</span> <a class="code" href="class_b_m_a_t_c_p_server_socket.html">BMATCPServerSocket</a> {</div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160; </div><div class="line"><a name="l00011"></a><span class="lineno"> 11</span>&#160; <span class="keyword">public</span>:</div><div class="line"><a name="l00012"></a><span class="lineno"> 12</span>&#160; <a class="code" href="class_b_m_a_console_server.html">BMAConsoleServer</a>(<a class="code" href="class_b_m_a_e_poll.html">BMAEPoll</a> &amp;ePoll, std::string url, <span class="keywordtype">short</span> <span class="keywordtype">int</span> port);</div><div class="line"><a name="l00013"></a><span class="lineno"> 13</span>&#160; ~<a class="code" href="class_b_m_a_console_server.html">BMAConsoleServer</a>(); </div><div class="line"><a name="l00014"></a><span class="lineno"> 14</span>&#160;</div><div class="line"><a name="l00015"></a><span class="lineno"> 15</span>&#160; <span class="keywordtype">void</span> sendToConnectedConsoles(std::string out);</div><div class="line"><a name="l00016"></a><span class="lineno"> 16</span>&#160;</div><div class="line"><a name="l00017"></a><span class="lineno"> 17</span>&#160; <a class="code" href="class_b_m_a_session.html">BMASession</a> * <a class="code" href="class_b_m_a_console_server.html#a94847bdd07f651825e5fce5237e98335">getSocketAccept</a>() <span class="keyword">override</span>;</div><div class="line"><a name="l00018"></a><span class="lineno"> 18</span>&#160;</div><div class="line"><a name="l00019"></a><span class="lineno"> 19</span>&#160; <span class="keywordtype">void</span> registerCommand(<a class="code" href="class_b_m_a_command.html">BMACommand</a> &amp;command);</div><div class="line"><a name="l00020"></a><span class="lineno"> 20</span>&#160;</div><div class="line"><a name="l00021"></a><span class="lineno"> 21</span>&#160; <span class="keywordtype">void</span> <a class="code" href="class_b_m_a_console_server.html#aa148f8b49c4460f06ca92bf05c786239">output</a>(<a class="code" href="class_b_m_a_session.html">BMASession</a> *session) <span class="keyword">override</span>; </div><div class="line"><a name="l00022"></a><span class="lineno"> 22</span>&#160;</div><div class="line"><a name="l00023"></a><span class="lineno"> 23</span>&#160; std::vector&lt;BMACommand *&gt; commands; </div><div class="line"><a name="l00024"></a><span class="lineno"> 24</span>&#160; </div><div class="line"><a name="l00025"></a><span class="lineno"> 25</span>&#160;};</div><div class="line"><a name="l00026"></a><span class="lineno"> 26</span>&#160;</div><div class="line"><a name="l00027"></a><span class="lineno"> 27</span>&#160;<span class="preprocessor">#endif</span></div><div class="ttc" id="class_b_m_a_t_c_p_socket_html"><div class="ttname"><a href="class_b_m_a_t_c_p_socket.html">BMATCPSocket</a></div><div class="ttdef"><b>Definition:</b> BMATCPSocket.h:18</div></div>
<div class="ttc" id="class_b_m_a_t_c_p_server_socket_html"><div class="ttname"><a href="class_b_m_a_t_c_p_server_socket.html">BMATCPServerSocket</a></div><div class="ttdef"><b>Definition:</b> BMATCPServerSocket.h:20</div></div>
<div class="ttc" id="class_b_m_a_e_poll_html"><div class="ttname"><a href="class_b_m_a_e_poll.html">BMAEPoll</a></div><div class="ttdef"><b>Definition:</b> BMAEPoll.h:29</div></div>
<div class="ttc" id="class_b_m_a_command_html"><div class="ttname"><a href="class_b_m_a_command.html">BMACommand</a></div><div class="ttdef"><b>Definition:</b> BMACommand.h:8</div></div>
<div class="ttc" id="class_b_m_a_console_server_html"><div class="ttname"><a href="class_b_m_a_console_server.html">BMAConsoleServer</a></div><div class="ttdef"><b>Definition:</b> BMAConsoleServer.h:9</div></div>
<div class="ttc" id="class_b_m_a_console_server_html_a94847bdd07f651825e5fce5237e98335"><div class="ttname"><a href="class_b_m_a_console_server.html#a94847bdd07f651825e5fce5237e98335">BMAConsoleServer::getSocketAccept</a></div><div class="ttdeci">BMASession * getSocketAccept() override</div><div class="ttdef"><b>Definition:</b> BMAConsoleServer.cpp:21</div></div>
<div class="ttc" id="class_b_m_a_console_server_html_aa148f8b49c4460f06ca92bf05c786239"><div class="ttname"><a href="class_b_m_a_console_server.html#aa148f8b49c4460f06ca92bf05c786239">BMAConsoleServer::output</a></div><div class="ttdeci">void output(BMASession *session) override</div><div class="ttdoc">Output the consoles array to the console. </div><div class="ttdef"><b>Definition:</b> BMAConsoleServer.cpp:29</div></div>
<div class="ttc" id="class_b_m_a_session_html"><div class="ttname"><a href="class_b_m_a_session.html">BMASession</a></div><div class="ttdef"><b>Definition:</b> BMASession.h:18</div></div>
</div><!-- fragment --></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.13
</small></address>
</body>
</html>
+114
View File
@@ -0,0 +1,114 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsoleSession.cpp File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsoleSession.cpp File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_console_session_8h_source.html">BMAConsoleSession.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsoleSession.cpp:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_session_8cpp__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8cpp" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8cpp" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8cpp">
<area shape="rect" id="node2" href="_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="77,109,235,136"/>
<area shape="rect" id="node3" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="5,557,136,584"/>
<area shape="rect" id="node8" href="_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="160,184,253,211"/>
<area shape="rect" id="node5" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="147,632,250,659"/>
<area shape="rect" id="node6" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="99,707,195,733"/>
<area shape="rect" id="node7" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="270,707,381,733"/>
<area shape="rect" id="node9" href="_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="309,259,411,285"/>
<area shape="rect" id="node11" href="_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="305,408,415,435"/>
<area shape="rect" id="node10" href="_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="319,333,401,360"/>
<area shape="rect" id="node12" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="211,483,379,509"/>
<area shape="rect" id="node13" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="261,557,432,584"/>
</map>
</div>
</div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,13 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.cpp" name="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.cpp">
<area shape="rect" id="node2" href="$_b_m_a_console_session_8h.html" title="BMAConsoleSession.h" alt="" coords="77,109,235,136"/>
<area shape="rect" id="node3" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="5,557,136,584"/>
<area shape="rect" id="node8" href="$_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="160,184,253,211"/>
<area shape="rect" id="node5" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="147,632,250,659"/>
<area shape="rect" id="node6" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="99,707,195,733"/>
<area shape="rect" id="node7" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="270,707,381,733"/>
<area shape="rect" id="node9" href="$_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="309,259,411,285"/>
<area shape="rect" id="node11" href="$_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="305,408,415,435"/>
<area shape="rect" id="node10" href="$_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="319,333,401,360"/>
<area shape="rect" id="node12" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="211,483,379,509"/>
<area shape="rect" id="node13" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="261,557,432,584"/>
</map>
@@ -0,0 +1 @@
7e205aee1976f689dd753ce77efaaa0d
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

+136
View File
@@ -0,0 +1,136 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.11"/>
<title>BMA Server Framework: /home/barant/Documents/Development/BMASockets/BMAConsoleSession.h File Reference</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
$(document).ready(function() { init_search(); });
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">BMA Server Framework
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.11 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "search",false,'Search');
</script>
<div id="navrow1" class="tabs">
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="annotated.html"><span>Classes</span></a></li>
<li class="current"><a href="files.html"><span>Files</span></a></li>
<li>
<div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</li>
</ul>
</div>
<div id="navrow2" class="tabs2">
<ul class="tablist">
<li><a href="files.html"><span>File&#160;List</span></a></li>
<li><a href="globals.html"><span>File&#160;Members</span></a></li>
</ul>
</div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
</div><!-- top -->
<div class="header">
<div class="summary">
<a href="#nested-classes">Classes</a> </div>
<div class="headertitle">
<div class="title">/home/barant/Documents/Development/BMASockets/BMAConsoleSession.h File Reference</div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><code>#include &quot;<a class="el" href="_b_m_a_t_c_p_socket_8h_source.html">BMATCPSocket.h</a>&quot;</code><br />
<code>#include &quot;<a class="el" href="_b_m_a_e_poll_8h_source.html">BMAEPoll.h</a>&quot;</code><br />
</div><div class="textblock"><div class="dynheader">
Include dependency graph for BMAConsoleSession.h:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_session_8h__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8h" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8h" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8h">
<area shape="rect" id="node2" href="_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="5,483,136,509"/>
<area shape="rect" id="node7" href="_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="160,109,253,136"/>
<area shape="rect" id="node4" href="_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="147,557,250,584"/>
<area shape="rect" id="node5" href="_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="99,632,195,659"/>
<area shape="rect" id="node6" href="_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="270,632,381,659"/>
<area shape="rect" id="node8" href="_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="309,184,411,211"/>
<area shape="rect" id="node10" href="_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="305,333,415,360"/>
<area shape="rect" id="node9" href="_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="319,259,401,285"/>
<area shape="rect" id="node11" href="_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="211,408,379,435"/>
<area shape="rect" id="node12" href="_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="261,483,432,509"/>
</map>
</div>
</div><div class="textblock"><div class="dynheader">
This graph shows which files directly or indirectly include this file:</div>
<div class="dyncontent">
<div class="center"><img src="_b_m_a_console_session_8h__dep__incl.png" border="0" usemap="#_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8hdep" alt=""/></div>
<map name="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8hdep" id="_2home_2barant_2_documents_2_development_2_b_m_a_sockets_2_b_m_a_console_session_8hdep">
<area shape="rect" id="node2" href="_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="5,109,189,165"/>
<area shape="rect" id="node3" href="_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="214,109,402,165"/>
<area shape="rect" id="node4" href="_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="427,109,611,165"/>
<area shape="rect" id="node5" href="_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="635,109,819,165"/>
<area shape="rect" id="node6" href="_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="843,109,1029,165"/>
</map>
</div>
</div>
<p><a href="_b_m_a_console_session_8h_source.html">Go to the source code of this file.</a></p>
<table class="memberdecls">
<tr class="heading"><td colspan="2"><h2 class="groupheader"><a name="nested-classes"></a>
Classes</h2></td></tr>
<tr class="memitem:"><td class="memItemLeft" align="right" valign="top">class &#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="class_b_m_a_console_session.html">BMAConsoleSession</a></td></tr>
<tr class="separator:"><td class="memSeparator" colspan="2">&#160;</td></tr>
</table>
</div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.11
</small></address>
</body>
</html>
@@ -0,0 +1,7 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.h" name="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.h">
<area shape="rect" id="node2" href="$_b_m_a_console_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsole.cpp" alt="" coords="5,109,189,165"/>
<area shape="rect" id="node3" href="$_b_m_a_console_command_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleCommand.cpp" alt="" coords="214,109,402,165"/>
<area shape="rect" id="node4" href="$_b_m_a_console_session_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAConsoleSession.cpp" alt="" coords="427,109,611,165"/>
<area shape="rect" id="node5" href="$_b_m_a_e_poll_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMAEPoll.cpp" alt="" coords="635,109,819,165"/>
<area shape="rect" id="node6" href="$_b_m_a_t_c_p_server_socket_8cpp.html" title="/home/barant/Documents\l/Development/BMASockets\l/BMATCPServerSocket.cpp" alt="" coords="843,109,1029,165"/>
</map>
@@ -0,0 +1 @@
4d30791f3f0568d6ec478ef422c955f0
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@@ -0,0 +1,12 @@
<map id="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.h" name="/home/barant/Documents/Development/BMASockets/BMAConsoleSession.h">
<area shape="rect" id="node2" href="$_b_m_a_t_c_p_socket_8h.html" title="BMATCPSocket.h" alt="" coords="5,483,136,509"/>
<area shape="rect" id="node7" href="$_b_m_a_e_poll_8h.html" title="BMAEPoll.h" alt="" coords="160,109,253,136"/>
<area shape="rect" id="node4" href="$_b_m_a_socket_8h.html" title="BMASocket.h" alt="" coords="147,557,250,584"/>
<area shape="rect" id="node5" href="$_b_m_a_event_8h.html" title="BMAEvent.h" alt="" coords="99,632,195,659"/>
<area shape="rect" id="node6" href="$_b_m_a_property_8h.html" title="BMAProperty.h" alt="" coords="270,632,381,659"/>
<area shape="rect" id="node8" href="$_b_m_a_thread_8h.html" title="BMAThread.h" alt="" coords="309,184,411,211"/>
<area shape="rect" id="node10" href="$_b_m_a_console_8h.html" title="BMAConsole.h" alt="" coords="305,333,415,360"/>
<area shape="rect" id="node9" href="$_b_m_a_log_8h.html" title="BMALog.h" alt="" coords="319,259,401,285"/>
<area shape="rect" id="node11" href="$_b_m_a_t_c_p_server_socket_8h.html" title="BMATCPServerSocket.h" alt="" coords="211,408,379,435"/>
<area shape="rect" id="node12" href="$_b_m_a_console_command_8h.html" title="BMAConsoleCommand.h" alt="" coords="261,483,432,509"/>
</map>
@@ -0,0 +1 @@
2b0ab5d848b25a9b41218328d7285dff
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Loaded 100 of 2863 files, more files were not shown because too many files have changed in this diff. Show more