diff --git a/code/logic/error.c b/code/logic/error.c index 01a0f0c..b5e4f9d 100644 --- a/code/logic/error.c +++ b/code/logic/error.c @@ -40,7 +40,7 @@ const char *fossil_io_what(fossil_status_t error_code) { switch (error_code) { // Success and General Errors case FOSSIL_ERROR_OK: return "No error, operation successful."; - case FOSSIL_ERROR_NULL_POINTER: return "Null pointer encountered."; + case FOSSIL_ERROR_CNULL_POINTER: return "Null pointer encountered."; case FOSSIL_ERROR_INVALID_ARGUMENT: return "Invalid argument provided."; case FOSSIL_ERROR_TYPE_MISMATCH: return "Type mismatch encountered."; case FOSSIL_ERROR_INVALID_OPERATION: return "Invalid operation."; diff --git a/code/logic/fossil/io/error.h b/code/logic/fossil/io/error.h index d4343a1..fa8910d 100644 --- a/code/logic/fossil/io/error.h +++ b/code/logic/fossil/io/error.h @@ -21,7 +21,7 @@ extern "C" { typedef enum { // Success and General Errors FOSSIL_ERROR_OK = 0, - FOSSIL_ERROR_NULL_POINTER, + FOSSIL_ERROR_CNULL_POINTER, FOSSIL_ERROR_INVALID_ARGUMENT, FOSSIL_ERROR_TYPE_MISMATCH, FOSSIL_ERROR_INVALID_OPERATION, @@ -200,7 +200,7 @@ namespace fossil { enum class ErrorCode { // Success and General Errors OK = FOSSIL_ERROR_OK, - NULL_POINTER = FOSSIL_ERROR_NULL_POINTER, + NULL_POINTER = FOSSIL_ERROR_CNULL_POINTER, INVALID_ARGUMENT = FOSSIL_ERROR_INVALID_ARGUMENT, TYPE_MISMATCH = FOSSIL_ERROR_TYPE_MISMATCH, INVALID_OPERATION = FOSSIL_ERROR_INVALID_OPERATION, diff --git a/code/logic/fossil/io/network.h b/code/logic/fossil/io/network.h index 935750f..9b9def6 100644 --- a/code/logic/fossil/io/network.h +++ b/code/logic/fossil/io/network.h @@ -14,169 +14,212 @@ #ifndef FOSSIL_IO_NETWORK_H #define FOSSIL_IO_NETWORK_H -#include - -#ifdef _WIN32 - #include - #include - typedef SOCKET fossil_io_socket_t; - #define FOSSIL_IO_INVALID_SOCKET INVALID_SOCKET -#else - #include - #include - #include - #include - #include - #include - #define closesocket close - typedef int fossil_io_socket_t; - #define FOSSIL_IO_INVALID_SOCKET (-1) -#endif - -#define FOSSIL_IO_SOCKET_TYPE_TCP SOCK_STREAM -#define FOSSIL_IO_SOCKET_TYPE_UDP SOCK_DGRAM +#include +#include #ifdef __cplusplus extern "C" { #endif /** - * Initialize the network stack. This function is necessary for Windows - * platforms to set up the Winsock library. On other platforms, it may - * perform other necessary initializations. - * - * @return 0 on success, non-zero on failure. - */ -int fossil_io_network_create(void); - -/** - * Clean up the network stack. This function is necessary for Windows - * platforms to clean up the Winsock library. On other platforms, it may - * perform other necessary clean-up operations. + * @file network.c + * @brief Network logic implementation for handling communication protocols and client types. + * + * This file contains the core logic for managing network communication in the application. + * It is responsible for implementing various protocols and handling different types of clients + * that interact with the system. The following details outline the key aspects of the network + * logic: + * + * ## Supported Protocols: + * 1. **TCP (Transmission Control Protocol):** + * - Provides reliable, ordered, and error-checked delivery of data. + * - Used for persistent connections and critical data exchange. + * - Ensures data integrity and retransmission in case of packet loss. + * + * 2. **UDP (User Datagram Protocol):** + * - Provides connectionless communication with minimal overhead. + * - Suitable for real-time applications where speed is prioritized over reliability. + * - Commonly used for streaming, gaming, and other low-latency scenarios. + * + * 3. **HTTP/HTTPS:** + * - Supports web-based communication using the Hypertext Transfer Protocol. + * - HTTPS ensures secure communication via SSL/TLS encryption. + * - Used for RESTful APIs and web client interactions. + * + * 4. **WebSocket:** + * - Enables full-duplex communication channels over a single TCP connection. + * - Ideal for real-time applications such as chat systems and live updates. + * + * ## Client Types: + * 1. **Standard Clients:** + * - General-purpose clients that use standard protocols like HTTP or WebSocket. + * - Typically include web browsers, mobile apps, and desktop applications. + * + * 2. **Embedded Clients:** + * - Lightweight clients running on embedded systems or IoT devices. + * - Often use UDP or custom lightweight protocols for communication. + * + * 3. **Service Clients:** + * - Backend services or microservices that interact with the system. + * - May use HTTP APIs, gRPC, or other service-to-service communication protocols. + * + * 4. **Real-Time Clients:** + * - Clients requiring low-latency communication, such as gaming or streaming applications. + * - Often rely on WebSocket or UDP for real-time data exchange. + * + * ## Supported Flags: + * - **Protocol Flags:** + * - `tcp`: Specifies the use of the TCP protocol. + * - `udp`: Specifies the use of the UDP protocol. + * - `http`: Specifies the use of the HTTP protocol. + * - `https`: Specifies the use of the HTTPS protocol. + * - `raw`: Specifies the use of raw sockets. + * - `icmp`: Specifies the use of ICMP protocol. + * - `sctp`: Specifies the use of SCTP protocol. + * - `ftp`: Specifies the use of the FTP protocol. + * - `ssh`: Specifies the use of the SSH protocol. + * - `dns`: Specifies the use of the DNS protocol. + * - `ntp`: Specifies the use of the NTP protocol. + * - `smtp`: Specifies the use of the SMTP protocol. + * - `pop3`: Specifies the use of the POP3 protocol. + * - `imap`: Specifies the use of the IMAP protocol. + * - `ldap`: Specifies the use of the LDAP protocol. + * - `mqtt`: Specifies the use of the MQTT protocol. + * + * - **Client Type Flags:** + * - `mail-server`: Represents a mail server client. + * - `server`: Represents a general server client. + * - `mail-client`: Represents a mail client. + * - `client`: Represents a general client. + * - `mail-bot`: Represents an automated mail bot client. + * - `bot`: Represents a general bot client. + * - `multicast`: Represents a multicast client. + * - `broadcast`: Represents a broadcast client. + * + * ## Additional Notes: + * - The implementation ensures scalability and fault tolerance for handling multiple clients + * simultaneously. + * - Security measures, such as encryption and authentication, are integrated to protect + * communication channels. + * - The file is designed to be modular, allowing easy extension for new protocols or client types. */ -void fossil_io_network_destroy(void); -/** - * Create a new socket of the specified type (TCP or UDP). - * - * @param type The type of socket to create (e.g., SOCK_STREAM for TCP, SOCK_DGRAM for UDP). - * @return A valid socket on success, or FOSSIL_IO_INVALID_SOCKET on failure. - */ -fossil_io_socket_t fossil_io_network_create_socket(int type); +typedef struct fossil_nstream_t fossil_nstream_t; + +typedef enum { + FOSSIL_PROTO_TCP, + FOSSIL_PROTO_UDP, + FOSSIL_PROTO_RAW, + FOSSIL_PROTO_ICMP, + FOSSIL_PROTO_SCTP, + FOSSIL_PROTO_HTTP, + FOSSIL_PROTO_HTTPS, + FOSSIL_PROTO_FTP, + FOSSIL_PROTO_SSH, + FOSSIL_PROTO_DNS, + FOSSIL_PROTO_NTP, + FOSSIL_PROTO_SMTP, + FOSSIL_PROTO_POP3, + FOSSIL_PROTO_IMAP, + FOSSIL_PROTO_LDAP, + FOSSIL_PROTO_MQTT, + FOSSIL_PROTO_UNKNOWN +} fossil_protocol_t; + +typedef enum { + FOSSIL_CLIENT_MAIL_SERVER, + FOSSIL_CLIENT_SERVER, + FOSSIL_CLIENT_MAIL_CLIENT, + FOSSIL_CLIENT_CLIENT, + FOSSIL_CLIENT_MAIL_BOT, + FOSSIL_CLIENT_BOT, + FOSSIL_CLIENT_MULTICAST, + FOSSIL_CLIENT_BROADCAST, + FOSSIL_CLIENT_UNKNOWN +} fossil_client_type_t; /** - * Bind a socket to a specific IP address and port. This function associates - * the socket with a local address so that it can listen for incoming connections - * or send data. - * - * @param sock The socket to bind. - * @param ip The IP address to bind to (e.g., "127.0.0.1" for localhost). - * @param port The port number to bind to. - * @return 0 on success, -1 on failure. + * Create a new network stream. + * + * @param protocol_flag The protocol to use (e.g., "tcp", "udp"). + * @param client_type_flag The type of client (e.g., "server", "client"). + * @return A pointer to the newly created network stream, or NULL on failure. */ -int fossil_io_network_bind(fossil_io_socket_t sock, const char *ip, uint16_t port); +fossil_nstream_t *fossil_nstream_create(const char *protocol_flag, const char *client_type_flag); /** - * Listen for incoming connections on a bound socket. This function puts the - * socket into a state where it can accept incoming connection requests. - * - * @param sock The socket to listen on. - * @param backlog The maximum length of the queue of pending connections. - * @return 0 on success, -1 on failure. + * Connect a network stream to a remote host. + * + * @param stream The network stream to connect. + * @param host The hostname or IP address of the remote host. + * @param port The port number to connect to. + * @return 0 on success, or -1 on failure. */ -int fossil_io_network_listen(fossil_io_socket_t sock, int backlog); +int fossil_nstream_connect(fossil_nstream_t *stream, const char *host, int port); /** - * Accept a new incoming connection on a listening socket. This function - * extracts the first connection request on the queue of pending connections - * and creates a new socket for the connection. - * - * @param sock The listening socket. - * @param client_ip A buffer to store the IP address of the connecting client. - * @param client_port A pointer to store the port number of the connecting client. - * @return A valid socket for the new connection on success, or FOSSIL_IO_INVALID_SOCKET on failure. + * Set up a network stream to listen for incoming connections. + * + * @param stream The network stream to set up. + * @param host The hostname or IP address to bind to. + * @param port The port number to listen on. + * @return 0 on success, or -1 on failure. */ -fossil_io_socket_t fossil_io_network_accept(fossil_io_socket_t sock, char *client_ip, uint16_t *client_port); +int fossil_nstream_listen(fossil_nstream_t *stream, const char *host, int port); /** - * Connect to a remote server. This function establishes a connection to a - * specified IP address and port. - * - * @param sock The socket to use for the connection. - * @param ip The IP address of the remote server. - * @param port The port number of the remote server. - * @return 0 on success, -1 on failure. + * Accept a new incoming connection on a listening network stream. + * + * @param server The listening network stream. + * @return A pointer to the new network stream for the accepted connection, or NULL on failure. */ -int fossil_io_network_connect(fossil_io_socket_t sock, const char *ip, uint16_t port); +fossil_nstream_t *fossil_nstream_accept(fossil_nstream_t *server); /** - * Send data over a connected socket. This function transmits data to the - * remote peer connected to the socket. - * - * @param sock The socket to use for sending data. - * @param data A pointer to the data to be sent. - * @param len The length of the data to be sent. + * Send data through a network stream. + * + * @param stream The network stream to send data through. + * @param buffer The buffer containing the data to send. + * @param size The size of the data to send, in bytes. * @return The number of bytes sent, or -1 on failure. */ -int fossil_io_network_send(fossil_io_socket_t sock, const void *data, size_t len); +ssize_t fossil_nstream_send(fossil_nstream_t *stream, const void *buffer, size_t size); /** - * Receive data from a connected socket. This function reads data from the - * remote peer connected to the socket. - * - * @param sock The socket to use for receiving data. - * @param buffer A buffer to store the received data. - * @param len The maximum length of data to be received. + * Receive data through a network stream. + * + * @param stream The network stream to receive data from. + * @param buffer The buffer to store the received data. + * @param size The maximum size of the buffer, in bytes. * @return The number of bytes received, or -1 on failure. */ -int fossil_io_network_receive(fossil_io_socket_t sock, void *buffer, size_t len); +ssize_t fossil_nstream_recv(fossil_nstream_t *stream, void *buffer, size_t size); /** - * Close a socket. This function releases the resources associated with the - * socket and closes the connection. - * - * @param sock The socket to be closed. - */ -void fossil_io_network_close(fossil_io_socket_t sock); - -/** - * Send data to a specific IP address and port using a UDP socket. This function - * transmits data to the specified address and port without establishing a connection. - * - * @param sock The socket to use for sending data. - * @param data A pointer to the data to be sent. - * @param len The length of the data to be sent. - * @param ip The IP address of the destination. - * @param port The port number of the destination. - * @return The number of bytes sent, or -1 on failure. + * Close a network stream. + * + * @param stream The network stream to close. */ -int fossil_io_network_sendto(fossil_io_socket_t sock, const void *data, size_t len, const char *ip, uint16_t port); +void fossil_nstream_close(fossil_nstream_t *stream); /** - * Receive data from a specific IP address and port using a UDP socket. This function - * reads data from the specified address and port without establishing a connection. - * - * @param sock The socket to use for receiving data. - * @param buffer A buffer to store the received data. - * @param len The maximum length of data to be received. - * @param ip A buffer to store the IP address of the sender. - * @param port A pointer to store the port number of the sender. - * @return The number of bytes received, or -1 on failure. + * Destroy a network stream and free its resources. + * + * @param stream The network stream to destroy. */ -int fossil_io_network_recvfrom(fossil_io_socket_t sock, void *buffer, size_t len, char *ip, uint16_t *port); +void fossil_nstream_destroy(fossil_nstream_t *stream); /** - * Bridge function for network operations. This function can be used to - * transfer data between two sockets, effectively bridging them. - * - * @param sock1 The first socket. - * @param sock2 The second socket. - * @return 0 on success, -1 on failure. + * Get a string describing the last error that occurred. + * + * @return A string describing the last error. */ -int fossil_io_network_bridge(fossil_io_socket_t sock1, fossil_io_socket_t sock2); +const char *fossil_nstream_last_error(void); #ifdef __cplusplus } +#include +#include /** * C++ wrapper for the output functions. @@ -189,173 +232,127 @@ namespace fossil { /** * Class for network operations. */ - class Network { + /** + * A C++ wrapper class for the Fossil network stream API. + * Provides an object-oriented interface for managing network streams. + */ + class NStream { public: /** - * Initialize the network stack. This function is necessary for Windows - * platforms to set up the Winsock library. On other platforms, it may - * perform other necessary initializations. - * - * @return 0 on success, non-zero on failure. - */ - static int init(void) { - return fossil_io_network_create(); - } - - /** - * Clean up the network stack. This function is necessary for Windows - * platforms to clean up the Winsock library. On other platforms, it may - * perform other necessary clean-up operations. + * Constructor to create a new network stream. + * + * @param protocol_flag The protocol to use (e.g., "tcp", "udp"). + * @param client_type_flag The type of client (e.g., "server", "client"). + * @throws std::runtime_error If the stream creation fails. */ - static void cleanup(void) { - fossil_io_network_destroy(); + NStream(const std::string &protocol_flag, const std::string &client_type_flag) { + stream_ = fossil_nstream_create(protocol_flag.c_str(), client_type_flag.c_str()); + if (!stream_) { + throw std::runtime_error(fossil_nstream_last_error()); + } } /** - * Create a new socket of the specified type (TCP or UDP). - * - * @param type The type of socket to create (e.g., SOCK_STREAM for TCP, SOCK_DGRAM for UDP). - * @return A valid socket on success, or FOSSIL_IO_INVALID_SOCKET on failure. + * Destructor to destroy the network stream and free its resources. */ - static fossil_io_socket_t create_socket(int type) { - return fossil_io_network_create_socket(type); + ~NStream() { + if (stream_) { + fossil_nstream_destroy(stream_); + } } /** - * Bind a socket to a specific IP address and port. This function associates - * the socket with a local address so that it can listen for incoming connections - * or send data. - * - * @param sock The socket to bind. - * @param ip The IP address to bind to (e.g., "127.0.0.1" for localhost). - * @param port The port number to bind to. - * @return 0 on success, -1 on failure. + * Connect the network stream to a remote host. + * + * @param host The hostname or IP address of the remote host. + * @param port The port number to connect to. + * @throws std::runtime_error If the connection fails. */ - static int bind(fossil_io_socket_t sock, const char *ip, uint16_t port) { - return fossil_io_network_bind(sock, ip, port); + void connect(const std::string &host, int port) { + if (fossil_nstream_connect(stream_, host.c_str(), port) != 0) { + throw std::runtime_error(fossil_nstream_last_error()); + } } /** - * Listen for incoming connections on a bound socket. This function puts the - * socket into a state where it can accept incoming connection requests. - * - * @param sock The socket to listen on. - * @param backlog The maximum length of the queue of pending connections. - * @return 0 on success, -1 on failure. + * Set up the network stream to listen for incoming connections. + * + * @param host The hostname or IP address to bind to. + * @param port The port number to listen on. + * @throws std::runtime_error If the setup fails. */ - static int listen(fossil_io_socket_t sock, int backlog) { - return fossil_io_network_listen(sock, backlog); + void listen(const std::string &host, int port) { + if (fossil_nstream_listen(stream_, host.c_str(), port) != 0) { + throw std::runtime_error(fossil_nstream_last_error()); + } } /** - * Accept a new incoming connection on a listening socket. This function - * extracts the first connection request on the queue of pending connections - * and creates a new socket for the connection. - * - * @param sock The listening socket. - * @param client_ip A buffer to store the IP address of the connecting client. - * @param client_port A pointer to store the port number of the connecting client. - * @return A valid socket for the new connection on success, or FOSSIL_IO_INVALID_SOCKET on failure. + * Accept a new incoming connection on a listening network stream. + * + * @return A pointer to a new NStream object for the accepted connection. + * @throws std::runtime_error If accepting the connection fails. */ - static fossil_io_socket_t accept(fossil_io_socket_t sock, char *client_ip, uint16_t *client_port) { - return fossil_io_network_accept(sock, client_ip, client_port); + NStream *accept() { + fossil_nstream_t *accepted_stream = fossil_nstream_accept(stream_); + if (!accepted_stream) { + throw std::runtime_error(fossil_nstream_last_error()); + } + return new NStream(accepted_stream); } /** - * Connect to a remote server. This function establishes a connection to a - * specified IP address and port. - * - * @param sock The socket to use for the connection. - * @param ip The IP address of the remote server. - * @param port The port number of the remote server. - * @return 0 on success, -1 on failure. + * Send data through the network stream. + * + * @param buffer The buffer containing the data to send. + * @param size The size of the data to send, in bytes. + * @return The number of bytes sent. + * @throws std::runtime_error If sending the data fails. */ - static int connect(fossil_io_socket_t sock, const char *ip, uint16_t port) { - return fossil_io_network_connect(sock, ip, port); + ssize_t send(const void *buffer, size_t size) { + ssize_t bytes_sent = fossil_nstream_send(stream_, buffer, size); + if (bytes_sent < 0) { + throw std::runtime_error(fossil_nstream_last_error()); + } + return bytes_sent; } /** - * Send data over a connected socket. This function transmits data to the - * remote peer connected to the socket. - * - * @param sock The socket to use for sending data. - * @param data A pointer to the data to be sent. - * @param len The length of the data to be sent. - * @return The number of bytes sent, or -1 on failure. + * Receive data through the network stream. + * + * @param buffer The buffer to store the received data. + * @param size The maximum size of the buffer, in bytes. + * @return The number of bytes received. + * @throws std::runtime_error If receiving the data fails. */ - static int send(fossil_io_socket_t sock, const void *data, size_t len) { - return fossil_io_network_send(sock, data, len); + ssize_t recv(void *buffer, size_t size) { + ssize_t bytes_received = fossil_nstream_recv(stream_, buffer, size); + if (bytes_received < 0) { + throw std::runtime_error(fossil_nstream_last_error()); + } + return bytes_received; } /** - * Receive data from a connected socket. This function reads data from the - * remote peer connected to the socket. - * - * @param sock The socket to use for receiving data. - * @param buffer A buffer to store the received data. - * @param len The maximum length of data to be received. - * @return The number of bytes received, or -1 on failure. + * Close the network stream. */ - static int receive(fossil_io_socket_t sock, void *buffer, size_t len) { - return fossil_io_network_receive(sock, buffer, len); + void close() { + fossil_nstream_close(stream_); } + private: /** - * Close a socket. This function releases the resources associated with the - * socket and closes the connection. - * - * @param sock The socket to be closed. + * Private constructor to wrap an existing fossil_nstream_t object. + * + * @param stream A pointer to an existing fossil_nstream_t object. */ - static void close(fossil_io_socket_t sock) { - fossil_io_network_close(sock); - } - - /** - * Send data to a specific IP address and port using a UDP socket. This function - * transmits data to the specified address and port without establishing a connection. - * - * @param sock The socket to use for sending data. - * @param data A pointer to the data to be sent. - * @param len The length of the data to be sent. - * @param ip The IP address of the destination. - * @param port The port number of the destination. - * @return The number of bytes sent, or -1 on failure. - */ - static int sendto(fossil_io_socket_t sock, const void *data, size_t len, const char *ip, uint16_t port) { - return fossil_io_network_sendto(sock, data, len, ip, port); - } - - /** - * Receive data from a specific IP address and port using a UDP socket. This function - * reads data from the specified address and port without establishing a connection. - * - * @param sock The socket to use for receiving data. - * @param buffer A buffer to store the received data. - * @param len The maximum length of data to be received. - * @param ip A buffer to store the IP address of the sender. - * @param port A pointer to store the port number of the sender. - * @return The number of bytes received, or -1 on failure. - */ - static int recvfrom(fossil_io_socket_t sock, void *buffer, size_t len, char *ip, uint16_t *port) { - return fossil_io_network_recvfrom(sock, buffer, len, ip, port); - } - - /** - * Bridge function for network operations. This function can be used to - * transfer data between two sockets, effectively bridging them. - * - * @param sock1 The first socket. - * @param sock2 The second socket. - * @return 0 on success, -1 on failure. - */ - static int bridge(fossil_io_socket_t sock1, fossil_io_socket_t sock2) { - return fossil_io_network_bridge(sock1, sock2); - } + NStream(fossil_nstream_t *stream) : stream_(stream) {} + fossil_nstream_t *stream_; /**< Pointer to the underlying network stream. */ }; } } #endif -#endif /* FOSSIL_IO_FRAMEWORK_H */ +#endif /* FOSSIL_IO_NETWORK_H */ diff --git a/code/logic/network.c b/code/logic/network.c index 2265d90..eeb2287 100644 --- a/code/logic/network.c +++ b/code/logic/network.c @@ -12,190 +12,355 @@ * ----------------------------------------------------------------------------- */ #include "fossil/io/network.h" + #include +#include #include -#include // For POSIX error handling -#include // For exit() +#include #ifdef _WIN32 - static WSADATA wsa; +#include +#include +typedef SOCKET socket_t; +#else +#include +#include +#include +#include +#include +#include +typedef int socket_t; #endif -int fossil_io_network_create(void) { +struct fossil_nstream_t { + socket_t socket_fd; + fossil_protocol_t protocol; + fossil_client_type_t client_type; + char protocol_flag[32]; + char client_type_flag[32]; + int is_connected; + int is_server; +}; + +static char fossil_last_error[256] = {0}; + +static void fossil_set_last_error(const char *msg) { + snprintf(fossil_last_error, sizeof(fossil_last_error), "%s", msg); +} + +const char *fossil_nstream_last_error(void) { + return fossil_last_error; +} + +static int fossil_socket_init(void) { #ifdef _WIN32 - return WSAStartup(MAKEWORD(2, 2), &wsa); + WSADATA wsa; + return WSAStartup(MAKEWORD(2,2), &wsa); #else - return 0; // No initialization needed on Unix-like systems + return 0; #endif } -void fossil_io_network_destroy(void) { +static void fossil_socket_cleanup(void) { #ifdef _WIN32 WSACleanup(); #endif } -fossil_io_socket_t fossil_io_network_create_socket(int type) { - fossil_io_socket_t sock = socket(AF_INET, type, 0); - if (sock == FOSSIL_IO_INVALID_SOCKET) { -#ifdef _WIN32 - fprintf(stderr, "Socket creation failed with error: %d\n", WSAGetLastError()); -#else - perror("Socket creation failed"); -#endif +typedef struct { + const char *name; + fossil_protocol_t proto; +} proto_entry_t; + +typedef struct { + const char *name; + fossil_client_type_t type; +} client_entry_t; + +static const proto_entry_t proto_table[] = { + {"tcp", FOSSIL_PROTO_TCP}, + {"udp", FOSSIL_PROTO_UDP}, + {"raw", FOSSIL_PROTO_RAW}, + {"icmp", FOSSIL_PROTO_ICMP}, + {"sctp", FOSSIL_PROTO_SCTP}, + {"http", FOSSIL_PROTO_HTTP}, + {"https", FOSSIL_PROTO_HTTPS}, + {"ftp", FOSSIL_PROTO_FTP}, + {"ssh", FOSSIL_PROTO_SSH}, + {"dns", FOSSIL_PROTO_DNS}, + {"ntp", FOSSIL_PROTO_NTP}, + {"smtp", FOSSIL_PROTO_SMTP}, + {"pop3", FOSSIL_PROTO_POP3}, + {"imap", FOSSIL_PROTO_IMAP}, + {"ldap", FOSSIL_PROTO_LDAP}, + {"mqtt", FOSSIL_PROTO_MQTT}, + {NULL, FOSSIL_PROTO_UNKNOWN} +}; + +static const client_entry_t client_table[] = { + {"mail-server", FOSSIL_CLIENT_MAIL_SERVER}, + {"server", FOSSIL_CLIENT_SERVER}, + {"mail-client", FOSSIL_CLIENT_MAIL_CLIENT}, + {"client", FOSSIL_CLIENT_CLIENT}, + {"mail-bot", FOSSIL_CLIENT_MAIL_BOT}, + {"bot", FOSSIL_CLIENT_BOT}, + {"multicast", FOSSIL_CLIENT_MULTICAST}, + {"broadcast", FOSSIL_CLIENT_BROADCAST}, + {NULL, FOSSIL_CLIENT_UNKNOWN} +}; + +fossil_protocol_t fossil_protocol_from_string(const char *str) { + if (!str) return FOSSIL_PROTO_UNKNOWN; + for (int i = 0; proto_table[i].name; ++i) { + if (strncmp(str, proto_table[i].name, strlen(proto_table[i].name)) == 0) + return proto_table[i].proto; } - return sock; + return FOSSIL_PROTO_UNKNOWN; } -int fossil_io_network_bind(fossil_io_socket_t sock, const char *ip, uint16_t port) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip ? inet_addr(ip) : INADDR_ANY; - - if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { -#ifdef _WIN32 - fprintf(stderr, "Bind failed with error: %d\n", WSAGetLastError()); -#else - perror("Bind failed"); -#endif - return -1; +fossil_client_type_t fossil_client_type_from_string(const char *str) { + if (!str) return FOSSIL_CLIENT_UNKNOWN; + for (int i = 0; client_table[i].name; ++i) { + if (strncmp(str, client_table[i].name, strlen(client_table[i].name)) == 0) + return client_table[i].type; } - return 0; + return FOSSIL_CLIENT_UNKNOWN; } -int fossil_io_network_listen(fossil_io_socket_t sock, int backlog) { - if (listen(sock, backlog) == -1) { -#ifdef _WIN32 - fprintf(stderr, "Listen failed with error: %d\n", WSAGetLastError()); -#else - perror("Listen failed"); -#endif - return -1; +const char *fossil_protocol_to_string(fossil_protocol_t proto) { + for (int i = 0; proto_table[i].name; ++i) { + if (proto_table[i].proto == proto) + return proto_table[i].name; } - return 0; + return "unknown"; } -fossil_io_socket_t fossil_io_network_accept(fossil_io_socket_t sock, char *client_ip, uint16_t *client_port) { - struct sockaddr_in client_addr; - socklen_t addr_len = sizeof(client_addr); - fossil_io_socket_t client_sock = accept(sock, (struct sockaddr*)&client_addr, &addr_len); +const char *fossil_client_type_to_string(fossil_client_type_t type) { + for (int i = 0; client_table[i].name; ++i) { + if (client_table[i].type == type) + return client_table[i].name; + } + return "unknown"; +} - if (client_sock == FOSSIL_IO_INVALID_SOCKET) { -#ifdef _WIN32 - fprintf(stderr, "Accept failed with error: %d\n", WSAGetLastError()); -#else - perror("Accept failed"); -#endif - } else if (client_ip) { - strcpy(client_ip, inet_ntoa(client_addr.sin_addr)); - if (client_port) { - *client_port = ntohs(client_addr.sin_port); - } +fossil_nstream_t *fossil_nstream_create(const char *protocol_flag, const char *client_type_flag) { + if (fossil_socket_init() != 0) { + fossil_set_last_error("Socket system initialization failed"); + return NULL; + } + + if (!protocol_flag || !client_type_flag) { + fossil_set_last_error("Invalid protocol or client type flag"); + return NULL; + } + + fossil_protocol_t protocol = fossil_protocol_from_string(protocol_flag); + fossil_client_type_t client_type = fossil_client_type_from_string(client_type_flag); + + if (protocol == FOSSIL_PROTO_UNKNOWN) { + fossil_set_last_error("Unsupported protocol"); + return NULL; + } + + if (client_type == FOSSIL_CLIENT_UNKNOWN) { + fossil_set_last_error("Unsupported client type"); + return NULL; } + + fossil_nstream_t *stream = (fossil_nstream_t *)calloc(1, sizeof(fossil_nstream_t)); + if (!stream) { + fossil_set_last_error("Memory allocation failed"); + return NULL; + } + + stream->protocol = protocol; + stream->client_type = client_type; + snprintf(stream->protocol_flag, sizeof(stream->protocol_flag), "%s", protocol_flag); + snprintf(stream->client_type_flag, sizeof(stream->client_type_flag), "%s", client_type_flag); + stream->socket_fd = -1; + return stream; +} - return client_sock; +static socket_t fossil_create_socket(fossil_protocol_t proto) { + int type = SOCK_STREAM; + int proto_num = IPPROTO_TCP; + + switch (proto) { + case FOSSIL_PROTO_TCP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_UDP: type = SOCK_DGRAM; proto_num = IPPROTO_UDP; break; + case FOSSIL_PROTO_RAW: type = SOCK_RAW; proto_num = IPPROTO_RAW; break; + case FOSSIL_PROTO_ICMP: type = SOCK_RAW; proto_num = IPPROTO_ICMP; break; + case FOSSIL_PROTO_SCTP: type = SOCK_STREAM; proto_num = IPPROTO_SCTP; break; + case FOSSIL_PROTO_HTTP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_HTTPS: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_FTP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_SSH: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_DNS: type = SOCK_DGRAM; proto_num = IPPROTO_UDP; break; + case FOSSIL_PROTO_NTP: type = SOCK_DGRAM; proto_num = IPPROTO_UDP; break; + case FOSSIL_PROTO_SMTP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_POP3: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_IMAP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_LDAP: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + case FOSSIL_PROTO_MQTT: type = SOCK_STREAM; proto_num = IPPROTO_TCP; break; + default: + fossil_set_last_error("Unsupported protocol for socket creation"); + return -1; + } + + return socket(AF_INET, type, proto_num); } -int fossil_io_network_connect(fossil_io_socket_t sock, const char *ip, uint16_t port) { - struct sockaddr_in server_addr; - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - server_addr.sin_addr.s_addr = inet_addr(ip); +int fossil_nstream_connect(fossil_nstream_t *stream, const char *host, int port) { + if (!stream) { + fossil_set_last_error("Stream is NULL"); + return -1; + } + + stream->socket_fd = fossil_create_socket(stream->protocol); + if ((int)stream->socket_fd < 0) { + return -1; + } + + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) { + fossil_set_last_error("Invalid address or address not supported"); + return -1; + } + + if (connect(stream->socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fossil_set_last_error("Connection to the server failed"); + return -1; + } + + stream->is_connected = 1; + return 0; +} - if (connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { -#ifdef _WIN32 - fprintf(stderr, "Connect failed with error: %d\n", WSAGetLastError()); -#else - perror("Connect failed"); -#endif +int fossil_nstream_listen(fossil_nstream_t *stream, const char *host, int port) { + if (!stream) { + fossil_set_last_error("Stream is NULL"); + return -1; + } + + stream->socket_fd = fossil_create_socket(stream->protocol); + if (stream->socket_fd == (socket_t)-1) { + return -1; + } + + int opt = 1; + if (setsockopt(stream->socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { + fossil_set_last_error("Failed to set socket options"); + return -1; + } + + struct sockaddr_in addr = {0}; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = (host == NULL) ? INADDR_ANY : inet_addr(host); + + if (bind(stream->socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fossil_set_last_error("Bind failed"); return -1; } + + if (listen(stream->socket_fd, SOMAXCONN) < 0) { + fossil_set_last_error("Listen failed"); + return -1; + } + + stream->is_server = 1; return 0; } -int fossil_io_network_send(fossil_io_socket_t sock, const void *data, size_t len) { - int bytes_sent = send(sock, data, (int)len, 0); - if (bytes_sent == -1) { +fossil_nstream_t *fossil_nstream_accept(fossil_nstream_t *server) { + if (!server || !server->is_server) { + fossil_set_last_error("Invalid server stream"); + return NULL; + } + + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + socket_t client_fd = accept(server->socket_fd, (struct sockaddr *)&addr, &addrlen); + if ((int)client_fd < 0) { #ifdef _WIN32 - fprintf(stderr, "Send failed with error: %d\n", WSAGetLastError()); + if (WSAGetLastError() != WSAEWOULDBLOCK) { #else - perror("Send failed"); + if (errno != EWOULDBLOCK && errno != EAGAIN) { #endif + fossil_set_last_error("Accept failed"); + } + return NULL; } - return bytes_sent; -} - -int fossil_io_network_receive(fossil_io_socket_t sock, void *buffer, size_t len) { - int bytes_received = recv(sock, buffer, (int)len, 0); - if (bytes_received == -1) { + + fossil_nstream_t *client = (fossil_nstream_t *)calloc(1, sizeof(fossil_nstream_t)); + if (!client) { + fossil_set_last_error("Memory allocation failed"); #ifdef _WIN32 - fprintf(stderr, "Receive failed with error: %d\n", WSAGetLastError()); + closesocket(client_fd); #else - perror("Receive failed"); + close(client_fd); #endif + return NULL; } - return bytes_received; + + *client = *server; + client->socket_fd = client_fd; + client->is_server = 0; + client->is_connected = 1; + + return client; } -int fossil_io_network_sendto(fossil_io_socket_t sock, const void *data, size_t len, const char *ip, uint16_t port) { - struct sockaddr_in dest_addr; - dest_addr.sin_family = AF_INET; - dest_addr.sin_port = htons(port); - dest_addr.sin_addr.s_addr = inet_addr(ip); - - int bytes_sent = sendto(sock, data, (int)len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)); - if (bytes_sent == -1) { +ssize_t fossil_nstream_send(fossil_nstream_t *stream, const void *buffer, size_t size) { + if (!stream || (int)stream->socket_fd < 0) { + fossil_set_last_error("Invalid stream or socket"); + return -1; + } #ifdef _WIN32 - fprintf(stderr, "Sendto failed with error: %d\n", WSAGetLastError()); + ssize_t sent_bytes = send(stream->socket_fd, (const char *)buffer, (int)size, 0); #else - perror("Sendto failed"); + ssize_t sent_bytes = send(stream->socket_fd, buffer, size, 0); #endif + if (sent_bytes < 0) { + fossil_set_last_error("Failed to send data"); } - return bytes_sent; + return sent_bytes; } -int fossil_io_network_recvfrom(fossil_io_socket_t sock, void *buffer, size_t len, char *ip, uint16_t *port) { - struct sockaddr_in src_addr; - socklen_t addr_len = sizeof(src_addr); - - int bytes_received = recvfrom(sock, buffer, (int)len, 0, (struct sockaddr*)&src_addr, &addr_len); - if (bytes_received == -1) { +ssize_t fossil_nstream_recv(fossil_nstream_t *stream, void *buffer, size_t size) { + if (!stream || stream->socket_fd == (socket_t)-1) { + fossil_set_last_error("Invalid stream or socket"); + return -1; + } #ifdef _WIN32 - fprintf(stderr, "Recvfrom failed with error: %d\n", WSAGetLastError()); + ssize_t received_bytes = recv(stream->socket_fd, (char *)buffer, (int)size, 0); #else - perror("Recvfrom failed"); + ssize_t received_bytes = recv(stream->socket_fd, buffer, size, 0); #endif - } else if (ip) { - strcpy(ip, inet_ntoa(src_addr.sin_addr)); - if (port) { - *port = ntohs(src_addr.sin_port); - } + if (received_bytes < 0) { + fossil_set_last_error("Failed to receive data"); } - return bytes_received; + return received_bytes; } -void fossil_io_network_close(fossil_io_socket_t sock) { +void fossil_nstream_close(fossil_nstream_t *stream) { + if (!stream || stream->socket_fd == (socket_t)-1) return; #ifdef _WIN32 - if (closesocket(sock) == SOCKET_ERROR) { - fprintf(stderr, "Close socket failed with error: %d\n", WSAGetLastError()); - } + closesocket(stream->socket_fd); #else - if (close(sock) == -1) { - perror("Close socket failed"); - } + close(stream->socket_fd); #endif + stream->socket_fd = -1; + stream->is_connected = 0; } -int fossil_io_network_bridge(fossil_io_socket_t sock1, fossil_io_socket_t sock2) { - char buffer[1024]; - int bytes_received; - - while ((bytes_received = fossil_io_network_receive(sock1, buffer, sizeof(buffer))) > 0) { - if (fossil_io_network_send(sock2, buffer, bytes_received) == -1) { - return -1; - } - } - - return bytes_received; +void fossil_nstream_destroy(fossil_nstream_t *stream) { + if (!stream) return; + fossil_nstream_close(stream); + free(stream); + fossil_socket_cleanup(); } diff --git a/code/logic/stream.c b/code/logic/stream.c index 7a91204..20c28c1 100644 --- a/code/logic/stream.c +++ b/code/logic/stream.c @@ -87,7 +87,7 @@ static const char *fossil_fstream_mode_from_keyword(const char *keyword) { int32_t fossil_fstream_open(fossil_fstream_t *stream, const char *filename, const char *mode) { if (stream == NULL || filename == NULL || mode == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (strlen(filename) >= FOSSIL_BUFFER_MEDIUM) { @@ -119,7 +119,7 @@ void fossil_fstream_close(fossil_fstream_t *stream) { int32_t fossil_fstream_freopen(fossil_fstream_t *stream, const char *filename, const char *mode, FILE *file) { if (stream == NULL || filename == NULL || mode == NULL || file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } FILE *new_file = freopen(filename, fossil_fstream_mode_from_keyword(mode), file); @@ -138,7 +138,7 @@ int32_t fossil_fstream_freopen(fossil_fstream_t *stream, const char *filename, c size_t fossil_fstream_read(fossil_fstream_t *stream, void *buffer, size_t size, size_t count) { if (stream == NULL || buffer == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } size_t bytes_read = fread(buffer, size, count, stream->file); @@ -155,7 +155,7 @@ size_t fossil_fstream_read(fossil_fstream_t *stream, void *buffer, size_t size, size_t fossil_fstream_write(fossil_fstream_t *stream, const void *buffer, size_t size, size_t count) { if (stream == NULL || buffer == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } size_t bytes_written = fwrite(buffer, size, count, stream->file); @@ -172,7 +172,7 @@ size_t fossil_fstream_write(fossil_fstream_t *stream, const void *buffer, size_t int32_t fossil_fstream_append(fossil_fstream_t *stream, const void * restrict buffer, size_t size, int32_t count) { if (stream == NULL || buffer == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } fseek(stream->file, 0, SEEK_END); @@ -190,7 +190,7 @@ int32_t fossil_fstream_append(fossil_fstream_t *stream, const void * restrict bu int32_t fossil_fstream_seek(fossil_fstream_t *stream, int64_t offset, int32_t origin) { if (stream == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } int32_t result = fseek(stream->file, offset, origin); @@ -207,7 +207,7 @@ int32_t fossil_fstream_seek(fossil_fstream_t *stream, int64_t offset, int32_t or int32_t fossil_fstream_tell(fossil_fstream_t *stream) { if (stream == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } long position = ftell(stream->file); @@ -224,7 +224,7 @@ int32_t fossil_fstream_tell(fossil_fstream_t *stream) { int32_t fossil_fstream_save(fossil_fstream_t *stream, const char *new_filename) { if (stream == NULL || stream->file == NULL || new_filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (strlen(new_filename) >= FOSSIL_BUFFER_MEDIUM) { @@ -236,7 +236,7 @@ int32_t fossil_fstream_save(fossil_fstream_t *stream, const char *new_filename) if (rename(stream->filename, new_filename) != 0) { fprintf(stderr, "Error: Failed to save %s\n", new_filename); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } // Reopen the file with the new name @@ -253,7 +253,7 @@ int32_t fossil_fstream_save(fossil_fstream_t *stream, const char *new_filename) int32_t fossil_fstream_copy(const char *source_filename, const char *destination_filename) { if (source_filename == NULL || destination_filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } FILE *source_file = fopen(source_filename, "rb"); @@ -291,7 +291,7 @@ int32_t fossil_fstream_copy(const char *source_filename, const char *destination int32_t fossil_fstream_remove(const char *filename) { if (filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (remove(filename) == 0) { @@ -305,7 +305,7 @@ int32_t fossil_fstream_remove(const char *filename) { int32_t fossil_fstream_rename(const char *old_filename, const char *new_filename) { if (old_filename == NULL || new_filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (rename(old_filename, new_filename) != 0) { @@ -319,7 +319,7 @@ int32_t fossil_fstream_rename(const char *old_filename, const char *new_filename int32_t fossil_fstream_flush(fossil_fstream_t *stream) { if (stream == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (fflush(stream->file) != 0) { @@ -333,7 +333,7 @@ int32_t fossil_fstream_flush(fossil_fstream_t *stream) { int32_t fossil_fstream_setpos(fossil_fstream_t *stream, int32_t pos) { if (stream == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (fseek(stream->file, pos, SEEK_SET) != 0) { @@ -347,7 +347,7 @@ int32_t fossil_fstream_setpos(fossil_fstream_t *stream, int32_t pos) { int32_t fossil_fstream_getpos(fossil_fstream_t *stream, int32_t *pos) { if (stream == NULL || stream->file == NULL || pos == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } *pos = ftell(stream->file); @@ -362,7 +362,7 @@ int32_t fossil_fstream_getpos(fossil_fstream_t *stream, int32_t *pos) { int32_t fossil_fstream_rotate(const char *filename, int32_t n) { if (filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } char old_filename[FOSSIL_BUFFER_MEDIUM]; @@ -389,7 +389,7 @@ int32_t fossil_fstream_rotate(const char *filename, int32_t n) { int32_t fossil_fstream_backup(const char *filename, const char *backup_suffix) { if (filename == NULL || backup_suffix == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } char backup_filename[FOSSIL_BUFFER_MEDIUM + 10]; // Length of backup_suffix + maximum integer length @@ -407,7 +407,7 @@ int32_t fossil_fstream_backup(const char *filename, const char *backup_suffix) { int32_t fossil_fstream_file_exists(const char *filename) { if (filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } FILE *file = fopen(filename, "r"); @@ -422,7 +422,7 @@ int32_t fossil_fstream_file_exists(const char *filename) { int32_t fossil_fstream_get_size(fossil_fstream_t *stream) { if (stream == NULL || stream->file == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } fseek(stream->file, 0, SEEK_END); @@ -441,7 +441,7 @@ int32_t fossil_fstream_get_size(fossil_fstream_t *stream) { int32_t fossil_fstream_delete(const char *filename) { if (filename == NULL) { fprintf(stderr, "Error: Null pointer\n"); - return FOSSIL_ERROR_NULL_POINTER; + return FOSSIL_ERROR_CNULL_POINTER; } if (remove(filename) == 0) { diff --git a/code/tests/cases/test_error.c b/code/tests/cases/test_error.c index 1847e51..c09ae20 100644 --- a/code/tests/cases/test_error.c +++ b/code/tests/cases/test_error.c @@ -48,8 +48,8 @@ FOSSIL_TEST_CASE(c_test_io_what_no_error) { ASSUME_ITS_EQUAL_CSTR("No error, operation successful.", result); } -FOSSIL_TEST_CASE(c_test_io_what_null_pointer) { - const char *result = fossil_io_what(FOSSIL_ERROR_NULL_POINTER); +FOSSIL_TEST_CASE(c_test_io_what_CNULL_pointer) { + const char *result = fossil_io_what(FOSSIL_ERROR_CNULL_POINTER); ASSUME_ITS_EQUAL_CSTR("Null pointer encountered.", result); } @@ -204,7 +204,7 @@ FOSSIL_TEST_CASE(c_test_io_what_serialization_failed) { FOSSIL_TEST_GROUP(c_error_tests) { FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_no_error); - FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_null_pointer); + FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_CNULL_pointer); FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_invalid_argument); FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_type_mismatch); FOSSIL_TEST_ADD(c_error_suite, c_test_io_what_invalid_operation); diff --git a/code/tests/cases/test_error.cpp b/code/tests/cases/test_error.cpp index 0f61f87..b02c840 100644 --- a/code/tests/cases/test_error.cpp +++ b/code/tests/cases/test_error.cpp @@ -48,8 +48,8 @@ FOSSIL_TEST_CASE(cpp_test_io_what_no_error) { ASSUME_ITS_EQUAL_CSTR("No error, operation successful.", result); } -FOSSIL_TEST_CASE(cpp_test_io_what_null_pointer) { - const char *result = fossil_io_what(FOSSIL_ERROR_NULL_POINTER); +FOSSIL_TEST_CASE(cpp_test_io_what_CNULL_pointer) { + const char *result = fossil_io_what(FOSSIL_ERROR_CNULL_POINTER); ASSUME_ITS_EQUAL_CSTR("Null pointer encountered.", result); } @@ -207,7 +207,7 @@ FOSSIL_TEST_CASE(cpp_test_io_error_exception_no_error) { } } -FOSSIL_TEST_CASE(cpp_test_io_error_exception_null_pointer) { +FOSSIL_TEST_CASE(cpp_test_io_error_exception_CNULL_pointer) { try { throw fossil::io::Error(fossil::io::ErrorCode::NULL_POINTER); } catch (const fossil::io::Error& e) { @@ -483,7 +483,7 @@ FOSSIL_TEST_CASE(cpp_test_io_error_exception_serialization_failed) { FOSSIL_TEST_GROUP(cpp_error_tests) { FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_no_error); - FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_null_pointer); + FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_CNULL_pointer); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_invalid_argument); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_type_mismatch); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_invalid_operation); @@ -515,7 +515,7 @@ FOSSIL_TEST_GROUP(cpp_error_tests) { FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_what_serialization_failed); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_no_error); - FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_null_pointer); + FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_CNULL_pointer); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_invalid_argument); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_type_mismatch); FOSSIL_TEST_ADD(cpp_error_suite, cpp_test_io_error_exception_invalid_operation); diff --git a/code/tests/cases/test_network.c b/code/tests/cases/test_network.c index 387ffe6..39f585a 100644 --- a/code/tests/cases/test_network.c +++ b/code/tests/cases/test_network.c @@ -12,108 +12,123 @@ * ----------------------------------------------------------------------------- */ #include - #include "fossil/io/framework.h" // * * * * * * * * * * * * * * * * * * * * * * * * -// * Fossil Logic Test Utilites +// * Fossil Logic Test Utilities // * * * * * * * * * * * * * * * * * * * * * * * * // Setup steps for things like test fixtures and // mock objects are set here. // * * * * * * * * * * * * * * * * * * * * * * * * -// Define the test suite and add test cases FOSSIL_TEST_SUITE(c_network_suite); // Setup function for the test suite FOSSIL_SETUP(c_network_suite) { - // Setup code here + // Setup code (if needed) } // Teardown function for the test suite FOSSIL_TEARDOWN(c_network_suite) { - // Teardown code here + // Teardown code (if needed) } // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Cases // * * * * * * * * * * * * * * * * * * * * * * * * -// The test cases below are provided as samples, inspired -// by the Meson build system's approach of using test cases -// as samples for library usage. -// * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(c_test_network_init) { - int result = fossil_io_network_create(); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_destroy(); +FOSSIL_TEST_CASE(c_test_nstream_create_and_destroy) { + const char *protocols[] = {"tcp", "udp", "raw", "icmp", "sctp", "http", "https", "ftp", "ssh", "dns", "ntp", "smtp", "pop3", "imap", "ldap", "mqtt"}; + const char *clients[] = {"mail-server", "server", "mail-client", "client", "mail-bot", "bot", "multicast", "broadcast"}; + + for (size_t i = 0; i < sizeof(protocols) / sizeof(protocols[0]); i++) { + for (size_t j = 0; j < sizeof(clients) / sizeof(clients[0]); j++) { + fossil_nstream_t *stream = fossil_nstream_create(protocols[i], clients[j]); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } + } } -FOSSIL_TEST_CASE(c_test_network_create_socket) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} +FOSSIL_TEST_CASE(c_test_nstream_connect_invalid_host) { + fossil_nstream_t *stream = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(stream); -FOSSIL_TEST_CASE(c_test_network_bind) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil_io_network_bind(sock, "127.0.0.1", 8080); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); + // Attempt to connect to an invalid host + ASSUME_ITS_EQUAL_I32(-1, fossil_nstream_connect(stream, "invalid_host", 12345)); + fossil_nstream_destroy(stream); } -FOSSIL_TEST_CASE(c_test_network_listen) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_bind(sock, "127.0.0.1", 8080); - int result = fossil_io_network_listen(sock, 5); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} +FOSSIL_TEST_CASE(c_test_nstream_listen_and_accept) { + fossil_nstream_t *server = fossil_nstream_create("tcp", "server"); + ASSUME_NOT_CNULL(server); -FOSSIL_TEST_CASE(c_test_network_close) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - // No direct way to test close, but we assume no errors if it reaches here - fossil_io_network_destroy(); + // Start listening on a local port + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_listen(server, "127.0.0.1", 12345)); + + // Simulate a client connecting + fossil_nstream_t *client = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(client); + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_connect(client, "127.0.0.1", 12345)); + + // Accept the client connection + fossil_nstream_t *accepted_client = fossil_nstream_accept(server); + ASSUME_NOT_CNULL(accepted_client); + + // Cleanup + fossil_nstream_destroy(client); + fossil_nstream_destroy(accepted_client); + fossil_nstream_destroy(server); } -FOSSIL_TEST_CASE(c_test_network_create_udp_socket) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - fossil_io_network_destroy(); +FOSSIL_TEST_CASE(c_test_nstream_send_and_receive) { + fossil_nstream_t *server = fossil_nstream_create("tcp", "server"); + ASSUME_NOT_CNULL(server); + + // Start listening on a local port + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_listen(server, "127.0.0.1", 12345)); + + // Simulate a client connecting + fossil_nstream_t *client = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(client); + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_connect(client, "127.0.0.1", 12345)); + + // Accept the client connection + fossil_nstream_t *accepted_client = fossil_nstream_accept(server); + ASSUME_NOT_CNULL(accepted_client); + + // Send and receive data + const char *message = "Hello, Fossil!"; + ASSUME_ITS_EQUAL_I32(strlen(message), fossil_nstream_send(client, message, strlen(message))); + + char buffer[1024] = {0}; + ASSUME_ITS_EQUAL_I32(strlen(message), fossil_nstream_recv(accepted_client, buffer, sizeof(buffer))); + ASSUME_ITS_EQUAL_CSTR(message, buffer); + + // Cleanup + fossil_nstream_destroy(client); + fossil_nstream_destroy(accepted_client); + fossil_nstream_destroy(server); } -FOSSIL_TEST_CASE(c_test_network_bind_udp) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil_io_network_bind(sock, "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); +FOSSIL_TEST_CASE(c_test_nstream_protocols) { + const char *protocols[] = {"tcp", "udp", "raw", "icmp", "sctp", "http", "https", "ftp", "ssh", "dns", "ntp", "smtp", "pop3", "imap", "ldap", "mqtt"}; + + for (size_t i = 0; i < sizeof(protocols) / sizeof(protocols[0]); i++) { + fossil_nstream_t *stream = fossil_nstream_create(protocols[i], "client"); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } } -FOSSIL_TEST_CASE(c_test_network_sendto_udp) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - const char *message = "Hello, UDP!"; - int bytes_sent = fossil_io_network_sendto(sock, message, strlen(message), "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32((int)strlen(message), bytes_sent); - fossil_io_network_close(sock); - fossil_io_network_destroy(); +FOSSIL_TEST_CASE(c_test_nstream_client_types) { + const char *clients[] = {"mail-server", "server", "mail-client", "client", "mail-bot", "bot", "multicast", "broadcast"}; + + for (size_t i = 0; i < sizeof(clients) / sizeof(clients[0]); i++) { + fossil_nstream_t *stream = fossil_nstream_create("tcp", clients[i]); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } } // * * * * * * * * * * * * * * * * * * * * * * * * @@ -121,14 +136,12 @@ FOSSIL_TEST_CASE(c_test_network_sendto_udp) { // * * * * * * * * * * * * * * * * * * * * * * * * FOSSIL_TEST_GROUP(c_network_tests) { - FOSSIL_TEST_ADD(c_network_suite, c_test_network_init); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_create_socket); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_bind); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_listen); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_close); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_create_udp_socket); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_bind_udp); - FOSSIL_TEST_ADD(c_network_suite, c_test_network_sendto_udp); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_create_and_destroy); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_connect_invalid_host); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_listen_and_accept); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_send_and_receive); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_protocols); + FOSSIL_TEST_ADD(c_network_suite, c_test_nstream_client_types); FOSSIL_TEST_REGISTER(c_network_suite); } diff --git a/code/tests/cases/test_network.cpp b/code/tests/cases/test_network.cpp index 75388a0..f4965e5 100644 --- a/code/tests/cases/test_network.cpp +++ b/code/tests/cases/test_network.cpp @@ -12,180 +12,186 @@ * ----------------------------------------------------------------------------- */ #include - #include "fossil/io/framework.h" // * * * * * * * * * * * * * * * * * * * * * * * * -// * Fossil Logic Test Utilites +// * Fossil Logic Test Utilities // * * * * * * * * * * * * * * * * * * * * * * * * // Setup steps for things like test fixtures and // mock objects are set here. // * * * * * * * * * * * * * * * * * * * * * * * * -// Define the test suite and add test cases FOSSIL_TEST_SUITE(cpp_network_suite); // Setup function for the test suite FOSSIL_SETUP(cpp_network_suite) { - // Setup code here + // Setup code (if needed) } // Teardown function for the test suite FOSSIL_TEARDOWN(cpp_network_suite) { - // Teardown code here + // Teardown code (if needed) } // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Cases // * * * * * * * * * * * * * * * * * * * * * * * * -// The test cases below are provided as samples, inspired -// by the Meson build system's approach of using test cases -// as samples for library usage. -// * * * * * * * * * * * * * * * * * * * * * * * * -FOSSIL_TEST_CASE(cpp_test_network_init) { - int result = fossil_io_network_create(); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_destroy(); -} +FOSSIL_TEST_CASE(cpp_test_nstream_create_and_destroy) { + const char *protocols[] = {"tcp", "udp", "raw", "icmp", "sctp", "http", "https", "ftp", "ssh", "dns", "ntp", "smtp", "pop3", "imap", "ldap", "mqtt"}; + const char *clients[] = {"mail-server", "server", "mail-client", "client", "mail-bot", "bot", "multicast", "broadcast"}; -FOSSIL_TEST_CASE(cpp_test_network_create_socket) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - fossil_io_network_destroy(); + for (size_t i = 0; i < sizeof(protocols) / sizeof(protocols[0]); i++) { + for (size_t j = 0; j < sizeof(clients) / sizeof(clients[0]); j++) { + fossil_nstream_t *stream = fossil_nstream_create(protocols[i], clients[j]); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } + } } -FOSSIL_TEST_CASE(cpp_test_network_bind) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil_io_network_bind(sock, "127.0.0.1", 8080); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} +FOSSIL_TEST_CASE(cpp_test_nstream_connect_invalid_host) { + fossil_nstream_t *stream = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(stream); -FOSSIL_TEST_CASE(cpp_test_network_listen) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_bind(sock, "127.0.0.1", 8080); - int result = fossil_io_network_listen(sock, 5); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); + // Attempt to connect to an invalid host + ASSUME_ITS_EQUAL_I32(-1, fossil_nstream_connect(stream, "invalid_host", 12345)); + fossil_nstream_destroy(stream); } -FOSSIL_TEST_CASE(cpp_test_network_close) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - // No direct way to test close, but we assume no errors if it reaches here - fossil_io_network_destroy(); -} +FOSSIL_TEST_CASE(cpp_test_nstream_listen_and_accept) { + fossil_nstream_t *server = fossil_nstream_create("tcp", "server"); + ASSUME_NOT_CNULL(server); -FOSSIL_TEST_CASE(cpp_test_network_create_udp_socket) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} + // Start listening on a local port + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_listen(server, "127.0.0.1", 12345)); -FOSSIL_TEST_CASE(cpp_test_network_bind_udp) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil_io_network_bind(sock, "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32(0, result); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} + // Simulate a client connecting + fossil_nstream_t *client = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(client); + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_connect(client, "127.0.0.1", 12345)); -FOSSIL_TEST_CASE(cpp_test_network_sendto_udp) { - fossil_io_network_create(); - fossil_io_socket_t sock = fossil_io_network_create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - const char *message = "Hello, UDP!"; - int bytes_sent = fossil_io_network_sendto(sock, message, strlen(message), "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32((int)strlen(message), bytes_sent); - fossil_io_network_close(sock); - fossil_io_network_destroy(); -} + // Accept the client connection + fossil_nstream_t *accepted_client = fossil_nstream_accept(server); + ASSUME_NOT_CNULL(accepted_client); -FOSSIL_TEST_CASE(cpp_test_network_class_init) { - int result = fossil::io::Network::init(); - ASSUME_ITS_EQUAL_I32(0, result); - fossil::io::Network::cleanup(); + // Cleanup + fossil_nstream_destroy(client); + fossil_nstream_destroy(accepted_client); + fossil_nstream_destroy(server); } -FOSSIL_TEST_CASE(cpp_test_network_class_create_socket) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_send_and_receive) { + fossil_nstream_t *server = fossil_nstream_create("tcp", "server"); + ASSUME_NOT_CNULL(server); + + // Start listening on a local port + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_listen(server, "127.0.0.1", 12345)); + + // Simulate a client connecting + fossil_nstream_t *client = fossil_nstream_create("tcp", "client"); + ASSUME_NOT_CNULL(client); + ASSUME_ITS_EQUAL_I32(0, fossil_nstream_connect(client, "127.0.0.1", 12345)); + + // Accept the client connection + fossil_nstream_t *accepted_client = fossil_nstream_accept(server); + ASSUME_NOT_CNULL(accepted_client); + + // Send and receive data + const char *message = "Hello, Fossil!"; + ASSUME_ITS_EQUAL_I32(strlen(message), fossil_nstream_send(client, message, strlen(message))); + + char buffer[1024] = {0}; + ASSUME_ITS_EQUAL_I32(strlen(message), fossil_nstream_recv(accepted_client, buffer, sizeof(buffer))); + ASSUME_ITS_EQUAL_CSTR(message, buffer); + + // Cleanup + fossil_nstream_destroy(client); + fossil_nstream_destroy(accepted_client); + fossil_nstream_destroy(server); } -FOSSIL_TEST_CASE(cpp_test_network_class_bind) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil::io::Network::bind(sock, "127.0.0.1", 8080); - ASSUME_ITS_EQUAL_I32(0, result); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_protocols) { + const char *protocols[] = {"tcp", "udp", "raw", "icmp", "sctp", "http", "https", "ftp", "ssh", "dns", "ntp", "smtp", "pop3", "imap", "ldap", "mqtt"}; + + for (size_t i = 0; i < sizeof(protocols) / sizeof(protocols[0]); i++) { + fossil_nstream_t *stream = fossil_nstream_create(protocols[i], "client"); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } } -FOSSIL_TEST_CASE(cpp_test_network_class_listen) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil::io::Network::bind(sock, "127.0.0.1", 8080); - int result = fossil::io::Network::listen(sock, 5); - ASSUME_ITS_EQUAL_I32(0, result); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_client_types) { + const char *clients[] = {"mail-server", "server", "mail-client", "client", "mail-bot", "bot", "multicast", "broadcast"}; + + for (size_t i = 0; i < sizeof(clients) / sizeof(clients[0]); i++) { + fossil_nstream_t *stream = fossil_nstream_create("tcp", clients[i]); + ASSUME_NOT_CNULL(stream); + fossil_nstream_destroy(stream); + } } -FOSSIL_TEST_CASE(cpp_test_network_class_close) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_TCP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_class_create_and_destroy) { + using namespace fossil::io; + + const std::string protocols[] = {"tcp", "udp", "raw", "icmp", "sctp", "http", "https", "ftp", "ssh", "dns", "ntp", "smtp", "pop3", "imap", "ldap", "mqtt"}; + const std::string clients[] = {"mail-server", "server", "mail-client", "client", "mail-bot", "bot", "multicast", "broadcast"}; + + for (const auto &protocol : protocols) { + for (const auto &client : clients) { + NStream stream(protocol, client); + } + } } -FOSSIL_TEST_CASE(cpp_test_network_class_create_udp_socket) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_class_connect_invalid_host) { + using namespace fossil::io; + + try { + NStream stream("tcp", "client"); + stream.connect("invalid_host", 12345); + ASSUME_ITS_FALSE("Expected exception for invalid host"); + } catch (const std::runtime_error &e) { + ASSUME_ITS_TRUE("Caught expected exception"); + } } -FOSSIL_TEST_CASE(cpp_test_network_class_bind_udp) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - int result = fossil::io::Network::bind(sock, "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32(0, result); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_class_listen_and_accept) { + using namespace fossil::io; + + NStream server("tcp", "server"); + server.listen("127.0.0.1", 12345); + + NStream client("tcp", "client"); + client.connect("127.0.0.1", 12345); + + NStream *accepted_client = server.accept(); + ASSUME_NOT_CNULL(accepted_client); + + delete accepted_client; } -FOSSIL_TEST_CASE(cpp_test_network_class_sendto_udp) { - fossil::io::Network::init(); - fossil_io_socket_t sock = fossil::io::Network::create_socket(FOSSIL_IO_SOCKET_TYPE_UDP); - ASSUME_NOT_EQUAL_I32(FOSSIL_IO_INVALID_SOCKET, sock); - const char *message = "Hello, UDP!"; - int bytes_sent = fossil::io::Network::sendto(sock, message, strlen(message), "127.0.0.1", 8081); - ASSUME_ITS_EQUAL_I32((int)strlen(message), bytes_sent); - fossil::io::Network::close(sock); - fossil::io::Network::cleanup(); +FOSSIL_TEST_CASE(cpp_test_nstream_class_send_and_receive) { + using namespace fossil::io; + + NStream server("tcp", "server"); + server.listen("127.0.0.1", 12345); + + NStream client("tcp", "client"); + client.connect("127.0.0.1", 12345); + + NStream *accepted_client = server.accept(); + ASSUME_NOT_CNULL(accepted_client); + + const std::string message = "Hello, Fossil!"; + client.send(message.c_str(), message.size()); + + char buffer[1024] = {0}; + ssize_t bytes_received = accepted_client->recv(buffer, sizeof(buffer)); + ASSUME_ITS_EQUAL_I32(message.size(), bytes_received); + ASSUME_ITS_EQUAL_CSTR(message.c_str(), buffer); + + delete accepted_client; } // * * * * * * * * * * * * * * * * * * * * * * * * @@ -193,23 +199,17 @@ FOSSIL_TEST_CASE(cpp_test_network_class_sendto_udp) { // * * * * * * * * * * * * * * * * * * * * * * * * FOSSIL_TEST_GROUP(cpp_network_tests) { - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_init); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_create_socket); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_bind); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_listen); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_close); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_create_udp_socket); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_bind_udp); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_sendto_udp); - - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_init); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_create_socket); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_bind); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_listen); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_close); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_create_udp_socket); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_bind_udp); - FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_network_class_sendto_udp); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_create_and_destroy); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_connect_invalid_host); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_listen_and_accept); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_send_and_receive); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_protocols); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_client_types); + + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_class_create_and_destroy); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_class_connect_invalid_host); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_class_listen_and_accept); + FOSSIL_TEST_ADD(cpp_network_suite, cpp_test_nstream_class_send_and_receive); FOSSIL_TEST_REGISTER(cpp_network_suite); }