// // File: hzIpServer.h // // Legal Notice: This file is part of the HadronZoo C++ Class Library. // // Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
#ifndef hzIpServer_h #define hzIpServer_h
#include <unistd.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/socket.h> #include <openssl/ssl.h> #include <sys/epoll.h>
#include "hzTmplList.h" #include "hzTmplVect.h" #include "hzTmplMapS.h" #include "hzChain.h" #include "hzIpaddr.h"
/* ** Definitions */
enum hzSvrStatus { // Category: Internet // // Server status
SERVER_OFFLINE, // Server offline or otherwise not accepting connections SERVER_ONLINE, // Server operating normally SERVER_SHUTDOWN // Server continues to operate but won't accept new connections } ;
enum hzCliStatus { // Category: Internet // // Client status
CLIENT_STATE_NONE = 0, // Client is created in this state CLIENT_INITIALIZED = 0x0001, // Client accepted but no data in as yet CLIENT_HELLO = 0x0002, // Client has been sent a hello CLIENT_READING = 0x0004, // Client can read CLIENT_READ_WHOLE = 0x0008, // A whole message has been read (may be more to come) CLIENT_HANGUP = 0x0010, // An epoll HANGUP condition has been detected CLIENT_TERMINATION = 0x0020, // The client has sent a 0-byte formal termination packet CLIENT_WRITING = 0x0040, // A response has been formulated and is in the process of being sent to the client CLIENT_WRITE_WHOLE = 0x0080, // The response has been written to the clinet CLIENT_BAD = 0x0100, // Server has deemed the client to be bad and will not send a response CLIENT_SSL_ACCEPTED = 0x0200 // Client has an initialized and accepted SSL session } ;
enum hzTcpCode { // Category: Internet // // TCP session return codes to direct if session is to be terminated or kept alive
TCP_TERMINATE, // Terminate connection TCP_KEEPALIVE, // Keep connection alive for next transmission even if current transmission has completed TCP_INCOMPLETE, // Keep connection alive as transmission is incomplete TCP_INVALID // Message processing failed for syntax/protocol reasons. Connection to terminate } ;
#define HZMAX_SERVNAMLEN 64 // Max domain name? #define HZMAX_IPADDRLEN 16 // IPV-4 Address length
/* ** The ListeningSocket class */
#define HZ_LISTEN_SECURE 0x01 // Connections under the listening socket must use SSL #define HZ_LISTEN_INTERNET 0x02 // Connections under the listening socket must use AF_INET (access from outside) else use AF_UNIX (local clients only) #define HZ_LISTEN_TCP 0x04 // Connections under the listening socket must use TCP #define HZ_LISTEN_HTTP 0x08 // Connections under the listening socket must use HTTP #define HZ_LISTEN_UDP 0x10 // Connections under the listening socket must use UDP #define HZ_LISTEN_SESSION 0x20 // Connections under the listening socket must use separate thread function
class hzIpConnex ; class hzIpServer ; class hzHttpEvent ;
class hzIpListen { // Category: Internet // // hzIpListen is a hzIpServer support class. It holds a listening socket and port, various control parameters and pointers to the OnIngress(), OnConnect() and OnDisconn() // functions used to handle client connections.
hzIpServer* m_pServer ; // Applicable hzIpServer instance hzLogger* m_pLog ; // Log channel
SOCKADDRIN m_Address ; // IP Addres of socket uint32_t m_nSocket ; // The actual listening socket uint32_t m_nTimeout ; // Timeout appllied to all connections to this port uint16_t m_nPort ; // Port number server listens on uint16_t m_nMaxConnections ; // Max number of simultaneous TCP connections on this port uint16_t m_nCurConnections ; // Current number of simultaneous TCP connections on this port uint16_t m_bOpflags ; // Operational flags (HZ_LISTEN_SECURE | HZ_LISTEN_INTERNET | HZ_LISTEN_UDP)
public: hzTcpCode (*m_OnIngress)(hzChain& Input, hzIpConnex* pCx) ; // To be called as soon as input exists. hzTcpCode (*m_OnConnect)(hzIpConnex* pCx) ; // Only for protocols in which the server sends the first message (server hello). hzTcpCode (*m_OnDisconn)(hzIpConnex* pCx) ; // Only provided if extra tidying up is needed on dissconnection. void* m_appFn ; // Cast to the specific callback function (eg HTTP)
hzIpListen (hzLogger* plog) { m_nPort = 0 ; m_nSocket = 0 ; m_nTimeout = 0 ; m_nMaxConnections = 0 ; m_nCurConnections = 0 ; m_OnIngress = 0 ; m_OnConnect = 0 ; m_appFn = 0 ; m_pLog = plog ; m_bOpflags = 0 ; }
~hzIpListen (void) { }
// Initialization
hzEcode Init ( hzIpServer* pServer, // Applicable hzIpServer instance hzTcpCode (*OnIngress)(hzChain&, hzIpConnex*), // App's packet handler function hzTcpCode (*OnConnect)(hzIpConnex*), // App's server hello func (if used) hzTcpCode (*OnDisconn)(hzIpConnex*), // App's server hello func (if used) uint32_t nTimeout, // Timeout applied to all connections on port uint32_t nPort, // Port application will listen on uint32_t nMaxClients, // Max number of simultaneous clients uint32_t bOpflags // Operational flags (HZ_LISTEN_SECURE | HZ_LISTEN_INTERNET | HZ_LISTEN_UDP) ) ;
hzEcode Activate (void) ;
// Get functions hzIpServer* GetServer (void) const { return m_pServer ; } hzLogger* GetLogger (void) const { return m_pLog ; } uint32_t GetPort (void) const { return m_nPort ; } uint32_t GetSocket (void) const { return m_nSocket ; } uint32_t GetTimeout (void) const { return m_nTimeout ; } uint32_t GetMaxConnections (void) const { return m_nMaxConnections ; } uint32_t GetCurConnections (void) const { return m_nCurConnections ; } bool UseUDP (void) const { return m_bOpflags & HZ_LISTEN_UDP ? true : false ; } bool UseSSL (void) const { return m_bOpflags & HZ_LISTEN_SECURE ? true : false ; } uint16_t Opflags (void) const { return m_bOpflags ; } } ;
/* ** Connection Info class */
class hzProcInfo { // Category: System // // The hzProcInfo or 'process data' class is used to pass process information in cases where client connections are handled in a separate thread. The thread handler function // must nessesarily have a single void* argument. In order to supply the client IP address, client socket, and any SSL structure to the thread function, this info is wrapped // in a hzProcInfo instance and a pointer to this is passed instead. SSL* m_pSSL ; // SSL session info (if applicable) hzIpaddr m_Ipa ; // IP address uint32_t m_Socket ; // The client socket
public: hzProcInfo (void) { m_pSSL = 0 ; m_Socket = 0 ; }
hzProcInfo (SSL* pSSL, uint32_t nSock, hzIpaddr ipa) { m_pSSL = pSSL ; m_Socket = nSock ; m_Ipa = ipa ; }
~hzProcInfo (void) { if (m_pSSL) SSL_free(m_pSSL) ; close(m_Socket) ; }
void SetParams (SSL* pSSL, uint32_t nSock, hzIpaddr ipa) { m_pSSL = pSSL ; m_Socket = nSock ; m_Ipa = ipa ; }
hzIpaddr& Ipaddr (void) { return m_Ipa ; } uint32_t Socket (void) { return m_Socket ; } } ;
class hzIpConnInfo { // Category: Internet // // The hzIpConnInfo class is the pure virtual base class for any form of session class. The session classes are application specific devices that maintain state.
public: virtual ~hzIpConnInfo (void) {} } ;
/* ** Conections (Inbound and outbound) */
class hzPktQue { // Category: Internet // // hzPktQue is a hzIpConnex support class, which queues instances of hzPacket for outgoing responses. hzPktQue is conceptually similar to hzChain, except that the unit of data // is a packet, rather than a byte. While both hzPktQue and hzChain are always appended and read from the begining, with hzPktQue, all packets may be partially filled and the // write and iteration processes are assumed to be ongoing, with packets being removed from the start of the chain after they have been processed.
public: hzPacket* m_pStart ; // The first IP packet in the que hzPacket* m_pFinal ; // The last IP packet in the que uint32_t m_nSize ; // Current size in bytes uint32_t m_nSeq ; // Packet sequence
hzPktQue (void) { m_pStart = m_pFinal = 0 ; m_nSize = m_nSeq = 0 ; }
uint32_t Size (void) const { return m_nSize ; } hzPacket* Peek (void) const { return m_pStart ; }
hzEcode Push (hzPacket& pkt) ; hzEcode Push (const hzChain& Z) ;
void Pull (void) ; void Clear (void) ; } ;
class hzIpConnex { // Category: Internet // // Generic client connection
hzChain m_Input ; // Incomming message chain hzChain::Iter m_MsgStart ; // For iteration of pipelined requests hzLogger* m_pLog ; // Log channel hzIpConnInfo* m_pInfo ; // Connection specific information hzPktQue m_Outgoing ; // Outgoing message stream
uint64_t m_ConnExpires ; // Nanosecond Epoch expiry uint64_t m_nsAccepted ; // Nanosecond Epoch connection accepted uint64_t m_nsRecvBeg ; // Nanosecond Epoch first packet of message uint64_t m_nsRecvEnd ; // Nanosecond Epoch request considered complete uint64_t m_nsSendBeg ; // Nanosecond Epoch response transmission began uint64_t m_nsSendEnd ; // Nanosecond Epoch response transmission ended SOCKADDRIN m_CliAddr ; // IP Addres of client socket socklen_t m_nCliLen ; // Length of socket address hzIpaddr m_ClientIP ; // IP address of client uint32_t m_nSock ; // Client socket uint32_t m_nMsgno ; // Event/Message number uint32_t m_nGlitch ; // Extent of incomplete write uint32_t m_nStart ; // Start position of current incomming message within chain uint32_t m_nTotalIn ; // Total size of outgoing response uint32_t m_nTotalOut ; // Total size of outgoing response uint32_t m_nExpected ; // Expected size of incomming request uint32_t m_bState ; // Client state uint16_t m_nPort ; // Incomimg port uint16_t m_bListen ; // Operational flags from listening socket (HZ_LISTEN_SECURE | HZ_LISTEN_INTERNET | HZ_LISTEN_UDP) bool m_bInitSSL ; // If there is an SSL conection, has it been accepted and set up?
public: // hzChain m_Track ; // Used to report on progress during the processing of a client request. Committed to logfile on completion or when connection terminated. void* m_appFn ; // Application message event handler void* m_pEventHdl ; // HTTP Event instance SSL* m_pSSL ; // SSL session info hzIpConnex* m_pProxy ; // Proxy connection bool m_bAcceptSSL ; // SSL has been accepted
hzTcpCode (*m_OnIngress)(hzChain& Input, hzIpConnex* pCx) ; // Required: Function to handle messages comming in on the specific port. hzTcpCode (*m_OnConnect)(hzIpConnex* pCx) ; // Optional: Called on connection e.g. Server hello. hzTcpCode (*m_OnDisconn)(hzIpConnex* pCx) ; // Optional: Called on disconnection for any tidying up.
// Constructor and destructor hzIpConnex (hzLogger* pLog) ; ~hzIpConnex (void) ;
// Init functions hzEcode Initialize (hzIpListen* pLS, SSL* pSSL, hzIpaddr ipa, uint32_t cliSock, uint32_t cliPort, uint32_t eventNo) ;
void SetSocket (uint32_t nSock) { m_nSock = nSock ; }
void Oxygen (void) { m_ConnExpires = RealtimeNano() ; m_ConnExpires += 25000000000 ; } // Adds 25 seconds to the time to live void Hypoxia (void) { m_ConnExpires = 0 ; } // Expires the connection
void Terminate (void) ; // Terminate connection
// Set functions void SetInfo (hzIpConnInfo* pInfo) { m_pInfo = pInfo ; }
// Get functions hzIpConnInfo* GetInfo (void) const { return m_pInfo ; }
hzLogger* GetLogger (void) const { return m_pLog ; } hzChain& InputZone (void) { return m_Input ; } hzIpaddr ClientIP (void) const { return m_ClientIP ; } uint64_t Expires (void) const { return m_ConnExpires ; } uint64_t TimeAcpt (void) const { return m_nsRecvBeg - m_nsAccepted ; } uint64_t TimeRecv (void) const { return m_nsRecvEnd - m_nsRecvBeg ; } uint64_t TimeProc (void) const { return m_nsSendBeg - m_nsRecvEnd ; } uint64_t TimeXmit (void) const { return m_nsSendEnd - m_nsSendBeg ; } uint32_t EventNo (void) const { return m_nMsgno ; } uint32_t CliSocket (void) const { return m_nSock ; } uint32_t CliPort (void) const { return m_nPort ; } uint32_t SizeIn (void) const { return m_Input.Size() - m_nStart ; } uint32_t TotalIn (void) const { return m_nTotalIn ; } uint32_t TotalOut (void) const { return m_nTotalOut ; } bool IsVirgin (void) const { return m_bState == CLIENT_INITIALIZED ; } bool IsCliTerm (void) const { return m_bState & CLIENT_TERMINATION ; } bool IsCliBad (void) const { return m_bState & CLIENT_BAD ; } bool _isxmit (void) const { return m_Outgoing.m_pStart ? true : false ; }
// Operational functions int32_t Recv (hzPacket& Buf) ; hzEcode SendData (const hzChain& Hdr, const hzChain& Body) ; hzEcode SendData (const hzChain& Z) ; void SendKill (void) ; int32_t _xmit (hzPacket& buf) ;
// Message size expectations void ExpectSize (uint32_t nBytes) { m_nExpected = nBytes ; } uint32_t ExpectSize (void) { return m_nExpected ; } bool MsgComplete (void) { return m_nExpected && m_Input.Size() >= (m_nExpected + m_nStart) ? true : false ; }
bool MsgReady (void) { // If a message has an expected size this condition will only be true if the input so far has reached or exceeded this size. In the common // scenario where an incoming message has a header stating the length, the expected size starts at zero and this function returns true. The // input is then provisionally processed and this will establish the expected size. This function will then not return true until the whole // message in in.
if (!m_nExpected) return true ;
if (m_Input.Size() >= (m_nExpected + m_nStart)) return true ; return false ; } } ;
/* ** The SERVER itself */
#define MAXEVENTS 100
class hzIpServer { // Category: Internet // // General purpose server class.
hzList<hzIpListen*> m_LS ; // Listening sockets //hzVect<hzIpConnex*> m_Inbound ; // Currently connected clients
hzMapS<hzIpaddr,hzIpConnex*> udpClients ; // Map of UDP clients by IP address hzMapS<uint32_t,hzIpConnex*> ConnInError ; // Map of TCP clients in error hzMapS<uint32_t,hzIpListen*> m_mapLS ; // Listening sockets map
struct epoll_event m_arEvents[MAXEVENTS] ; // Epoll event array
hzLogger* m_pLog ; // Log channel to use for events hzLogger* m_pStats ; // Log channel to use for stats socklen_t m_nCliLen ; // Length of client uint32_t m_nMaxClients ; // Maximum number of simultaneous connections. uint32_t m_nCurrentClients ; // Current number of connected clients. uint32_t m_nTimeout ; // Timeout for select uint32_t m_eError ; // Error code uint32_t m_nMaxSocket ; // Highest socket for the select function uint32_t m_nLoop ; // Number of time round epoll loop (run time total) bool m_bActive ; // Socket to start listening bool m_bShutdown ; // Socket to stop listening
/* ** Private constructor for singleton method */
hzIpServer (void) { m_pLog = 0 ; m_nMaxClients = 0 ; m_nCurrentClients = 0 ; m_bActive = false ; m_bShutdown = false ; m_nMaxSocket = 0 ; m_nTimeout = 30 ; }
hzEcode _nonblock (uint32_t nSock) ;
public: int32_t s_epollSocket ; // The epoll 'master' socket
static hzIpServer* GetInstance (hzLogger* pLogger) ;
~hzIpServer (void) {}
void SetTimeout (uint32_t Timeout) { m_nTimeout = Timeout ; } void SetLogger (hzLogger* pLog) { m_pLog = pLog ; } void SetStats (hzLogger* pStats) { m_pStats = pStats ; }
// Adds a TCP listening socket for invoking a user defined function that handles general client connections hzEcode AddPortTCP ( hzTcpCode (*OnIngress)(hzChain&, hzIpConnex*), hzTcpCode (*OnConnect)(hzIpConnex*), hzTcpCode (*OnDisconn)(hzIpConnex*), uint32_t nTimeout, uint32_t nPort, uint32_t nMaxClients, bool bSecure = false ) ;
// Adds a listening socket for HTTP connections. HTTP is a special case in which applications specify an OnHtpRq function to process complete HTTP requests // rather than an OnIngress function. The OnIngress function is in-built and extracts HTTP requests from the incomming data. hzEcode AddPortHTTP ( hzTcpCode (*OnHttpReq)(hzHttpEvent*), uint32_t nTimeout, uint32_t nPort, uint32_t nMaxClients, bool bSecure = false ) ;
// Adds a UDP socket. Note there cannot be an OnConnect function in the normal sense as UDP has no connections. However applications will need to ... hzEcode AddPortUDP ( hzTcpCode (*OnIngress)(hzChain&, hzIpConnex*), hzTcpCode (*OnConnect)(hzIpConnex*), hzTcpCode (*OnDisconn)(hzIpConnex*), uint32_t nTimeout, uint32_t nPort, uint32_t nMaxClients, bool bSecure = false ) ;
// followed by a single call to Activate sets all ports to listen hzEcode Activate (void) ;
// Proxy connections hzEcode ProxyTo (hzIpConnex* pConn, uint32_t nPort) ;
// Serve (standard mode) void Serve (void) ;
// If you need a multi-threaded server, you must use thread specialization and the ServeX() function. This listens, accepts new client connections and reads incoming requests, // but does not call a request handler function. Instead client requests are placed in a lock free queue from where they are drawn by ServeRequests() which must be called from // a separate thread (created by means of a thread entry function and a pthread_create() call). You may set up several such threads as required. Then in yet another thread, // ServeResponses() is called. This is nessesary as ServeX(), unlike Serve(), does not write to client sockets. Instead ServeReponses() does this job by drawing the responses // from a queue fed by ServeRequests calls to the Handler passed in AddPort(). void ServeRequests (void) ; void ServeResponses (void) ; void ServeX (void) ;
void Halt (void) { m_bShutdown = true ; } } ;
/* ** Prototypes */
hzEcode SetupHost (void) ; hzEcode InitDomainSSL (const char* pvtKey, const char* sslCert, const char* sslCA, const char* domain) ; hzEcode InitServerSSL (const char* pvtKey, const char* sslCert, const char* sslCA) ;
/* ** Globals */
extern hzMapS <hzIpaddr,hzIpinfo> _hzGlobal_StatusIP ; // Black and white listed IP addresses
extern hzString _hzGlobal_Hostname ; // String form of actual hostname of this server extern hzString _hzGlobal_HostIP ; // String form of assigned IP address of this server extern hzIpaddr _hzGlobal_localhost ; // Always the default IP address 127.0.0.1 extern hzIpaddr _hzGlobal_livehost ; // Assigned IP address of this server
#endif // hzIpServer_h