

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file NetworkConnections.h
	@brief Raw socket transport layer: TCP/UDP/SSL connections and TCP listeners with buffered, thread-safe I/O.

	This header defines the lowest layer of the CMSDK networking stack: the
	classes that own actual OS sockets. Above it sit NetworkProtocols.h
	(wire formats: HTTP, WebSocket, Telnet, binary messages) and
	NetworkManager.h (channel/connection lifecycle management and dispatch).

	Class roles:
	- TCPListener: binds and listens on a TCP port, accepting inbound
	  connections (plain or SSL, chosen by the encryption argument).
	- NetworkConnection: abstract base for all point-to-point connections;
	  provides the shared receive buffer, byte counters, speed statistics,
	  timeout-aware receive helpers and the reconnect contract.
	- TCPConnection / UDPConnection / SSLConnection: concrete transports.
	  SSLConnection adds certificate handling and peer/hostname verification
	  policy (compiled in only when @c _USE_SSL_ is defined).

	@par Platform notes
	Sockets differ between Windows and POSIX: on Windows the Winsock2 API is
	used (requiring one-time WSAStartup initialisation, performed via the
	CHECKNETWORKINIT macro in constructors) and sockets are of type SOCKET;
	on Linux/macOS BSD sockets (plain int file descriptors) are used and
	@c arpa/inet.h supplies htonl()/ntohl() byte-order conversion. All
	multi-byte values sent on the wire are converted to network byte order
	(big-endian) with htonl()/htons() so that mixed-endian hosts interoperate.
	The implementation (@ref NetworkConnections.cpp) isolates these
	differences behind @c \#ifdef WINDOWS / @c WIN32 / @c _WIN64 branches.

	@par Addressing
	Throughout the stack, a remote endpoint is packed into a single uint64
	"location": the IPv4 address in the upper bits and the port in the lower
	bits. This lets a full endpoint be passed, stored and compared as one
	integer (see getRemoteAddress(), getLocalAddress()).

	@par Thread-safety
	Each connection guards its receive buffer with an internal mutex and
	serialises writers with a separate send mutex, so one reader thread and
	multiple writer threads may safely share a connection. Blocking calls
	(receive(), waitForDataToRead(), acceptConnection()) take explicit
	timeouts in milliseconds.
*/

/*! @defgroup Networking Networking
	@brief Socket transports, wire protocols, and the NetworkManager
	connection/channel layer (including the built-in HTTP/WebSocket server).

@par Why CMSDK ships its own networking layer
Writing directly against the operating system's socket API means maintaining
two code paths (Winsock2 on Windows, BSD sockets on Linux/macOS), hand-rolling
byte-order conversion, framing, buffering, timeouts, reconnection and
threading — for every application. The CMSDK Networking subsystem packages all
of that once, portably, and integrates it with the rest of the SDK:

- @b One @b API @b across @b platforms: all Winsock-vs-POSIX differences
  (WSAStartup, SOCKET vs int descriptors, error codes, non-blocking connect
  semantics) are hidden below NetworkConnection and TCPListener; user code is
  identical on Windows, Linux and macOS.
- @b Consistent @b framing @b and @b byte @b order: HTTP, WebSocket (RFC 6455)
  and Telnet framing are implemented in NetworkProtocols.h; multi-byte header
  fields go over the wire in network (big-endian) byte order via
  htons()/htonl()/ntoh64().
- @b Native @b DataMessage @b transport: cmlabs::DataMessage objects — the
  SDK's universal typed container — can be sent as-is over TCP or UDP
  (::PROTOCOL_MESSAGE), which is what the higher-level request fabric
  (RequestGateway/RequestPort) is built on.
- @b Built-in @b threading: every listener and connection gets its own worker
  thread; parsed traffic is either pushed to a cmlabs::NetworkReceiver
  callback (async) or queued for blocking waitFor*() calls (sync). No
  select()/poll() loops to write.
- @b Extras @b for @b free: protocol auto-detection on a single port,
  automatic reconnection, throughput statistics, SSL/TLS, and a complete
  embedded HTTP/WebSocket server.

@par Architecture: three layers, two key classes
The subsystem is stacked in three headers:
- NetworkConnections.h — raw transports: TCPListener, cmlabs::NetworkConnection
  and its concrete subclasses TCPConnection, UDPConnection, SSLConnection.
  These own actual sockets and move bytes.
- NetworkProtocols.h — wire formats: HTTPRequest / HTTPReply, WebsocketData,
  TelnetLine, JSONM, plus the static protocol handlers (HTTPProtocol,
  MessageProtocol, TelnetProtocol) and per-protocol
  CheckBufferForCompatibility() sniffers used for auto-detection.
- NetworkManager.h — lifecycle and dispatch: cmlabs::NetworkManager and
  cmlabs::NetworkChannel.

The two classes you normally touch:
- cmlabs::NetworkManager is the single top-level owner in a process. Its
  create*() factories open listeners (createListener()) and outgoing
  TCP/UDP/WebSocket connections; it stores the SSL certificate for encrypted
  listeners (setSSLCertificate()), runs a supervision thread that completes
  delayed connects and performs auto-reconnection, offers a blocking HTTP(S)
  client (makeHTTPRequest()), and hosts the built-in HTTP/WebSocket server.
  It owns every channel and connection it creates.
- cmlabs::NetworkChannel groups related listeners/connections into one
  logical interface (e.g. "the web UI port" or "the gateway link") and is
  what the factories return. Each listener/connection under a channel runs
  on its own worker thread; the channel funnels parsed traffic to your
  NetworkReceiver (async mode) or into typed queues read with
  waitForMessage(), waitForHTTPRequest(), waitForWebsocketData(), etc.
  (sync mode). Sends are addressed by connection id
  (sendMessage(), sendHTTPReply(), sendWebsocketData(), ...).
  A single peer link is identified by its uint64 connection id; the packed
  uint64 "location" (IPv4 + port in one integer) identifies the endpoint.

@par Mode 1: binary DataMessage over TCP (the native mode)
The workhorse of CMSDK inter-process traffic: size-prefixed serialised
DataMessage frames (::PROTOCOL_MESSAGE). Synchronous server and client:
@code
// Server: listen on a caller-chosen port, sync (queued) mode
NetworkManager net;
NetworkChannel* srv = net.createListener(9000, NOENC, PROTOCOL_MESSAGE,
                                         false, 3000, true, 0, NULL);
uint64 conid;
DataMessage* msg = srv->waitForMessage(conid, 5000);   // caller owns msg
if (msg) {
    DataMessage* reply = new DataMessage();
    reply->setString("Result", "ok");
    srv->sendMessage(reply, conid);    // send does NOT take ownership
    delete reply;
    delete msg;
}

// Client: connect and exchange
NetworkManager cli;
uint64 cid, loc;
NetworkChannel* ch = cli.createTCPConnection("127.0.0.1", 9000, NOENC,
        PROTOCOL_MESSAGE, false, true, 0, NULL, cid, loc); // isAsync=false, autoreconnect=true
DataMessage* out = new DataMessage();
out->setString("URI", "ping");
ch->sendMessage(out, cid);
delete out;
DataMessage* in = ch->waitForMessage(cid, 3000);       // caller owns in
delete in;
@endcode
For push (async) delivery instead, pass @c isAsync=true and a
NetworkReceiver whose receiveMessage() override handles each message on the
connection's thread.

@par Mode 2: UDP datagrams
Connectionless DataMessage delivery — discovery, telemetry, best-effort
notifications:
@code
NetworkManager net;
uint64 conid;
net.createUDPConnection(9001, PROTOCOL_MESSAGE, false, true, 0, NULL, conid);
// ... elsewhere, fire a datagram at a packed uint64 endpoint:
DataMessage* m = new DataMessage();
m->setString("Event", "heartbeat");
net.sendUDPMessage(m, destination);   // delivery not guaranteed (UDP)
delete m;
@endcode

@par Mode 3: raw byte streams
When no framing is wanted at all, use the transport layer directly:
TCPConnection / TCPListener give you buffered, timeout-aware, thread-safe
byte I/O without any protocol on top:
@code
TCPConnection con;
uint64 location;
if (con.connect("192.168.0.10", 7000, location, 3000)) {
    con.send("HELLO\n", 6);
    char buf[256]; uint32 got = 0;
    if (con.receiveAvailable(buf, got, sizeof(buf), 2000))
        ; // got bytes in buf
    con.disconnect();
}
@endcode
Pass a NetworkDataReceiver to connect() to have bytes pushed to you from a
reader thread instead (push mode).

@par Mode 4: Telnet (line-based text)
Interactive consoles and diagnostics speak ::PROTOCOL_TELNET — plain text
lines (TelnetLine) with CRLF/LF handling:
@code
NetworkManager net;
NetworkChannel* tn = net.createListener(2323, NOENC, PROTOCOL_TELNET,
                                        false, 3000, true, 0, NULL);
uint64 conid;
TelnetLine* line = tn->waitForTelnetLine(conid, 10000);  // caller owns line
if (line) {
    TelnetLine* out = new TelnetLine(0);
    out->setLine("OK", true);
    tn->sendTelnetLine(out, conid);
    delete out;
    delete line;
}
@endcode

@par Mode 5: the built-in HTTP server
Any port becomes a web server with ::PROTOCOL_HTTP_SERVER — this is exactly
how the PsyProbe monitoring UI is served. Async style with a receiver:
@code
class MyServer : public NetworkReceiver {
    bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel,
                            uint64 conid) {
        HTTPReply* reply = new HTTPReply();
        reply->createPage(HTTP_OK, GetTimeNow(), "MyServer", 0,
                          req->keepAlive, false, "text/html",
                          "<html><body>Hello</body></html>");
        channel->sendHTTPReply(reply, conid);
        delete reply;
        delete req;      // we took ownership by returning true
        return true;
    }
};
MyServer handler;
NetworkManager net;
net.createListener(8080, NOENC, PROTOCOL_HTTP_SERVER,
                   true, 3000, true, 0, &handler);  // isAsync=true -> push mode
@endcode
Sync style: omit the receiver (@c isAsync=false) and loop on
NetworkChannel::waitForHTTPRequest() + sendHTTPReply(). HTTPReply also
serves files from disk (createFromFile(), with If-Modified-Since/304
support), canned error pages (createErrorPage()), Basic-auth challenges and
CORS preflight responses.

@par Mode 6: HTTP client
One blocking call, URL in, reply out (https:// switches to SSL
automatically; credentials may be embedded in the URL):
@code
NetworkManager net;
HTTPReply* reply = net.makeHTTPRequest("http://example.com/index.html", 5000);
if (reply) {
    uint32 size;
    const char* body = reply->getContent(size);
    delete reply;    // caller owns the reply
}
@endcode
Other overloads send pre-built HTTPRequest objects, custom headers, raw
typed bodies, or multipart/form-data uploads (HTTPPostEntry).

@par Mode 7: SSL/TLS
Any TCP-based mode above can be encrypted by passing ::SSLENC instead of
::NOENC (build with @c _USE_SSL_ / OpenSSL). Servers need a certificate
first; client-side peer verification policy is configurable per manager,
per channel or process-wide:
@code
NetworkManager net;
net.setSSLCertificate("server.pem", "server.key");     // before SSLENC listeners
net.createListener(8443, SSLENC, PROTOCOL_HTTP_SERVER,
                   true, 3000, true, 0, &handler);      // HTTPS server

net.setSSLAllowSelfSigned(true);                        // testing only!
HTTPReply* r = net.makeHTTPRequest("https://localhost:8443/", 5000);
@endcode
@warning Allowing self-signed certificates disables peer verification for
those connections — use only for testing or closed networks. Custom CA
files/paths can be set with setSSLCALocation().

@par Mode 8: WebSockets
The HTTP server upgrades to WebSocket (RFC 6455) on the same port: detect
the handshake with HTTPRequest::isWebsocketUpgrade(), answer with
HTTPReply::CreateWebsocketHTTPReply(), then exchange WebsocketData frames.
@code
class WSServer : public NetworkReceiver {
    bool receiveHTTPRequest(HTTPRequest* req, NetworkChannel* channel,
                            uint64 conid) {
        if (req->isWebsocketUpgrade()) {
            HTTPReply* up = HTTPReply::CreateWebsocketHTTPReply(
                req->getHeaderEntry("Sec-WebSocket-Key"),
                req->getHeaderEntry("Sec-WebSocket-Version"));
            channel->sendHTTPReply(up, conid);
            delete up;
        }
        delete req;
        return true;
    }
    bool receiveWebsocketData(WebsocketData* wsData, NetworkChannel* channel,
                              uint64 conid) {
        uint64 size;
        const char* payload = wsData->getContent(size);   // echo it back:
        WebsocketData* out = new WebsocketData();
        // server-to-client frames are sent unmasked (maskData=false)
        out->setData(WebsocketData::TEXT, false, payload, size);
        channel->sendWebsocketData(out, conid);
        delete out;
        delete wsData;
        return true;
    }
};
WSServer ws;
NetworkManager net;
net.createListener(8080, NOENC, PROTOCOL_HTTP_SERVER, true, 3000, true, 0, &ws);

// Client side — one call does connect + upgrade handshake:
uint64 conid;
NetworkChannel* ch = net.createWebsocketConnection("ws://localhost:8080/live",
                                                   0, NULL, conid);
WebsocketData* frame = new WebsocketData();
frame->setData(WebsocketData::TEXT, true, "hi", 2); // client frames: masked
ch->sendWebsocketData(frame, conid);
delete frame;
WebsocketData* answer = ch->waitForWebsocketData(conid, 3000);  // caller owns
delete answer;
@endcode

@par Multiple protocols on one port
PROTOCOL_* values are bit flags: OR them in createListener() and each
incoming connection's first bytes are sniffed
(NetworkChannel::autoDetectConnection() via the per-protocol
CheckBufferForCompatibility() tests) to pick the right handler thread:
@code
// One port serving web pages, binary messages AND a telnet console:
net.createListener(9000, NOENC,
                   PROTOCOL_HTTP_SERVER | PROTOCOL_MESSAGE | PROTOCOL_TELNET,
                   true, 3000, true, 0, &handler);
@endcode

@par Threading, ownership and lifecycle rules
@note Async (@c isAsync=true) callbacks arrive on each connection's own
protocol thread — receivers must be thread-safe. Objects handed to
receive*() callbacks become yours when you return true (delete them);
objects returned by waitFor*() and makeHTTPRequest() are always owned by
the caller. Send methods never take ownership of the object passed in.
Connection lifecycle changes (connect/disconnect/reconnect, buffer
pressure, protocol errors) are reported as NetworkEvent notifications
(NETWORKEVENT_* types).
@note Ports are always caller-chosen; the SDK deliberately has no
hard-coded default port. Connections created with @c autoreconnect=true
are transparently re-established by the manager's supervision thread.
@warning The binary DataMessage protocol sends the serialised message image
without byte-order conversion; both peers must share the same endianness
(fine for x86/ARM; see the NetworkProtocols.h byte-order notes). WebSocket
and HTTP framing, by contrast, are fully byte-order safe on the wire.
*/
#if !defined(_NETWORKCONNECTIONS_H_)
#define _NETWORKCONNECTIONS_H_

#pragma once

// #define UDPCON_PRINT_DEBUG
// #define UDPCON_PRINTBINARY_DEBUG
// #define TCPCON_PRINT_DEBUG
// #define TCPCON_PRINTBINARY_DEBUG

#include "ThreadManager.h"
#include "HTML.h"
//#include "PsyTime.h"

#ifdef _USE_SSL_
	#include "openssl/bio.h"
	#include <openssl/ssl.h>
	#include <openssl/err.h>
#endif // _USE_SSL_

namespace	cmlabs{

/*! @name Encryption modes
	Values accepted by listener/connection factory functions to select the
	transport security applied to a TCP stream. @{ */
#define NOENC			1	/*!< Plain, unencrypted transport. */
#define SSLENC			2	/*!< SSL/TLS encryption (requires build with @c _USE_SSL_). */
#define AESENC			3	/*!< AES encryption (reserved; not currently implemented). */
/*! @} */

/*! @name Connection types
	Values returned by NetworkConnection::getConnectionType(). @{ */
#define TCPCON			1	/*!< Plain TCP stream connection. */
#define UDPCON			2	/*!< UDP datagram connection. */
#define SSLCON			3	/*!< SSL/TLS-encrypted TCP connection. */
#define AESCON			4	/*!< AES-encrypted connection (reserved). */
/*! @} */

/*! Initial size in bytes of a connection's internal receive buffer;
	it grows on demand via NetworkConnection::resizeBuffer(). */
#define INITIALBUFFERSIZE	4096

/*! @name Network error codes
	Error identifiers passed to NetworkConnectionReceiver::registerError() and
	NetworkDataReceiver::registerError(), and to disconnect(). @{ */
#define NETWORKERROR_ACCEPT			1	/*!< accept() failed on a listener socket. */
#define NETWORKERROR_RECEIVE		2	/*!< A read from the socket failed (peer closed or socket error). */
#define NETWORKERROR_SEND_ERROR		3	/*!< A write to the socket failed. */
#define NETWORKERROR_SEND_TIMEOUT	4	/*!< A write could not complete within the timeout. */
#define NETWORKERROR_MEMORYFULL		5	/*!< The receive buffer could not grow to hold incoming data. */
#define NETWORKERROR_GREETING_ERROR	6	/*!< The initial greeting/handshake data exchange failed. */
/*! @} */

class TCPListener;
class TCPConnection;
class UDPConnection;
class NetworkConnection;

/*! Callback interface for asynchronous delivery of newly accepted connections.

	Implemented by objects (e.g. NetworkChannel internals) that want a
	TCPListener to run its own accept loop thread and hand over each inbound
	connection instead of the owner calling
	TCPListener::acceptConnection() synchronously.
	@note Callbacks are invoked on the listener's internal thread.
	@ingroup Networking */
class NetworkConnectionReceiver {
public:
	NetworkConnectionReceiver() {}
	/*! Called when the listener has accepted a new connection.
		@param con The newly created connection; ownership transfers to the
		receiver, which must eventually delete it.
		@return true if the connection was taken; false to have it discarded. */
	virtual bool receiveNetworkConnection(NetworkConnection* con) = 0;
	/*! Called when the listener encounters an error.
		@param error One of the NETWORKERROR_* codes.
		@param con The listener reporting the error (not owned by the receiver).
		@return true if the error was handled. */
	virtual bool registerError(uint16 error, TCPListener* con) = 0;
};

/*! Callback interface for asynchronous (push) delivery of received bytes.

	When a NetworkDataReceiver is attached to a connection at connect() time,
	the connection runs its own reader thread and pushes raw bytes to
	receiveData() as they arrive, instead of the owner pulling data with
	NetworkConnection::receive(). Protocol parsing on top of these raw bytes
	is the receiver's responsibility.
	@note Callbacks arrive on the connection's internal reader thread.
	@ingroup Networking */
class NetworkDataReceiver {
public:
	NetworkDataReceiver() {}
	/*! Called when data has been read from the socket.
		@param data Pointer to the received bytes; ownership transfers to the
		receiver (allocated with new[]; delete with delete[]).
		@param size Number of valid bytes at @p data.
		@param con The connection the data arrived on (not owned).
		@return true if the data was taken/handled. */
	virtual bool receiveData(char* data, uint32 size, NetworkConnection* con) = 0;
	/*! Called when the connection encounters an error (e.g. peer disconnect).
		@param error One of the NETWORKERROR_* codes.
		@param con The connection reporting the error (not owned).
		@return true if the error was handled. */
	virtual bool registerError(uint16 error, NetworkConnection* con) = 0;
};







/*! TCP server socket: binds a port and accepts inbound connections (plain or SSL).

	A TCPListener owns one listening socket. After init(), connections can be
	accepted either synchronously via acceptConnection() or asynchronously by
	supplying a NetworkConnectionReceiver to init(), in which case an internal
	thread runs the accept loop. When @p encryption is ::SSLENC the accepted
	connections are SSLConnection objects using the certificate set with
	setSSLCertificate(); otherwise they are plain TCPConnection objects.
	@note The socket is bound with SO_REUSEADDR semantics where available;
	init() fails (returns false) if the port is already in use.
	@ingroup Networking */
class TCPListener : public Runnable {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL TCPListenerRun(THREAD_ARG arg);

	TCPListener();
	virtual ~TCPListener();
	/*! Set the SSL certificate and private key used for inbound SSL connections.
		@param sslCertPath Path to the PEM certificate file.
		@param sslKeyPath Path to the PEM private key file.
		@return true if the paths were stored (validated later at handshake time).
		@note Must be called before init() when using ::SSLENC. */
	bool setSSLCertificate(const char* sslCertPath, const char* sslKeyPath);
	/*! Bind and start listening on a port.
		@param port TCP port to bind on all local interfaces.
		@param encryption ::NOENC or ::SSLENC (requires a certificate to be set).
		@param receiver Optional; when non-NULL an internal thread accepts
		connections and pushes them to this receiver (async mode).
		@param dataReceiver Optional data receiver installed on each accepted connection.
		@return true on success; false if binding/listening failed (e.g. port in use). */
	bool init(uint16 port, uint8 encryption, NetworkConnectionReceiver* receiver = NULL, NetworkDataReceiver* dataReceiver = NULL);
	/*! Stop listening and close the socket.
		@param error Optional NETWORKERROR_* reason propagated to the receiver.
		@return true when shut down. Safe to call more than once. */
	bool disconnect(uint16 error = 0);
	/*! @return true while the listening socket is open and accepting. */
	bool isConnected();
	/*! @return The local endpoint packed as a uint64 (IPv4 address + port), or 0 if not bound. */
	uint64 getLocalAddress();

	/*! Synchronously wait for and accept one inbound connection.
		@param timeout Maximum wait in milliseconds.
		@return A new NetworkConnection (TCPConnection or SSLConnection depending
		on the encryption mode) owned by the caller, or NULL on timeout/error.
		@note Blocking; only meaningful when no NetworkConnectionReceiver was
		given to init() (otherwise the internal thread consumes connections). */
	NetworkConnection* acceptConnection(uint32 timeout);

private:
	void disconnectInternal(uint16 error);  // cleanup without taking mutex (used by init failure paths and disconnect())
	uint8 encryption;
	uint64 localAddress;
	uint64 lastActivity;
	SOCKET socket;
	NetworkConnectionReceiver* receiver;
	NetworkDataReceiver* dataReceiver;
	utils::Mutex mutex;

	std::string sslCertPath;
	std::string sslKeyPath;

	bool run();
};

/*! Abstract base class for all point-to-point network connections.

	Owns the socket, an elastic receive buffer (starting at
	::INITIALBUFFERSIZE and grown on demand), throughput statistics and the
	connect-timeout setting. Concrete subclasses (TCPConnection,
	UDPConnection, SSLConnection) implement send(), reconnect() and the
	socket-specific read path.

	@par Pull vs push
	Data can be consumed in @e pull mode — the owner calls receive() /
	receiveAvailable() with a timeout — or in @e push mode by passing a
	NetworkDataReceiver to connect(), which starts an internal reader thread.

	@par Thread-safety
	The receive buffer is protected by @c mutex and sends are serialised by
	@c sendMutex, so one consumer plus concurrent producers are safe.
	Blocking calls take millisecond timeouts and never block indefinitely.
	@ingroup Networking */
class NetworkConnection : public Runnable {
public:
	NetworkConnection();
	~NetworkConnection();
	/*! Close the connection and release the socket.
		@param error Optional NETWORKERROR_* reason forwarded to a data receiver.
		@return true when disconnected. Safe to call repeatedly. */
	virtual bool disconnect(uint16 error = 0);
	/*! Re-establish the connection to the previously known remote endpoint.
		@param timeoutMS Maximum time to spend connecting, in milliseconds.
		@return true on success. Pure virtual; UDP treats this as a rebind. */
	virtual bool reconnect(uint32 timeoutMS) = 0;
	/*! Check/complete an in-progress (delayed) connect on the existing socket.
		@param timeout Milliseconds to wait for the connect to complete (0 = poll).
		@return true once the socket is writable, i.e. connected. */
	virtual bool didConnect(int timeout = 0);
	/*! Variant of didConnect() testing an explicit socket handle.
		@param s Socket to test. @param timeout Milliseconds to wait.
		@return true once @p s is connected. */
	virtual bool didConnect(SOCKET s, int timeout);
	/*! Test whether the connection is currently alive.
		@param timeout Milliseconds to allow for probing the socket (0 = immediate).
		@return true if connected. */
	virtual bool isConnected(int timeout = 0);
	/*! @return true if this connection was accepted from a remote peer
		(server side), false if it was initiated locally (client side). */
	virtual bool isRemote();
	/*! @return The remote endpoint packed as a uint64 (IPv4 address + port), or 0 if unknown. */
	virtual uint64 getRemoteAddress();
	/*! Set the timeout used by subsequent connect()/reconnect() attempts.
		@param timeoutMS Timeout in milliseconds. @return true always. */
	bool setConnectTimeout(uint32 timeoutMS);

	/*! Send raw bytes on the connection.
		@param data Bytes to send (caller retains ownership).
		@param size Number of bytes to send.
		@param receiver Destination endpoint (uint64 address+port); used by UDP,
		ignored by stream transports.
		@return true if all bytes were written.
		@note Serialised by an internal send mutex; may block until the OS
		buffer accepts the data or a send timeout occurs. */
	virtual bool send(const char* data, uint32 size, uint64 receiver = 0) = 0;
//	virtual int32 peekStream();
	/*! Receive exactly @p size bytes into @p data, waiting up to @p timeout ms.
		@param data Destination buffer of at least @p size bytes.
		@param size Exact number of bytes required.
		@param timeout Maximum wait in milliseconds.
		@param peek When true the bytes remain in the internal buffer
		(subsequent reads see them again).
		@return true if @p size bytes were delivered before the timeout. */
	virtual bool receive(char* data, uint32 size, uint32 timeout, bool peek = false);
	/*! Receive whatever bytes are available (up to @p maxSize).
		@param data Destination buffer.
		@param size Out: number of bytes actually delivered.
		@param maxSize Capacity of @p data.
		@param timeout Maximum wait in milliseconds for at least one byte.
		@param peek When true the bytes remain buffered.
		@return true if any bytes were delivered. */
	virtual bool receiveAvailable(char* data, uint32& size, uint32 maxSize, uint32 timeout, bool peek = false);
	/*! Drop @p size bytes from the front of the receive buffer (after a peek).
		@param size Number of bytes to discard. @return true on success. */
	virtual bool discard(uint32 size);
	/*! Discard all currently buffered input.
		@return The number of bytes discarded. */
	virtual uint32 clearBuffer();

	/*! Block until data is readable (buffered or on the socket).
		@param timeout Maximum wait in milliseconds.
		@return true if data is available before the timeout. */
	virtual bool waitForDataToRead(uint32 timeout);
	/*! Block until the socket is writable.
		@param timeout Maximum wait in milliseconds.
		@return true if writable before the timeout. */
	virtual bool waitForDataToBeWritten(uint32 timeout);

	/*! @return Recent outbound throughput in bytes/second (moving estimate). */
	virtual uint32 getOutputSpeed();
	/*! @return Recent inbound throughput in bytes/second (moving estimate). */
	virtual uint32 getInputSpeed();

	/*! @return The transport type: ::TCPCON, ::UDPCON, ::SSLCON or ::AESCON. */
	virtual uint8 getConnectionType();

	/*! Set greeting bytes sent automatically right after a connection is
		established (used e.g. to identify the peer or select a protocol).
		@param data Greeting bytes (copied). @param size Byte count.
		@return true on success. */
	bool setGreetingData(const char* data, uint32 size);
	char* greetingData;		/*!< Owned copy of the greeting bytes (NULL if unset). */
	uint32 greetingSize;	/*!< Size of ::greetingData in bytes. */

protected:
	void disconnectInternal(uint16 error);  // cleanup without taking mutex (call when mutex already held, or from disconnect())

	uint8 type;
	uint64 remoteAddress;
	uint64 localAddress;
	uint64 lastActivity;
	uint32 threadID;
	SOCKET socket;
	bool remote;
	uint32 bufferLen;
	uint32 bufferContentLen;
	uint32 bufferContentPos;
	NetworkDataReceiver* receiver;
	utils::Mutex mutex;
	utils::Mutex sendMutex;
	char* buffer;
	uint32 connectTimeoutMS;
	uint32 inputSpeed;
	uint32 outputSpeed;
	uint64 inputBytes;
	uint64 outputBytes;

	virtual int32 readIntoBuffer();
	virtual bool resizeBuffer(uint32 len);
	virtual bool run();
};


/*! UDP datagram connection (bound port for input, or output-only sender).

	Used for connectionless messaging (e.g. NetworkManager::sendUDPMessage and
	discovery/broadcast traffic). Because UDP has no session, reconnect()
	simply rebinds; datagrams may be lost or reordered by the network.
	@ingroup Networking */
class UDPConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL UDPConnectionRun(THREAD_ARG arg);

	UDPConnection();
	~UDPConnection();
	/*! Create an unbound socket usable only for sending datagrams.
		@return true on success. */
	bool initForOutputOnly();
	/*! Bind a local UDP port for receiving datagrams.
		@param port Local port to bind.
		@param receiver Optional push-mode receiver; when set, an internal
		thread reads datagrams and forwards them.
		@return true on success, false if the port could not be bound. */
	bool connect(uint16 port, NetworkDataReceiver* receiver = NULL);
	/*! Send one datagram.
		@param data Payload bytes. @param size Payload length (must fit one datagram).
		@param receiver Destination endpoint as packed uint64 (address+port);
		when 0 the default receiver set via setDefaultReceiver() is used.
		@return true if the datagram was handed to the OS. Delivery is not guaranteed. */
	bool send(const char* data, uint32 size, uint64 receiver = 0);
	/*! Rebind the local port (UDP has no session to re-establish).
		@param timeoutMS Unused for UDP beyond the bind attempt. @return true on success. */
	bool reconnect(uint32 timeoutMS);
	/*! Set the default destination used by send() when @c receiver == 0.
		@param receiver Packed uint64 endpoint. @return true always. */
	bool setDefaultReceiver(uint64 receiver);
	/*! @return The bound local endpoint packed as uint64, or 0 if unbound. */
	uint64 getLocalAddress();

protected:
	uint64 defaultReceiver;	/*!< Default destination endpoint for send() when none is given. */
};


/*! Plain TCP stream connection (client-initiated or accepted from a listener).

	Supports immediate connects (with timeout), multi-address connects that
	try several candidate IPv4 addresses for the same host, and
	@e delayed (non-blocking) connects completed later via didConnect() —
	used by NetworkManager to establish connections without stalling its
	management thread.
	@ingroup Networking */
class TCPConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL TCPConnectionRun(THREAD_ARG arg);

	TCPConnection();
	~TCPConnection();
	/*! Adopt an already-connected socket (server side, from a TCPListener).
		@param s Connected socket handle (ownership transfers to this object).
		@param localAddr Local endpoint packed as uint64.
		@param receiver Optional push-mode data receiver.
		@return true on success. */
	bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver* receiver = NULL);
	/*! Connect to a remote endpoint given as a packed uint64 (address+port).
		@param addr Packed endpoint. @param timeoutMS Connect timeout in ms.
		@param receiver Optional push-mode data receiver.
		@return true if connected within the timeout. Blocking. */
	bool connect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Connect to a host by name/IP string and port.
		@param addr Host name or dotted IPv4 string (resolved via DNS if needed).
		@param port Remote TCP port.
		@param location Out: resolved endpoint packed as uint64 (reusable for reconnects).
		@param timeoutMS Connect timeout in ms.
		@param receiver Optional push-mode data receiver.
		@return true if connected within the timeout. Blocking (includes DNS resolution). */
	bool connect(const char* addr, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Try a list of candidate IPv4 addresses (same port) until one connects.
		@param addresses Array of IPv4 addresses in host order.
		@param addressCount Number of entries in @p addresses.
		@param port Remote TCP port.
		@param location Out: the endpoint that succeeded, packed as uint64.
		@param timeoutMS Total connect budget in ms.
		@param receiver Optional push-mode data receiver.
		@return true if any address connected. */
	bool connect(const uint32* addresses, uint16 addressCount, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Begin a non-blocking connect; completion is checked with didConnect().
		@param addr Packed uint64 endpoint. @param timeoutMS Timeout used by later completion checks.
		@param receiver Optional push-mode data receiver.
		@return true if the connect was initiated. */
	bool delayedConnect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver* receiver);
	/*! Begin a non-blocking connect to a host name/IP string and port.
		@param addr Host name or dotted IPv4 string. @param port Remote port.
		@param location Out: resolved endpoint packed as uint64.
		@param timeoutMS Timeout used by later completion checks.
		@param receiver Optional push-mode data receiver.
		@return true if the connect was initiated. */
	bool delayedConnect(const char* addr, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver);
	/*! Send bytes on the stream (see NetworkConnection::send()).
		@param data Bytes to send. @param size Byte count. @param receiver Ignored for TCP.
		@return true if all bytes were written. */
	bool send(const char* data, uint32 size, uint64 receiver = 0);
	/*! Reconnect to the previously resolved remote endpoint.
		@param timeoutMS Connect timeout in ms. @return true on success. */
	bool reconnect(uint32 timeoutMS);

protected:
	/*! Query the OS for the peer address of the connected socket.
		@param addr Out: packed uint64 endpoint. @return true on success. */
	bool findRemoteAddress(uint64& addr);
};

THREAD_RET THREAD_FUNCTION_CALL SSLConnectionRun(THREAD_ARG arg);

/*! SSL/TLS-encrypted TCP connection (OpenSSL) with configurable peer verification.

	Mirrors the TCPConnection API but wraps the socket in an OpenSSL session.
	The existing inline comments below document the verification policy
	precisely; in summary: peer certificates are verified against the OS/CA
	trust store by default, hostname verification (SNI + SSL_set1_host) is
	applied when the hostname is known, self-signed peers can be allowed per
	connection or process-wide, and a custom CA file/path can replace the OS
	trust store. Full SSL support requires building with @c _USE_SSL_;
	without it the class exists but cannot establish encrypted sessions.
	@warning setAllowSelfSigned(true) disables peer verification entirely for
	that connection — use only for testing or closed networks.
	@ingroup Networking */
class SSLConnection : public NetworkConnection {
public:
	friend THREAD_RET THREAD_FUNCTION_CALL SSLConnectionRun(THREAD_ARG arg);

	SSLConnection();
	~SSLConnection();
	/*! Initialise the OpenSSL context for a client-side connection.
		@return true on success. Call before connect() (done implicitly by connect()). */
	bool init();
	/*! Initialise the OpenSSL context for a server-side connection with a certificate.
		@param certFile Path to PEM certificate. @param keyFile Path to PEM private key.
		@return true if the certificate/key pair loaded successfully. */
	bool init(const char *certFile, const char *keyFile);

	// Client certificate verification policy. Default (allowSelfSigned=false):
	// verify the peer certificate against the OS/CA trust store (secure by
	// default). setAllowSelfSigned(true) disables peer verification for this
	// connection only. SetDefaultAllowSelfSigned() sets the process-wide
	// default (e.g. from <psyspec allowselfsigned="yes">); per-connection
	// settings override it either way. Call before init()/connect().
	void setAllowSelfSigned(bool allow);
	bool getAllowSelfSigned() const { return allowSelfSigned; }
	static void SetDefaultAllowSelfSigned(bool allow);
	static bool GetDefaultAllowSelfSigned();

	// Hostname verification: when peer verification is on (allowSelfSigned ==
	// false) and a hostname is known, the peer certificate must also match
	// that hostname (SSL_set1_host) and SNI is sent. The hostname is recorded
	// automatically by connect(const char* addr, ...) when addr is not an IP
	// literal; setVerifyHostName() sets/overrides it explicitly (empty string
	// clears it -> chain-of-trust check only). Call before connect().
	void setVerifyHostName(const char* host);
	const char* getVerifyHostName() const { return verifyHostName.c_str(); }

	// Custom CA location for peer verification: when set, peer certificates
	// are verified against this CA file and/or directory (PEM) instead of the
	// OS default trust store. NULL/empty clears. SetDefaultCALocation() sets
	// the process-wide default (e.g. from <psyspec cafile="..." capath="...">);
	// per-connection settings override it. Call before init()/connect().
	void setCALocation(const char* caFile, const char* caPath);
	static void SetDefaultCALocation(const char* caFile, const char* caPath);
#ifdef _USE_SSL_
	// Returns the SSL_CTX verify mode currently configured (for tests)
	int getVerifyMode();
#endif //_USE_SSL_
	/*! Adopt an already-accepted socket and perform the server-side TLS handshake.
		@param s Connected socket (ownership transfers). @param localAddr Packed local endpoint.
		@param receiver Optional push-mode data receiver. @return true on handshake success. */
	bool connect(SOCKET s, uint64 localAddr, NetworkDataReceiver* receiver = NULL);
	/*! Connect and handshake to a packed uint64 endpoint (client side).
		@param addr Packed endpoint. @param timeoutMS Connect+handshake timeout in ms.
		@param receiver Optional push-mode data receiver. @return true on success. */
	bool connect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Connect and handshake to a host by name/IP and port (client side).
		When @p addr is a DNS name (not an IP literal) it is recorded for
		hostname verification and sent as SNI.
		@param addr Host name or IPv4 string. @param port Remote port.
		@param location Out: resolved endpoint packed as uint64.
		@param timeoutMS Connect+handshake timeout in ms.
		@param receiver Optional push-mode data receiver. @return true on success. */
	bool connect(const char* addr, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Try several candidate IPv4 addresses until one connects and handshakes.
		@param addresses IPv4 candidates. @param addressCount Count. @param port Remote port.
		@param location Out: winning endpoint. @param timeoutMS Total budget in ms.
		@param receiver Optional push-mode data receiver. @return true on success. */
	bool connect(const uint32* addresses, uint16 addressCount, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver = NULL);
	/*! Begin a non-blocking connect (TLS handshake completes in didConnect()).
		@param addr Packed endpoint. @param timeoutMS Completion timeout.
		@param receiver Optional push-mode data receiver. @return true if initiated. */
	bool delayedConnect(uint64 addr, uint32 timeoutMS, NetworkDataReceiver* receiver);
	/*! Begin a non-blocking connect to a host name/IP (see TCPConnection::delayedConnect()).
		@param addr Host name or IPv4 string. @param port Remote port.
		@param location Out: resolved endpoint. @param timeoutMS Completion timeout.
		@param receiver Optional push-mode data receiver. @return true if initiated. */
	bool delayedConnect(const char* addr, uint16 port, uint64& location, uint32 timeoutMS, NetworkDataReceiver* receiver);
	/*! Send bytes over the encrypted stream.
		@param data Bytes to send. @param size Byte count. @param receiver Ignored.
		@return true if all bytes were written through SSL_write. */
	bool send(const char* data, uint32 size, uint64 receiver = 0);
	/*! Reconnect and re-handshake to the previous endpoint.
		@param timeoutMS Timeout in ms. @return true on success. */
	bool reconnect(uint32 timeoutMS);
	/*! @copydoc NetworkConnection::isConnected() */
	bool isConnected(int timeout = 0);
	/*! @copydoc NetworkConnection::didConnect(int) */
	bool didConnect(int timeout = 0);
	/*! Shut down the TLS session and close the socket.
		@param error Optional NETWORKERROR_* reason. @return true when closed. */
	bool disconnect(uint16 error = 0);

	/*! Peek how many decrypted bytes are pending inside the SSL layer.
		@return Number of bytes available, or negative on error. */
	int32 peekStream();
	/*! Read decrypted bytes from the SSL layer into the internal buffer.
		@return Bytes read, 0 if none, negative on error. */
	int32 readIntoBuffer();
	/*! @copydoc NetworkConnection::receive() */
	bool receive(char* data, uint32 size, uint32 timeout, bool peek = false);
	/*! @copydoc NetworkConnection::receiveAvailable() */
	bool receiveAvailable(char* data, uint32& size, uint32 maxSize, uint32 timeout, bool peek = false);

	std::string certinfo;	/*!< Human-readable summary of the peer certificate (subject/issuer), filled after handshake. */

protected:
	bool findRemoteAddress(uint64& addr);
	bool allowSelfSigned;
	static bool DefaultAllowSelfSigned;
	std::string verifyHostName;
	std::string caFile;
	std::string caPath;
	static std::string DefaultCAFile;
	static std::string DefaultCAPath;
	#ifdef _USE_SSL_
		bool applyClientVerify();
		SSL_CTX *ctx;
		SSL *ssl;
		BIO *certbio;
		BIO *outbio;
#endif //_USE_SSL_
};

/*! Thread entry point for a TCPListener's internal accept loop. @ingroup Networking */
THREAD_RET THREAD_FUNCTION_CALL TCPListenerRun(THREAD_ARG arg);
/*! Thread entry point for a TCPConnection's push-mode reader loop. @ingroup Networking */
THREAD_RET THREAD_FUNCTION_CALL TCPConnectionRun(THREAD_ARG arg);
/*! Thread entry point for a UDPConnection's push-mode reader loop. @ingroup Networking */
THREAD_RET THREAD_FUNCTION_CALL UDPConnectionRun(THREAD_ARG arg);

/*! Loopback test server: listens on @p port and echoes test payloads.
	@param port TCP port to listen on. @param count Number of exchanges (0 = default).
	@return true when the test completes successfully. @ingroup Networking */
bool NetworkTest_TCPServer(uint16 port, uint32 count = 0);
/*! Loopback test client matching NetworkTest_TCPServer().
	@param address Server host. @param port Server port. @param count Exchanges (0 = default).
	@return true when the test completes successfully. @ingroup Networking */
bool NetworkTest_TCPClient(const char* address, uint16 port, uint32 count = 0);
/*! Send-and-verify helper used by the TCP tests.
	@param con Connection under test. @param data Payload buffer. @param dataLen Payload size.
	@param c Iteration index. @param receiveFirst Receive before sending when true.
	@return true if the exchanged data matched. @ingroup Networking */
bool NetworkTest_SendReceiveData(TCPConnection* con, char* data, uint32 dataLen, uint32 c, bool receiveFirst = false);

}

#endif // _NETWORKCONNECTIONS_H_
